diff --git a/.env.production.sample b/.env.production.sample index 8ea569fb0168cb..9ff63c49ef12c2 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -88,21 +88,3 @@ S3_ALIAS_HOST=files.example.com # ----------------------- IP_RETENTION_PERIOD=31556952 SESSION_RETENTION_PERIOD=31556952 - -# Fetch All Replies Behavior -# -------------------------- - -# Period to wait between fetching replies (in minutes) -FETCH_REPLIES_COOLDOWN_MINUTES=15 - -# Period to wait after a post is first created before fetching its replies (in minutes) -FETCH_REPLIES_INITIAL_WAIT_MINUTES=5 - -# Max number of replies to fetch - total, recursively through a whole reply tree -FETCH_REPLIES_MAX_GLOBAL=1000 - -# Max number of replies to fetch - for a single post -FETCH_REPLIES_MAX_SINGLE=500 - -# Max number of replies Collection pages to fetch - total -FETCH_REPLIES_MAX_PAGES=500 diff --git a/.gitignore b/.gitignore index 95a8f19a4577cc..7a172f631a486f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ /public/packs /public/packs-dev /public/packs-test +stats.html .env .env.production node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f634505428b5c4..b706b8ac47ef78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,18 @@ All notable changes to this project will be documented in this file. ### Added -- **Add support for allowing and authoring quotes** (#35355, #35578, #35614, #35618, #35624, #35626, #35652, #35629, #35665, #35653, #35670, #35677, #35690, #35697, #35689, #35699, #35700, #35701, #35709, #35714, #35713, #35715, #35725, #35749, #35769, #35780, #35762, #35804, #35808, #35805, #35819, #35824, #35828, #35822, #35835, #35865, #35860, #35832, #35891, #35894, #35895, #35820, #35917, #35924, #35925, #35914, #35930, #35941, #35939, #35948, #35955, #35967, #35990, #35991, #35975, #35971, #36002, #35986, #36031, #36034, #36038, #36054, #36052, #36055, #36065, #36068, #36083, #36087, #36080, #36091, #36090, #36118, #36119, #36128, #36094, #36129, #36138, #36132, #36151, #36158, #36171, #36194, #36220, #36169, #36130, #36249, #36153, #36299, #36291, #36301, #36315, #36317, #36364, #36383, #36381, #36459, #36464, #36461, #36516 and #36528 by @ChaosExAnima, @ClearlyClaire, @Lycolia, @diondiondion, and @tribela)\ +- **Add support for allowing and authoring quotes** (#35355, #35578, #35614, #35618, #35624, #35626, #35652, #35629, #35665, #35653, #35670, #35677, #35690, #35697, #35689, #35699, #35700, #35701, #35709, #35714, #35713, #35715, #35725, #35749, #35769, #35780, #35762, #35804, #35808, #35805, #35819, #35824, #35828, #35822, #35835, #35865, #35860, #35832, #35891, #35894, #35895, #35820, #35917, #35924, #35925, #35914, #35930, #35941, #35939, #35948, #35955, #35967, #35990, #35991, #35975, #35971, #36002, #35986, #36031, #36034, #36038, #36054, #36052, #36055, #36065, #36068, #36083, #36087, #36080, #36091, #36090, #36118, #36119, #36128, #36094, #36129, #36138, #36132, #36151, #36158, #36171, #36194, #36220, #36169, #36130, #36249, #36153, #36299, #36291, #36301, #36315, #36317, #36364, #36383, #36381, #36459, #36464, #36461, #36516, #36528, #36549, #36550 and #36559 by @ChaosExAnima, @ClearlyClaire, @Lycolia, @diondiondion, and @tribela)\ This includes a revamp of the composer interface.\ See https://blog.joinmastodon.org/2025/09/introducing-quote-posts/ for a user-centric overview of the feature, and https://docs.joinmastodon.org/client/quotes/ for API documentation. -- **Add support for fetching and refreshing replies to the web UI** (#35210, #35496, #35575, #35500, #35577, #35602, #35603, #35654, #36141, #36237, #36172, #36256, #36271, #36334, #36382, #36239, #36484 and #36481 by @ClearlyClaire, @Gargron, and @diondiondion) +- **Add support for fetching and refreshing replies to the web UI** (#35210, #35496, #35575, #35500, #35577, #35602, #35603, #35654, #36141, #36237, #36172, #36256, #36271, #36334, #36382, #36239, #36484, #36481, #36583, #36627 and #36547 by @ClearlyClaire, @diondiondion, @Gargron and @renchap) - **Add ability to block words in usernames** (#35407, #35655, and #35806 by @ClearlyClaire and @Gargron) +- Add ability to individually disable local or remote feeds for visitors or logged-in users `disabled` value to server setting for live and topic feeds, as well as user permission to bypass that (#36338, #36467, #36497, #36563, #36577, #36585, and #36607 by @ClearlyClaire)\ + This splits the `timeline_preview` setting into four more granular settings controlling live feeds and topic (hashtag, trending link) feeds, with 3 values each: `public`, `authenticated`, `disabled`.\ + When `disabled`, users with the “View live and topic feeds” will still be able to view them. - Add support for displaying of quote posts in Moderator UI (#35964 by @ThisIsMissEm) - Add support for displaying link previews for Admin UI (#35958 by @ThisIsMissEm) +- Add a new server setting to choose the server landing page (#36588 and #36602 by @ClearlyClaire and @renchap) +- Add support for `Update` activities on converted object types (#36322 by @ClearlyClaire) - Add support for dynamic viewport height (#36272 by @e1berd) - Add support for numeric-based URIs for new local accounts (#32724, #36304, #36316, and #36365 by @ClearlyClaire) - Add Traditional Mongolian to posting languages (#36196 by @shimon1024) @@ -28,7 +33,7 @@ All notable changes to this project will be documented in this file. - Add example of quote post with a preview card to development sample data (#35616 by @ClearlyClaire) - Add second set of blocked text that applies to accounts regardless of account age for spam-blocking (#35563 by @ClearlyClaire) - Added emoji from Twemoji v16 (#36501 and #36530 by @ChaosExAnima) -- Add experimental feature to select custom emoji rendering (#35229, #35282, #35253, #35424, #35473, #35483, #35505, #35568, #35605, #35659, #35664, #35739, #35985, #36051, #36071, #36137, #36165, #36248, #36262, #36275, #36293, #36341, #36342, #36366, #36377, #36378, #36385, #36393, #36397, #36403, #36413, #36410, #36454, #36402, #36503, #36502 and #36532 by @ChaosExAnima and @braddunbar)\ +- Add feature to select custom emoji rendering (#35229, #35282, #35253, #35424, #35473, #35483, #35505, #35568, #35605, #35659, #35664, #35739, #35985, #36051, #36071, #36137, #36165, #36248, #36262, #36275, #36293, #36341, #36342, #36366, #36377, #36378, #36385, #36393, #36397, #36403, #36413, #36410, #36454, #36402, #36503, #36502, #36532, #36603, #36409 and #36638 by @ChaosExAnima, @ClearlyClaire and @braddunbar)\ This also completely reworks the processing and rendering of emojis and server-rendered HTML in statuses and other places. ### Changed @@ -36,11 +41,17 @@ All notable changes to this project will be documented in this file. - Change confirmation dialogs for follow button actions “unfollow”, “unblock”, and “withdraw request” (#36289 by @diondiondion) - Change “Follow” button labels (#36264 by @diondiondion) - Change appearance settings to introduce new Advanced settings section (#36496 and #36506 by @diondiondion) +- Change display of blocked and muted quoted users (#36619 by @ClearlyClaire)\ + This adds `blocked_account`, `blocked_domain` and `muted_account` values to the `state` attribute of `Quote` and `ShallowQuote` REST API entities. - Change display of content warnings in Admin UI (#35935 by @ThisIsMissEm) +- Change styling of column banners (#36531 by @ClearlyClaire) +- Change recommended Node version to 24 (LTS) (#36539 by @renchap) +- Change min. characters required for logged-out account search from 5 to 3 (#36487 by @Gargron) +- Change browser target to Vite legacy plugin defaults (#36611 by @larouxn) - Change index on `follows` table to improve performance of some queries (#36374 by @ClearlyClaire) - Change links to accounts in settings and moderation views to link to local view unless account is suspended (#36340 by @diondiondion) - Change redirection for denied registration from web app to sign-in page with error message (#36384 by @ClearlyClaire) -- Change `timeline_preview` setting into four more granular settings (#36338, #36467 and #36497 by @ClearlyClaire) +- Change support for RFC9421 HTTP signatures to be enabled unconditionally (#36610 by @oneiros) - Change wording and design of interaction dialog to simplify it (#36124 by @diondiondion) - Change dropdown menus to allow disabled items to be focused (#36078 by @diondiondion) - Change modal background colours in light mode (#36069 by @diondiondion) @@ -48,7 +59,7 @@ All notable changes to this project will be documented in this file. - Change description of “Quiet public” (#36032 by @ClearlyClaire) - Change “Boost with original visibility” to “Share again with your followers” (#36035 by @ClearlyClaire) - Change handling of push subscriptions to automatically delete invalid ones on delivery (#35987 by @ThisIsMissEm) -- Change design of quote posts in web UI (#35584 and #35834 by @ClearlyClaire and @Gargron) +- Change design of quote posts in web UI (#35584 and #35834 by @Gargron) - Change auditable accounts to be sorted by username in admin action logs interface (#35272 by @breadtk) - Change order of translation restoration and service credit on post card (#33619 by @colindean) - Change position of ‘add more’ to be inside table toolbar on reports (#35963 by @ThisIsMissEm) @@ -59,6 +70,14 @@ All notable changes to this project will be documented in this file. - Fix relationship not being fetched to evaluate whether to show a quote post (#36517 by @ClearlyClaire) - Fix rendering of poll options in status history modal (#35633 by @ThisIsMissEm) - Fix “mute” button being displayed to unauthenticated visitors in hashtag dropdown (#36353 by @mkljczk) +- Fix URL comparison for mentions in case of empty path (#36613 and #36626 by @ClearlyClaire) +- Fix hashtags not being picked up when full-width hash sign is used (#36103 and #36625 by @ClearlyClaire and @Gargron) +- Fix layout of severed relationships when purged events are listed (#36593 by @mejofi) +- Fix vacuum tasks being interrupted by a single batch failure (#36606 by @Gargron) +- Fix handling of unreachable network error for search services (#36587 by @mjankowski) +- Fix bookmarks export when a bookmarked status is soft-deleted (#36576 by @ClearlyClaire) +- Fix text overflow alignment for long author names in News (#36562 by @diondiondion) +- Fix discovery preamble missing word in admin settings (#36560 by @belatedly) - Fix overflow handling of `.more-from-author` (#36310 by @edent) - Fix unfortunate action button wrapping in admin area (#36247 by @diondiondion) - Fix translate button width in Safari (#36164 and #36216 by @diondiondion) @@ -81,6 +100,10 @@ All notable changes to this project will be documented in this file. - Fix glitchy status keyboard navigation (#35455 and #35504 by @diondiondion) - Fix post being submitted when pressing “Enter” in the CW field (#35445 by @diondiondion) +### Removed + +- Remove support for PostgreSQL 13 (#36540 by @renchap) + ## [4.4.8] - 2025-10-21 ### Security diff --git a/Gemfile b/Gemfile index a12e716920071c..7d219344b65532 100644 --- a/Gemfile +++ b/Gemfile @@ -106,19 +106,19 @@ gem 'opentelemetry-api', '~> 1.7.0' group :opentelemetry do gem 'opentelemetry-exporter-otlp', '~> 0.31.0', require: false - gem 'opentelemetry-instrumentation-active_job', '~> 0.9.0', require: false - gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.23.0', require: false - gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.23.0', require: false - gem 'opentelemetry-instrumentation-excon', '~> 0.25.0', require: false - gem 'opentelemetry-instrumentation-faraday', '~> 0.29.0', require: false - gem 'opentelemetry-instrumentation-http', '~> 0.26.0', require: false - gem 'opentelemetry-instrumentation-http_client', '~> 0.25.0', require: false - gem 'opentelemetry-instrumentation-net_http', '~> 0.25.0', require: false - gem 'opentelemetry-instrumentation-pg', '~> 0.31.0', require: false - gem 'opentelemetry-instrumentation-rack', '~> 0.28.0', require: false - gem 'opentelemetry-instrumentation-rails', '~> 0.38.0', require: false - gem 'opentelemetry-instrumentation-redis', '~> 0.27.0', require: false - gem 'opentelemetry-instrumentation-sidekiq', '~> 0.27.0', require: false + gem 'opentelemetry-instrumentation-active_job', '~> 0.10.0', require: false + gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.24.0', require: false + gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.24.0', require: false + gem 'opentelemetry-instrumentation-excon', '~> 0.26.0', require: false + gem 'opentelemetry-instrumentation-faraday', '~> 0.30.0', require: false + gem 'opentelemetry-instrumentation-http', '~> 0.27.0', require: false + gem 'opentelemetry-instrumentation-http_client', '~> 0.26.0', require: false + gem 'opentelemetry-instrumentation-net_http', '~> 0.26.0', require: false + gem 'opentelemetry-instrumentation-pg', '~> 0.32.0', require: false + gem 'opentelemetry-instrumentation-rack', '~> 0.29.0', require: false + gem 'opentelemetry-instrumentation-rails', '~> 0.39.0', require: false + gem 'opentelemetry-instrumentation-redis', '~> 0.28.0', require: false + gem 'opentelemetry-instrumentation-sidekiq', '~> 0.28.0', require: false gem 'opentelemetry-sdk', '~> 1.4', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index f8d6f30550f3ae..2a0da53d4bf754 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -90,7 +90,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) aes_key_wrap (1.1.0) android_key_attestation (0.3.0) - annotaterb (4.19.0) + annotaterb (4.20.0) activerecord (>= 6.0.0) activesupport (>= 6.0.0) ast (2.4.3) @@ -116,7 +116,7 @@ GEM base64 (0.3.0) bcp47_spec (0.2.1) bcrypt (3.1.20) - benchmark (0.4.1) + benchmark (0.5.0) better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) @@ -168,7 +168,7 @@ GEM cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - crack (1.0.0) + crack (1.0.1) bigdecimal rexml crass (1.0.6) @@ -190,10 +190,10 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-two-factor (6.1.0) - activesupport (>= 7.0, < 8.1) + devise-two-factor (6.2.0) + activesupport (>= 7.0, < 8.2) devise (~> 4.0) - railties (>= 7.0, < 8.1) + railties (>= 7.0, < 8.2) rotp (~> 6.0) devise_pam_authenticatable2 (9.2.0) devise (>= 4.0.0) @@ -224,7 +224,7 @@ GEM mail (~> 2.7) email_validator (2.2.4) activemodel - erb (5.0.2) + erb (5.1.1) erubi (1.13.1) et-orbi (1.4.0) tzinfo @@ -426,7 +426,8 @@ GEM loofah (2.24.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop @@ -442,7 +443,7 @@ GEM mime-types-data (3.2025.0924) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (5.26.0) msgpack (1.8.0) multi_json (1.17.0) mutex_m (0.3.0) @@ -498,74 +499,74 @@ GEM tzinfo validate_url webfinger (~> 2.0) - openssl (3.3.1) + openssl (3.3.2) openssl-signature_algorithm (1.3.0) openssl (> 2.0) opentelemetry-api (1.7.0) opentelemetry-common (0.23.0) opentelemetry-api (~> 1.0) - opentelemetry-exporter-otlp (0.31.0) + opentelemetry-exporter-otlp (0.31.1) google-protobuf (>= 3.18) googleapis-common-protos-types (~> 1.3) opentelemetry-api (~> 1.1) opentelemetry-common (~> 0.20) - opentelemetry-sdk (~> 1.2) + opentelemetry-sdk (~> 1.10) opentelemetry-semantic_conventions opentelemetry-helpers-sql (0.2.0) opentelemetry-api (~> 1.7) - opentelemetry-helpers-sql-obfuscation (0.3.0) + opentelemetry-helpers-sql-obfuscation (0.4.0) opentelemetry-common (~> 0.21) - opentelemetry-instrumentation-action_mailer (0.5.0) - opentelemetry-instrumentation-active_support (~> 0.7) - opentelemetry-instrumentation-action_pack (0.14.1) - opentelemetry-instrumentation-rack (~> 0.21) - opentelemetry-instrumentation-action_view (0.10.0) - opentelemetry-instrumentation-active_support (~> 0.7) - opentelemetry-instrumentation-active_job (0.9.2) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-active_model_serializers (0.23.0) + opentelemetry-instrumentation-action_mailer (0.6.1) + opentelemetry-instrumentation-active_support (~> 0.10) + opentelemetry-instrumentation-action_pack (0.15.1) + opentelemetry-instrumentation-rack (~> 0.29) + opentelemetry-instrumentation-action_view (0.11.1) + opentelemetry-instrumentation-active_support (~> 0.10) + opentelemetry-instrumentation-active_job (0.10.1) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-active_model_serializers (0.24.0) opentelemetry-instrumentation-active_support (>= 0.7.0) - opentelemetry-instrumentation-active_record (0.10.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-active_storage (0.2.0) - opentelemetry-instrumentation-active_support (~> 0.7) - opentelemetry-instrumentation-active_support (0.9.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-base (0.24.0) + opentelemetry-instrumentation-active_record (0.11.1) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-active_storage (0.3.1) + opentelemetry-instrumentation-active_support (~> 0.10) + opentelemetry-instrumentation-active_support (0.10.1) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-base (0.25.0) opentelemetry-api (~> 1.7) opentelemetry-common (~> 0.21) opentelemetry-registry (~> 0.1) - opentelemetry-instrumentation-concurrent_ruby (0.23.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-excon (0.25.2) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-faraday (0.29.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-http (0.26.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-http_client (0.25.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-net_http (0.25.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-pg (0.31.1) + opentelemetry-instrumentation-concurrent_ruby (0.24.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-excon (0.26.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-faraday (0.30.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-http (0.27.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-http_client (0.26.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-net_http (0.26.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-pg (0.32.0) opentelemetry-helpers-sql opentelemetry-helpers-sql-obfuscation - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-rack (0.28.2) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-rails (0.38.0) - opentelemetry-instrumentation-action_mailer (~> 0.4) - opentelemetry-instrumentation-action_pack (~> 0.13) - opentelemetry-instrumentation-action_view (~> 0.9) - opentelemetry-instrumentation-active_job (~> 0.8) - opentelemetry-instrumentation-active_record (~> 0.9) - opentelemetry-instrumentation-active_storage (~> 0.1) - opentelemetry-instrumentation-active_support (~> 0.8) - opentelemetry-instrumentation-concurrent_ruby (~> 0.22) - opentelemetry-instrumentation-redis (0.27.1) - opentelemetry-instrumentation-base (~> 0.24) - opentelemetry-instrumentation-sidekiq (0.27.1) - opentelemetry-instrumentation-base (~> 0.24) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-rack (0.29.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-rails (0.39.1) + opentelemetry-instrumentation-action_mailer (~> 0.6) + opentelemetry-instrumentation-action_pack (~> 0.15) + opentelemetry-instrumentation-action_view (~> 0.11) + opentelemetry-instrumentation-active_job (~> 0.10) + opentelemetry-instrumentation-active_record (~> 0.11) + opentelemetry-instrumentation-active_storage (~> 0.3) + opentelemetry-instrumentation-active_support (~> 0.10) + opentelemetry-instrumentation-concurrent_ruby (~> 0.23) + opentelemetry-instrumentation-redis (0.28.0) + opentelemetry-instrumentation-base (~> 0.25) + opentelemetry-instrumentation-sidekiq (0.28.0) + opentelemetry-instrumentation-base (~> 0.25) opentelemetry-registry (0.4.0) opentelemetry-api (~> 1.1) opentelemetry-sdk (1.10.0) @@ -705,9 +706,9 @@ GEM io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rotp (6.3.0) rouge (4.6.1) @@ -744,7 +745,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 9) rspec-support (3.13.6) - rubocop (1.81.1) + rubocop (1.81.6) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -790,7 +791,7 @@ GEM ruby-vips (2.2.5) ffi (~> 1.12) logger - rubyzip (3.2.0) + rubyzip (3.2.1) rufus-scheduler (3.9.2) fugit (~> 1.1, >= 1.11.1) safety_net_attestation (0.5.0) @@ -821,9 +822,9 @@ GEM thor (>= 1.0, < 3.0) simple-navigation (4.4.0) activesupport (>= 2.3.2) - simple_form (5.3.1) - actionpack (>= 5.2) - activemodel (>= 5.2) + simple_form (5.4.0) + actionpack (>= 7.0) + activemodel (>= 7.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) @@ -834,7 +835,7 @@ GEM stackprof (0.2.27) starry (0.2.0) base64 - stoplight (5.3.8) + stoplight (5.4.0) zeitwerk stringio (3.1.7) strong_migrations (2.5.1) @@ -898,7 +899,7 @@ GEM zeitwerk (~> 2.2) warden (1.2.9) rack (>= 2.0.9) - webauthn (3.4.2) + webauthn (3.4.3) android_key_attestation (~> 0.3.0) bindata (~> 2.4) cbor (~> 0.5.9) @@ -910,7 +911,7 @@ GEM activesupport faraday (~> 2.0) faraday-follow_redirects - webmock (3.25.1) + webmock (3.26.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -1009,19 +1010,19 @@ DEPENDENCIES omniauth_openid_connect (~> 0.8.0) opentelemetry-api (~> 1.7.0) opentelemetry-exporter-otlp (~> 0.31.0) - opentelemetry-instrumentation-active_job (~> 0.9.0) - opentelemetry-instrumentation-active_model_serializers (~> 0.23.0) - opentelemetry-instrumentation-concurrent_ruby (~> 0.23.0) - opentelemetry-instrumentation-excon (~> 0.25.0) - opentelemetry-instrumentation-faraday (~> 0.29.0) - opentelemetry-instrumentation-http (~> 0.26.0) - opentelemetry-instrumentation-http_client (~> 0.25.0) - opentelemetry-instrumentation-net_http (~> 0.25.0) - opentelemetry-instrumentation-pg (~> 0.31.0) - opentelemetry-instrumentation-rack (~> 0.28.0) - opentelemetry-instrumentation-rails (~> 0.38.0) - opentelemetry-instrumentation-redis (~> 0.27.0) - opentelemetry-instrumentation-sidekiq (~> 0.27.0) + opentelemetry-instrumentation-active_job (~> 0.10.0) + opentelemetry-instrumentation-active_model_serializers (~> 0.24.0) + opentelemetry-instrumentation-concurrent_ruby (~> 0.24.0) + opentelemetry-instrumentation-excon (~> 0.26.0) + opentelemetry-instrumentation-faraday (~> 0.30.0) + opentelemetry-instrumentation-http (~> 0.27.0) + opentelemetry-instrumentation-http_client (~> 0.26.0) + opentelemetry-instrumentation-net_http (~> 0.26.0) + opentelemetry-instrumentation-pg (~> 0.32.0) + opentelemetry-instrumentation-rack (~> 0.29.0) + opentelemetry-instrumentation-rails (~> 0.39.0) + opentelemetry-instrumentation-redis (~> 0.28.0) + opentelemetry-instrumentation-sidekiq (~> 0.28.0) opentelemetry-sdk (~> 1.4) ox (~> 2.14) parslet diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index daed42c404ecf8..76093ad7377c29 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -140,10 +140,11 @@ def destroy @status = Status.where(account: current_account).find(params[:id]) authorize @status, :destroy? + json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true + @status.discard_with_reblogs StatusPin.find_by(status: @status)&.destroy @status.account.statuses_count = @status.account.statuses_count - 1 - json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) }) @@ -171,7 +172,7 @@ def set_thread end def set_quoted_status - @quoted_status = Status.find(status_params[:quoted_status_id]) if status_params[:quoted_status_id].present? + @quoted_status = Status.find(status_params[:quoted_status_id])&.proper if status_params[:quoted_status_id].present? authorize(@quoted_status, :quote?) if @quoted_status.present? rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError # TODO: distinguish between non-existing and non-quotable posts diff --git a/app/javascript/entrypoints/public.tsx b/app/javascript/entrypoints/public.tsx index fea3eb0d792e48..dd1956446daeeb 100644 --- a/app/javascript/entrypoints/public.tsx +++ b/app/javascript/entrypoints/public.tsx @@ -70,7 +70,7 @@ function loaded() { }; document.querySelectorAll('.emojify').forEach((content) => { - content.innerHTML = emojify(content.innerHTML, {}, true); // Force emojify as public doesn't load the new emoji system. + content.innerHTML = emojify(content.innerHTML); }); document diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 7b66e61304aafa..68271682b1289c 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -649,6 +649,7 @@ export function fetchComposeSuggestions(token) { fetchComposeSuggestionsEmojis(dispatch, getState, token); break; case '#': + case '#': fetchComposeSuggestionsTags(dispatch, getState, token); break; default: @@ -690,11 +691,11 @@ export function selectComposeSuggestion(position, token, suggestion, path) { dispatch(useEmoji(suggestion)); } else if (suggestion.type === 'hashtag') { - completion = `#${suggestion.name}`; - startPosition = position - 1; + completion = suggestion.name.slice(token.length - 1); + startPosition = position + token.length; } else if (suggestion.type === 'account') { - completion = getState().getIn(['accounts', suggestion.id, 'acct']); - startPosition = position; + completion = `@${getState().getIn(['accounts', suggestion.id, 'acct'])}`; + startPosition = position - 1; } // We don't want to replace hashtags that vary only in case due to accessibility, but we need to fire off an event so that @@ -754,7 +755,7 @@ function insertIntoTagHistory(recognizedTags, text) { // complicated because of new normalization rules, it's no longer just // a case sensitivity issue const names = recognizedTags.map(tag => { - const matches = text.match(new RegExp(`#${tag.name}`, 'i')); + const matches = text.match(new RegExp(`[##]${tag.name}`, 'i')); if (matches && matches.length > 0) { return matches[0].slice(1); diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index cdb91dc8a8e6a6..64999917445dea 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -1,8 +1,5 @@ import escapeTextContentForBrowser from 'escape-html'; -import { makeEmojiMap } from 'mastodon/models/custom_emoji'; - -import emojify from '../../features/emoji/emoji'; import { expandSpoilers, me } from '../../initial_state'; const domParser = new DOMParser(); @@ -104,11 +101,10 @@ export function normalizeStatus(status, normalOldStatus, options = undefined) { const spoilerText = normalStatus.spoiler_text || ''; const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); - const emojiMap = makeEmojiMap(normalStatus.emojis); normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; - normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); - normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap); + normalStatus.contentHtml = normalStatus.content; + normalStatus.spoilerHtml = escapeTextContentForBrowser(spoilerText); normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive; // Remove quote fallback link from the DOM so it doesn't mess with paragraph margins @@ -155,14 +151,12 @@ export function normalizeEmojiReactions(emoji_reactions) { } export function normalizeStatusTranslation(translation, status) { - const emojiMap = makeEmojiMap(status.get('emojis').toJS()); - const normalTranslation = { detected_source_language: translation.detected_source_language, language: translation.language, provider: translation.provider, - contentHtml: emojify(translation.content, emojiMap), - spoilerHtml: emojify(escapeTextContentForBrowser(translation.spoiler_text), emojiMap), + contentHtml: translation.content, + spoilerHtml: escapeTextContentForBrowser(translation.spoiler_text), spoiler_text: translation.spoiler_text, }; @@ -176,9 +170,8 @@ export function normalizeStatusTranslation(translation, status) { export function normalizeAnnouncement(announcement) { const normalAnnouncement = { ...announcement }; - const emojiMap = makeEmojiMap(normalAnnouncement.emojis); - normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap); + normalAnnouncement.contentHtml = normalAnnouncement.content; return normalAnnouncement; } diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index f9d784c2b47126..447bd630faa348 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -33,13 +33,20 @@ import { const randomUpTo = max => Math.floor(Math.random() * Math.floor(max)); +/** + * @typedef {import('mastodon/store').AppDispatch} Dispatch + * @typedef {import('mastodon/store').GetState} GetState + * @typedef {import('redux').UnknownAction} UnknownAction + * @typedef {function(Dispatch, GetState): Promise} FallbackFunction + */ + /** * @param {string} timelineId * @param {string} channelName * @param {Object.} params * @param {Object} options - * @param {function(Function, Function): Promise} [options.fallback] - * @param {function(): void} [options.fillGaps] + * @param {FallbackFunction} [options.fallback] + * @param {function(): UnknownAction} [options.fillGaps] * @param {function(object): boolean} [options.accept] * @returns {function(): void} */ @@ -47,13 +54,14 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti const { messages } = getLocale(); return connectStream(channelName, params, (dispatch, getState) => { + // @ts-ignore const locale = getState().getIn(['meta', 'locale']); // @ts-expect-error let pollingId; /** - * @param {function(Function, Function): Promise} fallback + * @param {FallbackFunction} fallback */ const useFallback = async fallback => { @@ -137,7 +145,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti }; /** - * @param {Function} dispatch + * @param {Dispatch} dispatch */ async function refreshHomeTimelineAndNotification(dispatch) { await dispatch(expandHomeTimeline({ maxId: undefined })); @@ -156,7 +164,11 @@ async function refreshHomeTimelineAndNotification(dispatch) { * @returns {function(): void} */ export const connectUserStream = () => - connectTimelineStream('home', 'user', {}, { fallback: refreshHomeTimelineAndNotification, fillGaps: fillHomeTimelineGaps }); + connectTimelineStream('home', 'user', {}, { + fallback: refreshHomeTimelineAndNotification, + // @ts-expect-error + fillGaps: fillHomeTimelineGaps + }); /** * @param {Object} options @@ -164,7 +176,10 @@ export const connectUserStream = () => * @returns {function(): void} */ export const connectCommunityStream = ({ onlyMedia } = {}) => - connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`, {}, { fillGaps: () => (fillCommunityTimelineGaps({ onlyMedia })) }); + connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`, {}, { + // @ts-expect-error + fillGaps: () => (fillCommunityTimelineGaps({ onlyMedia })) + }); /** * @param {Object} options @@ -173,7 +188,10 @@ export const connectCommunityStream = ({ onlyMedia } = {}) => * @returns {function(): void} */ export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => - connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, {}, { fillGaps: () => fillPublicTimelineGaps({ onlyMedia, onlyRemote }) }); + connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, {}, { + // @ts-expect-error + fillGaps: () => fillPublicTimelineGaps({ onlyMedia, onlyRemote }) + }); /** * @param {string} columnId @@ -196,11 +214,17 @@ export const connectDirectStream = () => * @returns {function(): void} */ export const connectListStream = listId => - connectTimelineStream(`list:${listId}`, 'list', { list: listId }, { fillGaps: () => fillListTimelineGaps(listId) }); + connectTimelineStream(`list:${listId}`, 'list', { list: listId }, { + // @ts-expect-error + fillGaps: () => fillListTimelineGaps(listId) + }); /** * @param {string} antennaId * @returns {function(): void} */ export const connectAntennaStream = antennaId => - connectTimelineStream(`antenna:${antennaId}`, 'antenna', { antenna: antennaId }, { fillGaps: () => fillAntennaTimelineGaps(antennaId) }); + connectTimelineStream(`antenna:${antennaId}`, 'antenna', { antenna: antennaId }, { + // @ts-expect-error + fillGaps: () => fillAntennaTimelineGaps(antennaId) + }); diff --git a/app/javascript/mastodon/components/account_bio.tsx b/app/javascript/mastodon/components/account_bio.tsx index e87ae654fdf585..6d4ab1ddd49b2c 100644 --- a/app/javascript/mastodon/components/account_bio.tsx +++ b/app/javascript/mastodon/components/account_bio.tsx @@ -1,11 +1,6 @@ -import { useCallback } from 'react'; - import classNames from 'classnames'; -import { useLinks } from 'mastodon/hooks/useLinks'; - import { useAppSelector } from '../store'; -import { isModernEmojiEnabled } from '../utils/environment'; import { EmojiHTML } from './emoji/html'; import { useElementHandledLink } from './status/handled_link'; @@ -21,22 +16,6 @@ export const AccountBio: React.FC = ({ accountId, showDropdown = false, }) => { - const handleClick = useLinks(showDropdown); - const handleNodeChange = useCallback( - (node: HTMLDivElement | null) => { - if ( - !showDropdown || - !node || - node.childNodes.length === 0 || - isModernEmojiEnabled() - ) { - return; - } - addDropdownToHashtags(node, accountId); - }, - [showDropdown, accountId], - ); - const htmlHandlers = useElementHandledLink({ hashtagAccountId: showDropdown ? accountId : undefined, }); @@ -62,30 +41,7 @@ export const AccountBio: React.FC = ({ htmlString={note} extraEmojis={extraEmojis} className={classNames(className, 'translate')} - onClickCapture={handleClick} - ref={handleNodeChange} {...htmlHandlers} /> ); }; - -function addDropdownToHashtags(node: HTMLElement | null, accountId: string) { - if (!node) { - return; - } - for (const childNode of node.childNodes) { - if (!(childNode instanceof HTMLElement)) { - continue; - } - if ( - childNode instanceof HTMLAnchorElement && - (childNode.classList.contains('hashtag') || - childNode.innerText.startsWith('#')) && - !childNode.dataset.menuHashtag - ) { - childNode.dataset.menuHashtag = accountId; - } else if (childNode.childNodes.length > 0) { - addDropdownToHashtags(childNode, accountId); - } - } -} diff --git a/app/javascript/mastodon/components/autosuggest_input.jsx b/app/javascript/mastodon/components/autosuggest_input.jsx index f707a18e1d697f..267c0442158323 100644 --- a/app/javascript/mastodon/components/autosuggest_input.jsx +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -61,7 +61,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { static defaultProps = { autoFocus: true, - searchTokens: ['@', ':', '#'], + searchTokens: ['@', '@', ':', '#', '#'], }; state = { diff --git a/app/javascript/mastodon/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.jsx index 68cf9e17fc92f5..137bad9b7e665f 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.jsx +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -25,7 +25,7 @@ const textAtCursorMatchesToken = (str, caretPosition) => { word = str.slice(left, right + caretPosition); } - if (!word || word.trim().length < 3 || ['@', ':', '#'].indexOf(word[0]) === -1) { + if (!word || word.trim().length < 3 || ['@', '@', ':', '#', '#'].indexOf(word[0]) === -1) { return [null, null]; } diff --git a/app/javascript/mastodon/components/carousel/carousel.stories.tsx b/app/javascript/mastodon/components/carousel/carousel.stories.tsx new file mode 100644 index 00000000000000..5117bc08e3530d --- /dev/null +++ b/app/javascript/mastodon/components/carousel/carousel.stories.tsx @@ -0,0 +1,126 @@ +import type { FC } from 'react'; + +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { fn, userEvent, expect } from 'storybook/test'; + +import type { CarouselProps } from './index'; +import { Carousel } from './index'; + +interface TestSlideProps { + id: number; + text: string; + color: string; +} + +const TestSlide: FC = ({ + active, + text, + color, +}) => ( +

+ {text} +
+); + +const slides: TestSlideProps[] = [ + { + id: 1, + text: 'first', + color: 'red', + }, + { + id: 2, + text: 'second', + color: 'pink', + }, + { + id: 3, + text: 'third', + color: 'orange', + }, +]; + +type StoryProps = Pick< + CarouselProps, + 'items' | 'renderItem' | 'emptyFallback' | 'onChangeSlide' +>; + +const meta = { + title: 'Components/Carousel', + args: { + items: slides, + renderItem(item, active) { + return ; + }, + onChangeSlide: fn(), + emptyFallback: 'No slides available', + }, + render(args) { + return ( + <> + + + + ); + }, + argTypes: { + emptyFallback: { + type: 'string', + }, + }, + tags: ['test'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + async play({ args, canvas }) { + const nextButton = await canvas.findByRole('button', { name: /next/i }); + const slides = await canvas.findAllByRole('group'); + await expect(slides).toHaveLength(slides.length); + + await userEvent.click(nextButton); + await expect(args.onChangeSlide).toHaveBeenCalledWith(1, slides[1]); + + await userEvent.click(nextButton); + await expect(args.onChangeSlide).toHaveBeenCalledWith(2, slides[2]); + + // Wrap around + await userEvent.click(nextButton); + await expect(args.onChangeSlide).toHaveBeenCalledWith(0, slides[0]); + }, +}; + +export const DifferentHeights: Story = { + args: { + items: slides.map((props, index) => ({ + ...props, + styles: { height: 100 + index * 100 }, + })), + }, +}; + +export const NoSlides: Story = { + args: { + items: [], + }, +}; diff --git a/app/javascript/mastodon/components/carousel/index.tsx b/app/javascript/mastodon/components/carousel/index.tsx new file mode 100644 index 00000000000000..f2b9e9823b2ffa --- /dev/null +++ b/app/javascript/mastodon/components/carousel/index.tsx @@ -0,0 +1,244 @@ +import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import type { + ComponentPropsWithoutRef, + ComponentType, + ReactElement, + ReactNode, +} from 'react'; + +import type { MessageDescriptor } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; + +import classNames from 'classnames'; + +import { usePrevious } from '@dnd-kit/utilities'; +import { animated, useSpring } from '@react-spring/web'; +import { useDrag } from '@use-gesture/react'; + +import type { CarouselPaginationProps } from './pagination'; +import { CarouselPagination } from './pagination'; + +import './styles.scss'; + +const defaultMessages = defineMessages({ + previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, + next: { id: 'lightbox.next', defaultMessage: 'Next' }, + current: { + id: 'carousel.current', + defaultMessage: 'Slide {current, number} / {max, number}', + }, + slide: { + id: 'carousel.slide', + defaultMessage: 'Slide {current, number} of {max, number}', + }, +}); + +export type MessageKeys = keyof typeof defaultMessages; + +export interface CarouselSlideProps { + id: string | number; +} + +export type RenderSlideFn< + SlideProps extends CarouselSlideProps = CarouselSlideProps, +> = (item: SlideProps, active: boolean, index: number) => ReactElement; + +export interface CarouselProps< + SlideProps extends CarouselSlideProps = CarouselSlideProps, +> { + items: SlideProps[]; + renderItem: RenderSlideFn; + onChangeSlide?: (index: number, ref: Element) => void; + paginationComponent?: ComponentType | null; + paginationProps?: Partial; + messages?: Record; + emptyFallback?: ReactNode; + classNamePrefix?: string; + slideClassName?: string; +} + +export const Carousel = < + SlideProps extends CarouselSlideProps = CarouselSlideProps, +>({ + items, + renderItem, + onChangeSlide, + paginationComponent: Pagination = CarouselPagination, + paginationProps = {}, + messages = defaultMessages, + children, + emptyFallback = null, + className, + classNamePrefix = 'carousel', + slideClassName, + ...wrapperProps +}: CarouselProps & ComponentPropsWithoutRef<'div'>) => { + // Handle slide change + const [slideIndex, setSlideIndex] = useState(0); + const wrapperRef = useRef(null); + const handleSlideChange = useCallback( + (direction: number) => { + setSlideIndex((prev) => { + const max = items.length - 1; + let newIndex = prev + direction; + if (newIndex < 0) { + newIndex = max; + } else if (newIndex > max) { + newIndex = 0; + } + + const slide = wrapperRef.current?.children[newIndex]; + if (slide) { + setCurrentSlideHeight(slide.scrollHeight); + onChangeSlide?.(newIndex, slide); + if (slide instanceof HTMLElement) { + slide.focus({ preventScroll: true }); + } + } + + return newIndex; + }); + }, + [items.length, onChangeSlide], + ); + + // Handle slide heights + const [currentSlideHeight, setCurrentSlideHeight] = useState( + wrapperRef.current?.scrollHeight ?? 0, + ); + const previousSlideHeight = usePrevious(currentSlideHeight); + const observerRef = useRef( + new ResizeObserver(() => { + handleSlideChange(0); + }), + ); + const wrapperStyles = useSpring({ + x: `-${slideIndex * 100}%`, + height: currentSlideHeight, + // Don't animate from zero to the height of the initial slide + immediate: !previousSlideHeight, + }); + useLayoutEffect(() => { + // Update slide height when the component mounts + if (currentSlideHeight === 0) { + handleSlideChange(0); + } + }, [currentSlideHeight, handleSlideChange]); + + // Handle swiping animations + const bind = useDrag( + ({ swipe: [swipeX] }) => { + handleSlideChange(swipeX * -1); // Invert swipe as swiping left loads the next slide. + }, + { pointer: { capture: false } }, + ); + const handlePrev = useCallback(() => { + handleSlideChange(-1); + }, [handleSlideChange]); + const handleNext = useCallback(() => { + handleSlideChange(1); + }, [handleSlideChange]); + + const intl = useIntl(); + + if (items.length === 0) { + return emptyFallback; + } + + return ( +
+
+ {children} + {Pagination && items.length > 1 && ( + + )} +
+ + + {items.map((itemsProps, index) => ( + + item={itemsProps} + renderItem={renderItem} + observer={observerRef.current} + index={index} + key={`slide-${itemsProps.id}`} + className={classNames(`${classNamePrefix}__slide`, slideClassName, { + active: index === slideIndex, + })} + active={index === slideIndex} + label={intl.formatMessage(messages.slide, { + current: index + 1, + max: items.length, + })} + /> + ))} + +
+ ); +}; + +type CarouselSlideWrapperProps = { + observer: ResizeObserver; + className: string; + active: boolean; + item: SlideProps; + index: number; + label: string; +} & Pick, 'renderItem'>; + +const CarouselSlideWrapper = ({ + observer, + className, + active, + renderItem, + item, + index, + label, +}: CarouselSlideWrapperProps) => { + const handleRef = useCallback( + (instance: HTMLDivElement | null) => { + if (instance) { + observer.observe(instance); + } + }, + [observer], + ); + + const children = useMemo( + () => renderItem(item, active, index), + [renderItem, item, active, index], + ); + + return ( +
+ {children} +
+ ); +}; diff --git a/app/javascript/mastodon/components/carousel/pagination.tsx b/app/javascript/mastodon/components/carousel/pagination.tsx new file mode 100644 index 00000000000000..a2666f486fe2ab --- /dev/null +++ b/app/javascript/mastodon/components/carousel/pagination.tsx @@ -0,0 +1,54 @@ +import type { FC, MouseEventHandler } from 'react'; + +import type { MessageDescriptor } from 'react-intl'; +import { useIntl } from 'react-intl'; + +import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; +import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; + +import { IconButton } from '../icon_button'; + +import type { MessageKeys } from './index'; + +export interface CarouselPaginationProps { + onNext: MouseEventHandler; + onPrev: MouseEventHandler; + current: number; + max: number; + className?: string; + messages: Record; +} + +export const CarouselPagination: FC = ({ + onNext, + onPrev, + current, + max, + className = '', + messages, +}) => { + const intl = useIntl(); + return ( +
+ + + {intl.formatMessage(messages.current, { + current: current + 1, + max, + sr: (chunk) => {chunk}, + })} + + +
+ ); +}; diff --git a/app/javascript/mastodon/components/carousel/styles.scss b/app/javascript/mastodon/components/carousel/styles.scss new file mode 100644 index 00000000000000..bcd0bc7d3af76b --- /dev/null +++ b/app/javascript/mastodon/components/carousel/styles.scss @@ -0,0 +1,28 @@ +.carousel { + gap: 16px; + overflow: hidden; + touch-action: pan-y; + + &__header { + padding: 8px 16px; + } + + &__pagination { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + } + + &__slides { + display: flex; + flex-wrap: nowrap; + align-items: start; + } + + &__slide { + flex: 0 0 100%; + width: 100%; + overflow: hidden; + } +} diff --git a/app/javascript/mastodon/components/display_name/display_name.stories.tsx b/app/javascript/mastodon/components/display_name/display_name.stories.tsx index d546fdd135ea82..6f1819a55744d4 100644 --- a/app/javascript/mastodon/components/display_name/display_name.stories.tsx +++ b/app/javascript/mastodon/components/display_name/display_name.stories.tsx @@ -74,6 +74,6 @@ export const Linked: Story = { acct: username, }) : undefined; - return ; + return ; }, }; diff --git a/app/javascript/mastodon/components/display_name/no-domain.tsx b/app/javascript/mastodon/components/display_name/no-domain.tsx index ee6e84050c3b89..530e0a08e0c077 100644 --- a/app/javascript/mastodon/components/display_name/no-domain.tsx +++ b/app/javascript/mastodon/components/display_name/no-domain.tsx @@ -9,9 +9,8 @@ import { Skeleton } from '../skeleton'; import type { DisplayNameProps } from './index'; export const DisplayNameWithoutDomain: FC< - Omit & - ComponentPropsWithoutRef<'span'> -> = ({ account, className, children, ...props }) => { + Omit & ComponentPropsWithoutRef<'span'> +> = ({ account, className, children, localDomain: _, ...props }) => { return ( & - ComponentPropsWithoutRef<'span'> -> = ({ account, ...props }) => { + Omit & ComponentPropsWithoutRef<'span'> +> = ({ account, localDomain: _, ...props }) => { if (!account) { return null; } diff --git a/app/javascript/mastodon/components/dropdown/index.tsx b/app/javascript/mastodon/components/dropdown/index.tsx index b6a04b9027f429..a571950fc34399 100644 --- a/app/javascript/mastodon/components/dropdown/index.tsx +++ b/app/javascript/mastodon/components/dropdown/index.tsx @@ -7,6 +7,7 @@ import type { MessageDescriptor } from 'react-intl'; import classNames from 'classnames'; import Overlay from 'react-overlays/Overlay'; +import type { Placement } from 'react-overlays/esm/usePopper'; import UnfoldMoreIcon from '@/material-icons/400-24px/unfold_more.svg?react'; @@ -25,6 +26,8 @@ interface DropdownProps { descriptionId?: string; emptyText?: MessageDescriptor; classPrefix: string; + placement?: Placement; + target?: React.RefObject; } export const Dropdown: FC< @@ -39,6 +42,8 @@ export const Dropdown: FC< classPrefix, className, id, + placement, + target, ...buttonProps }) => { const intl = useIntl(); @@ -106,10 +111,10 @@ export const Dropdown: FC< + {children} ); @@ -78,7 +72,7 @@ export const AnimateEmojiProvider = polymorphicForwardRef< return ( ( +export const EmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>( ( { extraEmojis, @@ -59,32 +56,4 @@ export const ModernEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>( ); }, ); -ModernEmojiHTML.displayName = 'ModernEmojiHTML'; - -export const LegacyEmojiHTML = polymorphicForwardRef<'div', EmojiHTMLProps>( - (props, ref) => { - const { - as: asElement, - htmlString, - extraEmojis, - className, - onElement, - onAttribute, - ...rest - } = props; - const Wrapper = asElement ?? 'div'; - return ( - - ); - }, -); -LegacyEmojiHTML.displayName = 'LegacyEmojiHTML'; - -export const EmojiHTML = isModernEmojiEnabled() - ? ModernEmojiHTML - : LegacyEmojiHTML; +EmojiHTML.displayName = 'EmojiHTML'; diff --git a/app/javascript/mastodon/components/emoji/index.tsx b/app/javascript/mastodon/components/emoji/index.tsx index 0dff8314ffc5ff..ac1a08f956fe10 100644 --- a/app/javascript/mastodon/components/emoji/index.tsx +++ b/app/javascript/mastodon/components/emoji/index.tsx @@ -61,11 +61,16 @@ export const Emoji: FC = ({ if (state.type === EMOJI_TYPE_CUSTOM) { const shortcode = `:${state.code}:`; + const style = + state.data.width && state.data.height + ? { aspectRatio: `${state.data.width} / ${state.data.height}` } + : {}; return ( {shortcode} diff --git a/app/javascript/mastodon/components/featured_carousel.tsx b/app/javascript/mastodon/components/featured_carousel.tsx index df64c43b421836..c35f2f37f06354 100644 --- a/app/javascript/mastodon/components/featured_carousel.tsx +++ b/app/javascript/mastodon/components/featured_carousel.tsx @@ -1,38 +1,43 @@ -import type { ComponentPropsWithRef } from 'react'; -import { - useCallback, - useEffect, - useLayoutEffect, - useRef, - useState, - useId, -} from 'react'; +import { useCallback, useEffect, useId } from 'react'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { defineMessages, FormattedMessage } from 'react-intl'; import type { Map as ImmutableMap } from 'immutable'; import { List as ImmutableList } from 'immutable'; -import type { AnimatedProps } from '@react-spring/web'; -import { animated, useSpring } from '@react-spring/web'; -import { useDrag } from '@use-gesture/react'; - import { expandAccountFeaturedTimeline } from '@/mastodon/actions/timelines'; import { Icon } from '@/mastodon/components/icon'; -import { IconButton } from '@/mastodon/components/icon_button'; import { StatusQuoteManager } from '@/mastodon/components/status_quoted'; -import { usePrevious } from '@/mastodon/hooks/usePrevious'; -import { useAppDispatch, useAppSelector } from '@/mastodon/store'; -import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; -import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; +import { + createAppSelector, + useAppDispatch, + useAppSelector, +} from '@/mastodon/store'; import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react'; +import { Carousel } from './carousel'; + +const pinnedStatusesSelector = createAppSelector( + [ + (state, accountId: string, tagged?: string) => + (state.timelines as ImmutableMap).getIn( + [`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'], + ImmutableList(), + ) as ImmutableList, + ], + (items) => items.toArray().map((id) => ({ id })), +); + const messages = defineMessages({ - previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' }, - next: { id: 'featured_carousel.next', defaultMessage: 'Next' }, + previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, + next: { id: 'lightbox.next', defaultMessage: 'Next' }, + current: { + id: 'featured_carousel.current', + defaultMessage: 'Post {current, number} / {max, number}', + }, slide: { id: 'featured_carousel.slide', - defaultMessage: '{index} of {total}', + defaultMessage: 'Post {current, number} of {max, number}', }, }); @@ -40,7 +45,6 @@ export const FeaturedCarousel: React.FC<{ accountId: string; tagged?: string; }> = ({ accountId, tagged }) => { - const intl = useIntl(); const accessibilityId = useId(); // Load pinned statuses @@ -50,175 +54,37 @@ export const FeaturedCarousel: React.FC<{ void dispatch(expandAccountFeaturedTimeline(accountId, { tagged })); } }, [accountId, dispatch, tagged]); - const pinnedStatuses = useAppSelector( - (state) => - (state.timelines as ImmutableMap).getIn( - [`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'], - ImmutableList(), - ) as ImmutableList, + const pinnedStatuses = useAppSelector((state) => + pinnedStatusesSelector(state, accountId, tagged), ); - // Handle slide change - const [slideIndex, setSlideIndex] = useState(0); - const wrapperRef = useRef(null); - const handleSlideChange = useCallback( - (direction: number) => { - setSlideIndex((prev) => { - const max = pinnedStatuses.size - 1; - let newIndex = prev + direction; - if (newIndex < 0) { - newIndex = max; - } else if (newIndex > max) { - newIndex = 0; - } - const slide = wrapperRef.current?.children[newIndex]; - if (slide) { - setCurrentSlideHeight(slide.scrollHeight); - } - return newIndex; - }); - }, - [pinnedStatuses.size], + const renderSlide = useCallback( + ({ id }: { id: string }) => ( + + ), + [], ); - // Handle slide heights - const [currentSlideHeight, setCurrentSlideHeight] = useState( - wrapperRef.current?.scrollHeight ?? 0, - ); - const previousSlideHeight = usePrevious(currentSlideHeight); - const observerRef = useRef( - new ResizeObserver(() => { - handleSlideChange(0); - }), - ); - const wrapperStyles = useSpring({ - x: `-${slideIndex * 100}%`, - height: currentSlideHeight, - // Don't animate from zero to the height of the initial slide - immediate: !previousSlideHeight, - }); - useLayoutEffect(() => { - // Update slide height when the component mounts - if (currentSlideHeight === 0) { - handleSlideChange(0); - } - }, [currentSlideHeight, handleSlideChange]); - - // Handle swiping animations - const bind = useDrag(({ swipe: [swipeX] }) => { - handleSlideChange(swipeX * -1); // Invert swipe as swiping left loads the next slide. - }); - const handlePrev = useCallback(() => { - handleSlideChange(-1); - }, [handleSlideChange]); - const handleNext = useCallback(() => { - handleSlideChange(1); - }, [handleSlideChange]); - - if (!accountId || pinnedStatuses.isEmpty()) { + if (!accountId || pinnedStatuses.length === 0) { return null; } return ( -
-
-

- - -

- {pinnedStatuses.size > 1 && ( - <> - - - - {(text) => {text}} - - {slideIndex + 1} / {pinnedStatuses.size} - - - - )} -
- - {pinnedStatuses.map((statusId, index) => ( - - ))} - -
- ); -}; - -interface FeaturedCarouselItemProps { - statusId: string; - active: boolean; - observer: ResizeObserver; -} - -const FeaturedCarouselItem: React.FC< - FeaturedCarouselItemProps & AnimatedProps> -> = ({ statusId, active, observer, ...props }) => { - const handleRef = useCallback( - (instance: HTMLDivElement | null) => { - if (instance) { - observer.observe(instance); - } - }, - [observer], - ); - - return ( - - - +

+ + +

+
); }; diff --git a/app/javascript/mastodon/components/hover_card_account.tsx b/app/javascript/mastodon/components/hover_card_account.tsx index 4c0c5de625aca1..d659b382cd3241 100644 --- a/app/javascript/mastodon/components/hover_card_account.tsx +++ b/app/javascript/mastodon/components/hover_card_account.tsx @@ -23,8 +23,6 @@ import { domain, isHideItem } from 'mastodon/initial_state'; import { getAccountHidden } from 'mastodon/selectors/accounts'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; -import { useLinks } from '../hooks/useLinks'; - export const HoverCardAccount = forwardRef< HTMLDivElement, { accountId?: string } @@ -68,8 +66,6 @@ export const HoverCardAccount = forwardRef< !isMutual && !isFollower; - const handleClick = useLinks(); - return (
-
+
onParentElement?.(...args) ?? onLinkElement(...args), [onLinkElement, onParentElement], ); - return ; + return ; }, ); diff --git a/app/javascript/mastodon/components/poll.tsx b/app/javascript/mastodon/components/poll.tsx index a9229e6ee4907a..98954fc2d95c0b 100644 --- a/app/javascript/mastodon/components/poll.tsx +++ b/app/javascript/mastodon/components/poll.tsx @@ -13,9 +13,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react'; import { openModal } from 'mastodon/actions/modal'; import { fetchPoll, vote } from 'mastodon/actions/polls'; import { Icon } from 'mastodon/components/icon'; -import emojify from 'mastodon/features/emoji/emoji'; import { useIdentity } from 'mastodon/identity_context'; -import { makeEmojiMap } from 'mastodon/models/custom_emoji'; import type * as Model from 'mastodon/models/poll'; import type { Status } from 'mastodon/models/status'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; @@ -235,12 +233,11 @@ const PollOption: React.FC = (props) => { let titleHtml = option.translation?.titleHtml ?? option.titleHtml; if (!titleHtml) { - const emojiMap = makeEmojiMap(poll.emojis); - titleHtml = emojify(escapeTextContentForBrowser(title), emojiMap); + titleHtml = escapeTextContentForBrowser(title); } return titleHtml; - }, [option, poll, title]); + }, [option, title]); // Handlers const handleOptionChange = useCallback(() => { diff --git a/app/javascript/mastodon/components/status/handled_link.tsx b/app/javascript/mastodon/components/status/handled_link.tsx index 3c8973992bdd1c..be816e985319ea 100644 --- a/app/javascript/mastodon/components/status/handled_link.tsx +++ b/app/javascript/mastodon/components/status/handled_link.tsx @@ -26,7 +26,12 @@ export const HandledLink: FC> = ({ ...props }) => { // Handle hashtags - if (text.startsWith('#') || prevText?.endsWith('#')) { + if ( + text.startsWith('#') || + prevText?.endsWith('#') || + text.startsWith('#') || + prevText?.endsWith('#') + ) { const hashtag = text.slice(1).trim(); return ( > = ({ {children} ); - } else if ((text.startsWith('@') || prevText?.endsWith('@')) && mention) { + } else if (mention) { // Handle mentions return ( ({ languages: state.getIn(['server', 'translationLanguages', 'items']), }); +const compareUrls = (href1, href2) => { + try { + const url1 = new URL(href1); + const url2 = new URL(href2); + + return url1.origin === url2.origin && url1.pathname === url2.pathname && url1.search === url2.search; + } catch { + return false; + } +}; + class StatusContent extends PureComponent { static propTypes = { identity: identityContextPropShape, @@ -108,41 +117,6 @@ class StatusContent extends PureComponent { onCollapsedToggle(collapsed); } - - // Exit if modern emoji is enabled, as it handles links using the HandledLink component. - if (isModernEmojiEnabled()) { - return; - } - - const links = node.querySelectorAll('a'); - - let link, mention; - - for (var i = 0; i < links.length; ++i) { - link = links[i]; - - if (link.classList.contains('status-link')) { - continue; - } - - link.classList.add('status-link'); - - mention = this.props.status.get('mentions').find(item => link.href === item.get('url')); - - if (mention) { - link.addEventListener('click', this.onMentionClick.bind(this, mention), false); - link.setAttribute('title', `@${mention.get('acct')}`); - link.setAttribute('href', `/@${mention.get('acct')}`); - link.setAttribute('data-hover-card-account', mention.get('id')); - } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) { - link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false); - link.setAttribute('href', `/tags/${link.text.replace(/^#/, '')}`); - link.setAttribute('data-menu-hashtag', this.props.status.getIn(['account', 'id'])); - } else { - link.setAttribute('title', link.href); - link.classList.add('unhandled-link'); - } - } } componentDidMount () { @@ -153,22 +127,6 @@ class StatusContent extends PureComponent { this._updateStatusLinks(); } - onMentionClick = (mention, e) => { - if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(`/@${mention.get('acct')}`); - } - }; - - onHashtagClick = (hashtag, e) => { - hashtag = hashtag.replace(/^#/, ''); - - if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(`/tags/${hashtag}`); - } - }; - handleMouseDown = (e) => { this.startXY = [e.clientX, e.clientY]; }; @@ -206,7 +164,7 @@ class StatusContent extends PureComponent { handleElement = (element, { key, ...props }, children) => { if (element instanceof HTMLAnchorElement) { - const mention = this.props.status.get('mentions').find(item => element.href === item.get('url')); + const mention = this.props.status.get('mentions').find(item => compareUrls(element.href, item.get('url'))); return ( ); - } else if (element instanceof HTMLParagraphElement && element.classList.contains('quote-inline')) { + } else if (element.classList.contains('quote-inline') && this.props.status.get('quote')) { return null; } return undefined; diff --git a/app/javascript/mastodon/components/status_quoted.tsx b/app/javascript/mastodon/components/status_quoted.tsx index a0024bbf6b298c..647672e054ee19 100644 --- a/app/javascript/mastodon/components/status_quoted.tsx +++ b/app/javascript/mastodon/components/status_quoted.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -83,6 +83,62 @@ const LimitedAccountHint: React.FC<{ accountId: string }> = ({ accountId }) => { ); }; +const FilteredQuote: React.FC<{ + reveal: VoidFunction; + quotedAccountId: string; + quoteState: string; +}> = ({ reveal, quotedAccountId, quoteState }) => { + const account = useAppSelector((state) => + quotedAccountId ? state.accounts.get(quotedAccountId) : undefined, + ); + + const quoteAuthorName = account?.acct; + const domain = quoteAuthorName?.split('@')[1]; + + let message; + + switch (quoteState) { + case 'blocked_account': + message = ( + + ); + break; + case 'blocked_domain': + message = ( + + ); + break; + case 'muted_account': + message = ( + + ); + } + + return ( + <> + {message} + + + ); +}; + interface QuotedStatusProps { quote: QuoteMap; contextType?: string; @@ -130,6 +186,11 @@ export const QuotedStatus: React.FC = ({ const isLoaded = loadingState === 'complete'; const isFetchingQuoteRef = useRef(false); + const [revealed, setRevealed] = useState(false); + + const reveal = useCallback(() => { + setRevealed(true); + }, [setRevealed]); useEffect(() => { if (isLoaded) { @@ -189,6 +250,20 @@ export const QuotedStatus: React.FC = ({ defaultMessage='Post removed by author' /> ); + } else if ( + (quoteState === 'blocked_account' || + quoteState === 'blocked_domain' || + quoteState === 'muted_account') && + !revealed && + accountId + ) { + quoteError = ( + + ); } else if ( !status || !quotedStatusId || diff --git a/app/javascript/mastodon/components/verified_badge.tsx b/app/javascript/mastodon/components/verified_badge.tsx index 43edbc79518849..cc8d8bf409d6d3 100644 --- a/app/javascript/mastodon/components/verified_badge.tsx +++ b/app/javascript/mastodon/components/verified_badge.tsx @@ -1,30 +1,10 @@ import { EmojiHTML } from '@/mastodon/components/emoji/html'; import CheckIcon from '@/material-icons/400-24px/check.svg?react'; -import { isModernEmojiEnabled } from '../utils/environment'; import type { OnAttributeHandler } from '../utils/html'; import { Icon } from './icon'; -const domParser = new DOMParser(); - -const stripRelMe = (html: string) => { - if (isModernEmojiEnabled()) { - return html; - } - const document = domParser.parseFromString(html, 'text/html').documentElement; - - document.querySelectorAll('a[rel]').forEach((link) => { - link.rel = link.rel - .split(' ') - .filter((x: string) => x !== 'me') - .join(' '); - }); - - const body = document.querySelector('body'); - return body?.innerHTML ?? ''; -}; - const onAttribute: OnAttributeHandler = (name, value, tagName) => { if (name === 'rel' && tagName === 'a') { if (value === 'me') { @@ -47,10 +27,6 @@ interface Props { export const VerifiedBadge: React.FC = ({ link }) => ( - + ); diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index 1cbe24a51970d4..f2dc4c0ffc5752 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -49,7 +49,6 @@ import { ShortNumber } from 'mastodon/components/short_number'; import { AccountNote } from 'mastodon/features/account/components/account_note'; import { DomainPill } from 'mastodon/features/account/components/domain_pill'; import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container'; -import { useLinks } from 'mastodon/hooks/useLinks'; import { useIdentity } from 'mastodon/identity_context'; import { autoPlayGif, @@ -216,7 +215,6 @@ export const AccountHeader: React.FC<{ state.relationships.get(accountId), ); const hidden = useAppSelector((state) => getAccountHidden(state, accountId)); - const handleLinkClick = useLinks(); const isHideRelationships = isHideItem('relationships'); @@ -939,10 +937,7 @@ export const AccountHeader: React.FC<{ {!(suspended || hidden) && (
-
+
{account.id !== me && signedIn && ( )} diff --git a/app/javascript/mastodon/features/community_timeline/index.jsx b/app/javascript/mastodon/features/community_timeline/index.jsx index 5652ea5327980c..b33fdd26b68302 100644 --- a/app/javascript/mastodon/features/community_timeline/index.jsx +++ b/app/javascript/mastodon/features/community_timeline/index.jsx @@ -10,7 +10,8 @@ import { connect } from 'react-redux'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { domain } from 'mastodon/initial_state'; +import { domain, localLiveFeedAccess } from 'mastodon/initial_state'; +import { canViewFeed } from 'mastodon/permissions'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { connectCommunityStream } from '../../actions/streaming'; @@ -120,8 +121,21 @@ class CommunityTimeline extends PureComponent { render () { const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props; + const { signedIn, permissions } = this.props.identity; const pinned = !!columnId; + const emptyMessage = canViewFeed(signedIn, permissions, localLiveFeedAccess) ? ( + + ) : ( + + ); + return ( } + emptyMessage={emptyMessage} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 1bad4e96d3ee4f..2f2a9ae38cf1c6 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -1,6 +1,5 @@ import Trie from 'substring-trie'; -import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; import { assetHost } from 'mastodon/utils/config'; import { autoPlayGif } from '../../initial_state'; @@ -86,6 +85,9 @@ const emojifyTextNode = (node, customEmojis) => { replacement.setAttribute('src', filename); replacement.setAttribute('data-original', custom_emoji.url); replacement.setAttribute('data-static', custom_emoji.static_url); + if (custom_emoji.width && custom_emoji.height) { + replacement.style.aspectRatio = `${custom_emoji.width} / ${custom_emoji.height}`; + } } else { // start of an unicode emoji rend = i + unicode_emoji.length; @@ -153,13 +155,9 @@ const emojifyNode = (node, customEmojis) => { * Legacy emoji processing function. * @param {string} str * @param {object} customEmojis - * @param {boolean} force If true, always emojify even if modern emoji is enabled * @returns {string} */ -const emojify = (str, customEmojis = {}, force = false) => { - if (isModernEmojiEnabled() && !force) { - return str; - } +const emojify = (str, customEmojis = {}) => { const wrapper = document.createElement('div'); wrapper.innerHTML = str; diff --git a/app/javascript/mastodon/features/emoji/emoji_compressed.mjs b/app/javascript/mastodon/features/emoji/emoji_compressed.mjs index c52c99824f98c6..c565b861fd75e2 100644 --- a/app/javascript/mastodon/features/emoji/emoji_compressed.mjs +++ b/app/javascript/mastodon/features/emoji/emoji_compressed.mjs @@ -14,8 +14,7 @@ import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data'; import data from './emoji_data.json'; import emojiMap from './emoji_map.json'; -import { unicodeToFilename } from './unicode_to_filename_s'; -import { unicodeToUnifiedName } from './unicode_to_unified_name_s'; +import { unicodeToFilename, unicodeToUnifiedName } from './unicode_utils'; emojiMartUncompress(data); diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts index 1315518179b889..59d995e25fff4f 100644 --- a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts +++ b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts @@ -9,7 +9,7 @@ import type { ShortCodesToEmojiData, } from 'virtual:mastodon-emoji-compressed'; -import { unicodeToUnifiedName } from './unicode_to_unified_name'; +import { unicodeToUnifiedName } from './unicode_utils'; type Emojis = Record< NonNullable, @@ -23,7 +23,7 @@ type Emojis = Record< const [ shortCodesToEmojiData, - skins, + _skins, categories, short_names, _emojisWithoutShortCodes, @@ -47,4 +47,4 @@ Object.keys(shortCodesToEmojiData).forEach((shortCode) => { }; }); -export { emojis, skins, categories, short_names }; +export { emojis, categories, short_names }; diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js index 83e154b0b287e5..038dd120c9a5a8 100644 --- a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js +++ b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js @@ -1,7 +1,7 @@ // This code is largely borrowed from: // https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js -import * as data from './emoji_mart_data_light'; +import { emojis, categories } from './emoji_mart_data_light'; import { getData, getSanitizedData, uniq, intersect } from './emoji_utils'; let originalPool = {}; @@ -10,8 +10,8 @@ let emojisList = {}; let emoticonsList = {}; let customEmojisList = []; -for (let emoji in data.emojis) { - let emojiData = data.emojis[emoji]; +for (let emoji in emojis) { + let emojiData = emojis[emoji]; let { short_names, emoticons } = emojiData; let id = short_names[0]; @@ -84,14 +84,14 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo if (include.length || exclude.length) { pool = {}; - data.categories.forEach(category => { + categories.forEach(category => { let isIncluded = include && include.length ? include.indexOf(category.name.toLowerCase()) > -1 : true; let isExcluded = exclude && exclude.length ? exclude.indexOf(category.name.toLowerCase()) > -1 : false; if (!isIncluded || isExcluded) { return; } - category.emojis.forEach(emojiId => pool[emojiId] = data.emojis[emojiId]); + category.emojis.forEach(emojiId => pool[emojiId] = emojis[emojiId]); }); if (custom.length) { @@ -171,7 +171,7 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo if (results) { if (emojisToShowFilter) { - results = results.filter((result) => emojisToShowFilter(data.emojis[result.id])); + results = results.filter((result) => emojisToShowFilter(emojis[result.id])); } if (results && results.length > maxResults) { diff --git a/app/javascript/mastodon/features/emoji/emoji_picker.tsx b/app/javascript/mastodon/features/emoji/emoji_picker.tsx index 38363d4310b3d8..37fc94dde7aa0c 100644 --- a/app/javascript/mastodon/features/emoji/emoji_picker.tsx +++ b/app/javascript/mastodon/features/emoji/emoji_picker.tsx @@ -2,7 +2,6 @@ import type { EmojiProps, PickerProps } from 'emoji-mart'; import EmojiRaw from 'emoji-mart/dist-es/components/emoji/nimble-emoji'; import PickerRaw from 'emoji-mart/dist-es/components/picker/nimble-picker'; -import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; import { assetHost } from 'mastodon/utils/config'; import { EMOJI_MODE_NATIVE } from './constants'; @@ -27,7 +26,7 @@ const Emoji = ({ sheetSize={sheetSize} sheetColumns={sheetColumns} sheetRows={sheetRows} - native={mode === EMOJI_MODE_NATIVE && isModernEmojiEnabled()} + native={mode === EMOJI_MODE_NATIVE} backgroundImageFn={backgroundImageFn} {...props} /> @@ -51,7 +50,7 @@ const Picker = ({ sheetColumns={sheetColumns} sheetRows={sheetRows} backgroundImageFn={backgroundImageFn} - native={mode === EMOJI_MODE_NATIVE && isModernEmojiEnabled()} + native={mode === EMOJI_MODE_NATIVE} {...props} /> ); diff --git a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts index a53496be2a8e2d..ecf36e3ea85ceb 100644 --- a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts +++ b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts @@ -8,7 +8,7 @@ import type { ShortCodesToEmojiDataKey, } from 'virtual:mastodon-emoji-compressed'; -import { unicodeToFilename } from './unicode_to_filename'; +import { unicodeToFilename } from './unicode_utils'; type UnicodeMapping = Record< FilenameData[number][0], diff --git a/app/javascript/mastodon/features/emoji/emoji_utils.js b/app/javascript/mastodon/features/emoji/emoji_utils.js index 2aa82c197a246e..3508a49f8e78a2 100644 --- a/app/javascript/mastodon/features/emoji/emoji_utils.js +++ b/app/javascript/mastodon/features/emoji/emoji_utils.js @@ -209,50 +209,9 @@ function intersect(a, b) { return uniqA.filter(item => uniqB.indexOf(item) >= 0); } -function deepMerge(a, b) { - let o = {}; - - for (let key in a) { - let originalValue = a[key], - value = originalValue; - - if (Object.hasOwn(b, key)) { - value = b[key]; - } - - if (typeof value === 'object') { - value = deepMerge(originalValue, value); - } - - o[key] = value; - } - - return o; -} - -// https://github.com/sonicdoe/measure-scrollbar -function measureScrollbar() { - const div = document.createElement('div'); - - div.style.width = '100px'; - div.style.height = '100px'; - div.style.overflow = 'scroll'; - div.style.position = 'absolute'; - div.style.top = '-9999px'; - - document.body.appendChild(div); - const scrollbarWidth = div.offsetWidth - div.clientWidth; - document.body.removeChild(div); - - return scrollbarWidth; -} - export { getData, getSanitizedData, uniq, intersect, - deepMerge, - unifiedToNative, - measureScrollbar, }; diff --git a/app/javascript/mastodon/features/emoji/handlers.ts b/app/javascript/mastodon/features/emoji/handlers.ts deleted file mode 100644 index 3b02028f3c3608..00000000000000 --- a/app/javascript/mastodon/features/emoji/handlers.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { autoPlayGif } from '@/mastodon/initial_state'; - -const PARENT_MAX_DEPTH = 10; - -export function handleAnimateGif(event: MouseEvent) { - // We already check this in ui/index.jsx, but just to be sure. - if (autoPlayGif) { - return; - } - - const { target, type } = event; - const animate = type === 'mouseover'; // Mouse over = animate, mouse out = don't animate. - - if (target instanceof HTMLImageElement) { - setAnimateGif(target, animate); - } else if (!(target instanceof HTMLElement) || target === document.body) { - return; - } - - let parent: HTMLElement | null = null; - let iter = 0; - - if (target.classList.contains('animate-parent')) { - parent = target; - } else { - // Iterate up to PARENT_MAX_DEPTH levels up the DOM tree to find a parent with the class 'animate-parent'. - let current: HTMLElement | null = target; - while (current) { - if (iter >= PARENT_MAX_DEPTH) { - return; // We can just exit right now. - } - current = current.parentElement; - if (current?.classList.contains('animate-parent')) { - parent = current; - break; - } - iter++; - } - } - - // Affect all animated children within the parent. - if (parent) { - const animatedChildren = - parent.querySelectorAll('img.custom-emoji'); - for (const child of animatedChildren) { - setAnimateGif(child, animate); - } - } -} - -function setAnimateGif(image: HTMLImageElement, animate: boolean) { - const { classList, dataset } = image; - if ( - !classList.contains('custom-emoji') || - !dataset.static || - !dataset.original - ) { - return; - } - image.src = animate ? dataset.original : dataset.static; -} diff --git a/app/javascript/mastodon/features/emoji/index.ts b/app/javascript/mastodon/features/emoji/index.ts index 3701ad67679ccc..11ee26aac295e2 100644 --- a/app/javascript/mastodon/features/emoji/index.ts +++ b/app/javascript/mastodon/features/emoji/index.ts @@ -1,8 +1,9 @@ import { initialState } from '@/mastodon/initial_state'; -import { loadWorker } from '@/mastodon/utils/workers'; import { toSupportedLocale } from './locale'; import { emojiLogger } from './utils'; +// eslint-disable-next-line import/default -- Importing via worker loader. +import EmojiWorker from './worker?worker&inline'; const userLocale = toSupportedLocale(initialState?.meta.locale ?? 'en'); @@ -16,9 +17,7 @@ export function initializeEmoji() { log('initializing emojis'); if (!worker && 'Worker' in window) { try { - worker = loadWorker(new URL('./worker', import.meta.url), { - type: 'module', - }); + worker = new EmojiWorker(); } catch (err) { console.warn('Error creating web worker:', err); } diff --git a/app/javascript/mastodon/features/emoji/types.ts b/app/javascript/mastodon/features/emoji/types.ts index 8cd0902aa4a138..e71fde457a5ec6 100644 --- a/app/javascript/mastodon/features/emoji/types.ts +++ b/app/javascript/mastodon/features/emoji/types.ts @@ -33,7 +33,7 @@ export type AnyEmojiData = CustomEmojiData | UnicodeEmojiData; type CustomEmojiRenderFields = Pick< CustomEmojiData, - 'shortcode' | 'static_url' | 'url' + 'shortcode' | 'static_url' | 'url' | 'width' | 'height' >; export interface EmojiStateUnicode { diff --git a/app/javascript/mastodon/features/emoji/unicode_to_filename_s.js b/app/javascript/mastodon/features/emoji/unicode_to_filename_s.js deleted file mode 100644 index cfe5539c7b9887..00000000000000 --- a/app/javascript/mastodon/features/emoji/unicode_to_filename_s.js +++ /dev/null @@ -1,26 +0,0 @@ -// taken from: -// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866 -export const unicodeToFilename = (str) => { - let result = ''; - let charCode = 0; - let p = 0; - let i = 0; - while (i < str.length) { - charCode = str.charCodeAt(i++); - if (p) { - if (result.length > 0) { - result += '-'; - } - result += (0x10000 + ((p - 0xD800) << 10) + (charCode - 0xDC00)).toString(16); - p = 0; - } else if (0xD800 <= charCode && charCode <= 0xDBFF) { - p = charCode; - } else { - if (result.length > 0) { - result += '-'; - } - result += charCode.toString(16); - } - } - return result; -}; diff --git a/app/javascript/mastodon/features/emoji/unicode_to_unified_name_s.js b/app/javascript/mastodon/features/emoji/unicode_to_unified_name_s.js deleted file mode 100644 index 15f60aa7c30e68..00000000000000 --- a/app/javascript/mastodon/features/emoji/unicode_to_unified_name_s.js +++ /dev/null @@ -1,21 +0,0 @@ -function padLeft(str, num) { - while (str.length < num) { - str = '0' + str; - } - - return str; -} - -export const unicodeToUnifiedName = (str) => { - let output = ''; - - for (let i = 0; i < str.length; i += 2) { - if (i > 0) { - output += '-'; - } - - output += padLeft(str.codePointAt(i).toString(16).toUpperCase(), 4); - } - - return output; -}; diff --git a/app/javascript/mastodon/features/emoji/unicode_utils.ts b/app/javascript/mastodon/features/emoji/unicode_utils.ts new file mode 100644 index 00000000000000..04175ee9f903ec --- /dev/null +++ b/app/javascript/mastodon/features/emoji/unicode_utils.ts @@ -0,0 +1,43 @@ +// taken from: +// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866 +export function unicodeToFilename(str: string) { + let result = ''; + let charCode = 0; + let p = 0; + let i = 0; + while (i < str.length) { + charCode = str.charCodeAt(i++); + if (p) { + if (result.length > 0) { + result += '-'; + } + result += (0x10000 + ((p - 0xd800) << 10) + (charCode - 0xdc00)).toString( + 16, + ); + p = 0; + } else if (0xd800 <= charCode && charCode <= 0xdbff) { + p = charCode; + } else { + if (result.length > 0) { + result += '-'; + } + result += charCode.toString(16); + } + } + return result; +} + +export function unicodeToUnifiedName(str: string) { + let output = ''; + + for (let i = 0; i < str.length; i += 2) { + if (i > 0) { + output += '-'; + } + + output += + str.codePointAt(i)?.toString(16).toUpperCase().padStart(4, '0') ?? ''; + } + + return output; +} diff --git a/app/javascript/mastodon/features/firehose/index.jsx b/app/javascript/mastodon/features/firehose/index.jsx index dd9deddda93967..658fa3d364dd95 100644 --- a/app/javascript/mastodon/features/firehose/index.jsx +++ b/app/javascript/mastodon/features/firehose/index.jsx @@ -13,7 +13,8 @@ import { changeSetting } from 'mastodon/actions/settings'; import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming'; import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines'; import { DismissableBanner } from 'mastodon/components/dismissable_banner'; -import { localLiveFeedAccess, remoteLiveFeedAccess, me, domain, enableLocalTimeline } from 'mastodon/initial_state'; +import { localLiveFeedAccess, remoteLiveFeedAccess, domain, enableLocalTimeline } from 'mastodon/initial_state'; +import { canViewFeed } from 'mastodon/permissions'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; import Column from '../../components/column'; @@ -23,6 +24,14 @@ import StatusListContainer from '../ui/containers/status_list_container'; const messages = defineMessages({ title: { id: 'column.firehose', defaultMessage: 'Live feeds' }, + title_local: { + id: 'column.firehose_local', + defaultMessage: 'Live feed for this server', + }, + title_singular: { + id: 'column.firehose_singular', + defaultMessage: 'Live feed', + }, }); const ColumnSettings = () => { @@ -52,7 +61,7 @@ const ColumnSettings = () => { const Firehose = ({ feedType, multiColumn }) => { const dispatch = useAppDispatch(); const intl = useIntl(); - const { signedIn } = useIdentity(); + const { signedIn, permissions } = useIdentity(); const columnRef = useRef(null); const onlyMedia = useAppSelector((state) => state.getIn(['settings', 'firehose', 'onlyMedia'], false)); @@ -151,13 +160,32 @@ const Firehose = ({ feedType, multiColumn }) => { /> ); + const canViewSelectedFeed = canViewFeed(signedIn, permissions, feedType === 'community' ? localLiveFeedAccess : remoteLiveFeedAccess); + + const disabledTimelineMessage = ( + + ); + + let title; + + if (canViewFeed(signedIn, permissions, localLiveFeedAccess) && canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) { + title = messages.title; + } else if (canViewFeed(signedIn, permissions, localLiveFeedAccess)) { + title = messages.title_local; + } else { + title = messages.title_singular; + } + return ( { - {(signedIn || (localLiveFeedAccess === 'public' && remoteLiveFeedAccess === 'public')) && enableLocalTimeline && ( + {(canViewFeed(signedIn, permissions, localLiveFeedAccess) && canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) && enableLocalTimeline && (
@@ -187,7 +215,7 @@ const Firehose = ({ feedType, multiColumn }) => { onLoadMore={handleLoadMore} trackScroll scrollKey='firehose' - emptyMessage={emptyMessage} + emptyMessage={canViewSelectedFeed ? emptyMessage : disabledTimelineMessage} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.jsx b/app/javascript/mastodon/features/getting_started/components/announcements.jsx deleted file mode 100644 index 96bd995d2b10e7..00000000000000 --- a/app/javascript/mastodon/features/getting_started/components/announcements.jsx +++ /dev/null @@ -1,458 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent, useCallback, useMemo } from 'react'; - -import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl'; - -import classNames from 'classnames'; -import { withRouter } from 'react-router-dom'; - -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; - -import { animated, useTransition } from '@react-spring/web'; -import ReactSwipeableViews from 'react-swipeable-views'; - -import elephantUIPlane from '@/images/elephant_ui_plane.svg'; -import AddIcon from '@/material-icons/400-24px/add.svg?react'; -import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; -import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; -import { AnimatedNumber } from 'mastodon/components/animated_number'; -import { Icon } from 'mastodon/components/icon'; -import { IconButton } from 'mastodon/components/icon_button'; -import EmojiPickerDropdown from 'mastodon/features/compose/containers/emoji_picker_dropdown_container'; -import { unicodeMapping } from 'mastodon/features/emoji/emoji_unicode_mapping_light'; -import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'mastodon/initial_state'; -import { assetHost } from 'mastodon/utils/config'; -import { WithRouterPropTypes } from 'mastodon/utils/react_router'; - -const messages = defineMessages({ - close: { id: 'lightbox.close', defaultMessage: 'Close' }, - previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, - next: { id: 'lightbox.next', defaultMessage: 'Next' }, -}); - -class ContentWithRouter extends ImmutablePureComponent { - static propTypes = { - announcement: ImmutablePropTypes.map.isRequired, - ...WithRouterPropTypes, - }; - - setRef = c => { - this.node = c; - }; - - componentDidMount () { - this._updateLinks(); - } - - componentDidUpdate () { - this._updateLinks(); - } - - _updateLinks () { - const node = this.node; - - if (!node) { - return; - } - - const links = node.querySelectorAll('a'); - - for (var i = 0; i < links.length; ++i) { - let link = links[i]; - - if (link.classList.contains('status-link')) { - continue; - } - - link.classList.add('status-link'); - - let mention = this.props.announcement.get('mentions').find(item => link.href === item.get('url')); - - if (mention) { - link.addEventListener('click', this.onMentionClick.bind(this, mention), false); - link.setAttribute('title', mention.get('acct')); - } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) { - link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false); - } else { - let status = this.props.announcement.get('statuses').find(item => link.href === item.get('url')); - if (status) { - link.addEventListener('click', this.onStatusClick.bind(this, status), false); - } - link.setAttribute('title', link.href); - link.classList.add('unhandled-link'); - } - - link.setAttribute('target', '_blank'); - link.setAttribute('rel', 'noopener'); - } - } - - onMentionClick = (mention, e) => { - if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(`/@${mention.get('acct')}`); - } - }; - - onHashtagClick = (hashtag, e) => { - hashtag = hashtag.replace(/^#/, ''); - - if (this.props.history&& e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(`/tags/${hashtag}`); - } - }; - - onStatusClick = (status, e) => { - if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - this.props.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`); - } - }; - - render () { - const { announcement } = this.props; - - return ( -
- ); - } - -} - -const Content = withRouter(ContentWithRouter); - -class Emoji extends PureComponent { - - static propTypes = { - emoji: PropTypes.string.isRequired, - emojiMap: ImmutablePropTypes.map.isRequired, - hovered: PropTypes.bool.isRequired, - }; - - render () { - const { emoji, emojiMap, hovered } = this.props; - - if (unicodeMapping[emoji]) { - const { filename, shortCode } = unicodeMapping[this.props.emoji]; - const title = shortCode ? `:${shortCode}:` : ''; - - return ( - {emoji} - ); - } else if (emojiMap.get(emoji)) { - const filename = (autoPlayGif || hovered) ? emojiMap.getIn([emoji, 'url']) : emojiMap.getIn([emoji, 'static_url']); - const shortCode = `:${emoji}:`; - - return ( - {shortCode} - ); - } else { - return null; - } - } - -} - -class Reaction extends ImmutablePureComponent { - - static propTypes = { - announcementId: PropTypes.string.isRequired, - reaction: ImmutablePropTypes.map.isRequired, - addReaction: PropTypes.func.isRequired, - removeReaction: PropTypes.func.isRequired, - emojiMap: ImmutablePropTypes.map.isRequired, - style: PropTypes.object, - }; - - state = { - hovered: false, - }; - - handleClick = () => { - const { reaction, announcementId, addReaction, removeReaction } = this.props; - - if (reaction.get('me')) { - removeReaction(announcementId, reaction.get('name')); - } else { - addReaction(announcementId, reaction.get('name')); - } - }; - - handleMouseEnter = () => this.setState({ hovered: true }); - - handleMouseLeave = () => this.setState({ hovered: false }); - - render () { - const { reaction } = this.props; - - let shortCode = reaction.get('name'); - - if (unicodeMapping[shortCode]) { - shortCode = unicodeMapping[shortCode].shortCode; - } - - return ( - - - - - - - - - ); - } - -} - -const ReactionsBar = ({ - announcementId, - reactions, - emojiMap, - addReaction, - removeReaction, -}) => { - const visibleReactions = useMemo(() => reactions.filter(x => x.get('count') > 0).toArray(), [reactions]); - - const handleEmojiPick = useCallback((emoji) => { - addReaction(announcementId, emoji.native.replaceAll(/:/g, '')); - }, [addReaction, announcementId]); - - const transitions = useTransition(visibleReactions, { - from: { - scale: 0, - }, - enter: { - scale: 1, - }, - leave: { - scale: 0, - }, - keys: visibleReactions.map(x => x.get('name')), - }); - - return ( -
- {transitions(({ scale }, reaction) => ( - `scale(${s})`) }} - addReaction={addReaction} - removeReaction={removeReaction} - announcementId={announcementId} - emojiMap={emojiMap} - /> - ))} - - {visibleReactions.length < 8 && ( - } - /> - )} -
- ); -}; -ReactionsBar.propTypes = { - announcementId: PropTypes.string.isRequired, - reactions: ImmutablePropTypes.list.isRequired, - addReaction: PropTypes.func.isRequired, - removeReaction: PropTypes.func.isRequired, - emojiMap: ImmutablePropTypes.map.isRequired, -}; - -class Announcement extends ImmutablePureComponent { - - static propTypes = { - announcement: ImmutablePropTypes.map.isRequired, - emojiMap: ImmutablePropTypes.map.isRequired, - addReaction: PropTypes.func.isRequired, - removeReaction: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - selected: PropTypes.bool, - }; - - state = { - unread: !this.props.announcement.get('read'), - }; - - componentDidUpdate () { - const { selected, announcement } = this.props; - if (!selected && this.state.unread !== !announcement.get('read')) { - this.setState({ unread: !announcement.get('read') }); - } - } - - render () { - const { announcement } = this.props; - const { unread } = this.state; - const startsAt = announcement.get('starts_at') && new Date(announcement.get('starts_at')); - const endsAt = announcement.get('ends_at') && new Date(announcement.get('ends_at')); - const now = new Date(); - const hasTimeRange = startsAt && endsAt; - const skipTime = announcement.get('all_day'); - - let timestamp = null; - if (hasTimeRange) { - const skipYear = startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear(); - const skipEndDate = startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear(); - timestamp = ( - <> - - - - ); - } else { - const publishedAt = new Date(announcement.get('published_at')); - timestamp = ( - - ); - } - - return ( -
- - - · {timestamp} - - - - - - - {unread && } -
- ); - } - -} - -class Announcements extends ImmutablePureComponent { - - static propTypes = { - announcements: ImmutablePropTypes.list, - emojiMap: ImmutablePropTypes.map.isRequired, - dismissAnnouncement: PropTypes.func.isRequired, - addReaction: PropTypes.func.isRequired, - removeReaction: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, - }; - - state = { - index: 0, - }; - - static getDerivedStateFromProps(props, state) { - if (props.announcements.size > 0 && state.index >= props.announcements.size) { - return { index: props.announcements.size - 1 }; - } else { - return null; - } - } - - componentDidMount () { - this._markAnnouncementAsRead(); - } - - componentDidUpdate () { - this._markAnnouncementAsRead(); - } - - _markAnnouncementAsRead () { - const { dismissAnnouncement, announcements } = this.props; - const { index } = this.state; - const announcement = announcements.get(announcements.size - 1 - index); - if (!announcement.get('read')) dismissAnnouncement(announcement.get('id')); - } - - handleChangeIndex = index => { - this.setState({ index: index % this.props.announcements.size }); - }; - - handleNextClick = () => { - this.setState({ index: (this.state.index + 1) % this.props.announcements.size }); - }; - - handlePrevClick = () => { - this.setState({ index: (this.props.announcements.size + this.state.index - 1) % this.props.announcements.size }); - }; - - render () { - const { announcements, intl } = this.props; - const { index } = this.state; - - if (announcements.isEmpty()) { - return null; - } - - return ( -
- - -
- - {announcements.map((announcement, idx) => ( - - )).reverse()} - - - {announcements.size > 1 && ( -
- - {index + 1} / {announcements.size} - -
- )} -
-
- ); - } - -} - -export default injectIntl(Announcements); diff --git a/app/javascript/mastodon/features/getting_started/containers/announcements_container.js b/app/javascript/mastodon/features/getting_started/containers/announcements_container.js deleted file mode 100644 index 3bb1b8e8d182c7..00000000000000 --- a/app/javascript/mastodon/features/getting_started/containers/announcements_container.js +++ /dev/null @@ -1,23 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { Map as ImmutableMap } from 'immutable'; -import { connect } from 'react-redux'; - - -import { addReaction, removeReaction, dismissAnnouncement } from 'mastodon/actions/announcements'; - -import Announcements from '../components/announcements'; - -const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap())); - -const mapStateToProps = state => ({ - announcements: state.getIn(['announcements', 'items']), - emojiMap: customEmojiMap(state), -}); - -const mapDispatchToProps = dispatch => ({ - dismissAnnouncement: id => dispatch(dismissAnnouncement(id)), - addReaction: (id, name) => dispatch(addReaction(id, name)), - removeReaction: (id, name) => dispatch(removeReaction(id, name)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(Announcements); diff --git a/app/javascript/mastodon/features/home_timeline/components/announcements/announcement.tsx b/app/javascript/mastodon/features/home_timeline/components/announcements/announcement.tsx index 8513e6169bd5e8..39d3c881b0ccab 100644 --- a/app/javascript/mastodon/features/home_timeline/components/announcements/announcement.tsx +++ b/app/javascript/mastodon/features/home_timeline/components/announcements/announcement.tsx @@ -15,24 +15,24 @@ export interface IAnnouncement extends ApiAnnouncementJSON { interface AnnouncementProps { announcement: IAnnouncement; - selected: boolean; + active?: boolean; } export const Announcement: FC = ({ announcement, - selected, + active, }) => { const [unread, setUnread] = useState(!announcement.read); useEffect(() => { // Only update `unread` marker once the announcement is out of view - if (!selected && unread !== !announcement.read) { + if (!active && unread !== !announcement.read) { setUnread(!announcement.read); } - }, [announcement.read, selected, unread]); + }, [announcement.read, active, unread]); return ( - - + + = ({ - {unread && } + {unread && } ); }; diff --git a/app/javascript/mastodon/features/home_timeline/components/announcements/index.tsx b/app/javascript/mastodon/features/home_timeline/components/announcements/index.tsx index 8c7c704849dfdc..cb44e1d075c0c7 100644 --- a/app/javascript/mastodon/features/home_timeline/components/announcements/index.tsx +++ b/app/javascript/mastodon/features/home_timeline/components/announcements/index.tsx @@ -1,65 +1,50 @@ -import { useCallback, useState } from 'react'; +import { useCallback } from 'react'; import type { FC } from 'react'; -import { defineMessages, useIntl } from 'react-intl'; - import type { Map, List } from 'immutable'; -import ReactSwipeableViews from 'react-swipeable-views'; - import elephantUIPlane from '@/images/elephant_ui_plane.svg'; +import type { RenderSlideFn } from '@/mastodon/components/carousel'; +import { Carousel } from '@/mastodon/components/carousel'; import { CustomEmojiProvider } from '@/mastodon/components/emoji/context'; -import { IconButton } from '@/mastodon/components/icon_button'; -import LegacyAnnouncements from '@/mastodon/features/getting_started/containers/announcements_container'; -import { mascot, reduceMotion } from '@/mastodon/initial_state'; +import { mascot } from '@/mastodon/initial_state'; import { createAppSelector, useAppSelector } from '@/mastodon/store'; -import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; -import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react'; -import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import type { IAnnouncement } from './announcement'; import { Announcement } from './announcement'; -const messages = defineMessages({ - close: { id: 'lightbox.close', defaultMessage: 'Close' }, - previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, - next: { id: 'lightbox.next', defaultMessage: 'Next' }, -}); - const announcementSelector = createAppSelector( [(state) => state.announcements as Map>>], (announcements) => - (announcements.get('items')?.toJS() as IAnnouncement[] | undefined) ?? [], + ((announcements.get('items')?.toJS() as IAnnouncement[] | undefined) ?? []) + .map((announcement) => ({ announcement, id: announcement.id })) + .toReversed(), ); -export const ModernAnnouncements: FC = () => { - const intl = useIntl(); - +export const Announcements: FC = () => { const announcements = useAppSelector(announcementSelector); const emojis = useAppSelector((state) => state.custom_emojis); - const [index, setIndex] = useState(0); - const handleChangeIndex = useCallback( - (idx: number) => { - setIndex(idx % announcements.length); - }, - [announcements.length], + const renderSlide: RenderSlideFn<{ + id: string; + announcement: IAnnouncement; + }> = useCallback( + (item, active) => ( + + ), + [], ); - const handleNextIndex = useCallback(() => { - setIndex((prevIndex) => (prevIndex + 1) % announcements.length); - }, [announcements.length]); - const handlePrevIndex = useCallback(() => { - setIndex((prevIndex) => - prevIndex === 0 ? announcements.length - 1 : prevIndex - 1, - ); - }, [announcements.length]); if (announcements.length === 0) { return null; } return ( -
+
{ src={mascot ?? elephantUIPlane} /> -
- - - {announcements - .map((announcement, idx) => ( - - )) - .reverse()} - - - - {announcements.length > 1 && ( -
- - - {index + 1} / {announcements.length} - - -
- )} -
+ + +
); }; - -export const Announcements = isModernEmojiEnabled() - ? ModernAnnouncements - : LegacyAnnouncements; diff --git a/app/javascript/mastodon/features/navigation_panel/index.tsx b/app/javascript/mastodon/features/navigation_panel/index.tsx index 4d281646c216df..c6d3447e7ae455 100644 --- a/app/javascript/mastodon/features/navigation_panel/index.tsx +++ b/app/javascript/mastodon/features/navigation_panel/index.tsx @@ -49,6 +49,7 @@ import { me, } from 'mastodon/initial_state'; import { transientSingleColumn } from 'mastodon/is_mobile'; +import { canViewFeed } from 'mastodon/permissions'; import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; @@ -68,6 +69,10 @@ const messages = defineMessages({ }, explore: { id: 'explore.title', defaultMessage: 'Trending' }, firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' }, + firehose_singular: { + id: 'column.firehose_singular', + defaultMessage: 'Live feed', + }, direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, @@ -207,7 +212,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({ multiColumn = false, }) => { const intl = useIntl(); - const { signedIn, disabledAccountId } = useIdentity(); + const { signedIn, permissions, disabledAccountId } = useIdentity(); const location = useLocation(); const showSearch = useBreakpoint('full') && !multiColumn; @@ -279,16 +284,18 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({ /> )} - {signedIn && enableLocalTimeline && ( - - )} + {signedIn && + enableLocalTimeline && + localLiveFeedAccess !== 'disabled' && ( + + )} {signedIn && enableDtlMenu && ( = ({ /> )} - {(signedIn || - localLiveFeedAccess === 'public' || - remoteLiveFeedAccess === 'public') && ( + {(canViewFeed(signedIn, permissions, localLiveFeedAccess) || + canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) && ( )} diff --git a/app/javascript/mastodon/features/notifications_v2/components/embedded_status_content.tsx b/app/javascript/mastodon/features/notifications_v2/components/embedded_status_content.tsx index 1d1b684b809c1d..9e7f66d112cd72 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/embedded_status_content.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/embedded_status_content.tsx @@ -1,47 +1,17 @@ import { useCallback, useMemo } from 'react'; -import { useHistory } from 'react-router-dom'; - import type { List } from 'immutable'; -import type { History } from 'history'; - -import type { ApiMentionJSON } from '@/mastodon/api_types/statuses'; import { EmojiHTML } from '@/mastodon/components/emoji/html'; import { useElementHandledLink } from '@/mastodon/components/status/handled_link'; import type { Status } from '@/mastodon/models/status'; -import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; import type { Mention } from './embedded_status'; -const handleMentionClick = ( - history: History, - mention: ApiMentionJSON, - e: MouseEvent, -) => { - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - history.push(`/@${mention.acct}`); - } -}; - -const handleHashtagClick = ( - history: History, - hashtag: string, - e: MouseEvent, -) => { - if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { - e.preventDefault(); - history.push(`/tags/${hashtag.replace(/^#/, '')}`); - } -}; - export const EmbeddedStatusContent: React.FC<{ status: Status; className?: string; }> = ({ status, className }) => { - const history = useHistory(); - const mentions = useMemo( () => (status.get('mentions') as List).toJS(), [status], @@ -57,55 +27,10 @@ export const EmbeddedStatusContent: React.FC<{ hrefToMention, }); - const handleContentRef = useCallback( - (node: HTMLDivElement | null) => { - if (!node || isModernEmojiEnabled()) { - return; - } - - const links = node.querySelectorAll('a'); - - for (const link of links) { - if (link.classList.contains('status-link')) { - continue; - } - - link.classList.add('status-link'); - - const mention = mentions.find((item) => link.href === item.url); - - if (mention) { - link.addEventListener( - 'click', - handleMentionClick.bind(null, history, mention), - false, - ); - link.setAttribute('title', `@${mention.acct}`); - link.setAttribute('href', `/@${mention.acct}`); - } else if ( - link.textContent.startsWith('#') || - link.previousSibling?.textContent?.endsWith('#') - ) { - link.addEventListener( - 'click', - handleHashtagClick.bind(null, history, link.text), - false, - ); - link.setAttribute('href', `/tags/${link.text.replace(/^#/, '')}`); - } else { - link.setAttribute('title', link.href); - link.classList.add('unhandled-link'); - } - } - }, - [mentions, history], - ); - return ( diff --git a/app/javascript/mastodon/features/public_timeline/index.jsx b/app/javascript/mastodon/features/public_timeline/index.jsx index aa5a02645d75d2..cf86a8a6df62d6 100644 --- a/app/javascript/mastodon/features/public_timeline/index.jsx +++ b/app/javascript/mastodon/features/public_timeline/index.jsx @@ -10,7 +10,8 @@ import { connect } from 'react-redux'; import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { domain } from 'mastodon/initial_state'; +import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'mastodon/initial_state'; +import { canViewFeed } from 'mastodon/permissions'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { connectPublicStream } from '../../actions/streaming'; @@ -123,8 +124,21 @@ class PublicTimeline extends PureComponent { render () { const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props; + const { signedIn, permissions } = this.props.identity; const pinned = !!columnId; + const emptyMessage = (canViewFeed(signedIn, permissions, localLiveFeedAccess) || canViewFeed(signedIn, permissions, remoteLiveFeedAccess)) ? ( + + ) : ( + + ); + return ( } + emptyMessage={emptyMessage} bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/reaction_deck/index.tsx b/app/javascript/mastodon/features/reaction_deck/index.tsx index 709434b08a53e6..5c8d593060c1f9 100644 --- a/app/javascript/mastodon/features/reaction_deck/index.tsx +++ b/app/javascript/mastodon/features/reaction_deck/index.tsx @@ -108,7 +108,9 @@ const ReactionEmoji: React.FC<{ } if (overlay) { - return
{content}
; + return ( +
{content}
+ ); } return ( diff --git a/app/javascript/mastodon/features/status/components/refresh_controller.tsx b/app/javascript/mastodon/features/status/components/refresh_controller.tsx index 253cce469197f6..92607797b54fe6 100644 --- a/app/javascript/mastodon/features/status/components/refresh_controller.tsx +++ b/app/javascript/mastodon/features/status/components/refresh_controller.tsx @@ -1,7 +1,9 @@ -import { useEffect, useState, useCallback } from 'react'; +import { useEffect, useState, useCallback, useMemo } from 'react'; import { useIntl, defineMessages } from 'react-intl'; +import { useDebouncedCallback } from 'use-debounce'; + import { fetchContext, completeContextRefresh, @@ -13,6 +15,8 @@ import { apiGetAsyncRefresh } from 'mastodon/api/async_refreshes'; import { Alert } from 'mastodon/components/alert'; import { ExitAnimationWrapper } from 'mastodon/components/exit_animation_wrapper'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; +import { useInterval } from 'mastodon/hooks/useInterval'; +import { useIsDocumentVisible } from 'mastodon/hooks/useIsDocumentVisible'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; const AnimatedAlert: React.FC< @@ -52,31 +56,53 @@ const messages = defineMessages({ type LoadingState = 'idle' | 'more-available' | 'loading' | 'success' | 'error'; -export const RefreshController: React.FC<{ - statusId: string; -}> = ({ statusId }) => { - const dispatch = useAppDispatch(); - const intl = useIntl(); +/** + * Age of thread below which we consider it new & fetch + * replies more frequently + */ +const NEW_THREAD_AGE_THRESHOLD = 30 * 60_000; +/** + * Interval at which we check for new replies for old threads + */ +const LONG_AUTO_FETCH_REPLIES_INTERVAL = 5 * 60_000; +/** + * Interval at which we check for new replies for new threads. + * Also used as a threshold to throttle repeated fetch calls + */ +const SHORT_AUTO_FETCH_REPLIES_INTERVAL = 60_000; +/** + * Number of refresh_async checks at which an early fetch + * will be triggered if there are results + */ +const LONG_RUNNING_FETCH_THRESHOLD = 3; - const refreshHeader = useAppSelector( - (state) => state.contexts.refreshing[statusId], - ); - const hasPendingReplies = useAppSelector( - (state) => !!state.contexts.pendingReplies[statusId]?.length, - ); - const [partialLoadingState, setLoadingState] = useState( - refreshHeader ? 'loading' : 'idle', - ); - const loadingState = hasPendingReplies - ? 'more-available' - : partialLoadingState; +/** + * Returns whether the thread is new, based on NEW_THREAD_AGE_THRESHOLD + */ +function getIsThreadNew(statusCreatedAt: string) { + const now = new Date(); + const newThreadThreshold = new Date(now.getTime() - NEW_THREAD_AGE_THRESHOLD); - const [wasDismissed, setWasDismissed] = useState(false); - const dismissPrompt = useCallback(() => { - setWasDismissed(true); - setLoadingState('idle'); - dispatch(clearPendingReplies({ statusId })); - }, [dispatch, statusId]); + return new Date(statusCreatedAt) > newThreadThreshold; +} + +/** + * This hook kicks off a background check for the async refresh job + * and loads any newly found replies once the job has finished, + * and when LONG_RUNNING_FETCH_THRESHOLD was reached and replies were found + */ +function useCheckForRemoteReplies({ + statusId, + refreshHeader, + isEnabled, + onChangeLoadingState, +}: { + statusId: string; + refreshHeader?: AsyncRefreshHeader; + isEnabled: boolean; + onChangeLoadingState: React.Dispatch>; +}) { + const dispatch = useAppDispatch(); useEffect(() => { let timeoutId: ReturnType; @@ -87,11 +113,11 @@ export const RefreshController: React.FC<{ ) => { timeoutId = setTimeout(() => { void apiGetAsyncRefresh(refresh.id).then((result) => { + const { status, result_count } = result.async_refresh; + // At three scheduled refreshes, we consider the job // long-running and attempt to fetch any new replies so far - const isLongRunning = iteration === 3; - - const { status, result_count } = result.async_refresh; + const isLongRunning = iteration === LONG_RUNNING_FETCH_THRESHOLD; // If the refresh status is not finished and not long-running, // we just schedule another refresh and exit @@ -109,7 +135,7 @@ export const RefreshController: React.FC<{ // Exit if there's nothing to fetch if (result_count === 0) { if (status === 'finished') { - setLoadingState('idle'); + onChangeLoadingState('idle'); } else { scheduleRefresh(refresh, iteration + 1); } @@ -126,7 +152,7 @@ export const RefreshController: React.FC<{ // resulted in new pending replies, the `hasPendingReplies` // flag will switch the loading state to 'more-available' if (status === 'finished') { - setLoadingState('idle'); + onChangeLoadingState('idle'); } else { // Keep background fetch going if `isLongRunning` is true scheduleRefresh(refresh, iteration + 1); @@ -134,22 +160,111 @@ export const RefreshController: React.FC<{ }) .catch(() => { // Show an error if the fetch failed - setLoadingState('error'); + onChangeLoadingState('error'); }); }); }, refresh.retry * 1000); }; // Initialise a refresh - if (refreshHeader && !wasDismissed) { + if (refreshHeader && isEnabled) { scheduleRefresh(refreshHeader, 1); - setLoadingState('loading'); + onChangeLoadingState('loading'); } return () => { clearTimeout(timeoutId); }; - }, [dispatch, statusId, refreshHeader, wasDismissed]); + }, [onChangeLoadingState, dispatch, statusId, refreshHeader, isEnabled]); +} + +/** + * This component fetches new post replies in the background + * and gives users the option to show them. + * + * The following three scenarios are handled: + * + * 1. When the browser tab is visible, replies are refetched periodically + * (more frequently for new posts, less frequently for old ones) + * 2. Replies are refetched when the browser tab is refocused + * after it was hidden or minimised + * 3. For remote posts, remote replies that might not yet be known to the + * server are imported & fetched using the AsyncRefresh API. + */ +export const RefreshController: React.FC<{ + statusId: string; + statusCreatedAt: string; + isLocal: boolean; +}> = ({ statusId, statusCreatedAt, isLocal }) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + + const refreshHeader = useAppSelector((state) => + isLocal ? undefined : state.contexts.refreshing[statusId], + ); + const hasPendingReplies = useAppSelector( + (state) => !!state.contexts.pendingReplies[statusId]?.length, + ); + const [partialLoadingState, setLoadingState] = useState( + refreshHeader ? 'loading' : 'idle', + ); + const loadingState = hasPendingReplies + ? 'more-available' + : partialLoadingState; + + const [wasDismissed, setWasDismissed] = useState(false); + const dismissPrompt = useCallback(() => { + setWasDismissed(true); + setLoadingState('idle'); + dispatch(clearPendingReplies({ statusId })); + }, [dispatch, statusId]); + + // Prevent too-frequent context calls + const debouncedFetchContext = useDebouncedCallback( + () => { + void dispatch(fetchContext({ statusId, prefetchOnly: true })); + }, + // Ensure the debounce is a bit shorter than the auto-fetch interval + SHORT_AUTO_FETCH_REPLIES_INTERVAL - 500, + { + leading: true, + trailing: false, + }, + ); + + const isDocumentVisible = useIsDocumentVisible({ + onChange: (isVisible) => { + // Auto-fetch new replies when the page is refocused + if (isVisible && partialLoadingState !== 'loading' && !wasDismissed) { + debouncedFetchContext(); + } + }, + }); + + // Check for remote replies + useCheckForRemoteReplies({ + statusId, + refreshHeader, + isEnabled: isDocumentVisible && !isLocal && !wasDismissed, + onChangeLoadingState: setLoadingState, + }); + + // Only auto-fetch new replies if there's no ongoing remote replies check + const shouldAutoFetchReplies = + isDocumentVisible && partialLoadingState !== 'loading' && !wasDismissed; + + const autoFetchInterval = useMemo( + () => + getIsThreadNew(statusCreatedAt) + ? SHORT_AUTO_FETCH_REPLIES_INTERVAL + : LONG_AUTO_FETCH_REPLIES_INTERVAL, + [statusCreatedAt], + ); + + useInterval(debouncedFetchContext, { + delay: autoFetchInterval, + isEnabled: shouldAutoFetchReplies, + }); useEffect(() => { // Hide success message after a short delay @@ -172,7 +287,7 @@ export const RefreshController: React.FC<{ }; }, [dispatch, statusId]); - const handleClick = useCallback(() => { + const showPending = useCallback(() => { dispatch(showPendingReplies({ statusId })); setLoadingState('success'); }, [dispatch, statusId]); @@ -196,7 +311,7 @@ export const RefreshController: React.FC<{ isActive={loadingState === 'more-available'} message={intl.formatMessage(messages.moreFound)} action={intl.formatMessage(messages.show)} - onActionClick={handleClick} + onActionClick={showPending} onDismiss={dismissPrompt} animateFrom='below' /> @@ -205,7 +320,7 @@ export const RefreshController: React.FC<{ isActive={loadingState === 'error'} message={intl.formatMessage(messages.error)} action={intl.formatMessage(messages.retry)} - onActionClick={handleClick} + onActionClick={showPending} onDismiss={dismissPrompt} animateFrom='below' /> diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx index b53ac40e510a54..9f42f3ed03d7a1 100644 --- a/app/javascript/mastodon/features/status/index.jsx +++ b/app/javascript/mastodon/features/status/index.jsx @@ -560,12 +560,14 @@ class Status extends ImmutablePureComponent { componentDidUpdate (prevProps) { const { status, ancestorsIds, descendantsIds, referencesIds } = this.props; - if (status && (ancestorsIds.length + referencesIds.length > prevProps.ancestorsIds.length + prevProps.referencesIds.length || prevProps.status?.get('id') !== status.get('id'))) { + const isSameStatus = status && (prevProps.status?.get('id') === status.get('id')); + + if (status && (ancestorsIds.length + referencesIds.length > prevProps.ancestorsIds.length + prevProps.referencesIds.length || !isSameStatus)) { this._scrollStatusIntoView(); } // Only highlight replies after the initial load - if (prevProps.descendantsIds.length) { + if (prevProps.descendantsIds.length && isSameStatus) { const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds); if (newRepliesIds.length) { @@ -631,14 +633,6 @@ class Status extends ImmutablePureComponent { const isLocal = status.getIn(['account', 'acct'], '').indexOf('@') === -1; const isIndexable = !status.getIn(['account', 'noindex']); - if (!isLocal) { - remoteHint = ( - - ); - } - const handlers = { reply: this.handleHotkeyReply, favourite: this.handleHotkeyFavourite, @@ -717,7 +711,12 @@ class Status extends ImmutablePureComponent { {descendants} - {remoteHint} + +
diff --git a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx index f260444265d27d..ae4c4ed4f7a38b 100644 --- a/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx +++ b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx @@ -14,7 +14,6 @@ import { IconButton } from 'mastodon/components/icon_button'; import InlineAccount from 'mastodon/components/inline_account'; import MediaAttachments from 'mastodon/components/media_attachments'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; -import emojify from 'mastodon/features/emoji/emoji'; import { EmojiHTML } from '@/mastodon/components/emoji/html'; import { CustomEmojiProvider } from '@/mastodon/components/emoji/context'; @@ -48,13 +47,8 @@ class CompareHistoryModal extends PureComponent { const { index, versions, language, onClose } = this.props; const currentVersion = versions.get(index); - const emojiMap = currentVersion.get('emojis').reduce((obj, emoji) => { - obj[`:${emoji.get('shortcode')}:`] = emoji.toJS(); - return obj; - }, {}); - - const content = emojify(currentVersion.get('content'), emojiMap); - const spoilerContent = emojify(escapeTextContentForBrowser(currentVersion.get('spoiler_text')), emojiMap); + const content = currentVersion.get('content'); + const spoilerContent = escapeTextContentForBrowser(currentVersion.get('spoiler_text')); const formattedDate = ; const formattedName = ; @@ -99,7 +93,7 @@ class CompareHistoryModal extends PureComponent { diff --git a/app/javascript/mastodon/features/ui/components/visibility_modal.tsx b/app/javascript/mastodon/features/ui/components/visibility_modal.tsx index 167325134fd1aa..e2348d67cde498 100644 --- a/app/javascript/mastodon/features/ui/components/visibility_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/visibility_modal.tsx @@ -1,4 +1,11 @@ -import { forwardRef, useCallback, useId, useMemo, useState } from 'react'; +import { + forwardRef, + useCallback, + useId, + useMemo, + useRef, + useState, +} from 'react'; import type { FC } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; @@ -21,6 +28,7 @@ import type { SelectItem } from '@/mastodon/components/dropdown_selector'; import { IconButton } from '@/mastodon/components/icon_button'; import { messages as privacyMessages } from '@/mastodon/features/compose/components/privacy_dropdown'; import { enabledVisibilites } from '@/mastodon/initial_state'; +import { isUserTouching } from '@/mastodon/is_mobile'; import { createAppSelector, useAppSelector } from '@/mastodon/store'; import CircleIcon from '@/material-icons/400-24px/account_circle.svg?react'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; @@ -420,6 +428,8 @@ export const VisibilityModal: FC = forwardRef( const circleLabelId = `${uniqueId}-circle-label`; const circleDescriptionId = `${uniqueId}-circle-desc`; + const saveRef = useRef(null); + return (
@@ -477,6 +487,8 @@ export const VisibilityModal: FC = forwardRef( descriptionId={visibilityDescriptionId} classPrefix='visibility-dropdown' disabled={disableVisibility} + placement={isUserTouching() ? 'top' : 'bottom-start'} + target={isUserTouching() ? saveRef : undefined} /> {!!statusId && (

= forwardRef(

; - } else if (trendsEnabled && trendsAsLanding) { + } else if (trendsEnabled && landingPage === 'trends') { redirect = ; + } else if (localLiveFeedAccess === 'public' && landingPage === 'local_feed') { + redirect = ; } else { redirect = ; } @@ -423,11 +424,6 @@ class UI extends PureComponent { window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('resize', this.handleResize, { passive: true }); - if (!autoPlayGif) { - window.addEventListener('mouseover', handleAnimateGif, { passive: true }); - window.addEventListener('mouseout', handleAnimateGif, { passive: true }); - } - document.addEventListener('dragenter', this.handleDragEnter, false); document.addEventListener('dragover', this.handleDragOver, false); document.addEventListener('drop', this.handleDrop, false); @@ -453,8 +449,6 @@ class UI extends PureComponent { window.removeEventListener('blur', this.handleWindowBlur); window.removeEventListener('beforeunload', this.handleBeforeUnload); window.removeEventListener('resize', this.handleResize); - window.removeEventListener('mouseover', handleAnimateGif); - window.removeEventListener('mouseout', handleAnimateGif); document.removeEventListener('dragenter', this.handleDragEnter); document.removeEventListener('dragover', this.handleDragOver); diff --git a/app/javascript/mastodon/hooks/useInterval.ts b/app/javascript/mastodon/hooks/useInterval.ts new file mode 100644 index 00000000000000..d80ef79d6b75f3 --- /dev/null +++ b/app/javascript/mastodon/hooks/useInterval.ts @@ -0,0 +1,39 @@ +import { useEffect, useLayoutEffect, useRef } from 'react'; + +/** + * Hook to create an interval that invokes a callback function + * at a specified delay using the setInterval API. + * Based on https://usehooks-ts.com/react-hook/use-interval + */ +export function useInterval( + callback: () => void, + { + delay, + isEnabled = true, + }: { + delay: number; + isEnabled?: boolean; + }, +) { + // Write callback to a ref so we can omit it from + // the interval effect's dependency array + const callbackRef = useRef(callback); + useLayoutEffect(() => { + callbackRef.current = callback; + }, [callback]); + + // Set up the interval. + useEffect(() => { + if (!isEnabled) { + return; + } + + const intervalId = setInterval(() => { + callbackRef.current(); + }, delay); + + return () => { + clearInterval(intervalId); + }; + }, [delay, isEnabled]); +} diff --git a/app/javascript/mastodon/hooks/useIsDocumentVisible.ts b/app/javascript/mastodon/hooks/useIsDocumentVisible.ts new file mode 100644 index 00000000000000..3587733dc2213a --- /dev/null +++ b/app/javascript/mastodon/hooks/useIsDocumentVisible.ts @@ -0,0 +1,32 @@ +import { useEffect, useRef, useState } from 'react'; + +export function useIsDocumentVisible({ + onChange, +}: { + onChange?: (isVisible: boolean) => void; +} = {}) { + const [isDocumentVisible, setIsDocumentVisible] = useState( + () => document.visibilityState === 'visible', + ); + + const onChangeRef = useRef(onChange); + useEffect(() => { + onChangeRef.current = onChange; + }, [onChange]); + + useEffect(() => { + function handleVisibilityChange() { + const isVisible = document.visibilityState === 'visible'; + + setIsDocumentVisible(isVisible); + onChangeRef.current?.(isVisible); + } + window.addEventListener('visibilitychange', handleVisibilityChange); + + return () => { + window.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, []); + + return isDocumentVisible; +} diff --git a/app/javascript/mastodon/hooks/useLinks.ts b/app/javascript/mastodon/hooks/useLinks.ts deleted file mode 100644 index 77609181be9954..00000000000000 --- a/app/javascript/mastodon/hooks/useLinks.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { useCallback } from 'react'; - -import { useHistory } from 'react-router-dom'; - -import { isFulfilled, isRejected } from '@reduxjs/toolkit'; - -import { openURL } from 'mastodon/actions/search'; -import { useAppDispatch } from 'mastodon/store'; - -import { isModernEmojiEnabled } from '../utils/environment'; - -const isMentionClick = (element: HTMLAnchorElement) => - element.classList.contains('mention') && - !element.classList.contains('hashtag'); - -const isHashtagClick = (element: HTMLAnchorElement) => - element.textContent.startsWith('#') || - element.previousSibling?.textContent?.endsWith('#'); - -export const useLinks = (skipHashtags?: boolean) => { - const history = useHistory(); - const dispatch = useAppDispatch(); - - const handleHashtagClick = useCallback( - (element: HTMLAnchorElement) => { - const { textContent } = element; - - if (!textContent) return; - - history.push(`/tags/${textContent.replace(/^#/, '')}`); - }, - [history], - ); - - const handleMentionClick = useCallback( - async (element: HTMLAnchorElement) => { - const result = await dispatch(openURL({ url: element.href })); - - if (isFulfilled(result)) { - if (result.payload.accounts[0]) { - history.push(`/@${result.payload.accounts[0].acct}`); - } else if (result.payload.statuses[0]) { - history.push( - `/@${result.payload.statuses[0].account.acct}/${result.payload.statuses[0].id}`, - ); - } else { - window.location.href = element.href; - } - } else if (isRejected(result)) { - window.location.href = element.href; - } - }, - [dispatch, history], - ); - - const handleClick = useCallback( - (e: React.MouseEvent) => { - // Exit early if modern emoji is enabled, as this is handled by HandledLink. - if (isModernEmojiEnabled()) { - return; - } - - const target = (e.target as HTMLElement).closest('a'); - - if (!target || e.button !== 0 || e.ctrlKey || e.metaKey) { - return; - } - - if (isMentionClick(target)) { - e.preventDefault(); - void handleMentionClick(target); - } else if (isHashtagClick(target) && !skipHashtags) { - e.preventDefault(); - handleHashtagClick(target); - } - }, - [skipHashtags, handleMentionClick, handleHashtagClick], - ); - - return handleClick; -}; diff --git a/app/javascript/mastodon/initial_state.ts b/app/javascript/mastodon/initial_state.ts index a2d576bd35c8fc..fe59662885d606 100644 --- a/app/javascript/mastodon/initial_state.ts +++ b/app/javascript/mastodon/initial_state.ts @@ -43,13 +43,13 @@ interface InitialStateMeta { single_user_mode: boolean; source_url: string; streaming_api_base_url: string; - local_live_feed_access: 'public' | 'authenticated'; - remote_live_feed_access: 'public' | 'authenticated'; - local_topic_feed_access: 'public' | 'authenticated'; - remote_topic_feed_access: 'public' | 'authenticated'; + local_live_feed_access: 'public' | 'authenticated' | 'disabled'; + remote_live_feed_access: 'public' | 'authenticated' | 'disabled'; + local_topic_feed_access: 'public' | 'authenticated' | 'disabled'; + remote_topic_feed_access: 'public' | 'authenticated' | 'disabled'; title: string; show_trends: boolean; - trends_as_landing_page: boolean; + landing_page: 'about' | 'trends' | 'local_feed'; use_blurhash: boolean; use_pending_items?: boolean; version: string; @@ -147,7 +147,7 @@ export const remoteLiveFeedAccess = getMeta('remote_live_feed_access'); export const localTopicFeedAccess = getMeta('local_topic_feed_access'); export const remoteTopicFeedAccess = getMeta('remote_topic_feed_access'); export const title = getMeta('title'); -export const trendsAsLanding = getMeta('trends_as_landing_page'); +export const landingPage = getMeta('landing_page'); export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 74ea49409b8c60..d257ed9f4dd03e 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -173,6 +173,8 @@ "column.edit_list": "Рэдагаваць спіс", "column.favourites": "Упадабанае", "column.firehose": "Стужкі", + "column.firehose_local": "Жывая стужка гэтага сервера", + "column.firehose_singular": "Жывая стужка", "column.follow_requests": "Запыты на падпіску", "column.home": "Галоўная", "column.list_members": "Кіраванне ўдзельнікамі спіса", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "У Вашых закладках яшчэ няма допісаў. Калі Вы дадасце закладку, яна з’явіцца тут.", "empty_column.community": "Мясцовая стужка пустая. Напішыце нешта публічнае, каб разварушыць справу!", "empty_column.direct": "Пакуль у Вас няма асабістых згадванняў. Калі Вы дашляце або атрымаеце штосьці, яно з’явіцца тут.", + "empty_column.disabled_feed": "Гэта стужка была адключаная Вашымі адміністратарамі сервера.", "empty_column.domain_blocks": "Заблакіраваных даменаў пакуль няма.", "empty_column.explore_statuses": "Зараз не ў трэндзе. Праверце пазней", "empty_column.favourited_statuses": "Вы яшчэ не ўпадабалі ніводны допіс. Калі гэта адбудзецца, Вы ўбачыце яго тут.", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 60256398848115..88df24cbb4a715 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -28,6 +28,7 @@ "account.disable_notifications": "Спиране на известяване при публикуване от @{name}", "account.domain_blocking": "Блокиране на домейн", "account.edit_profile": "Редактиране на профила", + "account.edit_profile_short": "Редактиране", "account.enable_notifications": "Известяване при публикуване от @{name}", "account.endorse": "Представи в профила", "account.familiar_followers_many": "Последвано от {name1}, {name2}, и {othersCount, plural, one {един друг, когото познавате} other {# други, които познавате}}", @@ -40,6 +41,9 @@ "account.featured_tags.last_status_never": "Няма публикации", "account.follow": "Последване", "account.follow_back": "Последване взаимно", + "account.follow_request_cancel": "Отказване на заявката", + "account.follow_request_cancel_short": "Отказ", + "account.follow_request_short": "Заявка", "account.followers": "Последователи", "account.followers.empty": "Още никой не следва потребителя.", "account.followers_counter": "{count, plural, one {{counter} последовател} other {{counter} последователи}}", @@ -238,6 +242,9 @@ "confirmations.missing_alt_text.secondary": "Все пак да се публикува", "confirmations.missing_alt_text.title": "Добавяте ли алтернативен текст?", "confirmations.mute.confirm": "Заглушаване", + "confirmations.quiet_post_quote_info.dismiss": "Без друго напомняне", + "confirmations.quiet_post_quote_info.got_it": "Схванах", + "confirmations.quiet_post_quote_info.title": "Цитиране на публикации за тиха публика", "confirmations.redraft.confirm": "Изтриване и преработване", "confirmations.redraft.message": "Наистина ли искате да изтриете тази публикация и да я направите чернова? Означаванията като любими и подсилванията ще се изгубят, а и отговорите към първоначалната публикация ще осиротеят.", "confirmations.redraft.title": "Изтривате и преработвате ли публикацията?", @@ -247,7 +254,11 @@ "confirmations.revoke_quote.confirm": "Премахване на публикация", "confirmations.revoke_quote.message": "Действието е неотменимо.", "confirmations.revoke_quote.title": "Премахвате ли публикацията?", + "confirmations.unblock.confirm": "Отблокиране", + "confirmations.unblock.title": "Отблокирате ли @{name}?", "confirmations.unfollow.confirm": "Без следване", + "confirmations.unfollow.title": "Спирате ли следване на {name}?", + "confirmations.withdraw_request.confirm": "Оттегляне на заявката", "content_warning.hide": "Скриване на публ.", "content_warning.show": "Нека се покаже", "content_warning.show_more": "Показване на още", @@ -442,10 +453,12 @@ "ignore_notifications_modal.private_mentions_title": "Пренебрегвате ли известия от непоискани лични споменавания?", "info_button.label": "Помощ", "info_button.what_is_alt_text": "

Какво е алтернативен текст?

Алтернативният текст осигурява описания на изображение за хора със зрителни увреждания, връзки с ниска честотна лента или търсещите допълнителен контекст.

Може да подобрите достъпността и разбираемостта за всеки, пишейки ясен, кратък и обективен алтернативен текст.

  • Уловете важните елементи
  • Обобщете текста в образите
  • Употребявайте правилна структура на изречението
  • Избягвайте излишна информация
  • Съсредоточете се върху тенденциите и ключови констатации в сложни онагледявания (като диаграми и карти)
", + "interaction_modal.action": "Трябва да влезете с акаунта си, в който и да е сървър на Mastodon, когото използвате, за да взаимодействате с публикация на {name}.", "interaction_modal.go": "Напред", "interaction_modal.no_account_yet": "Още ли нямате акаунт?", "interaction_modal.on_another_server": "На различен сървър", "interaction_modal.on_this_server": "На този сървър", + "interaction_modal.title": "Влезте, за да продължите", "interaction_modal.username_prompt": "Напр. {example}", "intervals.full.days": "{number, plural, one {# ден} other {# дни}}", "intervals.full.hours": "{number, plural, one {# час} other {# часа}}", @@ -596,6 +609,7 @@ "notification.moderation_warning.action_suspend": "Вашият акаунт е спрян.", "notification.own_poll": "Анкетата ви приключи", "notification.poll": "Анкета, в която гласувахте, приключи", + "notification.quoted_update": "{name} редактира публикация, която цитирахте", "notification.reblog": "{name} подсили ваша публикация", "notification.reblog.name_and_others_with_link": "{name} и {count, plural, one {# друг} other {# други}} подсилиха ваша публикация", "notification.relationships_severance_event": "Изгуби се връзката с {name}", @@ -715,10 +729,17 @@ "privacy.private.short": "Последователи", "privacy.public.long": "Всеки във и извън Mastodon", "privacy.public.short": "Публично", + "privacy.quote.anyone": "{visibility}, всеки може да цитира", + "privacy.quote.disabled": "{visibility}, цитатите са изключени", + "privacy.quote.limited": "{visibility}, цитатите са ограничени", "privacy.unlisted.additional": "Това действие е точно като публичното, с изключение на това, че публикацията няма да се появява в каналите на живо, хаштаговете, разглеждането или търсенето в Mastodon, дори ако сте избрали да се публично видими на ниво акаунт.", "privacy.unlisted.short": "Тиха публика", "privacy_policy.last_updated": "Последно осъвременяване на {date}", "privacy_policy.title": "Политика за поверителност", + "quote_error.edit": "Не може да се добавят цитати, редайтирайки публикация.", + "quote_error.poll": "Не може да се цитира при анкетиране.", + "quote_error.unauthorized": "Нямате право да цитирате тази публикация.", + "quote_error.upload": "Цитирането не е позволено с мултимедийни прикачвания.", "recommended": "Препоръчано", "refresh": "Опресняване", "regeneration_indicator.please_stand_by": "Изчакайте.", @@ -734,6 +755,8 @@ "relative_time.minutes": "{number}м.", "relative_time.seconds": "{number}с.", "relative_time.today": "днес", + "remove_quote_hint.button_label": "Схванах", + "remove_quote_hint.message": "Може да го направите от менюто възможности {icon}.", "reply_indicator.attachments": "{count, plural, one {# прикаване} other {# прикачвания}}", "reply_indicator.cancel": "Отказ", "reply_indicator.poll": "Анкета", @@ -825,13 +848,22 @@ "status.admin_account": "Отваряне на интерфейс за модериране за @{name}", "status.admin_domain": "Отваряне на модериращия интерфейс за {domain}", "status.admin_status": "Отваряне на публикацията в модериращия интерфейс", + "status.all_disabled": "Подсилването и цитатите са изключени", "status.block": "Блокиране на @{name}", "status.bookmark": "Отмятане", "status.cancel_reblog_private": "Край на подсилването", + "status.cannot_quote": "Не е позволено да цитирате тази публикация", "status.cannot_reblog": "Публикацията не може да се подсилва", + "status.context.loading": "Зареждане на още отговори", + "status.context.loading_error": "Не можаха да се заредят нови отговори", + "status.context.loading_success": "Новите отговори заредени", + "status.context.more_replies_found": "Още намерени отговори", + "status.context.retry": "Друг опит", + "status.context.show": "Показване", "status.continued_thread": "Продължена нишка", "status.copy": "Копиране на връзката към публикация", "status.delete": "Изтриване", + "status.delete.success": "Публикацията е изтрита", "status.detailed_status": "Подробен изглед на разговора", "status.direct": "Частно споменаване на @{name}", "status.direct_indicator": "Частно споменаване", @@ -855,23 +887,32 @@ "status.open": "Разширяване на публикацията", "status.pin": "Закачане в профила", "status.quote_error.filtered": "Скрито поради един от филтрите ви", + "status.quote_error.limited_account_hint.title": "Този акаунт е бил скрит от модераторите на {domain}.", "status.quote_error.not_available": "Неналична публикация", "status.quote_error.pending_approval": "Публикацията чака одобрение", + "status.quote_error.revoked": "Премахната публикация от автора", + "status.quote_followers_only": "Само последователи могат да цитират тази публикация", + "status.quote_manual_review": "Авторът ще преглежда ръчно", "status.quote_policy_change": "Промяна кой може да цитира", "status.quote_post_author": "Цитирах публикация от @{name}", + "status.quote_private": "Частните публикации не може да се цитират", "status.read_more": "Още за четене", "status.reblog": "Подсилване", + "status.reblog_or_quote": "Подсилване или цитиране", + "status.reblog_private": "Споделете пак с последователите си", "status.reblogged_by": "{name} подсили", "status.reblogs": "{count, plural, one {подсилване} other {подсилвания}}", "status.reblogs.empty": "Още никого не е подсилвал публикацията. Подсилващият ще се покаже тук.", "status.redraft": "Изтриване и преработване", "status.remove_bookmark": "Премахване на отметката", "status.remove_favourite": "Премахване от любими", + "status.remove_quote": "Премахване", "status.replied_in_thread": "Отговорено в нишката", "status.replied_to": "В отговор до {name}", "status.reply": "Отговор", "status.replyAll": "Отговор на нишка", "status.report": "Докладване на @{name}", + "status.request_quote": "Заявка за цитиране", "status.revoke_quote": "Премахване на моя публикация от публикацията на @{name}", "status.sensitive_warning": "Деликатно съдържание", "status.share": "Споделяне", @@ -910,6 +951,7 @@ "upload_button.label": "Добавете файл с образ, видео или звук", "upload_error.limit": "Превишено ограничението за качване на файлове.", "upload_error.poll": "Качването на файлове не е позволено с анкети.", + "upload_error.quote": "Цитирайки, не може да качвате файл.", "upload_form.drag_and_drop.instructions": "Натиснете интервал или enter, за да подберете мултимедийно прикачване. Провлачвайки, ползвайте клавишите със стрелки, за да премествате мултимедията във всяка дадена посока. Натиснете пак интервал или enter, за да се стовари мултимедийното прикачване в новото си положение или натиснете Esc за отмяна.", "upload_form.drag_and_drop.on_drag_cancel": "Провлачването е отменено. Мултимедийното прикачване {item} е спуснато.", "upload_form.drag_and_drop.on_drag_end": "Мултимедийното прикачване {item} е спуснато.", @@ -935,6 +977,13 @@ "video.volume_up": "Увеличаване на звука", "visibility_modal.button_title": "Задаване на видимост", "visibility_modal.header": "Видимост и взаимодействие", + "visibility_modal.helper.privacy_editing": "Видимостта не може да се променя след публикуване на публикацията.", + "visibility_modal.helper.privacy_private_self_quote": "Самоцитирането на частни публикации не може да се огласява публично.", + "visibility_modal.instructions": "Управлява кой може да взаимодейства с тази публикация. Може също да приложите настройките за всички бъдещи публикации, навигирайки към Предпочитания > Публикуване по подразбиране.", + "visibility_modal.privacy_label": "Видимост", "visibility_modal.quote_followers": "Само последователи", - "visibility_modal.quote_public": "Някой" + "visibility_modal.quote_label": "Кой може да цитира", + "visibility_modal.quote_nobody": "Само аз", + "visibility_modal.quote_public": "Някой", + "visibility_modal.save": "Запазване" } diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 5eccf71746ac78..3531a64227e6ed 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Zatím v záložkách nemáte žádné příspěvky. Až si do nich nějaký přidáte, zobrazí se zde.", "empty_column.community": "Místní časová osa je prázdná. Napište něco veřejně a rozhýbejte to tu!", "empty_column.direct": "Zatím nemáte žádné soukromé zmínky. Až nějakou pošlete nebo dostanete, zobrazí se zde.", + "empty_column.disabled_feed": "Tento kanál byl zakázán administrátory vašeho serveru.", "empty_column.domain_blocks": "Ještě nemáte žádné zablokované domény.", "empty_column.explore_statuses": "Momentálně není nic populární. Vraťte se později!", "empty_column.favourited_statuses": "Zatím nemáte žádné oblíbené příspěvky. Až si nějaký oblíbíte, zobrazí se zde.", @@ -876,6 +877,7 @@ "status.contains_quote": "Obsahuje citaci", "status.context.loading": "Načítání dalších odpovědí", "status.context.loading_error": "Nelze načíst nové odpovědi", + "status.context.loading_success": "Nové odpovědi načteny", "status.context.more_replies_found": "Nalezeny další odpovědi", "status.context.retry": "Zkusit znovu", "status.context.show": "Zobrazit", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 57119f0b1bb9bb..614348d36f5b11 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Does gennych chi ddim unrhyw bostiad wedi'u cadw fel nod tudalen eto. Pan fyddwch yn gosod nod tudalen i un, mi fydd yn ymddangos yma.", "empty_column.community": "Mae'r ffrwd lleol yn wag. Beth am ysgrifennu rhywbeth cyhoeddus!", "empty_column.direct": "Does gennych chi unrhyw grybwylliadau preifat eto. Pan fyddwch chi'n anfon neu'n derbyn un, bydd yn ymddangos yma.", + "empty_column.disabled_feed": "Mae'r ffrwd hon wedi'i hanalluogi gan weinyddwyr eich gweinydd.", "empty_column.domain_blocks": "Does dim parthau wedi'u rhwystro eto.", "empty_column.explore_statuses": "Does dim pynciau llosg ar hyn o bryd. Dewch nôl nes ymlaen!", "empty_column.favourited_statuses": "Rydych chi heb ffafrio unrhyw bostiadau eto. Pan byddwch chi'n ffafrio un, bydd yn ymddangos yma.", @@ -753,6 +754,7 @@ "privacy.unlisted.short": "Tewi'r cyhoeddus", "privacy_policy.last_updated": "Diweddarwyd ddiwethaf ar {date}", "privacy_policy.title": "Polisi Preifatrwydd", + "quote_error.edit": "Does dim modd ychwanegu dyfyniadau wrth olygu postiad.", "quote_error.poll": "Dyw dyfynnu ddim yn cael ei ganiatáu gyda pholau.", "quote_error.quote": "Dim ond un dyfyniad ar y tro sy'n cael ei ganiatáu.", "quote_error.unauthorized": "Does gennych chi ddim awdurdod i ddyfynnu'r postiad hwn.", @@ -875,6 +877,7 @@ "status.contains_quote": "Yn cynnwys dyfyniad", "status.context.loading": "Yn llwytho mwy o atebion", "status.context.loading_error": "Wedi methu llwytho atebion newydd", + "status.context.loading_success": "Atebion newydd wedi'u llwytho", "status.context.more_replies_found": "Mwy o atebion wedi'u canfod", "status.context.retry": "Ceisio eto", "status.context.show": "Dangos", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 8d3e5247430e5c..0e7733196ed59e 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -173,6 +173,8 @@ "column.edit_list": "Redigér liste", "column.favourites": "Favoritter", "column.firehose": "Live feeds", + "column.firehose_local": "Live feed for denne server", + "column.firehose_singular": "Live feed", "column.follow_requests": "Følgeanmodninger", "column.home": "Hjem", "column.list_members": "Håndtér listemedlemmer", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Du har ingen bogmærkede indlæg endnu. Når du bogmærker ét, vil det dukke op hér.", "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at sætte tingene i gang!", "empty_column.direct": "Du har ikke nogen private omtaler endnu. Når du sender eller modtager en, vil den blive vist her.", + "empty_column.disabled_feed": "Dette feed er blevet deaktiveret af dine serveradministratorer.", "empty_column.domain_blocks": "Ingen blokerede domæner endnu.", "empty_column.explore_statuses": "Ingen nye trends lige nu. Tjek igen senere!", "empty_column.favourited_statuses": "Du har endnu ingen favoritindlæg. Når du føjer et opslag til favoritter, vil det dukke op her.", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 5e65e8adc47bc0..86826091fafa46 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Du hast bisher keine Beiträge als Lesezeichen abgelegt. Sobald du einen Beitrag als Lesezeichen speicherst, wird er hier erscheinen.", "empty_column.community": "Die lokale Timeline ist leer. Schreibe einen öffentlichen Beitrag, um den Stein ins Rollen zu bringen!", "empty_column.direct": "Du hast noch keine privaten Erwähnungen. Sobald du eine sendest oder erhältst, wird sie hier erscheinen.", + "empty_column.disabled_feed": "Diesen Feed haben deine Server-Administrator*innen deaktiviert.", "empty_column.domain_blocks": "Du hast noch keine Domains blockiert.", "empty_column.explore_statuses": "Momentan ist nichts im Trend. Schau später wieder vorbei!", "empty_column.favourited_statuses": "Du hast noch keine Beiträge favorisiert. Sobald du einen favorisierst, wird er hier erscheinen.", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 8dc9e85e829dc5..62e3467a93c030 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -173,6 +173,8 @@ "column.edit_list": "Επεξεργασία λίστας", "column.favourites": "Αγαπημένα", "column.firehose": "Ζωντανές ροές", + "column.firehose_local": "Ζωντανή ροή για αυτόν τον διακομιστή", + "column.firehose_singular": "Ζωντανή ροή", "column.follow_requests": "Αιτήματα ακολούθησης", "column.home": "Αρχική", "column.list_members": "Διαχείριση μελών λίστας", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Δεν έχεις καμία ανάρτηση με σελιδοδείκτη ακόμα. Μόλις βάλεις κάποιον, θα εμφανιστεί εδώ.", "empty_column.community": "Η τοπική ροή είναι κενή. Γράψε κάτι δημόσια για να αρχίσει να κυλά η μπάλα!", "empty_column.direct": "Δεν έχεις καμία προσωπική επισήμανση ακόμα. Όταν στείλεις ή λάβεις μία, θα εμφανιστεί εδώ.", + "empty_column.disabled_feed": "Αυτή η ροή έχει απενεργοποιηθεί από τους διαχειριστές του διακομιστή σας.", "empty_column.domain_blocks": "Δεν υπάρχουν αποκλεισμένοι τομείς ακόμα.", "empty_column.explore_statuses": "Τίποτα δεν βρίσκεται στις τάσεις αυτή τη στιγμή. Έλεγξε αργότερα!", "empty_column.favourited_statuses": "Δεν έχεις καμία αγαπημένη ανάρτηση ακόμα. Μόλις αγαπήσεις κάποια, θα εμφανιστεί εδώ.", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index f965262609de31..d86d0d471b81c3 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -246,6 +246,8 @@ "bundle_modal_error.close": "Close", "bundle_modal_error.message": "Something went wrong while loading this screen.", "bundle_modal_error.retry": "Try again", + "carousel.current": "Slide {current, number} / {max, number}", + "carousel.slide": "Slide {current, number} of {max, number}", "circles.add_member": "Add", "circles.add_to_circle": "Add to circle", "circles.add_to_circles": "Add {name} to circles", @@ -297,6 +299,8 @@ "column.emoji_reactions": "Emoji Reactions", "column.favourites": "Favorites", "column.firehose": "Live feeds", + "column.firehose_local": "Live feed for this server", + "column.firehose_singular": "Live feed", "column.follow_requests": "Follow requests", "column.home": "Home", "column.list_members": "Manage list members", @@ -476,6 +480,7 @@ "empty_column.circle_statuses": "You don't have any circle posts yet. When you post one as circle, it will show up here.", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.", + "empty_column.disabled_feed": "This feed has been disabled by your server administrators.", "empty_column.domain_blocks": "There are no blocked domains yet.", "empty_column.emoji_reacted_statuses": "You don't have any emoji reacted posts yet. When you emoji react one, it will show up here.", "empty_column.emoji_reactions": "No one has reacted with emoji this post yet. When someone does, they will show up here.", @@ -504,11 +509,9 @@ "explore.trending_links": "News", "explore.trending_statuses": "Posts", "explore.trending_tags": "Hashtags", + "featured_carousel.current": "Post {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}", - "featured_carousel.next": "Next", - "featured_carousel.post": "Post", - "featured_carousel.previous": "Previous", - "featured_carousel.slide": "{index} of {total}", + "featured_carousel.slide": "Post {current, number} of {max, number}", "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", @@ -1118,9 +1121,12 @@ "status.pin": "Pin on profile", "status.quote": "Quote", "status.quote.cancel": "Cancel quote", + "status.quote_error.blocked_account_hint.title": "This post is hidden because you've blocked @{name}.", + "status.quote_error.blocked_domain_hint.title": "This post is hidden because you've blocked {domain}.", "status.quote_error.filtered": "Hidden due to one of your filters", "status.quote_error.limited_account_hint.action": "Show anyway", "status.quote_error.limited_account_hint.title": "This account has been hidden by the moderators of {domain}.", + "status.quote_error.muted_account_hint.title": "This post is hidden because you've muted @{name}.", "status.quote_error.not_available": "Post unavailable", "status.quote_error.pending_approval": "Post pending", "status.quote_error.pending_approval_popout.body": "On Mastodon, you can control whether someone can quote you. This post is pending while we're getting the original author's approval.", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index e5f79b23336c66..bc07edbd7bdc60 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -173,6 +173,8 @@ "column.edit_list": "Editar lista", "column.favourites": "Favoritos", "column.firehose": "Líneas temporales en vivo", + "column.firehose_local": "Línea temporal en vivo para este servidor", + "column.firehose_singular": "Línea temporal en vivo", "column.follow_requests": "Solicitudes de seguimiento", "column.home": "Principal", "column.list_members": "Administrar miembros de la lista", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Todavía no tenés mensajes guardados en \"Marcadores\". Cuando guardés uno en \"Marcadores\", se mostrará acá.", "empty_column.community": "La línea temporal local está vacía. ¡Escribí algo en modo público para que se empiece a correr la bola!", "empty_column.direct": "Todavía no tenés ninguna mención privada. Cuando enviés o recibás una, se mostrará acá.", + "empty_column.disabled_feed": "Esta línea temporal fue deshabilitada por los administradores de tu servidor.", "empty_column.domain_blocks": "Todavía no hay dominios bloqueados.", "empty_column.explore_statuses": "No hay nada en tendencia ahora mismo. ¡Volvé a revisar más tarde!", "empty_column.favourited_statuses": "Todavía no tenés mensajes favoritos. Cuando marqués uno como favorito, se mostrará acá.", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 26171460b699ab..b340851d0f47c1 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -173,6 +173,8 @@ "column.edit_list": "Editar lista", "column.favourites": "Favoritos", "column.firehose": "Cronologías", + "column.firehose_local": "Cronología para este servidor", + "column.firehose_singular": "Cronología", "column.follow_requests": "Solicitudes de seguimiento", "column.home": "Inicio", "column.list_members": "Administrar miembros de la lista", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Aún no tienes ninguna publicación guardada como marcador. Cuando guardes una, se mostrará aquí.", "empty_column.community": "La cronología local está vacía. ¡Escribe algo públicamente para ponerla en marcha!", "empty_column.direct": "Aún no tienes menciones privadas. Cuando envíes o recibas una, aparecerán aquí.", + "empty_column.disabled_feed": "Esta cronología ha sido desactivada por los administradores del servidor.", "empty_column.domain_blocks": "Todavía no hay dominios ocultos.", "empty_column.explore_statuses": "Nada es tendencia en este momento. ¡Revisa más tarde!", "empty_column.favourited_statuses": "Todavía no tienes publicaciones favoritas. Cuando le des favorito a una publicación se mostrarán acá.", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index e8bd6e294dda12..77d0f9f93acf7b 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -173,6 +173,8 @@ "column.edit_list": "Editar lista", "column.favourites": "Favoritos", "column.firehose": "Cronologías", + "column.firehose_local": "Cronología para este servidor", + "column.firehose_singular": "Cronología", "column.follow_requests": "Solicitudes de seguimiento", "column.home": "Inicio", "column.list_members": "Administrar miembros de la lista", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Aún no tienes ninguna publicación guardada como marcador. Cuando guardes una, se mostrará aquí.", "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!", "empty_column.direct": "Aún no tienes menciones privadas. Cuando envíes o recibas una, aparecerán aquí.", + "empty_column.disabled_feed": "Esta cronología ha sido desactivada por los administradores del servidor.", "empty_column.domain_blocks": "Todavía no hay dominios bloqueados.", "empty_column.explore_statuses": "No hay nada en tendencia en este momento. ¡Revisa más tarde!", "empty_column.favourited_statuses": "Todavía no tienes publicaciones favoritas. Cuando marques una publicación como favorita, se mostrarán aquí.", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index ff190a6ed2fc7b..f777e47943dcfb 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Järjehoidjatesse pole veel lisatud postitusi. Kui lisad mõne, näed neid siin.", "empty_column.community": "Kohalik ajajoon on tühi. Kirjuta midagi avalikult, et pall veerema ajada!", "empty_column.direct": "Sul pole veel ühtegi privaatset mainimist. Kui saadad või saad mõne, ilmuvad need siin.", + "empty_column.disabled_feed": "See infovoog on serveri peakasutajate poolt välja lülitatud.", "empty_column.domain_blocks": "Siin ei ole veel peidetud domeene.", "empty_column.explore_statuses": "Praegu pole ühtegi trendi. Tule hiljem tagasi!", "empty_column.favourited_statuses": "Pole veel lemmikpostitusi. Kui märgid mõne, näed neid siin.", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 94fd2f982a5086..3e73a6e75dac22 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -3,7 +3,7 @@ "about.contact": "Yhteydenotto:", "about.default_locale": "Oletus", "about.disclaimer": "Mastodon on vapaa avoimen lähdekoodin ohjelmisto ja Mastodon gGmbH:n tavaramerkki.", - "about.domain_blocks.no_reason_available": "Syy ei ole tiedossa", + "about.domain_blocks.no_reason_available": "Syytä ei ole ilmoitettu", "about.domain_blocks.preamble": "Mastodonin avulla voi yleensä tarkastella minkä tahansa fediversumiin kuuluvan palvelimen sisältöä ja olla yhteyksissä eri palvelinten käyttäjien kanssa. Nämä poikkeukset koskevat yksin tätä palvelinta.", "about.domain_blocks.silenced.explanation": "Et yleensä näe tämän palvelimen profiileja ja sisältöä, jollet erityisesti etsi juuri sitä tai liity siihen seuraamalla.", "about.domain_blocks.silenced.title": "Rajoitettu", @@ -173,6 +173,8 @@ "column.edit_list": "Muokkaa listaa", "column.favourites": "Suosikit", "column.firehose": "Livesyötteet", + "column.firehose_local": "Tämän palvelimen livesyöte", + "column.firehose_singular": "Livesyöte", "column.follow_requests": "Seurantapyynnöt", "column.home": "Koti", "column.list_members": "Hallitse listan jäseniä", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Et ole vielä lisännyt julkaisuja kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.", "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista, niin homma lähtee käyntiin!", "empty_column.direct": "Yksityismainintoja ei vielä ole. Jos lähetät tai sinulle lähetetään sellaisia, näet ne täällä.", + "empty_column.disabled_feed": "Palvelimen ylläpito on poistanut käytöstä tämän syötteen.", "empty_column.domain_blocks": "Verkkotunnuksia ei ole vielä estetty.", "empty_column.explore_statuses": "Mikään ei ole nyt suosittua. Tarkista myöhemmin uudelleen!", "empty_column.favourited_statuses": "Sinulla ei ole vielä yhtään suosikkijulkaisua. Kun lisäät sellaisen, näkyy se tässä.", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index e8357bba4b3a55..3a407e7cb697c7 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Tú hevur enn einki goymt uppslag. Tú tú goymir eitt uppslag, kemur tað her.", "empty_column.community": "Lokala tíðarlinjan er tóm. Skriva okkurt alment fyri at fáa boltin á rull!", "empty_column.direct": "Tú hevur ongar privatar umrøður enn. Tá tú sendir ella móttekur eina privata umrøðu, so verður hon sjónlig her.", + "empty_column.disabled_feed": "Hendan rásin er gjørd óvirkin av ambætaraumsitarunum hjá tær.", "empty_column.domain_blocks": "Enn eru eingi blokeraði domenir.", "empty_column.explore_statuses": "Einki rák er beint nú. Royn aftur seinni!", "empty_column.favourited_statuses": "Tú hevur ongar yndispostar enn. Tá tú gevur einum posti yndismerki, so sært tú hann her.", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 5d757f30da1848..0bf6beecdc0e10 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Níl aon phostáil leabharmharcaithe agat fós. Nuair a dhéanann tú leabharmharc, beidh sé le feiceáil anseo.", "empty_column.community": "Tá an amlíne áitiúil folamh. Foilsigh rud éigin go poiblí le tús a chur le cúrsaí!", "empty_column.direct": "Níl aon tagairtí príobháideacha agat fós. Nuair a sheolann tú nó a gheobhaidh tú ceann, beidh sé le feiceáil anseo.", + "empty_column.disabled_feed": "Tá an fotha seo díchumasaithe ag riarthóirí do fhreastalaí.", "empty_column.domain_blocks": "Níl aon fearainn bhactha ann go fóill.", "empty_column.explore_statuses": "Níl rud ar bith ag treochtáil faoi láthair. Tar ar ais ar ball!", "empty_column.favourited_statuses": "Níl aon postálacha is fearr leat fós. Nuair is fearr leat ceann, beidh sé le feiceáil anseo.", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index b677f8ec50ab18..00d5629dfecb80 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -28,6 +28,7 @@ "account.disable_notifications": "Na cuir brath thugam tuilleadh nuair a chuireas @{name} post ris", "account.domain_blocking": "Àrainn ’ga bacadh", "account.edit_profile": "Deasaich a’ phròifil", + "account.edit_profile_short": "Deasaich", "account.enable_notifications": "Cuir brath thugam nuair a chuireas @{name} post ris", "account.endorse": "Brosnaich air a’ phròifil", "account.familiar_followers_many": "’Ga leantainn le {name1}, {name2}, and {othersCount, plural, one {# eile air a bheil thu eòlach} other {# eile air a bheil thu eòlach}}", @@ -40,6 +41,11 @@ "account.featured_tags.last_status_never": "Gun phost", "account.follow": "Lean", "account.follow_back": "Lean air ais", + "account.follow_back_short": "Lean air ais", + "account.follow_request": "Iarr leantainn", + "account.follow_request_cancel": "Sguir dhen iarrtas", + "account.follow_request_cancel_short": "Sguir dheth", + "account.follow_request_short": "Iarr", "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} other {{counter} luchd-leantainn}}", @@ -125,7 +131,7 @@ "annual_report.summary.new_posts.new_posts": "postaichean ùra", "annual_report.summary.percentile.text": "Tha thu am measgdhen luchd-cleachdaidh as cliùitiche air {domain}.", "annual_report.summary.percentile.we_wont_tell_bernie": "Ainmeil ’nad latha ’s ’nad linn.", - "annual_report.summary.thanks": "Mòran taing airson conaltradh air Mastodon.", + "annual_report.summary.thanks": "Mòran taing airson conaltradh air Mastodon!", "attachments_list.unprocessed": "(gun phròiseasadh)", "audio.hide": "Falaich an fhuaim", "block_modal.remote_users_caveat": "Iarraidh sinn air an fhrithealaiche {domain} gun gèill iad ri do cho-dhùnadh. Gidheadh, chan eil barantas gun gèill iad on a làimhsicheas cuid a fhrithealaichean bacaidhean air dòigh eadar-dhealaichte. Dh’fhaoidte gum faic daoine gun chlàradh a-steach na postaichean poblach agad fhathast.", @@ -251,7 +257,12 @@ "confirmations.revoke_quote.confirm": "Thoir am post air falbh", "confirmations.revoke_quote.message": "Cha ghabh seo a neo-dhèanamh.", "confirmations.revoke_quote.title": "A bheil thu airson am post a thoirt air falbh?", + "confirmations.unblock.confirm": "Dì-bhac", + "confirmations.unblock.title": "A bheil thu airson {name} a dhì-bhacadh?", "confirmations.unfollow.confirm": "Na lean tuilleadh", + "confirmations.unfollow.title": "A bheil thu airson sgur de {name} a leantainn?", + "confirmations.withdraw_request.confirm": "Cuir d’ iarrtas dhan dàrna taobh", + "confirmations.withdraw_request.title": "A bheil thu airson d’ iarrtas gus {name} a leantainn a chur dhan dàrna taobh?", "content_warning.hide": "Falaich am post", "content_warning.show": "Seall e co-dhiù", "content_warning.show_more": "Seall barrachd dheth", @@ -734,14 +745,15 @@ "privacy.private.short": "Luchd-leantainn", "privacy.public.long": "Duine sam bith taobh a-staigh no a-muigh Mhastodon", "privacy.public.short": "Poblach", - "privacy.quote.anyone": "{visibility}, faodaidh neach sam bith a luaidh", - "privacy.quote.disabled": "{visibility}, luaidhean à comas", - "privacy.quote.limited": "{visibility}, luaidhean cuingichte", + "privacy.quote.anyone": "{visibility}, luaidhidh neach sam bith e", + "privacy.quote.disabled": "{visibility}, luaidh à comas", + "privacy.quote.limited": "{visibility}, luaidh cuingichte", "privacy.unlisted.additional": "Tha seo coltach ris an fhaicsinneachd phoblach ach cha nochd am post air loidhnichean-ama an t-saoghail phoblaich, nan tagaichean hais no an rùrachaidh no ann an toraidhean luirg Mhastodon fiù ’s ma thug thu ro-aonta airson sin seachad.", "privacy.unlisted.long": "Falaichte o na toraidhean-luirg, na treandaichean ’s na loichnichean-ama poblach", "privacy.unlisted.short": "Poblach ach sàmhach", "privacy_policy.last_updated": "An t-ùrachadh mu dheireadh {date}", "privacy_policy.title": "Poileasaidh prìobhaideachd", + "quote_error.edit": "Chan urrainn dhut luaidh a chur ris nuair a bhios tu ri deasachadh puist.", "quote_error.poll": "Chan fhaod thu luaidh a chur an cois cunntais-bheachd.", "quote_error.quote": "Chan eil taic ach ri aon luaidh aig an aon àm.", "quote_error.unauthorized": "Chan fhaod thu am post seo a luaidh.", @@ -861,6 +873,13 @@ "status.cancel_reblog_private": "Na brosnaich tuilleadh", "status.cannot_quote": "Chan fhaod thu am post seo a luaidh", "status.cannot_reblog": "Cha ghabh am post seo brosnachadh", + "status.contains_quote": "Tha luaidh na bhroinn", + "status.context.loading": "A’ luchdadh barrachd fhreagairtean", + "status.context.loading_error": "Cha b’ urrainn dhuinn nam freagairtean ùra a luchdadh", + "status.context.loading_success": "Chaidh na freagairtean ùra a luchdadh", + "status.context.more_replies_found": "Fhuair sinn lorg air barrachd fhreagairtean", + "status.context.retry": "Feuch ris a-rithist", + "status.context.show": "Seall", "status.continued_thread": "Pàirt de shnàithlean", "status.copy": "Dèan lethbhreac dhen cheangal dhan phost", "status.delete": "Sguab às", @@ -890,17 +909,22 @@ "status.quote": "Luaidh", "status.quote.cancel": "Sguir dhen luaidh", "status.quote_error.filtered": "Falaichte le criathrag a th’ agad", + "status.quote_error.limited_account_hint.action": "Seall e co-dhiù", + "status.quote_error.limited_account_hint.title": "Chaidh an cunntas seo fhalach le maoir {domain}.", "status.quote_error.not_available": "Chan eil am post ri fhaighinn", "status.quote_error.pending_approval": "Cha deach dèiligeadh ris a’ phost fhathast", "status.quote_error.pending_approval_popout.body": "Air Mastodon, ’s urrainn dhut stiùireadh am faod cuideigin do luaidh gus nach fhaod. Tha am post seo a’ feitheamh air aonta an ùghdair thùsail.", "status.quote_error.revoked": "Chaidh am post a thoirt air falbh leis an ùghdar", "status.quote_followers_only": "Chan fhaod ach luchd-leantainn am post seo a luaidh", "status.quote_manual_review": "Nì an t-ùghdar lèirmheas air a làimh", + "status.quote_noun": "Luaidh", "status.quote_policy_change": "Atharraich cò dh’fhaodas luaidh", "status.quote_post_author": "Luaidh air post le @{name}", "status.quote_private": "Chan fhaodar postaichean prìobhaideach a luaidh", "status.quotes": "{count, plural, one {luaidh} two {luaidh} few {luaidhean} other {luaidh}}", "status.quotes.empty": "Chan deach am post seo a luaidh le duine sam bith fhathast. Nuair a luaidheas cuideigin e, nochdaidh iad an-seo.", + "status.quotes.local_other_disclaimer": "Cha tèid luaidhean a dhiùilt an ùghdar a shealltainn.", + "status.quotes.remote_other_disclaimer": "Cha dèid ach luaidhean o {domain} a shealltainn an-seo le cinnt. Cha dèid luaidhean a dhiùilt an ùghdar a shealltainn.", "status.read_more": "Leugh an còrr", "status.reblog": "Brosnaich", "status.reblog_or_quote": "Brosnaich no luaidh", @@ -987,11 +1011,11 @@ "visibility_modal.helper.privacy_private_self_quote": "Chan fhaodar fèin-luaidhean air postaichean prìobhaideach a dhèanamh poblach.", "visibility_modal.helper.private_quoting": "Chan urrainn do chàch postaichean dhan luchd-leantainn a-mhàin a chaidh a sgrìobhadh le Mastodon a luaidh.", "visibility_modal.helper.unlisted_quoting": "Nuair a luaidheas daoine thu, thèid am post aca-san fhalach o loidhnichean-ama nan treandaichean.", - "visibility_modal.instructions": "Stiùirich cò dh’fhaodas eadar-ghabhahil leis a’ phost seo. ’S urrainn dhut do roghainnean airson nam postaichean ri teachd a thaghadh aig Roghainnean > Bun-roghainnean a’ phostaidh", + "visibility_modal.instructions": "Stiùirich cò dh’fhaodas eadar-ghabhahil leis a’ phost seo. ’S urrainn dhut do roghainnean airson nam postaichean ri teachd a thaghadh aig Roghainnean > Bun-roghainnean a’ phostaidh.", "visibility_modal.privacy_label": "Faicsinneachd", "visibility_modal.quote_followers": "Luchd-leantainn a-mhàin", "visibility_modal.quote_label": "Cò dh’fhaodas luaidh", "visibility_modal.quote_nobody": "Mi fhìn a-mhàin", - "visibility_modal.quote_public": "Duine sam bith", + "visibility_modal.quote_public": "Neach sam bith", "visibility_modal.save": "Sàbhail" } diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 668c3e5ba40238..6ce9544cb19051 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Aínda non marcaches ningunha publicación. Cando o fagas, aparecerán aquí.", "empty_column.community": "A cronoloxía local está baleira. Escribe algo de xeito público para espallalo!", "empty_column.direct": "Aínda non tes mencións privadas. Cando envíes ou recibas unha, aparecerá aquí.", + "empty_column.disabled_feed": "A administración do teu servidor desactivou esta canle.", "empty_column.domain_blocks": "Aínda non hai dominios agochados.", "empty_column.explore_statuses": "Non hai temas en voga. Volve máis tarde!", "empty_column.favourited_statuses": "Aínda non tes publicacións favoritas. Cando favorezas unha, aparecerá aquí.", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 3aa0cf900a8096..47f4ee91497563 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -173,6 +173,8 @@ "column.edit_list": "עריכת רשימה", "column.favourites": "חיבובים", "column.firehose": "פידים עדכניים", + "column.firehose_local": "פיד זמן האמת עבור שרת זה", + "column.firehose_singular": "פיד זמן אמת", "column.follow_requests": "בקשות מעקב", "column.home": "פיד הבית", "column.list_members": "ניהול חברי הרשימה", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "אין עדיין הודעות שחיבבת. כשתחבב את הראשונה, היא תופיע כאן.", "empty_column.community": "פיד השרת המקומי ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!", "empty_column.direct": "אין לך שום הודעות פרטיות עדיין. כשתשלחו או תקבלו אחת, היא תופיע כאן.", + "empty_column.disabled_feed": "פיד זה נחסם לשימוש על ידי מנהלי השרת שלך.", "empty_column.domain_blocks": "אין עדיין קהילות מוסתרות.", "empty_column.explore_statuses": "אין נושאים חמים כרגע. אולי אחר כך!", "empty_column.favourited_statuses": "אין עדיין הודעות שחיבבת. כשתחבב/י את הראשונה, היא תופיע כאן.", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 053e5d92c46854..67c9dcddc7a9b1 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Még nincs egyetlen könyvjelzőzött bejegyzésed sem. Ha könyvjelzőzöl egyet, itt fog megjelenni.", "empty_column.community": "A helyi idővonal üres. Tégy közzé valamit nyilvánosan, hogy elindítsd az eseményeket!", "empty_column.direct": "Még nincs egy személyes említésed sem. Küldéskor vagy fogadáskor itt fognak megjelenni.", + "empty_column.disabled_feed": "A kiszolgálód rendszergazdái letiltották ezt a hírfolyamot.", "empty_column.domain_blocks": "Még nem lett letiltva egyetlen domain sem.", "empty_column.explore_statuses": "Jelenleg semmi sem felkapott. Nézz vissza később!", "empty_column.favourited_statuses": "Még nincs egyetlen kedvenc bejegyzésed sem. Ha kedvencnek jelölsz egyet, itt fog megjelenni.", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index cd735bbe929275..1b5aaa39bf7fd6 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -172,7 +172,7 @@ "column.domain_blocks": "Dominios blocate", "column.edit_list": "Modificar lista", "column.favourites": "Favorites", - "column.firehose": "Fluxos in directo", + "column.firehose": "Fluxos in vivo", "column.follow_requests": "Requestas de sequimento", "column.home": "Initio", "column.list_members": "Gerer le membros del lista", @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Tu non ha ancora messages in marcapaginas. Quando tu adde un message al marcapaginas, illo apparera hic.", "empty_column.community": "Le chronologia local es vacue. Scribe qualcosa public pro poner le cosas in marcha!", "empty_column.direct": "Tu non ha ancora mentiones private. Quando tu invia o recipe un mention, illo apparera hic.", + "empty_column.disabled_feed": "Iste canal ha essite disactivate per le adminsistratores de tu servitor.", "empty_column.domain_blocks": "Il non ha dominios blocate ancora.", "empty_column.explore_statuses": "Il non ha tendentias in iste momento. Reveni plus tarde!", "empty_column.favourited_statuses": "Tu non ha alcun message favorite ancora. Quando tu marca un message como favorite, illo apparera hic.", @@ -460,7 +461,7 @@ "ignore_notifications_modal.not_following_title": "Ignorar notificationes de personas que tu non seque?", "ignore_notifications_modal.private_mentions_title": "Ignorar notificationes de mentiones private non requestate?", "info_button.label": "Adjuta", - "info_button.what_is_alt_text": "

Que es texto alternative?

Le texto alternative forni descriptiones de imagines a personas con impedimentos visual, con connexiones lente, o qui cerca contexto additional.

Tu pote meliorar le accessibilitate e le comprension pro totes scribente un texto alternative clar, concise e objective.

  • Captura le elementos importante
  • Summarisa texto in imagines
  • Usa le structura de phrase normal
  • Evita information redundante
  • In figuras complexe (como diagrammas o mappas), concentra te sur le tendentias e punctos clave
", + "info_button.what_is_alt_text": "

Que es texto alternative?

Le texto alternative forni descriptiones de imagines a personas con impedimentos visual, con connexiones lente a internet, o qui cerca contexto supplementari.

Tu pote meliorar le accessibilitate e le comprension pro totes si tu scribe un texto alternative clar, concise e objective.

  • Captura le elementos importante
  • Summarisa texto in imagines
  • Usa un structura conventional de phrases
  • Evita information redundante
  • In figuras complexe (como diagrammas o mappas), concentra te sur le tendentias e punctos clave
", "interaction_modal.action": "Pro interager con le message de {name}, tu debe acceder a tu conto sur le servitor Mastodon que tu usa.", "interaction_modal.go": "Revenir", "interaction_modal.no_account_yet": "Tu non ha ancora un conto?", @@ -574,8 +575,8 @@ "navigation_bar.follows_and_followers": "Sequites e sequitores", "navigation_bar.import_export": "Importar e exportar", "navigation_bar.lists": "Listas", - "navigation_bar.live_feed_local": "Canal in directo (local)", - "navigation_bar.live_feed_public": "Canal in directo (public)", + "navigation_bar.live_feed_local": "Canal in vivo (local)", + "navigation_bar.live_feed_public": "Canal in vivo (public)", "navigation_bar.logout": "Clauder session", "navigation_bar.moderation": "Moderation", "navigation_bar.more": "Plus", @@ -748,7 +749,7 @@ "privacy.quote.anyone": "{visibility}, omnes pote citar", "privacy.quote.disabled": "{visibility}, citation disactivate", "privacy.quote.limited": "{visibility}, citation limitate", - "privacy.unlisted.additional": "Isto es exactemente como public, excepte que le message non apparera in fluxos in directo, in hashtags, in Explorar, o in le recerca de Mastodon, mesmo si tu ha optate pro render tote le conto discoperibile.", + "privacy.unlisted.additional": "Isto es exactemente como public, excepte que le message non apparera in fluxos in vivo, in hashtags, in Explorar, o in le recerca de Mastodon, mesmo si tu ha optate pro render tote le conto discoperibile.", "privacy.unlisted.long": "Non apparera in le resultatos de recerca, tendentias e chronologias public de Mastodon", "privacy.unlisted.short": "Public, non listate", "privacy_policy.last_updated": "Ultime actualisation {date}", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 98fac141ab8f59..3f43cfb897e8b4 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -173,6 +173,8 @@ "column.edit_list": "Breyta lista", "column.favourites": "Eftirlæti", "column.firehose": "Bein streymi", + "column.firehose_local": "Beint streymi á þessum netþjóni", + "column.firehose_singular": "Beint streymi", "column.follow_requests": "Beiðnir um að fylgjast með", "column.home": "Heim", "column.list_members": "Sýsla með meðlimi listans", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Þú ert ekki ennþá með neinar bókamerktar færslur. Þegar þú bókamerkir færslu, mun það birtast hér.", "empty_column.community": "Staðværa tímalínan er tóm. Skrifaðu eitthvað opinberlega til að láta boltann fara að rúlla!", "empty_column.direct": "Þú ert ekki ennþá með neitt einkaspjall við neinn. Þegar þú sendir eða tekur við slíku, mun það birtast hér.", + "empty_column.disabled_feed": "Þetta streymi hefur verið gert óvirkt af stjórnendum netþjónis þíns.", "empty_column.domain_blocks": "Það eru ennþá engin útilokuð lén.", "empty_column.explore_statuses": "Ekkert er á uppleið í augnablikinu. Athugaðu aftur síðar!", "empty_column.favourited_statuses": "Þú ert ekki ennþá með neinar eftirlætisfærslur. Þegar þú setur færslu í eftirlæti, munu þau birtast hér.", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 07392e9f759e70..9e57f42fc96972 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Non hai ancora salvato nei segnalibri alcun post. Quando lo farai, apparirà qui.", "empty_column.community": "La cronologia locale è vuota. Scrivi qualcosa pubblicamente per dare inizio alla festa!", "empty_column.direct": "Non hai ancora alcuna menzione privata. Quando ne invierai o riceverai una, apparirà qui.", + "empty_column.disabled_feed": "Questo feed è stato disabilitato dagli amministratori del tuo server.", "empty_column.domain_blocks": "Ancora nessun dominio bloccato.", "empty_column.explore_statuses": "Nulla è in tendenza al momento. Ricontrolla più tardi!", "empty_column.favourited_statuses": "Non hai ancora alcun post preferito. Quando ne salverai uno tra i preferiti, apparirà qui.", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index d4c8cf1f250f2f..152061b557abba 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -28,6 +28,7 @@ "account.disable_notifications": "@{name} 의 게시물 알림 끄기", "account.domain_blocking": "도메인 차단함", "account.edit_profile": "프로필 편집", + "account.edit_profile_short": "수정", "account.enable_notifications": "@{name} 의 게시물 알림 켜기", "account.endorse": "프로필에 추천하기", "account.familiar_followers_many": "{name1}, {name2} 님 외 내가 아는 {othersCount, plural, other {#}} 명이 팔로우함", @@ -40,6 +41,11 @@ "account.featured_tags.last_status_never": "게시물 없음", "account.follow": "팔로우", "account.follow_back": "맞팔로우", + "account.follow_back_short": "맞팔로우", + "account.follow_request": "팔로우 요청", + "account.follow_request_cancel": "요청 취소", + "account.follow_request_cancel_short": "취소", + "account.follow_request_short": "요청", "account.followers": "팔로워", "account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.", "account.followers_counter": "{count, plural, other {팔로워 {counter}명}}", @@ -167,6 +173,8 @@ "column.edit_list": "리스트 편집", "column.favourites": "좋아요", "column.firehose": "실시간 피드", + "column.firehose_local": "이 서버에 대한 실시간 피드", + "column.firehose_singular": "실시간 피드", "column.follow_requests": "팔로우 요청", "column.home": "홈", "column.list_members": "리스트 구성원 관리", @@ -251,7 +259,12 @@ "confirmations.revoke_quote.confirm": "게시물 삭제", "confirmations.revoke_quote.message": "이 작업은 되돌릴 수 없습니다.", "confirmations.revoke_quote.title": "게시물을 지울까요?", + "confirmations.unblock.confirm": "차단 해제", + "confirmations.unblock.title": "{name} 님을 차단 해제할까요?", "confirmations.unfollow.confirm": "팔로우 해제", + "confirmations.unfollow.title": "{name} 님을 언팔로우 할까요?", + "confirmations.withdraw_request.confirm": "요청 삭제", + "confirmations.withdraw_request.title": "{name} 님에 대한 팔로우 요청을 취소할까요?", "content_warning.hide": "게시물 숨기기", "content_warning.show": "무시하고 보기", "content_warning.show_more": "더 보기", @@ -322,6 +335,7 @@ "empty_column.bookmarked_statuses": "아직 북마크에 저장한 게시물이 없습니다. 게시물을 북마크 지정하면 여기에 나타납니다.", "empty_column.community": "로컬 타임라인에 아무것도 없습니다. 아무거나 적어 보세요!", "empty_column.direct": "개인적인 멘션이 없습니다. 보내거나 받으면 여기에 표시됩니다.", + "empty_column.disabled_feed": "이 피드는 서버 관리자에 의해 비활성화되었습니다.", "empty_column.domain_blocks": "아직 차단한 도메인이 없습니다.", "empty_column.explore_statuses": "아직 유행하는 것이 없습니다. 나중에 다시 확인하세요!", "empty_column.favourited_statuses": "아직 좋아요한 게시물이 없습니다. 게시물을 좋아요 하면 여기에 나타납니다.", @@ -454,6 +468,7 @@ "interaction_modal.no_account_yet": "아직 계정이 없나요?", "interaction_modal.on_another_server": "다른 서버에", "interaction_modal.on_this_server": "이 서버에서", + "interaction_modal.title": "로그인해서 계속하기", "interaction_modal.username_prompt": "예시: {example}", "intervals.full.days": "{number} 일", "intervals.full.hours": "{number} 시간", @@ -740,6 +755,7 @@ "privacy.unlisted.short": "조용한 공개", "privacy_policy.last_updated": "{date}에 마지막으로 업데이트됨", "privacy_policy.title": "개인정보처리방침", + "quote_error.edit": "게시물을 수정하면서 인용을 추가할 수 없습니다.", "quote_error.poll": "인용과 투표를 함께 사용할 수 없습니다.", "quote_error.quote": "한 번의 인용만 허용됩니다.", "quote_error.unauthorized": "이 게시물을 인용할 권한이 없습니다.", @@ -857,9 +873,15 @@ "status.block": "@{name} 차단", "status.bookmark": "북마크", "status.cancel_reblog_private": "부스트 취소", - "status.cannot_quote": "인용을 비허용하는 게시물", + "status.cannot_quote": "인용을 비허용한 게시물", "status.cannot_reblog": "이 게시물은 부스트 할 수 없습니다", "status.contains_quote": "인용 포함", + "status.context.loading": "더 많은 답글 불러오는 중", + "status.context.loading_error": "새 답글을 불러올 수 없습니다", + "status.context.loading_success": "새 답글을 불러왔습니다", + "status.context.more_replies_found": "답글을 더 찾았습니다", + "status.context.retry": "재시도", + "status.context.show": "보기", "status.continued_thread": "이어지는 글타래", "status.copy": "게시물 링크 복사", "status.delete": "삭제", @@ -889,8 +911,11 @@ "status.quote": "인용", "status.quote.cancel": "인용 취소", "status.quote_error.filtered": "필터에 의해 가려짐", + "status.quote_error.limited_account_hint.action": "그냥 보기", + "status.quote_error.limited_account_hint.title": "이 계정은 {domain}의 중재자에 의해 숨겨진 상태입니다.", "status.quote_error.not_available": "게시물 사용 불가", "status.quote_error.pending_approval": "게시물 대기중", + "status.quote_error.revoked": "원작성자에 의해 게시물 삭제됨", "status.quote_followers_only": "팔로워만 인용할 수 있는 게시물", "status.quote_manual_review": "작성자가 직접 검토합니다", "status.quote_noun": "인용", @@ -909,6 +934,7 @@ "status.redraft": "지우고 다시 쓰기", "status.remove_bookmark": "북마크 삭제", "status.remove_favourite": "즐겨찾기에서 제거", + "status.remove_quote": "삭제", "status.replied_in_thread": "글타래에 답장", "status.replied_to": "{name} 님에게", "status.reply": "답장", @@ -979,6 +1005,8 @@ "video.volume_up": "음량 증가", "visibility_modal.button_title": "공개범위 설정", "visibility_modal.header": "공개범위와 반응", + "visibility_modal.helper.privacy_private_self_quote": "자신의 비공개 게시물을 공개 게시물로 인용할 수 없습니다.", + "visibility_modal.helper.private_quoting": "마스토돈에서 작성된 팔로워 전용 게시물은 다른 사용자가 인용할 수 없습니다.", "visibility_modal.helper.unlisted_quoting": "사람들에게 인용된 경우, 인용한 게시물도 유행 타임라인에서 감추게 됩니다.", "visibility_modal.instructions": "누가 이 게시물과 상호작용할 수 있는 지 제어합니다. 또한 환경설정 > 게시물 기본설정으로 이동해 향후 모든 게시물의 설정을 적용할 수 있습니다.", "visibility_modal.privacy_label": "공개 범위", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index b223288f5fc67c..6a0bda09357f59 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -31,6 +31,7 @@ "account.edit_profile_short": "Edita", "account.enable_notifications": "Avizame kuando @{name} publike", "account.endorse": "Avalia en profil", + "account.featured": "Avaliado", "account.featured.accounts": "Profiles", "account.featured.hashtags": "Etiketas", "account.featured_tags.last_status_at": "Ultima publikasyon de {date}", @@ -38,6 +39,8 @@ "account.follow": "Sige", "account.follow_back": "Sige tamyen", "account.follow_back_short": "Sige tambyen", + "account.follow_request": "Solisita segirle", + "account.follow_request_cancel": "Anula solisitud", "account.follow_request_cancel_short": "Anula", "account.follow_request_short": "Solisitud", "account.followers": "Suivantes", @@ -62,6 +65,7 @@ "account.mute_short": "Silensia", "account.muted": "Silensiado", "account.muting": "Silensyando", + "account.mutual": "Vos sigesh mutualmente", "account.no_bio": "No ay deskripsion.", "account.open_original_page": "Avre pajina orijnala", "account.posts": "Publikasyones", @@ -97,6 +101,7 @@ "alert.unexpected.title": "Atyo!", "alt_text_badge.title": "Teksto alternativo", "alt_text_modal.add_alt_text": "Adjusta teksto alternativo", + "alt_text_modal.add_text_from_image": "Adjusta teksto de imaje", "alt_text_modal.cancel": "Anula", "alt_text_modal.change_thumbnail": "Troka minyatura", "alt_text_modal.done": "Fecho", @@ -210,6 +215,7 @@ "confirmations.logout.message": "Estas siguro ke keres salir de tu kuento?", "confirmations.logout.title": "Salir?", "confirmations.missing_alt_text.confirm": "Adjusta teksto alternativo", + "confirmations.missing_alt_text.secondary": "Puvlika de todos modos", "confirmations.missing_alt_text.title": "Adjustar teksto alternativo?", "confirmations.mute.confirm": "Silensia", "confirmations.quiet_post_quote_info.got_it": "Entyendo", @@ -382,6 +388,7 @@ "hints.profiles.see_more_followers": "Ve mas suivantes en {domain}", "hints.profiles.see_more_follows": "Ve mas segidos en {domain}", "hints.profiles.see_more_posts": "Ve mas puvlikasyones en {domain}", + "home.column_settings.show_quotes": "Muestra sitas", "home.column_settings.show_reblogs": "Amostra repartajasyones", "home.column_settings.show_replies": "Amostra repuestas", "home.hide_announcements": "Eskonde pregones", @@ -397,8 +404,10 @@ "ignore_notifications_modal.private_mentions_title": "Ignorar avizos de mensyones privadas no solisitadas?", "info_button.label": "Ayuda", "interaction_modal.go": "Va", + "interaction_modal.no_account_yet": "Ainda no tienes kuento?", "interaction_modal.on_another_server": "En otro sirvidor", "interaction_modal.on_this_server": "En este sirvidor", + "interaction_modal.title": "Konektate para kontinuar", "interaction_modal.username_prompt": "Por enshemplo {example}", "intervals.full.days": "{number, plural, one {# diya} other {# diyas}}", "intervals.full.hours": "{number, plural, one {# ora} other {# oras}}", @@ -595,6 +604,7 @@ "onboarding.follows.done": "Fecho", "onboarding.follows.empty": "Malorozamente, no se pueden amostrar rezultados en este momento. Puedes aprovar uzar la bushkeda o navigar por la pajina de eksplorasyon para topar personas a las que segir, o aprovarlo de muevo mas tadre.", "onboarding.follows.search": "Bushka", + "onboarding.follows.title": "Sige personas para ampezar", "onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas", "onboarding.profile.discoverable_hint": "Kuando permites ke tu profil sea diskuvriravle en Mastodon, tus publikasyones podran apareser en rezultados de bushkedas i trendes i tu profil podra ser sujerido a personas kon intereses similares a los tuyos.", "onboarding.profile.display_name": "Nombre amostrado", @@ -631,6 +641,7 @@ "privacy_policy.title": "Politika de privasita", "recommended": "Rekomendado", "refresh": "Arefreska", + "regeneration_indicator.please_stand_by": "Aspera por favor.", "relative_time.days": "{number} d", "relative_time.full.days": "antes {number, plural, one {# diya} other {# diyas}}", "relative_time.full.hours": "antes {number, plural, one {# ora} other {# oras}}", @@ -733,8 +744,14 @@ "status.bookmark": "Marka", "status.cancel_reblog_private": "No repartaja", "status.cannot_reblog": "Esta publikasyon no se puede repartajar", + "status.contains_quote": "Kontriene sita", + "status.context.loading": "Kargando mas repuestas", + "status.context.loading_error": "No se pudieron kargar repuestas muevas", + "status.context.loading_success": "Muevas repuestas kargadas", + "status.context.more_replies_found": "Se toparon mas repuestas", "status.context.retry": "Reprova", "status.context.show": "Amostra", + "status.continued_thread": "Kontinuasion del filo", "status.copy": "Kopia atadijo de publikasyon", "status.delete": "Efasa", "status.delete.success": "Puvlikasyon kitada", @@ -744,6 +761,7 @@ "status.edit": "Edita", "status.edited": "Ultima edisyon: {date}", "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} vezes}}", + "status.embed": "Obtiene kodiche para enkrustar", "status.favourite": "Te plaze", "status.filter": "Filtra esta publikasyon", "status.history.created": "{name} kriyo {date}", @@ -760,9 +778,20 @@ "status.pin": "Fiksa en profil", "status.quote": "Sita", "status.quote.cancel": "Anula la sita", + "status.quote_error.limited_account_hint.action": "Amostra entanto", + "status.quote_error.limited_account_hint.title": "Este kuento fue eskondido por los moderadores de {domain}.", + "status.quote_error.not_available": "Puvlikasyon no desponivle", + "status.quote_error.pending_approval": "Puvlikasyon esta asperando", + "status.quote_error.revoked": "Puvlikasyon kitada por el otor", + "status.quote_followers_only": "Solo los suivantes pueden sitar esta puvlikasyon", "status.quote_noun": "Sita", + "status.quote_policy_change": "Troka ken puede sitar", + "status.quote_post_author": "Sito una puvlikasyon de @{name}", + "status.quote_private": "No se puede sitar puvlikasyones privadas", + "status.quotes": "{count, plural, one {sita} other {sitas}}", "status.read_more": "Melda mas", "status.reblog": "Repartaja", + "status.reblog_or_quote": "Repartaja o partaja", "status.reblogged_by": "{name} repartajo", "status.reblogs.empty": "Ainda nadie tiene repartajado esta publikasyon. Kuando algien lo aga, se amostrara aki.", "status.redraft": "Efasa i eskrive de muevo", @@ -774,6 +803,7 @@ "status.reply": "Arisponde", "status.replyAll": "Arisponde al filo", "status.report": "Raporta @{name}", + "status.request_quote": "Solisita sitasyon", "status.sensitive_warning": "Kontenido sensivle", "status.share": "Partaja", "status.show_less_all": "Amostra manko para todo", @@ -823,7 +853,12 @@ "video.pause": "Pauza", "video.play": "Reproduze", "video.unmute": "Desilensia", + "visibility_modal.button_title": "Konfigura la vizibilita", + "visibility_modal.header": "Vizibilita i enteraksyon", "visibility_modal.privacy_label": "Vizivilita", "visibility_modal.quote_followers": "Solo suivantes", + "visibility_modal.quote_label": "Ken puede sitar", + "visibility_modal.quote_nobody": "Solo yo", + "visibility_modal.quote_public": "Todos", "visibility_modal.save": "Guadra" } diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index ea5f7861445fa6..db021f52ebcc93 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -333,6 +333,7 @@ "empty_column.bookmarked_statuses": "Lí iáu無加添任何冊籤。Nā是lí加添冊籤,伊ē佇tsia顯示。", "empty_column.community": "本站時間線是空ê。緊來公開PO文oh!", "empty_column.direct": "Lí iáu無任何ê私人訊息。Nā是lí送á是收著私人訊息,ē佇tsia顯示。.", + "empty_column.disabled_feed": "Tsit ê feed已經hōo lí ê服侍器ê管理員停用。", "empty_column.domain_blocks": "Iáu無封鎖任何網域。", "empty_column.explore_statuses": "目前iáu無有流行ê趨勢,請sió等tsi̍t-ē,koh確認。", "empty_column.favourited_statuses": "Lí iáu無加添任何收藏 ê PO文。Nā是lí加收藏,伊ē佇tsia顯示。", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 98f0fdab057d18..fbc9a4ef2de31e 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -173,6 +173,8 @@ "column.edit_list": "Lijst bewerken", "column.favourites": "Favorieten", "column.firehose": "Openbare tijdlijnen", + "column.firehose_local": "Lokale tijdlijn", + "column.firehose_singular": "Openbare tijdlijn", "column.follow_requests": "Volgverzoeken", "column.home": "Start", "column.list_members": "Lijstleden beheren", @@ -333,6 +335,7 @@ "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 privéberichten. Wanneer je er een verstuurt of ontvangt, komen deze hier te staan.", + "empty_column.disabled_feed": "Deze tijdlijn is uitgeschakeld door je serverbeheerders.", "empty_column.domain_blocks": "Er zijn nog geen geblokkeerde servers.", "empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!", "empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je een bericht als favoriet markeert, valt deze hier te zien.", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index cdd109a7153a6e..549203306bc2e3 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -1,10 +1,12 @@ { "about.blocks": "Servidors moderats", "about.contact": "Contacte :", + "about.default_locale": "Per defaut", "about.disclaimer": "Mastodon es gratuit, un logicial libre e una marca de Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Rason pas disponibla", "about.domain_blocks.silenced.title": "Limitats", "about.domain_blocks.suspended.title": "Suspenduts", + "about.language_label": "Lenga", "about.not_available": "Aquesta informacion foguèt pas renduda disponibla sus aqueste servidor.", "about.powered_by": "Malhum social descentralizat propulsat per {mastodon}", "about.rules": "Règlas del servidor", @@ -16,17 +18,26 @@ "account.block_domain": "Tot amagar del domeni {domain}", "account.block_short": "Blocar", "account.blocked": "Blocat", + "account.blocking": "Blocatge", "account.cancel_follow_request": "Retirar la demanda d’abonament", "account.copy": "Copiar lo ligam del perfil", "account.direct": "Mencionar @{name} en privat", "account.disable_notifications": "Quitar de m’avisar quand @{name} publica quicòm", "account.edit_profile": "Modificar lo perfil", + "account.edit_profile_short": "Modificar", "account.enable_notifications": "M’avisar quand @{name} publica quicòm", "account.endorse": "Mostrar pel perfil", + "account.familiar_followers_many": "Seguit per {name1}, {name2} e {othersCount, plural, one {qualqu’un mai que coneissètz} other {# autras personas que coneissètz}}", + "account.familiar_followers_one": "Seguit per {name1}", + "account.familiar_followers_two": "Seguit per {name1} e {name2}", + "account.featured.accounts": "Perfils", + "account.featured.hashtags": "Etiquetas", "account.featured_tags.last_status_at": "Darrièra publicacion lo {date}", "account.featured_tags.last_status_never": "Cap de publicacion", "account.follow": "Sègre", "account.follow_back": "Sègre en retorn", + "account.follow_request_cancel": "Anullar la demanda", + "account.follow_request_cancel_short": "Anullar", "account.followers": "Seguidors", "account.followers.empty": "Degun sèc pas aqueste utilizaire pel moment.", "account.following": "Abonat", @@ -45,16 +56,20 @@ "account.mute_notifications_short": "Amudir las notificacions", "account.mute_short": "Amudir", "account.muted": "Mes en silenci", + "account.mutual": "Vos seguissètz", "account.no_bio": "Cap de descripcion pas fornida.", "account.open_original_page": "Dobrir la pagina d’origina", "account.posts": "Tuts", "account.posts_with_replies": "Tuts e responsas", + "account.remove_from_followers": "Tirar {name} dels seguidors", "account.report": "Senhalar @{name}", "account.requested_follow": "{name} a demandat a vos sègre", + "account.requests_to_follow_you": "Demanda a vos sègre", "account.share": "Partejar lo perfil a @{name}", "account.show_reblogs": "Mostrar los partatges de @{name}", "account.unblock": "Desblocar @{name}", "account.unblock_domain": "Desblocar {domain}", + "account.unblock_domain_short": "Desblocar", "account.unblock_short": "Desblocat", "account.unendorse": "Mostrar pas pel perfil", "account.unfollow": "Quitar de sègre", @@ -97,13 +112,18 @@ "column.blocks": "Personas blocadas", "column.bookmarks": "Marcadors", "column.community": "Flux public local", + "column.create_list": "Crear una lista", "column.direct": "Mencions privadas", "column.directory": "Percórrer los perfils", "column.domain_blocks": "Domenis resconduts", + "column.edit_list": "Modificar la lista", "column.favourites": "Favorits", "column.firehose": "Tuts en dirèct", + "column.firehose_local": "Fial en dirècte d’aqueste servidor", + "column.firehose_singular": "Fial en dirècte", "column.follow_requests": "Demandas d’abonament", "column.home": "Acuèlh", + "column.list_members": "Gestion dels membres de la lista", "column.lists": "Listas", "column.mutes": "Personas rescondudas", "column.notifications": "Notificacions", @@ -116,6 +136,7 @@ "column_header.pin": "Penjar", "column_header.show_settings": "Mostrar los paramètres", "column_header.unpin": "Despenjar", + "column_search.cancel": "Anullar", "community.column_settings.local_only": "Sonque local", "community.column_settings.media_only": "Solament los mèdias", "community.column_settings.remote_only": "Sonque alonhat", @@ -144,14 +165,28 @@ "confirmations.block.confirm": "Blocar", "confirmations.delete.confirm": "Escafar", "confirmations.delete.message": "Volètz vertadièrament escafar l’estatut ?", + "confirmations.delete.title": "Suprimir la publicacion ?", "confirmations.delete_list.confirm": "Suprimir", "confirmations.delete_list.message": "Volètz vertadièrament suprimir aquesta lista per totjorn ?", + "confirmations.delete_list.title": "Suprimir la lista ?", "confirmations.discard_edit_media.confirm": "Ignorar", "confirmations.logout.confirm": "Desconnexion", "confirmations.logout.message": "Volètz vertadièrament vos desconnectar ?", + "confirmations.missing_alt_text.confirm": "Apondre un tèxte alternatiu", + "confirmations.missing_alt_text.message": "Vòstra publicacion conten un mèdia sens tèxt alternatiu. L'apondon de descripcions ajuda a rendre vòstre contengut accessible a mai de monde.", + "confirmations.missing_alt_text.secondary": "Publicar malgrat tot", + "confirmations.missing_alt_text.title": "Apondre un tèxte alternatiu ?", "confirmations.mute.confirm": "Rescondre", + "confirmations.quiet_post_quote_info.dismiss": "Me remembrar pas mai", + "confirmations.quiet_post_quote_info.got_it": "Plan comprés", "confirmations.redraft.confirm": "Escafar & tornar formular", + "confirmations.revoke_quote.title": "Suprimir la publicacion ?", + "confirmations.unblock.confirm": "Desblocar", + "confirmations.unblock.title": "Desblocar {name} ?", "confirmations.unfollow.confirm": "Quitar de sègre", + "confirmations.unfollow.title": "Quitar de sègre {name} ?", + "content_warning.hide": "Amagar la publicacion", + "content_warning.show_more": "Ne veire mai", "conversation.delete": "Suprimir la conversacion", "conversation.mark_as_read": "Marcar coma legida", "conversation.open": "Veire la conversacion", @@ -206,6 +241,7 @@ "errors.unexpected_crash.copy_stacktrace": "Copiar las traças al quichapapièrs", "errors.unexpected_crash.report_issue": "Senhalar un problèma", "explore.suggested_follows": "Personas", + "explore.title": "Tendéncia", "explore.trending_links": "Novèlas", "explore.trending_statuses": "Publicacions", "explore.trending_tags": "Etiquetas", @@ -231,6 +267,7 @@ "footer.privacy_policy": "Politica de confidencialitat", "footer.source_code": "Veire lo còdi font", "footer.status": "Estat", + "footer.terms_of_service": "Condicions d’utilizacion", "generic.saved": "Enregistrat", "getting_started.heading": "Per començar", "hashtag.column_header.tag_mode.all": "e {additional}", @@ -246,6 +283,7 @@ "hashtag.counter_by_uses": "{count, plural, one {{counter} tut} other {{counter} tuts}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} tut} other {{counter} tuts}} uèi", "hashtag.follow": "Sègre l’etiqueta", + "hashtag.mute": "Amudar #{hashtag}", "hashtag.unfollow": "Quitar de sègre l’etiqueta", "hashtags.and_other": "…e {count, plural, one {}other {# de mai}}", "home.column_settings.show_reblogs": "Mostrar los partatges", @@ -253,6 +291,7 @@ "home.hide_announcements": "Rescondre las anóncias", "home.pending_critical_update.link": "Veire las mesas a jorn", "home.show_announcements": "Mostrar las anóncias", + "info_button.label": "Ajuda", "interaction_modal.on_another_server": "Sus un autre servidor", "interaction_modal.on_this_server": "Sus aqueste servidor", "intervals.full.days": "{number, plural, one {# jorn} other {# jorns}}", @@ -292,21 +331,29 @@ "keyboard_shortcuts.toot": "començar un estatut tot novèl", "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca", "keyboard_shortcuts.up": "far montar dins la lista", + "learn_more_link.got_it": "Plan comprés", + "learn_more_link.learn_more": "Ne saber mai", "lightbox.close": "Tampar", "lightbox.next": "Seguent", "lightbox.previous": "Precedent", "limited_account_hint.action": "Afichar lo perfil de tota manièra", "limited_account_hint.title": "Aqueste perfil foguèt rescondut per la moderacion de {domain}.", "link_preview.author": "Per {name}", + "link_preview.more_from_author": "Mai de {name}", "lists.delete": "Suprimir la lista", "lists.edit": "Modificar la lista", "lists.replies_policy.followed": "Quin seguidor que siá", "lists.replies_policy.list": "Membres de la lista", "lists.replies_policy.none": "Degun", + "lists.save": "Enregistrar", + "lists.search": "Recercar", "load_pending": "{count, plural, one {# nòu element} other {# nòu elements}}", "loading_indicator.label": "Cargament…", + "media_gallery.hide": "Rescondre", "navigation_bar.about": "A prepaus", + "navigation_bar.administration": "Administracion", "navigation_bar.advanced_interface": "Dobrir l’interfàcia web avançada", + "navigation_bar.automated_deletion": "Supression auto de las publicacions", "navigation_bar.blocks": "Personas blocadas", "navigation_bar.bookmarks": "Marcadors", "navigation_bar.direct": "Mencions privadas", @@ -316,19 +363,32 @@ "navigation_bar.follow_requests": "Demandas d’abonament", "navigation_bar.followed_tags": "Etiquetas seguidas", "navigation_bar.follows_and_followers": "Abonament e seguidors", + "navigation_bar.import_export": "Import e export", "navigation_bar.lists": "Listas", "navigation_bar.logout": "Desconnexion", + "navigation_bar.moderation": "Moderacion", + "navigation_bar.more": "Mai", "navigation_bar.mutes": "Personas rescondudas", "navigation_bar.preferences": "Preferéncias", "navigation_bar.search": "Recercar", + "navigation_bar.search_trends": "Recèrca / Tendéncia", "not_signed_in_indicator.not_signed_in": "Devètz vos connectar per accedir a aquesta ressorsa.", "notification.admin.report": "{name} senhalèt {target}", "notification.admin.sign_up": "{name} se marquèt", "notification.favourite": "{name} a mes vòstre estatut en favorit", "notification.follow": "{name} vos sèc", "notification.follow_request": "{name} a demandat a vos sègre", + "notification.label.mention": "Mencion", + "notification.label.private_mention": "Mencion privada", + "notification.label.private_reply": "Responsa privada", + "notification.label.quote": "{name} a citat vòstra publicacion", + "notification.label.reply": "Respondre", + "notification.mention": "Mencionar", + "notification.mentioned_you": "{name} vos a mencionat", + "notification.moderation-warning.learn_more": "Ne saber mai", "notification.own_poll": "Vòstre sondatge es acabat", "notification.reblog": "{name} a partejat vòstre estatut", + "notification.relationships_severance_event.learn_more": "Ne saber mai", "notification.status": "{name} ven de publicar", "notification.update": "{name} modiquè sa publicacion", "notifications.clear": "Escafar", @@ -379,7 +439,12 @@ "poll_button.add_poll": "Ajustar un sondatge", "poll_button.remove_poll": "Levar lo sondatge", "privacy.change": "Ajustar la confidencialitat del messatge", + "privacy.direct.long": "Sonque los qu’avètz mencionats dins la publicacion", + "privacy.direct.short": "Mencion privada", + "privacy.private.long": "Mostrar pas qu’als seguidors", + "privacy.private.short": "Seguidors", "privacy.public.short": "Public", + "privacy.unlisted.short": "Public silenciós", "privacy_policy.last_updated": "Darrièra actualizacion {date}", "privacy_policy.title": "Politica de confidencialitat", "refresh": "Actualizar", @@ -423,6 +488,7 @@ "report.target": "Senhalar {target}", "report.thanks.title": "Volètz pas veire aquò ?", "report.unfollow": "Quitar de sègre {name}", + "report.unfollow_explanation": "Seguissètz aqueste compte. Per veire pas pus lors publicacions dins vòstra cronologia, quitatz de lo sègre.", "report_notification.attached_statuses": "{count, plural, one {{count} publicacion junta} other {{count} publicacions juntas}}", "report_notification.categories.other": "Autre", "report_notification.categories.spam": "Messatge indesirable", @@ -454,6 +520,9 @@ "status.bookmark": "Marcador", "status.cancel_reblog_private": "Quitar de partejar", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", + "status.context.loading": "Cargament de mai de responsas", + "status.context.more_replies_found": "Mai de responsas trobadas", + "status.continued_thread": "Seguida del fial", "status.copy": "Copiar lo ligam de l’estatut", "status.delete": "Escafar", "status.detailed_status": "Vista detalhada de la convèrsa", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index d121bea1ae1a94..e56369233f54a0 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -20,7 +20,7 @@ "account.block": "Blokuj @{name}", "account.block_domain": "Blokuj wszystko z {domain}", "account.block_short": "Zablokuj", - "account.blocked": "Zablokowany(-a)", + "account.blocked": "Zablokowano", "account.blocking": "Blokowanie", "account.cancel_follow_request": "Nie obserwuj", "account.copy": "Skopiuj link do profilu", @@ -28,11 +28,12 @@ "account.disable_notifications": "Przestań powiadamiać mnie o wpisach @{name}", "account.domain_blocking": "Blokowanie domeny", "account.edit_profile": "Edytuj profil", + "account.edit_profile_short": "Edytuj", "account.enable_notifications": "Powiadamiaj mnie o wpisach @{name}", "account.endorse": "Wyróżnij na profilu", - "account.familiar_followers_many": "Obserwowane przez: {name1}, {name2} i {othersCount, plural, one {jeszcze jedną osobę, którą znasz} few {# inne osoby, które znasz} many {# innych osób, które znasz} other {# innych osób, które znasz}}", - "account.familiar_followers_one": "Obserwowane przez {name1}", - "account.familiar_followers_two": "Obserwowane przez {name1} i {name2}", + "account.familiar_followers_many": "To konto jest obserwowane przez {name1}, {name2} i {othersCount, plural, one {jedną inną znaną ci osobę} few {# inne znane ci osoby} many {# innych znanych ci osób} other {# innych znanych ci osób}}", + "account.familiar_followers_one": "To konto jest obserwowane przez {name1}", + "account.familiar_followers_two": "To konto jest obserwowane przez {name1} i {name2}", "account.featured": "Wyróżnione", "account.featured.accounts": "Profile", "account.featured.hashtags": "Tagi", @@ -40,10 +41,15 @@ "account.featured_tags.last_status_never": "Brak postów", "account.follow": "Obserwuj", "account.follow_back": "Również obserwuj", + "account.follow_back_short": "Również obserwuj", + "account.follow_request": "Poproś o zgodę na obserwowanie", + "account.follow_request_cancel": "Anuluj", + "account.follow_request_cancel_short": "Anuluj", + "account.follow_request_short": "Poproś", "account.followers": "Obserwujący", "account.followers.empty": "Nikt jeszcze nie obserwuje tego użytkownika.", "account.followers_counter": "{count, plural, one {{counter} obserwujący} few {{counter} obserwujących} many {{counter} obserwujących} other {{counter} obserwujących}}", - "account.followers_you_know_counter": "{counter} które znasz", + "account.followers_you_know_counter": "znasz {counter}", "account.following": "Obserwowani", "account.following_counter": "{count, plural, one {{counter} obserwowany} few {{counter} obserwowanych} many {{counter} obserwowanych} other {{counter} obserwowanych}}", "account.follows.empty": "Ten użytkownik nie obserwuje jeszcze nikogo.", @@ -62,15 +68,15 @@ "account.mute_notifications_short": "Wycisz powiadomienia", "account.mute_short": "Wycisz", "account.muted": "Wyciszony", - "account.muting": "Wyciszenie", - "account.mutual": "Obserwujecie siebie nazwajem", + "account.muting": "Wyciszanie", + "account.mutual": "Obserwujecie się wzajemnie", "account.no_bio": "Brak opisu.", "account.open_original_page": "Otwórz stronę oryginalną", "account.posts": "Wpisy", "account.posts_with_replies": "Wpisy i odpowiedzi", "account.remove_from_followers": "Usuń {name} z obserwujących", "account.report": "Zgłoś @{name}", - "account.requested_follow": "{name} chce cię zaobserwować", + "account.requested_follow": "{name} chce cię obserwować", "account.requests_to_follow_you": "Prośby o obserwowanie", "account.share": "Udostępnij profil @{name}", "account.show_reblogs": "Pokazuj podbicia od @{name}", @@ -85,8 +91,8 @@ "account.unmute_notifications_short": "Nie wyciszaj powiadomień", "account.unmute_short": "Nie wyciszaj", "account_note.placeholder": "Kliknij, aby dodać notatkę", - "admin.dashboard.daily_retention": "Wskaźnik utrzymania użytkowników po dniach od rejestracji", - "admin.dashboard.monthly_retention": "Wskaźnik utrzymania użytkowników po miesiącach od rejestracji", + "admin.dashboard.daily_retention": "Wskaźnik utrzymania użytkowników według dni od rejestracji", + "admin.dashboard.monthly_retention": "Wskaźnik utrzymania użytkowników według miesięcy od rejestracji", "admin.dashboard.retention.average": "Średnia", "admin.dashboard.retention.cohort": "Miesiąc rejestracji", "admin.dashboard.retention.cohort_size": "Nowi użytkownicy", @@ -111,10 +117,10 @@ "annual_report.summary.archetype.lurker": "Czyhający", "annual_report.summary.archetype.oracle": "Wyrocznia", "annual_report.summary.archetype.pollster": "Ankieter", - "annual_report.summary.archetype.replier": "Motyl społeczny", + "annual_report.summary.archetype.replier": "Towarzyski motyl", "annual_report.summary.followers.followers": "obserwujących", "annual_report.summary.followers.total": "łącznie {count}", - "annual_report.summary.here_it_is": "Oto przegląd Twojego {year} roku:", + "annual_report.summary.here_it_is": "Oto przegląd twojego {year} roku:", "annual_report.summary.highlighted_post.by_favourites": "najbardziej lubiany wpis", "annual_report.summary.highlighted_post.by_reblogs": "najczęściej podbijany wpis", "annual_report.summary.highlighted_post.by_replies": "wpis z największą liczbą komentarzy", @@ -187,7 +193,7 @@ "community.column_settings.media_only": "Tylko multimedia", "community.column_settings.remote_only": "Tylko zdalne", "compose.language.change": "Zmień język", - "compose.language.search": "Szukaj języków...", + "compose.language.search": "Wyszukaj języki...", "compose.published.body": "Wpis został opublikowany.", "compose.published.open": "Otwórz", "compose.saved.body": "Wpis został zapisany.", @@ -220,14 +226,14 @@ "confirmations.delete_list.title": "Usunąć listę?", "confirmations.discard_draft.confirm": "Odrzuć i kontynuuj", "confirmations.discard_draft.edit.cancel": "Wznów edytowanie", - "confirmations.discard_draft.edit.message": "Kontynuowanie spowoduje utratę wszystkich zmian wprowadzonych przez Ciebie w aktualnie edytowanym poście.", - "confirmations.discard_draft.edit.title": "Odrzucić zmiany w poście?", - "confirmations.discard_draft.post.cancel": "Wznów wersję roboczą", - "confirmations.discard_draft.post.message": "Kontynuacja odrzuci aktualnie tworzony post.", - "confirmations.discard_draft.post.title": "Anulować wersję roboczą?", + "confirmations.discard_draft.edit.message": "Kontynuowanie spowoduje utratę wszelkich zmian wprowadzonych w aktualnie edytowanym wpisie.", + "confirmations.discard_draft.edit.title": "Czy chcesz odrzucić zmiany w swoim wpisie?", + "confirmations.discard_draft.post.cancel": "Wznów szkic", + "confirmations.discard_draft.post.message": "Kontynuowanie spowoduje usunięcie aktualnie tworzonego wpisu.", + "confirmations.discard_draft.post.title": "Odrzucić szkic wpisu?", "confirmations.discard_edit_media.confirm": "Odrzuć", - "confirmations.discard_edit_media.message": "Masz niezapisane zmiany w opisie lub podglądzie, odrzucić je mimo to?", - "confirmations.follow_to_list.confirm": "Zaobserwuj i dodaj do listy", + "confirmations.discard_edit_media.message": "Masz niezapisane zmiany w opisie lub podglądzie multimediów. Czy chcesz je mimo to odrzucić?", + "confirmations.follow_to_list.confirm": "Obserwuj i dodaj do listy", "confirmations.follow_to_list.message": "Musisz obserwować {name}, aby dodać do listy.", "confirmations.follow_to_list.title": "Zaobserwować?", "confirmations.logout.confirm": "Wyloguj", @@ -235,19 +241,28 @@ "confirmations.logout.title": "Wylogować?", "confirmations.missing_alt_text.confirm": "Dodaj opis pomocniczy", "confirmations.missing_alt_text.message": "Twój wpis zawiera multimedia bez tekstu alternatywnego. Dodanie opisów pomaga zwiększyć dostępność tych treści dla większej liczby osób.", - "confirmations.missing_alt_text.secondary": "Opublikuj mimo to ", + "confirmations.missing_alt_text.secondary": "Opublikuj mimo wszystko", "confirmations.missing_alt_text.title": "Dodać tekst pomocniczy?", "confirmations.mute.confirm": "Wycisz", + "confirmations.quiet_post_quote_info.dismiss": "Nie przypominaj mi ponownie", + "confirmations.quiet_post_quote_info.got_it": "Rozumiem", + "confirmations.quiet_post_quote_info.message": "Kiedy cytujesz niewidoczny wpis publiczny, twój wpis zostanie ukryty z popularnych osi czasu.", + "confirmations.quiet_post_quote_info.title": "Cytowanie niewidocznych wpisów publicznych", "confirmations.redraft.confirm": "Usuń i popraw", "confirmations.redraft.message": "Czy na pewno chcesz usunąć i poprawić ten wpis? Polubienia, podbicia i komentarze pierwotnego wpisu zostaną utracone.", "confirmations.redraft.title": "Usunąć i poprawić wpis?", - "confirmations.remove_from_followers.confirm": "Usuń obserwującego", - "confirmations.remove_from_followers.message": "{name} przestanie Cię obserwować. Czy na pewno chcesz kontynuować?", - "confirmations.remove_from_followers.title": "Usunąć obserwującego?", - "confirmations.revoke_quote.confirm": "Usuń post", - "confirmations.revoke_quote.message": "Tej akcji nie można cofnąć.", - "confirmations.revoke_quote.title": "Usuń post?", + "confirmations.remove_from_followers.confirm": "Usuń z obserwujących", + "confirmations.remove_from_followers.message": "{name} przestanie cię obserwować. Czy na pewno chcesz kontynuować?", + "confirmations.remove_from_followers.title": "Usunąć z obserwujących?", + "confirmations.revoke_quote.confirm": "Usuń wpis", + "confirmations.revoke_quote.message": "Tej czynności nie można cofnąć.", + "confirmations.revoke_quote.title": "Usunąć wpis?", + "confirmations.unblock.confirm": "Odblokuj", + "confirmations.unblock.title": "Odblokować {name}?", "confirmations.unfollow.confirm": "Nie obserwuj", + "confirmations.unfollow.title": "Przestać obserwować {name}?", + "confirmations.withdraw_request.confirm": "Wycofaj prośbę", + "confirmations.withdraw_request.title": "Wycofać prośbę o zgodę na obserwowanie {name}?", "content_warning.hide": "Ukryj wpis", "content_warning.show": "Pokaż mimo to", "content_warning.show_more": "Pokaż więcej", @@ -281,7 +296,7 @@ "domain_pill.server": "Serwer", "domain_pill.their_handle": "Nazwa:", "domain_pill.their_server": "Cyfrowy dom wszystkich wpisów tej osoby.", - "domain_pill.their_username": "Unikalny identyfikator na serwerze. Możliwe jest znalezienie użytkowników o tej samej nazwie użytkownika na różnych serwerach.", + "domain_pill.their_username": "Unikalny identyfikator na serwerze. Możliwe jest znalezienie użytkowników o tej samej nazwie na różnych serwerach.", "domain_pill.username": "Nazwa użytkownika", "domain_pill.whats_in_a_handle": "Z czego składa się nazwa?", "domain_pill.who_they_are": "Dzięki temu, że nazwy wskazują, kim ktoś jest i gdzie się znajduje, możesz wchodzić w interakcje z innymi z różnych .", @@ -303,12 +318,12 @@ "emoji_button.objects": "Obiekty", "emoji_button.people": "Ludzie", "emoji_button.recent": "Najczęściej używane", - "emoji_button.search": "Szukaj…", + "emoji_button.search": "Wyszukaj...", "emoji_button.search_results": "Wyniki wyszukiwania", "emoji_button.symbols": "Symbole", "emoji_button.travel": "Podróże i miejsca", - "empty_column.account_featured.me": "Niczego jeszcze nie poleciłeś. Czy wiesz, że możesz wyświetlać swoje hashtagi, z których korzystasz najbardziej, a nawet konta znajomego na swoim profilu?", - "empty_column.account_featured.other": "{acct} nie wyróżnił jeszcze nic. Czy wiesz, że możesz wyświetlać swoje hashtagi, z których korzystasz najbardziej, a nawet konta znajomego na swoim profilu?", + "empty_column.account_featured.me": "Nie dodano jeszcze żadnych polecanych treści. Czy wiesz, że możesz wyróżnić najczęściej używane hashtagi, a nawet konta znajomych na swoim profilu?", + "empty_column.account_featured.other": "Konto {acct} nie wyróżniło jeszcze żadnych treści. Czy wiesz, że możesz wyróżnić najczęściej używane hashtagi, a nawet konta znajomych w swoim profilu?", "empty_column.account_featured_other.unknown": "To konto nie zostało jeszcze wyróżnione.", "empty_column.account_hides_collections": "Ta osoba postanowiła nie udostępniać tych informacji", "empty_column.account_suspended": "Konto zawieszone", @@ -318,6 +333,7 @@ "empty_column.bookmarked_statuses": "Nie dodano jeszcze żadnego wpisu do zakładek. Gdy to zrobisz, pojawi się tutaj.", "empty_column.community": "Lokalna oś czasu jest pusta. Opublikuj coś, by ruszyć z kopyta!", "empty_column.direct": "Nie ma tu jeszcze żadnych wzmianek bezpośrednich. Gdy je wyślesz lub otrzymasz, pojawią się tutaj.", + "empty_column.disabled_feed": "Ten kanał został wyłączony przez administratorów serwera.", "empty_column.domain_blocks": "Brak zablokowanych domen.", "empty_column.explore_statuses": "Nic nie cieszy się teraz popularnością. Sprawdź później!", "empty_column.favourited_statuses": "Nie polubiono jeszcze żadnego wpisu. Gdy to zrobisz, pojawi się tutaj.", @@ -342,10 +358,10 @@ "explore.trending_links": "Aktualności", "explore.trending_statuses": "Wpisy", "explore.trending_tags": "Hasztagi", - "featured_carousel.header": "{count, plural, one {Przypięty post} other {Przypięte posty}}", - "featured_carousel.next": "Następny", + "featured_carousel.header": "{count, plural, one {przypięty wpis} few {przypięte wpisy} many {przypięte wpisy} other {przypięte wpisy}}", + "featured_carousel.next": "Dalej", "featured_carousel.post": "Opublikuj", - "featured_carousel.previous": "Poprzedni", + "featured_carousel.previous": "Wstecz", "featured_carousel.slide": "{index} z {total}", "filter_modal.added.context_mismatch_explanation": "To filtrowanie nie dotyczy kategorii, w której pojawił się ten wpis. Jeśli chcesz, aby wpis był filtrowany również w tym kontekście, musisz edytować ustawienia filtrowania.", "filter_modal.added.context_mismatch_title": "Niewłaściwy kontekst!", @@ -446,10 +462,12 @@ "ignore_notifications_modal.private_mentions_title": "Ignorować powiadomienia od niechcianych wzmianek bezpośrednich?", "info_button.label": "Pomoc", "info_button.what_is_alt_text": "

Czym jest tekst alternatywny?

Tekst alternatywny zawiera opisy zdjęć dla osób niedowidzących, korzystających z połączeń o niskiej przepustowości lub szukających dodatkowego kontekstu.

\n

Możesz poprawić dostępność i czytelność dla wszystkich, pisząc jasny, zwięzły i precyzyjny tekst alternatywny.

\n
    \n
  • Podkreśl ważne elementy
  • \n
  • Streść tekst widoczny na zdjęciach
  • \n
  • Używaj poprawnej struktury zdań
  • \n
  • Unikaj zbędnych informacji
  • \n
  • Skoncentruj się na kluczowych informacjach zawartych w złożonych wizualizacjach (takich jak diagramy lub mapy)
  • \n
", + "interaction_modal.action": "Aby wejść w interakcję z wpisem od {name}, musisz zalogować się na swoje konto na dowolnym serwerze Mastodon, na którym masz już konto.", "interaction_modal.go": "Dalej", "interaction_modal.no_account_yet": "Nie masz jeszcze konta?", "interaction_modal.on_another_server": "Na innym serwerze", "interaction_modal.on_this_server": "Na tym serwerze", + "interaction_modal.title": "Zaloguj się, aby kontynuować", "interaction_modal.username_prompt": "Np. {example}", "intervals.full.days": "{number, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}", "intervals.full.hours": "{number, plural, one {# godzina} few {# godziny} many {# godzin} other {# godzin}}", @@ -470,6 +488,7 @@ "keyboard_shortcuts.home": "Otwórz stronę główną", "keyboard_shortcuts.hotkey": "Skrót klawiszowy", "keyboard_shortcuts.legend": "Wyświetl skróty klawiszowe", + "keyboard_shortcuts.load_more": "Aktywuj przycisk \"Załaduj więcej\"", "keyboard_shortcuts.local": "Otwórz lokalną oś czasu", "keyboard_shortcuts.mention": "Dodaj wzmiankę", "keyboard_shortcuts.muted": "Otwórz listę wyciszonych", @@ -731,9 +750,15 @@ "privacy.quote.disabled": "{visibility}, cytaty wyłączone", "privacy.quote.limited": "{visibility}, cytaty ograniczone", "privacy.unlisted.additional": "Dostępny podobnie jak wpis publiczny, ale nie będzie widoczny w aktualnościach, hashtagach ani wyszukiwarce Mastodon, nawet jeśli twoje konto jest widoczne.", + "privacy.unlisted.long": "Ukryte w wynikach wyszukiwania Mastodona, trendach i publicznych osiach czasu", "privacy.unlisted.short": "Niewidoczny", "privacy_policy.last_updated": "Data ostatniej aktualizacji: {date}", "privacy_policy.title": "Polityka prywatności", + "quote_error.edit": "Podczas edycji wpisu nie można dodawać cytatów.", + "quote_error.poll": "W ankietach nie można cytować.", + "quote_error.quote": "Dozwolone jest tylko jedno cytowanie na raz.", + "quote_error.unauthorized": "Nie masz uprawnień do cytowania tego wpisu.", + "quote_error.upload": "Cytowanie nie jest dozwolone w przypadku załączników multimedialnych.", "recommended": "Zalecane", "refresh": "Odśwież", "regeneration_indicator.please_stand_by": "Proszę czekać.", @@ -750,6 +775,7 @@ "relative_time.seconds": "{number} s.", "relative_time.today": "dzisiaj", "remove_quote_hint.button_label": "Rozumiem", + "remove_quote_hint.message": "Można to zrobić z poziomu menu opcji {icon}.", "remove_quote_hint.title": "Czy chcesz usunąć swój cytowany post?", "reply_indicator.attachments": "{count, plural, one {# załącznik} few {# załączniki} many {# załączników} other {# załączników}}", "reply_indicator.cancel": "Anuluj", @@ -842,10 +868,19 @@ "status.admin_account": "Otwórz interfejs moderacyjny dla @{name}", "status.admin_domain": "Otwórz interfejs moderacyjny dla {domain}", "status.admin_status": "Otwórz ten wpis w interfejsie moderacyjnym", + "status.all_disabled": "Podbicia i cytaty są wyłączone", "status.block": "Zablokuj @{name}", "status.bookmark": "Dodaj zakładkę", "status.cancel_reblog_private": "Cofnij podbicie", + "status.cannot_quote": "Nie można cytować tego wpisu", "status.cannot_reblog": "Ten wpis nie może zostać podbity", + "status.contains_quote": "Zawiera cytat", + "status.context.loading": "Wczytywanie kolejnych komentarzy", + "status.context.loading_error": "Nie można wczytać nowych komentarzy", + "status.context.loading_success": "Wczytano nowe komentarze", + "status.context.more_replies_found": "Znaleziono więcej komentarzy", + "status.context.retry": "Spróbuj ponownie", + "status.context.show": "Pokaż", "status.continued_thread": "Ciąg dalszy wątku", "status.copy": "Skopiuj odnośnik do wpisu", "status.delete": "Usuń", @@ -875,26 +910,39 @@ "status.quote": "Cytuj", "status.quote.cancel": "Anuluj cytat", "status.quote_error.filtered": "Ukryte z powodu jednego z Twoich filtrów", + "status.quote_error.limited_account_hint.action": "Pokaż mimo wszystko", + "status.quote_error.limited_account_hint.title": "To konto zostało ukryte przez moderatorów {domain}.", "status.quote_error.not_available": "Post niedostępny", "status.quote_error.pending_approval": "Post oczekujący", + "status.quote_error.pending_approval_popout.body": "Na Mastodon możesz kontrolować, czy ktoś może cytować twoje wpisy. Ten wpis oczekuje na zatwierdzenie przez autora.", + "status.quote_error.revoked": "Wpis został usunięty przez autora", "status.quote_followers_only": "Tylko obserwatorzy mogą cytować ten post", + "status.quote_manual_review": "Autor zatwierdzi ręcznie", + "status.quote_noun": "Cytuj", "status.quote_policy_change": "Zmień kto może cytować", "status.quote_post_author": "Zacytowano post @{name}", "status.quote_private": "Prywatne posty nie mogą być cytowane", + "status.quotes": "{count, plural, one {cytat} few {cytaty} many {cytatów} other {cytatów}}", + "status.quotes.empty": "Nikt jeszcze nie zacytował tego wpisu. Gdy ktoś to zrobi, pojawi się on tutaj.", + "status.quotes.local_other_disclaimer": "Cytaty odrzucone przez autora nie będą wyświetlane.", + "status.quotes.remote_other_disclaimer": "Będą tutaj wyświetlane tylko cytaty z {domain}. Cytaty odrzucone przez autora nie będą wyświetlane.", "status.read_more": "Czytaj dalej", "status.reblog": "Podbij", "status.reblog_or_quote": "Podbij lub cytuj", + "status.reblog_private": "Udostępnij ponownie swoim obserwującym", "status.reblogged_by": "Podbite przez {name}", "status.reblogs": "{count, plural, one {podbicie} few {podbicia} other {podbić}}", "status.reblogs.empty": "Nikt nie podbił jeszcze tego wpisu. Gdy ktoś to zrobi, pojawi się tutaj.", "status.redraft": "Usuń i przeredaguj", "status.remove_bookmark": "Usuń zakładkę", "status.remove_favourite": "Usuń z ulubionych", + "status.remove_quote": "Usuń", "status.replied_in_thread": "Odpowiedź w wątku", "status.replied_to": "Odpowiedź do wpisu użytkownika {name}", "status.reply": "Odpowiedz", "status.replyAll": "Odpowiedz na wątek", "status.report": "Zgłoś @{name}", + "status.request_quote": "Poproś o możliwość cytowania", "status.revoke_quote": "Usuń mój wpis z postu @{name}", "status.sensitive_warning": "Wrażliwa zawartość", "status.share": "Udostępnij", @@ -959,6 +1007,12 @@ "video.volume_up": "Zwiększ głośność", "visibility_modal.button_title": "Ustaw widoczność", "visibility_modal.header": "Widoczność i interakcja", + "visibility_modal.helper.direct_quoting": "Prywatne wzmianki opublikowane na Mastodonie nie mogą być cytowane przez inne osoby.", + "visibility_modal.helper.privacy_editing": "Widoczność nie może być zmieniona po opublikowaniu wpisu.", + "visibility_modal.helper.privacy_private_self_quote": "Cytaty z prywatnych wpisów nie mogą być publiczne.", + "visibility_modal.helper.private_quoting": "Wpisy publikowane na Mastodonie wyłącznie dla obserwujących nie mogą być cytowane przez inne osoby.", + "visibility_modal.helper.unlisted_quoting": "Kiedy ktoś cytuje twoje wpisy, będą one również ukryte na popularnych osiach czasu.", + "visibility_modal.instructions": "Kontroluj, kto może wchodzić w interakcję z tym wpisem. Możesz również zastosować ustawienia do wszystkich przyszłych wpisów, przechodząc do Preferencje > Domyślne ustawienia publikowania.", "visibility_modal.privacy_label": "Widoczność", "visibility_modal.quote_followers": "Tylko dla obserwujących", "visibility_modal.quote_label": "Kto może cytować", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 5457e91dbab9f1..c80297f67d1634 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -28,6 +28,7 @@ "account.disable_notifications": "Cancelar notificações de @{name}", "account.domain_blocking": "Bloqueando domínio", "account.edit_profile": "Editar perfil", + "account.edit_profile_short": "Editar", "account.enable_notifications": "Notificar novos toots de @{name}", "account.endorse": "Recomendar", "account.familiar_followers_many": "Seguido por {name1}, {name2}, e {othersCount, plural, one {um outro que você conhece} other {# outros que você conhece}}", @@ -40,6 +41,11 @@ "account.featured_tags.last_status_never": "Sem publicações", "account.follow": "Seguir", "account.follow_back": "Seguir de volta", + "account.follow_back_short": "Seguir de volta", + "account.follow_request": "Pedir para seguir", + "account.follow_request_cancel": "Cancelar solicitação", + "account.follow_request_cancel_short": "Cancelar", + "account.follow_request_short": "Solicitação", "account.followers": "Seguidores", "account.followers.empty": "Nada aqui.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", @@ -240,6 +246,8 @@ "confirmations.mute.confirm": "Silenciar", "confirmations.quiet_post_quote_info.dismiss": "Não me lembrar novamente", "confirmations.quiet_post_quote_info.got_it": "Entendi", + "confirmations.quiet_post_quote_info.message": "Ao citar uma publicação pública silenciosa, sua postagem será oculta das linhas de tempo em tendência.", + "confirmations.quiet_post_quote_info.title": "Citando publicações públicas silenciadas", "confirmations.redraft.confirm": "Excluir e rascunhar", "confirmations.redraft.message": "Você tem certeza de que quer apagar essa postagem e rascunhá-la? Favoritos e impulsos serão perdidos, e respostas à postagem original ficarão órfãs.", "confirmations.redraft.title": "Excluir e rascunhar publicação?", @@ -249,7 +257,12 @@ "confirmations.revoke_quote.confirm": "Remover publicação", "confirmations.revoke_quote.message": "Essa ação não pode ser desfeita.", "confirmations.revoke_quote.title": "Remover publicação?", + "confirmations.unblock.confirm": "Desbloquear", + "confirmations.unblock.title": "Desbloquear {name}?", "confirmations.unfollow.confirm": "Deixar de seguir", + "confirmations.unfollow.title": "Deixar de seguir {name}?", + "confirmations.withdraw_request.confirm": "Retirar solicitação", + "confirmations.withdraw_request.title": "Cancelar solicitação para seguir {name}?", "content_warning.hide": "Ocultar post", "content_warning.show": "Mostrar mesmo assim", "content_warning.show_more": "Mostrar mais", @@ -320,6 +333,7 @@ "empty_column.bookmarked_statuses": "Nada aqui. Quando você salvar um toot, ele aparecerá aqui.", "empty_column.community": "A linha local está vazia. Publique algo para começar!", "empty_column.direct": "Você ainda não tem mensagens privadas. Quando você enviar ou receber uma, será exibida aqui.", + "empty_column.disabled_feed": "Este feed foi desativado pelos administradores do servidor.", "empty_column.domain_blocks": "Nada aqui.", "empty_column.explore_statuses": "Nada está em alta no momento. Volte mais tarde!", "empty_column.favourited_statuses": "Você ainda não tem publicações favoritas. Quanto você marcar uma como favorita, ela aparecerá aqui.", @@ -448,10 +462,12 @@ "ignore_notifications_modal.private_mentions_title": "Ignorar notificações de menções privadas não solicitadas?", "info_button.label": "Ajuda", "info_button.what_is_alt_text": "

O que é texto alternativo?

O texto alternativo fornece descrições de imagens para pessoas com deficiências visuais, conexões de internet de baixa largura de banda ou aquelas que buscam mais contexto.

Você pode melhorar a acessibilidade e a compreensão para todos escrevendo texto alternativo claro, conciso e objetivo.

  • Capture elementos importantes
  • Resuma textos em imagens
  • Use estrutura de frases regular
  • Evite informações redundantes
  • Foque em tendências e descobertas principais em visuais complexos (como diagramas ou mapas)
", + "interaction_modal.action": "Para interagir com o post de {name}, você precisa entrar em sua conta em qualquer servidor Mastodon que você use.", "interaction_modal.go": "Ir", "interaction_modal.no_account_yet": "Não possui uma conta ainda?", "interaction_modal.on_another_server": "Em um servidor diferente", "interaction_modal.on_this_server": "Neste servidor", + "interaction_modal.title": "Faça login para continuar", "interaction_modal.username_prompt": "p. e.x.: {example}", "intervals.full.days": "{number, plural, one {# dia} other {# dias}}", "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}", @@ -734,9 +750,11 @@ "privacy.quote.disabled": "{visibility} Citações desabilitadas", "privacy.quote.limited": "{visibility} Citações limitadas", "privacy.unlisted.additional": "Isso se comporta exatamente como público, exceto que a publicação não aparecerá nos _feeds ao vivo_ ou nas _hashtags_, explorar, ou barra de busca, mesmo que você seja escolhido em toda a conta.", - "privacy.unlisted.short": "Público (silencioso)", + "privacy.unlisted.long": "Oculto para os resultados de pesquisa do Mastodon, tendências e linhas do tempo públicas", + "privacy.unlisted.short": "Público silenciado", "privacy_policy.last_updated": "Atualizado {date}", "privacy_policy.title": "Política de privacidade", + "quote_error.edit": "Citações não podem ser adicionadas durante a edição de uma publicação.", "quote_error.poll": "Citações não permitidas com enquetes.", "quote_error.quote": "Apenas uma citação por vez é permitido.", "quote_error.unauthorized": "Você não é autorizado a citar essa publicação.", @@ -756,6 +774,9 @@ "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", "relative_time.today": "hoje", + "remove_quote_hint.button_label": "Entendi", + "remove_quote_hint.message": "Você pode fazê-lo no menu de opções {icon}.", + "remove_quote_hint.title": "Deseja remover sua citação publicada?", "reply_indicator.attachments": "{count, plural, one {# attachment} other {# attachments}}", "reply_indicator.cancel": "Cancelar", "reply_indicator.poll": "Enquete", @@ -851,7 +872,15 @@ "status.block": "Bloquear @{name}", "status.bookmark": "Salvar", "status.cancel_reblog_private": "Desfazer boost", + "status.cannot_quote": "Você não tem permissão para citar esta publicação", "status.cannot_reblog": "Este toot não pode receber boost", + "status.contains_quote": "Contém citação", + "status.context.loading": "Carregando mais respostas", + "status.context.loading_error": "Não foi possível carregar novas respostas", + "status.context.loading_success": "Novas respostas carregadas", + "status.context.more_replies_found": "Mais respostas encontradas", + "status.context.retry": "Tentar novamente", + "status.context.show": "Mostrar", "status.continued_thread": "Continuação da conversa", "status.copy": "Copiar link", "status.delete": "Excluir", @@ -881,24 +910,33 @@ "status.quote": "Citar", "status.quote.cancel": "Cancelar citação", "status.quote_error.filtered": "Oculto devido a um dos seus filtros", + "status.quote_error.limited_account_hint.action": "Mostrar mesmo assim", + "status.quote_error.limited_account_hint.title": "Esta conta foi oculta pelos moderadores do {domain}.", "status.quote_error.not_available": "Publicação indisponível", "status.quote_error.pending_approval": "Publicação pendente", + "status.quote_error.pending_approval_popout.body": "No Mastodon, você pode controlar se alguém pode citar você. Esta publicação está pendente enquanto estamos recebendo a aprovação do autor original.", + "status.quote_error.revoked": "Publicação removida pelo autor", "status.quote_followers_only": "Apenas seguidores podem citar sua publicação", "status.quote_manual_review": "Autor irá revisar manualmente", + "status.quote_noun": "Citar", "status.quote_policy_change": "Mude quem pode citar", "status.quote_post_author": "Publicação citada por @{name}", "status.quote_private": "Publicações privadas não podem ser citadas", "status.quotes": "{count, plural, one {# voto} other {# votos}}", "status.quotes.empty": "Ninguém citou essa publicação até agora. Quando alguém citar aparecerá aqui.", + "status.quotes.local_other_disclaimer": "Citações rejeitadas pelo autor não serão exibidas.", + "status.quotes.remote_other_disclaimer": "Apenas citações do {domain} têm a garantia de serem exibidas aqui. Citações rejeitadas pelo autor não serão exibidas.", "status.read_more": "Ler mais", "status.reblog": "Dar boost", "status.reblog_or_quote": "Acelerar ou citar", + "status.reblog_private": "Compartilhar novamente com seus seguidores", "status.reblogged_by": "{name} deu boost", "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Nada aqui. Quando alguém der boost, o usuário aparecerá aqui.", "status.redraft": "Excluir e rascunhar", "status.remove_bookmark": "Remover do Salvos", "status.remove_favourite": "Remover dos favoritos", + "status.remove_quote": "Remover", "status.replied_in_thread": "Respondido na conversa", "status.replied_to": "Em resposta a {name}", "status.reply": "Responder", @@ -970,6 +1008,8 @@ "visibility_modal.button_title": "Selecionar Visibilidade", "visibility_modal.header": "Visibilidade e interação", "visibility_modal.helper.direct_quoting": "Menções privadas escritas no Mastodon.", + "visibility_modal.helper.privacy_editing": "A visibilidade não pode ser alterada após uma publicação ser publicada.", + "visibility_modal.helper.privacy_private_self_quote": "As auto-citações de publicações privadas não podem ser públicas.", "visibility_modal.helper.private_quoting": "Posts somente para seguidores feitos no Mastodon não podem ser citados por outros.", "visibility_modal.helper.unlisted_quoting": "Quando as pessoas citam você, sua publicação também será ocultada das linhas de tempo de tendência.", "visibility_modal.instructions": "Controle quem pode interagir com este post. Você também pode aplicar as configurações para todos os posts futuros navegando para Preferências > Postagem padrão.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 0acd1d9f407523..b08362d0303ed5 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -55,7 +55,7 @@ "account.follows.empty": "Este utilizador ainda não segue ninguém.", "account.follows_you": "Segue-te", "account.go_to_profile": "Ir para o perfil", - "account.hide_reblogs": "Esconder partilhas impulsionadas de @{name}", + "account.hide_reblogs": "Esconder partilhas de @{name}", "account.in_memoriam": "Em Memória.", "account.joined_short": "Juntou-se a", "account.languages": "Alterar idiomas subscritos", @@ -79,7 +79,7 @@ "account.requested_follow": "{name} pediu para seguir-te", "account.requests_to_follow_you": "Pediu para seguir-te", "account.share": "Partilhar o perfil @{name}", - "account.show_reblogs": "Mostrar partilhas impulsionadas de @{name}", + "account.show_reblogs": "Mostrar partilhas de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear o domínio {domain}", @@ -113,7 +113,7 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descreve isto para pessoas com problemas de visão…", "alt_text_modal.done": "Concluído", "announcement.announcement": "Mensagem de manutenção", - "annual_report.summary.archetype.booster": "O caçador de frescura", + "annual_report.summary.archetype.booster": "O caçador de tendências", "annual_report.summary.archetype.lurker": "O espreitador", "annual_report.summary.archetype.oracle": "O oráculo", "annual_report.summary.archetype.pollster": "O sondagens", @@ -122,7 +122,7 @@ "annual_report.summary.followers.total": "{count} no total", "annual_report.summary.here_it_is": "Aqui está um resumo do ano {year}:", "annual_report.summary.highlighted_post.by_favourites": "publicação mais favorita", - "annual_report.summary.highlighted_post.by_reblogs": "publicação mais impulsionada", + "annual_report.summary.highlighted_post.by_reblogs": "publicação mais partilhada", "annual_report.summary.highlighted_post.by_replies": "publicação com o maior número de respostas", "annual_report.summary.highlighted_post.possessive": "{name}", "annual_report.summary.most_used_app.most_used_app": "aplicação mais utilizada", @@ -142,9 +142,9 @@ "block_modal.they_will_know": "Ele pode ver que o bloqueaste.", "block_modal.title": "Bloquear utilizador?", "block_modal.you_wont_see_mentions": "Não verás publicações que mencionem este utilizador.", - "boost_modal.combo": "Podes premir {combo} para não voltares a ver isto", - "boost_modal.reblog": "Impulsionar a publicação?", - "boost_modal.undo_reblog": "Não impulsionar a publicação?", + "boost_modal.combo": "Pode clicar em {combo} para não voltar a ver isto", + "boost_modal.reblog": "Partilhar a publicação?", + "boost_modal.undo_reblog": "Deixar de partilhar a publicação?", "bundle_column_error.copy_stacktrace": "Copiar relatório de erros", "bundle_column_error.error.body": "A página solicitada não pôde ser sintetizada. Isto pode ser devido a uma falha no nosso código ou a um problema de compatibilidade com o navegador.", "bundle_column_error.error.title": "Ó, não!", @@ -173,6 +173,8 @@ "column.edit_list": "Editar lista", "column.favourites": "Favoritos", "column.firehose": "Cronologias em tempo real", + "column.firehose_local": "Cronologia em tempo real para este servidor", + "column.firehose_singular": "Cronologia em tempo real", "column.follow_requests": "Pedidos de seguidores", "column.home": "Início", "column.list_members": "Gerir membros da lista", @@ -249,7 +251,7 @@ "confirmations.quiet_post_quote_info.message": "Ao citar uma publicação não listada, a sua publicação não será exibida nos destaques.", "confirmations.quiet_post_quote_info.title": "Citação de publicação não listada", "confirmations.redraft.confirm": "Eliminar e reescrever", - "confirmations.redraft.message": "Tens a certeza de que queres eliminar e tornar a escrever esta publicação? Os favoritos e as publicações impulsionadas perder-se-ão e as respostas à publicação original ficarão órfãs.", + "confirmations.redraft.message": "Tem a certeza que pretende eliminar e tornar a escrever esta publicação? Os favoritos e as partilhas perder-se-ão e as respostas à publicação original ficarão órfãs.", "confirmations.redraft.title": "Eliminar e reescrever publicação?", "confirmations.remove_from_followers.confirm": "Remover seguidor", "confirmations.remove_from_followers.message": "{name} vai parar de seguir-te. Tens a certeza que prentedes continuar?", @@ -330,9 +332,10 @@ "empty_column.account_timeline": "Sem publicações por aqui!", "empty_column.account_unavailable": "Perfil indisponível", "empty_column.blocks": "Ainda não bloqueaste nenhum utilizador.", - "empty_column.bookmarked_statuses": "Ainda não tens nenhuma publicação marcada. Quando marcares uma, ela aparecerá aqui.", + "empty_column.bookmarked_statuses": "Ainda não tem nenhuma publicação salva. Quando salvar uma, ela aparecerá aqui.", "empty_column.community": "A cronologia local está vazia. Escreve algo publicamente para começar!", "empty_column.direct": "Ainda não tens qualquer menção privada. Quando enviares ou receberes uma, ela irá aparecer aqui.", + "empty_column.disabled_feed": "Esta cronologia foi desativada pelos administradores do seu servidor.", "empty_column.domain_blocks": "Ainda não há qualquer domínio bloqueado.", "empty_column.explore_statuses": "Não há nada em destaque neste momento. Volte mais tarde!", "empty_column.favourited_statuses": "Ainda não assinalaste qualquer publicação como favorita. Quando o fizeres, ela aparecerá aqui.", @@ -366,7 +369,7 @@ "filter_modal.added.context_mismatch_title": "O contexto não coincide!", "filter_modal.added.expired_explanation": "Esta categoria de filtro expirou, tens de alterar a data de validade para que ele seja aplicado.", "filter_modal.added.expired_title": "Filtro expirado!", - "filter_modal.added.review_and_configure": "Para rever e configurar mais detalhadamente esta categoria de filtro, vai a {settings_link}.", + "filter_modal.added.review_and_configure": "Para rever e configurar mais detalhadamente esta categoria de filtro, vá a {settings_link}.", "filter_modal.added.review_and_configure_title": "Definições do filtro", "filter_modal.added.settings_link": "página de definições", "filter_modal.added.short_explanation": "Esta publicação foi adicionada à seguinte categoria de filtro: {title}.", @@ -429,10 +432,10 @@ "hashtag.counter_by_uses": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} publicação} other {{counter} publicações}} hoje", "hashtag.feature": "Destacar no perfil", - "hashtag.follow": "Seguir #etiqueta", + "hashtag.follow": "Seguir etiqueta", "hashtag.mute": "Silenciar #{hashtag}", "hashtag.unfeature": "Não destacar no perfil", - "hashtag.unfollow": "Deixar de seguir #etiqueta", + "hashtag.unfollow": "Deixar de seguir a etiqueta", "hashtags.and_other": "…e {count, plural, other {mais #}}", "hints.profiles.followers_may_be_missing": "É possível que não estejam a ser mostrados todos os seguidores deste perfil.", "hints.profiles.follows_may_be_missing": "É possível que não estejam a ser mostrados todos os seguidos por este perfil.", @@ -441,7 +444,7 @@ "hints.profiles.see_more_follows": "Ver mais perfis seguidos em {domain}", "hints.profiles.see_more_posts": "Ver mais publicações em {domain}", "home.column_settings.show_quotes": "Mostrar citações", - "home.column_settings.show_reblogs": "Mostrar impulsos", + "home.column_settings.show_reblogs": "Mostrar partilhas", "home.column_settings.show_replies": "Mostrar respostas", "home.hide_announcements": "Ocultar mensagens de manutenção", "home.pending_critical_update.body": "Atualiza o teu servidor Mastodon assim que possível!", @@ -473,7 +476,7 @@ "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "keyboard_shortcuts.back": "voltar atrás", "keyboard_shortcuts.blocked": "abrir a lista de utilizadores bloqueados", - "keyboard_shortcuts.boost": "impulsionar a publicação", + "keyboard_shortcuts.boost": "Partilhar a publicação", "keyboard_shortcuts.column": "focar uma publicação numa das colunas", "keyboard_shortcuts.compose": "focar área de texto da publicação", "keyboard_shortcuts.description": "Descrição", @@ -564,7 +567,7 @@ "navigation_bar.advanced_interface": "Abrir na interface web avançada", "navigation_bar.automated_deletion": "Eliminação automática de publicações", "navigation_bar.blocks": "Utilizadores bloqueados", - "navigation_bar.bookmarks": "Marcadores", + "navigation_bar.bookmarks": "Itens salvos", "navigation_bar.direct": "Menções privadas", "navigation_bar.domain_blocks": "Domínios escondidos", "navigation_bar.favourites": "Favoritos", @@ -626,8 +629,8 @@ "notification.own_poll": "A tua sondagem terminou", "notification.poll": "Terminou uma sondagem em que votaste", "notification.quoted_update": "{name} editou uma publicação que citou", - "notification.reblog": "{name} impulsionou a tua publicação", - "notification.reblog.name_and_others_with_link": "{name} e {count, plural, one {# outro} other {# outros}} impulsionaram a tua publicação", + "notification.reblog": "{name} partilhou a sua publicação", + "notification.reblog.name_and_others_with_link": "{name} e {count, plural, one {# outro} other {# outros}} partilharam a sua publicação", "notification.relationships_severance_event": "Perdeu as ligações com {name}", "notification.relationships_severance_event.account_suspension": "Um administrador de {from} suspendeu {target}, o que significa que já não podes receber atualizações dele ou interagir com ele.", "notification.relationships_severance_event.domain_block": "Um administrador de {from} bloqueou {target}, incluindo {followersCount} dos teus seguidores e {followingCount, plural, one {# conta} other {# contas}} que segues.", @@ -670,7 +673,7 @@ "notifications.column_settings.poll": "Resultados da sondagem:", "notifications.column_settings.push": "Notificações \"push\"", "notifications.column_settings.quote": "Citações:", - "notifications.column_settings.reblog": "Impulsos:", + "notifications.column_settings.reblog": "Partilhas:", "notifications.column_settings.show": "Mostrar na coluna", "notifications.column_settings.sound": "Reproduzir som", "notifications.column_settings.status": "Novas publicações:", @@ -678,7 +681,7 @@ "notifications.column_settings.unread_notifications.highlight": "Destacar notificações por ler", "notifications.column_settings.update": "Edições:", "notifications.filter.all": "Todas", - "notifications.filter.boosts": "Impulsos", + "notifications.filter.boosts": "Partilhas", "notifications.filter.favourites": "Favoritos", "notifications.filter.follows": "Seguidores", "notifications.filter.mentions": "Menções", @@ -867,12 +870,12 @@ "status.admin_account": "Abrir a interface de moderação para @{name}", "status.admin_domain": "Abrir interface de moderação para {domain}", "status.admin_status": "Abrir esta publicação na interface de moderação", - "status.all_disabled": "Impulsos e citações estão desativados", + "status.all_disabled": "Partilhas e citações estão desativados", "status.block": "Bloquear @{name}", "status.bookmark": "Guardar nos marcadores", - "status.cancel_reblog_private": "Retirar impulso", + "status.cancel_reblog_private": "Deixar de partilhar", "status.cannot_quote": "Não lhe é permitido citar esta publicação", - "status.cannot_reblog": "Esta publicação não pode ser impulsionada", + "status.cannot_reblog": "Esta publicação não pode ser partilhada", "status.contains_quote": "Contém citação", "status.context.loading": "A carregar mais respostas", "status.context.loading_error": "Não foi possível carregar novas respostas", @@ -926,12 +929,12 @@ "status.quotes.local_other_disclaimer": "As citações rejeitadas pelo autor não serão exibidas.", "status.quotes.remote_other_disclaimer": "Apenas citações de {domain} serão garantidamente exibidas aqui. Citações rejeitadas pelo autor não serão exibidas.", "status.read_more": "Ler mais", - "status.reblog": "Impulsionar", + "status.reblog": "Partilhar", "status.reblog_or_quote": "Partilhe ou cite", "status.reblog_private": "Partilhe novamente com os seus seguidores", - "status.reblogged_by": "{name} impulsionou", - "status.reblogs": "{count, plural, one {impulso} other {impulsos}}", - "status.reblogs.empty": "Ainda ninguém impulsionou esta publicação. Quando alguém o fizer, aparecerá aqui.", + "status.reblogged_by": "{name} partilhou", + "status.reblogs": "{count, plural, one {partilha} other {partilhas}}", + "status.reblogs.empty": "Ainda ninguém partilhou esta publicação. Quando alguém o fizer, aparecerá aqui.", "status.redraft": "Eliminar e reescrever", "status.remove_bookmark": "Retirar dos marcadores", "status.remove_favourite": "Remover dos favoritos", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index a39bb0c078a4d9..2e368444ee96c5 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -329,6 +329,7 @@ "empty_column.bookmarked_statuses": "S’keni faqeruajtur ende ndonjë mesazh. Kur faqeruani një të tillë, ai do të shfaqet këtu.", "empty_column.community": "Rrjedha kohore vendore është e zbrazët. Shkruani diçka publikisht që t’i hyhet valles!", "empty_column.direct": "S’keni ende ndonjë përmendje private. Kur dërgoni ose merrni një të tillë, do të shfaqet këtu.", + "empty_column.disabled_feed": "Kjo prurje është çaktivizuar nga përgjegjësit e shërbyesit tuaj.", "empty_column.domain_blocks": "Ende s’ka përkatësi të fshehura.", "empty_column.explore_statuses": "Asgjë në modë tani. Kontrolloni më vonë!", "empty_column.favourited_statuses": "S’keni ende ndonjë postim të parapëlqyer. Kur të parapëlqeni një të tillë, do të shfaqet këtu.", diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json index c87d8ee5dd82f5..3162ee7351423b 100644 --- a/app/javascript/mastodon/locales/ug.json +++ b/app/javascript/mastodon/locales/ug.json @@ -9,7 +9,12 @@ "account.badges.group": "گۇرۇپپا", "account.block": "@{name} نى توس", "account.block_domain": "{domain} دائىرىنى توس", + "account.block_short": "توس", + "account.blocked": "توسۇلدى", + "account.blocking": "توسۇۋاتىدۇ", "account.cancel_follow_request": "ئەگىشىش ئىلتىماسىدىن ۋاز كەچ", + "account.copy": "تەرجىمىھال ئۇلانمىسىنى كۆچۈر", + "account.direct": "@{name} نى يوشۇرۇن ئاتا", "account.posts": "يازما", "account.posts_with_replies": "يازما ۋە ئىنكاس", "account.report": "@{name} نى پاش قىل", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index c68033c5789183..8c475d54807344 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -173,6 +173,8 @@ "column.edit_list": "Sửa danh sách", "column.favourites": "Những tút đã thích", "column.firehose": "Bảng tin", + "column.firehose_local": "Bảng tin máy chủ này", + "column.firehose_singular": "Bảng tin", "column.follow_requests": "Yêu cầu theo dõi", "column.home": "Trang chủ", "column.list_members": "Những người trong danh sách", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "Bạn chưa lưu tút nào. Nếu có, nó sẽ hiển thị ở đây.", "empty_column.community": "Máy chủ của bạn chưa có tút nào công khai. Bạn hãy thử viết gì đó đi!", "empty_column.direct": "Bạn chưa có tin nhắn riêng nào. Khi bạn gửi hoặc nhận một tin nhắn riêng, nó sẽ xuất hiện ở đây.", + "empty_column.disabled_feed": "Bảng tin này bị vô hiệu hóa bởi quản trị viên máy chủ của bạn.", "empty_column.domain_blocks": "Chưa ẩn bất kỳ máy chủ nào.", "empty_column.explore_statuses": "Chưa có gì hot. Kiểm tra lại sau!", "empty_column.favourited_statuses": "Bạn chưa thích tút nào. Hãy thử đi, nó sẽ xuất hiện ở đây.", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 6164df4edc4bef..7525498c2adf01 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -173,6 +173,8 @@ "column.edit_list": "編輯列表", "column.favourites": "最愛", "column.firehose": "即時內容", + "column.firehose_local": "本站伺服器之即時內容", + "column.firehose_singular": "即時內容", "column.follow_requests": "跟隨請求", "column.home": "首頁", "column.list_members": "管理列表成員", @@ -333,6 +335,7 @@ "empty_column.bookmarked_statuses": "您還沒有新增任何書籤。當您新增書籤時,它將於此顯示。", "empty_column.community": "本站時間軸是空的。快公開嘟些文搶頭香啊!", "empty_column.direct": "您還沒有收到任何私訊。當您私訊別人或收到私訊時,它將於此顯示。", + "empty_column.disabled_feed": "此內容已被您的伺服器管理員停用。", "empty_column.domain_blocks": "尚未封鎖任何網域。", "empty_column.explore_statuses": "目前沒有熱門討論,請稍候再回來看看!", "empty_column.favourited_statuses": "您還沒有加過任何嘟文至最愛。當您收藏嘟文時,它將於此顯示。", diff --git a/app/javascript/mastodon/main.tsx b/app/javascript/mastodon/main.tsx index 456cc21c318805..f89baf66cd6b63 100644 --- a/app/javascript/mastodon/main.tsx +++ b/app/javascript/mastodon/main.tsx @@ -9,11 +9,8 @@ import { me, reduceMotion } from 'mastodon/initial_state'; import ready from 'mastodon/ready'; import { store } from 'mastodon/store'; -import { - isProduction, - isDevelopment, - isModernEmojiEnabled, -} from './utils/environment'; +import { initializeEmoji } from './features/emoji'; +import { isProduction, isDevelopment } from './utils/environment'; function main() { perf.start('main()'); @@ -33,10 +30,7 @@ function main() { }); } - if (isModernEmojiEnabled()) { - const { initializeEmoji } = await import('@/mastodon/features/emoji'); - initializeEmoji(); - } + initializeEmoji(); const root = createRoot(mountNode); root.render(); diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index 11f59dd1d11f9b..b7c457214612f3 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -10,11 +10,10 @@ import type { ApiAccountJSON, ApiServerFeaturesJSON, } from 'mastodon/api_types/accounts'; -import emojify from 'mastodon/features/emoji/emoji'; import { unescapeHTML } from 'mastodon/utils/html'; -import { CustomEmojiFactory, makeEmojiMap } from './custom_emoji'; -import type { CustomEmoji, EmojiMap } from './custom_emoji'; +import { CustomEmojiFactory } from './custom_emoji'; +import type { CustomEmoji } from './custom_emoji'; // AccountField interface AccountFieldShape extends Required { @@ -135,17 +134,11 @@ export const accountDefaultValues: AccountShape = { const AccountFactory = ImmutableRecord(accountDefaultValues); -function createAccountField( - jsonField: ApiAccountFieldJSON, - emojiMap: EmojiMap, -) { +function createAccountField(jsonField: ApiAccountFieldJSON) { return AccountFieldFactory({ ...jsonField, - name_emojified: emojify( - escapeTextContentForBrowser(jsonField.name), - emojiMap, - ), - value_emojified: emojify(jsonField.value, emojiMap), + name_emojified: escapeTextContentForBrowser(jsonField.name), + value_emojified: jsonField.value, value_plain: unescapeHTML(jsonField.value), }); } @@ -153,8 +146,6 @@ function createAccountField( export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { const { moved, ...accountJSON } = serverJSON; - const emojiMap = makeEmojiMap(accountJSON.emojis); - const displayName = accountJSON.display_name.trim().length === 0 ? accountJSON.username @@ -167,7 +158,7 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { ...accountJSON, moved: moved?.id, fields: ImmutableList( - serverJSON.fields.map((field) => createAccountField(field, emojiMap)), + serverJSON.fields.map((field) => createAccountField(field)), ), emojis: ImmutableList( serverJSON.emojis.map((emoji) => CustomEmojiFactory(emoji)), @@ -175,11 +166,8 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { roles: ImmutableList( serverJSON.roles?.map((role) => AccountRoleFactory(role)), ), - display_name_html: emojify( - escapeTextContentForBrowser(displayName), - emojiMap, - ), - note_emojified: emojify(accountNote, emojiMap), + display_name_html: escapeTextContentForBrowser(displayName), + note_emojified: accountNote, note_plain: unescapeHTML(accountNote), url: accountJSON.url?.startsWith('http://') || diff --git a/app/javascript/mastodon/models/poll.ts b/app/javascript/mastodon/models/poll.ts index 6f5655680d6beb..46cbb1111d1660 100644 --- a/app/javascript/mastodon/models/poll.ts +++ b/app/javascript/mastodon/models/poll.ts @@ -1,10 +1,9 @@ import escapeTextContentForBrowser from 'escape-html'; import type { ApiPollJSON, ApiPollOptionJSON } from 'mastodon/api_types/polls'; -import emojify from 'mastodon/features/emoji/emoji'; -import { CustomEmojiFactory, makeEmojiMap } from './custom_emoji'; -import type { CustomEmoji, EmojiMap } from './custom_emoji'; +import { CustomEmojiFactory } from './custom_emoji'; +import type { CustomEmoji } from './custom_emoji'; interface PollOptionTranslation { title: string; @@ -17,16 +16,12 @@ export interface PollOption extends ApiPollOptionJSON { translation: PollOptionTranslation | null; } -export function createPollOptionTranslationFromServerJSON( - translation: { title: string }, - emojiMap: EmojiMap, -) { +export function createPollOptionTranslationFromServerJSON(translation: { + title: string; +}) { return { ...translation, - titleHtml: emojify( - escapeTextContentForBrowser(translation.title), - emojiMap, - ), + titleHtml: escapeTextContentForBrowser(translation.title), } as PollOptionTranslation; } @@ -50,8 +45,6 @@ export function createPollFromServerJSON( serverJSON: ApiPollJSON, previousPoll?: Poll, ) { - const emojiMap = makeEmojiMap(serverJSON.emojis); - return { ...pollDefaultValues, ...serverJSON, @@ -60,20 +53,15 @@ export function createPollFromServerJSON( const option = { ...optionJSON, voted: serverJSON.own_votes?.includes(index) || false, - titleHtml: emojify( - escapeTextContentForBrowser(optionJSON.title), - emojiMap, - ), + titleHtml: escapeTextContentForBrowser(optionJSON.title), } as PollOption; const prevOption = previousPoll?.options[index]; if (prevOption?.translation && prevOption.title === option.title) { const { translation } = prevOption; - option.translation = createPollOptionTranslationFromServerJSON( - translation, - emojiMap, - ); + option.translation = + createPollOptionTranslationFromServerJSON(translation); } return option; diff --git a/app/javascript/mastodon/permissions.ts b/app/javascript/mastodon/permissions.ts index d7695d2f5c7e02..a83e1d77a7de68 100644 --- a/app/javascript/mastodon/permissions.ts +++ b/app/javascript/mastodon/permissions.ts @@ -1,3 +1,4 @@ +export const PEMRISSION_VIEW_FEEDS = 0x0000000000100000; export const PERMISSION_INVITE_USERS = 0x0000000000010000; export const PERMISSION_MANAGE_USERS = 0x0000000000000400; export const PERMISSION_MANAGE_TAXONOMIES = 0x0000000000000100; @@ -22,3 +23,19 @@ export function canManageReports(permissions: number) { (permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS ); } + +export const canViewFeed = ( + signedIn: boolean, + permissions: number, + setting: 'public' | 'authenticated' | 'disabled' | undefined, +) => { + switch (setting) { + case 'public': + return true; + case 'authenticated': + return signedIn; + case 'disabled': + default: + return (permissions & PEMRISSION_VIEW_FEEDS) === PEMRISSION_VIEW_FEEDS; + } +}; diff --git a/app/javascript/mastodon/polyfills/index.ts b/app/javascript/mastodon/polyfills/index.ts index 1abfe0a935e2ac..00da2042ed8007 100644 --- a/app/javascript/mastodon/polyfills/index.ts +++ b/app/javascript/mastodon/polyfills/index.ts @@ -2,9 +2,6 @@ // If there are no polyfills, then this is just Promise.resolve() which means // it will execute in the same tick of the event loop (i.e. near-instant). -// eslint-disable-next-line import/extensions -- This file is virtual so it thinks it has an extension -import 'vite/modulepreload-polyfill'; - import { loadIntlPolyfills } from './intl'; function importExtraPolyfills() { @@ -17,6 +14,7 @@ export function loadPolyfills() { const needsExtraPolyfills = !window.requestIdleCallback; return Promise.all([ + loadVitePreloadPolyfill(), loadIntlPolyfills(), // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- those properties might not exist in old browsers, even if they are always here in types needsExtraPolyfills ? importExtraPolyfills() : Promise.resolve(), @@ -31,5 +29,13 @@ async function loadEmojiPolyfills() { } } +// Loads Vite's module preload polyfill for older browsers, but not in a Worker context. +function loadVitePreloadPolyfill() { + if (typeof document === 'undefined') return; + // @ts-expect-error -- This is a virtual module provided by Vite. + // eslint-disable-next-line import/extensions + return import('vite/modulepreload-polyfill'); +} + // Null unless polyfill is needed. export let emojiRegexPolyfill: RegExp | null = null; diff --git a/app/javascript/mastodon/reducers/polls.ts b/app/javascript/mastodon/reducers/polls.ts index aadf6741c13677..ac0917bd2016bd 100644 --- a/app/javascript/mastodon/reducers/polls.ts +++ b/app/javascript/mastodon/reducers/polls.ts @@ -1,7 +1,6 @@ import type { Reducer } from '@reduxjs/toolkit'; import { importPolls } from 'mastodon/actions/importer/polls'; -import { makeEmojiMap } from 'mastodon/models/custom_emoji'; import { createPollOptionTranslationFromServerJSON } from 'mastodon/models/poll'; import type { Poll } from 'mastodon/models/poll'; @@ -20,16 +19,11 @@ const statusTranslateSuccess = (state: PollsState, pollTranslation?: Poll) => { if (!poll) return; - const emojiMap = makeEmojiMap(poll.emojis); - pollTranslation.options.forEach((item, index) => { const option = poll.options[index]; if (!option) return; - option.translation = createPollOptionTranslationFromServerJSON( - item, - emojiMap, - ); + option.translation = createPollOptionTranslationFromServerJSON(item); }); }; diff --git a/app/javascript/mastodon/stream.js b/app/javascript/mastodon/stream.js index e131566bba3dbd..3f1d81f29168a6 100644 --- a/app/javascript/mastodon/stream.js +++ b/app/javascript/mastodon/stream.js @@ -140,10 +140,15 @@ const channelNameWithInlineParams = (channelName, params) => { return `${channelName}&${Object.keys(params).map(key => `${key}=${params[key]}`).join('&')}`; }; +/** + * @typedef {import('mastodon/store').AppDispatch} Dispatch + * @typedef {import('mastodon/store').GetState} GetState + */ + /** * @param {string} channelName * @param {Object.} params - * @param {function(Function, Function): { onConnect: (function(): void), onReceive: (function(StreamEvent): void), onDisconnect: (function(): void) }} callbacks + * @param {function(Dispatch, GetState): { onConnect: (function(): void), onReceive: (function(StreamEvent): void), onDisconnect: (function(): void) }} callbacks * @returns {function(): void} */ // @ts-expect-error @@ -231,7 +236,7 @@ const handleEventSourceMessage = (e, received) => { * @param {string} streamingAPIBaseURL * @param {string} accessToken * @param {string} channelName - * @param {{ connected: Function, received: function(StreamEvent): void, disconnected: Function, reconnected: Function }} callbacks + * @param {{ connected: function(): void, received: function(StreamEvent): void, disconnected: function(): void, reconnected: function(): void }} callbacks * @returns {WebSocketClient | EventSource} */ const createConnection = (streamingAPIBaseURL, accessToken, channelName, { connected, received, disconnected, reconnected }) => { @@ -244,12 +249,9 @@ const createConnection = (streamingAPIBaseURL, accessToken, channelName, { conne // @ts-expect-error const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken); - // @ts-expect-error ws.onopen = connected; ws.onmessage = e => received(JSON.parse(e.data)); - // @ts-expect-error ws.onclose = disconnected; - // @ts-expect-error ws.onreconnect = reconnected; return ws; diff --git a/app/javascript/mastodon/utils/environment.ts b/app/javascript/mastodon/utils/environment.ts index c2b6b1cf86a0d3..95075454f235fe 100644 --- a/app/javascript/mastodon/utils/environment.ts +++ b/app/javascript/mastodon/utils/environment.ts @@ -12,16 +12,8 @@ export function isProduction() { else return import.meta.env.PROD; } -export type Features = 'modern_emojis' | 'fasp' | 'http_message_signatures'; +export type Features = 'fasp' | 'http_message_signatures'; export function isFeatureEnabled(feature: Features) { return initialState?.features.includes(feature) ?? false; } - -export function isModernEmojiEnabled() { - try { - return isFeatureEnabled('modern_emojis'); - } catch { - return false; - } -} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 962314cc009f94..8c2a3de5e2a8f8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1380,7 +1380,7 @@ } } -.announcements__item__content { +.announcements__content { overflow-wrap: break-word; overflow-y: auto; @@ -4137,6 +4137,7 @@ a.account__display-name { background: lighten($ui-highlight-color, 5%); } +.follow_requests-unlocked_explanation, .switch-to-advanced { color: $light-text-color; background-color: $ui-base-color; @@ -4147,7 +4148,7 @@ a.account__display-name { font-size: 13px; line-height: 18px; - .switch-to-advanced__toggle { + a { color: $ui-button-tertiary-color; font-weight: bold; } @@ -5523,8 +5524,7 @@ a.status-card { } } -.empty-column-indicator, -.follow_requests-unlocked_explanation { +.empty-column-indicator { color: $dark-text-color; text-align: center; padding: 20px; @@ -5563,10 +5563,8 @@ a.status-card { } .follow_requests-unlocked_explanation { - background: var(--surface-background-color); - border-bottom: 1px solid var(--background-border-color); - contain: initial; - flex-grow: 0; + margin: 16px; + margin-bottom: 0; } .error-column { @@ -8738,6 +8736,10 @@ noscript { .reaction_deck__emoji { flex: 1; } + + &__overlay { + margin-inline-start: 48px; + } } } @@ -9407,10 +9409,21 @@ noscript { } .announcements { - background: lighten($ui-base-color, 8%); - font-size: 13px; - display: flex; - align-items: flex-end; + width: calc(100% - 124px); + flex: 0 0 auto; + position: relative; + overflow: hidden; + + @media screen and (max-width: (124px + 300px)) { + width: 100%; + } + + &__root { + background: lighten($ui-base-color, 8%); + font-size: 13px; + display: flex; + align-items: flex-end; + } &__mastodon { width: 124px; @@ -9421,19 +9434,16 @@ noscript { } } - &__container { - width: calc(100% - 124px); - flex: 0 0 auto; - position: relative; - - @media screen and (max-width: (124px + 300px)) { - width: 100%; - } + &__slides { + display: flex; + flex-wrap: nowrap; + align-items: start; } - &__item { + &__slide { box-sizing: border-box; width: 100%; + flex: 0 0 100%; padding: 15px; position: relative; font-size: 15px; @@ -9442,26 +9452,25 @@ noscript { font-weight: 400; max-height: 50vh; overflow: hidden; - display: flex; flex-direction: column; + } - &__range { - display: block; - font-weight: 500; - margin-bottom: 10px; - padding-inline-end: 18px; - } + &__range { + display: block; + font-weight: 500; + margin-bottom: 10px; + padding-inline-end: 18px; + } - &__unread { - position: absolute; - top: 19px; - inset-inline-end: 19px; - display: block; - background: $highlight-text-color; - border-radius: 50%; - width: 0.625rem; - height: 0.625rem; - } + &__unread { + position: absolute; + top: 19px; + inset-inline-end: 19px; + display: block; + background: $highlight-text-color; + border-radius: 50%; + width: 0.625rem; + height: 0.625rem; } &__pagination { @@ -9472,6 +9481,7 @@ noscript { inset-inline-end: 0; display: flex; align-items: center; + z-index: 1; } } @@ -9842,19 +9852,13 @@ noscript { &__shared { display: flex; - align-items: center; + align-items: baseline; color: $darker-text-color; gap: 8px; justify-content: space-between; font-size: 14px; line-height: 20px; - & > span { - display: flex; - align-items: center; - gap: 4px; - } - &__pill { background: var(--surface-variant-background-color); border-radius: 4px; @@ -9864,6 +9868,7 @@ noscript { font-size: 12px; font-weight: 500; line-height: 16px; + flex-shrink: 0; } &__author-link { @@ -12100,4 +12105,10 @@ noscript { height: 16px; } } + + &__pagination { + display: flex; + align-items: center; + gap: 4px; + } } diff --git a/app/javascript/types/dom.d.ts b/app/javascript/types/dom.d.ts new file mode 100644 index 00000000000000..fcb1b0346faf13 --- /dev/null +++ b/app/javascript/types/dom.d.ts @@ -0,0 +1,6 @@ +declare namespace React { + interface HTMLAttributes extends AriaAttributes, DOMAttributes { + // Add inert attribute support, which is only in React 19.2. See: https://github.com/facebook/react/pull/24730 + inert?: ''; + } +} diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 40d65a1b518ff3..fd5c0cab1ef8a6 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true class ActivityPub::Activity::Create < ActivityPub::Activity - include FormattingHelper include NgRuleHelper - LINK_MEDIA_TYPES = ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].freeze - def perform @account.schedule_refresh_if_stale! @@ -116,9 +113,9 @@ def process_status_params uri: @status_parser.uri, url: @status_parser.url || @status_parser.uri, account: @account, - text: converted_object_type? ? converted_text : (@status_parser.text || ''), + text: @status_parser.processed_text, language: @status_parser.language, - spoiler_text: converted_object_type? ? '' : (@status_parser.spoiler_text || ''), + spoiler_text: @status_parser.processed_spoiler_text, created_at: @status_parser.created_at, edited_at: @status_parser.edited_at && @status_parser.edited_at != @status_parser.created_at ? @status_parser.edited_at : nil, override_timestamps: @options[:override_timestamps], @@ -529,18 +526,6 @@ def in_reply_to_uri value_or_id(@object['inReplyTo']) end - def converted_text - [formatted_title, @status_parser.spoiler_text.presence, formatted_url].compact.join("\n\n") - end - - def formatted_title - "

#{@status_parser.title}

" if @status_parser.title.present? - end - - def formatted_url - linkify(@status_parser.url || @status_parser.uri) - end - def unsupported_media_type?(mime_type) mime_type.present? && !MediaAttachment.supported_mime_types.include?(mime_type) end diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index b8365819ccd340..f02654d2b29b4c 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -8,10 +8,8 @@ def perform if equals_or_includes_any?(@object['type'], %w(Application Group Organization Person Service)) update_account - elsif equals_or_includes_any?(@object['type'], %w(Note Question)) + elsif supported_object_type? || converted_object_type? update_status - elsif converted_object_type? - Status.find_by(uri: object_uri, account_id: @account.id) end end diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb index 531fa1d2270a43..020bed69e0cb0c 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class ActivityPub::Parser::StatusParser + include FormattingHelper include JsonLdHelper NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze @@ -49,6 +50,16 @@ def text end end + def processed_text + return text || '' unless converted_object_type? + + [ + title.presence && "

#{title}

", + spoiler_text.presence, + linkify(url || uri), + ].compact.join("\n\n") + end + def spoiler_text if @object['summary'].present? @object['summary'] @@ -57,6 +68,12 @@ def spoiler_text end end + def processed_spoiler_text + return '' if converted_object_type? + + spoiler_text || '' + end + def title if @object['name'].present? @object['name'] @@ -179,6 +196,10 @@ def quote_approval_uri as_array(@object['quoteAuthorization']).first end + def converted_object_type? + equals_or_includes_any?(@object['type'], ActivityPub::Activity::CONVERTED_TYPES) + end + private def quote_subpolicy(subpolicy) diff --git a/app/lib/extractor.rb b/app/lib/extractor.rb index 7e647a75873a46..206d989bf3d6f5 100644 --- a/app/lib/extractor.rb +++ b/app/lib/extractor.rb @@ -54,7 +54,7 @@ def extract_mentions_or_lists_with_indices(text) end def extract_hashtags_with_indices(text, _options = {}) - return [] unless text&.index('#') + return [] unless text&.index(/[##]/) possible_entries = [] diff --git a/app/lib/signed_request.rb b/app/lib/signed_request.rb index ca86460e67399a..d8887d9596ae6c 100644 --- a/app/lib/signed_request.rb +++ b/app/lib/signed_request.rb @@ -238,7 +238,7 @@ def missing_required_signature_parameters? def initialize(request) @signature = - if Mastodon::Feature.http_message_signatures_enabled? && request.headers['signature-input'].present? + if request.headers['signature-input'].present? HttpMessageSignature.new(request) else HttpSignature.new(request) diff --git a/app/lib/status_cache_hydrator.rb b/app/lib/status_cache_hydrator.rb index ae56ff1105f7d7..00558781011e38 100644 --- a/app/lib/status_cache_hydrator.rb +++ b/app/lib/status_cache_hydrator.rb @@ -63,6 +63,7 @@ def hydrate_reblog_payload(empty_payload, account_id, account, nested: false) payload[:filtered] = payload[:reblog][:filtered] payload[:favourited] = payload[:reblog][:favourited] payload[:reblogged] = payload[:reblog][:reblogged] + payload[:quote_approval] = payload[:reblog][:quote_approval] end end @@ -100,12 +101,12 @@ def hydrate_quote_payload(empty_payload, quote, account_id, nested: false) if quote.quoted_status.nil? payload[nested ? :quoted_status_id : :quoted_status] = nil payload[:state] = 'deleted' - elsif StatusFilter.new(quote.quoted_status, Account.find_by(id: account_id)).filtered_for_quote? - payload[nested ? :quoted_status_id : :quoted_status] = nil - payload[:state] = 'unauthorized' else - payload[:state] = 'accepted' - if nested + filter_state = StatusFilter.new(quote.quoted_status, Account.find_by(id: account_id)).filter_state_for_quote + payload[:state] = filter_state || 'accepted' + if filter_state == 'unauthorized' + payload[nested ? :quoted_status_id : :quoted_status] = nil + elsif nested payload[:quoted_status_id] = quote.quoted_status_id&.to_s else payload[:quoted_status] = StatusCacheHydrator.new(quote.quoted_status).hydrate(account_id, nested: true) diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb index 3400b202b7fa5b..7672c6402e1906 100644 --- a/app/lib/status_filter.rb +++ b/app/lib/status_filter.rb @@ -16,10 +16,18 @@ def filtered? blocked_by_policy? || (account_present? && filtered_status?) || silenced_account? end - def filtered_for_quote? - return false if !account.nil? && account.id == status.account_id - - blocked_by_policy? || (account_present? && filtered_status?) + def filter_state_for_quote + if !account.nil? && account.id == status.account_id + nil + elsif blocked_by_policy? + 'unauthorized' + elsif account_present? && blocking_domain? + 'blocked_domain' + elsif account_present? && blocking_account? + 'blocked_account' + elsif account_present? && muting_account? + 'muted_account' + end end private diff --git a/app/lib/vacuum/media_attachments_vacuum.rb b/app/lib/vacuum/media_attachments_vacuum.rb index e5581952905c7e..f9dc62d86b5ac8 100644 --- a/app/lib/vacuum/media_attachments_vacuum.rb +++ b/app/lib/vacuum/media_attachments_vacuum.rb @@ -17,12 +17,16 @@ def perform def vacuum_cached_files! media_attachments_past_retention_period.find_in_batches do |media_attachments| AttachmentBatch.new(MediaAttachment, media_attachments).clear + rescue => e + Rails.logger.error("Skipping batch while removing cached media attachments due to error: #{e}") end end def vacuum_orphaned_records! orphaned_media_attachments.find_in_batches do |media_attachments| AttachmentBatch.new(MediaAttachment, media_attachments).delete + rescue => e + Rails.logger.error("Skipping batch while removing orphaned media attachments due to error: #{e}") end end diff --git a/app/lib/vacuum/preview_cards_vacuum.rb b/app/lib/vacuum/preview_cards_vacuum.rb index 9e34c87c30c386..cc1c9efba9c9b7 100644 --- a/app/lib/vacuum/preview_cards_vacuum.rb +++ b/app/lib/vacuum/preview_cards_vacuum.rb @@ -16,6 +16,8 @@ def perform def vacuum_cached_images! preview_cards_past_retention_period.find_in_batches do |preview_card| AttachmentBatch.new(PreviewCard, preview_card).clear + rescue => e + Rails.logger.error("Skipping batch while removing cached preview cards due to error: #{e}") end end diff --git a/app/models/concerns/status/fetch_replies_concern.rb b/app/models/concerns/status/fetch_replies_concern.rb index 7ab46481747126..6d65fe41cb831f 100644 --- a/app/models/concerns/status/fetch_replies_concern.rb +++ b/app/models/concerns/status/fetch_replies_concern.rb @@ -4,8 +4,10 @@ module Status::FetchRepliesConcern extend ActiveSupport::Concern # debounce fetching all replies to minimize DoS - FETCH_REPLIES_COOLDOWN_MINUTES = (ENV['FETCH_REPLIES_COOLDOWN_MINUTES'] || 15).to_i.minutes - FETCH_REPLIES_INITIAL_WAIT_MINUTES = (ENV['FETCH_REPLIES_INITIAL_WAIT_MINUTES'] || 5).to_i.minutes + # Period to wait between fetching replies + FETCH_REPLIES_COOLDOWN_MINUTES = 15.minutes + # Period to wait after a post is first created before fetching its replies + FETCH_REPLIES_INITIAL_WAIT_MINUTES = 5.minutes included do scope :created_recently, -> { where(created_at: FETCH_REPLIES_INITIAL_WAIT_MINUTES.ago..) } diff --git a/app/models/export.rb b/app/models/export.rb index 6ed9f60c7c8cf4..b430a2a0d9d18c 100644 --- a/app/models/export.rb +++ b/app/models/export.rb @@ -12,7 +12,7 @@ def initialize(account) def to_bookmarks_csv CSV.generate do |csv| account.bookmarks.includes(:status).reorder(id: :desc).each do |bookmark| - csv << [ActivityPub::TagManager.instance.uri_for(bookmark.status)] + csv << [ActivityPub::TagManager.instance.uri_for(bookmark.status)] if bookmark.status.present? end end end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 0b241fbf9a15ab..406d08bdf99977 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -31,7 +31,6 @@ class Form::AdminSettings thumbnail mascot trends - trends_as_landing_page trendable_by_default show_domain_blocks show_domain_blocks_rationale @@ -71,6 +70,7 @@ class Form::AdminSettings remote_live_feed_access local_topic_feed_access remote_topic_feed_access + landing_page ).freeze INTEGER_KEYS = %i( @@ -97,7 +97,6 @@ class Form::AdminSettings preview_sensitive_media profile_directory trends - trends_as_landing_page trendable_by_default noindex require_invite_text @@ -142,7 +141,8 @@ class Form::AdminSettings DESCRIPTION_LIMIT = 200 DOMAIN_BLOCK_AUDIENCES = %w(disabled users all).freeze REGISTRATION_MODES = %w(open approved none).freeze - FEED_ACCESS_MODES = %w(public authenticated).freeze + FEED_ACCESS_MODES = %w(public authenticated disabled).freeze + LANDING_PAGE = %w(trends about local_feed).freeze attr_accessor(*KEYS) @@ -160,6 +160,7 @@ class Form::AdminSettings validates :site_short_description, length: { maximum: DESCRIPTION_LIMIT }, if: -> { defined?(@site_short_description) } validates :status_page_url, url: true, allow_blank: true validate :validate_site_uploads + validates :landing_page, inclusion: { in: LANDING_PAGE }, if: -> { defined?(@landing_page) } KEYS.each do |key| define_method(key) do diff --git a/app/models/link_feed.rb b/app/models/link_feed.rb index 29ea430cc0175a..4554796cc5c04c 100644 --- a/app/models/link_feed.rb +++ b/app/models/link_feed.rb @@ -15,18 +15,30 @@ def initialize(preview_card, account, options = {}) # @param [Integer] min_id # @return [Array] def get(limit, max_id = nil, since_id = nil, min_id = nil) + return [] if incompatible_feed_settings? + scope = public_scope scope.merge!(discoverable) scope.merge!(attached_to_preview_card) scope.merge!(account_filters_scope) if account? scope.merge!(language_scope) if account&.chosen_languages.present? + scope.merge!(local_only_scope) if local_only? + scope.merge!(remote_only_scope) if remote_only? scope.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id) end private + def local_feed_setting + Setting.local_topic_feed_access + end + + def remote_feed_setting + Setting.remote_topic_feed_access + end + def attached_to_preview_card Status.joins(:preview_cards_status).where(preview_cards_status: { preview_card_id: @preview_card.id }) end diff --git a/app/models/public_feed.rb b/app/models/public_feed.rb index c40a96e2e36965..da437b1ed4b1f8 100644 --- a/app/models/public_feed.rb +++ b/app/models/public_feed.rb @@ -20,6 +20,7 @@ def initialize(account, options = {}) # @return [Array] def get(limit, max_id = nil, since_id = nil, min_id = nil) return [] if local_only? && !Setting.enable_local_timeline + return [] if incompatible_feed_settings? scope = public_scope @@ -40,6 +41,21 @@ def get(limit, max_id = nil, since_id = nil, min_id = nil) attr_reader :account, :options + def incompatible_feed_settings? + (local_only? && !user_has_access_to_feed?(local_feed_setting)) || (remote_only? && !user_has_access_to_feed?(remote_feed_setting)) + end + + def user_has_access_to_feed?(setting) + case setting + when 'public' + true + when 'authenticated' + @account&.user&.functional? + when 'disabled' + @account&.user&.can?(:view_feeds) + end + end + def with_reblogs? options[:with_reblogs] end @@ -48,12 +64,20 @@ def with_replies? options[:with_replies] end + def local_feed_setting + Setting.local_live_feed_access + end + + def remote_feed_setting + Setting.remote_live_feed_access + end + def local_only? - options[:local] && !options[:remote] + (options[:local] && !options[:remote]) || !user_has_access_to_feed?(remote_feed_setting) end def remote_only? - options[:remote] && !options[:local] && Setting.enable_local_timeline + (options[:remote] && !options[:local] && Setting.enable_local_timeline) || !user_has_access_to_feed?(local_feed_setting) end def account? diff --git a/app/models/tag.rb b/app/models/tag.rb index 6a1b052d3fc2b1..7c05b6c5222fbf 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -42,7 +42,7 @@ class Tag < ApplicationRecord HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)' HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}".freeze - HASHTAG_RE = %r{(?] def get(limit, max_id = nil, since_id = nil, min_id = nil) return [] if local_only? && !Setting.enable_local_timeline + return [] if incompatible_feed_settings? scope = public_search_scope @@ -42,6 +43,14 @@ def get(limit, max_id = nil, since_id = nil, min_id = nil) private + def local_feed_setting + Setting.local_topic_feed_access + end + + def remote_feed_setting + Setting.remote_topic_feed_access + end + def tagged_with_any_scope Status.group(:id).tagged_with(tags_for(Array(@tag.name) | Array(options[:any]))) end diff --git a/app/models/user_role.rb b/app/models/user_role.rb index 38cbd746fdd23c..af121fd809d56b 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -36,6 +36,7 @@ class UserRole < ApplicationRecord manage_roles: (1 << 17), manage_user_access: (1 << 18), delete_user_data: (1 << 19), + view_feeds: (1 << 20), manage_sensitive_words: (1 << 29), manage_ng_words: (1 << 30), }.freeze @@ -70,6 +71,7 @@ module Flags manage_blocks manage_taxonomies manage_invites + view_feeds manage_ng_words manage_sensitive_words ).freeze, diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 273d6a14dbb0e0..e245186b54d1e6 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -50,7 +50,7 @@ def meta ].compact store[:enabled_visibilities] = enabled_visibilities store[:featured_tags] = object.current_account.featured_tags.pluck(:name) - store[:emoji_style] = object_account_user.settings['web.emoji_style'] if Mastodon::Feature.modern_emojis_enabled? + store[:emoji_style] = object_account_user.settings['web.emoji_style'] else store[:auto_play_gif] = Setting.auto_play_gif store[:display_media] = Setting.display_media @@ -148,7 +148,7 @@ def default_meta_store status_page_url: Setting.status_page_url, streaming_api_base_url: Rails.configuration.x.streaming_api_base_url, title: instance_presenter.title, - trends_as_landing_page: Setting.trends_as_landing_page, + landing_page: Setting.landing_page, trends_enabled: Setting.trends, version: instance_presenter.version, terms_of_service_enabled: TermsOfService.current.present?, diff --git a/app/serializers/rest/base_quote_serializer.rb b/app/serializers/rest/base_quote_serializer.rb index 2637014b697316..ac3b545d5c36ba 100644 --- a/app/serializers/rest/base_quote_serializer.rb +++ b/app/serializers/rest/base_quote_serializer.rb @@ -8,13 +8,12 @@ def state # Extra states when a status is unavailable return 'deleted' if object.quoted_status.nil? - return 'unauthorized' if status_filter.filtered_for_quote? - object.state + status_filter.filter_state_for_quote || object.state end def quoted_status - object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && !status_filter.filtered_for_quote? + object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized' end private diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index a46d57e0a9aab5..b64ec294dbf17a 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -245,9 +245,9 @@ def expires_at def quote_approval { - automatic: object.quote_policy_as_keys(:automatic), - manual: object.quote_policy_as_keys(:manual), - current_user: object.quote_policy_for_account(current_user&.account), + automatic: object.proper.quote_policy_as_keys(:automatic), + manual: object.proper.quote_policy_as_keys(:manual), + current_user: object.proper.quote_policy_for_account(current_user&.account), } end diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb index 5f2d0a69da342c..d6f8e79b2f6de1 100644 --- a/app/services/account_search_service.rb +++ b/app/services/account_search_service.rb @@ -6,7 +6,7 @@ class AccountSearchService < BaseService MENTION_ONLY_RE = /\A#{Account::MENTION_RE}\z/i # Min. number of characters to look for non-exact matches - MIN_QUERY_LENGTH = 5 + MIN_QUERY_LENGTH = 3 class QueryBuilder def initialize(query, account, options = {}) @@ -271,7 +271,7 @@ def from_elasticsearch ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call records - rescue Faraday::ConnectionFailed, Parslet::ParseFailed + rescue Faraday::ConnectionFailed, Parslet::ParseFailed, Errno::ENETUNREACH nil end diff --git a/app/services/activitypub/fetch_all_replies_service.rb b/app/services/activitypub/fetch_all_replies_service.rb index b771b8452656a6..a956e7c706f2b9 100644 --- a/app/services/activitypub/fetch_all_replies_service.rb +++ b/app/services/activitypub/fetch_all_replies_service.rb @@ -3,8 +3,8 @@ class ActivityPub::FetchAllRepliesService < ActivityPub::FetchRepliesService include JsonLdHelper - # Limit of replies to fetch per status - MAX_REPLIES = (ENV['FETCH_REPLIES_MAX_SINGLE'] || 500).to_i + # Max number of replies to fetch - for a single post + MAX_REPLIES = 500 def call(status_uri, collection_or_uri, max_pages: 1, batch_id: nil, request_id: nil) @status_uri = status_uri diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 5d8bca0cb366bd..ccf8d2b7c7564b 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -23,7 +23,6 @@ def call(status, activity_json, object_json, request_id: nil) @request_id = request_id @quote = nil - # Only native types can be updated at the moment return @status if !expected_type? || already_updated_more_recently? if @status_parser.edited_at.present? && (@status.edited_at.nil? || @status_parser.edited_at > @status.edited_at) @@ -227,8 +226,8 @@ def reference_to_local_stranger? end def update_immediate_attributes! - @status.text = @status_parser.text || '' - @status.spoiler_text = @status_parser.spoiler_text || '' + @status.text = @status_parser.processed_text + @status.spoiler_text = @status_parser.processed_spoiler_text @status.sensitive = @account.sensitized? || @status_parser.sensitive || false @status.language = @status_parser.language @@ -449,7 +448,7 @@ def update_counts! end def expected_type? - equals_or_includes_any?(@json['type'], %w(Note Question)) + equals_or_includes_any?(@json['type'], ActivityPub::Activity::SUPPORTED_TYPES) || equals_or_includes_any?(@json['type'], ActivityPub::Activity::CONVERTED_TYPES) end def record_previous_edit! diff --git a/app/services/statuses_search_service.rb b/app/services/statuses_search_service.rb index 3d87f80e71f30d..4a18d073845ee1 100644 --- a/app/services/statuses_search_service.rb +++ b/app/services/statuses_search_service.rb @@ -36,7 +36,7 @@ def status_search_results preloaded_relations = @account.relations_map(account_ids, account_domains) results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } - rescue Faraday::ConnectionFailed, Parslet::ParseFailed + rescue Faraday::ConnectionFailed, Parslet::ParseFailed, Errno::ENETUNREACH [] end diff --git a/app/services/tag_search_service.rb b/app/services/tag_search_service.rb index 57400b76ad140e..6a4af5c9a0a8cb 100644 --- a/app/services/tag_search_service.rb +++ b/app/services/tag_search_service.rb @@ -30,7 +30,7 @@ def from_elasticsearch definition = definition.filter(elastic_search_filter) if @options[:exclude_unreviewed] ensure_exact_match(definition.limit(@limit).offset(@offset).objects.compact) - rescue Faraday::ConnectionFailed, Parslet::ParseFailed + rescue Faraday::ConnectionFailed, Parslet::ParseFailed, Errno::ENETUNREACH nil end diff --git a/app/views/admin/settings/branding/show.html.haml b/app/views/admin/settings/branding/show.html.haml index 62e9c724592d92..05795a19745281 100644 --- a/app/views/admin/settings/branding/show.html.haml +++ b/app/views/admin/settings/branding/show.html.haml @@ -68,5 +68,12 @@ = material_symbol 'delete' = t('admin.site_uploads.delete') + .fields-row + = f.input :landing_page, + collection: f.object.class::LANDING_PAGE, + include_blank: false, + label_method: ->(page) { I18n.t("admin.settings.landing_page.values.#{page}") }, + wrapper: :with_label + .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/admin/settings/discovery/show.html.haml b/app/views/admin/settings/discovery/show.html.haml index 482fb913ab6494..58c194ca4618f2 100644 --- a/app/views/admin/settings/discovery/show.html.haml +++ b/app/views/admin/settings/discovery/show.html.haml @@ -17,11 +17,6 @@ as: :boolean, wrapper: :with_label - .fields-group - = f.input :trends_as_landing_page, - as: :boolean, - wrapper: :with_label - .fields-group = f.input :trendable_by_default, as: :boolean, diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index 83f602000c32f0..ad9e57972afaba 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -31,16 +31,15 @@ label: I18n.t('simple_form.labels.defaults.setting_theme'), wrapper: :with_label - - if Mastodon::Feature.modern_emojis_enabled? - .fields-group - = f.simple_fields_for :settings, current_user.settings do |ff| - = ff.input :'web.emoji_style', - collection: %w(auto twemoji native), - include_blank: false, - hint: I18n.t('simple_form.hints.defaults.setting_emoji_style'), - label: I18n.t('simple_form.labels.defaults.setting_emoji_style'), - label_method: ->(emoji_style) { I18n.t("emoji_styles.#{emoji_style}", default: emoji_style) }, - wrapper: :with_label + .fields-group + = f.simple_fields_for :settings, current_user.settings do |ff| + = ff.input :'web.emoji_style', + collection: %w(auto twemoji native), + include_blank: false, + hint: I18n.t('simple_form.hints.defaults.setting_emoji_style'), + label: I18n.t('simple_form.labels.defaults.setting_emoji_style'), + label_method: ->(emoji_style) { I18n.t("emoji_styles.#{emoji_style}", default: emoji_style) }, + wrapper: :with_label - unless I18n.locale == :en .flash-message.translation-prompt diff --git a/app/views/severed_relationships/_event.html.haml b/app/views/severed_relationships/_event.html.haml index adc7752cb960d3..f4279559b7078e 100644 --- a/app/views/severed_relationships/_event.html.haml +++ b/app/views/severed_relationships/_event.html.haml @@ -6,7 +6,7 @@ = l(event.created_at) %td= t("severed_relationships.event_type.#{event.type}", target_name: event.target_name) - if event.purged? - %td{ rowspan: 2 }= t('severed_relationships.purged') + %td{ colspan: 2 }= t('severed_relationships.purged') - else %td = render 'download', count: event.following_count, link: following_severed_relationship_path(event, format: :csv) diff --git a/app/workers/activitypub/fetch_all_replies_worker.rb b/app/workers/activitypub/fetch_all_replies_worker.rb index 128bfe7e8af5d3..2e91a3e95b8ae3 100644 --- a/app/workers/activitypub/fetch_all_replies_worker.rb +++ b/app/workers/activitypub/fetch_all_replies_worker.rb @@ -11,9 +11,10 @@ class ActivityPub::FetchAllRepliesWorker sidekiq_options queue: 'pull', retry: 3 - # Global max replies to fetch per request (all replies, recursively) - MAX_REPLIES = (ENV['FETCH_REPLIES_MAX_GLOBAL'] || 1000).to_i - MAX_PAGES = (ENV['FETCH_REPLIES_MAX_PAGES'] || 500).to_i + # Max number of replies to fetch - total, recursively through a whole reply tree + MAX_REPLIES = 1000 + # Max number of replies Collection pages to fetch - total + MAX_PAGES = 500 def perform(root_status_id, options = {}) @batch = WorkerBatch.new(options['batch_id']) diff --git a/config/locales/an.yml b/config/locales/an.yml index 64aa98ce5de9bc..ca7d6b8254dc19 100644 --- a/config/locales/an.yml +++ b/config/locales/an.yml @@ -680,7 +680,6 @@ an: title: Excluyir per defecto los usuarios d'a indexación d'os motors de busqueda discovery: follow_recommendations: Recomendacions de cuentas - preamble: Exposar conteniu interesant a la superficie ye fundamental pa incorporar nuevos usuarios que pueden no conoixer a dengún Mastodon. Controla cómo funcionan quantas opcions d'escubrimiento en o tuyo servidor. profile_directory: Directorio de perfils public_timelines: Linias de tiempo publicas publish_statistics: Publicar las estatisticas diff --git a/config/locales/ar.yml b/config/locales/ar.yml index fb60171805750c..8774b0227adb18 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -889,7 +889,6 @@ ar: title: عدم السماح مبدئيا لمحركات البحث بفهرسة الملفات التعريفية للمستخدمين discovery: follow_recommendations: اتبع التوصيات - preamble: يُعد إتاحة رؤية المحتوى المثير للاهتمام أمرًا ضروريًا لجذب مستخدمين جدد قد لا يعرفون أي شخص في Mastodon. تحكم في كيفية عمل ميزات الاكتشاف المختلفة على خادمك الخاص. privacy: الخصوصية profile_directory: دليل الصفحات التعريفية public_timelines: الخيوط الزمنية العامة diff --git a/config/locales/ast.yml b/config/locales/ast.yml index b0e1052382a3bb..9bb87ce8e12761 100644 --- a/config/locales/ast.yml +++ b/config/locales/ast.yml @@ -316,7 +316,6 @@ ast: title: Retención del conteníu discovery: follow_recommendations: Recomendación de cuentes - preamble: L'apaición de conteníu interesante ye fundamental p'atrayer persones nueves que nun conozan nada de Mastodon. Controla'l funcionamientu de delles funciones de descubrimientu d'esti sirvidor. profile_directory: Direutoriu de perfiles public_timelines: Llinies de tiempu públiques publish_statistics: Espublizamientu d'estadístiques diff --git a/config/locales/be.yml b/config/locales/be.yml index 5e5b1f1e65240b..2a9a61c34722f9 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -824,6 +824,8 @@ be: view_dashboard_description: Дазваляе праглядаць панэль кіравання і розныя метрыкі view_devops: DevOps view_devops_description: Дае доступ да панэляў кіравання Sidekiq і pgHero + view_feeds: Глядзець жывую і тэматычныя стужкі + view_feeds_description: Даць карыстальнікам доступ да жывой і тэматычных стужак, незалежна ад налад сервера title: Ролі rules: add_new: Дадаць правіла @@ -865,7 +867,7 @@ be: title: Перадвызначана выключыць карыстальнікаў з індэксацыі пашуковымі рухавікамі discovery: follow_recommendations: Выконвайце рэкамендацыі - preamble: Прадстаўленне цікавага кантэнту дапамагае прыцягнуць новых карыстальнікаў, якія могуць не ведаць нікога на Mastodon. Кантралюйце працу розных функцый выяўлення на вашым серверы. + preamble: Паказ цікавага кантэнту карысны ў прывабліванні новых карыстальнікаў, якія могуць нікога не ведаць у Mastodon. Кантралюйце, як розныя функцыі выяўлення працуюць на Вашым серверы. privacy: Прыватнасць profile_directory: Дырэкторыя профіляў public_timelines: Публічная паслядоўнасць публікацый @@ -879,7 +881,13 @@ be: feed_access: modes: authenticated: Толькі аўтэнтыфікаваныя карыстальнікі + disabled: Запатрабаваць адмысловую ролю карыстальніка public: Усе + landing_page: + values: + about: Падрабязна + local_feed: Тутэйшая стужка + trends: Трэнды registrations: moderation_recommandation: Пераканайцеся, што ў вас ёсць адэкватная і аператыўная каманда мадэратараў, перш чым адчыняць рэгістрацыю для ўсіх жадаючых! preamble: Кантралюйце, хто можа ствараць уліковы запіс на вашым серверы. diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 1f22bf0047e695..251eef9cb9ba5b 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -825,7 +825,6 @@ bg: title: По подразбиране изключете индексирането от търсачки за вашите потребители discovery: follow_recommendations: Препоръки за следване - preamble: За потребители, които са нови и не познават никого в Mastodon, показването на интересно съдържание е ключово. Настройте начина, по който различни функции по откриване на съдържание работят на вашия сървър. privacy: Поверителност profile_directory: Указател на профила public_timelines: Публични хронологии @@ -836,6 +835,14 @@ bg: all: До всеки disabled: До никого users: До влезнали локални потребители + feed_access: + modes: + authenticated: Само удостоверени потребители + disabled: Изисква особена потребителска роля + public: Всеки + landing_page: + values: + trends: Пламенности registrations: moderation_recommandation: Уверете се, че имате адекватен и реактивен модераторски екип преди да отворите регистриранията за всеки! preamble: Управлява кой може да създава акаунт на сървъра ви. @@ -889,6 +896,7 @@ bg: no_status_selected: Няма промяна, тъй като няма избрани публикации open: Отваряне на публикация original_status: Първообразна публикация + quotes: Цитати reblogs: Блогване пак replied_to_html: Отговорено до %{acct_link} status_changed: Публикацията променена @@ -896,6 +904,7 @@ bg: title: Публикации на акаунт - @%{name} trending: Изгряващи view_publicly: Преглед като публично + view_quoted_post: Преглед на цитираната публикация visibility: Видимост with_media: С мултимедия strikes: @@ -1166,7 +1175,10 @@ bg: hint_html: Ако желаете да се преместите от друг акаунт към този, тук можете да създадете псевдоним, което се изисква преди да можете да пристъпите към преместване на последователите си от стария акаунт към този. Това действие е безопасно и възстановимо. Миграцията към новия акаунт се инициира от стария акаунт. remove: Разкачвне на псевдонима appearance: + advanced_settings: Разширени настройки animations_and_accessibility: Анимация и достъпност + boosting_preferences: Настройки за подсилване + boosting_preferences_info_html: "Съвет: Без значение от настройките, Shift + Щрак върху иконата %{icon} Подсилване веднага ще подсили." discovery: Откриване localization: body: Mastodon е преведено от доброволци. @@ -1568,6 +1580,13 @@ bg: expires_at: Изтича на uses: Използвания title: Поканете хора + link_preview: + author_html: От %{name} + potentially_sensitive_content: + action: Щракване за показване + confirm_visit: Наистина ли искате да отворите тази връзка? + hide_button: Скриване + label: Възможно деликатно съдържание lists: errors: limit: Достигнахте максималния брой списъци @@ -1720,6 +1739,9 @@ bg: self_vote: Не може да гласувате в свои анкети too_few_options: трябва да има повече от един елемент too_many_options: не може да съдържа повече от %{max} елемента + vote: Гласувам + posting_defaults: + explanation: Тези настройки ще се употребяват като стандартни, когато създавате нови публикации, но може да ги редактирате за всяка публикация в редактора. preferences: other: Друго posting_defaults: По подразбиране за публикации @@ -1875,6 +1897,9 @@ bg: other: "%{count} видеозаписа" boosted_from_html: Раздуто от %{acct_link} content_warning: 'Предупреждение за съдържание: %{warning}' + content_warnings: + hide: Скриване на публ. + show: Показване на още default_language: Същият като езика на интерфейса disallowed_hashtags: one: 'съдържа непозволен хаштаг: %{tags}' @@ -1889,9 +1914,22 @@ bg: limit: Вече сте закачили максималния брой публикации ownership: Публикация на някого другиго не може да бъде закачена reblog: Раздуване не може да бъде закачано + quote_error: + not_available: Неналична публикация + pending_approval: Публикацията чака одобрение + revoked: Премахната публикация от автора + quote_policies: + followers: Само последователи + nobody: Само аз + public: Някой + quote_post_author: Цитирах публикация от %{acct} title: "%{name}: „%{quote}“" visibilities: + direct: Частно споменаване + private: Само последователи public: Публично + public_long: Всеки във и извън Mastodon + unlisted: Тиха публика statuses_cleanup: enabled: Автоматично изтриване на стари публикации enabled_hint: От само себе си трие публикациите ви, щом достигнат указания възрастов праг, освен ако не съвпаднат с някое от изключенията долу diff --git a/config/locales/ca.yml b/config/locales/ca.yml index c3be44daa8e853..caa472fb399b94 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -832,7 +832,6 @@ ca: title: Exclou per defecte els usuaris de la indexació dels motors de cerca discovery: follow_recommendations: Seguir les recomanacions - preamble: L'aparició de contingut interessant és fonamental per atraure els nous usuaris que podrien no saber res de Mastodon. Controla com funcionen diverses opcions de descobriment en el teu servidor. privacy: Privacitat profile_directory: Directori de perfils public_timelines: Línies de temps públiques diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 3919dbe98d0d8c..9c69ab844916b7 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -824,6 +824,8 @@ cs: view_dashboard_description: Umožňuje uživatelům přístup k ovládacímu panelu a různým metrikám view_devops: DevOps view_devops_description: Umožňuje uživatelům přístup k ovládacím panelům Sidekiq a pgHero + view_feeds: Zobrazit živé a tematické kanály + view_feeds_description: Umožňuje uživatelům přístup k živým a tematickým kanálům bez ohledu na nastavení serveru title: Role rules: add_new: Přidat pravidlo @@ -865,12 +867,12 @@ cs: title: Odhlásit uživatele ze standardního indexování vyhledávačů discovery: follow_recommendations: Doporučená sledování - preamble: Povrchový zajímavý obsah je užitečný pro zapojení nových uživatelů, kteří možná neznají žádného Mastodona. Mějte pod kontrolou, jak různé objevovací funkce fungují na vašem serveru. + preamble: Zobrazování zajímavého obsahu je užitečné pro zapojovní nových uživatelů, kteří nemusí na Mastodonu nikoho znát. Kontrolujte, jak fungují různé funkce objevování obsahu na vašem serveru. privacy: Soukromí profile_directory: Adresář profilů public_timelines: Veřejné časové osy publish_statistics: Zveřejnit statistiku - title: Objevujte + title: Objevování trends: Trendy domain_blocks: all: Všem @@ -879,7 +881,13 @@ cs: feed_access: modes: authenticated: Pouze autentifikovaní uživatelé + disabled: Vyžadovat specifickou uživatelskou roli public: Všichni + landing_page: + values: + about: O službě + local_feed: Místní kanál + trends: Trendy registrations: moderation_recommandation: Před otevřením registrací všem se ujistěte, že máte vhodný a reaktivní moderační tým! preamble: Mějte pod kontrolou, kdo může vytvořit účet na vašem serveru. @@ -933,6 +941,7 @@ cs: no_status_selected: Nebyly změněny žádné příspěvky, neboť žádné nebyly vybrány open: Otevřít příspěvek original_status: Původní příspěvek + quotes: Citace reblogs: Boosty replied_to_html: Odpověděl %{acct_link} status_changed: Příspěvek změněn @@ -940,6 +949,7 @@ cs: title: Příspěvky na účtu - @%{name} trending: Populární view_publicly: Zobrazit veřejně + view_quoted_post: Zobrazit citovaný příspěvek visibility: Viditelnost with_media: S médii strikes: @@ -1224,7 +1234,10 @@ cs: hint_html: Chcete-li se přesunout z jiného účtu na tento, můžete si zde vytvořit alias, který je vyžadován předtím, než můžete pokračovat přesunem sledujících ze starého účtu na tento. Tato akce sama o sobě je neškodná a vratná. Přesun účtu se zahajuje ze starého účtu. remove: Odpojit alias appearance: + advanced_settings: Pokročilá nastavení animations_and_accessibility: Animace a přístupnost + boosting_preferences: Předvolby boostování + boosting_preferences_info_html: "Tip: Bez ohledu na nastavení Shift + Klik na ikonu %{icon} Boost okamžitě boostne." discovery: Objevování localization: body: Mastodon je překládán dobrovolníky. @@ -2008,10 +2021,15 @@ cs: limit: Už jste si připnuli maximální počet příspěvků ownership: Nelze připnout příspěvek někoho jiného reblog: Boosty nelze připnout + quote_error: + not_available: Příspěvek není dostupný + pending_approval: Příspěvek čeká na schválení + revoked: Příspěvek odstraněn autorem quote_policies: followers: Pouze sledující nobody: Jen já public: Kdokoliv + quote_post_author: Citovali příspěvek od %{acct} title: "%{name}: „%{quote}“" visibilities: direct: Soukromá zmínka diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 61d1d9d8afab65..4b875b2c02cbd4 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -852,6 +852,8 @@ cy: view_dashboard_description: Yn galluogi defnyddwyr i gael mynediad i'r bwrdd gwaith a metrigau amrywiol view_devops: DevOps view_devops_description: Yn caniatáu i ddefnyddwyr gael mynediad i fyrddau gwaith Sidekiq a pgHero + view_feeds: Gweld ffrydiau byw a phynciol + view_feeds_description: Yn caniatáu i ddefnyddwyr gael mynediad at y ffrydiau byw a phynciol beth bynnag yw gosodiadau'r gweinydd title: Rolau rules: add_new: Ychwanegu rheol @@ -893,7 +895,7 @@ cy: title: Eithrio defnyddwyr o fynegai peiriannau chwilio, fel rhagosodiad discovery: follow_recommendations: Dilyn yr argymhellion - preamble: Mae amlygu cynnwys diddorol yn allweddol ar gyfer derbyn defnyddwyr newydd nad ydynt efallai'n gyfarwydd ag unrhyw un Mastodon. Rheolwch sut mae nodweddion darganfod amrywiol yn gweithio ar eich gweinydd. + preamble: Mae amlygu cynnwys diddorol yn allweddol wrth ddenu defnyddwyr newydd sydd ddim o bosib yn adnabod neb ar Mastodon. Rheolwch sut mae gwahanol nodweddion darganfod yn gweithio ar eich gweinydd. privacy: Preifatrwydd profile_directory: Cyfeiriadur proffiliau public_timelines: Ffrydiau cyhoeddus @@ -907,6 +909,7 @@ cy: feed_access: modes: authenticated: Defnyddwyr dilys yn unig + disabled: Gofyn am rôl defnyddiwr penodol public: Pawb registrations: moderation_recommandation: Gwnewch yn siŵr bod gennych chi dîm cymedroli digonol ac adweithiol cyn i chi agor cofrestriadau i bawb! @@ -961,6 +964,7 @@ cy: no_status_selected: Heb newid postiad gan na ddewiswyd dim un open: Agor postiad original_status: Postiad gwreiddiol + quotes: Dyfyniadau reblogs: Ailflogiadau replied_to_html: Wedi ymateb i %{acct_link} status_changed: Postiad wedi'i newid @@ -968,6 +972,7 @@ cy: title: Postiadau cyfrif - @%{name} trending: Yn trendio view_publicly: Gweld yn gyhoeddus + view_quoted_post: Gweld y postiad wedi'i ddyfynnu visibility: Gwelededd with_media: Gyda chyfryngau strikes: @@ -1262,7 +1267,10 @@ cy: hint_html: Os ydych chi am symud o gyfrif arall i'r un hwn, gallwch greu enw arall yma, sy'n ofynnol cyn y gallwch symud ymlaen i symud dilynwyr o'r hen gyfrif i'r un hwn. Mae'r weithred hon ynddo'i hun yn ddiniwed ac yn wrthdroadwy. Mae'r mudo cyfrif yn cael ei wneud o'r hen gyfrif. remove: Dadgysylltu'r enw arall appearance: + advanced_settings: Gosodiadau uwch animations_and_accessibility: Animeiddiadau a hygyrchedd + boosting_preferences: Dewisiadau hybu + boosting_preferences_info_html: "Awgrym: Beth bynnag yw'r gosodiadau, bydd Shift + Clici ar yr eicon Hybu %{icon} yn hybu'n syth." discovery: Darganfod localization: body: Mae Mastodon yn cael ei gyfieithu gan wirfoddolwyr. @@ -2094,10 +2102,15 @@ cy: limit: Rydych chi eisoes wedi pinio uchafswm nifer y postiadau ownership: Nid oes modd pinio postiad rhywun arall reblog: Nid oes modd pinio hwb + quote_error: + not_available: Dyw'r postiad ddim ar gael + pending_approval: Postiad ar ei ffordd + revoked: Postiad wedi'i ddileu gan yr awdur quote_policies: followers: Dilynwyr yn unig nobody: Dim ond fi public: Pawb + quote_post_author: Wedi dyfynnu postiad gan %{acct} title: '%{name}: "%{quote}"' visibilities: direct: Crybwylliad preifat diff --git a/config/locales/da.yml b/config/locales/da.yml index 969bece7dbe342..c6a30afaaee955 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -796,6 +796,8 @@ da: view_dashboard_description: Tillader brugere at tilgå Dashboard'et og forskellige målinger view_devops: DevOps view_devops_description: Tillader brugere at tilgå Sidekiq- og pgHero-dashboards + view_feeds: Se live- og emne-feeds + view_feeds_description: Giver brugerne adgang til live- og emne-feeds uanset serverindstillinger title: Roller rules: add_new: Tilføj regel @@ -837,7 +839,7 @@ da: title: Fravælg som standard søgemaskineindeksering for brugere discovery: follow_recommendations: Følg-anbefalinger - preamble: At vise interessant indhold er vitalt ifm. at få nye brugere om bord, som måske ikke kender nogen på Mastodon. Styr, hvordan forskellige opdagelsesfunktioner fungerer på serveren. + preamble: At bringe interessant indhold frem er afgørende for at engagere nye brugere, der måske ikke kender nogen på Mastodon. Kontroller, hvordan forskellige funktioner til at finde indhold fungerer på din server. privacy: Fortrolighed profile_directory: Profiloversigt public_timelines: Offentlige tidslinjer @@ -851,7 +853,13 @@ da: feed_access: modes: authenticated: Kun godkendte brugere + disabled: Kræv specifik brugerrolle public: Alle + landing_page: + values: + about: Om + local_feed: Lokalt feed + trends: Trends registrations: moderation_recommandation: Sørg for, at der er et tilstrækkeligt og reaktivt moderationsteam, før registrering åbnes for alle! preamble: Styr, hvem der kan oprette en konto på serveren. diff --git a/config/locales/de.yml b/config/locales/de.yml index db183a199d3fc3..e4a579575f12a0 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -837,7 +837,7 @@ de: title: Profile standardmäßig von der Suchmaschinen-Indizierung ausnehmen discovery: follow_recommendations: Follower-Empfehlungen - preamble: Das Auffinden interessanter Inhalte ist wichtig, um neue Nutzer einzubinden, die Mastodon noch nicht kennen. Bestimme, wie verschiedene Suchfunktionen auf deinem Server funktionieren. + preamble: Interessante Inhalte sichtbar zu machen, ist entscheidend, um neue Nutzer*innen, die möglicherweise noch niemanden auf Mastodon kennen, zu gewinnen. Überprüfe, wie die einzelnen Suchfunktionen auf deinem Server funktionieren. privacy: Datenschutz profile_directory: Profilverzeichnis public_timelines: Öffentliche Timeline @@ -852,6 +852,9 @@ de: modes: authenticated: Nur authentifizierte Nutzer*innen public: Alle + landing_page: + values: + about: Über registrations: moderation_recommandation: Bitte vergewissere dich, dass du ein geeignetes und reaktionsschnelles Moderationsteam hast, bevor du die Registrierungen uneingeschränkt zulässt! preamble: Lege fest, wer auf deinem Server ein Konto erstellen darf. @@ -2067,7 +2070,7 @@ de: title: Wichtige Mitteilung warning: appeal: Einspruch erheben - appeal_description: Wenn du glaubst, dass es sich um einen Fehler handelt, kannst du einen Einspruch an die Administration von %{instance} senden. + appeal_description: Solltest du der Meinung sein, dass es sich bei der Sperre um einen Fehler handelt, kannst du Einspruch erheben, in dem du dich an das Team von %{instance} wendest. categories: spam: Spam violation: Inhalt verstößt gegen die folgenden Serverregeln @@ -2077,7 +2080,7 @@ de: mark_statuses_as_sensitive: Ein oder mehrere deiner Beiträge wurden von den Moderator*innen von %{instance} mit einer Inhaltswarnung versehen. Das bedeutet, dass die Medien in den Beiträgen erst angeklickt werden müssen, bevor sie angezeigt werden. Beim Verfassen der nächsten Beiträge kannst du auch selbst eine Inhaltswarnung für hochgeladene Medien festlegen. sensitive: Von nun an werden alle deine hochgeladenen Mediendateien mit einer Inhaltswarnung versehen und hinter einer Warnung versteckt. silence: Du kannst dein Konto weiterhin verwenden, aber nur Personen, die dir bereits folgen, sehen deine Beiträge auf diesem Server. Ebenso kannst du aus verschiedenen Suchfunktionen ausgeschlossen werden. Andere können dir jedoch weiterhin manuell folgen. - suspend: Du kannst dein Konto nicht mehr verwenden und dein Profil und andere Daten sind nicht mehr verfügbar. Du kannst dich immer noch anmelden, um eine Sicherung deiner Daten anzufordern, bis die Daten innerhalb von 30 Tagen vollständig gelöscht wurden. Allerdings werden wir einige Daten speichern, um zu verhindern, dass du die Sperrung umgehst. + suspend: Du kannst dein Konto nicht mehr verwenden und dein Profil und weitere Daten sind nicht mehr verfügbar. Du kannst dich immer noch anmelden, um eine Sicherung deiner Daten anzufordern, bis die Daten innerhalb von 30 Tagen vollständig gelöscht wurden. Allerdings werden wir einige Daten behalten, um zu verhindern, dass du die Kontosperre umgehst. reason: 'Begründung:' statuses: 'Betroffene Beiträge:' subject: diff --git a/config/locales/devise.bg.yml b/config/locales/devise.bg.yml index 5cb41fa6163623..612658b6bd722b 100644 --- a/config/locales/devise.bg.yml +++ b/config/locales/devise.bg.yml @@ -7,6 +7,7 @@ bg: send_paranoid_instructions: Ако вашият имейл адрес съществува в нашата база данни, ще получите имейл с указания как да потвърдите имейл адреса си след няколко минути. Проверете спам папката си, ако не сте получили такъв имейл. failure: already_authenticated: Вече сте влезли. + closed_registrations: Вашият опит за регистриране е блокиран заради мрежова политика. Ако вярвате, че е грешка, то свържете се с %{email}. inactive: Акаунтът ви още не е задействан. invalid: Невалиден %{authentication_keys} или парола. last_attempt: Разполагате с още един опит преди акаунтът ви да се заключи. diff --git a/config/locales/devise.cs.yml b/config/locales/devise.cs.yml index 42ecc1d53e4a2c..e9aa54c65bfa98 100644 --- a/config/locales/devise.cs.yml +++ b/config/locales/devise.cs.yml @@ -7,6 +7,7 @@ cs: send_paranoid_instructions: Pokud je vaše e-mailová adresa v naší databázi, obdržíte za několik minut e-mail s instrukcemi pro potvrzení vaší e-mailové adresy. Pokud tento e-mail neobdržíte, podívejte se prosím také do složky „spam“. failure: already_authenticated: Již jste přihlášeni. + closed_registrations: Váš pokus o registraci byl zablokován z důvodů síťové politiky. Pokud se domníváte, že se jedná o chybu, kontaktujte %{email}. inactive: Váš účet ještě není aktivován. invalid: Neplatné %{authentication_keys} nebo heslo. last_attempt: Máte ještě jeden pokus, než bude váš účet uzamčen. diff --git a/config/locales/devise.cy.yml b/config/locales/devise.cy.yml index da383f70ae4704..7f3564dba35194 100644 --- a/config/locales/devise.cy.yml +++ b/config/locales/devise.cy.yml @@ -7,6 +7,7 @@ cy: send_paranoid_instructions: Os yw eich cyfeiriad e-bost yn bodoli yn ein cronfa ddata, byddwch yn derbyn e-bost gyda chyfarwyddiadau ar sut i gadarnhau eich cyfeiriad e-bost mewn ychydig funudau. Gwiriwch eich ffolder sbam os na dderbynioch yr e-bost hwn. failure: already_authenticated: Rydych chi eisoes wedi mewngofnodi. + closed_registrations: Mae eich ymgais i gofrestru wedi'i rhwystro oherwydd polisi rhwydwaith. Os ydych chi'n credu bod hwn yn wall, cysylltwch â %{email}. inactive: Nid yw eich cyfrif yn weithredol eto. invalid: "%{authentication_keys} neu gyfrinair annilys." last_attempt: Mae gennych un cyfle arall cyn i'ch cyfrif gael ei gloi. diff --git a/config/locales/devise.gd.yml b/config/locales/devise.gd.yml index d110bc799b68ba..c7ddce255897c9 100644 --- a/config/locales/devise.gd.yml +++ b/config/locales/devise.gd.yml @@ -7,6 +7,7 @@ gd: send_paranoid_instructions: Ma tha an seòladh puist-d agad san stòr-dàta againn, gheibh thu post-d an ceann corra mionaid le stiùireadh air mar a dhearbhas tu an seòladh puist-d agad. Thoir sùil air pasgan an spama agad mura faigh thu am post-d seo. failure: already_authenticated: Tha thu air do chlàradh a-steach mu thràth. + closed_registrations: Chaidh d’ oidhirp clàraidh a bhacadh ri linn poileasaidh lìonraidh. Ma tha thu dhen bheachd gur e mearachd a th’ ann, cuir fios gu %{email}. inactive: Cha deach an cunntas agad a ghnìomhachadh fhathast. invalid: "%{authentication_keys} no facal-faire mì-dhligheach." last_attempt: Tha aon oidhirp eile agad mus dèid an cunntas agad a ghlasadh. diff --git a/config/locales/devise.ko.yml b/config/locales/devise.ko.yml index b57ef9684c6828..ebdd3ed44993c9 100644 --- a/config/locales/devise.ko.yml +++ b/config/locales/devise.ko.yml @@ -7,6 +7,7 @@ ko: send_paranoid_instructions: 이메일 주소가 저희 데이터베이스에 있는 경우, 몇 분 내에 이메일 주소를 확인하는 방법에 대한 안내가 포함된 이메일을 받을 수 있습니다. 이 이메일을 받지 못했다면 스팸 폴더를 확인해 주세요. failure: already_authenticated: 이미 로그인 된 상태입니다. + closed_registrations: 네트워크 정책에 의해 가입 시도가 차단되었습니다. 오류라고 생각된다면 %{email}에 문의해주세요. inactive: 계정이 아직 활성화 되지 않았습니다. invalid: 올바르지 않은 %{authentication_keys} 혹은 암호입니다. last_attempt: 계정이 잠기기까지 한 번의 시도가 남았습니다. diff --git a/config/locales/devise.pl.yml b/config/locales/devise.pl.yml index 093ea6961dc5c7..1d5ad09c1bc804 100644 --- a/config/locales/devise.pl.yml +++ b/config/locales/devise.pl.yml @@ -7,6 +7,7 @@ pl: send_paranoid_instructions: Jeśli Twój adres e-mail już istnieje w naszej bazie danych, w ciągu kilku minut otrzymasz wiadomość e-mail z instrukcją jak potwierdzić Twój adres e-mail. Jeżeli nie otrzymano wiadomości, sprawdź folder ze spamem. failure: already_authenticated: Jesteś już zalogowany(-a). + closed_registrations: Twoja próba rejestracji została zablokowana ze względu na politykę sieciową. Jeśli uważasz, że jest to błąd, skontaktuj się z %{email}. inactive: Twoje konto nie zostało jeszcze aktywowane. invalid: Nieprawidłowy %{authentication_keys} lub hasło. last_attempt: Masz jeszcze jedną próbę; Twoje konto zostanie zablokowane jeśli się nie powiedzie. diff --git a/config/locales/devise.pt-BR.yml b/config/locales/devise.pt-BR.yml index aa1190dfd0f3e5..89a3800cf47d52 100644 --- a/config/locales/devise.pt-BR.yml +++ b/config/locales/devise.pt-BR.yml @@ -7,6 +7,7 @@ pt-BR: send_paranoid_instructions: Se o seu endereço de e-mail já existir em nosso banco de dados, você receberá um e-mail com instruções para confirmá-lo dentro de alguns minutos. Verifique sua caixa de spam caso ainda não o tenha recebido. failure: already_authenticated: Você entrou na sua conta. + closed_registrations: Sua tentativa de registro foi bloqueada devido a uma política de rede. Se você acredita que isso é um erro, entre em contato com %{email}. inactive: Sua conta não foi confirmada ainda. invalid: "%{authentication_keys} ou senha inválida." last_attempt: Você tem mais uma tentativa antes de sua conta ser bloqueada. diff --git a/config/locales/devise.pt-PT.yml b/config/locales/devise.pt-PT.yml index 9660c3edb07756..8db3487fb17adf 100644 --- a/config/locales/devise.pt-PT.yml +++ b/config/locales/devise.pt-PT.yml @@ -7,6 +7,7 @@ pt-PT: send_paranoid_instructions: Se o teu endereço de e-mail já existir na nossa base de dados, vais receber um e-mail com as instruções de confirmação dentro de alguns minutos. Verifica na caixa de spam se não recebeste o e-mail. failure: already_authenticated: Já iniciaste a sessão. + closed_registrations: A sua tentativa de registo foi bloqueada devido a uma política de rede. Se acredita que se trata de um erro, contacte %{email}. inactive: A tua conta ainda não está ativada. invalid: "%{authentication_keys} ou palavra-passe inválida." last_attempt: Tens mais uma tentativa antes de a tua conta ser bloqueada. diff --git a/config/locales/doorkeeper.pt-PT.yml b/config/locales/doorkeeper.pt-PT.yml index 6285794aa52bdb..ae7dc28b620b81 100644 --- a/config/locales/doorkeeper.pt-PT.yml +++ b/config/locales/doorkeeper.pt-PT.yml @@ -125,7 +125,7 @@ pt-PT: admin/reports: Administração de denúncias all: Acesso total à sua conta Mastodon blocks: Bloqueios - bookmarks: Marcadores + bookmarks: Itens Salvos conversations: Conversas crypto: Encriptação ponta a ponta favourites: Favoritos @@ -172,7 +172,7 @@ pt-PT: read: ler todos os dados da tua conta read:accounts: ver as informações da conta read:blocks: ver os teus bloqueios - read:bookmarks: ver os teus marcadores + read:bookmarks: ver os seus itens salvos read:favourites: ver os teus favoritos read:filters: ver os teus filtros read:follows: ver quem segues @@ -185,7 +185,7 @@ pt-PT: write: alterar todos os dados da tua conta write:accounts: alterar o teu perfil write:blocks: bloquear contas e domínios - write:bookmarks: marcar publicações + write:bookmarks: salvar publicações write:conversations: ocultar e eliminar conversas write:favourites: assinalar como favoritas write:filters: criar filtros diff --git a/config/locales/el.yml b/config/locales/el.yml index 0b3094b7cd551a..0f2339b1cadde9 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -796,6 +796,8 @@ el: view_dashboard_description: Επιτρέπει στους χρήστες να έχουν πρόσβαση στον ταμπλό πληροφοριών και σε διάφορες μετρήσεις view_devops: DevOps view_devops_description: Επιτρέπει στους χρήστες να έχουν πρόσβαση στα ταμπλό πληροφοριών Sidekiq και pgHero + view_feeds: Προβολή ζωντανών και θεματικών ροών + view_feeds_description: Επιτρέπει στους χρήστες να έχουν πρόσβαση στις ζωντανές και θεματικές ροές ανεξάρτητα από τις ρυθμίσεις του διακομιστή title: Ρόλοι rules: add_new: Προσθήκη κανόνα @@ -837,7 +839,7 @@ el: title: Εξαίρεση χρηστών από τις μηχανές αναζήτησης discovery: follow_recommendations: Ακολούθησε τις προτάσεις - preamble: Δημοσιεύοντας ενδιαφέρον περιεχόμενο είναι καθοριστικό για την ενσωμάτωση νέων χρηστών που μπορεί να μη γνωρίζουν κανέναν στο Mastodon. Έλεγξε πώς λειτουργούν διάφορες δυνατότητες ανακάλυψης στον διακομιστή σας. + preamble: Εμφανίζοντας ενδιαφέρον περιεχόμενο είναι καθοριστικό για την ενσωμάτωση νέων χρηστών που μπορεί να μη γνωρίζουν κανέναν στο Mastodon. Ελέγξτε πώς λειτουργούν διάφορες δυνατότητες ανακάλυψης στον διακομιστή σας. privacy: Απόρρητο profile_directory: Κατάλογος προφίλ public_timelines: Δημόσιες ροές @@ -851,7 +853,13 @@ el: feed_access: modes: authenticated: Πιστοποιημένοι χρήστες μόνο + disabled: Να απαιτείται συγκεκριμένος ρόλος χρήστη public: Όλοι + landing_page: + values: + about: Σχετικά + local_feed: Τοπική ροή + trends: Τάσεις registrations: moderation_recommandation: Παρακαλώ βεβαιώσου ότι έχεις μια επαρκής και ενεργή ομάδα συντονισμού πριν ανοίξεις τις εγγραφές για όλους! preamble: Έλεγξε ποιος μπορεί να δημιουργήσει ένα λογαριασμό στον διακομιστή σας. diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 8cfd1b76217c92..eda44eaa90468d 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -831,7 +831,6 @@ en-GB: title: Opt users out of search engine indexing by default discovery: follow_recommendations: Follow recommendations - preamble: Surfacing interesting content is instrumental in onboarding new users who may not know anyone Mastodon. Control how various discovery features work on your server. privacy: Privacy profile_directory: Profile directory public_timelines: Public timelines diff --git a/config/locales/en.yml b/config/locales/en.yml index f53b6cac182a22..461eafef2612e9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1029,6 +1029,8 @@ en: view_dashboard_description: Allows users to access the dashboard and various metrics view_devops: DevOps view_devops_description: Allows users to access Sidekiq and pgHero dashboards + view_feeds: View live and topic feeds + view_feeds_description: Allows users to access the live and topic feeds regardless of server settings title: Roles rules: add_new: Add rule @@ -1085,7 +1087,7 @@ en: emoji_reactions: Emoji reaction follow_recommendations: Follow recommendations friend_servers: Friend servers - preamble: Surfacing interesting content is instrumental in onboarding new users who may not know anyone Mastodon. Control how various discovery features work on your server. + preamble: Surfacing interesting content is instrumental in onboarding new users who may not know anyone on Mastodon. Control how various discovery features work on your server. privacy: Privacy profile_directory: Profile directory public_timelines: Public timelines @@ -1100,7 +1102,13 @@ en: feed_access: modes: authenticated: Authenticated users only + disabled: Require specific user role public: Everyone + landing_page: + values: + about: About + local_feed: Local feed + trends: Trends registrations: moderation_recommandation: Please make sure you have an adequate and reactive moderation team before you open registrations to everyone! preamble: Control who can create an account on your server. diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 2a8c98b1941de0..008a72ecffd81c 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -831,7 +831,6 @@ eo: title: Defaŭltigi, ke uzantoj ne estu aŭtomate aldonitaj al la serĉilo-indekso sen sia konsento discovery: follow_recommendations: Sekvorekomendoj - preamble: Interesa enhavo estas grava por novaj uzantoj kiuj eble ne konas ajn iun. privacy: Privateco profile_directory: Profilujo public_timelines: Publikaj templinioj diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 35aafafb3d1cf7..eec0c3f465d4f2 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -796,6 +796,8 @@ es-AR: view_dashboard_description: Permite a los usuarios acceder al panel de control y varias métricas view_devops: Operadores de desarrollo view_devops_description: Permite a los usuarios acceder a los paneles de Sidekiq y pgHero + view_feeds: Ver líneas temporales y por temas + view_feeds_description: Permite a los usuarios acceder a las líneas temporales en vivo y por temas, sin importar la configuración del servidor title: Roles rules: add_new: Agregar regla @@ -851,7 +853,13 @@ es-AR: feed_access: modes: authenticated: Solo usuarios autenticados + disabled: Requerir un rol de específico de usuario public: Todos + landing_page: + values: + about: Información + local_feed: Línea temporal local + trends: Tendencias registrations: moderation_recommandation: Por favor, ¡asegurate de tener un equipo de moderación adecuado y reactivo antes de abrir los registros a todos! preamble: Controlá quién puede crear una cuenta en tu servidor. diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 3e3e32896c72cc..ecfc06836ef0ff 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -796,6 +796,8 @@ es-MX: view_dashboard_description: Permite a los usuarios acceder al panel de control y varias métricas view_devops: DevOps view_devops_description: Permite a los usuarios acceder a los paneles de control Sidekiq y pgHero + view_feeds: Visualización de cronologías y tendencias + view_feeds_description: Permitir a los usuarios acceder a las cronologías públicas y tendencias sin importar la configuración del servidor title: Roles rules: add_new: Añadir norma @@ -837,7 +839,7 @@ es-MX: title: Optar por los usuarios fuera de la indexación en los motores de búsqueda por defecto discovery: follow_recommendations: Recomendaciones de cuentas - preamble: Exponer contenido interesante a la superficie es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie Mastodon. Controla cómo funcionan varias opciones de descubrimiento en tu servidor. + preamble: Exponer contenido interesante es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie en Mastodon. Controla cómo funcionan en tu servidor las diferentes opciones de descubrimiento. privacy: Privacidad profile_directory: Directorio de perfiles public_timelines: Lineas de tiempo públicas @@ -851,7 +853,13 @@ es-MX: feed_access: modes: authenticated: Solo usuarios autenticados + disabled: Requerir un rol de usuario específico public: Todos + landing_page: + values: + about: Acerca de + local_feed: Cronología local + trends: Tendencias registrations: moderation_recommandation: "¡Por favor, asegúrate de contar con un equipo de moderación adecuado y activo antes de abrir el registro al público!" preamble: Controla quién puede crear una cuenta en tu servidor. diff --git a/config/locales/es.yml b/config/locales/es.yml index 291a5f16aa5ccc..da0d9ac34bb9bc 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -796,6 +796,8 @@ es: view_dashboard_description: Permite a los usuarios acceder al panel de control y varias métricas view_devops: DevOps view_devops_description: Permite a los usuarios acceder a los paneles de control Sidekiq y pgHero + view_feeds: Visualización de cronologías y tendencias + view_feeds_description: Permitir a los usuarios acceder a las cronologías públicas y tendencias sin importar la configuración del servidor title: Roles rules: add_new: Añadir norma @@ -837,7 +839,7 @@ es: title: Excluir por defecto a los usuarios de la indexación del motor de búsqueda discovery: follow_recommendations: Recomendaciones de cuentas - preamble: Exponer contenido interesante a la superficie es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie Mastodon. Controla cómo funcionan varias opciones de descubrimiento en tu servidor. + preamble: Exponer contenido interesante es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie en Mastodon. Controla cómo funcionan en tu servidor las diferentes opciones de descubrimiento. privacy: Privacidad profile_directory: Directorio de perfiles public_timelines: Lineas de tiempo públicas @@ -851,7 +853,13 @@ es: feed_access: modes: authenticated: Solo usuarios autenticados + disabled: Requerir un rol de usuario específico public: Todos + landing_page: + values: + about: Acerca de + local_feed: Cronología local + trends: Tendencias registrations: moderation_recommandation: Por favor, ¡asegúrate de tener un equipo de moderación adecuado y reactivo antes de abrir los registros a todo el mundo! preamble: Controla quién puede crear una cuenta en tu servidor. diff --git a/config/locales/et.yml b/config/locales/et.yml index e9b082ab9343a9..8f56b20b1afffe 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -851,7 +851,11 @@ et: feed_access: modes: authenticated: Vaid autenditud kasutajad + disabled: Eelda konkreetse kasutajarolli olemasolu public: Kõik + landing_page: + values: + trends: Trendid registrations: moderation_recommandation: Enne kõigi jaoks registreerimise avamist veendu, et oleks olemas adekvaatne ja reageerimisvalmis modereerijaskond! preamble: Kes saab serveril konto luua. diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 9e1fcc88a7a436..242bfa51ae9e6e 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -798,7 +798,6 @@ eu: title: Erabiltzaileei bilaketa-motorraren indexaziotik at egoteko aukera ematen die lehenetsitako aukera modura discovery: follow_recommendations: Jarraitzeko gomendioak - preamble: Eduki interesgarria aurkitzea garrantzitsua da Mastodoneko erabiltzaile berrientzat, behar bada inor ez dutelako ezagutuko. Kontrolatu zure zerbitzariko aurkikuntza-ezaugarriek nola funtzionatzen duten. privacy: Pribatutasuna profile_directory: Profil-direktorioa public_timelines: Denbora-lerro publikoak diff --git a/config/locales/fa.yml b/config/locales/fa.yml index bfcb4815891ca4..92e305bcee3671 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -837,7 +837,6 @@ fa: title: درخواست خروج از اندیس‌گذاری پیش‌گزیدهٔ موتور جست‌وجو discovery: follow_recommendations: پیروی از پیشنهادها - preamble: ارائه محتوای جالب در جذب کاربران جدیدی که ممکن است کسی ماستودون را نشناسند، مفید است. نحوه عملکرد ویژگی‌های کشف مختلف روی سرور خود را کنترل کنید. privacy: محرمانگی profile_directory: شاخهٔ نمایه public_timelines: خط زمانی‌های عمومی diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 9a5d8f90053b0a..86494261296958 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -796,6 +796,8 @@ fi: view_dashboard_description: Sallii käyttäjille pääsyn koontinäyttöön ja erilaisiin mittareihin view_devops: DevOps view_devops_description: Sallii käyttäjille pääsyn Sidekiq- ja pgHero-hallintapaneeleihin + view_feeds: Näytä live- ja aihesyötteet + view_feeds_description: Sallii käyttäjien tarkastella live- ja aihesyötteitä palvelimen asetuksista riippumatta title: Roolit rules: add_new: Lisää sääntö @@ -837,7 +839,7 @@ fi: title: Jätä käyttäjät oletusarvoisesti hakukoneindeksoinnin ulkopuolelle discovery: follow_recommendations: Seurantasuositukset - preamble: Mielenkiintoisen sisällön esille tuominen auttaa saamaan uusia käyttäjiä, jotka eivät ehkä tunne ketään Mastodonista. Määrittele, kuinka erilaiset löytämisominaisuudet toimivat palvelimellasi. + preamble: Mielenkiintoisen sisällön esille tuominen on keskeistä uusien käyttäjien perehdyttämisessä, jotka eivät ehkä tunne ketään Mastodonissa. Hallitse, miten eri löydettävyysominaisuudet toimivat palvelimellasi. privacy: Yksityisyys profile_directory: Profiilihakemisto public_timelines: Julkiset aikajanat @@ -851,7 +853,13 @@ fi: feed_access: modes: authenticated: Vain todennetut käyttäjät + disabled: Vaadi tiettyä käyttäjäroolia public: Kaikki + landing_page: + values: + about: Tietoja + local_feed: Paikallinen syöte + trends: Trendit registrations: moderation_recommandation: Varmista, että sinulla on riittävä ja toimintavalmis joukko moderaattoreita, ennen kuin avaat rekisteröitymisen kaikille! preamble: Määritä, kuka voi luoda tilin palvelimellesi. diff --git a/config/locales/fo.yml b/config/locales/fo.yml index c1f02e17cee3ed..3938235eee8ac2 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -796,6 +796,8 @@ fo: view_dashboard_description: Gevur brúkarum atgongd til kunningarbrettið og ymisk mát view_devops: DevOps view_devops_description: Gevur brúkarum atgongd til Sidekiq- og pgHero-kunningarbretti + view_feeds: Vís beinleiðis rásir og evnisrásir + view_feeds_description: Loyvir brúkarum atgongd til beinleiðis rásir og evnisrásir, óansæð ambætisstillingar title: Leiklutir rules: add_new: Ger nýggja reglu @@ -851,7 +853,13 @@ fo: feed_access: modes: authenticated: Einans váttaðir brúkarar + disabled: Krev serstakan brúkaraleiklut public: Øll + landing_page: + values: + about: Um + local_feed: Lokal rás + trends: Rák registrations: moderation_recommandation: Vinarliga tryggja tær, at tú hevur eitt nøktandi og klárt umsjónartoymi, áðreen tú letur upp fyri skrásetingum frá øllum! preamble: Stýr, hvør kann stovna eina kontu á tínum ambætara. diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index 0ae44cf373a08c..b27d53eea8d167 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -812,7 +812,6 @@ fr-CA: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations - preamble: Faire apparaître un contenu intéressant est essentiel pour interagir avec de nouveaux utilisateurs qui ne connaissent peut-être personne sur Mastodonte. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. profile_directory: Annuaire des profils public_timelines: Fils publics publish_statistics: Publier les statistiques diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 42e0507fe404a7..6b04aca1afb681 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -812,7 +812,6 @@ fr: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations - preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. profile_directory: Annuaire des profils public_timelines: Fils publics publish_statistics: Publier les statistiques diff --git a/config/locales/fy.yml b/config/locales/fy.yml index bad1e43f2166a5..9c489c03ec4303 100644 --- a/config/locales/fy.yml +++ b/config/locales/fy.yml @@ -837,7 +837,6 @@ fy: title: Brûkers standert net troch sykmasinen yndeksearje litte discovery: follow_recommendations: Oanrekommandearre accounts - preamble: It toanen fan ynteressante ynhâld is fan essinsjeel belang foar it oan board heljen fan nije brûkers dy’t mooglik net ien fan Mastodon kinne. Bepaal hoe’t ferskate funksjes foar it ûntdekken fan ynhâld en brûkers op jo server wurkje. privacy: Privacy profile_directory: Profylmap public_timelines: Iepenbiere tiidlinen diff --git a/config/locales/ga.yml b/config/locales/ga.yml index 401751ce5b8523..cb27ec1f9e6f74 100644 --- a/config/locales/ga.yml +++ b/config/locales/ga.yml @@ -838,6 +838,8 @@ ga: view_dashboard_description: Ligeann sé d’úsáideoirí rochtain a fháil ar an deais agus ar mhéadrachtaí éagsúla view_devops: DevOps view_devops_description: Ligeann sé d’úsáideoirí rochtain a fháil ar dheais Sidekiq agus pgHero + view_feeds: Féach ar fhothaí beo agus topaicí + view_feeds_description: Ceadaíonn sé d’úsáideoirí rochtain a fháil ar na fothaí beo agus topaicí beag beann ar shocruithe an fhreastalaí title: Róil rules: add_new: Cruthaigh riail @@ -879,7 +881,7 @@ ga: title: Diúltaigh d'innéacsú inneall cuardaigh mar réamhshocrú d'úsáideoirí discovery: follow_recommendations: Lean na moltaí - preamble: Tá sé ríthábhachtach dromchla a chur ar ábhar suimiúil chun úsáideoirí nua a chur ar bord nach bhfuil aithne acu ar dhuine ar bith Mastodon. Rialú conas a oibríonn gnéithe fionnachtana éagsúla ar do fhreastalaí. + preamble: Tá sé ríthábhachtach ábhar suimiúil a chur chun cinn chun úsáideoirí nua a thabhairt isteach ar Mastodon, úsáideoirí nach mbeadh aithne acu ar aon duine. Rialú conas a oibríonn gnéithe éagsúla fionnachtana ar do fhreastalaí. privacy: Príobháideacht profile_directory: Eolaire próifíle public_timelines: Amlínte poiblí @@ -893,7 +895,13 @@ ga: feed_access: modes: authenticated: Úsáideoirí fíordheimhnithe amháin + disabled: Éiligh ról úsáideora sonrach public: Gach duine + landing_page: + values: + about: Maidir + local_feed: Fotha áitiúil + trends: Treochtaí registrations: moderation_recommandation: Cinntigh le do thoil go bhfuil foireann mhodhnóireachta imoibríoch leordhóthanach agat sula n-osclaíonn tú clárúcháin do gach duine! preamble: Rialú cé atá in ann cuntas a chruthú ar do fhreastalaí. diff --git a/config/locales/gd.yml b/config/locales/gd.yml index 802b925415367d..3b1f956fe12207 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -876,6 +876,10 @@ gd: all: Dhan a h-uile duine disabled: Na seall idir users: Dhan luchd-chleachdaidh a clàraich a-steach gu h-ionadail + feed_access: + modes: + authenticated: Cleachdaichean a chlàraich a-steach a-mhàin + public: A h-uile duine registrations: moderation_recommandation: Dèan cinnteach gu bheil sgioba maoir deiseil is deònach agad mus fhosgail thu an clàradh dhan a h-uile duine! preamble: Stiùirich cò dh’fhaodas cunntas a chruthachadh air an fhrithealaiche agad. @@ -929,6 +933,7 @@ gd: no_status_selected: Cha deach post sam bith atharrachadh o nach deach gin dhiubh a thaghadh open: Fosgail am post original_status: Am post tùsail + quotes: Luaidhean reblogs: Brosnachaidhean replied_to_html: Freagairt do %{acct_link} status_changed: Post air atharrachadh @@ -936,6 +941,7 @@ gd: title: Postaichean a’ chunntais – @%{name} trending: A’ treandadh view_publicly: Seall gu poblach + view_quoted_post: Seall am post ’ga luaidh visibility: Faicsinneachd with_media: Le meadhanan riutha strikes: @@ -1220,7 +1226,10 @@ gd: hint_html: Nam bu mhiann leat imrich o chunntas eile dhan fhear seo, ’s urrainn dhut alias a chruthachadh an-seo agus feumaidh tu sin a dhèanamh mus urrainn dhut tòiseachadh air an luchd-leantainn agad imrich on seann-chunntas dhan fhear seo. Tha an gnìomh seo fhèin neo-chronail is chan eil e buan. Tòisichidh tu air imrich a’ chunntais on t-seann-chunntas. remove: Dì-cheangail an t-alias appearance: + advanced_settings: Roghainnean adhartach animations_and_accessibility: Beòthachaidhean agus so-ruigsinneachd + boosting_preferences: Roghainnean brosnachaidh + boosting_preferences_info_html: "Gliocas: Ge b’ e dè na roghainnean, ma nì thuShift + Briogadh air ìomhaigheag%{icon} a’ bhrosnachaidh, thèid a bhrosnachadh sa bhad." discovery: Rùrachadh localization: body: Tha Mastodon ’ga eadar-theangachadh le saor-thoilich. @@ -1563,10 +1572,10 @@ gd: other: Tha thu an impis suas ri %{count} àrainn o %{filename} a chur an àite liosta nam bacaidhean àrainne agad. two: Tha thu an impis suas ri %{count} àrainn o %{filename} a chur an àite liosta nam bacaidhean àrainne agad. following_html: - few: Tha thu an impis suas ri %{count} cunntasan o %{filename} a leantainn agus sguiridh tu a leantainn duine sam bith eile. - one: Tha thu an impis suas ri %{count} chunntas o %{filename} a leantainn agus sguiridh tu a leantainn duine sam bith eile. - other: Tha thu an impis suas ri %{count} cunntas o %{filename} a leantainn agus sguiridh tu a leantainn duine sam bith eile. - two: Tha thu an impis suas ri %{count} chunntas o %{filename} a leantainn agus sguiridh tu a leantainn duine sam bith eile. + few: Tha thu an impis suas ri %{count} cunntasan o %{filename} a leantainn agus sguiridh tu a leantainn neach sam bith eile. + one: Tha thu an impis suas ri %{count} chunntas o %{filename} a leantainn agus sguiridh tu a leantainn neach sam bith eile. + other: Tha thu an impis suas ri %{count} cunntas o %{filename} a leantainn agus sguiridh tu a leantainn neach sam bith eile. + two: Tha thu an impis suas ri %{count} chunntas o %{filename} a leantainn agus sguiridh tu a leantainn neach sam bith eile. lists_html: few: Tha thu an impis susbaint %{filename} a chur an àite nan liostaichean agad. Thèid suas ri %{count} cunntasan a chur ri liostaichean ùra. one: Tha thu an impis susbaint %{filename} a chur an àite nan liostaichean agad. Thèid suas ri %{count} chunntas a chur ri liostaichean ùra. @@ -1662,6 +1671,13 @@ gd: expires_at: Falbhaidh an ùine air uses: Cleachdadh title: Thoir cuireadh do dhaoine + link_preview: + author_html: Le %{name} + potentially_sensitive_content: + action: Dèan briogadh gus a shealltainn + confirm_visit: A bheil thu cinnteach gu bheil thu airson an ceangal seo fhosgladh? + hide_button: Falaich + label: Susbaint fhrionasach ma dh’fhaoidte lists: errors: limit: Ràinig thu na tha ceadaichte dhut de liostaichean @@ -1978,6 +1994,9 @@ gd: two: "%{count} video" boosted_from_html: Brosnachadh o %{acct_link} content_warning: 'Rabhadh susbainte: %{warning}' + content_warnings: + hide: Falaich am post + show: Seall barrachd dheth default_language: Co-ionnan ri cànan na h-eadar-aghaidh disallowed_hashtags: few: "– bha na tagaichean hais toirmisgte seo ann: %{tags}" @@ -1994,16 +2013,21 @@ gd: limit: Tha an àireamh as motha de phostaichean prìnichte agad a tha ceadaichte ownership: Chan urrainn dhut post càich a phrìneachadh reblog: Chan urrainn dhut brosnachadh a phrìneachadh + quote_error: + not_available: Chan eil am post ri fhaighinn + pending_approval: Cha deach dèiligeadh ris a’ phost fhathast + revoked: Chaidh am post a thoirt air falbh leis an ùghdar quote_policies: followers: Luchd-leantainn a-mhàin nobody: Mi fhìn a-mhàin - public: Duine sam bith + public: Neach sam bith + quote_post_author: Luaidh air post le %{acct} title: "%{name}: “%{quote}”" visibilities: direct: Iomradh prìobhaideach private: Luchd-leantainn a-mhàin public: Poblach - public_long: Duine sam bith taobh a-staigh no a-muigh Mhastodon + public_long: Neach sam bith taobh a-staigh no a-muigh Mhastodon unlisted: Poblach ach sàmhach unlisted_long: Falaichte o na toraidhean-luirg, na treandaichean ’s na loichnichean-ama poblach statuses_cleanup: diff --git a/config/locales/gl.yml b/config/locales/gl.yml index fe278de63d0977..85d351d20e6b27 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -796,6 +796,8 @@ gl: view_dashboard_description: Permite acceder ao taboleiro e varias métricas do servidor view_devops: DevOps view_devops_description: Permite acceder aos taboleiros Sidekiq e phHero + view_feeds: Ver as canles do directo e temas + view_feeds_description: Permite ás usuarias acceder ás canles de directo e temas independentemento dos axustes do servidor. title: Roles rules: add_new: Engadir regra @@ -851,7 +853,13 @@ gl: feed_access: modes: authenticated: Só para usuarias con sesión iniciada + disabled: Requerir un rol da usuaria específico public: Para calquera + landing_page: + values: + about: Sobre + local_feed: Cronoloxía Local + trends: Tendencias registrations: moderation_recommandation: Por favor, pon interese en crear un equipo de moderación competente e reactivo antes de permitir que calquera poida crear unha conta! preamble: Xestiona quen pode crear unha conta no teu servidor. diff --git a/config/locales/he.yml b/config/locales/he.yml index c7b4bec65b92d6..eebc9000da7a76 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -824,6 +824,8 @@ he: view_dashboard_description: אפשר למשתמשים לגשת ללוח המחוונים view_devops: DevOps view_devops_description: מאפשר למשתמשים לגשת ללוחות המחוונים של Sidekiq ושל pgHero + view_feeds: צפיה בפיד החי ונושאים + view_feeds_description: מאפשר למשמתמשיםות גישה לפיד החי ופיד נושאים בלי קשר להגדרות השרת title: תפקידים rules: add_new: הוספת כלל @@ -879,7 +881,13 @@ he: feed_access: modes: authenticated: משתמשים מאומתים בלבד + disabled: נדרש תפקיד משתמש מסוים public: כולם + landing_page: + values: + about: אודות + local_feed: פיד מקומי + trends: נושאים חמים registrations: moderation_recommandation: יש לוודא שלאתר יש צוות מנחות ומנחי שיחה מספק ושירותי בטרם תבחרו לפתוח הרשמה לכולם! preamble: שליטה בהרשאות יצירת חשבון בשרת שלך. diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 67a175acdc950c..a90e8a5c2ca526 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -796,6 +796,8 @@ hu: view_dashboard_description: Lehetővé teszi, hogy a felhasználó elérje az irányítópultot és vele számos metrikát view_devops: DevOps view_devops_description: Lehetővé teszi, hogy a felhasználó elérje a Sidekiq és pgHero irányítópultjait + view_feeds: Élő és témahírfolyamok megtekintése + view_feeds_description: A kiszolgálóbeállításoktól függetlenül engedélyezi az élő és témahírfolyamok elérését title: Szerepek rules: add_new: Szabály hozzáadása @@ -851,6 +853,7 @@ hu: feed_access: modes: authenticated: Csak hitelesített felhasználók + disabled: Konkrét felhasználói szerepkör megkövetelése public: Mindenki registrations: moderation_recommandation: Győződj meg arról, hogy megfelelő és gyors reagálású moderátor csapatod van, mielőtt mindenki számára megnyitod a regisztrációt! diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 08f40d511393a7..b08b01e0ef2b9e 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -320,7 +320,7 @@ ia: edit: title: Modificar annuncio empty: Necun annuncios trovate. - live: In directo + live: In vivo new: create: Crear annuncio title: Nove annuncio @@ -796,6 +796,8 @@ ia: view_dashboard_description: Permitte que usatores accede al tabuliero de instrumentos e a varie statisticas view_devops: DevOps view_devops_description: Permitte que usatores accede al tabulieros de instrumentos de Sidekiq e pgHero + view_feeds: Vider canales thematic e in vivo + view_feeds_description: Permitte que usatores acceder al canales thematic e in vivo independentemente del configuration del servitor title: Rolos rules: add_new: Adder regula @@ -851,7 +853,13 @@ ia: feed_access: modes: authenticated: Solmente usatores authenticate + disabled: Requirer un rolo de usator specific public: Omnes + landing_page: + values: + about: A proposito + local_feed: Canal local + trends: Tendentias registrations: moderation_recommandation: Per favor assecura te de haber un equipa de moderation adequate e reactive ante de aperir le inscription a omnes! preamble: Controla qui pote crear un conto sur tu servitor. diff --git a/config/locales/id.yml b/config/locales/id.yml index 81fc5903107240..eea3637e334761 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -670,7 +670,6 @@ id: title: Keluarkan pengguna dari pengindeksan mesin telusur secara bawaan discovery: follow_recommendations: Ikuti rekomendasi - preamble: Menampilkan konten menarik penting dalam memandu pengguna baru yang mungkin tidak tahu siapa pun di Mastodon. Atur bagaimana berbagai fitur penemuan bekerja di server Anda. profile_directory: Direktori profil public_timelines: Linimasa publik title: Penemuan diff --git a/config/locales/ie.yml b/config/locales/ie.yml index ce219a9d6826bd..f92894853632d8 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -729,7 +729,6 @@ ie: title: Predefinir que usatores ne apari in índexes de serchatores discovery: follow_recommendations: Seque-recomandationes - preamble: Exposir interessant contenete es importantissim por incorporar nov usatores qui fórsan conosse nequi che Mastodon. Decider qualmen diferent utensiles de decovrition functiona che vor servitor. profile_directory: Profilarium public_timelines: Public témpor-lineas publish_statistics: Publicar statisticas diff --git a/config/locales/io.yml b/config/locales/io.yml index eaca822f1afdbd..924df63c7ddacd 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -780,7 +780,6 @@ io: title: Despartoprenigez uzanti de serchilo-indexi quale originala stando discovery: follow_recommendations: Sequez rekomendaji - preamble: Montrar interesanta kontenajo esas importanta ye voligar nova uzanti quo forsan ne savas irgu. Dominacez quale ca deskovrotraiti funcionar en ca servilo. profile_directory: Profiluyo public_timelines: Publika tempolinei publish_statistics: Publikar statistiki diff --git a/config/locales/is.yml b/config/locales/is.yml index 7bcaf5648f822b..104bd638941c9d 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -796,6 +796,8 @@ is: view_dashboard_description: Leyfir notendum að skoða stjórnborðið og sjá ýmsar mælingar view_devops: DevOps view_devops_description: Leyfir notendum að skoða Sidekiq og pgHero stjórnborð + view_feeds: Skoða bein streymi og efnistengd + view_feeds_description: Gefur notendum aðgang að beinum streymum og efnistengdum, burtséð frá stillingum netþjóns title: Hlutverk rules: add_new: Skrá reglu @@ -853,7 +855,13 @@ is: feed_access: modes: authenticated: Einungis auðkenndir notendur + disabled: Krefjast sérstaks hlutverks notanda public: Allir + landing_page: + values: + about: Um hugbúnaðinn + local_feed: Staðbundið streymi + trends: Vinsælt registrations: moderation_recommandation: Tryggðu að þú hafir hæft og aðgengilegt umsjónarteymi til taks áður en þú opnar á skráningar fyrir alla! preamble: Stýrðu því hverjir geta útbúið notandaaðgang á netþjóninum þínum. diff --git a/config/locales/it.yml b/config/locales/it.yml index 9e7a2a15cb6fef..d1061229023e49 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -796,6 +796,8 @@ it: view_dashboard_description: Consente agli utenti di accedere alla dashboard e alle varie metriche view_devops: DevOps view_devops_description: Consente agli utenti di accedere alle dashboard Sidekiq e pgHero + view_feeds: Visualizza feed in diretta e feed di argomenti + view_feeds_description: Consente agli utenti di accedere ai feed in diretta e ai feed di argomenti indipendentemente dalle impostazioni del server title: Ruoli rules: add_new: Aggiungi regola @@ -837,7 +839,7 @@ it: title: Esclude gli utenti dall'indicizzazione dei motori di ricerca per impostazione predefinita discovery: follow_recommendations: Segui le raccomandazioni - preamble: La comparsa di contenuti interessanti è determinante per l'arrivo di nuovi utenti che potrebbero non conoscere nessuno su Mastodon. Controlla in che modo varie funzionalità di scoperta funzionano sul tuo server. + preamble: La scoperta di contenuti interessanti è fondamentale per l'inserimento di nuovi utenti che potrebbero non conoscere nessuno su Mastodon. Controlla l'andamento delle varie funzionalità di scoperta sul tuo server. privacy: Privacy profile_directory: Directory del profilo public_timelines: Timeline pubbliche @@ -851,6 +853,7 @@ it: feed_access: modes: authenticated: Solo utenti autenticati + disabled: Richiedi un ruolo utente specifico public: Tutti registrations: moderation_recommandation: Assicurati di avere un team di moderazione adeguato e reattivo prima di aprire le registrazioni a tutti! diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 490a576600cb67..a38baafa64305f 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1062,7 +1062,6 @@ ja: emoji_reactions: 絵文字リアクション follow_recommendations: おすすめフォロー friend_servers: フレンドサーバー - preamble: Mastodon を知らないユーザーを取り込むには、興味深いコンテンツを浮上させることが重要です。サーバー上で様々なディスカバリー機能がどのように機能するかを制御します。 privacy: プライバシー profile_directory: ディレクトリ public_timelines: 公開タイムライン diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3402c5487ed1a5..118f7ed905d8ac 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -784,6 +784,8 @@ ko: view_dashboard_description: 사용자가 여러 통계정보를 볼 수 있는 대시보드에 접근할 수 있도록 허용 view_devops: 데브옵스 view_devops_description: Sidekiq과 pgHero 대시보드에 접근할 수 있도록 허용 + view_feeds: 실시간, 해시태그 피드 보기 + view_feeds_description: 서버 설정에 관계 없이 실시간과 해시태그 피드에 접근할 수 있도록 허용 title: 역할 rules: add_new: 규칙 추가 @@ -825,7 +827,6 @@ ko: title: 사용자들이 기본적으로 검색엔진에 인덱싱되지 않도록 합니다 discovery: follow_recommendations: 팔로우 추천 - preamble: 흥미로운 콘텐츠를 노출하는 것은 마스토돈을 알지 못할 수도 있는 신규 사용자를 유입시키는 데 중요합니다. 이 서버에서 작동하는 다양한 발견하기 기능을 제어합니다. privacy: 개인정보 profile_directory: 프로필 책자 public_timelines: 공개 타임라인 @@ -836,6 +837,16 @@ ko: all: 모두에게 disabled: 아무에게도 안 함 users: 로그인 한 사용자에게 + feed_access: + modes: + authenticated: 로그인한 사용자들만 + disabled: 특정한 사용자 역할 필요 + public: 모두 + landing_page: + values: + about: 정보 + local_feed: 로컬 피드 + trends: 유행 registrations: moderation_recommandation: 모두에게 가입을 열기 전에 적절하고 반응이 빠른 중재 팀을 데리고 있는지 확인해 주세요! preamble: 누가 이 서버에 계정을 만들 수 있는지 제어합니다. @@ -897,6 +908,7 @@ ko: title: 계정 게시물 - @%{name} trending: 유행 중 view_publicly: 공개시점으로 보기 + view_quoted_post: 인용된 게시물 보기 visibility: 공개 설정 with_media: 미디어 있음 strikes: @@ -1166,7 +1178,10 @@ ko: hint_html: 다른 계정에서 이 계정으로 옮기길 원하는 경우, 여기에서 별칭을 만들 수 있습니다, 기존 계정의 팔로워를 이쪽으로 옮기고 싶은 경우 필요한 과정입니다. 이 행동 자체는 해롭지 않고 되돌리기가 가능합니다.계정 이주는 이전 계정에서 착수하게 됩니다 remove: 별칭 연결 끊기 appearance: + advanced_settings: 고급 설정 animations_and_accessibility: 애니메이션과 접근성 + boosting_preferences: 부스팅 설정 + boosting_preferences_info_html: "팁: 설정에 관계 없이 %{icon}을 쉬프트+클릭하여 곧바로 부스트할 수 있습니다." discovery: 발견하기 localization: body: 마스토돈은 자원봉사자들에 의해 번역되었습니다. @@ -1548,6 +1563,13 @@ ko: expires_at: 만료 uses: 이용 수 title: 초대하기 + link_preview: + author_html: "%{name} 작성" + potentially_sensitive_content: + action: 클릭하여 보기 + confirm_visit: 정말로 이 링크를 여시겠습니까? + hide_button: 숨기기 + label: 민감한 컨텐츠일 수 있음 lists: errors: limit: 리스트 최대 개수를 초과합니다 @@ -1855,6 +1877,9 @@ ko: other: "%{count} 개의 영상" boosted_from_html: "%{acct_link}의 글을 부스트" content_warning: '열람 주의: %{warning}' + content_warnings: + hide: 게시물 숨기기 + show: 더 보기 default_language: 화면 표시 언어와 동일하게 disallowed_hashtags: other: '허용되지 않은 해시태그를 포함하고 있습니다: %{tags}' @@ -1868,13 +1893,19 @@ ko: limit: 이미 너무 많은 게시물을 고정했습니다 ownership: 다른 사람의 게시물은 고정될 수 없습니다 reblog: 부스트는 고정될 수 없습니다 + quote_error: + not_available: 게시물 사용 불가 + pending_approval: 게시물 대기중 + revoked: 원작성자에 의해 게시물 삭제됨 quote_policies: followers: 팔로워만 nobody: 나만 public: 누구나 + quote_post_author: 인용된 %{acct} 님의 게시물 title: '%{name}: "%{quote}"' visibilities: direct: 개인 멘션 + private: 팔로워만 public: 공개 public_long: 마스토돈 내외 모두 unlisted: 조용한 공개 diff --git a/config/locales/ku.yml b/config/locales/ku.yml index 31a0a1cf18b450..a1b50a2befded2 100644 --- a/config/locales/ku.yml +++ b/config/locales/ku.yml @@ -679,7 +679,6 @@ ku: title: Pêlrêçkirna bikarhêneran ji motorê lêgerînê dûr bixe discovery: follow_recommendations: Pêşniyarên şopandinê - preamble: Rûbirûbûna naveroka balkêş ji bo bikarhênerên nû yên ku li ser Mastodon kesek nas nakin pir bi bandor e. Kontrol bike ka çend taybetmendiyên vekolînê li ser rajekarê te çawa dixebite. profile_directory: Rêgeha profîlê public_timelines: Demnameya gelemperî title: Vekolîne diff --git a/config/locales/lad.yml b/config/locales/lad.yml index df5188e5a3d263..a0486bfcc8ce5e 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -189,6 +189,7 @@ lad: create_relay: Kriya relevo create_unavailable_domain: Kriya domeno no desponivle create_user_role: Kriya rolo + create_username_block: Kriya regla de nombre de utilizador demote_user: Degrada utilizador destroy_announcement: Efasa pregon destroy_canonical_email_block: Efasa bloko de posta elektronika @@ -202,6 +203,7 @@ lad: destroy_status: Efasa publikasyon destroy_unavailable_domain: Efasa domeno no desponivle destroy_user_role: Efasa rolo + destroy_username_block: Suprime regla de nombre de utilizador disable_2fa_user: Inkapasita autentifikasyon en dos pasos disable_custom_emoji: Inkapasita emoji personalizados disable_relay: Dezaktiva relevo @@ -236,6 +238,7 @@ lad: update_report: Aktualiza raporto update_status: Aktualiza publikasyon update_user_role: Aktualiza rolo + update_username_block: Aktualiza regla de nombre de utilizador actions: approve_appeal_html: "%{name} acheto la solisitasyon de moderasyon de %{target}" approve_user_html: "%{name} acheto el enrejistramiento de %{target}" @@ -476,9 +479,12 @@ lad: ip: Adreso IP providers: active: Aktivo + base_url: URL baza delete: Efasa + edit: Edita prokurador finish_registration: Finaliza enrejistrasyon name: Nombre + providers: Prokuradores registration_requested: Enrejistrasyon rekerida registrations: confirm: Konfirma @@ -693,6 +699,7 @@ lad: delete_data_html: Efasa el profil i kontenido de @%{acct} en 30 dias si no sea desuspendido en akel tiempo preview_preamble_html: "@%{acct} resivira una avertensya komo esta:" record_strike_html: Enrejistra un amonestamiento kontra @%{acct} para ke te ayude eskalar las violasyones de reglas de este kuento en el avenir + send_email_html: Embia un mesaj de avertensia a la posta elektronika de @%{acct} warning_placeholder: Adisionalas, opsionalas razones la aksyon de moderasyon. target_origin: Orijin del kuento raportado title: Raportos @@ -769,6 +776,8 @@ lad: description_html: Aunke la majorita afirma aver meldado i estar de akodro kon los terminos de servisyo, la djente normalmente no los melda asta dempues de ke surja algun problema. Az ke sea mas kolay ver las normas de tu sirvidor de un vistazo estipulándolas en una lista de puntos. Aprova ke kada norma sea corta i kolay, ama sin estar divididas en munchos puntos. edit: Edita regla empty: Dinguna regla del sirvidor tiene sido definida. + move_down: Mueve verso abasho + move_up: Mueve verso arriva title: Reglas del sirvidor translation: Traduksyon translations: Traduksyones @@ -796,7 +805,7 @@ lad: title: Ekskluye utilizadores de la indeksasyon de los bushkadores komo preferensya predeterminada discovery: follow_recommendations: Rekomendasyones de kuentos - preamble: Ekspone kontenido enteresante a la superfisie es fundamental para inkorporar muevos utilizadores ke pueden no koneser a dinguno Mastodon. Kontrola komo fonksionan varias opsiones de diskuvrimiento en tu sirvidor. + preamble: Ekspone kontenido enteresante a la superfisie es fundamental para inkorporar muevos utilizadores ke pueden no koneser a dinguno en Mastodon. Kontrola komo fonksionan varias opsiones de diskuvrimiento en tu sirvidor. privacy: Privasita profile_directory: Katalogo de profiles public_timelines: Linyas de tiempo publikas @@ -810,6 +819,10 @@ lad: feed_access: modes: public: Todos + landing_page: + values: + about: Sovre esto + trends: Trendes registrations: moderation_recommandation: Por favor, asigurate ke tyenes una taifa de moderasyon adekuada i reaktiva antes de avrir los enrejistramyentos a todos! preamble: Kontrola ken puede kriyar un kuento en tu sirvidor. @@ -847,6 +860,7 @@ lad: back_to_account: Retorna al kuento back_to_report: Retorna a la pajina del raporto batch: + add_to_report: 'Adjusta al raporto #%{id}' remove_from_report: Kita del raporto report: Raporto contents: Kontenidos @@ -861,10 +875,12 @@ lad: no_status_selected: No se troko dinguna publikasyon al no eskojer dinguna open: Avre publikasyon original_status: Publikasyon orijinala + quotes: Sitas reblogs: Repartajasyones status_changed: Publikasyon trokada trending: Trendes view_publicly: Ve puvlikamente + view_quoted_post: Ve puvlikasyon sitada visibility: Vizivilita with_media: Kon multimedia strikes: @@ -942,13 +958,17 @@ lad: updated_msg: Konfigurasyon de etiketas aktualizada kon sukseso terms_of_service: changelog: Ke troko + create: Uza los tuyos current: Aktual + generate: Uza modelo generates: action: Djenera + title: Konfigurasyon de terminos de servisyo history: Istorya live: En bivo notify_users: Aviza a los utilizadores publish: Publika + published_on_html: Puvlikado el %{date} title: Terminos de servisyo title: Administrasyon trends: @@ -1020,10 +1040,19 @@ lad: block_registrations: Bloka enrejistrasyones comparison: contains: Kontyene + equals: Es egual a + contains_html: Kontyene %{string} + created_msg: Regla de nombre de utilizador kriyada kon reusho delete: Efasa + edit: + title: Edita regla de nombre de utilizador + matches_exactly_html: Es egual a %{string} new: create: Kriya regla + title: Kriya mueva regla de nombre de utilizador not_permitted: Sin permiso + title: Reglas de nombre de utilizador + updated_msg: Regla de nombre de utilizador aktualizada kon reusho warning_presets: add_new: Adjusta muevo delete: Efasa @@ -1095,7 +1124,9 @@ lad: hint_html: Si keres migrar de otro kuento a este, aki puedes kriyar un alias, kale proseder antes de ampesar a mover suivantes del kuento anterior a este. Esta aksion por si mezma es inofensiva i reversivle. La migrasyon del kuento se inisya dizde el kuento viejo. remove: Dezata alias appearance: + advanced_settings: Konfigurasyon avansada animations_and_accessibility: Animasyones i aksesivilita + boosting_preferences: Preferensias de repartajar discovery: Diskuvrimiento localization: body: Mastodon es trezladado por volontarios. @@ -1200,6 +1231,7 @@ lad: example_title: Teksto de enshemplo more_from_html: Mas de %{name} s_blog: Blog de %{name} + title: Atribusyon del otor challenge: confirm: Kontinua hint_html: "Konsejo: No retornaremos a demandarte por el kod durante la sigiente ora." @@ -1735,6 +1767,7 @@ lad: preferences: Preferensyas profile: Profil publiko relationships: Segidos i suivantes + severed_relationships: Relasyones kortadas statuses_cleanup: Efasasyon otomatika de publikasyones strikes: Amonestamientos de moderasyon two_factor_authentication: Autentifikasyon en dos pasos @@ -1742,6 +1775,8 @@ lad: severed_relationships: download: Abasha (%{count}) event_type: + account_suspension: Suspensyon de kuento (%{target_name}) + domain_block: Suspensyon de sirvidor (%{target_name}) user_domain_block: Blokates a %{target_name} lost_followers: Suivantes pedridos lost_follows: Segimyentos pedridos @@ -1777,10 +1812,15 @@ lad: limit: Ya tienes fiksado el numero maksimo de publikasyones ownership: La publikasyon de otra persona no puede fiksarse reblog: No se puede fixar una repartajasyon + quote_error: + not_available: Puvlikasyon no desponivle + pending_approval: Puvlikasyon esta asperando + revoked: Puvlikasyon kitada por el otor quote_policies: followers: Solo suivantes nobody: Solo yo public: Todos + quote_post_author: Sito una puvlikasyon de %{acct} title: '%{name}: "%{quote}"' visibilities: direct: Enmentadura privada @@ -1894,6 +1934,8 @@ lad: subject: Tu kuento fue aksedido dizde un muevo adreso IP title: Una mueva koneksyon kon tu kuento terms_of_service_changed: + sign_off: La taifa de %{domain} + subject: Aktualizasyones de muestros terminos de sirvisyo title: Aktualizasyon emportante warning: appeal: Embia una apelasyon diff --git a/config/locales/lv.yml b/config/locales/lv.yml index d3fc9e18491abf..40078ed3264269 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -832,7 +832,6 @@ lv: title: Pēc noklusējuma lietotāji būs atteikušies no meklētājprogrammu indeksēšanas discovery: follow_recommendations: Sekotšanas rekomendācijas - preamble: Interesanta satura parādīšana palīdz piesaistīt jaunus lietotājus, kuri, iespējams, nepazīst nevienu Mastodon. Kontrolē, kā tavā serverī darbojas dažādi atklāšanas līdzekļi. privacy: Konfidencialitāte profile_directory: Profila direktorija public_timelines: Publiskās ziņu lentas diff --git a/config/locales/ms.yml b/config/locales/ms.yml index dc3c9c36a9b069..76d7bd4d209766 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -714,7 +714,6 @@ ms: title: Tarik pengguna keluar daripada pengindeksan enjin carian secara lalai discovery: follow_recommendations: Ikut cadangan - preamble: Memaparkan kandungan yang menarik adalah penting dalam memasukkan pengguna baharu yang mungkin tidak mengenali sesiapa Mastodon. Kawal cara pelbagai ciri penemuan berfungsi pada server anda. profile_directory: Direktori profil public_timelines: Garis masa awam publish_statistics: Terbitkan statistik diff --git a/config/locales/my.yml b/config/locales/my.yml index bdf051ca6ee46a..687c350f3ef939 100644 --- a/config/locales/my.yml +++ b/config/locales/my.yml @@ -709,7 +709,6 @@ my: title: ပုံမှန်အားဖြင့် ရှာဖွေမှုအညွှန်းကိန်းမှ သုံးစွဲသူများကို ဖယ်ထုတ်ပါ discovery: follow_recommendations: အကြံပြုချက်များကို စောင့်ကြည့်ပါ - preamble: စိတ်ဝင်စားစရာကောင်းသော အကြောင်းအရာများပြထားခြင်းမှာ Mastodon ကို မသိသေးသော သုံးစွဲသူအသစ်များအတွက် အရေးပါပါသည်။ သင့်ဆာဗာတွင် မည်သည့်ရှာဖွေတွေ့ရှိမှုအကြောင်းအရာများ ပြထားမည်ကို ထိန်းချုပ်ပါ။ profile_directory: ပရိုဖိုင်လမ်းညွှန် public_timelines: အများမြင်စာမျက်နှာ publish_statistics: စာရင်းဇယားထုတ်ပြန်မည် diff --git a/config/locales/nan.yml b/config/locales/nan.yml index 19d9f8390ce920..8127cc56791d5c 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -782,6 +782,8 @@ nan: view_dashboard_description: 允准用者接近使用tsit ê la-jí-báng kap tsē-tsē指標 view_devops: DevOps view_devops_description: 允准用者接近使用Sidekiq kap pgHero ê la-jí-báng + view_feeds: 看即時內容kap主題ê feed + view_feeds_description: 允准用者接近使用即時kap主題feed,無論服侍器ê設定。 title: 角色 rules: add_new: 加添規則 @@ -837,6 +839,7 @@ nan: feed_access: modes: authenticated: Kan-ta hōo登入ê用者 + disabled: 愛特別ê用者角色 public: Ta̍k lâng registrations: moderation_recommandation: 佇開放hōo ta̍k ê lâng註冊進前,請確認lí有夠額koh主動反應ê管理團隊! @@ -1154,6 +1157,62 @@ nan: subject: "%{instance} 有通the̍h ê Mastodon ê新版本!" new_trends: body: 下kha ê項目愛審查,tsiah ē當公開顯示: + new_trending_links: + title: 趨勢ê連結 + new_trending_statuses: + title: 趨勢ê PO文 + new_trending_tags: + title: 趨勢ê hashtag + subject: "%{instance} 頂有新ê趨勢愛審查" + aliases: + add_new: 加添別名 + created_msg: 別名成功加添ah。Lí ē當開始tuì舊ê口座轉。 + deleted_msg: Thâi掉捌名成功。Bē當tuì hit ê口座轉kàu tsit ê ah。 + empty: Lí bô半个別名。 + hint_html: Nā lí beh tuì別ê口座轉kah tsit ê,lí通佇tsia加別名,tse是必要ê,了後lí ē當kā跟tuè lí ê tuì舊口座suá到tsit ê。Tsit ê動作是無害koh通還原著tī舊口座suá口座。 + remove: 取消連結別名 + appearance: + advanced_settings: 進一步ê設定 + animations_and_accessibility: 動畫kap無障礙設定 + boosting_preferences: 轉送ê偏好設定 + boosting_preferences_info_html: "撇步:無論án-nuá設定,Shift + Click 佇%{icon} 轉送標á ē liâm-mī轉送。" + discovery: 發現 + localization: + body: Mastodon是由志工翻譯。 + guide_link: https://crowdin.com/project/mastodon + guide_link_text: Ta̍k家lóng ē當貢獻。 + sensitive_content: 敏感ê內容 + application_mailer: + notification_preferences: 改電子phue ê偏好 + salutation: "%{name}、" + settings: 改電子phue ê偏好:%{link} + unsubscribe: 取消訂 + view: 檢視: + view_profile: 看個人資料 + view_status: 看PO文 + applications: + created: 應用程式成功加添 + destroyed: 應用程式成功thâi掉 + logout: 登出 + regenerate_token: 重頭產生接近使用ê token + token_regenerated: 接近使用ê token 成功重頭產生 + warning: 注意!毋通kā分享hōo別lâng! + your_token: Lí ê接近使用token + auth: + apply_for_account: 申請口座 + captcha_confirmation: + help_html: Nā lí完成CAPTCHA ê時陣有問題,lí通用 %{email} kap guán 聯絡,guán ē幫tsān lí。 + hint_html: 上尾步ah!Guán愛確認lí是人類(以免pùn-sò訊息入侵!)解決下kha êCAPTCHA了後,ji̍h「繼續」。 + title: 安全檢查 + confirmations: + awaiting_review: Lí ê電子phue地址有確認ah!%{domain} ê人員leh審查lí ê註冊。Nā in允准lí ê口座,Lí ē收著電子phue! + awaiting_review_title: Lí ê註冊當leh審查 + clicking_this_link: Ji̍h tsit ê連結 + login_link: 登入 + proceed_to_login_html: Lí tsit-má通繼續去 %{login_link}。 + redirect_to_app_html: Lí應該受重轉kàu %{app_name}應用程式,若是iáu-buē,試 %{clicking_this_link} á是手動轉去tsit ê應用程式。 + registration_complete: Lí佇 %{domain} ê註冊完成ah! + welcome_title: 歡迎 %{name}! scheduled_statuses: too_soon: Tio̍h用未來ê日期。 statuses: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 68f76a44a7558c..9179337c6f2319 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -796,6 +796,8 @@ nl: view_dashboard_description: Geeft gebruikers toegang tot het dashboard en verschillende statistieken view_devops: DevOps view_devops_description: Geeft gebruikers toegang tot de dashboards van Sidekiq en pgHero + view_feeds: Openbare en hashtagtijdlijnen bekijken + view_feeds_description: Hiermee kunnen gebruikers toegang krijgen tot de openbare en hashtagtijdlijnen, ongeacht de serverinstellingen title: Rollen rules: add_new: Regel toevoegen @@ -837,7 +839,7 @@ nl: title: Gebruikers standaard niet door zoekmachines laten indexeren discovery: follow_recommendations: Aanbevolen accounts - preamble: Het tonen van interessante inhoud is van essentieel belang voor het aan boord halen van nieuwe gebruikers, die mogelijk niemand van Mastodon kennen. Bepaal hoe verschillende functies voor het ontdekken van inhoud en gebruikers op jouw server werken. + preamble: Interessante inhoud uitlichten is van essentieel belang voor onboarding van nieuwe gebruikers, die mogelijk niemand op Mastodon kennen. Bepaal hoe verschillende functies voor het ontdekken van inhoud en gebruikers op jouw server werken. privacy: Privacy profile_directory: Gebruikersgids public_timelines: Openbare tijdlijnen @@ -851,7 +853,13 @@ nl: feed_access: modes: authenticated: Alleen ingelogde gebruikers + disabled: Specifieke gebruikersrol vereisen public: Iedereen + landing_page: + values: + about: Over + local_feed: Lokale tijdlijn + trends: Trends registrations: moderation_recommandation: Zorg ervoor dat je een adequaat en responsief moderatieteam hebt voordat je registraties voor iedereen openstelt! preamble: Toezicht houden op wie een account op deze server kan registreren. diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 614b0aa8ddc8ae..c3e37405c5eaa7 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -837,7 +837,7 @@ nn: title: Ikkje la brukarar indekserast av søkjemotorar som standard discovery: follow_recommendations: Fylgjeforslag - preamble: Å framheva interessant innhald er vitalt i mottakinga av nye brukarar som ikkje nødvendigvis kjenner nokon på Mastodon. Kontroller korleis oppdagingsfunksjonane på tenaren din fungerar. + preamble: Å framheva interessant innhald er viktig for å ta imot nye brukarar som ikkje nødvendigvis kjenner nokon på Mastodon. Kontroller korleis oppdagingsfunksjonane på tenaren din fungerer. privacy: Personvern profile_directory: Profilkatalog public_timelines: Offentlege tidsliner diff --git a/config/locales/no.yml b/config/locales/no.yml index 72cbad788e35ed..f627c931848e68 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -729,7 +729,6 @@ title: Ikke la brukere indekseres av søkemotorer som standard discovery: follow_recommendations: Følg anbefalinger - preamble: Å fremheve interessant innhold er viktig i ombordstigning av nye brukere som kanskje ikke kjenner noen Mastodon. Kontroller hvordan ulike oppdagelsesfunksjoner fungerer på serveren. profile_directory: Profilkatalog public_timelines: Offentlige tidslinjer publish_statistics: Publiser statistikk diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 418d908f8c2f99..cea6bf5ee4e877 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -331,6 +331,7 @@ pl: create: Utwórz ogłoszenie title: Nowe ogłoszenie preview: + disclaimer: Ponieważ użytkownicy nie mogą zrezygnować z otrzymywania powiadomień email, powinny one ograniczać się do ważnych ogłoszeń, takich jak powiadomienia o naruszeniu bezpieczeństwa danych osobowych lub zamknięciu serwera. explanation_html: 'Wiadomość e-mail zostanie wysłana do %{display_count} użytkowników. Otrzymają oni wiadomość o następującej treści:' title: Podgląd powiadomienia publish: Opublikuj @@ -507,6 +508,7 @@ pl: created_at: 'Utworzono:' delete: Usuń ip: Adres IP + request_body: Treść zgłoszenia providers: active: Aktywne base_url: Podstawowy adres URL @@ -515,10 +517,12 @@ pl: finish_registration: Zakończ rejestrację name: Nazwa providers: Dostawca + public_key_fingerprint: Odcisk klucza publicznego registration_requested: Wymagana rejestracja registrations: confirm: Zatwierdź reject: Odrzuć + title: Potwierdź rejestrację FASP save: Zapisz sign_in: Zaloguj się status: Status @@ -600,6 +604,10 @@ pl: title: Moderacja moderation_notes: create: Dodaj notatkę moderacyjną + created_msg: Notatka dotycząca moderacji instancji została pomyślnie utworzona! + description_html: Wyświetlaj i zostawiaj notatki dla innych moderatorów oraz dla siebie samego w przyszłości + destroyed_msg: Notatka dotycząca moderacji instancji została pomyślnie usunięta! + placeholder: Informacje o tej instancji, podjętych działaniach lub wszelkie inne informacje, które pomogą ci moderować tę instancję w przyszłości. title: Notatki moderacyjne private_comment: Prywatny komentarz public_comment: Publiczny komentarz @@ -812,6 +820,8 @@ pl: view_dashboard_description: Pozwala użytkownikom na dostęp do panelu i różnych metryk view_devops: DevOps view_devops_description: Pozwala użytkownikom na dostęp do paneli Sidekiq i pgHero + view_feeds: Wyświetlaj aktualności i kanały tematyczne + view_feeds_description: Umożliwia użytkownikom dostęp do aktualności i kanałów tematycznych niezależnie od ustawień serwera title: Role rules: add_new: Dodaj zasadę @@ -825,6 +835,7 @@ pl: title: Regulamin serwera translation: Tłumaczenie translations: Tłumaczenia + translations_explanation: Możesz opcjonalnie dodać tłumaczenia reguł. Wyświetlany będzie tekst domyślny, jeśli nie ma dostępnej wersji przetłumaczonej. Zawsze upewnij się, że podane tłumaczenie jest zgodne z tekstem domyślnym. settings: about: manage_rules: Zarządzaj regułami serwera @@ -849,7 +860,6 @@ pl: title: Domyślnie żądaj nieindeksowania użytkowników przez wyszukiwarki discovery: follow_recommendations: Polecane konta - preamble: Prezentowanie interesujących treści ma kluczowe znaczenie dla nowych użytkowników, którzy mogą nie znać nikogo z Mastodona. Kontroluj, jak różne funkcje odkrywania działają na Twoim serwerze. privacy: Prywatność profile_directory: Katalog profilów public_timelines: Publiczne osie czasu @@ -1977,6 +1987,8 @@ pl: title: '%{name}: "%{quote}"' visibilities: public: Publiczne + unlisted: Niewidoczny + unlisted_long: Ukryte w wynikach wyszukiwania Mastodona, trendach i publicznych osiach czasu statuses_cleanup: enabled: Automatycznie usuwaj stare wiadomości enabled_hint: Automatycznie usuwa Twoje posty, gdy osiągną określony próg wiekowy, chyba że spełniają jeden z poniższych wyjątków @@ -2022,6 +2034,8 @@ pl: terms_of_service: title: Regulamin terms_of_service_interstitial: + future_preamble_html: Wprowadzamy pewne zmiany w naszych warunkach świadczenia usług, które zaczną obowiązywać od %{date}. Zachęcamy do zapoznania się z aktualnymi warunkami. + past_preamble_html: Od czasu twojej ostatniej wizyty zmieniliśmy warunki korzystania z usługi. Zachęcamy do zapoznania się z aktualnymi warunkami. review_link: Przeglądnij Warunki Korzystania title: Warunki korzystania z %{domain} zmieniają się themes: @@ -2056,6 +2070,7 @@ pl: webauthn: Klucze bezpieczeństwa user_mailer: announcement_published: + description: 'Administratorzy %{domain} publikują ogłoszenie:' subject: Ogłoszenie serwisu title: Ogłoszenie serwisu %{domain} appeal_approved: @@ -2090,6 +2105,8 @@ pl: terms_of_service_changed: agreement: Kontynuując używanie %{domain}, zgadzasz się na te warunki. Jeśli nie zgadzasz się ze zaktualizowanymi warunkami, możesz wypowiedzieć umowę z %{domain} w dowolnym momencie, usuwając swoje konto. changelog: 'W skrócie oto co oznacza dla Ciebie ta aktualizacja:' + description: 'Otrzymujesz tę wiadomość email, ponieważ wprowadzamy pewne zmiany w naszych warunkach świadczenia usług w domenie %{domain}. Aktualizacje te zaczną obowiązywać od dnia %{date}. Zachęcamy do zapoznania się z pełną treścią zaktualizowanych warunków tutaj:' + description_html: Otrzymujesz tę wiadomość email, ponieważ wprowadzamy pewne zmiany w naszych warunkach świadczenia usług w domenie %{domain}. Aktualizacje te zaczną obowiązywać od dnia %{date}. Zachęcamy do zapoznania się z pełną treścią zaktualizowanych warunków tutaj. sign_off: Zespół %{domain} subject: Aktualizacja warunków korzystania z usług subtitle: Warunki korzystania z %{domain} zmieniają się diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 5af8275951087e..44e6d18311e326 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -837,7 +837,6 @@ pt-BR: title: Optar por excluir usuários da indexação de mecanismos de pesquisa por padrão discovery: follow_recommendations: Seguir recomendações - preamble: Navegar por um conteúdo interessante é fundamental para integrar novos usuários que podem não conhecer ninguém no Mastodon. Controle como várias características de descoberta funcionam no seu servidor. privacy: Privacidade profile_directory: Diretório de perfis public_timelines: Timelines públicas @@ -848,6 +847,16 @@ pt-BR: all: Para todos disabled: Para ninguém users: Para usuários locais logados + feed_access: + modes: + authenticated: Apenas usuários autenticados + disabled: Exige função específica de usuário + public: Todos + landing_page: + values: + about: Sobre + local_feed: Feed local + trends: Em alta registrations: moderation_recommandation: Por favor, certifique-se de ter uma equipe de moderação adequada e reativa antes de abrir as inscrições para todos! preamble: Controle quem pode criar uma conta no seu servidor. @@ -901,6 +910,7 @@ pt-BR: no_status_selected: Nenhuma publicação foi modificada porque nenhuma estava selecionada open: Publicação aberta original_status: Publicação original + quotes: Citações reblogs: Reblogs replied_to_html: Respondeu à %{acct_link} status_changed: Publicação alterada @@ -908,6 +918,7 @@ pt-BR: title: Publicações da conta - @%{name} trending: Em alta view_publicly: Ver publicamente + view_quoted_post: Visualizar citação publicada visibility: Visibilidade with_media: Com mídia strikes: @@ -1182,7 +1193,9 @@ pt-BR: hint_html: Se você quiser migrar de uma outra conta para esta, você pode criar um atalho aqui, o que é necessário antes que você possa migrar os seguidores da conta antiga para esta. Esta ação por si só é inofensiva e reversível. A migração da conta é iniciada pela conta antiga. remove: Desvincular alias appearance: + advanced_settings: Configurações avançadas animations_and_accessibility: Animações e acessibilidade + boosting_preferences: Adicionar preferências discovery: Descobrir localization: body: Mastodon é traduzido por voluntários. @@ -1584,6 +1597,13 @@ pt-BR: expires_at: Expira em uses: Usos title: Convidar pessoas + link_preview: + author_html: Por %{name} + potentially_sensitive_content: + action: Clique para mostrar + confirm_visit: Tem certeza que deseja abrir esse link? + hide_button: Ocultar + label: Conteúdo potencialmente sensível lists: errors: limit: Você atingiu o número máximo de listas @@ -1894,6 +1914,9 @@ pt-BR: other: "%{count} vídeos" boosted_from_html: Impulso de %{acct_link} content_warning: 'Aviso de conteúdo: %{warning}' + content_warnings: + hide: Ocultar publicação + show: Exibir mais default_language: Igual ao idioma da interface disallowed_hashtags: one: 'continha hashtag não permitida: %{tags}' @@ -1908,15 +1931,22 @@ pt-BR: limit: Você alcançou o número limite de publicações fixadas ownership: As publicações dos outros não podem ser fixadas reblog: Um impulso não pode ser fixado + quote_error: + not_available: Publicação indisponível + pending_approval: Publicação pendente + revoked: Publicação removida pelo autor quote_policies: followers: Apenas seguidores nobody: Apenas eu public: Qualquer um + quote_post_author: Publicação citada por %{acct} title: '%{name}: "%{quote}"' visibilities: direct: Citação privada + private: Apenas seguidores public: Público public_long: Qualquer um dentro ou fora do Mástodon + unlisted: Publicação silenciada unlisted_long: Oculto aos resultados de pesquisa em Mástodon statuses_cleanup: enabled: Excluir publicações antigas automaticamente diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index e420d0c2f929ce..24a21f886c51c3 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -796,6 +796,8 @@ pt-PT: view_dashboard_description: Permite aos utilizadores acederem ao painel de controlo e a várias estatísticas view_devops: DevOps view_devops_description: Permite aos utilizadores aceder aos painéis de controlo do Sidekiq e pgHero + view_feeds: Ver cronologia em tempo real e de etiquetas + view_feeds_description: Permitir aos utilizadores aceder às cronologias em tempo real e de etiquetas independentemente das definições do servidor title: Funções rules: add_new: Adicionar regra @@ -817,7 +819,7 @@ pt-PT: rules_hint: Existe uma área dedicada às regras a que os teus utilizadores devem aderir. title: Sobre allow_referrer_origin: - desc: Quando os seus utilizadores clicam em links para sites externos, o navegador deles pode enviar o endereço do seu servidor Mastodon como referenciador. Desative esta opção se isso identificar inequivocamente os seus utilizadores, por exemplo, se este for um servidor Mastodon pessoal. + desc: Quando os seus utilizadores clicam em hiperligações para sites externos, o navegador destes pode enviar o endereço do seu servidor Mastodon como referenciador. Desative esta opção se isso identificar inequivocamente os seus utilizadores, por exemplo, se este for um servidor Mastodon pessoal. title: Permitir que sites externos vejam o seu servidor Mastodon como uma fonte de tráfego appearance: preamble: Personaliza a interface web do Mastodon. @@ -837,7 +839,7 @@ pt-PT: title: Desativar, por omissão, a indexação de utilizadores por parte dos motores de pesquisa discovery: follow_recommendations: Recomendações de contas - preamble: Revelar conteúdos interessantes é fundamental para a entrada de novos utilizadores que podem não conhecer ninguém no Mastodon. Controla como os vários recursos de descoberta funcionam no teu servidor. + preamble: Apresentar conteúdos interessantes é fundamental para atrair novos utilizadores que talvez não conheçam ninguém no Mastodon. Controle como várias funcionalidades de descoberta funcionam no seu servidor. privacy: Privacidade profile_directory: Diretório de perfis public_timelines: Cronologias públicas @@ -851,7 +853,13 @@ pt-PT: feed_access: modes: authenticated: Apesar utilizadores autenticados + disabled: Requerer função de utilizador especifica public: Todos + landing_page: + values: + about: Sobre + local_feed: Cronologia local + trends: Tendências registrations: moderation_recommandation: Certifique-se de que dispõe de uma equipa de moderação adequada e reativa antes de abrir as inscrições a todos! preamble: Controle quem pode criar uma conta no seu servidor. @@ -905,6 +913,7 @@ pt-PT: no_status_selected: Nenhum estado foi alterado porque nenhum foi selecionado open: Abrir publicação original_status: Publicação original + quotes: Citações reblogs: Impulsos replied_to_html: Respondeu a %{acct_link} status_changed: Publicação alterada @@ -912,6 +921,7 @@ pt-PT: title: Publicações da conta - @%{name} trending: Em destaque view_publicly: Visualizar publicamente + view_quoted_post: Ver publicação citada visibility: Visibilidade with_media: Com multimédia strikes: @@ -1186,7 +1196,10 @@ pt-PT: hint_html: Se quiseres mudar de outra conta para esta, podes criar aqui um pseudónimo, que é necessário antes de poderes prosseguir com a migração de seguidores da conta antiga para esta. Esta ação por si só é inofensiva e reversível. A migração da conta é iniciada a partir da conta antiga. remove: Desvincular pseudónimo appearance: + advanced_settings: Definições avançadas animations_and_accessibility: Animações e acessibilidade + boosting_preferences: Definições de partilha + boosting_preferences_info_html: "Dica: Independente das definições, Shift + Clique no ícone %{icon} de Partilhar vai partilhar de imediato." discovery: Descobrir localization: body: O Mastodon é traduzido por voluntários. @@ -1221,7 +1234,7 @@ pt-PT: clicking_this_link: clicar nesta hiperligação login_link: iniciar sessão proceed_to_login_html: Podes agora prosseguir para %{login_link}. - redirect_to_app_html: Devias ter sido reencaminhado para a aplicação %{app_name}. Se isso não aconteceu, tenta %{clicking_this_link} ou regressa manualmente para a aplicação. + redirect_to_app_html: Devia ter sido reencaminhado para a aplicação %{app_name}. Se isso não aconteceu, tente %{clicking_this_link} ou regresse manualmente para a aplicação. registration_complete: O teu registo sem %{domain} está agora concluído! welcome_title: Bem-vindo(a), %{name}! wrong_email_hint: Se este endereço de correio eletrónico não estiver correto, podes alterá-lo nas definições de conta. @@ -1406,7 +1419,7 @@ pt-PT: request: Pedir o teu arquivo size: Tamanho blocks: Bloqueaste - bookmarks: Marcadores + bookmarks: Itens Salvos csv: CSV domain_blocks: Bloqueios de domínio lists: Listas @@ -1503,8 +1516,8 @@ pt-PT: one: Estás prestes a substituir a tua lista de bloqueios com até conta%{count} de %{filename}. other: Estás prestes a substituir a tua lista de bloqueios com até %{count} contas de %{filename}. bookmarks_html: - one: Estás prestes a substituir os teus marcadores com até %{count} publicações de %{filename}. - other: Estás prestes a substituir os teus marcadores com até %{count} publicação de %{filename}. + one: Está prestes a substituir os seus itens salvos com até %{count} publicação de %{filename}. + other: Está prestes a substituir os seus itens salvos com até %{count} publicações de %{filename}. domain_blocking_html: one: Estás prestes a substituir a tua lista de bloqueios de domínio com até %{count} domínio de %{filename}. other: Estás prestes a substituir a tua lista de bloqueios de domínio com até %{count} domínios de %{filename}. @@ -1522,8 +1535,8 @@ pt-PT: one: Estás prestes a bloquear até %{count} conta de %{filename}. other: Estás prestes a bloquear até %{count} contas de %{filename}. bookmarks_html: - one: Estás prestes a adicionar até %{count} publicação de %{filename} aos teus marcadores. - other: Estás prestes a adicionar até %{count} publicações de %{filename} aos teus marcadores. + one: Está prestes a adicionar até %{count} publicação de %{filename} aos teus marcadores. + other: Está prestes a adicionar até %{count} publicações de %{filename} aos teus marcadores. domain_blocking_html: one: Estás prestes a bloquear até %{count} domínio de %{filename}. other: Estás prestes a bloquear até %{count} domínios de %{filename}. @@ -1548,18 +1561,18 @@ pt-PT: time_started: Iniciado em titles: blocking: Importar contas bloqueadas - bookmarks: Importar marcadores + bookmarks: Importar Itens Salvos domain_blocking: Importar domínios bloqueados following: Importar contas seguidas lists: Importar listas muting: Importar contas ocultadas type: Tipo de importação type_groups: - constructive: Seguidores e marcadores + constructive: Seguidores e Itens Salvos destructive: Bloqueios e ocultados types: blocking: Lista de bloqueios - bookmarks: Marcadores + bookmarks: Itens Salvos domain_blocking: Lista de domínios bloqueados following: Lista de pessoas que estás a seguir lists: Listas @@ -1592,7 +1605,7 @@ pt-PT: author_html: Por %{name} potentially_sensitive_content: action: Clicar para mostrar - confirm_visit: Tem a certeza que prentende abrir esta ligação? + confirm_visit: Tem a certeza que prentende abrir esta hiperligação? hide_button: Esconder label: Conteúdo potencialmente sensível lists: @@ -1620,7 +1633,7 @@ pt-PT: follow: e-mails de notificação de seguidor follow_request: e-mails de pedido de seguidor mention: e-mails de notificação de menção - reblog: e-mails de notificação de impulsos + reblog: e-mails de notificação de partilhas resubscribe_html: Se tiveres anulado a subscrição por engano, podes voltar a subscrevê-la nas definições de notificação por e-mail. success_html: Não receberás novamente %{type} do Mastodon em %{domain} para o teu e-mail em %{email}. title: Cancelar subscrição @@ -1700,8 +1713,8 @@ pt-PT: subject: "%{name} citou a sua publicação" title: Nova citação reblog: - body: 'A tua publicação foi impulsionada por %{name}:' - subject: "%{name} impulsionou a tua publicação" + body: 'A tua publicação foi partilhada por %{name}:' + subject: "%{name} partilhou a sua publicação" title: Novo impulso status: subject: "%{name} acabou de publicar" @@ -1903,7 +1916,7 @@ pt-PT: video: one: "%{count} vídeo" other: "%{count} vídeos" - boosted_from_html: Impulsionado por %{acct_link} + boosted_from_html: Partilhado por %{acct_link} content_warning: 'Aviso de conteúdo: %{warning}' content_warnings: hide: Esconder publicação @@ -1921,11 +1934,16 @@ pt-PT: direct: As publicações que só são visíveis para os utilizadores mencionados não podem ser fixadas limit: Já fixaste a quantidade máxima de publicações ownership: Não podem ser fixadas publicações de outras pessoas - reblog: Não é possível fixar um impulso + reblog: Não é possível fixar uma partilha + quote_error: + not_available: Publicação indisponível + pending_approval: Publicação pendente + revoked: Publicação removida pelo autor quote_policies: followers: Apenas para seguidores nobody: Apenas eu public: Todos + quote_post_author: Citou uma publicação de %{acct} title: '%{name}: "%{quote}"' visibilities: direct: Menção privada @@ -1940,9 +1958,9 @@ pt-PT: exceptions: Exceções explanation: Como eliminar publicações é uma operação custosa, isto é feito lentamente ao longo do tempo, quando o servidor não está ocupado. Por esta razão, as tuas publicações podem ser eliminadas um pouco depois de atingirem o limite de idade definido. ignore_favs: Ignorar favoritos - ignore_reblogs: Ignorar os impulsos + ignore_reblogs: Ignorar partilhas interaction_exceptions: Exceções baseadas em interações - interaction_exceptions_explanation: Tem em atenção que não há garantia de que as mensagens sejam eliminadas se ficarem abaixo do limite de favoritos ou de impulsionamento depois de os terem ultrapassado. + interaction_exceptions_explanation: Observe que não há garantia que as publicações serão excluídas se ficarem abaixo do limite de favoritos ou partilhas depois de terem ultrapassado esses limites. keep_direct: Manter mensagens diretas keep_direct_hint: Não elimina nenhuma das tuas mensagens diretas keep_media: Manter publicações com anexos de multimédia @@ -1951,8 +1969,8 @@ pt-PT: keep_pinned_hint: Não elimina nenhuma das tuas publicações afixadas keep_polls: Manter sondagens keep_polls_hint: Não elimina nenhuma das tuas sondagens - keep_self_bookmark: Manter as publicações que marquei - keep_self_bookmark_hint: Não elimina as tuas próprias publicações se as tiveres nos marcadores + keep_self_bookmark: Manter as publicações que salvou + keep_self_bookmark_hint: Não eliminar as suas próprias publicações se as tiver salvo keep_self_fav: Manter as publicações que adicionei aos favoritos keep_self_fav_hint: Não elimina as tuas próprias publicações se as tiveres adicionado aos favoritos min_age: @@ -1967,8 +1985,8 @@ pt-PT: min_age_label: Limite de idade min_favs: Manter publicações adicionadas aos favoritos pelos menos min_favs_hint: Não elimina nenhuma das tuas publicações que tenham sido adicionadas aos favoritos este número de vezes. Deixa em branco para eliminar publicações, independentemente do número de vezes que tenham sido adicionadas aos favoritos - min_reblogs: Manter as publicações impulsionadas, pelo menos - min_reblogs_hint: Não elimina nenhuma das tuas mensagens que tenham sido impulsionada pelo menos este número de vezes. Deixa em branco para eliminar as mensagens independentemente do número de impulsionamentos + min_reblogs: Manter, pelo menos, as publicações partilhadas + min_reblogs_hint: Não elimina nenhuma das suas publicações que tenha sido partilhadas pelo menos este número de vezes. Deixe em branco para eliminar publicações independentemente do número de partilhas stream_entries: sensitive_content: Conteúdo sensível strikes: @@ -2095,7 +2113,7 @@ pt-PT: checklist_subtitle: 'Vamos começar nesta nova fronteira social:' checklist_title: Passos de boas-vindas edit_profile_action: Personalizar - edit_profile_step: Aumenta as tuas interações com um perfil completo. + edit_profile_step: Aumente as suas interações ao ter um perfil mais completo. edit_profile_title: Personaliza o teu perfil explanation: Aqui estão algumas dicas para começar feature_action: Mais informações @@ -2136,7 +2154,7 @@ pt-PT: seamless_external_login: A sessão foi iniciada através de um serviço externo, pelo que as definições de palavra-passe e e-mail não estão disponíveis. signed_in_as: 'Registado como:' verification: - extra_instructions_html: Dica: a hiperligação no teu site pode ser invisível. A parte importante é rel="me" que impede a falsificação de identidade em sítios na web com conteúdos gerados pelos utilizadores. Podes até utilizar uma etiqueta link no cabeçalho da página ao invés de a, mas o HTML deve ser acessível sem executar JavaScript. + extra_instructions_html: Dica: a hiperligação no seu site pode ser invisível. A parte importante é rel="me" que impede a falsificação de identidade em sítios na web com conteúdos gerados pelos utilizadores. Pode até utilizar uma etiqueta link no cabeçalho da página ao invés de a, mas o HTML deve ser acessível sem executar JavaScript. here_is_how: Eis o que fazer hint_html: "Verificar a sua identidade no Mastodon é para todos. Baseado em normas públicas da web, agora e para sempre gratuitas. Tudo o que precisas é de um site pessoal pelo qual as pessoas te reconheçam. Quando colocas no teu perfil uma hiperligação para esse site, vamos verificar que o site tem uma hiperligação de volta para o teu perfil e mostrar um indicador visual." instructions_html: Copia e cola o código abaixo no HTML do teu site. Em seguida, adiciona o endereço do teu site num dos campos extras no teu perfil, na aba "Editar perfil" e guarda as alterações. diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 12c73955791dfb..7ea9083c8fa622 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -851,7 +851,6 @@ ru: title: Исключить пользователей из индексации поисковиками по умолчанию discovery: follow_recommendations: Рекомендации подписок - preamble: Наблюдение интересного контента играет важную роль при открытии новых пользователей, которые могут не знать ни одного Mastodon. Контролируйте как работают различные функции обнаружения на вашем сервере. privacy: Конфиденциальность profile_directory: Каталог профилей public_timelines: Публичные ленты diff --git a/config/locales/sco.yml b/config/locales/sco.yml index f90c7e692c2d84..777bd10ef9307a 100644 --- a/config/locales/sco.yml +++ b/config/locales/sco.yml @@ -672,7 +672,6 @@ sco: title: Content retention discovery: follow_recommendations: Follae recommendations - preamble: Bringin forret interestin content helps ye tae bring in new uisers thit mibbie wullnae ken oniebody on Mastodon yit. Control hou various discovery features wirk on yer server. profile_directory: Profile directory public_timelines: Public timelines title: Discovery diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml index eef49ef8fee220..75ed51833f6ecc 100644 --- a/config/locales/simple_form.ar.yml +++ b/config/locales/simple_form.ar.yml @@ -104,7 +104,6 @@ ar: thumbnail: عرض حوالي 2:1 صورة إلى جانب معلومات الخادم الخاص بك. trendable_by_default: تخطي مراجعة المحتوى التريند اليدوي. لا يزال من الممكن الإزالة اللاحقة للعناصر الفردية من التريندات. trends: تُظهِر المتداولة أي من المنشورات والوسوم وقصص الأخبار التي تجذب الانتباه على خادمك. - trends_as_landing_page: إظهار المحتوى المتداوَل للمستخدمين والزوار غير المسجلين بدلاً من وصف هذا الخادم. يتطلب هذا تفعيل المتداولة. form_challenge: current_password: إنك بصدد الدخول إلى منطقة آمنة imports: @@ -290,7 +289,6 @@ ar: thumbnail: الصورة المصغرة للخادم trendable_by_default: السماح للوسوم بالظهور على المتداوَلة دون مراجعة مسبقة trends: تمكين المتداوَلة - trends_as_landing_page: استخدام المُتداوَلة كصفحة ترحيب interactions: must_be_follower: حظر الإشعارات القادمة من حسابات لا تتبعك must_be_following: حظر الإشعارات القادمة من الحسابات التي لا تتابعها diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml index cee48c7dedcfcb..c097562a2a2a5d 100644 --- a/config/locales/simple_form.be.yml +++ b/config/locales/simple_form.be.yml @@ -65,7 +65,7 @@ be: setting_display_media_hide_all: Заўсёды хаваць медыя setting_display_media_show_all: Заўсёды паказваць медыя setting_emoji_style: Як паказваць эмодзі. "Аўтаматычны" будзе намагацца выкарыстоўваць мясцовыя эмодзі, але для састарэлых браўзераў — Twemoji. - setting_quick_boosting_html: Калі ўключана, клік па %{boost_icon} іконцы пашырэння адразу пашырыць допіс, без адкрыцця меню "пашырыць/цытаваць". Перасоўвае дзеянне цытавання ў меню %{options_icon} (выбару). + setting_quick_boosting_html: Калі ўключана, націсканне на %{boost_icon} значок пашырэння адразу пашырыць допіс замест адкрыцця меню пашырэння/цытавання. Перасоўвае дзеянне цытавання ў меню %{options_icon} (выбару). setting_system_scrollbars_ui: Працуе толькі ў камп'ютарных браўзерах на аснове Safari і Chrome setting_use_blurhash: Градыенты заснаваны на колерах схаваных выяў, але размываюць дэталі setting_use_pending_items: Схаваць абнаўленні стужкі за клікам замест аўтаматычнага пракручвання стужкі @@ -93,6 +93,7 @@ be: content_cache_retention_period: Усе допісы з іншых сервераў (разам з пашырэннямі і адказамі) будуць выдалены праз паказаную колькасць дзён, незалежна ад таго, як лакальны карыстальнік узаемадзейнічаў з гэтымі допісамі. Гэта датычыцца і тых допісаў, якія лакальны карыстальнік пазначыў у закладкі або ўпадабанае. Прыватныя згадванні паміж карыстальнікамі з розных экзэмпляраў сервераў таксама будуць страчаны і іх нельга будзе аднавіць. Выкарыстанне гэтай налады прызначана для экзэмпляраў сервераў спецыяльнага прызначэння і парушае многія чаканні карыстальнікаў пры выкарыстанні ў агульных мэтах. custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon. favicon: WEBP, PNG, GIF ці JPG. Замяняе прадвызначаны favicon Mastodon на ўласны значок. + landing_page: Выбірае, якую старонку бачаць новыя наведвальнікі, калі прыходзяць на Ваш сервер. Калі выбераце "Трэнды", тады неабходна іх уключыць у наладах Выяўленне. Калі выбераце "Тутэйшая стужка", тады ў наладах Выяўленне ў налады "Доступ да жывых стужак з лакальнымі допісамі" мусіць стаяць варыянт "Усе". mascot: Замяняе ілюстрацыю ў пашыраным вэб-інтэрфейсе. media_cache_retention_period: Медыяфайлы з допісаў, зробленых карыстальнікамі з іншых сервераў, кэшыруюцца на вашым серверы. Пры станоўчым значэнні медыяфайлы будуць выдалены праз пазначаную колькасць дзён. Калі медыяданыя будуць запытаныя пасля выдалення, яны будуць спампаваныя зноў, калі зыходнае змесціва усё яшчэ даступнае. У сувязі з абмежаваннямі на частату абнаўлення картак перадпрагляду іншых сайтаў, рэкамендуецца ўсталяваць значэнне не менш за 14 дзён, інакш гэтыя карткі не будуць абнаўляцца па запыце раней за гэты тэрмін. min_age: Карыстальнікі будуць атрымліваць запыт на пацвярджэнне даты нараджэння падчас рэгістрацыі @@ -110,7 +111,6 @@ be: thumbnail: Выява памерамі прыкладна 2:1, якая паказваецца побач з інфармацыяй пра ваш сервер. trendable_by_default: Прапусціць ручны агляд трэндавага змесціва. Асобныя элементы ўсё яшчэ можна будзе выдаліць з трэндаў пастфактум. trends: Трэнды паказваюць, якія допісы, хэштэгі і навіны набываюць папулярнасць на вашым серверы. - trends_as_landing_page: Паказваць папулярнае змесціва карыстальнікам, якія выйшлі з сістэмы, і наведвальнікам, замест апісання гэтага сервера. Патрабуецца ўключэнне трэндаў. form_challenge: current_password: Вы ўваходзіце ў бяспечную зону imports: @@ -289,6 +289,7 @@ be: content_cache_retention_period: Перыяд захоўвання змесціва з іншых сервераў custom_css: CSS карыстальніка favicon: Значок сайта + landing_page: Старонка прыбыцця для новых наведвальнікаў local_live_feed_access: Доступ да жывых стужак з лакальнымі допісамі local_topic_feed_access: Доступ да хэштэгавых і спасылачных стужак з лакальнымі допісамі mascot: Уласны маскот(спадчына) @@ -313,7 +314,6 @@ be: thumbnail: Мініяцюра сервера trendable_by_default: Дазваляць трэнды без папярэдняй праверкі trends: Уключыць трэнды - trends_as_landing_page: Выкарыстоўваць трэнды ў якасці лэндзінга interactions: must_be_follower: Заблакіраваць апавяшчэнні ад непадпісаных людзей must_be_following: Заблакіраваць апавяшчэнні ад людзей на якіх вы не падпісаны diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 047199adc2a336..e14b05ed90110a 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -105,7 +105,6 @@ bg: thumbnail: Образ в съотношение около 2:1, показвано до информацията за сървъра ви. trendable_by_default: Прескачане на ръчния преглед на изгряващо съдържание. Отделни елементи още могат да се премахват от изгряващи постфактум. trends: В раздел „Налагащо се“ се показват публикации, хаштагове и новини, набрали популярност на сървъра ви. - trends_as_landing_page: Показване на налагащото се съдържание за излизащите потребители и посетители вместо на описа на този сървър. Изисква налагащото се да бъде включено. form_challenge: current_password: Влизате в зона за сигурност imports: @@ -243,6 +242,7 @@ bg: setting_emoji_style: Стил на емоджито setting_expand_spoilers: Винаги разширяване на публикации, отбелязани с предупреждения за съдържание setting_hide_network: Скриване на социалния ви свързан граф + setting_quick_boosting: Включване на бързо подсилване setting_reduce_motion: Обездвижване на анимациите setting_system_font_ui: Употреба на стандартния шрифт на системата setting_system_scrollbars_ui: Употреба на системната подразбираща се лента за превъртане @@ -296,7 +296,6 @@ bg: thumbnail: Образче на сървъра trendable_by_default: Без преглед на налагащото се trends: Включване на налагащи се - trends_as_landing_page: Употреба на налагащото се като целева страница interactions: must_be_follower: Блокирай известия от не-последователи must_be_following: Блокиране на известия от неследваните diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 9c3a6d826aae32..ceca6b0e7fcb86 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -107,7 +107,6 @@ ca: thumbnail: Una imatge d'aproximadament 2:1 que es mostra al costat la informació del teu servidor. trendable_by_default: Omet la revisió manual del contingut en tendència. Els articles individuals poden encara ser eliminats després del fet. trends: Les tendències mostren quins tuts, etiquetes i notícies estan guanyant força en el teu servidor. - trends_as_landing_page: Mostra el contingut en tendència als usuaris i visitants no autenticats enlloc de la descripció d'aquest servidor. Requereix que les tendències estiguin activades. form_challenge: current_password: Estàs entrant en una àrea segura imports: @@ -295,7 +294,6 @@ ca: thumbnail: Miniatura del servidor trendable_by_default: Permet tendències sense revisió prèvia trends: Activa les tendències - trends_as_landing_page: Fer servir les tendències com a pàgina inicial interactions: must_be_follower: Bloqueja les notificacions de persones que no em segueixen must_be_following: Bloqueja les notificacions de persones no seguides diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index d4847acc8bb7ed..cd5a51901a7a1c 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -54,8 +54,10 @@ cs: password: Použijte alespoň 8 znaků phrase: Shoda bude nalezena bez ohledu na velikost písmen v textu příspěvku či varování o obsahu scopes: Která API bude aplikace moct používat. Pokud vyberete rozsah nejvyššího stupně, nebudete je muset vybírat jednotlivě. + setting_advanced_layout: Zobrazit Mastodon ve vícesloupovém rozvržení, umožňující zobrazení časoé osy, oznámení a třetího sloupce vašeho výběru. Není doporučeno pro menší obrazovky. setting_aggregate_reblogs: Nezobrazovat nové boosty pro příspěvky, které byly nedávno boostnuty (ovlivňuje pouze nově přijaté boosty) setting_always_send_emails: Jinak nebudou e-mailové notifikace posílány, když Mastodon aktivně používáte + setting_boost_modal: Pokud je povoleno, boostnutí nejprve otevře dialogové okno pro potvrzení, ve kterém můžete změnit viditelnost svého boostu. setting_default_quote_policy_private: Příspěvky pouze pro sledující, které jsou vytvořeny na Mastodonu, nemohou být citovány ostatními. setting_default_quote_policy_unlisted: Když vás lidé citují, jejich příspěvek bude v časové ose populárních příspěvků také skryt. setting_default_sensitive: Citlivá média jsou ve výchozím stavu skryta a mohou být zobrazena kliknutím @@ -63,6 +65,7 @@ cs: setting_display_media_hide_all: Vždy skrývat média setting_display_media_show_all: Vždy zobrazovat média setting_emoji_style: Jak se budou zobrazovat emoji. "Auto" zkusí použít výchozí emoji, ale pro starší prohlížeče použije Twemoji. + setting_quick_boosting_html: Pokud je povoleno, kliknutím na %{boost_icon} Boost ikonu okamžitě boostnete místo otevření rozbalovací nabídky boost/citace. Přemístí citaci do nabídky %{options_icon} (Možnosti). setting_system_scrollbars_ui: Platí pouze pro desktopové prohlížeče založené na Safari nebo Chrome setting_use_blurhash: Gradienty jsou vytvořeny na základě barvev skrytých médií, ale zakrývají veškeré detaily setting_use_pending_items: Aktualizovat časovou osu až po kliknutí namísto automatického rolování kanálu @@ -90,6 +93,7 @@ cs: content_cache_retention_period: Všechny příspěvky z jiných serverů (včetně boostů a odpovědí) budou po uplynutí stanoveného počtu dní smazány bez ohledu na interakci místního uživatele s těmito příspěvky. To se týká i příspěvků, které místní uživatel přidal do záložek nebo oblíbených. Soukromé zmínky mezi uživateli z různých instancí budou rovněž ztraceny a nebude možné je obnovit. Použití tohoto nastavení je určeno pro instance pro speciální účely a při implementaci pro obecné použití porušuje mnohá očekávání uživatelů. custom_css: Můžete použít vlastní styly ve verzi Mastodonu. favicon: WEBP, PNG, GIF nebo JPG. Nahradí výchozí favicon Mastodonu vlastní ikonou. + landing_page: Vybere stránku, kterou návštěvníci uvidí, když prvně přijdou na tvůj server. Pokud zvolíte "Trendy", je třeba povolit trendy v nastavení objevování. Pokud zvolíte "Místní kanál", je třeba v nastavení Objevování nastavit "Přístup k živým kanálům s lokálními příspěvky" na "Všichni". mascot: Přepíše ilustraci v pokročilém webovém rozhraní. media_cache_retention_period: Mediální soubory z příspěvků vzdálených uživatelů se ukládají do mezipaměti na vašem serveru. Pokud je nastaveno na kladnou hodnotu, budou média po zadaném počtu dní odstraněna. Pokud jsou mediální data vyžádána po jejich odstranění, budou znovu stažena, pokud je zdrojový obsah stále k dispozici. Vzhledem k omezením týkajícím se četnosti dotazů karet náhledů odkazů na weby třetích stran se doporučuje nastavit tuto hodnotu alespoň na 14 dní, jinak nebudou karty náhledů odkazů na vyžádání aktualizovány dříve. min_age: Uživatelé budou požádáni, aby při registraci potvrdili datum svého narození @@ -107,7 +111,6 @@ cs: thumbnail: Přibližně 2:1 obrázek zobrazený vedle informací o vašem serveru. trendable_by_default: Přeskočit manuální kontrolu populárního obsahu. Jednotlivé položky mohou být odstraněny z trendů později. trends: Trendy zobrazují, které příspěvky, hashtagy a zprávy získávají na serveru pozornost. - trends_as_landing_page: Zobrazit populární obsah odhlášeným uživatelům a návštěvníkům místo popisu tohoto serveru. Vyžaduje povolení trendů. form_challenge: current_password: Vstupujete do zabezpečeného prostoru imports: @@ -236,10 +239,12 @@ cs: setting_aggregate_reblogs: Seskupovat boosty v časových osách setting_always_send_emails: Vždy posílat e-mailová oznámení setting_auto_play_gif: Automaticky přehrávat animace GIF + setting_boost_modal: Ovládání viditelnosti boostování setting_default_language: Jazyk příspěvků setting_default_privacy: Viditelnost příspěvků setting_default_quote_policy: Kdo může citovat setting_default_sensitive: Vždy označovat média jako citlivá + setting_delete_modal: Upozornit před odstraněním příspěvku setting_disable_hover_cards: Zakázat náhled profilu při přejetí myší setting_disable_swiping: Vypnout gesta přejetí prsty setting_display_media: Zobrazování médií @@ -249,6 +254,8 @@ cs: setting_emoji_style: Styl emoji setting_expand_spoilers: Vždy rozbalit příspěvky označené varováními o obsahu setting_hide_network: Skrýt mou síť + setting_missing_alt_text_modal: Upozornit před odesláním médií bez popisného textu + setting_quick_boosting: Povolit rychlé boostnutí setting_reduce_motion: Omezit pohyb v animacích setting_system_font_ui: Použít výchozí písmo systému setting_system_scrollbars_ui: Použít výchozí posuvník systému @@ -282,7 +289,8 @@ cs: content_cache_retention_period: Doba uchovávání vzdáleného obsahu custom_css: Vlastní CSS favicon: Favicon - local_live_feed_access: Přístup k live kanálům s lokálními příspěvky + landing_page: Úvodní stránka pro nové návštěvníky + local_live_feed_access: Přístup k živým kanálům s lokálními příspěvky local_topic_feed_access: Přístup ke kanálům s hashtagy a odkazy s lokálními příspěvky mascot: Vlastní maskot (zastaralé) media_cache_retention_period: Doba uchovávání mezipaměti médií @@ -306,7 +314,6 @@ cs: thumbnail: Miniatura serveru trendable_by_default: Povolit trendy bez předchozí revize trends: Povolit trendy - trends_as_landing_page: Použít trendy jako vstupní stránku interactions: must_be_follower: Blokovat oznámení od lidí, kteří vás nesledují must_be_following: Blokovat oznámení od lidí, které nesledujete diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index 89127c13a719e7..0f79229caa9c99 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -54,8 +54,10 @@ cy: password: Defnyddiwch o leiaf 8 nod phrase: Caiff ei gyfateb heb ystyriaeth o briflythrennu mewn testun neu rhybudd ynghylch cynnwys postiad scopes: Pa APIs y bydd y rhaglen yn cael mynediad iddynt. Os dewiswch gwmpas lefel uchaf, nid oes angen i chi ddewis rhai unigol. + setting_advanced_layout: Dangos Mastodon fel cynllun aml-golofn, sy'n eich galluogi i weld y llinell amser, hysbysiadau, a thrydedd golofn o'ch dewis chi. Nid gyfer sgriniau llai. setting_aggregate_reblogs: Peidiwch â dangos hybiau newydd ar bostiadau sydd wedi cael eu hybu'n ddiweddar (dim ond yn effeithio ar hybiau newydd ei dderbyn) setting_always_send_emails: Fel arfer ni fydd hysbysiadau e-bost yn cael eu hanfon pan fyddwch chi wrthi'n defnyddio Mastodon + setting_boost_modal: Pan fydd wedi'i alluogi, bydd hybu'n agor deialog cadarnhau yn gyntaf lle gallwch newid gwelededd eich hwb. setting_default_quote_policy_private: Does dim modd dyfynnu postiadau sydd wedi'u hysgrifennu ar Mastodon ar gyfer dim ond dilynwyr. setting_default_quote_policy_unlisted: Pan fydd pobl yn eich dyfynnu, bydd eu postiad hefyd yn cael ei guddio rhag llinellau amser sy'n trendio. setting_default_sensitive: Mae cyfryngau sensitif wedi'u cuddio yn rhagosodedig a gellir eu datgelu trwy glicio @@ -63,6 +65,7 @@ cy: setting_display_media_hide_all: Cuddio cyfryngau bob tro setting_display_media_show_all: Dangos cyfryngau bob tro setting_emoji_style: Sut i arddangos emojis. Bydd "Awto" yn ceisio defnyddio emoji cynhenid, ond mae'n disgyn yn ôl i Twemoji ar gyfer porwyr traddodiadol. + setting_quick_boosting_html: Pan fydd wedi'i alluogi, bydd clicio ar yr eicon Hwb %{boost_icon} yn rhoi hwb ar unwaith yn lle agor y gwymplen hwb/dyfynnu. Mae'n symud y weithred dyfynnu i'r ddewislen %{options_icon} (Dewisiadau). setting_system_scrollbars_ui: Yn berthnasol i borwyr bwrdd gwaith yn seiliedig ar Safari a Chrome yn unig setting_use_blurhash: Mae graddiannau wedi'u seilio ar liwiau'r delweddau cudd ond maen nhw'n cuddio unrhyw fanylion setting_use_pending_items: Cuddio diweddariadau llinell amser y tu ôl i glic yn lle sgrolio'n awtomatig @@ -107,7 +110,6 @@ cy: thumbnail: Delwedd tua 2:1 yn cael ei dangos ochr yn ochr â manylion eich gweinydd. trendable_by_default: Hepgor adolygiad llaw o gynnwys sy'n tueddu. Gall eitemau unigol gael eu tynnu o dueddiadau o hyd ar ôl y ffaith. trends: Mae pynciau llosg yn dangos y postiadau, hashnodau, a newyddion sy'n denu sylw ar eich gweinydd. - trends_as_landing_page: Dangos cynnwys tueddiadol i ddefnyddwyr ac ymwelwyr sydd wedi allgofnodi yn lle disgrifiad o'r gweinydd hwn. Mae angen galluogi tueddiadau. form_challenge: current_password: Rydych chi'n mynd i mewn i ardal ddiogel imports: @@ -238,10 +240,12 @@ cy: setting_aggregate_reblogs: Grwpio hybiau mewn ffrydiau setting_always_send_emails: Anfonwch hysbysiadau e-bost bob amser setting_auto_play_gif: Chwarae GIFs wedi'u hanimeiddio yn awtomatig + setting_boost_modal: Rheoli hybu gwelededd setting_default_language: Iaith postio setting_default_privacy: Gwelededd postio setting_default_quote_policy: Pwy sy'n gallu dyfynnu setting_default_sensitive: Marcio cyfryngau fel eu bod yn sensitif bob tro + setting_delete_modal: Rhybuddio fi cyn dileu postiad setting_disable_hover_cards: Analluogi rhagolwg proffil ar lusgo setting_disable_swiping: Analluogi cynigion llusgo setting_display_media: Dangos cyfryngau @@ -251,6 +255,8 @@ cy: setting_emoji_style: Arddull Emojis setting_expand_spoilers: Dangos postiadau wedi'u marcio â rhybudd cynnwys bob tro setting_hide_network: Cuddio eich graff cymdeithasol + setting_missing_alt_text_modal: Rhybuddio fi cyn postio cyfryngau heb destun amgen + setting_quick_boosting: Galluogi hybu cyflym setting_reduce_motion: Lleihau mudiant mewn animeiddiadau setting_system_font_ui: Defnyddio ffont rhagosodedig y system setting_system_scrollbars_ui: Defnyddiwch far sgrolio rhagosodedig y system @@ -308,7 +314,6 @@ cy: thumbnail: Bawdlun y gweinydd trendable_by_default: Caniatáu pynciau llosg heb adolygiad trends: Galluogi pynciau llosg - trends_as_landing_page: Defnyddio tueddiadau fel y dudalen gartref interactions: must_be_follower: Blocio hysbysiadau o bobl nad ydynt yn eich dilyn must_be_following: Blocio hysbysiadau o bobl nad ydych yn eu dilyn diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index d29dfc24194dea..6c5711aa5fcbb0 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -93,6 +93,7 @@ da: content_cache_retention_period: Alle indlæg fra andre servere (herunder fremhævelser og besvarelser) slettes efter det angivne antal dage uden hensyn til lokal brugerinteraktion med disse indlæg. Dette omfatter indlæg, hvor en lokal bruger har markeret dem som bogmærker eller favoritter. Private omtaler mellem brugere fra forskellige instanser vil også være tabt og umulige at gendanne. Brugen af denne indstilling er beregnet til særlige formål instanser og bryder mange brugerforventninger ved implementering til almindelig brug. custom_css: Man kan anvende tilpassede stilarter på Mastodon-webversionen. favicon: WEBP, PNG, GIF eller JPG. Tilsidesætter standard Mastodon favikonet på mobilenheder med et tilpasset ikon. + landing_page: Vælger, hvilken side nye besøgende ser, når de først ankommer til din server. Hvis du vælger "Trender", skal trends være aktiveret i Opdagelse-indstillingerne. Hvis du vælger "Lokalt feed", skal "Adgang til live feeds med lokale indlæg" være indstillet til "Alle" i Opdagelse-indstillingerne. mascot: Tilsidesætter illustrationen i den avancerede webgrænseflade. media_cache_retention_period: Mediefiler fra indlæg oprettet af eksterne brugere er cachet på din server. Når sat til positiv værdi, slettes medier efter det angivne antal dage. Anmodes om mediedata efter de er slettet, gendownloades de, hvis kildeindholdet stadig er tilgængeligt. Grundet begrænsninger på, hvor ofte linkforhåndsvisningskort forespørger tredjeparts websteder, anbefales det at sætte denne værdi til mindst 14 dage, ellers opdateres linkforhåndsvisningskort ikke efter behov før det tidspunkt. min_age: Brugere anmodes om at bekræfte deres fødselsdato under tilmelding @@ -110,7 +111,6 @@ da: thumbnail: Et ca. 2:1 billede vist sammen med serveroplysningerne. trendable_by_default: Spring manuel gennemgang af trendindhold over. Individuelle elementer kan stadig fjernes fra trends efter kendsgerningen. trends: Tendenser viser, hvilke indlæg, hashtags og nyheder opnår momentum på serveren. - trends_as_landing_page: Vis tendensindhold til udloggede brugere og besøgende i stedet for en beskrivelse af denne server. Kræver, at tendenser er aktiveret. form_challenge: current_password: Du bevæger dig ind på et sikkert område imports: @@ -287,6 +287,7 @@ da: content_cache_retention_period: Opbevaringsperiode for eksternt indhold custom_css: Tilpasset CSS favicon: Favikon + landing_page: Landingside for nye besøgende local_live_feed_access: Adgang til live feeds med lokale indlæg local_topic_feed_access: Adgang til hashtag- og link-feeds med lokale indlæg mascot: Tilpasset maskot (ældre funktion) @@ -311,7 +312,6 @@ da: thumbnail: Serverminiaturebillede trendable_by_default: Tillad ikke-reviderede trends trends: Aktivér trends - trends_as_landing_page: Brug tendenser som destinationssiden interactions: must_be_follower: Blokér notifikationer fra bruger, der ikke følger dig must_be_following: Blokér notifikationer fra brugere, du ikke følger diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 2133cc67295c1d..74b6e5d9ef2b6d 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -107,7 +107,6 @@ de: thumbnail: Ein Bild ungefähr im 2:1-Format, das neben den Server-Informationen angezeigt wird. trendable_by_default: Manuelles Überprüfen angesagter Inhalte überspringen. Einzelne Elemente können später noch aus den Trends entfernt werden. trends: Trends zeigen, welche Beiträge, Hashtags und Nachrichten auf deinem Server immer beliebter werden. - trends_as_landing_page: Dies zeigt nicht angemeldeten Personen Trendinhalte anstelle einer Beschreibung des Servers an. Erfordert, dass Trends aktiviert sind. form_challenge: current_password: Du betrittst einen sicheren Bereich imports: @@ -305,7 +304,6 @@ de: thumbnail: Vorschaubild des Servers trendable_by_default: Trends ohne vorherige Überprüfung erlauben trends: Trends aktivieren - trends_as_landing_page: Trends als Landingpage verwenden interactions: must_be_follower: Benachrichtigungen von Profilen, die mir nicht folgen, ausblenden must_be_following: Benachrichtigungen von Profilen, denen ich nicht folge, ausblenden diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index 0de3351ed03b84..03dd292e0fd341 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -93,6 +93,7 @@ el: content_cache_retention_period: Όλες οι αναρτήσεις από άλλους διακομιστές (συμπεριλαμβανομένων των ενισχύσεων και απαντήσεων) θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, χωρίς να λαμβάνεται υπόψη οποιαδήποτε αλληλεπίδραση τοπικού χρήστη με αυτές τις αναρτήσεις. Αυτό περιλαμβάνει αναρτήσεις όπου ένας τοπικός χρήστης την έχει χαρακτηρίσει ως σελιδοδείκτη ή αγαπημένη. Θα χαθούν επίσης ιδιωτικές αναφορές μεταξύ χρηστών από διαφορετικές οντότητες και θα είναι αδύνατο να αποκατασταθούν. Η χρήση αυτής της ρύθμισης προορίζεται για οντότητες ειδικού σκοπού και χαλάει πολλές προσδοκίες του χρήστη όταν εφαρμόζεται για χρήση γενική σκοπού. custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon. favicon: WEBP, PNG, GIF ή JPG. Παρακάμπτει το προεπιλεγμένο favicon του Mastodon με ένα προσαρμοσμένο εικονίδιο. + landing_page: Επιλέγει ποια σελίδα βλέπουν οι νέοι επισκέπτες όταν φτάνουν για πρώτη φορά στο διακομιστή σας. Αν επιλέξετε "Τάσεις", τότε οι τάσεις πρέπει να είναι ενεργοποιημένες στις Ρυθμίσεις Ανακάλυψης. Αν επιλέξετε "Τοπική ροή", τότε το "Πρόσβαση σε ζωντανές ροές με τοπικές αναρτήσεις" πρέπει να οριστεί σε "Όλοι" στις Ρυθμίσεις Ανακάλυψης. mascot: Παρακάμπτει την εικονογραφία στην προηγμένη διεπαφή ιστού. media_cache_retention_period: Τα αρχεία πολυμέσων από αναρτήσεις που γίνονται από απομακρυσμένους χρήστες αποθηκεύονται προσωρινά στο διακομιστή σου. Όταν οριστεί μια θετική τιμή, τα μέσα θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών. Αν τα δεδομένα πολυμέσων ζητηθούν μετά τη διαγραφή τους, θα γίνει ε, αν το πηγαίο περιεχόμενο είναι ακόμα διαθέσιμο. Λόγω περιορισμών σχετικά με το πόσο συχνά οι κάρτες προεπισκόπησης συνδέσμων συνδέονται σε ιστοσελίδες τρίτων, συνιστάται να ορίσεις αυτή την τιμή σε τουλάχιστον 14 ημέρες ή οι κάρτες προεπισκόπησης συνδέσμων δεν θα ενημερώνονται κατ' απάιτηση πριν από εκείνη την ώρα. min_age: Οι χρήστες θα κληθούν να επιβεβαιώσουν την ημερομηνία γέννησής τους κατά την εγγραφή @@ -110,7 +111,6 @@ el: thumbnail: Μια εικόνα περίπου 2:1 που εμφανίζεται παράλληλα με τις πληροφορίες του διακομιστή σου. trendable_by_default: Παράλειψη χειροκίνητης αξιολόγησης του περιεχομένου σε τάση. Μεμονωμένα στοιχεία μπορούν ακόμα να αφαιρεθούν από τις τάσεις μετέπειτα. trends: Τάσεις δείχνουν ποιες δημοσιεύσεις, ετικέτες και ειδήσεις προκαλούν έλξη στο διακομιστή σας. - trends_as_landing_page: Εμφάνιση περιεχομένου σε τάση σε αποσυνδεδεμένους χρήστες και επισκέπτες αντί για μια περιγραφή αυτού του διακομιστή. Απαιτεί οι τάσεις να έχουν ενεργοποιηθεί. form_challenge: current_password: Μπαίνεις σε ασφαλή περιοχή imports: @@ -287,6 +287,7 @@ el: content_cache_retention_period: Περίοδος διατήρησης απομακρυσμένου περιεχομένου custom_css: Προσαρμοσμένο CSS favicon: Favicon + landing_page: Σελίδα προσγείωσης για νέους επισκέπτες local_live_feed_access: Πρόσβαση σε ζωντανές ροές με τοπικές αναρτήσεις local_topic_feed_access: Πρόσβαση σε ροές ετικετών και συνδέσμων με τοπικές αναρτήσεις mascot: Προσαρμοσμένη μασκότ (απαρχαιωμένο) @@ -311,7 +312,6 @@ el: thumbnail: Μικρογραφία διακομιστή trendable_by_default: Επίτρεψε τις τάσεις χωρίς προηγούμενη αξιολόγηση trends: Ενεργοποίηση τάσεων - trends_as_landing_page: Χρήση των τάσεων ως σελίδα προορισμού interactions: must_be_follower: Μπλόκαρε τις ειδοποιήσεις από όσους δεν σε ακολουθούν must_be_following: Μπλόκαρε τις ειδοποιήσεις από όσους δεν ακολουθείς diff --git a/config/locales/simple_form.en-GB.yml b/config/locales/simple_form.en-GB.yml index c0944507a3b9b6..5ef7647c5651f2 100644 --- a/config/locales/simple_form.en-GB.yml +++ b/config/locales/simple_form.en-GB.yml @@ -105,7 +105,6 @@ en-GB: thumbnail: A roughly 2:1 image displayed alongside your server information. trendable_by_default: Skip manual review of trending content. Individual items can still be removed from trends after the fact. trends: Trends show which posts, hashtags and news stories are gaining traction on your server. - trends_as_landing_page: Show trending content to logged-out users and visitors instead of a description of this server. Requires trends to be enabled. form_challenge: current_password: You are entering a secure area imports: @@ -290,7 +289,6 @@ en-GB: thumbnail: Server thumbnail trendable_by_default: Allow trends without prior review trends: Enable trends - trends_as_landing_page: Use trends as the landing page interactions: must_be_follower: Block notifications from non-followers must_be_following: Block notifications from people you don't follow diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index c4fe31b1278110..e09c4ff8cff7f4 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -122,6 +122,7 @@ en: enable_public_unlisted_visibility: If true, your community maybe closed-minded. If turn it false, strongly recommend that you disclose that you have disabled this setting! enable_public_visibility: If disabled, public posts will be forcibly replaced with "Local public" or "Unlisted" posts. favicon: WEBP, PNG, GIF or JPG. Overrides the default Mastodon favicon with a custom icon. + landing_page: Selects what page new visitors see when they first arrive on your server. If you select "Trends", then trends needs to be enabled in the Discovery Settings. If you select "Local feed", then "Access to live feeds featuring local posts" needs to be set to "Everyone" in the Discovery Settings. mascot: Overrides the illustration in the advanced web interface. media_cache_retention_period: Media files from posts made by remote users are cached on your server. When set to a positive value, media will be deleted after the specified number of days. If the media data is requested after it is deleted, it will be re-downloaded, if the source content is still available. Due to restrictions on how often link preview cards poll third-party sites, it is recommended to set this value to at least 14 days, or link preview cards will not be updated on demand before that time. min_age: Users will be asked to confirm their date of birth during sign-up @@ -145,7 +146,6 @@ en: thumbnail: A roughly 2:1 image displayed alongside your server information. trendable_by_default: Skip manual review of trending content. Individual items can still be removed from trends after the fact. trends: Trends show which posts, hashtags and news stories are gaining traction on your server. - trends_as_landing_page: Show trending content to logged-out users and visitors instead of a description of this server. Requires trends to be enabled. form_challenge: current_password: You are entering a secure area imports: @@ -412,6 +412,7 @@ en: enable_public_unlisted_visibility: Enable public-unlisted visibility / public-unlisted searchability enable_public_visibility: Enable public visibility favicon: Favicon + landing_page: Landing page for new visitors local_live_feed_access: Access to live feeds featuring local posts local_topic_feed_access: Access to hashtag and link feeds featuring local posts mascot: Custom mascot (legacy) @@ -447,7 +448,6 @@ en: thumbnail: Server thumbnail trendable_by_default: Allow trends without prior review trends: Enable trends - trends_as_landing_page: Use trends as the landing page unlocked_friend: Accept all friend server follows automatically interactions: must_be_follower: Block notifications from non-followers diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 541113c28aa215..eed4671bb789ee 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -107,7 +107,6 @@ eo: thumbnail: Ĉirkaua 2:1 bildo montritas kun via servilinformo. trendable_by_default: Ignori permanan kontrolon de tendenca enhavo. trends: Tendencoj montras kiu mesaĝoj, kradvortoj kaj novaĵoj populariĝas en via servilo. - trends_as_landing_page: Montru tendencan enhavon al elsalutitaj uzantoj kaj vizitantoj anstataŭ priskribo de ĉi tiu servilo. Necesas ke tendencoj estu ebligitaj. form_challenge: current_password: Vi eniras sekuran areon imports: @@ -296,7 +295,6 @@ eo: thumbnail: Bildeto de servilo trendable_by_default: Permesi tendencojn sen deviga kontrolo trends: Ŝalti furorojn - trends_as_landing_page: Uzu tendencojn kiel la landpaĝon interactions: must_be_follower: Bloki sciigojn de nesekvantoj must_be_following: Bloki sciigojn de homoj, kiujn vi ne sekvas diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml index 760832e2af9199..2be68cd4b146e3 100644 --- a/config/locales/simple_form.es-AR.yml +++ b/config/locales/simple_form.es-AR.yml @@ -93,6 +93,7 @@ es-AR: content_cache_retention_period: Todos los mensajes de otros servidores (incluyendo adhesiones y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes que un usuario local haya agregado a marcadores o los haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes servidores también se perderán y también serán imposibles de restaurar. El uso de esta configuración está destinado a servidores de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general. custom_css: Podés aplicar estilos personalizados a la versión web de Mastodon. favicon: WEBP, PNG, GIF o JPG. Reemplaza el favicón predeterminado de Mastodon con uno personalizado. + landing_page: Selecciona qué página se carga para los nuevos visitantes cuando llegan por primera vez a tu servidor. Si seleccionás «Tendencias», entonces las tendencias tienen que estar habilitadas en la configuración de «Descubrimiento». Si seleccionás «Línea temporal local», entonces «Acceso a líneas temporales en vivo, destacando mensajes locales» tiene que estar establecida a «Todos» en la configuración de «Descubrimiento». mascot: Reemplaza la ilustración en la interface web avanzada. media_cache_retention_period: Los archivos de medios de mensajes publicados por usuarios remotos se almacenan en la memoria caché en tu servidor. Cuando se establece un valor positivo, los medios se eliminarán después del número especificado de días. Si los datos multimedia se solicitan después de eliminarse, se volverán a descargar, si es que el contenido fuente todavía está disponible. Debido a restricciones en la frecuencia con la que las tarjetas de previsualización de enlace consultan a sitios web de terceros, se recomienda establecer este valor a, al menos, 14 días, o las tarjetas de previsualización de enlaces no se actualizarán a pedido antes de ese momento. min_age: Se pedirá a los usuarios que confirmen su fecha de nacimiento durante el registro @@ -110,7 +111,6 @@ es-AR: thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor. trendable_by_default: Omití la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias. trends: Las tendencias muestran qué mensajes, etiquetas y noticias están ganando tracción en tu servidor. - trends_as_landing_page: Mostrar contenido en tendencia para usuarios que no iniciaron sesión y visitantes, en lugar de una descripción de este servidor. Requiere que las tendencias estén habilitadas. form_challenge: current_password: Estás ingresando en un área segura imports: @@ -287,6 +287,7 @@ es-AR: content_cache_retention_period: Período de retención de contenido remoto custom_css: CSS personalizado favicon: Favicón + landing_page: Página de carga para nuevos visitantes local_live_feed_access: Acceso a líneas temporales en vivo, destacando mensajes locales local_topic_feed_access: Acceso a líneas temporales de etiquetas y enlaces, destacando mensajes locales mascot: Mascota personalizada (legado) @@ -311,7 +312,6 @@ es-AR: thumbnail: Miniatura del servidor trendable_by_default: Permitir tendencias sin revisión previa trends: Habilitar tendencias - trends_as_landing_page: Usar las tendencias como la página de destino interactions: must_be_follower: Bloquear notificaciones de cuentas que no te siguen must_be_following: Bloquear notificaciones de cuentas que no seguís diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 87ab1274ad89b2..9494a930671bc3 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -93,6 +93,7 @@ es-MX: content_cache_retention_period: Todas las publicaciones de otros servidores (incluyendo impuestos y respuestas) serán borrados después del número de días especificado, sin tener en cuenta cualquier interacción del usuario local con esas publicaciones. Esto incluye los mensajes que un usuario local haya marcado como favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán y será imposible restaurarlas. El uso de esta configuración está pensado para instancias de propósito especial y rompe muchas expectativas de los usuarios cuando se implementa para uso general. custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon. favicon: WEBP, PNG, GIF o JPG. Reemplaza el icono predeterminado de Mastodon con un icono personalizado. + landing_page: Selecciona qué página ven los nuevos visitantes cuando llegan por primera vez a tu servidor. Si seleccionas "Tendencias", entonces las tendencias deben estar habilitadas en la Configuración de Descubrimiento. Si selecciona "Cronología local", entonces "Acceso a las cronologías que destacan publicaciones locales" debe configurarse a "Todos" en la Configuración de Descubrimiento. mascot: Reemplaza la ilustración en la interfaz web avanzada. media_cache_retention_period: Los archivos multimedia de las publicaciones realizadas por usuarios remotos se almacenan en caché en su servidor. Si se establece en un valor positivo, los archivos multimedia se eliminarán tras el número de días especificado. Si los datos multimedia se solicitan después de haber sido eliminados, se volverán a descargar, si el contenido de origen sigue estando disponible. Debido a las restricciones sobre la frecuencia con la que las tarjetas de previsualización de enlaces sondean sitios de terceros, se recomienda establecer este valor en al menos 14 días, o las tarjetas de previsualización de enlaces no se actualizarán bajo demanda antes de ese tiempo. min_age: Se pedirá a los usuarios que confirmen su fecha de nacimiento al registrarse @@ -110,7 +111,6 @@ es-MX: thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor. trendable_by_default: Omitir la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias. trends: Las tendencias muestran qué mensajes, etiquetas y noticias están ganando tracción en tu servidor. - trends_as_landing_page: Mostrar contenido en tendencia para usuarios y visitantes desconectados en lugar de una descripción de este servidor. Requiere tendencias para ser habilitado. form_challenge: current_password: Estás entrando en un área segura imports: @@ -287,6 +287,7 @@ es-MX: content_cache_retention_period: Periodo de conservación de contenidos remotos custom_css: CSS personalizado favicon: Favicon + landing_page: Página de inicio para nuevos visitantes local_live_feed_access: Acceso a las cronologías que destacan publicaciones locales local_topic_feed_access: Acceso a las etiquetas y enlaces en tendencia que destacan publicaciones locales mascot: Mascota personalizada (legado) @@ -311,7 +312,6 @@ es-MX: thumbnail: Miniatura del servidor trendable_by_default: Permitir tendencias sin revisión previa trends: Habilitar tendencias - trends_as_landing_page: Usar tendencias como página de destino interactions: must_be_follower: Bloquear notificaciones de personas que no te siguen must_be_following: Bloquear notificaciones de personas que no sigues diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index aa4208e553b6dc..5c431d04632190 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -93,6 +93,7 @@ es: content_cache_retention_period: Todas las publicaciones de otros servidores (incluso impulsos y respuestas) se eliminarán después del número de días especificado, sin tener en cuenta la interacción del usuario local con esos mensajes. Esto incluye mensajes donde un usuario local los ha marcado como marcadores o favoritos. Las menciones privadas entre usuarios de diferentes instancias también se perderán sin posibilidad de recuperación. El uso de esta configuración está destinado a instancias de propósito especial, y rompe muchas expectativas de los usuarios cuando se implementa para un uso de propósito general. custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon. favicon: WEBP, PNG, GIF o JPG. Reemplaza el favicon predeterminado de Mastodon con un icono personalizado. + landing_page: Selecciona qué página ven los nuevos visitantes cuando llegan por primera vez a tu servidor. Si seleccionas "Tendencias", entonces las tendencias deben estar habilitadas en la Configuración de Descubrimiento. Si selecciona "Cronología local", entonces "Acceso a las cronologías que destacan publicaciones locales" debe configurarse a "Todos" en la Configuración de Descubrimiento. mascot: Reemplaza la ilustración en la interfaz web avanzada. media_cache_retention_period: Los archivos multimedia de las publicaciones creadas por usuarios remotos se almacenan en caché en tu servidor. Cuando se establece un valor positivo, estos archivos se eliminarán después del número especificado de días. Si los datos multimedia se solicitan después de eliminarse, se volverán a descargar, si el contenido fuente todavía está disponible. Debido a restricciones en la frecuencia con la que las tarjetas de previsualización de enlaces realizan peticiones a sitios de terceros, se recomienda establecer este valor a al menos 14 días, o las tarjetas de previsualización de enlaces no se actualizarán bajo demanda antes de ese momento. min_age: Se pedirá a los usuarios que confirmen su fecha de nacimiento durante el registro @@ -110,7 +111,6 @@ es: thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor. trendable_by_default: Omitir la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias. trends: Las tendencias muestran qué publicaciones, etiquetas y noticias están ganando tracción en tu servidor. - trends_as_landing_page: Mostrar contenido en tendencia para usuarios y visitantes en lugar de una descripción de este servidor. Requiere que las tendencias estén habilitadas. form_challenge: current_password: Estás entrando en un área segura imports: @@ -287,6 +287,7 @@ es: content_cache_retention_period: Período de retención de contenido remoto custom_css: CSS personalizado favicon: Favicon + landing_page: Página de inicio para nuevos visitantes local_live_feed_access: Acceso a las cronologías que destacan publicaciones locales local_topic_feed_access: Acceso a las etiquetas y enlaces en tendencia que destacan publicaciones locales mascot: Mascota personalizada (legado) @@ -311,7 +312,6 @@ es: thumbnail: Miniatura del servidor trendable_by_default: Permitir tendencias sin revisión previa trends: Habilitar tendencias - trends_as_landing_page: Usar tendencias como la página de inicio interactions: must_be_follower: Bloquear notificaciones de personas que no te siguen must_be_following: Bloquear notificaciones de personas que no sigues diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml index dda3aa76368e3e..a7c318f04f3c3f 100644 --- a/config/locales/simple_form.et.yml +++ b/config/locales/simple_form.et.yml @@ -107,7 +107,6 @@ et: thumbnail: Umbes 2:1 mõõdus pilt serveri informatsiooni kõrval. trendable_by_default: Populaarse sisu ülevaatuse vahele jätmine. Pärast seda on siiski võimalik üksikuid üksusi trendidest eemaldada. trends: Trendid näitavad, millised postitused, sildid ja uudislood koguvad sinu serveris tähelepanu. - trends_as_landing_page: Näitab välja logitud kasutajatele ja külalistele serveri kirjelduse asemel populaarset sisu. Populaarne sisu (trendid) peab selleks olema sisse lülitatud. form_challenge: current_password: Turvalisse alasse sisenemine imports: @@ -307,7 +306,6 @@ et: thumbnail: Serveri pisipilt trendable_by_default: Luba trendid eelneva ülevaatuseta trends: Luba trendid - trends_as_landing_page: Kasuta maabumislehena lehte Populaarne interactions: must_be_follower: Keela teavitused mittejälgijatelt must_be_following: Keela teavitused kasutajatelt, keda sa ei jälgi diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml index 12eddb55f0b99e..fc7879896bdb5c 100644 --- a/config/locales/simple_form.eu.yml +++ b/config/locales/simple_form.eu.yml @@ -105,7 +105,6 @@ eu: thumbnail: Zerbitzariaren informazioaren ondoan erakusten den 2:1 inguruko irudia. trendable_by_default: Saltatu joeretako edukiaren eskuzko berrikuspena. Ondoren elementuak banan-bana kendu daitezke joeretatik. trends: Joeretan zure zerbitzarian bogan dauden bidalketa, traola eta albisteak erakusten dira. - trends_as_landing_page: Erakutsi pil-pilean dagoen edukia saioa hasita ez duten erabiltzaileei eta bisitariei, zerbitzari honen deskribapena erakutsi ordez. Joerak aktibatuak edukitzea beharrezkoa da. form_challenge: current_password: Zonalde seguruan sartzen ari zara imports: @@ -278,7 +277,6 @@ eu: thumbnail: Zerbitzariaren koadro txikia trendable_by_default: Onartu joerak aurrez berrikusi gabe trends: Gaitu joerak - trends_as_landing_page: Erabili joerak hasierako orri gisa interactions: must_be_follower: Blokeatu jarraitzaile ez direnen jakinarazpenak must_be_following: Blokeatu zuk jarraitzen ez dituzu horien jakinarazpenak diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml index 41f59bed40cde8..d244cb5283439c 100644 --- a/config/locales/simple_form.fa.yml +++ b/config/locales/simple_form.fa.yml @@ -105,7 +105,6 @@ fa: thumbnail: یک تصویر تقریباً 2:1 در کنار اطلاعات سرور شما نمایش داده می شود. trendable_by_default: از بررسی دستی محتوای پرطرفدار صرف نظر کنید. آیتم های فردی هنوز هم می توانند پس از واقعیت از روند حذف شوند. trends: روندها نشان می‌دهند که کدام پست‌ها، هشتگ‌ها و داستان‌های خبری در سرور شما مورد توجه قرار گرفته‌اند. - trends_as_landing_page: به جای توضیح این سرور، محتوای پرطرفدار را به کاربران و بازدیدکنندگان از سیستم خارج شده نشان دهید. نیاز به فعال شدن روندها دارد. form_challenge: current_password: شما در حال ورود به یک منطقهٔ‌ حفاظت‌شده هستید imports: @@ -297,7 +296,6 @@ fa: thumbnail: بندانگشتی کارساز trendable_by_default: اجازهٔ پرطرفدار شدن بدون بازبینی پیشین trends: به کار انداختن پرطرفدارها - trends_as_landing_page: استفاده از داغ‌ها به عنوان صفحهٔ فرود interactions: must_be_follower: انسداد آگاهی‌ها از ناپی‌گیران must_be_following: انسداد آگاهی‌ها از افرادی که پی نمی‌گیرید diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 0189b58495417b..296d5def5b63f2 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -93,6 +93,7 @@ fi: content_cache_retention_period: Kaikki muiden palvelinten julkaisut (mukaan lukien tehostukset ja vastaukset) poistuvat, kun määritetty määrä päiviä on kulunut, lukuun ottamatta paikallisen käyttäjän vuorovaikutusta näiden julkaisujen kanssa. Tämä sisältää julkaisut, jotka paikallinen käyttäjä on merkinnyt kirjanmerkiksi tai suosikiksi. Myös yksityismaininnat eri palvelinten käyttäjien välillä menetetään, eikä niitä voi palauttaa. Tämä asetus on tarkoitettu käytettäväksi erityistapauksissa ja rikkoo monia käyttäjien odotuksia, kun sitä sovelletaan yleiskäyttöön. custom_css: Voit käyttää mukautettuja tyylejä Mastodonin selainversiossa. favicon: WEBP, PNG, GIF tai JPG. Korvaa oletusarvoisen Mastodonin sivustokuvakkeen haluamallasi kuvakkeella. + landing_page: Valitsee mitä sivua uudet kävijät näkevät saapuessaan palvelimellesi. Jos valitset "Trendit", trendien tulee olla käytössä Löytöasetuksissa. Jos valitset "Paikallinen syöte", "Pääsy paikallisten julkaisujen live-syötteisiin" tulee asettaa "Kaikille" Löytöasetuksissa. mascot: Korvaa kuvituksen edistyneessä selainkäyttöliittymässä. media_cache_retention_period: Etäkäyttäjien tekemien julkaisujen mediatiedostot ovat välimuistissa palvelimellasi. Kun kentän arvo on positiivinen, media poistuu, kun määritetty määrä päiviä on kulunut. Jos mediaa pyydetään sen poistamisen jälkeen, se ladataan uudelleen, jos lähdesisältö on vielä saatavilla. Koska linkkien esikatselun kyselyitä kolmansien osapuolien sivustoille on rajoitettu, on suositeltavaa asettaa tämä arvo vähintään 14 päivään, tai linkkien kortteja ei päivitetä pyynnöstä ennen tätä ajankohtaa. min_age: Käyttäjiä pyydetään rekisteröitymisen aikana vahvistamaan syntymäpäivänsä @@ -110,7 +111,6 @@ fi: thumbnail: Noin 2:1 kuva näkyy palvelimen tietojen ohessa. trendable_by_default: Ohita suositun sisällön manuaalinen tarkastus. Yksittäisiä kohteita voidaan edelleen poistaa jälkikäteen. trends: Trendit osoittavat, mitkä julkaisut, aihetunnisteet ja uutiset keräävät huomiota palvelimellasi. - trends_as_landing_page: Näytä vierailijoille ja uloskirjautuneille käyttäjille suosittua sisältöä palvelimen kuvauksen sijaan. Edellyttää, että trendit on otettu käyttöön. form_challenge: current_password: Olet menossa suojatulle alueelle imports: @@ -145,6 +145,7 @@ fi: admin_email: Oikeudellisiin ilmoituksiin kuuluvat vastailmoitukset, oikeuden määräykset, poistopyynnöt ja lainvalvontaviranomaisten pyynnöt. arbitration_address: Voi olla sama kuin edellä mainittu Fyysinen osoite tai ”N/A”, jos käytät sähköpostia. arbitration_website: Voi olla verkkolomake tai ”N/A”, jos käytät sähköpostia. + choice_of_law: Kaupunki, alue, territoriumi tai valtio, jonka sisäiset substantiiviset lait säätelevät kaikkia vaateita. dmca_address: Yhdysvaltalaisten operaattoreiden on käytettävä DMCA Designated Agent Directory -luetteloon rekisteröityä osoitetta. Postilokeroluettelo on saatavissa suoralla pyynnöllä, joten käytä DMCA Designated Agent Post Office Box Waiver Request -lomaketta lähettääksesi sähköpostia tekijänoikeusvirastolle ja kuvaile, että olet kotona toimiva sisältömoderaattori, joka pelkää kostoa tai rangaistusta toimistaan ja tarvitsee postilokeroa pitääkseen kotiosoitteensa poissa julkisuudesta. dmca_email: Voi olla sama kuin edellä mainittu ”Sähköpostiosoite oikeudellisille ilmoituksille”. domain: Tarjoamasi verkkopalvelun yksilöllinen tunniste. @@ -286,6 +287,7 @@ fi: content_cache_retention_period: Etäsisällön säilytysaika custom_css: Mukautettu CSS favicon: Sivustokuvake + landing_page: Uusien kävijöiden aloitussivu local_live_feed_access: Pääsy paikallisia julkaisuja esitteleviin livesyötteisiin local_topic_feed_access: Pääsy paikallisia julkaisuja esitteleviin aihetunniste- ja linkkisyötteisiin mascot: Mukautettu maskotti (vanhentunut) @@ -310,7 +312,6 @@ fi: thumbnail: Palvelimen pienoiskuva trendable_by_default: Salli trendit ilman ennakkotarkastusta trends: Ota trendit käyttöön - trends_as_landing_page: Käytä trendejä aloitussivuna interactions: must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index 9f972d0c23fa62..14a97e07bcf4ff 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -93,6 +93,7 @@ fo: content_cache_retention_period: Allir postar frá øðrum ambætarum (íroknað stimbranir og svar) verða strikaði eftir ásetta talið av døgum, óansæð hvussu lokalir brúkarar hava samvirkað við hesar postar. Hetta fevnir eisini um postar, sum lokalir brúkarar hava bókamerkt ella yndismerkt. Privatar umrøður millum brúkarar frá ymiskum ambætarum verða eisini burturmistar og ómøguligar at endurskapa. Brúk av hesi stillingini er einans hugsað til serligar støður og oyðileggur nógv, sum brúkarar vænta av einum vanligum ambætara. custom_css: Tú kanst seta títt egna snið upp í net-útgávuni av Mastodon. favicon: WEBP, PNG, GIF ella JPG. Býtir vanligu Mastodon fav-ikonina um við eina ser-ikon. + landing_page: Velur hvørja síðu nýggj vitandi síggja tá tey koma á ambætaran hjá tær. Neyðugt er at rák eru gjørd virkin í Uppdagingarstillingum, um tú velur "Rák". Velur tú "Lokal rás" má "Atgongd til beinleiðis rásir við lokalum postum" vera sett til "Øll" í Uppdagingarstillingum. mascot: Skúgvar til viks myndprýðingina í framkomna vev-markamótinum. media_cache_retention_period: Miðlafílur frá postum, sum fjarbrúkarar hava gjørt, verða goymdir á tínum ambætara. Tá hetta er sett til eitt virði størri enn 0, so verða miðlafílurnar strikaðar eftir ásetta talið av døgum. Um miðladátur verða umbidnar eftir at tær eru strikaðar, verða tær tiknar innaftur á ambætaran, um keldutilfarið enn er tøkt. Vegna avmarkingar á hvussu ofta undanvísingarkort til leinki spyrja triðjapartsstøð, so verður mælt til at seta hetta virðið til í minsta lagi 14 dagar. Annars verða umbønir um dagføringar av undanvísingarkortum til leinki ikki gjørdar áðrenn hetta. min_age: Brúkarar verða spurdir um at vátta teirra føðingardag, tá tey skráseta seg @@ -110,7 +111,6 @@ fo: thumbnail: Ein mynd í lutfallinum 2:1, sum verður víst saman við ambætaraupplýsingunum hjá tær. trendable_by_default: Loyp uppum serskilda eftirkannan av tilfari, sum er vælumtókt. Einstakir lutir kunnu framvegis strikast frá listum við vælumtóktum tilfari seinni. trends: Listar við vælumtóktum tilfari vísa, hvørjir postar, frámerki og tíðindasøgur hava framburð á tínum ambætara. - trends_as_landing_page: Vís vitjandi og brúkarum, sum ikki eru innritaðir, rák í staðin fyri eina lýsing av ambætaranum. Krevur at rák eru virkin. form_challenge: current_password: Tú ert á veg til eitt trygt øki imports: @@ -287,6 +287,7 @@ fo: content_cache_retention_period: Tíðarskeið fyri varðveiðslu av fjartilfari custom_css: Serskilt CSS favicon: Favikon + landing_page: Heimasíða til nýggj vitjandi local_live_feed_access: Atgongd til beinleiðis rásir við lokalum postum local_topic_feed_access: Atgongd til frámerki og rásir við leinkjum við lokalum postum mascot: Serskildur maskottur (arvur) @@ -311,7 +312,6 @@ fo: thumbnail: Ambætarasmámynd trendable_by_default: Loyv vælumtóktum tilfari uttan at viðgera tað fyrst trends: Loyv ráki - trends_as_landing_page: Brúka rák sum lendingarsíðu interactions: must_be_follower: Blokera fráboðanum frá teimum, sum ikki fylgja tær must_be_following: Blokera fráboðanum frá teimum, tú ikki fylgir diff --git a/config/locales/simple_form.fr-CA.yml b/config/locales/simple_form.fr-CA.yml index 0798eebfa09b0a..b0dae17121ed48 100644 --- a/config/locales/simple_form.fr-CA.yml +++ b/config/locales/simple_form.fr-CA.yml @@ -104,7 +104,6 @@ fr-CA: thumbnail: Une image d'environ 2:1 affichée à côté des informations de votre serveur. trendable_by_default: Ignorer l'examen manuel du contenu tendance. Des éléments individuels peuvent toujours être supprimés des tendances après coup. trends: Les tendances montrent quelles publications, hashtags et actualités sont en train de gagner en traction sur votre serveur. - trends_as_landing_page: Afficher le contenu tendance au lieu d'une description de ce serveur pour les comptes déconnectés et les non-inscrit⋅e⋅s. Nécessite que les tendances soient activées. form_challenge: current_password: Vous entrez une zone sécurisée imports: @@ -288,7 +287,6 @@ fr-CA: thumbnail: Miniature du serveur trendable_by_default: Autoriser les tendances sans révision préalable trends: Activer les tendances - trends_as_landing_page: Utiliser les tendances comme page d'accueil interactions: must_be_follower: Bloquer les notifications des personnes qui ne vous suivent pas must_be_following: Bloquer les notifications des personnes que vous ne suivez pas diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 117523e38b08f6..270a259dec1d4c 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -104,7 +104,6 @@ fr: thumbnail: Une image d'environ 2:1 affichée à côté des informations de votre serveur. trendable_by_default: Ignorer l'examen manuel du contenu tendance. Des éléments individuels peuvent toujours être supprimés des tendances après coup. trends: Les tendances montrent quels messages, hashtags et actualités gagnent en popularité sur votre serveur. - trends_as_landing_page: Afficher le contenu tendance au lieu d'une description de ce serveur pour les comptes déconnectés et les non-inscrit⋅e⋅s. Nécessite que les tendances soient activées. form_challenge: current_password: Vous entrez une zone sécurisée imports: @@ -288,7 +287,6 @@ fr: thumbnail: Miniature du serveur trendable_by_default: Autoriser les tendances sans révision préalable trends: Activer les tendances - trends_as_landing_page: Utiliser les tendances comme page d'accueil interactions: must_be_follower: Bloquer les notifications des personnes qui ne vous suivent pas must_be_following: Bloquer les notifications des personnes que vous ne suivez pas diff --git a/config/locales/simple_form.fy.yml b/config/locales/simple_form.fy.yml index f7fe06332c18e9..9b0be65b3e9b6d 100644 --- a/config/locales/simple_form.fy.yml +++ b/config/locales/simple_form.fy.yml @@ -105,7 +105,6 @@ fy: thumbnail: In ôfbylding fan ûngefear in ferhâlding fan 2:1 dy’t njonken jo serverynformaasje toand wurdt. trendable_by_default: Hânmjittige beoardieling fan trends oerslaan. Yndividuele items kinne letter dochs noch ôfkard wurde. trends: Trends toane hokker berjochten, hashtags en nijsberjochten op jo server oan populariteit winne. - trends_as_landing_page: Toan trending ynhâld oan ôfmelde brûkers en besikers yn stee fan in beskriuwing fan dizze server. Fereasket dat trends ynskeakele binne. form_challenge: current_password: Jo betrêdzje in feilige omjouwing imports: @@ -293,7 +292,6 @@ fy: thumbnail: Serverthumbnail trendable_by_default: Trends goedkarre sûnder yn it foar geande beoardieling trends: Trends ynskeakelje - trends_as_landing_page: Lit trends op de startside sjen interactions: must_be_follower: Meldingen fan minsken dy’t jo net folgje blokkearje must_be_following: Meldingen fan minsken dy’t jo net folgje blokkearje diff --git a/config/locales/simple_form.ga.yml b/config/locales/simple_form.ga.yml index 5e6e418d278109..decbe02cecd4b2 100644 --- a/config/locales/simple_form.ga.yml +++ b/config/locales/simple_form.ga.yml @@ -93,6 +93,7 @@ ga: content_cache_retention_period: Scriosfar gach postáil ó fhreastalaithe eile (lena n-áirítear treisithe agus freagraí) tar éis an líon sonraithe laethanta, gan aird ar aon idirghníomhaíocht úsáideora áitiúil leis na postálacha sin. Áirítear leis seo postálacha ina bhfuil úsáideoir áitiúil tar éis é a mharcáil mar leabharmharcanna nó mar cheanáin. Caillfear tagairtí príobháideacha idir úsáideoirí ó chásanna éagsúla freisin agus ní féidir iad a athchóiriú. Tá úsáid an tsocraithe seo beartaithe le haghaidh cásanna sainchuspóra agus sáraítear go leor ionchais úsáideoirí nuair a chuirtear i bhfeidhm é le haghaidh úsáid ghinearálta. custom_css: Is féidir leat stíleanna saincheaptha a chur i bhfeidhm ar an leagan gréasáin de Mastodon. favicon: WEBP, PNG, GIF nó JPG. Sáraíonn sé an favicon Mastodon réamhshocraithe le deilbhín saincheaptha. + landing_page: Roghnaíonn sé seo an leathanach a fheiceann cuairteoirí nua nuair a shroicheann siad do fhreastalaí den chéad uair. Má roghnaíonn tú "Treochtaí", ní mór treochtaí a chumasú sna Socruithe Fionnachtana. Má roghnaíonn tú "Fotha Áitiúil", ní mór "Rochtain ar fhothaí beo ina bhfuil poist áitiúla" a shocrú go "Gach Duine" sna Socruithe Fionnachtana. mascot: Sáraíonn sé an léaráid san ardchomhéadan gréasáin. media_cache_retention_period: Déantar comhaid meán ó phoist a dhéanann cianúsáideoirí a thaisceadh ar do fhreastalaí. Nuair a bheidh luach dearfach socraithe, scriosfar na meáin tar éis an líon sonraithe laethanta. Má iarrtar na sonraí meán tar éis é a scriosadh, déanfar é a ath-íoslódáil, má tá an t-ábhar foinse fós ar fáil. Mar gheall ar shrianta ar cé chomh minic is atá cártaí réamhamhairc ag vótaíocht do shuíomhanna tríú páirtí, moltar an luach seo a shocrú go 14 lá ar a laghad, nó ní dhéanfar cártaí réamhamhairc naisc a nuashonrú ar éileamh roimh an am sin. min_age: Iarrfar ar úsáideoirí a ndáta breithe a dhearbhú le linn clárúcháin @@ -110,7 +111,6 @@ ga: thumbnail: Íomhá thart ar 2:1 ar taispeáint taobh le faisnéis do fhreastalaí. trendable_by_default: Léim ar athbhreithniú láimhe ar ábhar treochta. Is féidir míreanna aonair a bhaint as treochtaí fós tar éis an fhíric. trends: Léiríonn treochtaí cé na postálacha, hashtags agus scéalta nuachta atá ag tarraingt ar do fhreastalaí. - trends_as_landing_page: Taispeáin inneachar treochta d'úsáideoirí agus do chuairteoirí atá logáilte amach in ionad cur síos ar an bhfreastalaí seo. Éilíonn treochtaí a chumasú. form_challenge: current_password: Tá tú ag dul isteach i limistéar slán imports: @@ -290,6 +290,7 @@ ga: content_cache_retention_period: Tréimhse choinneála inneachair cianda custom_css: CSS saincheaptha favicon: Favicon + landing_page: Leathanach tuirlingthe do chuairteoirí nua local_live_feed_access: Rochtain ar bheatha bheo ina bhfuil poist áitiúla local_topic_feed_access: Rochtain ar fhothaí hashtag agus nasc ina bhfuil poist áitiúla mascot: Mascóg saincheaptha (oidhreacht) @@ -314,7 +315,6 @@ ga: thumbnail: Mionsamhail freastalaí trendable_by_default: Ceadaigh treochtaí gan athbhreithniú roimh ré trends: Cumasaigh treochtaí - trends_as_landing_page: Úsáid treochtaí mar an leathanach tuirlingthe interactions: must_be_follower: Cuir bac ar fhógraí ó dhaoine nach leantóirí iad must_be_following: Cuir bac ar fhógraí ó dhaoine nach leanann tú diff --git a/config/locales/simple_form.gd.yml b/config/locales/simple_form.gd.yml index a4870f88cecc08..bc4c3b7f0a9c10 100644 --- a/config/locales/simple_form.gd.yml +++ b/config/locales/simple_form.gd.yml @@ -54,8 +54,10 @@ gd: password: Cleachd co-dhiù 8 caractaran phrase: Thèid a mhaidseadh gun aire air litrichean mòra ’s beaga no air rabhadh susbainte puist scopes: Na APIan a dh’fhaodas an aplacaid inntrigeadh. Ma thaghas tu sgòp air ìre as àirde, cha leig thu leas sgòpaichean fa leth a thaghadh. + setting_advanced_layout: Seall Mastodon ’na ioma-cholbh ach am faic thu an loidhne-ama, na brathan agus treas colbh a thaghas tu fhèin. Cha mholamaid seo air sgrìnichean beaga. setting_aggregate_reblogs: Na seall brosnachaidhean ùra do phostaichean a chaidh a bhrosnachadh o chionn goirid (cha doir seo buaidh ach air brosnachaidhean ùra o seo a-mach) setting_always_send_emails: Mar as àbhaist, cha dèid brathan puist-d a chur nuair a a bhios tu ri Mastodon gu cunbhalach + setting_boost_modal: Ma tha seo an comas, thèid còmhradh dearbhaidh a shealltainn far an urrainn dhut faicsinneachd a’ phuist agad atharrachadh nuair a bhios tu ri brosnachadh. setting_default_quote_policy_private: Chan urrainn do chàch postaichean dhan luchd-leantainn a-mhàin a chaidh a sgrìobhadh le Mastodon a luaidh. setting_default_quote_policy_unlisted: Nuair a luaidheas daoine thu, thèid am post aca-san fhalach o loidhnichean-ama nan treandaichean. setting_default_sensitive: Thèid meadhanan frionasach fhalach a ghnàth is gabhaidh an nochdadh le briogadh orra @@ -63,6 +65,7 @@ gd: setting_display_media_hide_all: Falaich na meadhanan an-còmhnaidh setting_display_media_show_all: Seall na meadhanan an-còmhnaidh setting_emoji_style: An dòigh air an dèid emojis a shealltainn. Feuchaidh “Fèin-obrachail” ris na h-emojis tùsail a chleachdadh ach thèid Twemoji a chleachdadh ’nan àite air seann-bhrabhsairean. + setting_quick_boosting_html: Ma tha seo an comas, ma nì thu briogadh air ìomhaigheag %{boost_icon} a’ bhrosnachaidh, thèid a bhriosnachadh sa bhad seach a bhith a’ fosgladh clàr-taice teàrnach a’ bhrosnachaidh/luaidh. Thèid gnìomh an luaidh a ghluasad gu clàr-taice nan %{options_icon} (roghainnean). setting_system_scrollbars_ui: Chan obraich seo ach air brabhsairean desktop stèidhichte air Safari ’s Chrome setting_use_blurhash: Tha caiseadan stèidhichte air dathan nan nithean lèirsinneach a chaidh fhalach ach chan fhaicear am mion-fhiosrachadh setting_use_pending_items: Falaich ùrachaidhean na loidhne-ama air cùlaibh briogaidh seach a bhith a’ sgroladh nam postaichean gu fèin-obrachail @@ -107,7 +110,6 @@ gd: thumbnail: Dealbh mu 2:1 a thèid a shealltainn ri taobh fiosrachadh an fhrithealaiche agad. trendable_by_default: Geàrr leum thar lèirmheas a làimh na susbainte a’ treandadh. Gabhaidh nithean fa leth a thoirt far nan treandaichean fhathast an uairsin. trends: Seallaidh na treandaichean na postaichean, tagaichean hais is naidheachdan a tha fèill mhòr orra air an fhrithealaiche agad. - trends_as_landing_page: Seall susbaint a’ treandadh dhan fheadhainn nach do chlàraich a-steach is do dh’aoighean seach tuairisgeul an fhrithealaiche seo. Feumaidh treandaichean a bhith an comas airson sin. form_challenge: current_password: Tha thu a’ tighinn a-steach gu raon tèarainte imports: @@ -165,6 +167,7 @@ gd: username_block: allow_with_approval: An àite bacadh clàraidh gu tur, bidh clàraidhean a mhaidsicheas feumach air d’ aonta comparison: Thoir an aire air an Scunthorpe Problem nuair a bhacas tu maidsichean pàirteach + username: Bidh am maidseadh coma mu litrichean mòra ’s beaga agus co-ghlifichean cumanta a leithid “4” an àite “a” no “3” an àite “e” webhook: events: Tagh na tachartasan a thèid a chur template: Cruthaich an JSON payload agad fhèin le eadar-phòlachadh chaochladairean. Fàg seo bàn airson JSON bunaiteach fhaighinn. @@ -235,10 +238,12 @@ gd: setting_aggregate_reblogs: Buidhnich na brosnachaidhean air an loidhne-ama setting_always_send_emails: Cuir brathan puist-d an-còmhnaidh setting_auto_play_gif: Cluich GIFs beòthaichte gu fèin-obrachail + setting_boost_modal: Smachd air faicsinneachd nam brosnachaidhean setting_default_language: Cànan postaidh setting_default_privacy: Faicsinneachd nam post setting_default_quote_policy: Cò dh’fhaodas luaidh setting_default_sensitive: Cuir comharra ri meadhanan an-còmhnaidh gu bheil iad frionasach + setting_delete_modal: Thoir rabhadh dhomh mus dèid post a sguabadh às setting_disable_hover_cards: Na ro-sheall pròifil nuair a dh’fhanas mi os a cionn setting_disable_swiping: Cuir gluasadan grad-shlaighdidh à comas setting_display_media: Sealltainn nam meadhanan @@ -248,6 +253,8 @@ gd: setting_emoji_style: Stoidhle nan Emojis setting_expand_spoilers: Leudaich postaichean ris a bheil rabhadh susbainte an-còmhnaidh setting_hide_network: Falaich an graf sòisealta agad + setting_missing_alt_text_modal: Thoir rabhadh dhomh mus postaich mi meadhan às aonais roghainn teacsa + setting_quick_boosting: Cuir am brosnachadh luath an comas setting_reduce_motion: Ìslich an gluasad sna beòthachaidhean setting_system_font_ui: Cleachd cruth-clò bunaiteach an t-siostaim setting_system_scrollbars_ui: Seall bàr-sgrolaidh bunaiteach an t-siostaim @@ -281,12 +288,16 @@ gd: content_cache_retention_period: Ùine glèidhidh aig susbaint chèin custom_css: CSS gnàthaichte favicon: Favicon + local_live_feed_access: Inntrigeadh dhan t-saoghal bheò sa bheil postaichean ionadail + local_topic_feed_access: Inntrigeadh dha loidhnichean-ama nan tagaichean hais is ceanglaichean sa bheil postaichean ionadail mascot: Suaichnean gnàthaichte (dìleabach) media_cache_retention_period: Ùine glèidhidh aig tasgadan nam meadhanan min_age: Riatanas aoise as lugha peers_api_enabled: Foillsich liosta nam frithealaichean a chaidh a rùrachadh san API profile_directory: Cuir eòlaire nam pròifil an comas registrations_mode: Cò dh’fhaodas clàradh + remote_live_feed_access: Inntrigeadh dhan t-saoghal bheò sa bheil postaichean cèine + remote_topic_feed_access: Inntrigeadh dha loidhnichean-ama nan tagaichean hais is ceanglaichean sa bheil postaichean cèine require_invite_text: Iarr adhbhar clàraidh show_domain_blocks: Seall bacaidhean àrainne show_domain_blocks_rationale: Seall carson a chaidh àrainnean a bacadh @@ -301,7 +312,6 @@ gd: thumbnail: Dealbhag an fhrithealaiche trendable_by_default: Ceadaich treandaichean gun lèirmheas ro làimh trends: Cuir na treandaichean an comas - trends_as_landing_page: Cleachd na treandaichean ’nan duilleag-laighe interactions: must_be_follower: Bac na brathan nach eil o luchd-leantainn must_be_following: Bac na brathan o dhaoine nach lean thu diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 982f76a97f4dc7..5599f7d5413393 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -93,6 +93,7 @@ gl: content_cache_retention_period: Todas as publicacións procedentes de outros servidores (incluído promocións e respostas) van ser eliminadas despois do número de días indicado, sen importar as interaccións das usuarias locais con esas publicacións. Esto inclúe publicacións que a usuaria local marcou como favoritas ou incluíu nos marcadores. As mencións privadas entre usuarias de diferentes instancias tamén se eliminarán e non se poderán restablecer. O uso desta ferramenta esta orientado a situacións especiais e estraga moitas das expectativas das usuarias ao implementala cun propósito de uso xeral. custom_css: Podes aplicar deseños personalizados na versión web de Mastodon. favicon: WEBP, PNG, GIF ou JPG. Sobrescribe a icona de favoritos de Mastodon por defecto cunha icona personalizada. + landing_page: Elixe a páxina que verán as persoas que se acheguen por primeira vez ao teu servidor. Se elixes «Tendencias» entón debes activar as tendencias nos Axustes de Descubrimento. Se elixes «Cronoloxía local», hai que configurar nos Axustes de Descubrimento a opción «Acceso das cronoloxías en directo ás publicacións locais» como «Calquera». mascot: Sobrescribe a ilustración na interface web avanzada. media_cache_retention_period: Os ficheiros multimedia de publicacións de usuarias remotas están almacenados no teu servidor. Ao establecer un valor positivo, o multimedia vaise eliminar despois do número de días establecido. Se o multimedia fose requerido após ser eliminado entón descargaríase outra vez, se aínda está dispoñible na orixe. Debido a restricións sobre a frecuencia en que o servizo de vista previa trae recursos de terceiras partes, é recomendable establecer este valor polo menos en 14 días, ou as tarxetas de vista previa non se actualizarán baixo demanda para casos anteriores a ese prazo. min_age: Váiselle pedir ás usuarias que confirmen a súa data de nacemento cando creen a conta @@ -110,7 +111,6 @@ gl: thumbnail: Imaxe con proporcións 2:1 mostrada xunto á información sobre o servidor. trendable_by_default: Omitir a revisión manual dos contidos populares. Poderás igualmente eliminar manualmente os elementos que vaian aparecendo. trends: As tendencias mostran publicacións, cancelos e novas historias que teñen popularidade no teu servidor. - trends_as_landing_page: Mostrar contidos en voga para as persoas sen sesión iniciada e visitantes no lugar dunha descrición deste servidor. Require ter activado Popularidade. form_challenge: current_password: Estás entrando nun área segura imports: @@ -287,6 +287,7 @@ gl: content_cache_retention_period: Período de retención de contido remoto custom_css: CSS personalizado favicon: Favicon + landing_page: Páxina que se mostra ás visitas local_live_feed_access: Acceso a cronoloxías ao vivo que mostran publicacións locais local_topic_feed_access: Acceso a cronoloxías de ligazóns e cancelos que mostran publicacións locais mascot: Mascota propia (herdado) @@ -311,7 +312,6 @@ gl: thumbnail: Icona do servidor trendable_by_default: Permitir tendencias sen aprobación previa trends: Activar tendencias - trends_as_landing_page: Usar as tendencias como páxina de benvida interactions: must_be_follower: Bloquea as notificacións de persoas que non te seguen must_be_following: Bloquea as notificacións de persoas que non segues diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 4563a61d5d3655..5f96869d76b752 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -93,6 +93,7 @@ he: content_cache_retention_period: כל ההודעות משרתים אחרים (לרבות הדהודים ותגובות) ימחקו אחרי מספר ימים, ללא קשר לאינטראקציה של משתמשים מקומיים איתם. בכלל זה הודעות שהמתשתמשים המקומיים סימנו בסימניה או חיבוב. איזכורים פרטיים ("דיאם") בין משתמשים בין שרתים שונים יאבדו גם הם ולא תהיה אפשרות לשחזרם. השימוש באפשרות הזו מיועד לשרתים עם ייעוד מיוחד ושובר את ציפיותיהם של רב המשתמשים כאשר האפשרות מופעלת בשרת לשימוש כללי. custom_css: ניתן לבחור ערכות סגנון אישיות בגרסת הדפדפן של מסטודון. favicon: WEBP, PNG, GIF או JPG. גובר על "פאבאייקון" ברירת המחדל ומחליף אותו באייקון נבחר בדפדפן. + landing_page: בחירה בעמוד שיוצג ראשון למבקרים חדשים בביקור הראשון בשרת שלך. אם תבחרו "נושאים חמים", אזי הנושאים החמים צריכים להיות מאופשרים בהעדפות "תגליות". אם תבחרו "פיד מקומי", אז "גישה לפידים חיים המציגים הודעות מקומיות" חייב להיות מכוון למצב "כולם" בהעדפות תגליות. mascot: בחירת ציור למנשק הווב המתקדם. media_cache_retention_period: קבצי מדיה מהודעות שהגיעו משרתים רחוקים נשמרות על השרת שלך. כאשר יבחר פה מספר חיובי, המדיה תמחק לאחר מספר ימים כמצוין. אם המידע יבוקש שוב לאחר שנמחק, הוא יורד מחדש, אם המידע עדיין זמין בצד הרחוק. עקב מגבלות על תכיפות שליפת כרטיסי קדימון מאתרים מרוחקים, מומלץ לכוון את הערך ל־14 יום לפחות, או שכרטיסי קדימונים לא יעודכנו לפי דרישה לפני חלוף חלון הזמן הזה. min_age: משתמשיםות יתבקשו לאשר את תאריך הלידה בתהליך ההרשמה @@ -110,7 +111,6 @@ he: thumbnail: תמונה ביחס 2:1 בערך שתוצג ליד המידע על השרת שלך. trendable_by_default: לדלג על בדיקה ידנית של התכנים החמים. פריטים ספציפיים עדיין ניתנים להסרה לאחר מעשה. trends: נושאים חמים יציגו אילו הודעות, תגיות וידיעות חדשות צוברות חשיפה על השרת שלך. - trends_as_landing_page: הצג למבקרים ולמשתמשים שאינם מחוברים את הנושאים החמים במקום את תיאור השרת. מחייב הפעלה של אפשרות הנושאים החמים. form_challenge: current_password: את.ה נכנס. ת לאזור מאובטח imports: @@ -289,6 +289,7 @@ he: content_cache_retention_period: תקופת השמירה על תוכן חיצוני custom_css: CSS בהתאמה אישית favicon: סמל מועדפים (Favicon) + landing_page: דף נחיתה למבקרים חדשים local_live_feed_access: גישה לפידים חיים המציגים הודעות מקומיות local_topic_feed_access: גישה לפידים של תגיות וקישורים המציגים הודעות מקומיות mascot: סמל השרת (ישן) @@ -313,7 +314,6 @@ he: thumbnail: תמונה ממוזערת מהשרת trendable_by_default: הרשאה לפריטים להופיע בנושאים החמים ללא אישור מוקדם trends: אפשר פריטים חמים (טרנדים) - trends_as_landing_page: דף הנחיתה יהיה "נושאים חמים" interactions: must_be_follower: חסימת התראות משאינם עוקבים must_be_following: חסימת התראות משאינם נעקבים diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index 93071a73998f65..0e41641d61b31f 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -110,7 +110,6 @@ hu: thumbnail: Egy durván 2:1 arányú kép, amely a kiszolgálóinformációk mellett jelenik meg. trendable_by_default: Kézi felülvizsgálat kihagyása a felkapott tartalmaknál. Az egyes elemek utólag távolíthatók el a trendek közül. trends: A trendek azt mondják meg, hogy mely bejegyzések, hashtagek és hírbejegyzések felkapottak a kiszolgálódon. - trends_as_landing_page: Felkapott tartalmak mutatása a kijelentkezett felhasználók és látogatók számára ennek a kiszolgálónak a leírása helyett. Szükséges hozzá a trendek engedélyezése. form_challenge: current_password: Beléptél egy biztonsági térben imports: @@ -311,7 +310,6 @@ hu: thumbnail: Kiszolgáló bélyegképe trendable_by_default: Trendek engedélyezése előzetes ellenőrzés nélkül trends: Trendek engedélyezése - trends_as_landing_page: Trendek használata nyitóoldalként interactions: must_be_follower: Nem követőidtől érkező értesítések tiltása must_be_following: Nem követettjeidtől érkező értesítések tiltása diff --git a/config/locales/simple_form.ia.yml b/config/locales/simple_form.ia.yml index a7385d7c2a2d95..b231ff0dedd22e 100644 --- a/config/locales/simple_form.ia.yml +++ b/config/locales/simple_form.ia.yml @@ -93,6 +93,7 @@ ia: content_cache_retention_period: Tote le messages de altere servitores (includite impulsos e responsas) essera delite post le numero de dies specificate, independentemente de tote interaction de usatores local con ille messages. Isto include le messages addite al marcapaginas o marcate como favorite per un usator local. Le mentiones private inter usatores de differente instantias tamben essera irrecuperabilemente perdite. Le uso de iste parametro es intendite pro instantias con scopos specific e viola multe expectationes de usatores si es implementate pro uso general. custom_css: Tu pote applicar stilos personalisate sur le version de web de Mastodon. favicon: WEBP, PNG, GIF o JPG. Supplanta le favicone predefinite de Mastodon con un icone personalisate. + landing_page: Selige le pagina presentate al nove visitatores al prime arrivata sur tu servitor. Si tu selige “Tendentias”, alora le tendentias debe esser activate in le Parametros de discoperta. Si tu selige “Canal local”, alora le option “Accesso a canales in vivo con messages local” debe esser mittite a “Omnes” in le Parametros de discoperta. mascot: Illo substitue le illustration in le interfacie web avantiate. media_cache_retention_period: Le files multimedial de messages producite per usatores distante se immagazina in cache sur tu servitor. Quando iste option es definite a un valor positive, tal files essera delite post le numero specificate de dies. Si alcuno requesta le datos multimedial post lor deletion, illos essera re-discargate si le contento original es ancora disponibile. Debite a limitationes sur le frequentia con que le cartas de previsualisation de ligamines se connecte al sitos de tertios, il es recommendate definir iste valor a al minus 14 dies, alteremente le previsualisationes de ligamines non essera actualisate sur demanda ante ille tempore. min_age: Le usatores debera confirmar lor data de nascentia durante le inscription @@ -110,7 +111,6 @@ ia: thumbnail: Un imagine de circa 2:1 monstrate al latere del informationes de tu servitor. trendable_by_default: Saltar le revision manual del contento de tendentia. Elementos singule pote ancora esser removite de tendentias post le facto. trends: Tendentias monstra que messages, hashtags e novas gania traction sur tu servitor. - trends_as_landing_page: Monstrar contento de tendentia a usatores disconnexe e visitatores in vice que un description de iste servitor. Require tendentias esser activate. form_challenge: current_password: Tu entra in un area secur imports: @@ -287,6 +287,7 @@ ia: content_cache_retention_period: Periodo de retention del contento remote custom_css: CSS personalisate favicon: Favicon + landing_page: Pagina de arrivata pro nove visitatores local_live_feed_access: Accesso a canales in vivo con messages local local_topic_feed_access: Accesso a canales de hashtag e ligamines con messages local mascot: Personalisar le mascotte (hereditage) @@ -311,7 +312,6 @@ ia: thumbnail: Miniatura de servitor trendable_by_default: Permitter tendentias sin revision previe trends: Activar tendentias - trends_as_landing_page: Usar tendentias como pagina de destination interactions: must_be_follower: Blocar notificationes de personas qui non te seque must_be_following: Blocar notificationes de personas que tu non seque diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml index f6da22eedc4400..728e9e81b59716 100644 --- a/config/locales/simple_form.ie.yml +++ b/config/locales/simple_form.ie.yml @@ -99,7 +99,6 @@ ie: thumbnail: Un image de dimensiones circa 2:1 monstrat along tui servitor-information. trendable_by_default: Pretersaltar un manual revision de contenete in tendentie. Mem pos to on posse remover índividual pezzes de tendentie. trends: Tendenties monstra quel postas, hashtags e novas es ganiant atention sur tui servitor. - trends_as_landing_page: Monstrar populari contenete a ínregistrat visitantes vice un description del servitor. Besona que tendenties es activisat. form_challenge: current_password: Tu nu intra un area secur imports: @@ -260,7 +259,6 @@ ie: thumbnail: Miniatura del servitor trendable_by_default: Possibilisar tendenties sin priori inspection trends: Possibilisar tendenties - trends_as_landing_page: Usar tendenties quam frontispicie interactions: must_be_follower: Bloccar notificationes de tis qui ne seque te must_be_following: Bloccar notificationes de tis quem tu ne seque diff --git a/config/locales/simple_form.io.yml b/config/locales/simple_form.io.yml index 1b47be720a17ea..03c0fd24c1970f 100644 --- a/config/locales/simple_form.io.yml +++ b/config/locales/simple_form.io.yml @@ -102,7 +102,6 @@ io: thumbnail: Cirkum 2:1 imajo montresar kun informo di ca servilo. trendable_by_default: Ignorez manuala kontrolar di populara enhavajo. trends: Populari montras quala afishi, gretvorti e novaji populareskas en vua servilo. - trends_as_landing_page: Montrez populara posti a uzanti neeniriti e vizitanti vice deskriptajo pri ca servilo. Bezonas ke populari es aktivita. form_challenge: current_password: Vu eniras sekura areo imports: @@ -278,7 +277,6 @@ io: thumbnail: Servilimajeto trendable_by_default: Permisez populari sen kontrolo trends: Ebligar populari - trends_as_landing_page: Uzar populari quale la iniciala pagino interactions: must_be_follower: Celar la savigi da homi, qui ne sequas tu must_be_following: Celar la savigi da homi, quin tu ne sequas diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml index 0702b55615d22a..71c48f2305493c 100644 --- a/config/locales/simple_form.is.yml +++ b/config/locales/simple_form.is.yml @@ -93,6 +93,7 @@ is: content_cache_retention_period: Öllum færslum af öðrum netþjónum (þar með taldar endurbirtingar og svör) verður eytt eftir uppgefinn fjölda daga, án tillits til gagnvirkni staðværra notenda við þessar færslur. Þetta á einnig við um færslur sem notandinn hefur merkt sem bókamerki eða eftirlæti. Beinar tilvísanir (einkaspjall) milli notenda af mismunandi netþjónum munu einnig tapast og er engin leið til að endurheimta þær. Notkun á þessari stillingu er einungis ætluð sérstilltum netþjónum og mun skemma fyrir notendum ef þetta er sett upp fyrir almenna notkun. custom_css: Þú getur virkjað sérsniðna stíla í vefútgáfu Mastodon. favicon: WEBP, PNG, GIF eða JPG. Tekur yfir sjálfgefna Mastodon favicon-táknmynd með sérsniðinni táknmynd. + landing_page: Þetta ákvarðar hvaða síðu nýir gestir sjá þegar þeir koma fyrst á netþjóninn þinn. Ef þú velur "Vinsælt" þá þurfa vinsælar færslur að vera virkjaðar í stillingum fyrir Uppgötvun. Ef þú velur "Staðbundið streymi" þá þarf "Aðgangur að beinum streymum, þar með töldum staðværum færslum" að vera stillt á "Allir" í stillingum fyrir Uppgötvun. mascot: Þetta tekyr yfir myndskreytinguna í ítarlega vefviðmótinu. media_cache_retention_period: Myndefnisskrár úr færslum sem gerðar eru af fjartengdum notendum eru geymdar á netþjóninum þínum. Þegar þetta er stillt á jákvætt gildi, verður þessum skrám eytt sjáfkrafa eftir þeim tiltekna fjölda daga. Ef beðið er um myndefnið eftir að því er eytt, mun það verða sótt aftur ef frumgögnin eru ennþá aðgengileg. Vegna takmarkana á hversu oft forskoðunarspjöld tengla eru sótt á utanaðkomandi netþjóna, þá er mælt með því að setja þetta gildi á að minnsta kosti 14 daga, annars gæti mistekist að uppfæra forskoðunarspjöld tengla eftir þörfum fyrir þann tíma. min_age: Notendur verða beðnir um að staðfesta fæðingardag sinn við nýskráningu @@ -110,7 +111,6 @@ is: thumbnail: Mynd um það bil 2:1 sem birtist samhliða upplýsingum um netþjóninn þinn. trendable_by_default: Sleppa handvirkri yfirferð á vinsælu efni. Áfram verður hægt að fjarlægja stök atriði úr vinsældarlistum. trends: Vinsældir sýna hvaða færslur, myllumerki og fréttasögur séu í umræðunni á netþjóninum þínum. - trends_as_landing_page: Sýna vinsælt efni til ekki-innskráðra notenda í stað lýsingar á þessum netþjóni. Krefst þess að vinsældir efnis sé virkjað. form_challenge: current_password: Þú ert að fara inn á öryggissvæði imports: @@ -287,6 +287,7 @@ is: content_cache_retention_period: Tímabil sem á að geyma fjartengt efni custom_css: Sérsniðið CSS favicon: Auðkennismynd + landing_page: Kynningarsíða fyrir nýja gesti local_live_feed_access: Aðgangur að beinum streymum, þar með töldum staðværum færslum local_topic_feed_access: Aðgangur að myllumerkjum og tengdum streymum, þar með töldum staðværum færslum mascot: Sérsniðið gæludýr (eldra) @@ -311,7 +312,6 @@ is: thumbnail: Smámynd vefþjóns trendable_by_default: Leyfa vinsælt efni án undanfarandi yfirferðar trends: Virkja vinsælt - trends_as_landing_page: Nota vinsælasta sem upphafssíðu interactions: must_be_follower: Loka á tilkynningar frá þeim sem ekki eru fylgjendur must_be_following: Loka á tilkynningar frá þeim sem þú fylgist ekki með diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 5e2219c6aa9775..bc06874e3092e8 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -110,7 +110,6 @@ it: thumbnail: Un'immagine approssimativamente 2:1 visualizzata insieme alle informazioni del tuo server. trendable_by_default: Salta la revisione manuale dei contenuti di tendenza. I singoli elementi possono ancora essere rimossi dalle tendenze dopo il fatto. trends: Le tendenze mostrano quali post, hashtag e notizie stanno guadagnando popolarità sul tuo server. - trends_as_landing_page: Mostra i contenuti di tendenza agli utenti disconnessi e ai visitatori, invece di una descrizione di questo server. Richiede l'abilitazione delle tendenze. form_challenge: current_password: Stai entrando in un'area sicura imports: @@ -311,7 +310,6 @@ it: thumbnail: Miniatura del server trendable_by_default: Consenti le tendenze senza revisione preventiva trends: Abilita le tendenze - trends_as_landing_page: Usa le tendenze come pagina di destinazione interactions: must_be_follower: Blocca notifiche da chi non ti segue must_be_following: Blocca notifiche dalle persone che non segui diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 240096436ab3a5..cea5cd5b4357b7 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -138,7 +138,6 @@ ja: thumbnail: サーバー情報と共に表示される、アスペクト比が約 2:1 の画像。 trendable_by_default: トレンドの審査を省略します。トレンドは掲載後でも個別に除外できます。 trends: トレンドは、サーバー上で人気を集めている投稿、ハッシュタグ、ニュース記事などが表示されます。 - trends_as_landing_page: ログインしていないユーザーに対して、サーバーの説明の代わりにトレンドコンテンツを表示します。トレンドを有効にする必要があります。 form_challenge: current_password: セキュリティ上重要なエリアにアクセスしています imports: @@ -425,7 +424,6 @@ ja: thumbnail: サーバーのサムネイル trendable_by_default: 審査前のトレンドの掲載を許可する trends: トレンドを有効にする - trends_as_landing_page: 新規登録画面にトレンドを表示する unlocked_friend: 全てのフレンドサーバー申請を自動承認する interactions: must_be_follower: フォロワー以外からの通知をブロック diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 9ab33619ad32c4..5b7c6f33e0e250 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -56,11 +56,15 @@ ko: scopes: 애플리케이션에 허용할 API들입니다. 최상위 스코프를 선택하면 개별적인 것은 선택하지 않아도 됩니다. setting_aggregate_reblogs: 최근에 부스트 됐던 게시물은 새로 부스트 되어도 보여주지 않기 (새로 받은 부스트에만 적용됩니다) setting_always_send_emails: 기본적으로 마스토돈을 활동적으로 사용하고 있을 때에는 이메일 알림이 보내지지 않습니다 + setting_boost_modal: 활성화하면 부스트하기 전에 부스트의 공개설정을 바꿀 수 있는 확인창이 먼저 뜨게 됩니다. + setting_default_quote_policy_private: 마스토돈에서 작성된 팔로워 전용 게시물은 다른 사용자가 인용할 수 없습니다. + setting_default_quote_policy_unlisted: 사람들에게 인용된 경우, 인용한 게시물도 유행 타임라인에서 감추게 됩니다. setting_default_sensitive: 민감한 미디어는 기본적으로 가려져 있으며 클릭해서 볼 수 있습니다 setting_display_media_default: 민감함으로 표시된 미디어 가리기 setting_display_media_hide_all: 모든 미디어를 가리기 setting_display_media_show_all: 모든 미디어를 보이기 setting_emoji_style: 에모지 표현 방식. "자동"은 시스템 기본 에모지를 적용하고 그렇지 못하는 오래된 브라우저의 경우 트웨모지를 사용합니다. + setting_quick_boosting_html: 활성화하면 %{boost_icon}부스트 아이콘을 클릭했을 때 부스트/인용 드롭다운 메뉴가 뜨지 않고 바로 부스트하게 됩니다. 인용은 %{options_icon} (옵션) 메뉴 안으로 이동합니다. setting_system_scrollbars_ui: 사파리와 크롬 기반의 데스크탑 브라우저만 적용됩니다 setting_use_blurhash: 그라디언트는 숨겨진 내용의 색상을 기반으로 하지만 상세 내용은 보이지 않게 합니다 setting_use_pending_items: 타임라인의 새 게시물을 자동으로 보여 주는 대신, 클릭해서 나타내도록 합니다 @@ -105,7 +109,6 @@ ko: thumbnail: 대략 2:1 비율의 이미지가 서버 정보 옆에 표시됩니다. trendable_by_default: 유행하는 콘텐츠에 대한 수동 승인을 건너뜁니다. 이 설정이 적용된 이후에도 각각의 항목들을 삭제할 수 있습니다. trends: 트렌드는 어떤 게시물, 해시태그 그리고 뉴스 기사가 이 서버에서 인기를 끌고 있는지 보여줍니다. - trends_as_landing_page: 로그아웃한 사용자와 방문자에게 서버 설명 대신 유행하는 내용을 보여줍니다. 유행 기능을 활성화해야 합니다. form_challenge: current_password: 당신은 보안 구역에 진입하고 있습니다 imports: @@ -230,10 +233,12 @@ ko: setting_aggregate_reblogs: 타임라인의 부스트를 그룹화 setting_always_send_emails: 항상 이메일 알림 보내기 setting_auto_play_gif: 애니메이션 GIF를 자동 재생 + setting_boost_modal: 부스트 공개범위 제어 setting_default_language: 게시물 언어 setting_default_privacy: 게시물 공개 범위 setting_default_quote_policy: 인용할 수 있는 사람 setting_default_sensitive: 미디어를 언제나 민감한 콘텐츠로 설정 + setting_delete_modal: 게시물을 삭제하기 전 경고하기 setting_disable_hover_cards: 호버시 프로필 미리보기를 비활성화 setting_disable_swiping: 스와이프 모션 비활성화 setting_display_media: 미디어 표시 @@ -243,6 +248,8 @@ ko: setting_emoji_style: 에모지 스타일 setting_expand_spoilers: 내용 경고로 표시된 게시물을 항상 펼치기 setting_hide_network: 내 인맥 숨기기 + setting_missing_alt_text_modal: 대체텍스트가 없는 미디어를 포함하여 게시하기 전 경고 + setting_quick_boosting: 빠른 부스트 활성화 setting_reduce_motion: 애니메이션 줄이기 setting_system_font_ui: 시스템의 기본 글꼴을 사용 setting_system_scrollbars_ui: 시스템 기본 스크롤바 사용 @@ -276,12 +283,17 @@ ko: content_cache_retention_period: 리모트 콘텐츠 보유 기간 custom_css: 사용자 정의 CSS favicon: 파비콘 + landing_page: 새 방문자를 위한 랜딩 페이지 + local_live_feed_access: 로컬 게시물에 대한 실시간 피드 접근 + local_topic_feed_access: 로컬 게시물에 대한 해시태그와 링크 피드 접근 mascot: 사용자 정의 마스코트 (legacy) media_cache_retention_period: 미디어 캐시 유지 기한 min_age: 최소 연령 제한 peers_api_enabled: API에 발견 된 서버들의 목록 발행 profile_directory: 프로필 책자 활성화 registrations_mode: 누가 가입할 수 있는지 + remote_live_feed_access: 리모트 게시물에 대한 실시간 피드 접근 + remote_topic_feed_access: 리모트 게시물에 대한 해시태그와 링크 피드 접근 require_invite_text: 가입 하는 이유를 필수로 입력하게 하기 show_domain_blocks: 도메인 차단 보여주기 show_domain_blocks_rationale: 왜 도메인이 차단되었는지 보여주기 @@ -296,7 +308,6 @@ ko: thumbnail: 서버 썸네일 trendable_by_default: 사전 리뷰 없이 트렌드에 오르는 것을 허용 trends: 유행 활성화 - trends_as_landing_page: 유행을 방문 페이지로 쓰기 interactions: must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단 must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단 diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index e96f97c1aaf1cf..9b626e3c13de65 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -95,7 +95,6 @@ lad: thumbnail: Una imaje de aproksimadamente 2:1 se amostra djunto a la enformasyon de tu sirvidor. trendable_by_default: Omite la revizyon manuala del kontenido en trend. Los elementos individuales ainda podran supremirse de los trendes. trends: Los trendes amostran ke mesajes, etiketas i haberes estan ganando traksyon en tu sirvidor. - trends_as_landing_page: Amostra kontenido en trend para utilizadores i vizitantes en lugar de una deskripsyon de este sirvidor. Rekiere ke los trendes esten kapasitados. form_challenge: current_password: Estas entrando en un area siguro imports: @@ -245,6 +244,7 @@ lad: favicon: Ikona de favoritos mascot: Maskota personalizada (legado) media_cache_retention_period: Periodo de retensyon de kashe multimedia + min_age: Edad minima rekerida peers_api_enabled: Publika lista de sirvidores diskuviertos en la API profile_directory: Kapasita katalogo de profiles registrations_mode: Ken puede enrejistrarse @@ -262,7 +262,6 @@ lad: thumbnail: Minyatura del sirvidor trendable_by_default: Permite trendes sin revizyon previa trends: Kapasita trendes - trends_as_landing_page: Kulanea trendes komo la pajina prinsipala interactions: must_be_follower: Bloka avizos de personas ke no te sigen must_be_following: Bloka avizos de personas a las kualas no siges @@ -314,6 +313,7 @@ lad: terms_of_service_generator: choice_of_law: Legislasyon aplikavle domain: Domeno + min_age: Edad minima user: date_of_birth_1i: Diya date_of_birth_2i: Mez @@ -326,6 +326,8 @@ lad: name: Nombre permissions_as_keys: Permisos position: Priorita + username_block: + allow_with_approval: Permite enrejistrasyones kon aprovasyon webhook: events: Evenimientos kapasitados template: Modelo de kontenido diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index f48b889992fa36..bd510a3ebd4444 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -92,7 +92,6 @@ lt: site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę. thumbnail: Maždaug 2:1 dydžio vaizdas, rodomas šalia tavo serverio informacijos. trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo. - trends_as_landing_page: Rodyti tendencingą turinį atsijungusiems naudotojams ir lankytojams vietoj šio serverio aprašymo. Reikia, kad tendencijos būtų įjungtos. imports: data: CSV failas, eksportuotas iš kito „Mastodon“ serverio. invite_request: @@ -208,7 +207,6 @@ lt: thumbnail: Serverio miniatūra trendable_by_default: Leisti tendencijas be išankstinės peržiūros trends: Įjungti tendencijas - trends_as_landing_page: Naudoti tendencijas kaip nukreipimo puslapį invite: comment: Komentuoti invite_request: diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 1c228ffab7179e..d43d89e0340801 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -104,7 +104,6 @@ lv: thumbnail: Aptuveni 2:1 attēls, kas tiek parādīts kopā ar tava servera informāciju. trendable_by_default: Izlaist aktuālā satura manuālu pārskatīšanu. Atsevišķas preces joprojām var noņemt no tendencēm pēc fakta. trends: Tendences parāda, kuras ziņas, atsauces un ziņu stāsti gūst panākumus tavā serverī. - trends_as_landing_page: Šī servera apraksta vietā rādīt aktuālo saturu lietotājiem un apmeklētājiem, kuri ir atteikušies. Nepieciešams iespējot tendences. form_challenge: current_password: Tu ieej drošā zonā imports: @@ -282,7 +281,6 @@ lv: thumbnail: Servera sīkbilde trendable_by_default: Atļaut tendences bez iepriekšējas pārskatīšanas trends: Iespējot tendences - trends_as_landing_page: Izmantojiet tendences kā galveno lapu interactions: must_be_follower: Bloķēt paziņojumus no ne-sekotājiem must_be_following: Bloķēt paziņojumus no cilvēkiem, kuriem tu neseko diff --git a/config/locales/simple_form.ms.yml b/config/locales/simple_form.ms.yml index 1395ff8388ba28..e4ed284c362bde 100644 --- a/config/locales/simple_form.ms.yml +++ b/config/locales/simple_form.ms.yml @@ -93,7 +93,6 @@ ms: thumbnail: Imej kira-kira 2:1 dipaparkan bersama maklumat server anda. trendable_by_default: Langkau semakan manual kandungan sohor kini. Item individu masih boleh dialih keluar daripada trend selepas fakta itu. trends: Aliran menunjukkan pos, hashtag dan cerita berita yang mendapat tarikan pada server anda. - trends_as_landing_page: Tunjukkan kandungan trend kepada pengguna dan pelawat yang log keluar dan bukannya penerangan tentang server ini. Memerlukan trend untuk didayakan. form_challenge: current_password: Anda sedang memasuki kawasan selamat imports: @@ -255,7 +254,6 @@ ms: thumbnail: Server thumbnail trendable_by_default: Benarkan aliran tanpa semakan terlebih dahulu trends: Dayakan trend - trends_as_landing_page: Gunakan trend sebagai halaman pendaratan interactions: must_be_follower: Sekat pemberitahuan daripada bukan pengikut must_be_following: Sekat pemberitahuan daripada orang yang anda tidak ikuti diff --git a/config/locales/simple_form.my.yml b/config/locales/simple_form.my.yml index a1f5d98998eb0c..6f45a51eba01a1 100644 --- a/config/locales/simple_form.my.yml +++ b/config/locales/simple_form.my.yml @@ -92,7 +92,6 @@ my: thumbnail: သင့်ဆာဗာအချက်အလက်နှင့်အတူ အကြမ်းဖျင်းအားဖြင့် ၂:၁ ဖြင့် ပြသထားသောပုံတစ်ပုံ။ trendable_by_default: ခေတ်စားနေသော အကြောင်းအရာများ၏ ကိုယ်တိုင်သုံးသပ်ချက်ကို ကျော်ပါ။ နောက်ပိုင်းတွင် အချက်အလက်တစ်ခုချင်းစီကို ခေတ်စားနေသောအကြောင်းအရာများကဏ္ဍမှ ဖယ်ရှားနိုင်ပါသေးသည်။ trends: လက်ရှိခေတ်စားနေသာပို့စ်များ၊ hashtag များနှင့် သတင်းဇာတ်လမ်းများကို သင့်ဆာဗာပေါ်တွင် တွေ့မြင်နိုင်ပါမည်။ - trends_as_landing_page: ဤဆာဗာဖော်ပြချက်အစား အကောင့်မှ ထွက်ထားသူများနှင့် ဝင်ရောက်ကြည့်ရှုသူများအတွက် ခေတ်စားနေသော အကြောင်းအရာများကို ပြသပါ။ ခေတ်စားနေသောပို့စ်များကို ဖွင့်ထားရန် လိုအပ်သည်။ form_challenge: current_password: သင်သည် လုံခြုံသောနေရာသို့ ဝင်ရောက်နေပါသည် imports: @@ -251,7 +250,6 @@ my: thumbnail: ဆာဗာ ပုံသေး trendable_by_default: ကြိုမသုံးသပ်ဘဲ ခေတ်စားနေသောအကြောင်းအရာများကို ခွင့်ပြုပါ trends: လက်ရှိခေတ်စားနေမှုများကိုပြပါ - trends_as_landing_page: ခေတ်စားနေသောပို့စ်များကို landing စာမျက်နှာအဖြစ် အသုံးပြုပါ interactions: must_be_follower: စောင့်ကြည့်မနေသူများထံမှ အသိပေးချက်များကို ပိတ်ပါ must_be_following: သင် စောင့်ကြည့်မထားသူများထံမှ အသိပေးချက်များကို ပိတ်ပါ diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 5603213f868438..36522a669544b9 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -93,6 +93,7 @@ nl: content_cache_retention_period: Alle berichten van andere servers (inclusief boosts en reacties) worden verwijderd na het opgegeven aantal dagen, ongeacht enige lokale gebruikersinteractie met die berichten. Dit betreft ook berichten die een lokale gebruiker aan diens bladwijzers heeft toegevoegd of als favoriet heeft gemarkeerd. Privéberichten tussen gebruikers van verschillende servers gaan ook verloren en zijn onmogelijk te herstellen. Het gebruik van deze instelling is bedoeld voor servers die een speciaal doel dienen en overtreedt veel gebruikersverwachtingen wanneer deze voor algemeen gebruik wordt geïmplementeerd. custom_css: Je kunt aangepaste CSS toepassen op de webversie van deze Mastodon-server. favicon: WEBP, PNG, GIF of JPG. Vervangt de standaard Mastodon favicon met een aangepast pictogram. + landing_page: Selecteert welke pagina nieuwe bezoekers te zien krijgen wanneer ze voor het eerst op jouw server terechtkomen. Wanneer je ‘Trends’ selecteert, moeten trends ingeschakeld zijn onder 'Serverinstellingen > Ontdekken'. Als je ‘Lokale tijdlijn’ selecteert, moet ‘Toegang tot openbare lokale berichten’ worden ingesteld op ‘Iedereen’ onder 'Serverinstellingen > Ontdekken'. mascot: Overschrijft de illustratie in de geavanceerde webomgeving. media_cache_retention_period: Mediabestanden van berichten van externe gebruikers worden op jouw server in de cache opgeslagen. Indien ingesteld op een positieve waarde, worden media verwijderd na het opgegeven aantal dagen. Als de mediagegevens worden opgevraagd nadat ze zijn verwijderd, worden ze opnieuw gedownload wanneer de originele inhoud nog steeds beschikbaar is. Vanwege beperkingen op hoe vaak linkvoorbeelden sites van derden raadplegen, wordt aanbevolen om deze waarde in te stellen op ten minste 14 dagen. Anders worden linkvoorbeelden niet op aanvraag bijgewerkt. min_age: Gebruikers krijgen tijdens hun inschrijving de vraag om hun geboortedatum te bevestigen @@ -110,7 +111,6 @@ nl: thumbnail: Een afbeelding van ongeveer een verhouding van 2:1 die naast jouw serverinformatie wordt getoond. trendable_by_default: Handmatige beoordeling van trends overslaan. Individuele items kunnen later alsnog worden afgekeurd. trends: Trends laten zien welke berichten, hashtags en nieuwsberichten op jouw server aan populariteit winnen. - trends_as_landing_page: Toon trending inhoud aan uitgelogde gebruikers en bezoekers in plaats van een beschrijving van deze server. Vereist dat trends zijn ingeschakeld. form_challenge: current_password: Je betreedt een veilige omgeving imports: @@ -287,6 +287,7 @@ nl: content_cache_retention_period: Bewaartermijn voor externe inhoud custom_css: Aangepaste CSS favicon: Favicon + landing_page: Landingspagina voor nieuwe bezoekers local_live_feed_access: Toegang tot openbare lokale berichten local_topic_feed_access: Toegang tot overzicht met lokale hashtags en links mascot: Aangepaste mascotte (legacy) @@ -311,7 +312,6 @@ nl: thumbnail: Server-miniatuur trendable_by_default: Trends goedkeuren zonder voorafgaande beoordeling trends: Trends inschakelen - trends_as_landing_page: Laat trends op de startpagina zien interactions: must_be_follower: Meldingen van mensen die jou niet volgen blokkeren must_be_following: Meldingen van mensen die jij niet volgt blokkeren diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index a536fdf99b4fb5..f6916c35475f63 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -110,7 +110,6 @@ nn: thumbnail: Eit omlag 2:1 bilete vist saman med informasjon om tenaren. trendable_by_default: Hopp over manuell gjennomgang av populært innhald. Enkeltståande innlegg kan fjernast frå trendar i etterkant. trends: Trendar viser kva for nokre innlegg, emneknaggar og nyheiter som er populære på tenaren. - trends_as_landing_page: Vis populært innhald til utlogga brukarar og folk som kjem innom sida i staden for ei skildring av tenaren. Du må ha skrudd på trendar for å kunna bruka dette. form_challenge: current_password: Du går inn i eit trygt område imports: @@ -311,7 +310,6 @@ nn: thumbnail: Miniatyrbilete for tenaren trendable_by_default: Tillat trendar utan gjennomgang på førehand trends: Aktiver trendar - trends_as_landing_page: Bruk trendar som startside interactions: must_be_follower: Blokker varsel frå folk som ikkje fylgjer deg must_be_following: Blokker varsel frå folk du ikkje fylgjer diff --git a/config/locales/simple_form.no.yml b/config/locales/simple_form.no.yml index d209887e6c2c86..b1afd7e8010939 100644 --- a/config/locales/simple_form.no.yml +++ b/config/locales/simple_form.no.yml @@ -94,7 +94,6 @@ thumbnail: Et omtrent 2:1 bilde vist sammen med serverinformasjonen din. trendable_by_default: Hopp over manuell gjennomgang av populære innhold. Individuelle elementer kan fjernes fra populært etter faktaen. trends: Trender viser hvilke innlegg, emneknagger og nyheter som får trekkraft på serveren din. - trends_as_landing_page: Vis populære innhold til innloggede brukere og besøkende i stedet for en beskrivelse av tjeneren. Krever populært for å bli aktivert. form_challenge: current_password: Du går inn i et sikkert område imports: @@ -254,7 +253,6 @@ thumbnail: Miniatyrbilde til server trendable_by_default: Tillat trender uten foregående vurdering trends: Aktiver trender - trends_as_landing_page: Bruk trender som landingsside interactions: must_be_follower: Blokker varslinger fra ikke-følgere must_be_following: Blokker varslinger fra personer du ikke følger diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index d0584d577aaa8f..5cc3c85ce2df92 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -54,13 +54,18 @@ pl: password: Użyj co najmniej 8 znaków phrase: Zostanie wykryte nawet, gdy znajduje się za ostrzeżeniem o zawartości scopes: Wybór API, do których aplikacja będzie miała dostęp. Jeżeli wybierzesz nadrzędny zakres, nie musisz wybierać jego elementów. + setting_advanced_layout: Wyświetlaj Mastodona w układzie wielokolumnowym, umożliwiającym przeglądanie osi czasu, powiadomień oraz trzeciej kolumny według własnego wyboru. Niezalecane w przypadku mniejszych ekranów. setting_aggregate_reblogs: Nie pokazuj nowych podbić dla wpisów, które zostały niedawno podbite (dotyczy tylko nowo otrzymanych podbić) setting_always_send_emails: Powiadomienia e-mail zwykle nie będą wysyłane, gdy używasz Mastodon + setting_boost_modal: Po włączeniu tej funkcji najpierw otworzy się okno potwierdzenia podbicia, w którym można zmienić jego widoczność. + setting_default_quote_policy_private: Wpisy publikowane na Mastodonie wyłącznie dla obserwujących nie mogą być cytowane przez inne osoby. + setting_default_quote_policy_unlisted: Kiedy ktoś cytuje twoje wpisy, będą one również ukryte na popularnych osiach czasu. setting_default_sensitive: Wrażliwe multimedia są domyślnie schowane i mogą być odkryte kliknięciem setting_display_media_default: Ukrywaj zawartość multimedialną oznaczoną jako wrażliwa setting_display_media_hide_all: Zawsze ukrywaj zawartość multimedialną setting_display_media_show_all: Zawsze pokazuj zawartość multimedialną setting_emoji_style: Jak wyświetlić emotikony. "Auto" spróbuje użyć natywnych emoji, ale wróci do Twemoji dla starszych przeglądarek. + setting_quick_boosting_html: Po włączeniu tej opcji kliknięcie ikonki %{boost_icon} spowoduje natychmiastowe podbicie zamiast otwarcia menu rozwijanego z opcją podbicia lub cytatu. Przenosi to akcję cytowania do menu %{options_icon} (Opcje). setting_system_scrollbars_ui: Stosuje się tylko do przeglądarek komputerowych opartych na Safari i Chrome setting_use_blurhash: Gradienty są oparte na kolorach ukrywanej zawartości, ale uniewidaczniają wszystkie szczegóły setting_use_pending_items: Ukryj aktualizacje osi czasu za kliknięciem, zamiast automatycznego przewijania strumienia @@ -105,7 +110,6 @@ pl: thumbnail: Obraz o proporcjach mniej więcej 2:1 wyświetlany obok informacji o serwerze. trendable_by_default: Pomiń ręczny przegląd treści trendów. Pojedyncze elementy nadal mogą być usuwane z trendów po fakcie. trends: Tendencje pokazują, które posty, hasztagi i newsy zyskują popularność na Twoim serwerze. - trends_as_landing_page: Pokaż najpopularniejsze treści niezalogowanym użytkownikom i odwiedzającym zamiast opisu tego serwera. Wymaga włączenia trendów. form_challenge: current_password: Wchodzisz w strefę bezpieczną imports: @@ -234,9 +238,12 @@ pl: setting_aggregate_reblogs: Grupuj podbicia na osiach czasu setting_always_send_emails: Zawsze wysyłaj powiadomienia e-mail setting_auto_play_gif: Automatycznie odtwarzaj animowane GIFy + setting_boost_modal: Kontroluj widoczność podbić setting_default_language: Język wpisów + setting_default_privacy: Widoczność wpisów setting_default_quote_policy: Kto może cytować setting_default_sensitive: Zawsze oznaczaj zawartość multimedialną jako wrażliwą + setting_delete_modal: Ostrzegaj mnie przed usunięciem wpisu setting_disable_hover_cards: Wyłącz podgląd profilu po najechaniu setting_disable_swiping: Wyłącz ruchy przesuwania setting_display_media: Wyświetlanie zawartości multimedialnej @@ -246,6 +253,8 @@ pl: setting_emoji_style: Styl emoji setting_expand_spoilers: Zawsze rozwijaj wpisy oznaczone ostrzeżeniem o zawartości setting_hide_network: Ukryj swoją sieć + setting_missing_alt_text_modal: Ostrzegaj mnie przed publikowaniem multimediów bez tekstu alternatywnego + setting_quick_boosting: Włącz szybkie podbijanie setting_reduce_motion: Ogranicz ruch w animacjach setting_system_font_ui: Używaj domyślnej czcionki systemu setting_system_scrollbars_ui: Używaj domyślnego paska przewijania systemu @@ -279,12 +288,16 @@ pl: content_cache_retention_period: Okres zachowywania zdalnych treści custom_css: Niestandardowy CSS favicon: Favicon + local_live_feed_access: Uzyskaj dostęp do kanałów zawierających lokalne wpisy + local_topic_feed_access: Uzyskaj dostęp do hashtagów i linków zawierających lokalne wpisy mascot: Własna ikona media_cache_retention_period: Okres przechowywania pamięci podręcznej min_age: Wymagany minimalny wiek peers_api_enabled: Opublikuj listę odkrytych serwerów w API profile_directory: Włącz katalog profilów registrations_mode: Kto może się zarejestrować + remote_live_feed_access: Uzyskaj dostęp do kanałów zawierających zdalne wpisy + remote_topic_feed_access: Uzyskaj dostęp do hashtagów i linków zawierających zdalne wpisy require_invite_text: Wymagaj powodu, aby dołączyć show_domain_blocks: Pokazuj zablokowane domeny show_domain_blocks_rationale: Pokaż dlaczego domeny zostały zablokowane @@ -299,7 +312,6 @@ pl: thumbnail: Miniaturka serwera trendable_by_default: Zezwalaj na trendy bez wcześniejszego przeglądu trends: Włącz trendy - trends_as_landing_page: Użyj trendów jako strony początkowej interactions: must_be_follower: Nie wyświetlaj powiadomień od osób, które Cię nie obserwują must_be_following: Nie wyświetlaj powiadomień od osób, których nie obserwujesz diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 90a0afcb02322a..605bcf8d98ca46 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -44,7 +44,7 @@ pt-BR: bot: Sinaliza aos outros de que essa conta executa principalmente ações automatizadas e pode não ser monitorada context: Um ou mais contextos onde o filtro deve atuar current_password: Para fins de segurança, digite a senha da conta atual - current_username: Para confirmar, digite o nome de usuário da conta atual + current_username: Para confirmar, entre com nome de usuário da conta atual digest: Enviado apenas após um longo período de inatividade com um resumo das menções recebidas durante ausência email: Você receberá um e-mail de confirmação header: WEBP, PNG, GIF ou JPG. No máximo %{size}. Será reduzido para %{dimensions}px @@ -56,6 +56,8 @@ pt-BR: scopes: Quais APIs o aplicativo vai ter permissão de acessar. Se você selecionar uma autorização de alto nível, você não precisa selecionar individualmente os outros. setting_aggregate_reblogs: Não mostrar novos impulsos para publicações que já foram impulsionadas recentemente (afeta somente os impulsos mais recentes) setting_always_send_emails: Normalmente, as notificações por e-mail não serão enviadas enquanto você estiver usando ativamente o Mastodon + setting_default_quote_policy_private: Publicações exclusivas de seguidores criadas no Mastodon não podem ser citadas por outras pessoas. + setting_default_quote_policy_unlisted: Quando as pessoas citarem você, suas publicações também ficarão ocultas da linha do tempo. setting_default_sensitive: Mídia sensível está oculta por padrão e pode ser revelada com um clique setting_display_media_default: Sempre ocultar mídia sensível setting_display_media_hide_all: Sempre ocultar todas as mídias @@ -105,7 +107,6 @@ pt-BR: thumbnail: Uma imagem de aproximadamente 2:1 exibida ao lado da informação de sua instância. trendable_by_default: Pular a revisão manual do conteúdo em tendência. Itens individuais ainda poderão ser removidos das tendências após a sua exibição. trends: Tendências mostram quais publicações, hashtags e notícias estão ganhando destaque na sua instância. - trends_as_landing_page: Mostrar conteúdo de tendências para usuários deslogados e visitantes em vez de uma descrição deste servidor. Requer que as tendências sejam ativadas. form_challenge: current_password: Você está entrando em uma área segura imports: @@ -236,6 +237,7 @@ pt-BR: setting_default_privacy: Visibilidade da publicação setting_default_quote_policy: Quem pode citar setting_default_sensitive: Sempre marcar mídia como sensível + setting_delete_modal: Avise-me antes de apagar uma publicação setting_disable_hover_cards: Desativar visualização de perfil ao passar o mouse por cima setting_disable_swiping: Desabilitar movimentos deslizantes setting_display_media: Exibição das mídias @@ -245,6 +247,7 @@ pt-BR: setting_emoji_style: Estilo de emoji setting_expand_spoilers: Sempre expandir toots com Aviso de Conteúdo setting_hide_network: Ocultar suas relações + setting_missing_alt_text_modal: Avise-me antes de publicar mídia sem texto alternado setting_reduce_motion: Reduzir animações setting_system_font_ui: Usar fonte padrão do sistema setting_system_scrollbars_ui: Usar barra de rolagem padrão do sistema @@ -278,12 +281,17 @@ pt-BR: content_cache_retention_period: Período de retenção de conteúdo remoto custom_css: CSS personalizável favicon: Favicon + landing_page: Página inicial para novos visitantes + local_live_feed_access: Acessar feeds ao vivo com destaque em publicações locais + local_topic_feed_access: Acessar hasthtag e endereços de feed com destaque em publicações locais mascot: Mascote personalizado (legado) media_cache_retention_period: Período de retenção do cachê de mídia min_age: Requisito de idade mínimia peers_api_enabled: Publicar lista de instâncias de servidor descobertas na API profile_directory: Ativar diretório de perfis registrations_mode: Quem pode se inscrever + remote_live_feed_access: Acessar feeds ao vivo com destaque em publicações antigas + remote_topic_feed_access: Acessar hasthtag e endereços de feed com destaque em publicações antigas require_invite_text: Exigir uma razão para entrar show_domain_blocks: Mostrar domínios bloqueados show_domain_blocks_rationale: Mostrar por que domínios foram bloqueados @@ -298,7 +306,6 @@ pt-BR: thumbnail: Miniatura do servidor trendable_by_default: Permitir tendências sem revisão prévia trends: Habilitar tendências - trends_as_landing_page: Usar tendências como página inicial interactions: must_be_follower: Bloquear notificações de não-seguidores must_be_following: Bloquear notificações de não-seguidos diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index 95b1b24f876d2b..e8aa2692eca754 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -54,8 +54,10 @@ pt-PT: password: Use pelo menos 8 caracteres phrase: Será correspondido independentemente da capitalização ou do aviso de conteúdo duma publicação scopes: Quais as API a que a aplicação terá permissão para aceder. Se selecionar um âmbito de nível superior, não precisa de selecionar âmbitos individuais. - setting_aggregate_reblogs: Não mostrar os novos impulsos para publicações que tenham sido recentemente impulsionadas (apenas afeta os impulsos recentemente recebidos) + setting_advanced_layout: Exibe o Mastodon num layout com várias colunas, permitindo-lhe visualizar a cronologia, as notificações e uma terceira coluna à sua escolha. Não recomendado para ecrãs mais pequenos. + setting_aggregate_reblogs: Não mostrar as novas partilhas para publicações que tenham sido partilhadas recentemente (apenas afeta as partilhas recebidas recentemente) setting_always_send_emails: Normalmente as notificações por e-mail não serão enviadas quando estiver a utilizar ativamente o Mastodon + setting_boost_modal: Quando ativado, ao partilhar abrirá primeiro uma caixa de diálogo de confirmação onde poderá alterar a visibilidade da sua partilha. setting_default_quote_policy_private: As publicações exclusivas para seguidores criadas no Mastodon não podem ser citadas por outras pessoas. setting_default_quote_policy_unlisted: Quando as pessoas o citarem, as respetivas publicações também serão ocultadas dos destaques. setting_default_sensitive: Os multimédia sensíveis são ocultados por predefinição e podem ser revelados com um clique/toque @@ -63,6 +65,7 @@ pt-PT: setting_display_media_hide_all: Esconder sempre toda a multimédia setting_display_media_show_all: Mostrar sempre a multimédia setting_emoji_style: Como apresentar emojis. "Auto" tenta usar emojis nativos, mas reverte para Twemoji em navegadores mais antigos. + setting_quick_boosting_html: Quando ativado, clicar no ícone %{boost_icon} Partilhar irá de imediato partilhar ao invés de abrir o menu de Partilhar/Citar. Relocaliza a ação Citar para o menu %{options_icon} (Opções). setting_system_scrollbars_ui: Aplica-se apenas a navegadores de desktop baseados no Safari e Chrome setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer pormenores setting_use_pending_items: Ocultar as atualizações da cronologia após um clique em vez de percorrer automaticamente a cronologia @@ -87,11 +90,12 @@ pt-PT: backups_retention_period: Os utilizadores têm a possibilidade de gerar arquivos das suas publicações para descarregar mais tarde. Quando definido para um valor positivo, estes arquivos serão automaticamente eliminados do seu armazenamento após o número de dias especificado. bootstrap_timeline_accounts: Estas contas serão destacadas no topo das recomendações aos novos utilizadores. closed_registrations_message: Apresentado quando as inscrições estiverem encerradas - content_cache_retention_period: Todas as mensagens de outros servidores (incluindo impulsos e respostas) serão eliminadas após o número de dias especificado, independentemente de qualquer interação do utilizador local com essas mensagens. Isto inclui mensagens em que um utilizador local as tenha marcado ou adicionado aos favoritos. As menções privadas entre utilizadores de instâncias diferentes também se perderão e serão impossíveis de restaurar. A utilização desta definição destina-se a instâncias para fins especiais e quebra muitas expectativas dos utilizadores quando implementada para utilização geral. + content_cache_retention_period: Todas as publicações de outros servidores (incluindo partilhas e respostas) serão eliminadas após o número de dias especificado, independentemente de qualquer interação do utilizador local com essas publicações. Isto inclui mensagens em que um utilizador local as tenha salvo ou adicionado aos favoritos. As menções privadas entre utilizadores de instâncias diferentes também se perderão e serão impossíveis de recuperar. A utilização desta definição destina-se a instâncias para fins especiais e quebra muitas expectativas dos utilizadores quando implementada para utilização geral. custom_css: Pode aplicar estilos personalizados na versão web do Mastodon. favicon: WEBP, PNG, GIF ou JPG. Substitui o ícone de favorito padrão do Mastodon por um ícone personalizado. + landing_page: Seleciona a página que os novos visitantes veem quando chegam ao seu servidor pela primeira vez. Se selecionar «Tendências», então as tendências precisam estar ativas nas Definições de Descoberta. Se selecionar «Cronologia local», então «Acesso a cronologias com publicações locais em destaque» precisa de estar definido como «Todos» nas Definições de Descoberta. mascot: Sobrepõe-se à ilustração na interface web avançada. - media_cache_retention_period: Os ficheiros multimédia de publicações feitas por utilizadores remotos são armazenados em cache no seu servidor. Quando definido para um valor positivo, os ficheiros multimédia serão eliminados após o número de dias especificado. Se os ficheiros multimédia forem solicitados depois de terem sido eliminados, serão transferidos novamente, se o conteúdo de origem ainda estiver disponível. Devido a restrições sobre a frequência com que os cartões de pré-visualização de links pesquisam sites de terceiros, recomenda-se que este valor seja definido para, pelo menos, 14 dias, ou os cartões de pré-visualização de links não serão atualizados a pedido antes desse período. + media_cache_retention_period: Os ficheiros multimédia de publicações feitas por utilizadores remotos são armazenados em cache no seu servidor. Quando definido para um valor positivo, os ficheiros multimédia serão eliminados após o número de dias especificado. Se os ficheiros multimédia forem solicitados depois de terem sido eliminados, serão transferidos novamente, se o conteúdo de origem ainda estiver disponível. Devido a restrições sobre a frequência com que os cartões de pré-visualização de hiperligação pesquisam sites de terceiros, recomenda-se que este valor seja definido para, pelo menos, 14 dias, ou os cartões de pré-visualização de hiperligação não serão atualizados a pedido antes desse período. min_age: Os utilizadores serão convidados a confirmar a sua data de nascimento durante o processo de inscrição peers_api_enabled: Uma lista de nomes de domínio que este servidor encontrou no fediverso. Nenhum dado é incluído aqui sobre se você federa com um determinado servidor, apenas que o seu servidor o conhece. Este serviço é utilizado por serviços que recolhem estatísticas na federação, em termos gerais. profile_directory: O diretório de perfis lista todos os utilizadores que optaram por ter a sua conta a ser sugerida a outros. @@ -107,7 +111,6 @@ pt-PT: thumbnail: Uma imagem de cerca de 2:1, apresentada ao lado da informação do seu servidor. trendable_by_default: Ignorar a revisão manual do conteúdo em destaque. Os itens individuais poderão ainda assim ser posteriormente removidos das tendências. trends: As tendências mostram quais as publicações, etiquetas e notícias que estão a ganhar destaque no seu servidor. - trends_as_landing_page: Mostrar conteúdo em destaque a utilizadores sem sessão iniciada e visitantes, ao invés de uma descrição deste servidor. Requer que os destaques estejam ativados. form_challenge: current_password: Está a entrar numa área segura imports: @@ -231,13 +234,15 @@ pt-PT: password: Palavra-passe phrase: Palavra-chave ou frase setting_advanced_layout: Ativar interface web avançada - setting_aggregate_reblogs: Agrupar impulsos em linhas de tempo + setting_aggregate_reblogs: Agrupar partilhas nas cronologias setting_always_send_emails: Enviar sempre notificações por e-mail setting_auto_play_gif: Reproduzir GIF automaticamente + setting_boost_modal: Controlar visibilidade da partilha setting_default_language: Idioma de publicação setting_default_privacy: Visibilidade da publicação setting_default_quote_policy: Quem pode citar setting_default_sensitive: Marcar sempre os multimédia como sensíveis + setting_delete_modal: Avisar-me antes de eliminar uma publicação setting_disable_hover_cards: Desativar visualização de perfil ao passar o cursor setting_disable_swiping: Desativar os movimentos de deslize setting_display_media: Visualização de multimédia @@ -247,6 +252,8 @@ pt-PT: setting_emoji_style: Estilo de emojis setting_expand_spoilers: Expandir sempre as publicações marcadas com avisos de conteúdo setting_hide_network: Esconder a tua rede + setting_missing_alt_text_modal: Avisar-me antes de publicar media sem texto alternativo + setting_quick_boosting: Ativar partilha rápida setting_reduce_motion: Reduzir movimento em animações setting_system_font_ui: Usar o tipo de letra padrão do sistema setting_system_scrollbars_ui: Utilizar a barra de deslocação predefinida do sistema @@ -280,12 +287,17 @@ pt-PT: content_cache_retention_period: Período de retenção de conteúdos remotos custom_css: CSS personalizado favicon: Ícone de favoritos + landing_page: Página inicial para novos visitantes + local_live_feed_access: Acesso a cronologias com publicações locais em destaque + local_topic_feed_access: Acesso a cronologias de etiquetas e hiperligações de publicações locais em destaque mascot: Mascote personalizada (legado) media_cache_retention_period: Período de retenção de ficheiros multimédia em cache min_age: Idade mínima requerida peers_api_enabled: Publicar lista de servidores descobertos na API profile_directory: Ativar o diretório de perfis registrations_mode: Quem se pode inscrever + remote_live_feed_access: Acesso a cronologias com publicações remotas em destaque + remote_topic_feed_access: Acesso a cronologias de etiquetas e hiperligações de publicações remotas em destaque require_invite_text: Requerer uma razão para entrar show_domain_blocks: Mostrar domínios bloqueados show_domain_blocks_rationale: Mostrar porque os domínios foram bloqueados @@ -300,7 +312,6 @@ pt-PT: thumbnail: Miniatura do servidor trendable_by_default: Permitir tendências sem revisão prévia trends: Ativar destaques - trends_as_landing_page: Usar destaques como página de apresentação interactions: must_be_follower: Bloquear notificações de não-seguidores must_be_following: Bloquear notificações de pessoas que não segues @@ -326,7 +337,7 @@ pt-PT: mention: Alguém o mencionou pending_account: Uma nova conta aguarda aprovação quote: Alguém o citou - reblog: Alguém impulsionou uma publicação sua + reblog: Alguém partilhou uma publicação sua report: Uma nova denúncia foi submetida software_updates: all: Notificar todas as atualizações diff --git a/config/locales/simple_form.ro.yml b/config/locales/simple_form.ro.yml index f242a90181b58d..fe86121f242008 100644 --- a/config/locales/simple_form.ro.yml +++ b/config/locales/simple_form.ro.yml @@ -100,7 +100,6 @@ ro: thumbnail: O imagine de aproximativ 2:1 afișată alături de informațiile serverului dvs. trendable_by_default: Omiteți revizuirea manuală a conținutului în tendințe. Elementele individuale pot fi în continuare eliminate din tendințe după fapt. trends: Tendințele arată ce postări, hashtag-uri și știri câștigă teren pe serverul dvs. - trends_as_landing_page: Afișați conținut în tendințe utilizatorilor deconectați și vizitatorilor în loc de o descriere a acestui server. Necesită ca tendințele să fie activate. form_challenge: current_password: Ați intrat într-o zonă securizată imports: diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index bb00e86f828923..5798dd25bcf20e 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -104,7 +104,6 @@ ru: thumbnail: Изображение примерно 2:1, отображаемое рядом с информацией о вашем сервере. trendable_by_default: Пропустить ручной просмотр трендового контента. Отдельные элементы могут быть удалены из трендов уже постфактум. trends: Тренды показывают, какие посты, хэштеги и новостные истории набирают обороты на вашем сервере. - trends_as_landing_page: Показывать популярный контент для выходов пользователей и посетителей, а не для описания этого сервера. Требует включения тенденций. form_challenge: current_password: Вы переходите к настройкам безопасности вашей учётной записи imports: @@ -179,18 +178,18 @@ ru: acct: Адрес новой учётной записи account_warning_preset: text: Текст шаблона - title: Заголовок + title: Название admin_account_action: - include_statuses: Включать в письмо жалобы на посты - send_email_notification: Уведомить пользователя по электронной почте + include_statuses: Сообщить пользователю о том, на какие из его постов пожаловались + send_email_notification: Отправить пользователю уведомление по электронной почте text: Текст предупреждения type: Действие types: - disable: Заморозить - none: Ничего не делать + disable: Отключить + none: Вынести предупреждение sensitive: Отметить как «деликатного характера» - silence: Скрыть - suspend: Заблокировать и безвозвратно удалить все данные учётной записи + silence: Ограничить + suspend: Заблокировать warning_preset_id: Использовать шаблон предупреждения announcement: all_day: Весь день @@ -294,7 +293,6 @@ ru: thumbnail: Изображение сервера trendable_by_default: Разрешить треды без предварительной проверки trends: Включить тренды - trends_as_landing_page: Использовать тенденции в качестве целевой страницы interactions: must_be_follower: Блокировать уведомления от людей, которые не подписаны на вас must_be_following: Блокировать уведомления от людей, на которых вы не подписаны diff --git a/config/locales/simple_form.si.yml b/config/locales/simple_form.si.yml index 54e2a0012ea9d8..28d434225ea7f6 100644 --- a/config/locales/simple_form.si.yml +++ b/config/locales/simple_form.si.yml @@ -104,7 +104,6 @@ si: thumbnail: ඔබගේ සේවාදායක තොරතුරු සමඟ ආසන්න වශයෙන් 2:1 රූපයක් දර්ශනය වේ. trendable_by_default: ප්‍රවණතා අන්තර්ගතයන් අතින් සමාලෝචනය කිරීම මඟ හරින්න. කාරණයෙන් පසුවත් තනි අයිතම ප්‍රවණතා වලින් ඉවත් කළ හැකිය. trends: ප්‍රවණතා මඟින් ඔබේ සේවාදායකයේ ආකර්ෂණය ලබා ගන්නා පළ කිරීම්, හැෂ් ටැග් සහ ප්‍රවෘත්ති කථා පෙන්වයි. - trends_as_landing_page: මෙම සේවාදායකයේ විස්තරයක් වෙනුවට පිටව ගිය පරිශීලකයින්ට සහ අමුත්තන්ට ප්‍රවණතා අන්තර්ගතය පෙන්වන්න. ප්‍රවණතා සක්‍රීය කිරීම අවශ්‍ය වේ. form_challenge: current_password: ඔබ ආරක්ෂිත ප්‍රදේශයකට ඇතුල් වේ imports: @@ -288,7 +287,6 @@ si: thumbnail: සේවාදායක සිඟිති රුව trendable_by_default: පූර්ව සමාලෝචනයකින් තොරව ප්‍රවණතා වලට ඉඩ දෙන්න. trends: ප්‍රවණතා සක්‍රීය කරන්න - trends_as_landing_page: ගොඩබෑමේ පිටුව ලෙස ප්‍රවණතා භාවිතා කරන්න interactions: must_be_follower: අනුගාමිකයින් නොවන අයගෙන් ලැබෙන දැනුම්දීම් අවහිර කරන්න must_be_following: ඔබ අනුගමනය නොකරන පුද්ගලයින්ගෙන් ලැබෙන දැනුම්දීම් අවහිර කරන්න diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 81b46b5f307cd2..97196e9a00a34b 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -103,7 +103,6 @@ sl: thumbnail: Slika v razmerju stranic približno 2:1, prikazana vzdolž podatkov o vašem strežniku. trendable_by_default: Preskočite ročni pregled vsebine v trendu. Posamezne elemente še vedno lahko odstranite iz trenda post festum. trends: Trendi prikažejo, katere objave, ključniki in novice privlačijo zanimanje na vašem strežniku. - trends_as_landing_page: Odjavljenim uporabnikom in obiskovalcem namesto opisa tega strežnika pokažite vsebine v trendu. Trendi morajo biti omogočeni. form_challenge: current_password: Vstopate v varovano območje imports: @@ -284,7 +283,6 @@ sl: thumbnail: Sličica strežnika trendable_by_default: Dovoli trende brez predhodnega pregleda trends: Omogoči trende - trends_as_landing_page: Uporabi trende za pristopno stran interactions: must_be_follower: Blokiraj obvestila nesledilcev must_be_following: Blokiraj obvestila oseb, ki jim ne sledite diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index 9c93c4a47aab9a..9df506083b92d6 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -92,6 +92,7 @@ sq: content_cache_retention_period: Krejt postimet prej shërbyesve të tjerë (përfshi përforcime dhe përgjigje) do të fshihen pas numrit të caktuar të ditëve, pa marrë parasysh çfarëdo ndërveprimi përdoruesi me këto postime. Kjo përfshin postime kur një përdorues vendor u ka vënë shenjë si faqerojtës, ose të parapëlqyer. Do të humbin gjithashtu dhe përmendje private mes përdoruesish nga instanca të ndryshme dhe s’do të jetë e mundshme të rikthehen. Përdorimi i këtij rregullimi është menduar për instanca me qëllim të caktuar dhe ndërhyn në çka presin mjaft përdorues, kur sendërtohet për përdorim të përgjithshëm. custom_css: Stile vetjakë mund të aplikoni në versionin web të Mastodon-it. favicon: WEBP, PNG, GIF, ose JPG. Anashkalon favikonën parazgjedhje Mastodon me një ikonë vetjake. + landing_page: Përzgjedh cilën faqe shohin vizitorët e rinj, kur vijnë për herë të parë në shërbyesin tuaj. Nëse përzgjidhni “Në modë”, atëherë “në modë” duhet aktivizuar te Rregullime për Zbulime. Nëse përzgjidhni “Prurje vendore”, atëherë “Hyrje te prurje vendore që përmbajnë postime vendore” duhet vënë si “Gjithkush”, te Rregullime për Zbulime. mascot: Anashkalon ilustrimin te ndërfaqja web e thelluar. media_cache_retention_period: Kartela media nga postime të bëra nga përdorues të largët ruhen në një fshehtinë në shërbyesin tuaj. Kur i jepet një vlerë pozitive, media do të fshihet pas numrit të dhënë të ditëve. Nëse të dhënat e medias duhen pas fshirjes, do të rishkarkohen, nëse lënda burim mund të kihet ende. Për shkak kufizimesh mbi sa shpesh skeda paraparjesh lidhjesh ndërveprojnë me sajte palësh të treta, rekomandohet të vihet kjo vlerë të paktën 14 ditë, ose skedat e paraparjes së lidhje s’do të përditësohen duke e kërkuar para asaj kohe. min_age: Përdoruesve do t’ju kërkohet gjatë regjistrimit të ripohojnë datën e lindjes @@ -109,7 +110,6 @@ sq: thumbnail: Një figurë afërsisht 2:1 e shfaqur tok me hollësi mbi shërbyesin tuaj. trendable_by_default: Anashkalo shqyrtim dorazi lënde në modë. Gjëra individuale prapë mund të hiqen nga lëndë në modë pas publikimi. trends: Gjërat në modë shfaqin cilat postime, hashtagë dhe histori të reja po tërheqin vëmendjen në shërbyesin tuaj. - trends_as_landing_page: Shfaq lëndë në modë për përdorues jo të futur në llogari dhe për vizitorë, në vend se të një përshkrimi të këtij shërbyesi. Lyp që të jenë të aktivizuara gjërat në modë. form_challenge: current_password: Po hyni në një zonë të sigurt imports: @@ -286,6 +286,7 @@ sq: content_cache_retention_period: Periudhë mbajtjeje lënde të largët custom_css: CSS Vetjake favicon: Favikonë + landing_page: Faqe mbërritje për vizitorë të rinj local_live_feed_access: Hyrje te prurje të atypëratyshme që përmbajnë postime vendore local_topic_feed_access: Hyrje te prurje hashtag-ësh dhe lidhjesh që përmbajnë postime vendore mascot: Simbol vetjak (e dikurshme) @@ -310,7 +311,6 @@ sq: thumbnail: Miniaturë shërbyesi trendable_by_default: Lejoni gjëra në modë pa shqyrtim paraprak trends: Aktivizo gjëra në modë - trends_as_landing_page: Përdor gjërat në modë si faqe hyrëse interactions: must_be_follower: Blloko njoftime nga jo-ndjekës must_be_following: Blloko njoftime nga persona që s’i ndiqni diff --git a/config/locales/simple_form.sr-Latn.yml b/config/locales/simple_form.sr-Latn.yml index 20e9aacf34e90d..e444f45ca60a34 100644 --- a/config/locales/simple_form.sr-Latn.yml +++ b/config/locales/simple_form.sr-Latn.yml @@ -99,7 +99,6 @@ sr-Latn: thumbnail: Slika u razmeri od približno 2:1 koja se prikazuje pored informacija o Vašem serveru. trendable_by_default: Preskoči ručni pregled sadržaja koji je u trendu. Pojedinačne stavke se nakon toga i dalje mogu ukloniti iz trendova. trends: Trendovi pokazuju koje objave, heš oznake i vesti postaju sve popularnije na Vašem serveru. - trends_as_landing_page: Prikaži sadržaj u trendu odjavljenim korisnicima i posetiocima umesto opisa ovog servera. Zahteva da trendovi budu omogućeni. form_challenge: current_password: Ulazite u bezbedno područje imports: @@ -263,7 +262,6 @@ sr-Latn: thumbnail: Sličica servera trendable_by_default: Dozvoli trendove bez prethodnog pregleda trends: Omogući trendove - trends_as_landing_page: Koristite trendove kao stranicu dočeka interactions: must_be_follower: Blokiraj obaveštenja od korisnika koji me ne prate must_be_following: Blokiraj obaveštenja od ljudi koje ne pratim diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml index 628c8df33f8058..1f5e9d517010a2 100644 --- a/config/locales/simple_form.sr.yml +++ b/config/locales/simple_form.sr.yml @@ -99,7 +99,6 @@ sr: thumbnail: Слика у размери од приближно 2:1 која се приказује поред информација о Вашем серверу. trendable_by_default: Прескочи ручни преглед садржаја који је у тренду. Појединачне ставке се након тога и даље могу уклонити из трендова. trends: Трендови показују које објаве, хеш ознаке и вести постају све популарније на Вашем серверу. - trends_as_landing_page: Прикажи садржај у тренду одјављеним корисницима и посетиоцима уместо описа овог сервера. Захтева да трендови буду омогућени. form_challenge: current_password: Улазите у безбедно подручје imports: @@ -263,7 +262,6 @@ sr: thumbnail: Сличица сервера trendable_by_default: Дозволи трендове без претходног прегледа trends: Омогући трендове - trends_as_landing_page: Користите трендове као страницу дочека interactions: must_be_follower: Блокирај обавештења од корисника који ме не прате must_be_following: Блокирај обавештења од људи које не пратим diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 97a6c74e26d5eb..5f08efa3ff1f2d 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -105,7 +105,6 @@ sv: thumbnail: En bild i cirka 2:1-proportioner som visas tillsammans med din serverinformation. trendable_by_default: Hoppa över manuell granskning av trendande innehåll. Enskilda objekt kan ändå raderas från trender retroaktivt. trends: Trender visar vilka inlägg, hashtaggar och nyheter det pratas om på din server. - trends_as_landing_page: Visa trendande innehåll för utloggade användare och besökare istället för en beskrivning om servern. Kräver att trender är aktiverat. form_challenge: current_password: Du går in i ett säkert område imports: @@ -297,7 +296,6 @@ sv: thumbnail: Serverns tumnagelbild trendable_by_default: Tillåt trender utan föregående granskning trends: Aktivera trender - trends_as_landing_page: Använd trender som landningssida interactions: must_be_follower: Blockera notiser från icke-följare must_be_following: Blockera notiser från personer du inte följer diff --git a/config/locales/simple_form.th.yml b/config/locales/simple_form.th.yml index 79425617020faf..85df9148042257 100644 --- a/config/locales/simple_form.th.yml +++ b/config/locales/simple_form.th.yml @@ -101,7 +101,6 @@ th: thumbnail: แสดงภาพ 2:1 โดยประมาณควบคู่ไปกับข้อมูลเซิร์ฟเวอร์ของคุณ trendable_by_default: ข้ามการตรวจทานเนื้อหาที่กำลังนิยมด้วยตนเอง ยังคงสามารถเอารายการแต่ละรายการออกจากแนวโน้มได้หลังจากเกิดเหตุ trends: แนวโน้มแสดงว่าโพสต์, แฮชแท็ก และเรื่องข่าวใดกำลังได้รับความสนใจในเซิร์ฟเวอร์ของคุณ - trends_as_landing_page: แสดงเนื้อหาที่กำลังนิยมแก่ผู้ใช้และผู้เยี่ยมชมที่ออกจากระบบแทนที่จะเป็นคำอธิบายของเซิร์ฟเวอร์นี้ ต้องมีการเปิดใช้งานแนวโน้ม form_challenge: current_password: คุณกำลังเข้าสู่พื้นที่ปลอดภัย imports: @@ -268,7 +267,6 @@ th: thumbnail: ภาพขนาดย่อเซิร์ฟเวอร์ trendable_by_default: อนุญาตแนวโน้มโดยไม่มีการตรวจทานล่วงหน้า trends: เปิดใช้งานแนวโน้ม - trends_as_landing_page: ใช้แนวโน้มเป็นหน้าเริ่มต้น interactions: must_be_follower: ปิดกั้นการแจ้งเตือนจากผู้ที่ไม่ใช่ผู้ติดตาม must_be_following: ปิดกั้นการแจ้งเตือนจากผู้คนที่คุณไม่ได้ติดตาม diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 6809bb3f509cd4..f64c12cf907ea6 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -93,6 +93,7 @@ tr: content_cache_retention_period: Diğer sunuculardaki (öne çıkarma ve yanıtlar da dahil olmak üzere) tüm gönderiler belirlenen gün sonunda, yerel bir kullanıcının etkileşimine bakılmadan, silinecektir. Yerel bir kullanıcının yerimlerine veya favorilerine eklediği gönderiler de dahildir. Farklı sunuculardaki kullanıcılar arasındaki özel bahsetmeler de kaybolacak ve geri getirilmeleri mümkün olmayacaktır. Bu ayarın kullanımı özel amaçlı sunucular içindir ve genel amaçlı kullanımda etkinleştirildiğinde kullanıcı beklentilerini karşılamayabilir. custom_css: Mastodon'un web sürümüne özel biçimler uygulayabilirsiniz. favicon: WEBP, PNG, GIF veya JPG. Varsayılan Mastodon simgesini isteğe bağlı bir simgeyle değiştirir. + landing_page: Yeni ziyaretçilerin sunucunuza ilk geldiklerinde görecekleri sayfayı seçer. "Öne çıkanlar" seçeneğini seçerseniz, Keşif Ayarlarında öne çıkanların etkinleştirilmesi gerekir. "Yerel akış" seçeneğini seçerseniz, Keşif Ayarlarında "Yerel gönderileri içeren canlı akışlara erişim" seçeneğinin "Herkes" olarak ayarlanması gerekir. mascot: Gelişmiş web arayüzündeki illüstrasyonu geçersiz kılar. media_cache_retention_period: Uzak kullanıcıların gönderilerindeki ortam dosyaları sunucunuzda önbelleklenir. Pozitif bir değer verildiğinde, ortam dosyaları belirlenen gün sonunda silinecektir. Eğer ortam dosyaları silindikten sonra istenirse, kaynak içerik hala mevcutsa, tekrar indirilecektir. Bağlantı önizleme kartlarının üçüncü parti siteleri yoklamasına ilişkin kısıtlamalar nedeniyle, bu değeri en azından 14 gün olarak ayarlamanız önerilir, yoksa bağlantı önizleme kartları bu süreden önce isteğe bağlı olarak güncellenmeyecektir. min_age: Kullanıcılardan kayıt olurken doğum tarihlerini doğrulamaları istenecektir @@ -110,7 +111,6 @@ tr: thumbnail: Sunucu bilginizin yanında gösterilen yaklaşık 2:1'lik görüntü. trendable_by_default: Öne çıkan içeriğin elle incelenmesini atla. Tekil öğeler sonrada öne çıkanlardan kaldırılabilir. trends: Öne çıkanlar, sunucunuzda ilgi toplayan gönderileri, etiketleri ve haber yazılarını gösterir. - trends_as_landing_page: Giriş yapmış kullanıcılar ve ziyaretçilere sunucunun açıklması yerine öne çıkan içeriği göster. Öne çıkanların etkin olması gerekir. form_challenge: current_password: Güvenli bir bölgeye giriyorsunuz imports: @@ -287,6 +287,7 @@ tr: content_cache_retention_period: Uzak içerik saklama süresi custom_css: Özel CSS favicon: Yer imi simgesi + landing_page: Yeni ziyaretçiler için giriş sayfası local_live_feed_access: Yerel gönderileri ön plana çıkaran canlı akışlara erişim local_topic_feed_access: Yerel gönderileri ön plana çıkaran etiket ve bağlantı akışlarına erişim mascot: Özel maskot (eski) @@ -311,7 +312,6 @@ tr: thumbnail: Sunucu küçük resmi trendable_by_default: Ön incelemesiz öne çıkanlara izin ver trends: Öne çıkanları etkinleştir - trends_as_landing_page: Giriş sayfası olarak öne çıkanları kullan interactions: must_be_follower: Takipçim olmayan kişilerden gelen bildirimleri engelle must_be_following: Takip etmediğim kişilerden gelen bildirimleri engelle diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index a798f24d167717..40be33811a5e20 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -104,7 +104,6 @@ uk: thumbnail: Зображення приблизно 2:1, що показується поряд з відомостями про ваш сервер. trendable_by_default: Пропустити ручний огляд популярних матеріалів. Індивідуальні елементи все ще можна вилучити з популярних постфактум. trends: Популярні показують, які дописи, хештеґи та новини набувають популярності на вашому сервері. - trends_as_landing_page: Показувати популярні матеріали для зареєстрованих користувачів і відвідувачів замість опису цього сервера. Для активації потрібні тренди. form_challenge: current_password: Ви входите до безпечної зони imports: @@ -286,7 +285,6 @@ uk: thumbnail: Мініатюра сервера trendable_by_default: Дозволити популярне без попереднього огляду trends: Увімкнути популярні - trends_as_landing_page: Використовуйте тенденції як цільову сторінку interactions: must_be_follower: Блокувати сповіщення від непідписаних людей must_be_following: Блокувати сповіщення від людей, на яких ви не підписані diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index ea81a76df342ab..8432ded89d2dcb 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -93,6 +93,7 @@ vi: content_cache_retention_period: Tất cả tút từ các máy chủ khác (bao gồm cả đăng lại và trả lời) sẽ bị xóa sau số ngày được chỉ định mà không tính đến bất kỳ tương tác nào của người dùng cục bộ với các tút đó. Điều này bao gồm các tút mà người dùng cục bộ đã đánh dấu nó là dấu trang hoặc mục yêu thích. Những lượt nhắc riêng tư giữa những người dùng từ các máy chủ khác nhau cũng sẽ bị mất và không thể khôi phục. Việc sử dụng cài đặt này dành cho các trường hợp có mục đích đặc biệt và phá vỡ nhiều kỳ vọng của người dùng khi được triển khai cho mục đích sử dụng chung. custom_css: Bạn có thể tùy chỉnh phong cách trên bản web của Mastodon. favicon: WEBP, PNG, GIF hoặc JPG. Dùng favicon Maston tùy chỉnh. + landing_page: Chọn trang mà khách truy cập mới sẽ thấy khi họ lần đầu truy cập máy chủ của bạn. Nếu bạn chọn "Xu hướng", thì cần bật xu hướng trong Cài đặt Khám phá. Nếu bạn chọn "Bảng tin máy chủ", thì cần đặt "Truy cập vào nguồn cấp dữ liệu trực tiếp có bài đăng cục bộ" thành "Mọi người" trong Cài đặt Khám phá. mascot: Ghi đè hình minh họa trong giao diện web nâng cao. media_cache_retention_period: Các tệp phương tiện từ các tút do người dùng máy chủ khác thực hiện sẽ được lưu vào bộ đệm trên máy chủ của bạn. Khi được đặt thành giá trị dương, phương tiện sẽ bị xóa sau số ngày được chỉ định. Nếu dữ liệu phương tiện được yêu cầu sau khi bị xóa, dữ liệu đó sẽ được tải xuống lại nếu nội dung nguồn vẫn còn. Do những hạn chế về tần suất thẻ xem trước liên kết thăm dò ý kiến ​​các trang web của bên thứ ba, bạn nên đặt giá trị này thành ít nhất 14 ngày, nếu không thẻ xem trước liên kết sẽ không được cập nhật theo yêu cầu trước thời gian đó. min_age: Thành viên sẽ được yêu cầu xác nhận ngày sinh của họ trong quá trình đăng ký @@ -110,7 +111,6 @@ vi: thumbnail: 'Một hình ảnh tỉ lệ 2: 1 được hiển thị cùng với thông tin máy chủ của bạn.' trendable_by_default: Bỏ qua việc duyệt thủ công nội dung xu hướng. Các mục riêng lẻ vẫn có thể bị xóa khỏi xu hướng sau này. trends: Hiển thị những tút, hashtag và tin tức đang được thảo luận nhiều trên máy chủ của bạn. - trends_as_landing_page: Hiển thị nội dung xu hướng cho người dùng chưa đăng nhập thay vì mô tả về máy chủ này. Yêu cầu xu hướng được kích hoạt. form_challenge: current_password: Biểu mẫu này an toàn imports: @@ -286,6 +286,7 @@ vi: content_cache_retention_period: Khoảng thời gian lưu giữ nội dung máy chủ khác custom_css: Tùy chỉnh CSS favicon: Favicon + landing_page: Trang mở đầu dành cho khách ghé thăm local_live_feed_access: Truy cập bảng tin gồm những tút của máy chủ local_topic_feed_access: Truy cập hashtag và bảng tin liên kết gồm những tút của máy chủ mascot: Tùy chỉnh linh vật (kế thừa) @@ -310,7 +311,6 @@ vi: thumbnail: Hình thu nhỏ của máy chủ trendable_by_default: Cho phép lên xu hướng mà không cần duyệt trước trends: Bật xu hướng - trends_as_landing_page: Dùng trang xu hướng làm trang chào mừng interactions: must_be_follower: Những người không theo dõi bạn must_be_following: Những người bạn không theo dõi diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index d9fe807f460e16..c740aba19f62c4 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -93,6 +93,7 @@ zh-CN: content_cache_retention_period: 来自其它实例的所有嘟文(包括转嘟与回复)都将在指定天数后被删除,不论本实例用户是否与这些嘟文产生过交互。这包括被本实例用户喜欢和收藏的嘟文。实例间用户的私下提及也将丢失并无法恢复。此设置针对的是特殊用途的实例,用于一般用途时会打破许多用户的期望。 custom_css: 你可以为网页版 Mastodon 应用自定义样式。 favicon: WEBP、PNG、GIF 或 JPG。使用自定义图标覆盖 Mastodon 的默认图标。 + landing_page: 选择新访客首次访问您的服务器时看到的页面。 如果选择“热门”,则需要在“发现”设置中启用热门趋势。 如果选择“本站动态”,则在“发现”设置中“展示本站嘟文的实时动态访问权限”一项需要设置为“所有人”。 mascot: 覆盖高级网页界面中的绘图形象。 media_cache_retention_period: 来自外站用户嘟文的媒体文件将被缓存到你的实例上。当该值被设为正值时,缓存的媒体文件将在指定天数后被清除。如果媒体文件在被清除后重新被请求,且源站内容仍然可用,它将被重新下载。由于链接预览卡拉取第三方站点的频率受到限制,建议将此值设置为至少 14 天,如果小于该值,链接预览卡将不会按需更新。 min_age: 用户注册时必须确认出生日期 @@ -110,7 +111,6 @@ zh-CN: thumbnail: 与服务器信息一并展示的约 2:1 比例的图像。 trendable_by_default: 跳过对热门内容的手工审核。个别项目仍可在之后从趋势中删除。 trends: 热门页中会显示正在你服务器上受到关注的嘟文、标签和新闻故事。 - trends_as_landing_page: 向注销的用户和访问者显示热门内容,而不是对该服务器的描述,需要启用热门。 form_challenge: current_password: 你正在进入安全区域 imports: @@ -286,6 +286,7 @@ zh-CN: content_cache_retention_period: 外站内容保留期 custom_css: 自定义 CSS favicon: Favicon + landing_page: 新访客的主页 local_live_feed_access: 展示本站嘟文的实时动态访问权限 local_topic_feed_access: 展示本站嘟文的话题标签及实时动态访问权限 mascot: 自定义吉祥物(旧) @@ -310,7 +311,6 @@ zh-CN: thumbnail: 本站缩略图 trendable_by_default: 允许在未审核的情况下将话题置为热门 trends: 启用热门 - trends_as_landing_page: 使用热门页作为登陆页面 interactions: must_be_follower: 屏蔽来自未关注我的用户的通知 must_be_following: 屏蔽来自我未关注的用户的通知 diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml index 98b5abad6d74ad..08358ff8c45227 100644 --- a/config/locales/simple_form.zh-HK.yml +++ b/config/locales/simple_form.zh-HK.yml @@ -97,7 +97,6 @@ zh-HK: thumbnail: 一幅約 2:1 的圖片顯示在你的伺服器資訊的旁邊。 trendable_by_default: 跳過對趨勢內容的手動審查,事後仍可從趨勢中刪除個別項目。 trends: 趨勢顯示哪些帖文、標籤和新聞故事在你的伺服器上較有吸引力。 - trends_as_landing_page: 向未登入的使用者及訪客展示趨勢內容,而非只有此伺服器的描述。需要啟用趨勢。 form_challenge: current_password: 你正要進入安全區域 imports: @@ -260,7 +259,6 @@ zh-HK: thumbnail: 伺服器縮圖 trendable_by_default: 允許未經審核的趨勢 trends: 啟用趨勢 - trends_as_landing_page: 使用趨勢作為登陸頁面 interactions: must_be_follower: 隱藏你關注者以外的人的通知 must_be_following: 隱藏你不關注的人的通知 diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index ee927aa994c411..181965d514b6fd 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -93,6 +93,7 @@ zh-TW: content_cache_retention_period: 所有來自其他伺服器之嘟文(包括轉嘟與回嘟)將於指定之天數後自動刪除,不論這些嘟文與本地使用者間的任何互動。這將包含本地使用者已標記為書籤或最愛之嘟文。不同站點使用者間之私訊亦將遺失且不可回復。此設定應適用於特殊情況,若常規使用將超乎多數使用者預期。 custom_css: 您於 Mastodon 網頁版本中能套用客製化風格。 favicon: WEBP、PNG、GIF、或 JPG。使用自訂圖示替代預設 Mastodon favicon 圖示。 + landing_page: 選擇當新訪客第一次造訪您伺服器時所見之頁面。若您選擇「熱門趨勢」,則該功能必須於探索設定中啟用。若您選擇「本站時間軸」,則探索設定中「允許瀏覽本站嘟文之即時內容」功能必須設定為「任何人」。 mascot: 覆寫進階網頁介面中的圖例。 media_cache_retention_period: 來自遠端伺服器嘟文中之多媒體內容將快取於您的伺服器。當設定為正值時,這些多媒體內容將於指定之天數後自您的儲存空間中自動刪除。若多媒體資料於刪除後被請求,且原始內容仍可存取,它們將被重新下載。由於連結預覽中第三方網站查詢頻率限制,建議將其設定為至少 14 日,否則於此之前連結預覽將不被即時更新。 min_age: 使用者將於註冊時被要求確認他們的生日 @@ -110,7 +111,6 @@ zh-TW: thumbnail: 大約 2:1 圖片會顯示於您伺服器資訊之旁。 trendable_by_default: 跳過手動審核熱門內容。仍能於登上熱門趨勢後移除個別內容。 trends: 熱門趨勢將顯示於您伺服器上正在吸引大量注意力的嘟文、主題標籤、或者新聞。 - trends_as_landing_page: 顯示熱門趨勢內容至未登入使用者及訪客而不是關於此伺服器之描述。需要啟用熱門趨勢。 form_challenge: current_password: 您正要進入安全區域 imports: @@ -286,6 +286,7 @@ zh-TW: content_cache_retention_period: 遠端內容保留期限 custom_css: 自訂 CSS favicon: 網站圖示 (Favicon) + landing_page: 新訪客之登陸頁面 local_live_feed_access: 允許瀏覽本站嘟文之即時內容 local_topic_feed_access: 允許瀏覽本站嘟文之主題標籤與連結 mascot: 自訂吉祥物 (legacy) @@ -310,7 +311,6 @@ zh-TW: thumbnail: 伺服器縮圖 trendable_by_default: 允許熱門趨勢直接顯示,不需經過審核 trends: 啟用熱門趨勢 - trends_as_landing_page: 以熱門趨勢作為登陸頁面 interactions: must_be_follower: 封鎖非跟隨者的通知 must_be_following: 封鎖您未跟隨之使用者的通知 diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 3b957a1569a86e..e77132a8840255 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -812,7 +812,6 @@ sl: title: Privzeto izvzemi uporabnike iz indeksiranja iskalnika discovery: follow_recommendations: Sledi priporočilom - preamble: Izpostavljanje zanimivih vsebin je ključno za pridobivanje novih uporabnikov, ki morda ne poznajo nikogar na Mastodonu. Nadzirajte, kako različne funkcionalnosti razkritja delujejo na vašem strežniku. profile_directory: Imenik profilov public_timelines: Javne časovnice publish_statistics: Objavi statistiko diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 1a8d9a91ab661d..6aa95821d467cf 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -831,7 +831,7 @@ sq: title: Lëri, si parazgjedhje, përdoruesit jashtë indeksimi nga motorë kërkimesh discovery: follow_recommendations: Rekomandime ndjekjeje - preamble: Shpërfaqja e lëndës interesante është me rëndësi kyçe për mirëseardhjen e përdoruesve të rinj që mund të mos njohin njeri në Mastodon. Kontrolloni se si funksionojnë në shërbyesin tuaj veçori të ndryshme zbulimi. + preamble: Shpërfaqja e lëndës interesante është me rëndësi për mirëseardhjen e përdoruesve të rinj, të cilët mund të mos njohin njeri në Mastodon. Kontrolloni se funksionojnë në shërbyesin tuaj veçori të ndryshme zbulimi lënde. privacy: Privatësi profile_directory: Drejtori profilesh public_timelines: Rrjedha kohore publike @@ -845,7 +845,13 @@ sq: feed_access: modes: authenticated: Vetëm përdorues të mirëfilltësuar + disabled: Lyp doemos rol specifik përdoruesi public: Kushdo + landing_page: + values: + about: Mbi + local_feed: Prurje vendore + trends: Në modë registrations: moderation_recommandation: Ju lutemi, sigurohuni si keni një ekip adekuat dhe reagues moderimi, përpara se të hapni regjistrimet për këdo! preamble: Kontrolloni cilët mund të krijojnë llogari në shërbyesin tuaj. diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 9f422b058b606a..7d75fe3bd1369e 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -745,7 +745,6 @@ sr-Latn: title: Podrazumevano isključi korisnike iz indeksiranja pretraživača discovery: follow_recommendations: Preporuke za praćenje - preamble: Održavanje zanimljivih sadržaja na površini je ključno u privlačenju novih korisnika koji možda ne znaju nikoga na Mastodon-u. Kontrolišite kako različiti načini istraživanja funkcionišu na vašem serveru. profile_directory: Direktorijum profilâ public_timelines: Javne vremenske linije publish_statistics: Objavi statistiku diff --git a/config/locales/sr.yml b/config/locales/sr.yml index b8d3ad1fc14d7b..25b3728186abf1 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -775,7 +775,6 @@ sr: title: Подразумевано искључи кориснике из индексирања претраживача discovery: follow_recommendations: Препоруке за праћење - preamble: Одржавање занимљивих садржаја на површини је кључно у привлачењу нових корисника који можда не знају никога на Mastodon-у. Контролишите како различити начини истраживања функционишу на вашем серверу. profile_directory: Директоријум профилâ public_timelines: Јавне временске линије publish_statistics: Објави статистику diff --git a/config/locales/sv.yml b/config/locales/sv.yml index df222cf147a4f7..ad1e36bc860234 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -837,7 +837,6 @@ sv: title: Undantag användare från sökmotorindexering som standard discovery: follow_recommendations: Följrekommendationer - preamble: Att visa intressant innehåll är avgörande i onboarding av nya användare som kanske inte känner någon på Mastodon. Styr hur olika upptäcktsfunktioner fungerar på din server. privacy: Integritet profile_directory: Profilkatalog public_timelines: Offentliga tidslinjer diff --git a/config/locales/th.yml b/config/locales/th.yml index 46236daa91efd8..202e2c0035a3f9 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -782,7 +782,6 @@ th: title: เลือกให้ผู้ใช้ไม่รับการทำดัชนีโดยเครื่องมือค้นหาเป็นค่าเริ่มต้น discovery: follow_recommendations: คำแนะนำการติดตาม - preamble: การแสดงเนื้อหาที่น่าสนใจเป็นเครื่องมือในการเตรียมความพร้อมให้ผู้ใช้ใหม่ที่อาจไม่รู้จักใครก็ตามใน Mastodon ควบคุมวิธีที่คุณลักษณะการค้นพบต่าง ๆ ทำงานในเซิร์ฟเวอร์ของคุณ privacy: ความเป็นส่วนตัว profile_directory: ไดเรกทอรีโปรไฟล์ public_timelines: เส้นเวลาสาธารณะ diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 8f2894e0c15310..3cedf77bbe360c 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -796,6 +796,8 @@ tr: view_dashboard_description: Kullanıcıların ana panele ve çeşitli ölçütlere erişmesine izin verir view_devops: DevOps view_devops_description: Kullanıcıların Sidekiq ve pgHero panellerine erişmesine izin verir + view_feeds: Canlı ve konu akışlarını görüntüle + view_feeds_description: Kullanıcıların sunucu ayarlarından bağımsız olarak canlı ve konu akışlarına erişmelerini sağlar title: Roller rules: add_new: Kural ekle @@ -851,7 +853,13 @@ tr: feed_access: modes: authenticated: Sadece yetkilendirilmiş kullanıcılar + disabled: Belirli kullanıcı rolü gerekir public: Herkes + landing_page: + values: + about: Hakkında + local_feed: Yerel akış + trends: Öne çıkanlar registrations: moderation_recommandation: Lütfen kayıtları herkese açmadan önce yeterli ve duyarlı bir denetleyici ekibine sahip olduğunuzdan emin olun! preamble: Sunucunuzda kimin hesap oluşturabileceğini denetleyin. diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 1d667b9eab4216..bd94e9da3f1f89 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -825,7 +825,6 @@ uk: title: Усталено відмовитись від індексації користувачів пошуковими системами discovery: follow_recommendations: Поради щодо підписок - preamble: Показ цікавих матеріалів відіграє важливу роль у залученні нових користувачів, які, можливо, не знають нікого з Mastodon. Контролюйте роботу різних функцій виявлення на вашому сервері. privacy: Конфіденційність profile_directory: Каталог профілів public_timelines: Публічна стрічка diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 6df758f92a9bb6..0d3866f652083c 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -782,6 +782,8 @@ vi: view_dashboard_description: Cho phép truy cập trang tổng quan và các chỉ số khác view_devops: Nhà phát triển view_devops_description: Cho phép truy cập trang tổng quan Sidekiq và pgHero + view_feeds: Xem nguồn cấp dữ liệu trực tiếp và theo chủ đề + view_feeds_description: Cho phép người dùng truy cập nguồn cấp dữ liệu trực tiếp và theo chủ đề bất kể cài đặt máy chủ title: Danh sách vai trò rules: add_new: Thêm nội quy @@ -823,7 +825,7 @@ vi: title: Mặc định người dùng không xuất hiện trong công cụ tìm kiếm discovery: follow_recommendations: Gợi ý theo dõi - preamble: Hiển thị nội dung thú vị là công cụ để thu hút người dùng mới, những người có thể không quen bất kỳ ai trong Mastodon. Kiểm soát cách các tính năng khám phá hoạt động trên máy chủ của bạn. + preamble: Hiển thị nội dung thú vị là công cụ để thu hút người dùng mới, những người có thể không quen bất kỳ ai trên Mastodon. Kiểm soát cách các tính năng khám phá hoạt động trên máy chủ của bạn. privacy: Riêng tư profile_directory: Danh bạ public_timelines: Bảng tin @@ -837,7 +839,13 @@ vi: feed_access: modes: authenticated: Chỉ những người dùng đã xác minh + disabled: Yêu cầu vai trò người dùng cụ thể public: Mọi người + landing_page: + values: + about: Giới thiệu + local_feed: Bảng tin máy chủ + trends: Xu hướng registrations: moderation_recommandation: Vui lòng đảm bảo rằng bạn có một đội ngũ kiểm duyệt và phản ứng nhanh trước khi mở đăng ký cho mọi người! preamble: Kiểm soát những ai có thể tạo tài khoản trên máy chủ của bạn. diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index b9147389020fca..e7385e392e8137 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -823,7 +823,7 @@ zh-CN: title: 默认不让用户被搜索引擎索引 discovery: follow_recommendations: 关注推荐 - preamble: 露出有趣的内容有助于新加入 Mastodon 的用户融入。可在这里控制多种发现功能如何在你的服务器上工作。 + preamble: 展示有趣的内容有助于新加入 Mastodon 的用户融入。可在这里控制多种发现功能如何在你的服务器上工作。 privacy: 隐私 profile_directory: 个人资料目录 public_timelines: 公共时间线 @@ -838,6 +838,11 @@ zh-CN: modes: authenticated: 仅已登录用户 public: 所有人 + landing_page: + values: + about: 关于 + local_feed: 本站动态 + trends: 热门 registrations: moderation_recommandation: 在向所有人开放注册之前,请确保你拥有一个人手足够且反应迅速的管理团队! preamble: 控制谁可以在你的服务器上创建账号。 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 6045808fee303a..d565d3604c836b 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -736,7 +736,6 @@ zh-HK: title: 預設用戶不在搜尋引擎索引之內 discovery: follow_recommendations: 追蹤建議 - preamble: 呈現有趣的內容有助於吸引不認識 Mastodon 的使用者新手上路。控制各種探索功能在你的伺服器上的運作方式。 privacy: 私隱 profile_directory: 個人檔案目錄 public_timelines: 公共時間軸 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index ce4fc73de36ae8..6c56aaf90f721c 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -782,6 +782,8 @@ zh-TW: view_dashboard_description: 允許使用者存取儀表板與各種指標 view_devops: DevOps view_devops_description: 允許使用者存取 Sidekiq 與 pgHero 儀表板 + view_feeds: 檢視即時內容與主題 + view_feeds_description: 允許使用者無論伺服器設定為何皆可存取即時內容與主題 title: 角色 rules: add_new: 新增規則 @@ -825,7 +827,7 @@ zh-TW: title: 預設將使用者排除於搜尋引擎索引 discovery: follow_recommendations: 跟隨建議 - preamble: 呈現有趣的內容有助於 Mastodon 上一人不識的新手上路。控制各種不同的分類於您伺服器上如何被探索到。 + preamble: 呈現有趣的內容有助於 Mastodon 上一人不識的新手上路。控制您伺服器上各類探索功能之運作方式。 privacy: 隱私權 profile_directory: 個人檔案目錄 public_timelines: 公開時間軸 @@ -839,7 +841,13 @@ zh-TW: feed_access: modes: authenticated: 僅限已登入之使用者 + disabled: 需要特定使用者權限 public: 任何人 + landing_page: + values: + about: 關於 + local_feed: 本站時間軸 + trends: 熱門趨勢 registrations: moderation_recommandation: 對所有人開放註冊之前,請確保您有人手充足且反應靈敏的管理員團隊! preamble: 控制誰能於您伺服器上建立帳號。 @@ -1894,7 +1902,7 @@ zh-TW: followers: 僅限跟隨者 nobody: 僅有我 public: 所有人 - quote_post_author: 以引用 %{acct} 之嘟文 + quote_post_author: 已引用 %{acct} 之嘟文 title: "%{name}:「%{quote}」" visibilities: direct: 私訊 diff --git a/config/roles.yml b/config/roles.yml index 30e7d60be60b02..9e6887dda9c2ed 100644 --- a/config/roles.yml +++ b/config/roles.yml @@ -4,6 +4,7 @@ moderator: permissions: - view_dashboard - view_audit_log + - view_feeds - manage_users - manage_reports - manage_taxonomies diff --git a/config/settings.yml b/config/settings.yml index e7719818127cab..b532452c4047bf 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -27,7 +27,6 @@ defaults: &defaults noindex: false theme: 'system' trends: true - trends_as_landing_page: true trendable_by_default: false disallowed_hashtags: # space separated string or list of hashtags without the hash bootstrap_timeline_accounts: '' @@ -52,6 +51,7 @@ defaults: &defaults emoji_reaction_disallow_domains: [] allow_referrer_origin: false auto_accept_legacy_quotes: false + landing_page: 'trends' development: <<: *defaults diff --git a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb index 4d180bd8f39ee0..9a18a6451ce421 100644 --- a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb +++ b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb @@ -12,8 +12,7 @@ def up Setting.upsert_all( %w(local_live_feed_access remote_live_feed_access local_topic_feed_access remote_topic_feed_access).map do |var| { var: var, value: value ? "--- public\n" : "--- authenticated\n" } - end, - unique_by: :var + end ) end diff --git a/db/migrate/20251023210145_migrate_landing_page_setting.rb b/db/migrate/20251023210145_migrate_landing_page_setting.rb new file mode 100644 index 00000000000000..e8448bc75e3b26 --- /dev/null +++ b/db/migrate/20251023210145_migrate_landing_page_setting.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class MigrateLandingPageSetting < ActiveRecord::Migration[8.0] + class Setting < ApplicationRecord; end + + def up + setting = Setting.find_by(var: 'trends_as_landing_page') + return unless setting.present? && setting.attributes['value'].present? + + value = YAML.safe_load(setting.attributes['value'], permitted_classes: [ActiveSupport::HashWithIndifferentAccess, Symbol]) + + Setting.upsert({ + var: 'landing_page', + value: value ? "--- trends\n" : "--- about\n", + }) + end + + def down; end +end diff --git a/db/schema.rb b/db/schema.rb index 572c8a42317e03..459f736917a6af 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_10_07_142305) do +ActiveRecord::Schema[8.0].define(version: 2025_10_23_210145) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 8ae82f4d07953d..391d8a4b7e3aa1 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -35,7 +35,7 @@ def patch end def default_prerelease - 'beta.2' + 'rc.1' end def prerelease diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake index 1c2ef77a6a87bf..2b0cfca9e7bb3c 100644 --- a/lib/tasks/tests.rake +++ b/lib/tasks/tests.rake @@ -144,6 +144,16 @@ namespace :tests do exit(1) end + unless Setting.landing_page == 'about' + puts 'Landing page settings not migrated as expected' + exit(1) + end + + unless Setting.local_live_feed_access == 'authenticated' + puts 'Local live feed access not migrated as expected' + exit(1) + end + puts 'No errors found. Database state is consistent with a successful migration process.' end @@ -162,6 +172,13 @@ namespace :tests do (1, 'https://example.com/users/foobar', 'foobar@example.com', now(), now()), (1, 'https://example.com/users/foobar', 'foobar@example.com', now(), now()); + /* trends_as_landing_page is technically not a 3.3.0 setting, but it's easier to just add it here */ + INSERT INTO "settings" + (id, thing_type, thing_id, var, value, created_at, updated_at) + VALUES + (7, NULL, NULL, 'timeline_preview', E'--- false\n', now(), now()), + (8, NULL, NULL, 'trends_as_landing_page', E'--- false\n', now(), now()); + /* Doorkeeper records While the `read:me` scope was technically not valid in 3.3.0, it is still useful for the purposes of testing the `ChangeReadMeScopeToProfile` diff --git a/package.json b/package.json index ebfcebb448f01e..1eefe3a4e1ae86 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,7 @@ "eslint-import-resolver-typescript": "^4.2.5", "eslint-plugin-formatjs": "^5.3.1", "eslint-plugin-import": "~2.32.0", - "eslint-plugin-jsdoc": "^54.0.0", + "eslint-plugin-jsdoc": "^60.0.0", "eslint-plugin-jsx-a11y": "~6.10.2", "eslint-plugin-promise": "~7.2.1", "eslint-plugin-react": "^7.37.4", diff --git a/spec/controllers/concerns/api/error_handling_spec.rb b/spec/controllers/concerns/api/error_handling_spec.rb index eff01605d2a89a..496f38648729e8 100644 --- a/spec/controllers/concerns/api/error_handling_spec.rb +++ b/spec/controllers/concerns/api/error_handling_spec.rb @@ -32,7 +32,7 @@ def failure Mastodon::ValidationError => 422, OpenSSL::SSL::SSLError => 503, Seahorse::Client::NetworkingError => 503, - Stoplight::Error::RedLight => 503, + Stoplight::Error::RedLight.new(:name, cool_off_time: 1, retry_after: 1) => 503, }.each do |error, code| it "Handles error class of #{error}" do allow(FakeService) diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb index 2798219716a9f7..443fc502cf97bb 100644 --- a/spec/lib/activitypub/activity/update_spec.rb +++ b/spec/lib/activitypub/activity/update_spec.rb @@ -210,18 +210,17 @@ shared_examples 'updates counts' do it 'updates the reblog count' do - expect(status.untrusted_reblogs_count).to eq reblogs + expect { subject.perform }.to change { status.reload.untrusted_reblogs_count }.to(reblogs) end it 'updates the favourites count' do - expect(status.untrusted_favourites_count).to eq favourites + expect { subject.perform }.to change { status.reload.untrusted_favourites_count }.to(favourites) end end context 'with an implicit update' do before do status.update!(uri: ActivityPub::TagManager.instance.uri_for(status)) - subject.perform end it_behaves_like 'updates counts' @@ -234,11 +233,89 @@ before do status.update!(uri: ActivityPub::TagManager.instance.uri_for(status)) - subject.perform end it_behaves_like 'updates counts' end end + + context 'with an Article object' do + let(:updated) { nil } + let(:favourites) { 50 } + let(:reblogs) { 100 } + + let!(:status) do + Fabricate( + :status, + uri: 'https://example.com/statuses/article', + account: sender, + text: "

Future of the Fediverse

\n\n

Guest article by John Mastodon

The fediverse is great reading this you will find out why!

" + ) + end + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Update', + actor: sender.uri, + object: { + type: 'Article', + id: status.uri, + name: 'Future of the Fediverse', + summary: '

Guest article by Jane Mastodon

The fediverse is great reading this you will find out why!

', + content: 'Foo', + updated: updated, + likes: { + id: "#{status.uri}/likes", + type: 'Collection', + totalItems: favourites, + }, + shares: { + id: "#{status.uri}/shares", + type: 'Collection', + totalItems: reblogs, + }, + }, + }.with_indifferent_access + end + + shared_examples 'updates counts' do + it 'updates the reblog count' do + expect { subject.perform }.to change { status.reload.untrusted_reblogs_count }.to(reblogs) + end + + it 'updates the favourites count' do + expect { subject.perform }.to change { status.reload.untrusted_favourites_count }.to(favourites) + end + end + + context 'with an implicit update' do + before do + status.update!(uri: ActivityPub::TagManager.instance.uri_for(status)) + end + + it_behaves_like 'updates counts' + end + + context 'with an explicit update' do + let(:favourites) { 150 } + let(:reblogs) { 200 } + let(:updated) { Time.now.utc.iso8601 } + + before do + status.update!(uri: ActivityPub::TagManager.instance.uri_for(status)) + end + + it_behaves_like 'updates counts' + + it 'changes the contents as expected' do + expect { subject.perform } + .to(change { status.reload.text }) + + expect(status.text).to start_with("

Future of the Fediverse

\n\n

Guest article by Jane Mastodon

The fediverse is great reading this you will find out why!

") + end + end + end end end diff --git a/spec/lib/extractor_spec.rb b/spec/lib/extractor_spec.rb index bc3ee8ac496a2b..e1a57d57883ca5 100644 --- a/spec/lib/extractor_spec.rb +++ b/spec/lib/extractor_spec.rb @@ -35,12 +35,24 @@ end describe 'extract_hashtags_with_indices' do - it 'returns an empty array if it does not have #' do + it 'returns an empty array if it does not have # or #' do text = 'a string without hash sign' extracted = described_class.extract_hashtags_with_indices(text) expect(extracted).to eq [] end + it 'returns hashtags preceded by an ASCII hash' do + text = 'hello #world' + extracted = described_class.extract_hashtags_with_indices(text) + expect(extracted).to eq [{ hashtag: 'world', indices: [6, 12] }] + end + + it 'returns hashtags preceded by a full-width hash' do + text = 'hello #world' + extracted = described_class.extract_hashtags_with_indices(text) + expect(extracted).to eq [{ hashtag: 'world', indices: [6, 12] }] + end + it 'does not exclude normal hash text before ://' do text = '#hashtag://' extracted = described_class.extract_hashtags_with_indices(text) diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb index 81aaf8858556fe..ce4c966087849f 100644 --- a/spec/models/export_spec.rb +++ b/spec/models/export_spec.rb @@ -14,15 +14,20 @@ end describe '#to_bookmarks_csv' do - before { Fabricate.times(2, :bookmark, account: account) } - + let!(:bookmark) { Fabricate(:bookmark, account: account) } let(:export) { CSV.parse(subject.to_bookmarks_csv) } + let!(:second_bookmark) { Fabricate(:bookmark, account: account) } + let!(:bookmark_of_soft_deleted) { Fabricate(:bookmark, account: account) } + + before do + bookmark_of_soft_deleted.status.discard + end it 'returns a csv of bookmarks' do expect(export) .to contain_exactly( - include(/statuses/), - include(/statuses/) + [ActivityPub::TagManager.instance.uri_for(bookmark.status)], + [ActivityPub::TagManager.instance.uri_for(second_bookmark.status)] ) end end diff --git a/spec/models/public_feed_spec.rb b/spec/models/public_feed_spec.rb index 63878d563e0146..b139bb1e6ca1c5 100644 --- a/spec/models/public_feed_spec.rb +++ b/spec/models/public_feed_spec.rb @@ -322,5 +322,320 @@ end end end + + context 'when both local_live_feed_access and remote_live_feed_access are disabled' do + before do + Setting.local_live_feed_access = 'disabled' + Setting.remote_live_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'includes all expected statuses' do + expect(subject).to include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(viewer, local: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(viewer, remote: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'includes remote statuses only' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end + + context 'when local_live_feed_access is disabled' do + before do + Setting.local_live_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(viewer, local: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(viewer, remote: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end + + context 'when remote_live_feed_access is disabled' do + before do + Setting.remote_live_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + + it 'is not affected by personal domain blocks' do + viewer.block_domain!('test.com') + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(viewer, local: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + + it 'is not affected by personal domain blocks' do + viewer.block_domain!('test.com') + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(viewer, remote: true).get(20).map(&:id) } + + let!(:local_account) { Fabricate(:account, domain: nil) } + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { Fabricate(:status, account: local_account) } + let!(:remote_status) { Fabricate(:status, account: remote_account) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end end end diff --git a/spec/models/tag_feed_spec.rb b/spec/models/tag_feed_spec.rb index 21016c384dea45..db806d91e66714 100644 --- a/spec/models/tag_feed_spec.rb +++ b/spec/models/tag_feed_spec.rb @@ -132,5 +132,311 @@ results = described_class.new(tag_cats, account).get(20) expect(results).to_not include status_tagged_with_cats end + + context 'when both local_topic_feed_access and remote_topic_feed_access are disabled' do + before do + Setting.local_topic_feed_access = 'disabled' + Setting.remote_topic_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(tag_cats, viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'includes all expected statuses' do + expect(subject).to include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(tag_cats, viewer, local: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(tag_cats, viewer, remote: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'includes remote statuses only' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end + + context 'when local_topic_feed_access is disabled' do + before do + Setting.local_topic_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(tag_cats, viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(tag_cats, viewer, local: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(tag_cats, viewer, remote: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end + + context 'when remote_topic_feed_access is disabled' do + before do + Setting.remote_topic_feed_access = 'disabled' + end + + context 'without local_only option' do + subject { described_class.new(tag_cats, viewer).get(20).map(&:id) } + + let(:viewer) { nil } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + + it 'is not affected by personal domain blocks' do + viewer.block_domain!('test.com') + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a local_only option set' do + subject { described_class.new(tag_cats, viewer, local: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'does not include remote instances statuses' do + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + + it 'is not affected by personal domain blocks' do + viewer.block_domain!('test.com') + expect(subject).to include(local_status.id) + expect(subject).to_not include(remote_status.id) + end + end + end + + context 'with a remote_only option set' do + subject { described_class.new(tag_cats, viewer, remote: true).get(20).map(&:id) } + + let!(:remote_account) { Fabricate(:account, domain: 'test.com') } + let!(:local_status) { status_tagged_with_cats } + let!(:remote_status) { Fabricate(:status, account: remote_account, tags: [tag_cats]) } + + context 'without a viewer' do + let(:viewer) { nil } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a viewer' do + let(:viewer) { Fabricate(:account, username: 'viewer') } + + it 'returns an empty list' do + expect(subject).to be_empty + end + end + + context 'with a moderator as viewer' do + let(:viewer) { Fabricate(:moderator_user).account } + + it 'does not include local instances statuses' do + expect(subject).to_not include(local_status.id) + expect(subject).to include(remote_status.id) + end + end + end + end end end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 18378c000d2e61..d41d3a9e21e1e5 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -84,6 +84,10 @@ def previous_name_error_message expect(subject.match('this is #aesthetic').to_s).to eq '#aesthetic' end + it 'matches #foo' do + expect(subject.match('this is #foo').to_s).to eq '#foo' + end + it 'matches digits at the start' do expect(subject.match('hello #3d').to_s).to eq '#3d' end diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 1586d7402a33f1..5024c29043ce81 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -337,6 +337,29 @@ end end + context 'with a quote of a reblog' do + let(:quoted_status) { Fabricate(:status, quote_approval_policy: Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] << 16) } + let(:reblog) { Fabricate(:status, reblog: quoted_status) } + let(:params) do + { + status: 'Hello world, this is a self-quote', + quoted_status_id: reblog.id, + } + end + + it 'returns a quote post, as well as rate limit headers', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body[:quote]).to be_present + expect(response.parsed_body[:quote][:quoted_status][:id]).to eq quoted_status.id.to_s + expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s + expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s + end + end + context 'with a self-quote post and a CW but no text' do let(:quoted_status) { Fabricate(:status, account: user.account) } let(:params) do diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb index eccb2babc98050..3119138a0a01d4 100644 --- a/spec/requests/signature_verification_spec.rb +++ b/spec/requests/signature_verification_spec.rb @@ -352,34 +352,7 @@ end end - # TODO: Remove when feature is enabled - context 'with an HTTP Message Signature (final RFC version) when support is disabled' do - before { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) } - - context 'with a valid signature on a GET request' do - let(:signature_input) do - 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"' - end - let(:signature_header) do - 'sig1=:WfM6q/qBqhUyqPUDt9metjadJGtLLpmMTBzk/t+R3byKe4/TGAXC6vBB/M6NsD5qv8GCmQGtisCMQxJQO0IGODGzi+Jv+eqDJ50agMVXNV6nUOzY44c4/XTPoI98qyx1oEMa4Hefy3vSYKq96iDVAc+RDLCMTeGP3wn9wizjD1SNmU0RZI1bTB+eCkywMP9mM5zXzUOYF+Qkuf+WdEpPR1XUGPlnqfdvPalcKVfaI/VThBjI91D/lmUGoa69x4EBEHM+aJmW6086e7/dVh+FndKkdGfXslZXFZKi2flTGQZgEWLn948SqAaJQROkJg8B14Sb1NONS1qZBhK3Mum8Pg==:' # rubocop:disable Layout/LineLength - end - - it 'cannot verify signature', :aggregate_failures do - get '/activitypub/signature_required', headers: { - 'Host' => 'www.example.com', - 'Signature-Input' => signature_input, - 'Signature' => signature_header, - } - - expect(response).to have_http_status(401) - expect(response.parsed_body).to match( - error: 'Error parsing signature parameters' - ) - end - end - end - - context 'with an HTTP Message Signature (final RFC version)', feature: :http_message_signatures do + context 'with an HTTP Message Signature (final RFC version)' do context 'with a known account' do let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) } diff --git a/spec/system/home_spec.rb b/spec/system/home_spec.rb index 0838b3d8e7e667..aafa9323c0b07e 100644 --- a/spec/system/home_spec.rb +++ b/spec/system/home_spec.rb @@ -23,5 +23,41 @@ .to have_css('noscript', text: /Mastodon/) .and have_css('body', class: 'app-body') end + + context 'when the landing page is set to about' do + before do + Setting.landing_page = 'about' + end + + it 'visits the root path and is redirected to the about page', :js do + visit root_path + + expect(page).to have_current_path('/about') + end + end + + context 'when the landing page is set to trends' do + before do + Setting.landing_page = 'trends' + end + + it 'visits the root path and is redirected to the trends page', :js do + visit root_path + + expect(page).to have_current_path('/explore') + end + end + + context 'when the landing page is set to local_feed' do + before do + Setting.landing_page = 'local_feed' + end + + it 'visits the root path and is redirected to the local live feed page', :js do + visit root_path + + expect(page).to have_current_path('/public/local') + end + end end end diff --git a/streaming/database.js b/streaming/database.js index 553c9149cc0aa6..9dd7a97cfe72bb 100644 --- a/streaming/database.js +++ b/streaming/database.js @@ -65,7 +65,7 @@ export function configFromEnv(env, environment) { if (typeof parsedUrl.ssl === 'boolean') { baseConfig.ssl = parsedUrl.ssl; } else if (typeof parsedUrl.ssl === 'object' && !Array.isArray(parsedUrl.ssl) && parsedUrl.ssl !== null) { - /** @type {Record} */ + /** @type {Record} */ const sslOptions = parsedUrl.ssl; baseConfig.ssl = {}; diff --git a/streaming/index.js b/streaming/index.js index 59d17c42b5e847..75517064535caa 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -29,6 +29,7 @@ import * as Redis from './redis.js'; import { isTruthy, normalizeHashtag, firstParam } from './utils.js'; const environment = process.env.NODE_ENV || 'development'; +const PERMISSION_VIEW_FEEDS = 0x0000000000100000; // Correctly detect and load .env or .env.production file based on environment: const dotenvFile = environment === 'production' ? '.env.production' : '.env'; @@ -52,6 +53,18 @@ initializeLogLevel(process.env, environment); * @property {string[]} scopes * @property {string} accountId * @property {string[]} chosenLanguages + * @property {number} permissions + */ + +/** + * @typedef {http.IncomingMessage & ResolvedAccount & { + * path: string + * query: Record + * remoteAddress?: string + * cachedFilters: unknown + * scopes: string[] + * necessaryScopes: string[] + * }} Request */ /** @@ -59,8 +72,8 @@ initializeLogLevel(process.env, environment); * from redis and when receiving a message from a client over a websocket * connection, this is why it accepts a `req` argument. * @param {string} json - * @param {any?} req - * @returns {Object.|null} + * @param {Request?} req + * @returns {Object.|null} */ const parseJSON = (json, req) => { try { @@ -189,6 +202,7 @@ const startServer = async () => { let resolvedAccount; try { + // @ts-expect-error resolvedAccount = await accountFromRequest(request); } catch (err) { // Unfortunately for using the on('upgrade') setup, we need to manually @@ -250,7 +264,7 @@ const startServer = async () => { }); /** - * @type {Object.): void>>} + * @type {Object.): void>>} */ const subs = {}; @@ -381,7 +395,7 @@ const startServer = async () => { }; /** - * @param {http.IncomingMessage & ResolvedAccount} req + * @param {Request} req * @param {string[]} necessaryScopes * @returns {boolean} */ @@ -390,11 +404,11 @@ const startServer = async () => { /** * @param {string} token - * @param {any} req + * @param {Request} req * @returns {Promise} */ const accountFromToken = async (token, req) => { - const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id INNER JOIN accounts ON accounts.id = users.account_id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL AND users.disabled IS FALSE AND accounts.suspended_at IS NULL LIMIT 1', [token]); + const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes, COALESCE(user_roles.permissions, 0) AS permissions FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id INNER JOIN accounts ON accounts.id = users.account_id LEFT OUTER JOIN user_roles ON user_roles.id = users.role_id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL AND users.disabled IS FALSE AND accounts.suspended_at IS NULL LIMIT 1', [token]); if (result.rows.length === 0) { throw new AuthenticationError('Invalid access token'); @@ -410,34 +424,32 @@ const startServer = async () => { scopes: result.rows[0].scopes.split(' '), accountId: result.rows[0].account_id, chosenLanguages: result.rows[0].chosen_languages, + permissions: result.rows[0].permissions, }; }; /** - * @param {any} req + * @param {Request} req * @returns {Promise} */ - const accountFromRequest = (req) => - new Promise((resolve, reject) => { - const authorization = req.headers.authorization; - const location = url.parse(req.url, true); - const accessToken = - location.query.access_token || req.headers['sec-websocket-protocol']; + const accountFromRequest = (req) => new Promise((resolve, reject) => { + const authorization = req.headers.authorization; + const location = req.url ? url.parse(req.url, true) : undefined; + const accessToken = location?.query.access_token || req.headers['sec-websocket-protocol']; - if (!authorization && !accessToken) { - reject(new AuthenticationError('Missing access token')); - return; - } + if (!authorization && !accessToken) { + reject(new AuthenticationError('Missing access token')); + return; + } - const token = authorization - ? authorization.replace(/^Bearer /, '') - : accessToken; + const token = authorization ? authorization.replace(/^Bearer /, '') : accessToken; - resolve(accountFromToken(token, req)); - }); + // @ts-expect-error + resolve(accountFromToken(token, req)); + }); /** - * @param {any} req + * @param {Request} req * @returns {string|undefined} */ const channelNameFromPath = (req) => { @@ -471,7 +483,7 @@ const startServer = async () => { }; /** - * @param {http.IncomingMessage & ResolvedAccount} req + * @param {Request} req * @param {import('pino').Logger} logger * @param {string|undefined} channelName * @returns {Promise.} @@ -519,7 +531,7 @@ const startServer = async () => { */ /** - * @param {any} req + * @param {Request} req * @param {SystemMessageHandlers} eventHandlers * @returns {SubscriptionListener} */ @@ -546,7 +558,7 @@ const startServer = async () => { }; /** - * @param {http.IncomingMessage & ResolvedAccount} req + * @param {Request} req * @param {http.OutgoingMessage} res */ const subscribeHttpToSystemChannel = (req, res) => { @@ -577,8 +589,8 @@ const startServer = async () => { }; /** - * @param {any} req - * @param {any} res + * @param {Request} req + * @param {http.ServerResponse} res * @param {function(Error=): void} next */ const authenticationMiddleware = (req, res, next) => { @@ -611,8 +623,8 @@ const startServer = async () => { /** * @param {Error} err - * @param {any} req - * @param {any} res + * @param {Request} req + * @param {http.ServerResponse} res * @param {function(Error=): void} next */ const errorMiddleware = (err, req, res, next) => { @@ -630,17 +642,15 @@ const startServer = async () => { }; /** - * @param {any[]} arr + * @param {string[]} arr * @param {number=} shift * @returns {string} */ - // @ts-ignore - const placeholders = (arr, shift = 0) => - arr.map((_, i) => `$${i + 1 + shift}`).join(', '); + const placeholders = (arr, shift = 0) => arr.map((_, i) => `$${i + 1 + shift}`).join(', '); /** * @param {string} listId - * @param {any} req + * @param {Request} req * @returns {Promise.} */ const authorizeListAccess = async (listId, req) => { @@ -658,7 +668,7 @@ const startServer = async () => { /** * @param {string} antennaId - * @param {any} req + * @param {Request} req * @returns {Promise.} */ const authorizeAntennaAccess = (antennaId, req) => @@ -693,25 +703,55 @@ const startServer = async () => { }); }); + /** + * @param {string} kind + * @param {ResolvedAccount} account + * @returns {Promise.<{ localAccess: boolean, remoteAccess: boolean }>} + */ + const getFeedAccessSettings = async (kind, account) => { + const access = { localAccess: true, remoteAccess: true }; + + if (account.permissions & PERMISSION_VIEW_FEEDS) { + return access; + } + + let localAccessVar, remoteAccessVar; + + if (kind === 'hashtag') { + localAccessVar = 'local_topic_feed_access'; + remoteAccessVar = 'remote_topic_feed_access'; + } else { + localAccessVar = 'local_live_feed_access'; + remoteAccessVar = 'remote_live_feed_access'; + } + + const result = await pgPool.query('SELECT var, value FROM settings WHERE var IN ($1, $2)', [localAccessVar, remoteAccessVar]); + + result.rows.forEach((row) => { + if (row.var === localAccessVar) { + access.localAccess = row.value !== "--- disabled\n"; + } else { + access.remoteAccess = row.value !== "--- disabled\n"; + } + }); + + return access; + }; + /** * @param {string[]} channelIds - * @param {http.IncomingMessage & ResolvedAccount} req + * @param {Request} req * @param {import('pino').Logger} log * @param {function(string, string): void} output * @param {undefined | function(string[], SubscriptionListener): void} attachCloseHandler * @param {'websocket' | 'eventsource'} destinationType - * @param {boolean=} needsFiltering + * @param {Object} options + * @param {boolean} options.needsFiltering + * @param {boolean=} options.filterLocal + * @param {boolean=} options.filterRemote * @returns {SubscriptionListener} */ - const streamFrom = ( - channelIds, - req, - log, - output, - attachCloseHandler, - destinationType, - needsFiltering = false, - ) => { + const streamFrom = (channelIds, req, log, output, attachCloseHandler, destinationType, { needsFiltering, filterLocal, filterRemote } = { needsFiltering: false, filterLocal: false, filterRemote: false }) => { log.info({ channelIds }, `Starting stream`); /** @@ -747,8 +787,11 @@ const startServer = async () => { // reference_texts property is not working if ProcessReferencesWorker is // used on PostStatusService and so on. (Asynchronous processing) + // @ts-expect-error const reference_texts = payload?.reference_texts || []; + // @ts-expect-error if (payload && typeof payload.reference_texts !== 'undefined') { + // @ts-expect-error delete payload.reference_texts; } @@ -762,10 +805,8 @@ const startServer = async () => { // // The channels that need filtering are determined in the function // `channelNameToIds` defined below: - if ( - !needsFiltering || - (event !== 'update' && event !== 'status.update') - ) { + if (!needsFiltering || (event !== 'update' && event !== 'status.update')) { + // @ts-expect-error transmit(event, payload); return; } @@ -773,14 +814,19 @@ const startServer = async () => { // The rest of the logic from here on in this function is to handle // filtering of statuses: + // @ts-expect-error + const localPayload = payload.account.username === payload.account.acct; + if (localPayload ? filterLocal : filterRemote) { + // @ts-expect-error + log.debug(`Message ${payload.id} filtered by feed settings`); + return; + } + // Filter based on language: - if ( - Array.isArray(req.chosenLanguages) && - req.chosenLanguages.indexOf(payload.language) === -1 - ) { - log.debug( - `Message ${payload.id} filtered by language (${payload.language})`, - ); + // @ts-expect-error + if (Array.isArray(req.chosenLanguages) && req.chosenLanguages.indexOf(payload.language) === -1) { + // @ts-expect-error + log.debug(`Message ${payload.id} filtered by language (${payload.language})`); return; } @@ -791,10 +837,9 @@ const startServer = async () => { } // Filter based on domain blocks, blocks, mutes, or custom filters: - // @ts-ignore - const targetAccountIds = [payload.account.id].concat( - payload.mentions.map((item) => item.id), - ); + // @ts-expect-error + const targetAccountIds = [payload.account.id].concat(payload.mentions.map(item => item.id)); + // @ts-expect-error const accountDomain = payload.account.acct.split('@')[1]; // TODO: Move this logic out of the message handling loop @@ -805,9 +850,8 @@ const startServer = async () => { } const queries = [ - // @ts-ignore - client.query( - `SELECT 1 + // @ts-expect-error + client.query(`SELECT 1 FROM blocks WHERE (account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})) OR (account_id = $2 AND target_account_id = $1) @@ -815,234 +859,184 @@ const startServer = async () => { SELECT 1 FROM mutes WHERE account_id = $1 - AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, - [req.accountId, payload.account.id].concat(targetAccountIds), - ), + AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, [req.accountId, payload. + // @ts-expect-error + account.id].concat(targetAccountIds)), ]; if (accountDomain) { - // @ts-ignore - queries.push( - client.query( - 'SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', - [req.accountId, accountDomain], - ), - ); + // @ts-expect-error + queries.push(client.query('SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', [req.accountId, accountDomain])); } - // @ts-ignore + // @ts-expect-error if (!payload.filtered && !req.cachedFilters) { - // @ts-ignore - queries.push( - client.query( - 'SELECT filter.id AS id, filter.phrase AS title, filter.context AS context, filter.expires_at AS expires_at, filter.action AS filter_action, filter.with_profile AS with_profile, keyword.keyword AS keyword, keyword.whole_word AS whole_word, filter.exclude_follows AS exclude_follows, filter.exclude_localusers AS exclude_localusers FROM custom_filter_keywords keyword JOIN custom_filters filter ON keyword.custom_filter_id = filter.id WHERE filter.account_id = $1 AND (filter.expires_at IS NULL OR filter.expires_at > NOW())', - [req.accountId], - ), - ); + // @ts-expect-error + queries.push(client.query('SELECT filter.id AS id, filter.phrase AS title, filter.context AS context, filter.expires_at AS expires_at, filter.action AS filter_action, filter.with_profile AS with_profile, keyword.keyword AS keyword, keyword.whole_word AS whole_word, filter.exclude_follows AS exclude_follows, filter.exclude_localusers AS exclude_localusers FROM custom_filter_keywords keyword JOIN custom_filters filter ON keyword.custom_filter_id = filter.id WHERE filter.account_id = $1 AND (filter.expires_at IS NULL OR filter.expires_at > NOW())', [req.accountId])); } + // @ts-expect-error if (!payload.filtered) { // @ts-ignore - queries.push( - client.query( - `SELECT 1 - FROM follows - WHERE (account_id = $1 AND target_account_id = $2)`, - [req.accountId, payload.account.id], - ), - ); + queries.push(client.query(`SELECT 1 FROM follows WHERE (account_id = $1 AND target_account_id = $2)`, [req.accountId, payload.account.id])); } - Promise.all(queries) - .then((values) => { - releasePgConnection(); - - // Handling blocks & mutes and domain blocks: If one of those applies, - // then we don't transmit the payload of the event to the client - if ( - values[0].rows.length > 0 || - (accountDomain && values[1].rows.length > 0) - ) { - return; - } + Promise.all(queries).then((values) => { + releasePgConnection(); - // If the payload already contains the `filtered` property, it means - // that filtering has been applied on the ruby on rails side, as - // such, we don't need to construct or apply the filters in streaming: - if (Object.hasOwn(payload, 'filtered')) { - transmit(event, payload); - return; - } - - const following = values[values.length - 1].rows.length > 0; - - // Handling for constructing the custom filters and caching them on the request - // TODO: Move this logic out of the message handling lifecycle - // @ts-ignore - if (!req.cachedFilters) { - const filterRows = values[accountDomain ? 2 : 1].rows; - - // @ts-ignore - req.cachedFilters = filterRows.reduce((cache, filter) => { - if (cache[filter.id]) { - cache[filter.id].keywords.push([ - filter.keyword, - filter.whole_word, - ]); - } else { - cache[filter.id] = { - keywords: [[filter.keyword, filter.whole_word]], - expires_at: filter.expires_at, - filter: { - id: filter.id, - title: filter.title, - context: filter.context, - expires_at: filter.expires_at, - // filter.filter_action is the value from the - // custom_filters.action database column, it is an integer - // representing a value in an enum defined by Ruby on Rails: - // - // enum { warn: 0, hide: 1 } - filter_action: ['warn', 'hide'][filter.filter_action], - withAccountName: filter.with_profile, - excludeFollows: filter.exclude_follows, - excludeLocalusers: filter.exclude_localusers, - }, - }; - } - - return cache; - }, {}); - - // Construct the regular expressions for the custom filters: This - // needs to be done in a separate loop as the database returns one - // filterRow per keyword, so we need all the keywords before - // constructing the regular expression - // @ts-ignore - Object.keys(req.cachedFilters).forEach((key) => { - // @ts-ignore - req.cachedFilters[key].regexp = new RegExp( - req.cachedFilters[key].keywords - .map(([keyword, whole_word]) => { - let expr = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - - if (whole_word) { - if (/^[\w]/.test(expr)) { - expr = `\\b${expr}`; - } - - if (/[\w]$/.test(expr)) { - expr = `${expr}\\b`; - } - } - - return expr; - }) - .join('|'), - 'i', - ); - }); - } + // Handling blocks & mutes and domain blocks: If one of those applies, + // then we don't transmit the payload of the event to the client + // @ts-expect-error + if (values[0].rows.length > 0 || (accountDomain && values[1].rows.length > 0)) { + return; + } - // Apply cachedFilters against the payload, constructing a - // `filter_results` array of FilterResult entities - // @ts-ignore - if (req.cachedFilters) { - const status = payload; - // TODO: Calculate searchableContent in Ruby on Rails: - // @ts-ignore - const searchableContent = [ - status.spoiler_text || '', - status.content, - ...(reference_texts || []), - ] - .concat( - status.poll && status.poll.options - ? status.poll.options.map((option) => option.title) - : [], - ) - .concat(status.media_attachments.map((att) => att.description)) - .join('\n\n') - .replace(//g, '\n') - .replace(/<\/p>

/g, '\n\n'); - const searchableTextContent = - JSDOM.fragment(searchableContent).textContent; - const searchableAccountContent = JSDOM.fragment( - [status.account.display_name, status.account.note].join('\n\n'), - ).textContent; - - const now = new Date(); - // @ts-ignore - const filter_results = Object.values(req.cachedFilters).reduce( - (results, cachedFilter) => { - // Check the filter hasn't expired before applying: - if ( - cachedFilter.expires_at !== null && - cachedFilter.expires_at < now - ) { - return results; - } + // If the payload already contains the `filtered` property, it means + // that filtering has been applied on the ruby on rails side, as + // such, we don't need to construct or apply the filters in streaming: + if (Object.hasOwn(payload, 'filtered')) { + transmit(event, payload); + return; + } - if ( - cachedFilter.filter && - cachedFilter.filter.excludeFollows && - following - ) { - return results; - } + // @ts-expect-error + const following = values[values.length - 1].rows.length > 0; - if ( - cachedFilter.filter && - cachedFilter.filter.excludeLocalusers && - !accountDomain - ) { - return results; + // Handling for constructing the custom filters and caching them on the request + // TODO: Move this logic out of the message handling lifecycle + // @ts-ignore + if (!req.cachedFilters) { + // @ts-expect-error + const filterRows = values[accountDomain ? 2 : 1].rows; + + req.cachedFilters = filterRows.reduce((cache, filter) => { + if (cache[filter.id]) { + cache[filter.id].keywords.push([filter.keyword, filter.whole_word]); + } else { + cache[filter.id] = { + keywords: [[filter.keyword, filter.whole_word]], + expires_at: filter.expires_at, + filter: { + id: filter.id, + title: filter.title, + context: filter.context, + expires_at: filter.expires_at, + // filter.filter_action is the value from the + // custom_filters.action database column, it is an integer + // representing a value in an enum defined by Ruby on Rails: + // + // enum { warn: 0, hide: 1 } + filter_action: ['warn', 'hide'][filter.filter_action], + withAccountName: filter.with_profile, + excludeFollows: filter.exclude_follows, + excludeLocalusers: filter.exclude_localusers, + }, + }; + } + + return cache; + }, {}); + + // Construct the regular expressions for the custom filters: This + // needs to be done in a separate loop as the database returns one + // filterRow per keyword, so we need all the keywords before + // constructing the regular expression + // @ts-expect-error + Object.keys(req.cachedFilters).forEach((key) => { + // @ts-expect-error + req.cachedFilters[key].regexp = new RegExp(req.cachedFilters[key].keywords.map(([keyword, whole_word]) => { + let expr = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + + if (whole_word) { + if (/^[\w]/.test(expr)) { + expr = `\\b${expr}`; } - // Just in-case JSDOM fails to find textContent in searchableContent - if (!searchableTextContent) { - return results; + if (/[\w]$/.test(expr)) { + expr = `${expr}\\b`; } + } - const keyword_matches = - searchableTextContent.match(cachedFilter.regexp) || - (cachedFilter.withAccountName && searchableAccountContent - ? searchableAccountContent.match(cachedFilter.regexp) - : null); - if (keyword_matches) { - // results is an Array of FilterResult; status_matches is always - // null as we only are only applying the keyword-based custom - // filters, not the status-based custom filters. - // https://docs.joinmastodon.org/entities/FilterResult/ - results.push({ - filter: cachedFilter.filter, - keyword_matches, - status_matches: null, - }); - } + return expr; + }).join('|'), 'i'); + }); + } - return results; - }, - [], - ); - - // Send the payload + the FilterResults as the `filtered` property - // to the streaming connection. To reach this code, the `event` must - // have been either `update` or `status.update`, meaning the - // `payload` is a Status entity, which has a `filtered` property: - // - // filtered: https://docs.joinmastodon.org/entities/Status/#filtered - transmit(event, { - ...payload, - filtered: filter_results, - }); - } else { - transmit(event, payload); - } - }) - .catch((err) => { - log.error(err); - releasePgConnection(); - }); + // Apply cachedFilters against the payload, constructing a + // `filter_results` array of FilterResult entities + if (req.cachedFilters) { + const status = payload; + // TODO: Calculate searchableContent in Ruby on Rails: + // @ts-expect-error + const searchableContent = ([status.spoiler_text || '', status.content, ...(reference_texts || [])].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(//g, '\n').replace(/<\/p>

/g, '\n\n'); + const searchableTextContent = JSDOM.fragment(searchableContent).textContent; + // @ts-expect-error + const searchableAccountContent = JSDOM.fragment([status.account?.display_name, status.account?.note].join('\n\n')).textContent; + + const now = new Date(); + const filter_results = Object.values(req.cachedFilters).reduce((results, cachedFilter) => { + // Check the filter hasn't expired before applying: + if (cachedFilter.expires_at !== null && cachedFilter.expires_at < now) { + return results; + } + + if ( + cachedFilter.filter && + cachedFilter.filter.excludeFollows && + following + ) { + return results; + } + + if ( + cachedFilter.filter && + cachedFilter.filter.excludeLocalusers && + !accountDomain + ) { + return results; + } + + // Just in-case JSDOM fails to find textContent in searchableContent + if (!searchableTextContent) { + return results; + } + + const keyword_matches = + searchableTextContent.match(cachedFilter.regexp) || + (cachedFilter.withAccountName && searchableAccountContent + ? searchableAccountContent.match(cachedFilter.regexp) + : null); + if (keyword_matches) { + // results is an Array of FilterResult; status_matches is always + // null as we only are only applying the keyword-based custom + // filters, not the status-based custom filters. + // https://docs.joinmastodon.org/entities/FilterResult/ + results.push({ + filter: cachedFilter.filter, + keyword_matches, + status_matches: null, + }); + } + + return results; + }, []); + + // Send the payload + the FilterResults as the `filtered` property + // to the streaming connection. To reach this code, the `event` must + // have been either `update` or `status.update`, meaning the + // `payload` is a Status entity, which has a `filtered` property: + // + // filtered: https://docs.joinmastodon.org/entities/Status/#filtered + transmit(event, { + ...payload, + filtered: filter_results + }); + } else { + transmit(event, payload); + } + }).catch(err => { + log.error(err); + releasePgConnection(); + }); }); }; @@ -1058,8 +1052,8 @@ const startServer = async () => { }; /** - * @param {any} req - * @param {any} res + * @param {Request} req + * @param {http.ServerResponse} res * @returns {function(string, string): void} */ const streamToHttp = (req, res) => { @@ -1105,7 +1099,7 @@ const startServer = async () => { }; /** - * @param {any} req + * @param {Request} req * @param {function(): void} [closeHandler] * @returns {function(string[], SubscriptionListener): void} */ @@ -1157,10 +1151,13 @@ const startServer = async () => { app.use(api); + // @ts-expect-error api.use(authenticationMiddleware); + // @ts-expect-error api.use(errorMiddleware); api.get('/api/v1/streaming/*', (req, res) => { + // @ts-expect-error const channelName = channelNameFromPath(req); // FIXME: In theory we'd never actually reach here due to @@ -1171,30 +1168,23 @@ const startServer = async () => { return; } - channelNameToIds(req, channelName, req.query) - .then(({ channelIds, options }) => { - const onSend = streamToHttp(req, res); - const onEnd = streamHttpEnd(req, subscriptionHeartbeat(channelIds)); + // @ts-expect-error + channelNameToIds(req, channelName, req.query).then(({ channelIds, options }) => { + // @ts-expect-error + const onSend = streamToHttp(req, res); + // @ts-expect-error + const onEnd = streamHttpEnd(req, subscriptionHeartbeat(channelIds)); - // @ts-ignore - streamFrom( - channelIds, - req, - req.log, - onSend, - onEnd, - 'eventsource', - options.needsFiltering, - ); - }) - .catch((err) => { - const { statusCode, errorMessage } = extractErrorStatusAndMessage(err); + // @ts-ignore + streamFrom(channelIds, req, req.log, onSend, onEnd, 'eventsource', options); + }).catch(err => { + const {statusCode, errorMessage } = extractErrorStatusAndMessage(err); - res.log.info({ err }, 'Eventsource subscription error'); + res.log.info({ err }, 'Eventsource subscription error'); - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ error: errorMessage })); - }); + res.writeHead(statusCode, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: errorMessage })); + }); }); /** @@ -1206,7 +1196,7 @@ const startServer = async () => { */ /** - * @param {any} req + * @param {Request} req * @returns {string[]} */ const channelsForUserStream = (req) => { @@ -1220,19 +1210,34 @@ const startServer = async () => { }; /** - * @param {any} req + * @param {Request} req * @param {string} name * @param {StreamParams} params - * @returns {Promise.<{ channelIds: string[], options: { needsFiltering: boolean } }>} + * @returns {Promise.<{ channelIds: string[], options: { needsFiltering: boolean, filterLocal?: boolean, filterRemote?: boolean } }>} */ - const channelNameToIds = (req, name, params) => - new Promise((resolve, reject) => { - switch (name) { - case 'user': - resolve({ - channelIds: channelsForUserStream(req), - options: { needsFiltering: false }, - }); + const channelNameToIds = (req, name, params) => new Promise((resolve, reject) => { + /** + * @param {string} feedKind + * @param {string} channelId + * @param {{ needsFiltering: boolean }} options + */ + const resolveFeed = (feedKind, channelId, options) => { + getFeedAccessSettings(feedKind, req).then(({ localAccess, remoteAccess }) => { + resolve({ + channelIds: [channelId], + options: { ...options, filterLocal: !localAccess, filterRemote: !remoteAccess }, + }); + }).catch(() => { + reject(new Error('Error getting feed access settings')); + }); + }; + + switch (name) { + case 'user': + resolve({ + channelIds: channelsForUserStream(req), + options: { needsFiltering: false }, + }); break; case 'user:notification': @@ -1241,118 +1246,86 @@ const startServer = async () => { options: { needsFiltering: false }, }); - break; - case 'public': - resolve({ - channelIds: ['timeline:public'], - options: { needsFiltering: true }, - }); - - break; - case 'public:local': - resolve({ - channelIds: ['timeline:public:local'], - options: { needsFiltering: true }, - }); - - break; - case 'public:remote': - resolve({ - channelIds: ['timeline:public:remote'], - options: { needsFiltering: true }, - }); - - break; - case 'public:media': - resolve({ - channelIds: ['timeline:public:media'], - options: { needsFiltering: true }, - }); + break; + case 'public': + resolveFeed('public', 'timeline:public', { needsFiltering: true }); + break; + case 'public:local': + resolveFeed('public', 'timeline:public:local', { needsFiltering: true }); + break; + case 'public:remote': + resolveFeed('public', 'timeline:public:remote', { needsFiltering: true }); + break; + case 'public:media': + resolveFeed('public', 'timeline:public:media', { needsFiltering: true }); + break; + case 'public:local:media': + resolveFeed('public', 'timeline:public:local:media', { needsFiltering: true }); + break; + case 'public:remote:media': + resolveFeed('public', 'timeline:public:remote:media', { needsFiltering: true }); + break; + case 'direct': + resolve({ + channelIds: [`timeline:direct:${req.accountId}`], + options: { needsFiltering: false }, + }); - break; - case 'public:local:media': - resolve({ - channelIds: ['timeline:public:local:media'], - options: { needsFiltering: true }, - }); + break; + case 'hashtag': + if (!params.tag) { + reject(new RequestError('Missing tag name parameter')); + return; + } - break; - case 'public:remote:media': - resolve({ - channelIds: ['timeline:public:remote:media'], - options: { needsFiltering: true }, - }); + resolveFeed('hashtag', `timeline:hashtag:${normalizeHashtag(params.tag)}`, { needsFiltering: true }); - break; - case 'direct': - resolve({ - channelIds: [`timeline:direct:${req.accountId}`], - options: { needsFiltering: false }, - }); + break; + case 'hashtag:local': + if (!params.tag) { + reject(new RequestError('Missing tag name parameter')); + return; + } - break; - case 'hashtag': - if (!params.tag) { - reject(new RequestError('Missing tag name parameter')); - } else { - resolve({ - channelIds: [`timeline:hashtag:${normalizeHashtag(params.tag)}`], - options: { needsFiltering: true }, - }); - } + resolveFeed('hashtag', `timeline:hashtag:${normalizeHashtag(params.tag)}:local`, { needsFiltering: true }); - break; - case 'hashtag:local': - if (!params.tag) { - reject(new RequestError('Missing tag name parameter')); - } else { - resolve({ - channelIds: [ - `timeline:hashtag:${normalizeHashtag(params.tag)}:local`, - ], - options: { needsFiltering: true }, - }); - } + break; + case 'list': + if (!params.list) { + reject(new RequestError('Missing list name parameter')); + return; + } - break; - case 'list': - if (!params.list) { - reject(new RequestError('Missing list name parameter')); - return; - } + authorizeListAccess(params.list, req).then(() => { + resolve({ + channelIds: [`timeline:list:${params.list}`], + options: { needsFiltering: false }, + }); + }).catch(() => { + reject(new AuthenticationError('Not authorized to stream this list')); + }); - authorizeListAccess(params.list, req) - .then(() => { - resolve({ - channelIds: [`timeline:list:${params.list}`], - options: { needsFiltering: false }, - }); - }) - .catch(() => { - reject( - new AuthenticationError('Not authorized to stream this list'), - ); - }); + break; + case 'antenna': + if (!params.antenna) { + reject(new RequestError('Missing antenna name parameter')); + return; + } - break; - case 'antenna': - // @ts-ignore - authorizeAntennaAccess(params.antenna, req) - .then(() => { - resolve({ - channelIds: [`timeline:antenna:${params.antenna}`], - options: { needsFiltering: false }, - }); - }) - .catch(() => { - reject('Not authorized to stream this antenna'); - }); + authorizeAntennaAccess(params.antenna, req).then(() => { + resolve({ + channelIds: [`timeline:antenna:${params.antenna}`], + options: { needsFiltering: false }, + }); + }).catch(() => { + reject(new AuthenticationError('Not authorized to stream this antenna')); + }); - break; - default: - reject(new RequestError('Unknown stream type')); - } - }); + break; + default: + reject(new RequestError('Unknown stream type')); + } + }); /** * @param {string} channelName @@ -1377,7 +1350,7 @@ const startServer = async () => { /** * @typedef WebSocketSession * @property {import('ws').WebSocket & { isAlive: boolean}} websocket - * @property {http.IncomingMessage & ResolvedAccount} request + * @property {Request} request * @property {import('pino').Logger} logger * @property {Object.} subscriptions */ @@ -1400,21 +1373,9 @@ const startServer = async () => { return; } - const onSend = streamToWs( - request, - websocket, - streamNameFromChannelName(channelName, params), - ); - const stopHeartbeat = subscriptionHeartbeat(channelIds); - const listener = streamFrom( - channelIds, - request, - logger, - onSend, - undefined, - 'websocket', - options.needsFiltering, - ); + const onSend = streamToWs(request, websocket, streamNameFromChannelName(channelName, params)); + const stopHeartbeat = subscriptionHeartbeat(channelIds); + const listener = streamFrom(channelIds, request, logger, onSend, undefined, 'websocket', options); metrics.connectedChannels .labels({ type: 'websocket', channel: channelName }) @@ -1536,7 +1497,7 @@ const startServer = async () => { /** * @param {import('ws').WebSocket & { isAlive: boolean }} ws - * @param {http.IncomingMessage & ResolvedAccount} req + * @param {Request} req * @param {import('pino').Logger} log */ function onConnection(ws, req, log) { @@ -1606,9 +1567,19 @@ const startServer = async () => { const { type, stream, ...params } = json; if (type === 'subscribe') { - subscribeWebsocketToChannel(session, firstParam(stream), params); + subscribeWebsocketToChannel( + session, + // @ts-expect-error + firstParam(stream), + params + ); } else if (type === 'unsubscribe') { - unsubscribeWebsocketFromChannel(session, firstParam(stream), params); + unsubscribeWebsocketFromChannel( + session, + // @ts-expect-error + firstParam(stream), + params + ); } else { // Unknown action type } @@ -1631,14 +1602,14 @@ const startServer = async () => { wss.on('connection', onConnection); setInterval(() => { - wss.clients.forEach((ws) => { - // @ts-ignore + wss.clients.forEach(ws => { + // @ts-expect-error if (ws.isAlive === false) { ws.terminate(); return; } - // @ts-ignore + // @ts-expect-error ws.isAlive = false; ws.ping('', false); }); @@ -1668,14 +1639,16 @@ const startServer = async () => { }; /** - * @param {any} server + * @param {http.Server} server * @param {function(string): void} [onSuccess] */ const attachServerWithConfig = (server, onSuccess) => { if (process.env.SOCKET) { server.listen(process.env.SOCKET, () => { if (onSuccess) { + // @ts-expect-error fs.chmodSync(server.address(), 0o666); + // @ts-expect-error onSuccess(server.address()); } }); @@ -1690,6 +1663,7 @@ const attachServerWithConfig = (server, onSuccess) => { server.listen(port, bind, () => { if (onSuccess) { + // @ts-expect-error onSuccess(`${server.address().address}:${server.address().port}`); } }); diff --git a/streaming/logging.js b/streaming/logging.js index e1c552c22ed8f1..61946b622c3756 100644 --- a/streaming/logging.js +++ b/streaming/logging.js @@ -100,7 +100,7 @@ export function createWebsocketLogger(request, resolvedAccount) { /** * Initializes the log level based on the environment - * @param {Object} env + * @param {Object} env * @param {string} environment */ export function initializeLogLevel(env, environment) { diff --git a/streaming/redis.js b/streaming/redis.js index 6c2559dbdac798..a69329d50ead72 100644 --- a/streaming/redis.js +++ b/streaming/redis.js @@ -6,6 +6,7 @@ import { parseIntFromEnvValue } from './utils.js'; * @typedef RedisConfiguration * @property {string|undefined} url * @property {import('ioredis').RedisOptions} options + * @property {string|undefined} namespace */ /** diff --git a/streaming/utils.js b/streaming/utils.js index 47c63dd4caed7a..dd5e82c67cab92 100644 --- a/streaming/utils.js +++ b/streaming/utils.js @@ -13,11 +13,15 @@ const FALSE_VALUES = [ ]; /** - * @param {any} value + * @typedef {typeof FALSE_VALUES[number]} FalseValue + */ + +/** + * @param {unknown} value * @returns {boolean} */ export function isTruthy(value) { - return value && !FALSE_VALUES.includes(value); + return !!value && !FALSE_VALUES.includes(/** @type {FalseValue} */ (value)); } /** diff --git a/vite.config.mts b/vite.config.mts index 6876302dd90f85..30c0741aaa6d54 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -73,7 +73,6 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { port: 3036, }, build: { - target: 'modules', commonjsOptions: { transformMixedEsModules: true }, chunkSizeWarningLimit: 1 * 1024 * 1024, // 1MB sourcemap: true, diff --git a/yarn.lock b/yarn.lock index a243be793029ae..c6c9a2b57fc384 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2067,16 +2067,16 @@ __metadata: languageName: node linkType: hard -"@es-joy/jsdoccomment@npm:~0.52.0": - version: 0.52.0 - resolution: "@es-joy/jsdoccomment@npm:0.52.0" +"@es-joy/jsdoccomment@npm:~0.71.0": + version: 0.71.0 + resolution: "@es-joy/jsdoccomment@npm:0.71.0" dependencies: "@types/estree": "npm:^1.0.8" - "@typescript-eslint/types": "npm:^8.34.1" + "@typescript-eslint/types": "npm:^8.46.0" comment-parser: "npm:1.4.1" esquery: "npm:^1.6.0" - jsdoc-type-pratt-parser: "npm:~4.1.0" - checksum: 10c0/4def78060ef58859f31757b9d30c4939fc33e7d9ee85637a7f568c1d209c33aa0abd2cf5a3a4f3662ec5b12b85ecff2f2035d809dc93b9382a31a6dfb200d83c + jsdoc-type-pratt-parser: "npm:~6.6.0" + checksum: 10c0/fe64b729c18238c7e83f8fab30eab8ce97da6565adbb963011463f9abedef5393972ac1eeebd04b17b189e94bc389274dcb8f707023e96fd922d12dc608b5409 languageName: node linkType: hard @@ -2871,7 +2871,7 @@ __metadata: eslint-import-resolver-typescript: "npm:^4.2.5" eslint-plugin-formatjs: "npm:^5.3.1" eslint-plugin-import: "npm:~2.32.0" - eslint-plugin-jsdoc: "npm:^54.0.0" + eslint-plugin-jsdoc: "npm:^60.0.0" eslint-plugin-jsx-a11y: "npm:~6.10.2" eslint-plugin-promise: "npm:~7.2.1" eslint-plugin-react: "npm:^7.37.4" @@ -3278,6 +3278,13 @@ __metadata: languageName: node linkType: hard +"@pinojs/redact@npm:^0.4.0": + version: 0.4.0 + resolution: "@pinojs/redact@npm:0.4.0" + checksum: 10c0/4b311ba17ee0cf154ff9c39eb063ec04cd0d0017cb3750efcdf06c2d485df3e1095e13e872175993568c5568c23e4508dd877c981bbc9c5ae5e384d569efcdff + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3373,8 +3380,8 @@ __metadata: linkType: hard "@reduxjs/toolkit@npm:^2.0.1": - version: 2.9.1 - resolution: "@reduxjs/toolkit@npm:2.9.1" + version: 2.9.2 + resolution: "@reduxjs/toolkit@npm:2.9.2" dependencies: "@standard-schema/spec": "npm:^1.0.0" "@standard-schema/utils": "npm:^0.3.0" @@ -3390,7 +3397,7 @@ __metadata: optional: true react-redux: optional: true - checksum: 10c0/11e99b665560c7e4bda80d26ad1308866282156bc177500558d72888d18819c303ebebf1f96121552facde3d6bd9c114b0e1f5c41e618c9ce0eaf464518f39dc + checksum: 10c0/577416200c76ffd82bce6158aaeb63e836ed2c2a14e670253056dcaec505da77643e79b47208b4e493a0c120a4a2bc049efe60cd555a2699053af5b03f2f2953 languageName: node linkType: hard @@ -3405,10 +3412,10 @@ __metadata: languageName: node linkType: hard -"@rolldown/pluginutils@npm:1.0.0-beta.38": - version: 1.0.0-beta.38 - resolution: "@rolldown/pluginutils@npm:1.0.0-beta.38" - checksum: 10c0/8353ec2528349f79e27d1a3193806725b85830da334e935cbb606d88c1177c58ea6519c578e4e93e5f677f5b22aecb8738894dbed14603e14b6bffe3facf1002 +"@rolldown/pluginutils@npm:1.0.0-beta.43": + version: 1.0.0-beta.43 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.43" + checksum: 10c0/1c17a0b16c277a0fdbab080fd22ef91e37c1f0d710ecfdacb6a080068062eb14ff030d0e9d2ec2325a1d4246dba0c49625755c82c0090f6cbf98d16e80183e02 languageName: node linkType: hard @@ -4664,13 +4671,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.45.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.45.0": +"@typescript-eslint/types@npm:8.45.0, @typescript-eslint/types@npm:^8.45.0": version: 8.45.0 resolution: "@typescript-eslint/types@npm:8.45.0" checksum: 10c0/0213a0573c671d13bc91961a2b2e814ec7f6381ff093bce6704017bd96b2fc7fee25906c815cedb32a0601cf5071ca6c7c5f940d087c3b0d3dd7d4bc03478278 languageName: node linkType: hard +"@typescript-eslint/types@npm:^8.46.0": + version: 8.46.1 + resolution: "@typescript-eslint/types@npm:8.46.1" + checksum: 10c0/90887acaa5b33b45af20cf7f87ec4ae098c0daa88484245473e73903fa6e542f613247c22148132167891ca06af6549a60b9d2fd14a65b22871e016901ce3756 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.45.0": version: 8.45.0 resolution: "@typescript-eslint/typescript-estree@npm:8.45.0" @@ -4893,18 +4907,18 @@ __metadata: linkType: hard "@vitejs/plugin-react@npm:^5.0.0": - version: 5.0.4 - resolution: "@vitejs/plugin-react@npm:5.0.4" + version: 5.1.0 + resolution: "@vitejs/plugin-react@npm:5.1.0" dependencies: "@babel/core": "npm:^7.28.4" "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" - "@rolldown/pluginutils": "npm:1.0.0-beta.38" + "@rolldown/pluginutils": "npm:1.0.0-beta.43" "@types/babel__core": "npm:^7.20.5" - react-refresh: "npm:^0.17.0" + react-refresh: "npm:^0.18.0" peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - checksum: 10c0/bb9360a4b4c0abf064d22211756b999faf23889ac150de490590ca7bd029b0ef7f4cd8ba3a32b86682a62d46fb7bebd75b3fa9835c57c78123f4a646de2e0136 + checksum: 10c0/e192a12e2b854df109eafb1d06c0bc848e8e2b162c686aa6b999b1048658983e72674b2068ccc37562fcce44d32ad92b65f3a4e1897a0cb7859c2ee69cc63eac languageName: node linkType: hard @@ -5464,13 +5478,13 @@ __metadata: linkType: hard "axios@npm:^1.4.0": - version: 1.12.2 - resolution: "axios@npm:1.12.2" + version: 1.13.0 + resolution: "axios@npm:1.13.0" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.4" proxy-from-env: "npm:^1.1.0" - checksum: 10c0/80b063e318cf05cd33a4d991cea0162f3573481946f9129efb7766f38fde4c061c34f41a93a9f9521f02b7c9565ccbc197c099b0186543ac84a24580017adfed + checksum: 10c0/2af09f8ad9db9565bf97055eb0ddd2fd4abd9a03d23157b409348c9589370a88c3ede02e11fd1268becb780a77b62bdf9488650dd7208eda57edceca1d65622e languageName: node linkType: hard @@ -6375,7 +6389,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.1": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -7142,23 +7156,25 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jsdoc@npm:^54.0.0": - version: 54.0.0 - resolution: "eslint-plugin-jsdoc@npm:54.0.0" +"eslint-plugin-jsdoc@npm:^60.0.0": + version: 60.8.3 + resolution: "eslint-plugin-jsdoc@npm:60.8.3" dependencies: - "@es-joy/jsdoccomment": "npm:~0.52.0" + "@es-joy/jsdoccomment": "npm:~0.71.0" are-docs-informative: "npm:^0.0.2" comment-parser: "npm:1.4.1" - debug: "npm:^4.4.1" + debug: "npm:^4.4.3" escape-string-regexp: "npm:^4.0.0" espree: "npm:^10.4.0" esquery: "npm:^1.6.0" + html-entities: "npm:^2.6.0" + object-deep-merge: "npm:^1.0.5" parse-imports-exports: "npm:^0.2.4" semver: "npm:^7.7.2" spdx-expression-parse: "npm:^4.0.0" peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10c0/cf0a388fc670ababe26f9584c467bc8c1592aa83affcf16118d8181c186a6d8f02a8ea65250766b45168fca5cb879a6af66e8457cdb98f0f923bd927572e2de5 + checksum: 10c0/2c5aa623a3e5f7410b36464df759ae5e7265ba6f9aaf67f7c16f9033c4a699532a3de702afe5bd6132717a61196be44aff170db36b71600278800770a9cd88ab languageName: node linkType: hard @@ -8166,6 +8182,13 @@ __metadata: languageName: node linkType: hard +"html-entities@npm:^2.6.0": + version: 2.6.0 + resolution: "html-entities@npm:2.6.0" + checksum: 10c0/7c8b15d9ea0cd00dc9279f61bab002ba6ca8a7a0f3c36ed2db3530a67a9621c017830d1d2c1c65beb9b8e3436ea663e9cf8b230472e0e413359399413b27c8b7 + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -8405,8 +8428,8 @@ __metadata: linkType: hard "ioredis@npm:^5.3.2": - version: 5.8.1 - resolution: "ioredis@npm:5.8.1" + version: 5.8.2 + resolution: "ioredis@npm:5.8.2" dependencies: "@ioredis/commands": "npm:1.4.0" cluster-key-slot: "npm:^1.1.0" @@ -8417,7 +8440,7 @@ __metadata: redis-errors: "npm:^1.2.0" redis-parser: "npm:^3.0.0" standard-as-callback: "npm:^2.1.0" - checksum: 10c0/4ed66444017150da027bce940a24bf726994691e2a7b3aa11d52f8aeb37f258068cc171af4d9c61247acafc28eb086fa8a7c79420b8e8d2907d2f74f39584465 + checksum: 10c0/305e385f811d49908899e32c2de69616cd059f909afd9e0a53e54f596b1a5835ee3449bfc6a3c49afbc5a2fd27990059e316cc78f449c94024957bd34c826d88 languageName: node linkType: hard @@ -8915,10 +8938,10 @@ __metadata: languageName: node linkType: hard -"jsdoc-type-pratt-parser@npm:~4.1.0": - version: 4.1.0 - resolution: "jsdoc-type-pratt-parser@npm:4.1.0" - checksum: 10c0/7700372d2e733a32f7ea0a1df9cec6752321a5345c11a91b2ab478a031a426e934f16d5c1f15c8566c7b2c10af9f27892a29c2c789039f595470e929a4aa60ea +"jsdoc-type-pratt-parser@npm:~6.6.0": + version: 6.6.0 + resolution: "jsdoc-type-pratt-parser@npm:6.6.0" + checksum: 10c0/3cb9c28a945a66a925ebe40fd752113af01e655a0a0fedc6b1702e23c8f9ed187c45caf6cf94f009bde6cf5c98562524aa7a74ebb4571fca6d3ee5bef0344ec1 languageName: node linkType: hard @@ -9874,6 +9897,15 @@ __metadata: languageName: node linkType: hard +"object-deep-merge@npm:^1.0.5": + version: 1.0.5 + resolution: "object-deep-merge@npm:1.0.5" + dependencies: + type-fest: "npm:4.2.0" + checksum: 10c0/6664ecb43a2519c9b101f1c3b130dfc73e108d86ec06fbe7261505e1522cf8b69b10dd53b8cbb4cde35cca9d44d349667e2404f06fff85cf9f50b825bb6d1839 + languageName: node + linkType: hard + "object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": version: 1.13.4 resolution: "object-inspect@npm:1.13.4" @@ -10404,9 +10436,10 @@ __metadata: linkType: hard "pino@npm:^9.0.0": - version: 9.13.1 - resolution: "pino@npm:9.13.1" + version: 9.14.0 + resolution: "pino@npm:9.14.0" dependencies: + "@pinojs/redact": "npm:^0.4.0" atomic-sleep: "npm:^1.0.0" on-exit-leak-free: "npm:^2.1.0" pino-abstract-transport: "npm:^2.0.0" @@ -10415,12 +10448,11 @@ __metadata: quick-format-unescaped: "npm:^4.0.3" real-require: "npm:^0.2.0" safe-stable-stringify: "npm:^2.3.1" - slow-redact: "npm:^0.3.0" sonic-boom: "npm:^4.0.1" thread-stream: "npm:^3.0.0" bin: pino: bin.js - checksum: 10c0/c99e879f9538f7255488ad276a46a857cf9114217b754b850b7f1441e31b724a6d6f0697228ead954d3d9601522704e03cad5d441c228108073eed2f37ea0e41 + checksum: 10c0/9a10d9bf820a585eae9bc270fb4e55c895e48280d54adbbb4063ec061694b22d8809c80203cf5fe9f920a54c832b0b8dfb67cb28a04baa13abebaf261a9c9f3e languageName: node linkType: hard @@ -11309,10 +11341,10 @@ __metadata: languageName: node linkType: hard -"react-refresh@npm:^0.17.0": - version: 0.17.0 - resolution: "react-refresh@npm:0.17.0" - checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c +"react-refresh@npm:^0.18.0": + version: 0.18.0 + resolution: "react-refresh@npm:0.18.0" + checksum: 10c0/34a262f7fd803433a534f50deb27a148112a81adcae440c7d1cbae7ef14d21ea8f2b3d783e858cb7698968183b77755a38b4d4b5b1d79b4f4689c2f6d358fff2 languageName: node linkType: hard @@ -12345,13 +12377,6 @@ __metadata: languageName: node linkType: hard -"slow-redact@npm:^0.3.0": - version: 0.3.0 - resolution: "slow-redact@npm:0.3.0" - checksum: 10c0/bb2f77830f64fb01079849e0c6433c15e782b88cccb82d4b0d62ce216307cf514ea3f92e9b2c6ae1b1d613ac7743305d5f0324e94c9dc8e41908939456248f9a - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -13401,6 +13426,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:4.2.0": + version: 4.2.0 + resolution: "type-fest@npm:4.2.0" + checksum: 10c0/75e0c112ae91d3b68c75da9b7563cf393f91ebdfca5d53d0b3f0405690217eadca318f9ddb89d58ee6ed67b8e32d23a4eae2aabc4e351e5ae184d610247bf772 + languageName: node + linkType: hard + "type-fest@npm:^0.16.0": version: 0.16.0 resolution: "type-fest@npm:0.16.0" @@ -13942,8 +13974,8 @@ __metadata: linkType: hard "vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0, vite@npm:^7.1.1": - version: 7.1.11 - resolution: "vite@npm:7.1.11" + version: 7.1.12 + resolution: "vite@npm:7.1.12" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.5.0" @@ -13992,7 +14024,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/c4aa7f47b1fb07f734ed6f4f605d73e5acf7ff9754d75b4adbfbdddf0e520413019834620c1f7b4a207bce7e1d20a2636c584db2b1b17f5a3ba2cd23d47e50ab + checksum: 10c0/cef4d4b4a84e663e09b858964af36e916892ac8540068df42a05ced637ceeae5e9ef71c72d54f3cfc1f3c254af16634230e221b6e2327c2a66d794bb49203262 languageName: node linkType: hard