diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 2bffaaf27ba..678a256b748 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -5,7 +5,6 @@ 'customManagers:dockerfileVersions', ':labels(dependencies)', ':prConcurrentLimitNone', // Remove limit for open PRs at any time. - ':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour. ':enableVulnerabilityAlertsWithLabel(security)', ], rebaseWhen: 'conflicted', diff --git a/.prettierignore b/.prettierignore index 098dac67177..bd382506695 100644 --- a/.prettierignore +++ b/.prettierignore @@ -68,6 +68,7 @@ docker-compose.override.yml # Ignore vendored CSS reset app/javascript/styles/mastodon/reset.scss +app/javascript/styles_new/mastodon/reset.scss # Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631 *.js diff --git a/.storybook/static/mockServiceWorker.js b/.storybook/static/mockServiceWorker.js index 15623f1090b..02115fb4d99 100644 --- a/.storybook/static/mockServiceWorker.js +++ b/.storybook/static/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.11.3' +const PACKAGE_VERSION = '2.12.1' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() @@ -205,6 +205,7 @@ async function resolveMainClient(event) { * @param {FetchEvent} event * @param {Client | undefined} client * @param {string} requestId + * @param {number} requestInterceptedAt * @returns {Promise} */ async function getResponse(event, client, requestId, requestInterceptedAt) { diff --git a/CHANGELOG.md b/CHANGELOG.md index f30b502ad14..45bb26b5140 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,42 @@ All notable changes to this project will be documented in this file. +## [4.5.2] - 2025-11-20 + +### Changed + +- Change private quote education modal to not show up on self-quotes (#36926 by @ClearlyClaire) + +### Fixed + +- Fix missing fallback link in CW-only quote posts (#36963 by @ClearlyClaire) +- Fix statuses without text being hidden while loading (#36962 by @ClearlyClaire) +- Fix `g` + `h` keyboard shortcut not working when a post is focused (#36935 by @diondiondion) +- Fix quoting overwriting current content warning (#36934 by @ClearlyClaire) +- Fix scroll-to-status in threaded view being unreliable (#36927 by @ClearlyClaire) +- Fix path resolution for emoji worker (#36897 by @ChaosExAnima) +- Fix `tootctl upgrade storage-schema` failing with `ArgumentError` (#36914 by @shugo) +- Fix cross-origin handling of CSS modules (#36890 by @ClearlyClaire) +- Fix error with remote tags including percent signs (#36886 and #36925 by @ChaosExAnima and @ClearlyClaire) +- Fix bogus quote approval policy not always being replaced correctly (#36885 by @ClearlyClaire) +- Fix hashtag completion not being inserted correctly (#36884 by @ClearlyClaire) +- Fix Cmd/Ctrl + Enter in the composer triggering confirmation dialog action (#36870 by @diondiondion) + +## [4.5.1] - 2025-11-13 + +### Fixed + +- Fix Cmd/Ctrl + Enter not submitting Alt text modal on some browsers (#36866 by @diondiondion) +- Fix posts coming from public/hashtag streaming being marked as unquotable (#36860 and #36869 by @ClearlyClaire) +- Fix old previously-undiscovered posts being treated as new when receiving an `Update` (#36848 by @ClearlyClaire) +- Fix blank screen in browsers that don't support `Intl.DisplayNames` (#36847 by @diondiondion) +- Fix filters not being applied to quotes in detailed view (#36843 by @ClearlyClaire) +- Fix scroll shift caused by fetch-all-replies alerts (#36807 by @diondiondion) +- Fix dropdown menu not focusing first item when opened via keyboard (#36804 by @diondiondion) +- Fix assets build issue on arch64 (#36781 by @ClearlyClaire) +- Fix `/api/v1/statuses/:id/context` sometimes returing `Mastodon-Async-Refresh` without `result_count` (#36779 by @ClearlyClaire) +- Fix prepared quote not being discarded with contents when replying (#36778 by @ClearlyClaire) + ## [4.5.0] - 2025-11-06 ### Added diff --git a/Gemfile b/Gemfile index e54842fcda0..599686d5bae 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'ruby-vips', '~> 2.2', require: false gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.8' -gem 'bootsnap', '~> 1.18.0', require: false +gem 'bootsnap', '~> 1.19.0', require: false gem 'browser' gem 'charlock_holmes', '~> 0.7.7' gem 'chewy', '~> 7.3' diff --git a/Gemfile.lock b/Gemfile.lock index 251cf771e7c..19dfbfa5dd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,8 +96,8 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1180.0) - aws-sdk-core (3.236.0) + aws-partitions (1.1186.0) + aws-sdk-core (3.239.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -105,10 +105,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.116.0) + aws-sdk-kms (1.117.0) aws-sdk-core (~> 3, >= 3.234.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.203.0) + aws-sdk-s3 (1.205.0) aws-sdk-core (~> 3, >= 3.234.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -129,7 +129,7 @@ GEM binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) blurhash (0.1.8) - bootsnap (1.18.6) + bootsnap (1.19.0) msgpack (~> 1.2) brakeman (7.1.1) racc @@ -182,7 +182,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0) database_cleaner-core (2.0.1) - date (3.4.1) + date (3.5.0) debug (1.11.0) irb (~> 1.10) reline (>= 0.3.8) @@ -324,13 +324,14 @@ GEM rainbow (>= 2.0.0) i18n (1.14.7) concurrent-ruby (~> 1.0) - i18n-tasks (1.0.15) + i18n-tasks (1.1.1) activesupport (>= 4.0.2) ast (>= 2.1.0) erubi - highline (>= 2.0.0) + highline (>= 3.0.0) i18n parser (>= 3.2.2.1) + prism rails-i18n rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.8, >= 1.8.1) @@ -349,7 +350,7 @@ GEM azure-blob (~> 0.5.2) hashie (~> 5.0) jmespath (1.6.2) - json (2.15.2) + json (2.16.0) json-canonicalization (1.0.0) json-jwt (1.17.0) activesupport (>= 4.2) @@ -446,7 +447,7 @@ GEM mime-types-data (3.2025.0924) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.26.0) + minitest (5.26.2) msgpack (1.8.0) multi_json (1.17.0) mutex_m (0.3.0) @@ -608,7 +609,7 @@ GEM premailer (~> 1.7, >= 1.7.9) prettyprint (0.2.0) prism (1.6.0) - prometheus_exporter (2.3.0) + prometheus_exporter (2.3.1) webrick propshaft (1.3.1) actionpack (>= 7.0.0) @@ -684,7 +685,7 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) + rake (13.3.1) rdf (3.3.4) bcp47_spec (~> 0.2) bigdecimal (~> 3.1, >= 3.1.5) @@ -705,7 +706,7 @@ GEM redis-client (0.26.1) connection_pool regexp_parser (2.11.3) - reline (0.6.2) + reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) @@ -759,7 +760,7 @@ GEM rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.47.1) + rubocop-ast (1.48.0) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-capybara (2.22.1) @@ -778,10 +779,10 @@ GEM rack (>= 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.44.0, < 2.0) - rubocop-rspec (3.7.0) + rubocop-rspec (3.8.0) lint_roller (~> 1.1) - rubocop (~> 1.72, >= 1.72.1) - rubocop-rspec_rails (2.31.0) + rubocop (~> 1.81) + rubocop-rspec_rails (2.32.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-rspec (~> 3.5) @@ -838,9 +839,9 @@ GEM stackprof (0.2.27) starry (0.2.0) base64 - stoplight (5.5.0) + stoplight (5.6.0) zeitwerk - stringio (3.1.7) + stringio (3.1.8) strong_migrations (2.5.1) activerecord (>= 7.1) swd (2.0.3) @@ -941,7 +942,7 @@ DEPENDENCIES better_errors (~> 2.9) binding_of_caller (~> 1.0) blurhash (~> 0.1) - bootsnap (~> 1.18.0) + bootsnap (~> 1.19.0) brakeman (~> 7.0) browser bundler-audit (~> 0.9) diff --git a/app/controllers/admin/site_uploads_controller.rb b/app/controllers/admin/site_uploads_controller.rb index 96e61cf6bbc..e30c783a493 100644 --- a/app/controllers/admin/site_uploads_controller.rb +++ b/app/controllers/admin/site_uploads_controller.rb @@ -9,7 +9,7 @@ module Admin @site_upload.destroy! - redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg') + redirect_back_or_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg') end private diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb index ddb6b79c866..dbf56f45a04 100644 --- a/app/helpers/languages_helper.rb +++ b/app/helpers/languages_helper.rb @@ -35,7 +35,7 @@ module LanguagesHelper cy: ['Welsh', 'Cymraeg'].freeze, da: ['Danish', 'dansk'].freeze, de: ['German', 'Deutsch'].freeze, - dv: ['Divehi', 'Dhivehi'].freeze, + dv: ['Divehi', 'ދިވެހި'].freeze, dz: ['Dzongkha', 'རྫོང་ཁ'].freeze, ee: ['Ewe', 'Eʋegbe'].freeze, el: ['Greek', 'Ελληνικά'].freeze, @@ -100,7 +100,7 @@ module LanguagesHelper lo: ['Lao', 'ລາວ'].freeze, lt: ['Lithuanian', 'lietuvių kalba'].freeze, lu: ['Luba-Katanga', 'Tshiluba'].freeze, - lv: ['Latvian', 'latviešu valoda'].freeze, + lv: ['Latvian', 'Latviski'].freeze, mg: ['Malagasy', 'fiteny malagasy'].freeze, mh: ['Marshallese', 'Kajin M̧ajeļ'].freeze, mi: ['Māori', 'te reo Māori'].freeze, diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index 00b4a6d2b3f..32734b93af9 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -4,11 +4,11 @@ module ThemeHelper def theme_style_tags(theme) if theme == 'system' ''.html_safe.tap do |tags| - tags << vite_stylesheet_tag('themes/mastodon-light', type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') - tags << vite_stylesheet_tag('themes/default', type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag(theme_path_for('mastodon-light'), type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag(theme_path_for('default'), type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') end else - vite_stylesheet_tag "themes/#{theme}", type: :virtual, media: 'all', crossorigin: 'anonymous' + vite_stylesheet_tag theme_path_for(theme), type: :virtual, media: 'all', crossorigin: 'anonymous' end end @@ -53,4 +53,8 @@ module ThemeHelper def theme_color_for(theme) theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark] end + + def theme_path_for(theme) + Mastodon::Feature.theme_tokens_enabled? ? "themes/#{theme}_theme_tokens" : "themes/#{theme}" + end end diff --git a/app/inputs/date_of_birth_input.rb b/app/inputs/date_of_birth_input.rb index 131234b02eb..37f48133b30 100644 --- a/app/inputs/date_of_birth_input.rb +++ b/app/inputs/date_of_birth_input.rb @@ -2,25 +2,25 @@ class DateOfBirthInput < SimpleForm::Inputs::Base OPTIONS = [ - { autocomplete: 'bday-day', maxlength: 2, pattern: '[0-9]+', placeholder: 'DD' }.freeze, - { autocomplete: 'bday-month', maxlength: 2, pattern: '[0-9]+', placeholder: 'MM' }.freeze, { autocomplete: 'bday-year', maxlength: 4, pattern: '[0-9]+', placeholder: 'YYYY' }.freeze, + { autocomplete: 'bday-month', maxlength: 2, pattern: '[0-9]+', placeholder: 'MM' }.freeze, + { autocomplete: 'bday-day', maxlength: 2, pattern: '[0-9]+', placeholder: 'DD' }.freeze, ].freeze def input(wrapper_options = nil) merged_input_options = merge_wrapper_options(input_html_options, wrapper_options) merged_input_options[:inputmode] = 'numeric' - values = (object.public_send(attribute_name) || '').split('.') + values = (object.public_send(attribute_name) || '').to_s.split('-') - safe_join(Array.new(3) do |index| + safe_join(2.downto(0).map do |index| options = merged_input_options.merge(OPTIONS[index]).merge id: generate_id(index), 'aria-label': I18n.t("simple_form.labels.user.date_of_birth_#{index + 1}i"), value: values[index] @builder.text_field("#{attribute_name}(#{index + 1}i)", options) end) end def label_target - "#{attribute_name}_1i" + "#{attribute_name}_3i" end private diff --git a/app/javascript/entrypoints/admin.tsx b/app/javascript/entrypoints/admin.tsx index af9309d342c..a2c53d472b2 100644 --- a/app/javascript/entrypoints/admin.tsx +++ b/app/javascript/entrypoints/admin.tsx @@ -1,7 +1,7 @@ import { createRoot } from 'react-dom/client'; -import Rails from '@rails/ujs'; import { decode, ValidationError } from 'blurhash'; +import { on } from 'delegated-events'; import ready from '../mastodon/ready'; @@ -24,10 +24,9 @@ const setAnnouncementEndsAttributes = (target: HTMLInputElement) => { } }; -Rails.delegate( - document, - 'input[type="datetime-local"]#announcement_starts_at', +on( 'change', + 'input[type="datetime-local"]#announcement_starts_at', ({ target }) => { if (target instanceof HTMLInputElement) setAnnouncementEndsAttributes(target); @@ -63,7 +62,7 @@ const hideSelectAll = () => { if (hiddenField) hiddenField.value = '0'; }; -Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { +on('change', '#batch_checkbox_all', ({ target }) => { if (!(target instanceof HTMLInputElement)) return; const selectAllMatchingElement = document.querySelector( @@ -85,7 +84,7 @@ Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { } }); -Rails.delegate(document, '.batch-table__select-all button', 'click', () => { +on('click', '.batch-table__select-all button', () => { const hiddenField = document.querySelector( '#select_all_matching', ); @@ -113,7 +112,7 @@ Rails.delegate(document, '.batch-table__select-all button', 'click', () => { } }); -Rails.delegate(document, batchCheckboxClassName, 'change', () => { +on('change', batchCheckboxClassName, () => { const checkAllElement = document.querySelector( 'input#batch_checkbox_all', ); @@ -140,14 +139,9 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => { } }); -Rails.delegate( - document, - '.filter-subset--with-select select', - 'change', - ({ target }) => { - if (target instanceof HTMLSelectElement) target.form?.submit(); - }, -); +on('change', '.filter-subset--with-select select', ({ target }) => { + if (target instanceof HTMLSelectElement) target.form?.submit(); +}); const onDomainBlockSeverityChange = (target: HTMLSelectElement) => { const rejectMediaDiv = document.querySelector( @@ -168,11 +162,11 @@ const onDomainBlockSeverityChange = (target: HTMLSelectElement) => { } }; -Rails.delegate(document, '#domain_block_severity', 'change', ({ target }) => { +on('change', '#domain_block_severity', ({ target }) => { if (target instanceof HTMLSelectElement) onDomainBlockSeverityChange(target); }); -const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => { +function onEnableBootstrapTimelineAccountsChange(target: HTMLInputElement) { const bootstrapTimelineAccountsField = document.querySelector( '#form_admin_settings_bootstrap_timeline_accounts', @@ -194,12 +188,11 @@ const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => { ); } } -}; +} -Rails.delegate( - document, - '#form_admin_settings_enable_bootstrap_timeline_accounts', +on( 'change', + '#form_admin_settings_enable_bootstrap_timeline_accounts', ({ target }) => { if (target instanceof HTMLInputElement) onEnableBootstrapTimelineAccountsChange(target); @@ -239,11 +232,11 @@ const onChangeRegistrationMode = (target: HTMLSelectElement) => { }); }; -const convertUTCDateTimeToLocal = (value: string) => { +function convertUTCDateTimeToLocal(value: string) { const date = new Date(value + 'Z'); const twoChars = (x: number) => x.toString().padStart(2, '0'); return `${date.getFullYear()}-${twoChars(date.getMonth() + 1)}-${twoChars(date.getDate())}T${twoChars(date.getHours())}:${twoChars(date.getMinutes())}`; -}; +} function convertLocalDatetimeToUTC(value: string) { const date = new Date(value); @@ -251,14 +244,9 @@ function convertLocalDatetimeToUTC(value: string) { return fullISO8601.slice(0, fullISO8601.indexOf('T') + 6); } -Rails.delegate( - document, - '#form_admin_settings_registrations_mode', - 'change', - ({ target }) => { - if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target); - }, -); +on('change', '#form_admin_settings_registrations_mode', ({ target }) => { + if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target); +}); async function mountReactComponent(element: Element) { const componentName = element.getAttribute('data-admin-component'); @@ -305,7 +293,7 @@ ready(() => { if (registrationMode) onChangeRegistrationMode(registrationMode); const checkAllElement = document.querySelector( - 'input#batch_checkbox_all', + '#batch_checkbox_all', ); if (checkAllElement) { const allCheckboxes = Array.from( @@ -318,7 +306,7 @@ ready(() => { } document - .querySelector('a#add-instance-button') + .querySelector('a#add-instance-button') ?.addEventListener('click', (e) => { const domain = document.querySelector( 'input[type="text"]#by_domain', @@ -342,7 +330,7 @@ ready(() => { } }); - Rails.delegate(document, 'form', 'submit', ({ target }) => { + on('submit', 'form', ({ target }) => { if (target instanceof HTMLFormElement) target .querySelectorAll('input[type="datetime-local"]') diff --git a/app/javascript/entrypoints/public.tsx b/app/javascript/entrypoints/public.tsx index dd1956446da..6e88eb87780 100644 --- a/app/javascript/entrypoints/public.tsx +++ b/app/javascript/entrypoints/public.tsx @@ -4,8 +4,8 @@ import { IntlMessageFormat } from 'intl-messageformat'; import type { MessageDescriptor, PrimitiveType } from 'react-intl'; import { defineMessages } from 'react-intl'; -import Rails from '@rails/ujs'; import axios from 'axios'; +import { on } from 'delegated-events'; import { throttle } from 'lodash'; import { timeAgoString } from '../mastodon/components/relative_timestamp'; @@ -175,10 +175,9 @@ function loaded() { }); } - Rails.delegate( - document, - 'input#user_account_attributes_username', + on( 'input', + 'input#user_account_attributes_username', throttle( ({ target }) => { if (!(target instanceof HTMLInputElement)) return; @@ -202,60 +201,47 @@ function loaded() { ), ); - Rails.delegate( - document, - '#user_password,#user_password_confirmation', - 'input', - () => { - const password = document.querySelector( - 'input#user_password', - ); - const confirmation = document.querySelector( - 'input#user_password_confirmation', - ); - if (!confirmation || !password) return; + on('input', '#user_password,#user_password_confirmation', () => { + const password = document.querySelector( + 'input#user_password', + ); + const confirmation = document.querySelector( + 'input#user_password_confirmation', + ); + if (!confirmation || !password) return; - if ( - confirmation.value && - confirmation.value.length > password.maxLength - ) { - confirmation.setCustomValidity( - formatMessage(messages.passwordExceedsLength), - ); - } else if (password.value && password.value !== confirmation.value) { - confirmation.setCustomValidity( - formatMessage(messages.passwordDoesNotMatch), - ); - } else { - confirmation.setCustomValidity(''); - } - }, - ); + if (confirmation.value && confirmation.value.length > password.maxLength) { + confirmation.setCustomValidity( + formatMessage(messages.passwordExceedsLength), + ); + } else if (password.value && password.value !== confirmation.value) { + confirmation.setCustomValidity( + formatMessage(messages.passwordDoesNotMatch), + ); + } else { + confirmation.setCustomValidity(''); + } + }); } -Rails.delegate( - document, - '#edit_profile input[type=file]', - 'change', - ({ target }) => { - if (!(target instanceof HTMLInputElement)) return; +on('change', '#edit_profile input[type=file]', ({ target }) => { + if (!(target instanceof HTMLInputElement)) return; - const avatar = document.querySelector( - `img#${target.id}-preview`, - ); + const avatar = document.querySelector( + `img#${target.id}-preview`, + ); - if (!avatar) return; + if (!avatar) return; - let file: File | undefined; - if (target.files) file = target.files[0]; + let file: File | undefined; + if (target.files) file = target.files[0]; - const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; + const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc; - if (url) avatar.src = url; - }, -); + if (url) avatar.src = url; +}); -Rails.delegate(document, '.input-copy input', 'click', ({ target }) => { +on('click', '.input-copy input', ({ target }) => { if (!(target instanceof HTMLInputElement)) return; target.focus(); @@ -263,7 +249,7 @@ Rails.delegate(document, '.input-copy input', 'click', ({ target }) => { target.setSelectionRange(0, target.value.length); }); -Rails.delegate(document, '.input-copy button', 'click', ({ target }) => { +on('click', '.input-copy button', ({ target }) => { if (!(target instanceof HTMLButtonElement)) return; const input = target.parentNode?.querySelector( @@ -312,22 +298,22 @@ const toggleSidebar = () => { sidebar.classList.toggle('visible'); }; -Rails.delegate(document, '.sidebar__toggle__icon', 'click', () => { +on('click', '.sidebar__toggle__icon', () => { toggleSidebar(); }); -Rails.delegate(document, '.sidebar__toggle__icon', 'keydown', (e) => { +on('keydown', '.sidebar__toggle__icon', (e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); toggleSidebar(); } }); -Rails.delegate(document, 'img.custom-emoji', 'mouseover', ({ target }) => { +on('mouseover', 'img.custom-emoji', ({ target }) => { if (target instanceof HTMLImageElement && target.dataset.original) target.src = target.dataset.original; }); -Rails.delegate(document, 'img.custom-emoji', 'mouseout', ({ target }) => { +on('mouseout', 'img.custom-emoji', ({ target }) => { if (target instanceof HTMLImageElement && target.dataset.static) target.src = target.dataset.static; }); @@ -376,22 +362,17 @@ const setInputHint = ( } }; -Rails.delegate( - document, - '#account_statuses_cleanup_policy_enabled', - 'change', - ({ target }) => { - if (!(target instanceof HTMLInputElement) || !target.form) return; +on('change', '#account_statuses_cleanup_policy_enabled', ({ target }) => { + if (!(target instanceof HTMLInputElement) || !target.form) return; - target.form - .querySelectorAll< - HTMLInputElement | HTMLSelectElement - >('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select') - .forEach((input) => { - setInputDisabled(input, !target.checked); - }); - }, -); + target.form + .querySelectorAll< + HTMLInputElement | HTMLSelectElement + >('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select') + .forEach((input) => { + setInputDisabled(input, !target.checked); + }); +}); const updateDefaultQuotePrivacyFromPrivacy = ( privacySelect: EventTarget | null, @@ -414,18 +395,13 @@ const updateDefaultQuotePrivacyFromPrivacy = ( } }; -Rails.delegate( - document, - '#user_settings_attributes_default_privacy', - 'change', - ({ target }) => { - updateDefaultQuotePrivacyFromPrivacy(target); - }, -); +on('change', '#user_settings_attributes_default_privacy', ({ target }) => { + updateDefaultQuotePrivacyFromPrivacy(target); +}); // Empty the honeypot fields in JS in case something like an extension // automatically filled them. -Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => { +on('submit', '#registration_new_user,#new_user', () => { [ 'user_website', 'user_confirm_password', @@ -439,7 +415,7 @@ Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => { }); }); -Rails.delegate(document, '.rules-list button', 'click', ({ target }) => { +on('click', '.rules-list button', ({ target }) => { if (!(target instanceof HTMLElement)) { return; } diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 232c4b1c19c..bd1cb3ca9bc 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -673,8 +673,8 @@ export function selectComposeSuggestion(position, token, suggestion, path) { dispatch(useEmoji(suggestion)); } else if (suggestion.type === 'hashtag') { - completion = suggestion.name.slice(token.length - 1); - startPosition = position + token.length; + completion = token + suggestion.name.slice(token.length - 1); + startPosition = position - 1; } else if (suggestion.type === 'account') { completion = `@${getState().getIn(['accounts', suggestion.id, 'acct'])}`; startPosition = position - 1; diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index 5854482dc5c..6a85231bb69 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -46,11 +46,11 @@ export function importFetchedAccounts(accounts) { return importAccounts({ accounts: normalAccounts }); } -export function importFetchedStatus(status) { - return importFetchedStatuses([status]); +export function importFetchedStatus(status, options = {}) { + return importFetchedStatuses([status], options); } -export function importFetchedStatuses(statuses) { +export function importFetchedStatuses(statuses, options = {}) { return (dispatch, getState) => { const accounts = []; const normalStatuses = []; @@ -58,7 +58,7 @@ export function importFetchedStatuses(statuses) { const filters = []; function processStatus(status) { - pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]))); + pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]), options)); pushUnique(accounts, status.account); if (status.filtered) { diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 32c3d766665..075dc84ef1c 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -27,9 +27,12 @@ function stripQuoteFallback(text) { return wrapper.innerHTML; } -export function normalizeStatus(status, normalOldStatus) { +export function normalizeStatus(status, normalOldStatus, { bogusQuotePolicy = false }) { const normalStatus = { ...status }; + if (bogusQuotePolicy) + normalStatus.quote_approval = null; + normalStatus.account = status.account.id; if (status.reblog && status.reblog.id) { @@ -109,6 +112,8 @@ export function normalizeStatus(status, normalOldStatus) { } if (normalOldStatus) { + normalStatus.quote_approval ||= normalOldStatus.get('quote_approval'); + const list = normalOldStatus.get('media_attachments'); if (normalStatus.media_attachments && list) { normalStatus.media_attachments.forEach(item => { diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 7572efe95f5..5602fcadb69 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -203,8 +203,8 @@ export function deleteStatusFail(id, error) { }; } -export const updateStatus = status => dispatch => - dispatch(importFetchedStatus(status)); +export const updateStatus = (status, { bogusQuotePolicy }) => dispatch => + dispatch(importFetchedStatus(status, { bogusQuotePolicy })); export function muteStatus(id) { return (dispatch) => { diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index 4299bad5c32..34d094f519c 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -52,6 +52,9 @@ const randomUpTo = max => export const connectTimelineStream = (timelineId, channelName, params = {}, options = {}) => { const { messages } = getLocale(); + // Public streams are currently not returning personalized quote policies + const bogusQuotePolicy = channelName.startsWith('public') || channelName.startsWith('hashtag'); + return connectStream(channelName, params, (dispatch, getState) => { // @ts-ignore const locale = getState().getIn(['meta', 'locale']); @@ -97,11 +100,11 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti switch (data.event) { case 'update': // @ts-expect-error - dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept)); + dispatch(updateTimeline(timelineId, JSON.parse(data.payload), { accept: options.accept, bogusQuotePolicy })); break; case 'status.update': // @ts-expect-error - dispatch(updateStatus(JSON.parse(data.payload))); + dispatch(updateStatus(JSON.parse(data.payload), { bogusQuotePolicy })); break; case 'delete': dispatch(deleteFromTimelines(data.payload)); diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 65b6d804511..f1d8dd9a97e 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -32,7 +32,7 @@ export const loadPending = timeline => ({ timeline, }); -export function updateTimeline(timeline, status, accept) { +export function updateTimeline(timeline, status, { accept = undefined, bogusQuotePolicy = false } = {}) { return (dispatch, getState) => { if (typeof accept === 'function' && !accept(status)) { return; @@ -45,7 +45,7 @@ export function updateTimeline(timeline, status, accept) { return; } - dispatch(importFetchedStatus(status)); + dispatch(importFetchedStatus(status, { bogusQuotePolicy })); dispatch({ type: TIMELINE_UPDATE, diff --git a/app/javascript/mastodon/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.jsx index 137bad9b7e6..a9acc87252d 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.jsx +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -50,6 +50,7 @@ const AutosuggestTextarea = forwardRef(({ onKeyUp, onKeyDown, onPaste, + onDrop, onFocus, autoFocus = true, lang, @@ -153,6 +154,12 @@ const AutosuggestTextarea = forwardRef(({ onPaste(e); }, [onPaste]); + const handleDrop = useCallback((e) => { + if (onDrop) { + onDrop(e); + } + }, [onDrop]); + // Show the suggestions again whenever they change and the textarea is focused useEffect(() => { if (suggestions.size > 0 && textareaRef.current === document.activeElement) { @@ -204,6 +211,7 @@ const AutosuggestTextarea = forwardRef(({ onFocus={handleFocus} onBlur={handleBlur} onPaste={handlePaste} + onDrop={handleDrop} dir='auto' aria-autocomplete='list' aria-label={placeholder} @@ -235,6 +243,7 @@ AutosuggestTextarea.propTypes = { onKeyUp: PropTypes.func, onKeyDown: PropTypes.func, onPaste: PropTypes.func.isRequired, + onDrop: PropTypes.func, onFocus:PropTypes.func, autoFocus: PropTypes.bool, lang: PropTypes.string, diff --git a/app/javascript/mastodon/components/dropdown_menu.tsx b/app/javascript/mastodon/components/dropdown_menu.tsx index 3a26c676ae4..dacbc337895 100644 --- a/app/javascript/mastodon/components/dropdown_menu.tsx +++ b/app/javascript/mastodon/components/dropdown_menu.tsx @@ -26,6 +26,7 @@ import { closeDropdownMenu, } from 'mastodon/actions/dropdown_menu'; import { openModal, closeModal } from 'mastodon/actions/modal'; +import { fetchStatus } from 'mastodon/actions/statuses'; import { CircularProgress } from 'mastodon/components/circular_progress'; import { isUserTouching } from 'mastodon/is_mobile'; import { @@ -303,6 +304,7 @@ interface DropdownProps { */ scrollKey?: string; status?: ImmutableMap; + needsStatusRefresh?: boolean; forceDropdown?: boolean; renderItem?: RenderItemFn; renderHeader?: RenderHeaderFn; @@ -326,6 +328,7 @@ export const Dropdown = ({ placement = 'bottom', offset = [5, 5], status, + needsStatusRefresh, forceDropdown = false, renderItem, renderHeader, @@ -345,6 +348,7 @@ export const Dropdown = ({ const prefetchAccountId = status ? status.getIn(['account', 'id']) : undefined; + const statusId = status?.get('id') as string | undefined; const handleClose = useCallback(() => { if (buttonRef.current) { @@ -409,6 +413,15 @@ export const Dropdown = ({ dispatch(fetchRelationships([prefetchAccountId])); } + if (needsStatusRefresh && statusId) { + dispatch( + fetchStatus(statusId, { + forceFetch: true, + alsoFetchContext: false, + }), + ); + } + if (isUserTouching() && !forceDropdown) { dispatch( openModal({ @@ -442,6 +455,8 @@ export const Dropdown = ({ items, forceDropdown, handleClose, + statusId, + needsStatusRefresh, ], ); diff --git a/app/javascript/mastodon/components/hotkeys/index.tsx b/app/javascript/mastodon/components/hotkeys/index.tsx index b1484ec3acb..81ca28eb872 100644 --- a/app/javascript/mastodon/components/hotkeys/index.tsx +++ b/app/javascript/mastodon/components/hotkeys/index.tsx @@ -180,25 +180,24 @@ export function useHotkeys(handlers: HandlerMap) { if (shouldHandleEvent) { const matchCandidates: { - handler: (event: KeyboardEvent) => void; + // A candidate will be have an undefined handler if it's matched, + // but handled in a parent component rather than this one. + handler: ((event: KeyboardEvent) => void) | undefined; priority: number; }[] = []; (Object.keys(hotkeyMatcherMap) as HotkeyName[]).forEach( (handlerName) => { const handler = handlersRef.current[handlerName]; + const hotkeyMatcher = hotkeyMatcherMap[handlerName]; - if (handler) { - const hotkeyMatcher = hotkeyMatcherMap[handlerName]; + const { isMatch, priority } = hotkeyMatcher( + event, + bufferedKeys.current, + ); - const { isMatch, priority } = hotkeyMatcher( - event, - bufferedKeys.current, - ); - - if (isMatch) { - matchCandidates.push({ handler, priority }); - } + if (isMatch) { + matchCandidates.push({ handler, priority }); } }, ); diff --git a/app/javascript/mastodon/components/icon_button.tsx b/app/javascript/mastodon/components/icon_button.tsx index de9cbc19bb4..9f64a257f0d 100644 --- a/app/javascript/mastodon/components/icon_button.tsx +++ b/app/javascript/mastodon/components/icon_button.tsx @@ -2,6 +2,8 @@ import { useCallback, forwardRef } from 'react'; import classNames from 'classnames'; +import { usePrevious } from '../hooks/usePrevious'; + import { AnimatedNumber } from './animated_number'; import type { IconProp } from './icon'; import { Icon } from './icon'; @@ -91,12 +93,15 @@ export const IconButton = forwardRef( ...(active ? activeStyle : {}), }; + const previousActive = usePrevious(active) ?? active; + const shouldAnimate = animate && active !== previousActive; + const classes = classNames(className, 'icon-button', { active, disabled, inverted, - activate: animate && active, - deactivate: animate && !active, + activate: shouldAnimate && active, + deactivate: shouldAnimate && !active, overlayed: overlay, 'icon-button--with-counter': typeof counter !== 'undefined', }); diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 2a8c9bfb2d8..1e746e5f710 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -553,7 +553,6 @@ class Status extends ImmutablePureComponent { } const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); - return (
diff --git a/app/javascript/mastodon/components/status/boost_button.tsx b/app/javascript/mastodon/components/status/boost_button.tsx index 723fa32aa1b..023ba8ff195 100644 --- a/app/javascript/mastodon/components/status/boost_button.tsx +++ b/app/javascript/mastodon/components/status/boost_button.tsx @@ -8,6 +8,7 @@ import classNames from 'classnames'; import { quoteComposeById } from '@/mastodon/actions/compose_typed'; import { toggleReblog } from '@/mastodon/actions/interactions'; import { openModal } from '@/mastodon/actions/modal'; +import { fetchStatus } from '@/mastodon/actions/statuses'; import { quickBoosting } from '@/mastodon/initial_state'; import type { ActionMenuItem } from '@/mastodon/models/dropdown_menu'; import type { Status } from '@/mastodon/models/status'; @@ -63,6 +64,7 @@ const StandaloneBoostButton: FC = ({ status, counters }) => { title={intl.formatMessage(meta ?? title)} icon='retweet' iconComponent={iconComponent} + className='status__action-bar__button' onClick={!disabled ? handleClick : undefined} counter={ counters @@ -111,18 +113,7 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { const statusId = status.get('id') as string; const wasBoosted = !!status.get('reblogged'); - - let count: number | undefined; - if (counters) { - count = 0; - // Ensure count is a valid integer. - if (Number.isInteger(status.get('reblogs_count'))) { - count += status.get('reblogs_count') as number; - } - if (Number.isInteger(status.get('quotes_count'))) { - count += status.get('quotes_count') as number; - } - } + const quoteApproval = status.get('quote_approval'); const showLoginPrompt = useCallback(() => { dispatch( @@ -179,9 +170,16 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { dispatch(toggleReblog(status.get('id'), true)); return false; } + + if (quoteApproval === null) { + dispatch( + fetchStatus(statusId, { forceFetch: true, alsoFetchContext: false }), + ); + } + return true; }, - [dispatch, isLoggedIn, showLoginPrompt, status], + [dispatch, isLoggedIn, showLoginPrompt, status, quoteApproval, statusId], ); return ( @@ -198,8 +196,14 @@ const BoostOrQuoteMenu: FC = ({ status, counters }) => { isMenuDisabled ? messages.all_disabled : messages.reblog_or_quote, )} icon='retweet' + className='status__action-bar__button' iconComponent={boostIcon} - counter={count} + counter={ + counters + ? (status.get('reblogs_count') as number) + + (status.get('quotes_count') as number) + : undefined + } active={isReblogged} /> diff --git a/app/javascript/mastodon/components/status/handled_link.tsx b/app/javascript/mastodon/components/status/handled_link.tsx index be816e98531..8b6db98fda0 100644 --- a/app/javascript/mastodon/components/status/handled_link.tsx +++ b/app/javascript/mastodon/components/status/handled_link.tsx @@ -27,16 +27,18 @@ export const HandledLink: FC> = ({ }) => { // Handle hashtags if ( - text.startsWith('#') || - prevText?.endsWith('#') || - text.startsWith('#') || - prevText?.endsWith('#') + (text.startsWith('#') || + prevText?.endsWith('#') || + text.startsWith('#') || + prevText?.endsWith('#')) && + !text.includes('%') ) { const hashtag = text.slice(1).trim(); + return ( diff --git a/app/javascript/mastodon/components/status_action_bar/index.jsx b/app/javascript/mastodon/components/status_action_bar/index.jsx index 91b9e05b145..5c79970e23f 100644 --- a/app/javascript/mastodon/components/status_action_bar/index.jsx +++ b/app/javascript/mastodon/components/status_action_bar/index.jsx @@ -404,16 +404,21 @@ class StatusActionBar extends ImmutablePureComponent { { dismissQuoteHint(); return true; }} - /> + > + + )}
diff --git a/app/javascript/mastodon/features/about/components/rules.tsx b/app/javascript/mastodon/features/about/components/rules.tsx index 078fb68c089..1b1e28a7ff2 100644 --- a/app/javascript/mastodon/features/about/components/rules.tsx +++ b/app/javascript/mastodon/features/about/components/rules.tsx @@ -104,17 +104,19 @@ export const RulesSection: FC = ({ isLoading = false }) => { defaultMessage='Language' /> - +
+ +
)} @@ -169,9 +171,13 @@ const localeOptionsSelector = createSelector( }, }; // Use the default locale as a target to translate language names. - const intlLocale = new Intl.DisplayNames(intl.locale, { - type: 'language', - }); + const intlLocale = + // Intl.DisplayNames can be undefined in old browsers + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + Intl.DisplayNames && + (new Intl.DisplayNames(intl.locale, { + type: 'language', + }) as Intl.DisplayNames | undefined); for (const { translations } of rules) { for (const locale in translations) { if (langs[locale]) { @@ -179,7 +185,7 @@ const localeOptionsSelector = createSelector( } langs[locale] = { value: locale, - text: intlLocale.of(locale) ?? locale, + text: intlLocale?.of(locale) ?? locale, }; } } diff --git a/app/javascript/mastodon/features/account/components/follow_request_note.jsx b/app/javascript/mastodon/features/account/components/follow_request_note.jsx index 9c20f1e0626..27ebb8fd64b 100644 --- a/app/javascript/mastodon/features/account/components/follow_request_note.jsx +++ b/app/javascript/mastodon/features/account/components/follow_request_note.jsx @@ -24,12 +24,12 @@ export default class FollowRequestNote extends ImmutablePureComponent {
- - diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx index a6b621e6d98..3c54fa47249 100644 --- a/app/javascript/mastodon/features/alt_text_modal/index.tsx +++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx @@ -329,7 +329,7 @@ export const AltTextModal = forwardRef>( }); }, [dispatch, setIsSaving, mediaId, onClose, position, description]); - const handleKeyUp = useCallback( + const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); @@ -456,7 +456,7 @@ export const AltTextModal = forwardRef>( id='description' value={isDetecting ? ' ' : description} onChange={handleDescriptionChange} - onKeyUp={handleKeyUp} + onKeyDown={handleKeyDown} lang={lang} placeholder={intl.formatMessage( type === 'audio' diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index 770f776049e..ec2b531b5fd 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -64,6 +64,7 @@ class ComposeForm extends ImmutablePureComponent { onSuggestionSelected: PropTypes.func.isRequired, onChangeSpoilerText: PropTypes.func.isRequired, onPaste: PropTypes.func.isRequired, + onDrop: PropTypes.func.isRequired, onPickEmoji: PropTypes.func.isRequired, autoFocus: PropTypes.bool, withoutNavigation: PropTypes.bool, @@ -102,6 +103,7 @@ class ComposeForm extends ImmutablePureComponent { handleKeyDownPost = (e) => { if (e.key.toLowerCase() === 'enter' && (e.ctrlKey || e.metaKey)) { this.handleSubmit(); + e.preventDefault(); } this.blurOnEscape(e); }; @@ -248,7 +250,7 @@ class ComposeForm extends ImmutablePureComponent { }; render () { - const { intl, onPaste, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props; + const { intl, onPaste, onDrop, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props; const { highlighted } = this.state; return ( @@ -304,6 +306,7 @@ class ComposeForm extends ImmutablePureComponent { onSuggestionsClearRequested={this.onSuggestionsClearRequested} onSuggestionSelected={this.onSuggestionSelected} onPaste={onPaste} + onDrop={onDrop} autoFocus={autoFocus} lang={this.props.lang} className='compose-form__input' diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx deleted file mode 100644 index 1c9434502c0..00000000000 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx +++ /dev/null @@ -1,158 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import { injectIntl, defineMessages } from 'react-intl'; - -import classNames from 'classnames'; - -import Overlay from 'react-overlays/Overlay'; - -import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; -import LockIcon from '@/material-icons/400-24px/lock.svg?react'; -import PublicIcon from '@/material-icons/400-24px/public.svg?react'; -import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react'; -import { DropdownSelector } from 'mastodon/components/dropdown_selector'; -import { Icon } from 'mastodon/components/icon'; - -export const messages = defineMessages({ - public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, - public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' }, - unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' }, - unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Hidden from Mastodon search results, trending, and public timelines' }, - private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' }, - private_long: { id: 'privacy.private.long', defaultMessage: 'Only your followers' }, - direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' }, - direct_long: { id: 'privacy.direct.long', defaultMessage: 'Everyone mentioned in the post' }, - change_privacy: { id: 'privacy.change', defaultMessage: 'Change post privacy' }, - unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' }, -}); - -class PrivacyDropdown extends PureComponent { - - static propTypes = { - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - noDirect: PropTypes.bool, - container: PropTypes.func, - disabled: PropTypes.bool, - intl: PropTypes.object.isRequired, - }; - - state = { - open: false, - placement: 'bottom', - }; - - handleToggle = () => { - if (this.state.open && this.activeElement) { - this.activeElement.focus({ preventScroll: true }); - } - - this.setState({ open: !this.state.open }); - }; - - handleKeyDown = e => { - switch(e.key) { - case 'Escape': - this.handleClose(); - break; - } - }; - - handleMouseDown = () => { - if (!this.state.open) { - this.activeElement = document.activeElement; - } - }; - - handleButtonKeyDown = (e) => { - switch(e.key) { - case ' ': - case 'Enter': - this.handleMouseDown(); - break; - } - }; - - handleClose = () => { - if (this.state.open && this.activeElement) { - this.activeElement.focus({ preventScroll: true }); - } - this.setState({ open: false }); - }; - - handleChange = value => { - this.props.onChange(value); - }; - - UNSAFE_componentWillMount () { - const { intl: { formatMessage } } = this.props; - - this.options = [ - { icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, - { icon: 'unlock', iconComponent: QuietTimeIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long), extra: formatMessage(messages.unlisted_extra) }, - { icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, - ]; - - if (!this.props.noDirect) { - this.options.push( - { icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, - ); - } - } - - setTargetRef = c => { - this.target = c; - }; - - findTarget = () => { - return this.target; - }; - - handleOverlayEnter = (state) => { - this.setState({ placement: state.placement }); - }; - - render () { - const { value, container, disabled, intl } = this.props; - const { open, placement } = this.state; - - const valueOption = this.options.find(item => item.value === value); - - return ( -
- - - - {({ props, placement }) => ( -
-
- -
-
- )} -
-
- ); - } - -} - -export default injectIntl(PrivacyDropdown); diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx new file mode 100644 index 00000000000..5a463d86e79 --- /dev/null +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx @@ -0,0 +1,199 @@ +import { useCallback, useRef, useState } from 'react'; + +import { defineMessages, useIntl } from 'react-intl'; + +import classNames from 'classnames'; + +import type { OverlayProps } from 'react-overlays/Overlay'; +import Overlay from 'react-overlays/Overlay'; + +import type { StatusVisibility } from '@/mastodon/api_types/statuses'; +import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; +import LockIcon from '@/material-icons/400-24px/lock.svg?react'; +import PublicIcon from '@/material-icons/400-24px/public.svg?react'; +import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react'; +import { DropdownSelector } from 'mastodon/components/dropdown_selector'; +import { Icon } from 'mastodon/components/icon'; + +export const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + public_long: { + id: 'privacy.public.long', + defaultMessage: 'Anyone on and off Mastodon', + }, + unlisted_short: { + id: 'privacy.unlisted.short', + defaultMessage: 'Quiet public', + }, + unlisted_long: { + id: 'privacy.unlisted.long', + defaultMessage: + 'Hidden from Mastodon search results, trending, and public timelines', + }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' }, + private_long: { + id: 'privacy.private.long', + defaultMessage: 'Only your followers', + }, + direct_short: { + id: 'privacy.direct.short', + defaultMessage: 'Specific people', + }, + direct_long: { + id: 'privacy.direct.long', + defaultMessage: 'Everyone mentioned in the post', + }, + change_privacy: { + id: 'privacy.change', + defaultMessage: 'Change post privacy', + }, + unlisted_extra: { + id: 'privacy.unlisted.additional', + defaultMessage: + 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.', + }, +}); + +interface PrivacyDropdownProps { + value: StatusVisibility; + onChange: (value: StatusVisibility) => void; + noDirect?: boolean; + container?: OverlayProps['container']; + disabled?: boolean; +} + +const PrivacyDropdown: React.FC = ({ + value, + onChange, + noDirect, + container, + disabled, +}) => { + const intl = useIntl(); + const overlayTargetRef = useRef(null); + const previousFocusTargetRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + + const handleClose = useCallback(() => { + if (isOpen && previousFocusTargetRef.current) { + previousFocusTargetRef.current.focus({ preventScroll: true }); + } + setIsOpen(false); + }, [isOpen]); + + const handleToggle = useCallback(() => { + if (isOpen) { + handleClose(); + } + setIsOpen((prev) => !prev); + }, [handleClose, isOpen]); + + const registerPreviousFocusTarget = useCallback(() => { + if (!isOpen) { + previousFocusTargetRef.current = document.activeElement as HTMLElement; + } + }, [isOpen]); + + const handleButtonKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if ([' ', 'Enter'].includes(e.key)) { + registerPreviousFocusTarget(); + } + }, + [registerPreviousFocusTarget], + ); + + const options = [ + { + icon: 'globe', + iconComponent: PublicIcon, + value: 'public', + text: intl.formatMessage(messages.public_short), + meta: intl.formatMessage(messages.public_long), + }, + { + icon: 'unlock', + iconComponent: QuietTimeIcon, + value: 'unlisted', + text: intl.formatMessage(messages.unlisted_short), + meta: intl.formatMessage(messages.unlisted_long), + extra: intl.formatMessage(messages.unlisted_extra), + }, + { + icon: 'lock', + iconComponent: LockIcon, + value: 'private', + text: intl.formatMessage(messages.private_short), + meta: intl.formatMessage(messages.private_long), + }, + ]; + + if (!noDirect) { + options.push({ + icon: 'at', + iconComponent: AlternateEmailIcon, + value: 'direct', + text: intl.formatMessage(messages.direct_short), + meta: intl.formatMessage(messages.direct_long), + }); + } + + const selectedOption = + options.find((item) => item.value === value) ?? options.at(0); + + return ( +
+ + + + {({ props, placement }) => ( +
+
+ +
+
+ )} +
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default PrivacyDropdown; diff --git a/app/javascript/mastodon/features/compose/components/text_icon_button.jsx b/app/javascript/mastodon/features/compose/components/text_icon_button.jsx deleted file mode 100644 index 166d022b88a..00000000000 --- a/app/javascript/mastodon/features/compose/components/text_icon_button.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -const iconStyle = { - height: null, - lineHeight: '27px', - minWidth: `${18 * 1.28571429}px`, -}; - -export default class TextIconButton extends PureComponent { - - static propTypes = { - label: PropTypes.string.isRequired, - title: PropTypes.string, - active: PropTypes.bool, - onClick: PropTypes.func.isRequired, - ariaControls: PropTypes.string, - }; - - render () { - const { label, title, active, ariaControls } = this.props; - - return ( - - ); - } - -} diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js index 15b1c7cc411..4ace6d934de 100644 --- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js +++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js @@ -13,11 +13,29 @@ import { import { pasteLinkCompose } from 'mastodon/actions/compose_typed'; import { openModal } from 'mastodon/actions/modal'; import { PRIVATE_QUOTE_MODAL_ID } from 'mastodon/features/ui/components/confirmation_modals/private_quote_notify'; +import { me } from 'mastodon/initial_state'; import ComposeForm from '../components/compose_form'; const urlLikeRegex = /^https?:\/\/[^\s]+\/[^\s]+$/i; +const processPasteOrDrop = (transfer, e, dispatch) => { + if (transfer && transfer.files.length === 1) { + dispatch(uploadCompose(transfer.files)); + e.preventDefault(); + } else if (transfer && transfer.files.length === 0) { + const data = transfer.getData('text/plain'); + if (!data.match(urlLikeRegex)) return; + + try { + const url = new URL(data); + dispatch(pasteLinkCompose({ url })); + } catch { + return; + } + } +}; + const mapStateToProps = state => ({ text: state.getIn(['compose', 'text']), suggestions: state.getIn(['compose', 'suggestions']), @@ -36,6 +54,7 @@ const mapStateToProps = state => ({ quoteToPrivate: !!state.getIn(['compose', 'quoted_status_id']) && state.getIn(['compose', 'privacy']) === 'private' + && state.getIn(['statuses', state.getIn(['compose', 'quoted_status_id']), 'account']) !== me && !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]), isInReply: state.getIn(['compose', 'in_reply_to']) !== null, lang: state.getIn(['compose', 'language']), @@ -85,20 +104,11 @@ const mapDispatchToProps = (dispatch, props) => ({ }, onPaste (e) { - if (e.clipboardData && e.clipboardData.files.length === 1) { - dispatch(uploadCompose(e.clipboardData.files)); - e.preventDefault(); - } else if (e.clipboardData && e.clipboardData.files.length === 0) { - const data = e.clipboardData.getData('text/plain'); - if (!data.match(urlLikeRegex)) return; + processPasteOrDrop(e.clipboardData, e, dispatch); + }, - try { - const url = new URL(data); - dispatch(pasteLinkCompose({ url })); - } catch { - return; - } - } + onDrop (e) { + processPasteOrDrop(e.dataTransfer, e, dispatch); }, onPickEmoji (position, data, needsSpace) { diff --git a/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js deleted file mode 100644 index 803dcb1a4a0..00000000000 --- a/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js +++ /dev/null @@ -1,19 +0,0 @@ -import { connect } from 'react-redux'; - -import { changeComposeVisibility } from '@/mastodon/actions/compose_typed'; - -import PrivacyDropdown from '../components/privacy_dropdown'; - -const mapStateToProps = state => ({ - value: state.getIn(['compose', 'privacy']), -}); - -const mapDispatchToProps = dispatch => ({ - - onChange (value) { - dispatch(changeComposeVisibility(value)); - }, - -}); - -export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown); diff --git a/app/javascript/mastodon/features/compose/index.tsx b/app/javascript/mastodon/features/compose/index.tsx index 892cbb97617..527e541e84d 100644 --- a/app/javascript/mastodon/features/compose/index.tsx +++ b/app/javascript/mastodon/features/compose/index.tsx @@ -166,7 +166,7 @@ const Compose: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
-
+
diff --git a/app/javascript/mastodon/features/emoji/index.ts b/app/javascript/mastodon/features/emoji/index.ts index 11ee26aac29..4b0f79133c5 100644 --- a/app/javascript/mastodon/features/emoji/index.ts +++ b/app/javascript/mastodon/features/emoji/index.ts @@ -1,6 +1,7 @@ import { initialState } from '@/mastodon/initial_state'; import { toSupportedLocale } from './locale'; +import type { LocaleOrCustom } from './types'; import { emojiLogger } from './utils'; // eslint-disable-next-line import/default -- Importing via worker loader. import EmojiWorker from './worker?worker&inline'; @@ -24,19 +25,17 @@ export function initializeEmoji() { } if (worker) { - // Assign worker to const to make TS happy inside the event listener. - const thisWorker = worker; const timeoutId = setTimeout(() => { log('worker is not ready after timeout'); worker = null; void fallbackLoad(); }, WORKER_TIMEOUT); - thisWorker.addEventListener('message', (event: MessageEvent) => { + worker.addEventListener('message', (event: MessageEvent) => { const { data: message } = event; if (message === 'ready') { log('worker ready, loading data'); clearTimeout(timeoutId); - thisWorker.postMessage('custom'); + messageWorker('custom'); void loadEmojiLocale(userLocale); // Load English locale as well, because people are still used to // using it from before we supported other locales. @@ -55,20 +54,35 @@ export function initializeEmoji() { async function fallbackLoad() { log('falling back to main thread for loading'); const { importCustomEmojiData } = await import('./loader'); - await importCustomEmojiData(); + const emojis = await importCustomEmojiData(); + if (emojis) { + log('loaded %d custom emojis', emojis.length); + } await loadEmojiLocale(userLocale); if (userLocale !== 'en') { await loadEmojiLocale('en'); } } -export async function loadEmojiLocale(localeString: string) { +async function loadEmojiLocale(localeString: string) { const locale = toSupportedLocale(localeString); + const { importEmojiData, localeToPath } = await import('./loader'); if (worker) { - worker.postMessage(locale); + const path = await localeToPath(locale); + log('asking worker to load locale %s from %s', locale, path); + messageWorker(locale, path); } else { - const { importEmojiData } = await import('./loader'); - await importEmojiData(locale); + const emojis = await importEmojiData(locale); + if (emojis) { + log('loaded %d emojis to locale %s', emojis.length, locale); + } } } + +function messageWorker(locale: LocaleOrCustom, path?: string) { + if (!worker) { + return; + } + worker.postMessage({ locale, path }); +} diff --git a/app/javascript/mastodon/features/emoji/loader.ts b/app/javascript/mastodon/features/emoji/loader.ts index 3196b28b9c6..7251559d6b2 100644 --- a/app/javascript/mastodon/features/emoji/loader.ts +++ b/app/javascript/mastodon/features/emoji/loader.ts @@ -1,5 +1,5 @@ import { flattenEmojiData } from 'emojibase'; -import type { CompactEmoji, FlatCompactEmoji } from 'emojibase'; +import type { CompactEmoji, FlatCompactEmoji, Locale } from 'emojibase'; import { putEmojiData, @@ -8,45 +8,64 @@ import { putLatestEtag, } from './database'; import { toSupportedLocale, toSupportedLocaleOrCustom } from './locale'; -import type { CustomEmojiData, LocaleOrCustom } from './types'; -import { emojiLogger } from './utils'; +import type { CustomEmojiData } from './types'; -const log = emojiLogger('loader'); - -export async function importEmojiData(localeString: string) { +export async function importEmojiData(localeString: string, path?: string) { const locale = toSupportedLocale(localeString); - const emojis = await fetchAndCheckEtag(locale); + + // Validate the provided path. + if (path && !/^[/a-z]*\/packs\/assets\/compact-\w+\.json$/.test(path)) { + throw new Error('Invalid path for emoji data'); + } else { + // Otherwise get the path if not provided. + path ??= await localeToPath(locale); + } + + const emojis = await fetchAndCheckEtag(locale, path); if (!emojis) { return; } const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis); - log('loaded %d for %s locale', flattenedEmojis.length, locale); await putEmojiData(flattenedEmojis, locale); + return flattenedEmojis; } export async function importCustomEmojiData() { - const emojis = await fetchAndCheckEtag('custom'); + const emojis = await fetchAndCheckEtag( + 'custom', + '/api/v1/custom_emojis', + ); if (!emojis) { return; } - log('loaded %d custom emojis', emojis.length); await putCustomEmojiData(emojis); + return emojis; } -async function fetchAndCheckEtag( - localeOrCustom: LocaleOrCustom, +const modules = import.meta.glob( + '../../../../../node_modules/emojibase-data/**/compact.json', + { + query: '?url', + import: 'default', + }, +); + +export function localeToPath(locale: Locale) { + const key = `../../../../../node_modules/emojibase-data/${locale}/compact.json`; + if (!modules[key] || typeof modules[key] !== 'function') { + throw new Error(`Unsupported locale: ${locale}`); + } + return modules[key](); +} + +export async function fetchAndCheckEtag( + localeString: string, + path: string, ): Promise { - const locale = toSupportedLocaleOrCustom(localeOrCustom); + const locale = toSupportedLocaleOrCustom(localeString); // Use location.origin as this script may be loaded from a CDN domain. - const url = new URL(location.origin); - if (locale === 'custom') { - url.pathname = '/api/v1/custom_emojis'; - } else { - // This doesn't use isDevelopment() as that module loads initial state - // which breaks workers, as they cannot access the DOM. - url.pathname = `/packs${import.meta.env.DEV ? '-dev' : ''}/emoji/${locale}.json`; - } + const url = new URL(path, location.origin); const oldEtag = await loadLatestEtag(locale); const response = await fetch(url, { @@ -61,21 +80,19 @@ async function fetchAndCheckEtag( } if (!response.ok) { throw new Error( - `Failed to fetch emoji data for ${localeOrCustom}: ${response.statusText}`, + `Failed to fetch emoji data for ${locale}: ${response.statusText}`, ); } const data = (await response.json()) as ResultType; if (!Array.isArray(data)) { - throw new Error( - `Unexpected data format for ${localeOrCustom}: expected an array`, - ); + throw new Error(`Unexpected data format for ${locale}: expected an array`); } // Store the ETag for future requests const etag = response.headers.get('ETag'); if (etag) { - await putLatestEtag(etag, localeOrCustom); + await putLatestEtag(etag, localeString); } return data; diff --git a/app/javascript/mastodon/features/emoji/render.test.ts b/app/javascript/mastodon/features/emoji/render.test.ts index 05dbc388c45..3c96cbfb553 100644 --- a/app/javascript/mastodon/features/emoji/render.test.ts +++ b/app/javascript/mastodon/features/emoji/render.test.ts @@ -162,7 +162,7 @@ describe('loadEmojiDataToState', () => { const dbCall = vi .spyOn(db, 'loadEmojiByHexcode') .mockRejectedValue(new db.LocaleNotLoadedError('en')); - vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce(); + vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce(undefined); const consoleCall = vi .spyOn(console, 'warn') .mockImplementationOnce(() => null); diff --git a/app/javascript/mastodon/features/emoji/worker.ts b/app/javascript/mastodon/features/emoji/worker.ts index 6fb7d36e936..5360484d778 100644 --- a/app/javascript/mastodon/features/emoji/worker.ts +++ b/app/javascript/mastodon/features/emoji/worker.ts @@ -1,18 +1,25 @@ -import { importEmojiData, importCustomEmojiData } from './loader'; +import { importCustomEmojiData, importEmojiData } from './loader'; addEventListener('message', handleMessage); self.postMessage('ready'); // After the worker is ready, notify the main thread -function handleMessage(event: MessageEvent) { - const { data: locale } = event; - void loadData(locale); +function handleMessage(event: MessageEvent<{ locale: string; path?: string }>) { + const { + data: { locale, path }, + } = event; + void loadData(locale, path); } -async function loadData(locale: string) { - if (locale !== 'custom') { - await importEmojiData(locale); +async function loadData(locale: string, path?: string) { + let importCount: number | undefined; + if (locale === 'custom') { + importCount = (await importCustomEmojiData())?.length; + } else if (path) { + importCount = (await importEmojiData(locale, path))?.length; } else { - await importCustomEmojiData(); + throw new Error('Path is required for loading locale emoji data'); + } + if (importCount) { + self.postMessage(`loaded ${importCount} emojis into ${locale}`); } - self.postMessage(`loaded ${locale}`); } diff --git a/app/javascript/mastodon/features/hashtag_timeline/components/hashtag_header.tsx b/app/javascript/mastodon/features/hashtag_timeline/components/hashtag_header.tsx index c11874e7d42..fafe186cb41 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/components/hashtag_header.tsx +++ b/app/javascript/mastodon/features/hashtag_timeline/components/hashtag_header.tsx @@ -197,13 +197,16 @@ export const HashtagHeader: React.FC<{ /> )} -
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.jsx b/app/javascript/mastodon/features/hashtag_timeline/index.jsx index 791f494d3d8..36049d4331d 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/index.jsx +++ b/app/javascript/mastodon/features/hashtag_timeline/index.jsx @@ -16,7 +16,7 @@ import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines import Column from 'mastodon/components/column'; import ColumnHeader from 'mastodon/components/column_header'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; -import { remoteTopicFeedAccess, me } from 'mastodon/initial_state'; +import { remoteTopicFeedAccess, me, localTopicFeedAccess } from 'mastodon/initial_state'; import StatusListContainer from '../ui/containers/status_list_container'; @@ -25,9 +25,11 @@ import ColumnSettingsContainer from './containers/column_settings_container'; const mapStateToProps = (state, props) => { const local = props.params.local || (!me && remoteTopicFeedAccess !== 'public'); + const hasFeedAccess = !!me || localTopicFeedAccess === 'public'; return ({ local, + hasFeedAccess, hasUnread: state.getIn(['timelines', `hashtag:${props.params.id}${local ? ':local' : ''}`, 'unread']) > 0, }); }; @@ -127,11 +129,13 @@ class HashtagTimeline extends PureComponent { } _load() { - const { dispatch, local } = this.props; + const { dispatch, local, hasFeedAccess } = this.props; const { id, tags } = this.props.params; - this._subscribe(dispatch, id, tags, local); - dispatch(expandHashtagTimeline(id, { tags, local })); + if (hasFeedAccess) { + this._subscribe(dispatch, id, tags, local); + dispatch(expandHashtagTimeline(id, { tags, local })); + } } componentDidMount () { @@ -164,7 +168,7 @@ class HashtagTimeline extends PureComponent { }; render () { - const { hasUnread, columnId, multiColumn, local } = this.props; + const { hasUnread, columnId, multiColumn, local, hasFeedAccess } = this.props; const { id } = this.props.params; const pinned = !!columnId; @@ -192,7 +196,20 @@ class HashtagTimeline extends PureComponent { scrollKey={`hashtag_timeline-${columnId}`} timelineId={`hashtag:${id}${local ? ':local' : ''}`} onLoadMore={this.handleLoadMore} - emptyMessage={} + initialLoadingState={hasFeedAccess} + emptyMessage={ + hasFeedAccess ? ( + + ) : ( + + ) + } bindToDocument={!multiColumn} /> diff --git a/app/javascript/mastodon/features/navigation_panel/components/disabled_account_banner.tsx b/app/javascript/mastodon/features/navigation_panel/components/disabled_account_banner.tsx index 9103170fa75..6cb450d1d61 100644 --- a/app/javascript/mastodon/features/navigation_panel/components/disabled_account_banner.tsx +++ b/app/javascript/mastodon/features/navigation_panel/components/disabled_account_banner.tsx @@ -75,7 +75,7 @@ export const DisabledAccountBanner: React.FC = () => { } {errorType === 'error' && } - + diff --git a/app/javascript/mastodon/features/ui/components/media_modal.tsx b/app/javascript/mastodon/features/ui/components/media_modal.tsx index 7ba2fb7b732..daa0aebeb3f 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/media_modal.tsx @@ -46,6 +46,8 @@ interface MediaModalProps { volume?: number; } +const MIN_SWIPE_DISTANCE = 400; + export const MediaModal: FC = forwardRef< HTMLDivElement, MediaModalProps @@ -69,21 +71,30 @@ export const MediaModal: FC = forwardRef< const [zoomedIn, setZoomedIn] = useState(false); const currentMedia = media.get(index); + const [wrapperStyles, api] = useSpring(() => ({ + x: `-${index * 100}%`, + })); + const handleChangeIndex = useCallback( - (newIndex: number) => { + (newIndex: number, animate = false) => { if (newIndex < 0) { newIndex = media.size + newIndex; + } else if (newIndex >= media.size) { + newIndex = newIndex % media.size; } - setIndex(newIndex % media.size); + setIndex(newIndex); setZoomedIn(false); + if (animate) { + void api.start({ x: `-${newIndex * 100}%` }); + } }, - [media.size], + [api, media.size], ); const handlePrevClick = useCallback(() => { - handleChangeIndex(index - 1); + handleChangeIndex(index - 1, true); }, [handleChangeIndex, index]); const handleNextClick = useCallback(() => { - handleChangeIndex(index + 1); + handleChangeIndex(index + 1, true); }, [handleChangeIndex, index]); const handleKeyDown = useCallback( @@ -101,6 +112,25 @@ export const MediaModal: FC = forwardRef< [handleNextClick, handlePrevClick], ); + const bind = useDrag( + ({ active, movement: [mx], direction: [xDir], cancel }) => { + // If dragging and swipe distance is enough, change the index. + if ( + active && + Math.abs(mx) > Math.min(window.innerWidth / 4, MIN_SWIPE_DISTANCE) + ) { + handleChangeIndex(index - xDir); + cancel(); + } + // Set the x position via calc to ensure proper centering regardless of screen size. + const x = active ? mx : 0; + void api.start({ + x: `calc(-${index * 100}% + ${x}px)`, + }); + }, + { pointer: { capture: false } }, + ); + useEffect(() => { window.addEventListener('keydown', handleKeyDown, false); @@ -145,17 +175,6 @@ export const MediaModal: FC = forwardRef< setZoomedIn((prev) => !prev); }, []); - const wrapperStyles = useSpring({ - x: `-${index * 100}%`, - }); - const bind = useDrag( - ({ swipe: [swipeX] }) => { - if (swipeX === 0) return; - handleChangeIndex(index + swipeX * -1); // Invert swipe as swiping left loads the next slide. - }, - { pointer: { capture: false } }, - ); - const [navigationHidden, setNavigationHidden] = useState(false); const handleToggleNavigation = useCallback(() => { setNavigationHidden((prev) => !prev); diff --git a/app/javascript/mastodon/features/ui/components/modal_placeholder.tsx b/app/javascript/mastodon/features/ui/components/modal_placeholder.tsx index 13ec6ca2c82..9901ab6492b 100644 --- a/app/javascript/mastodon/features/ui/components/modal_placeholder.tsx +++ b/app/javascript/mastodon/features/ui/components/modal_placeholder.tsx @@ -46,7 +46,7 @@ export const ModalPlaceholder: React.FC<{ defaultMessage='Try again' /> - ist.", + "domain_pill.who_they_are": "Adressen teilen mit, wer jemand ist und wo sich jemand im Fediverse aufhält. Daher kannst du mit Leuten im gesamten Social Web interagieren, wenn es eine durch ist.", "domain_pill.who_you_are": "Deine Adresse teilt mit, wer du bist und wo du dich aufhältst. Daher können andere Leute im gesamten Social Web mit dir interagieren, wenn es eine durch ist.", "domain_pill.your_handle": "Deine Adresse:", "domain_pill.your_server": "Deine digitale Heimat. Hier „leben“ alle Beiträge von dir. Falls es dir hier nicht gefällt, kannst du jederzeit den Server wechseln und ebenso deine Follower übertragen.", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Alles klar! Hier gibt es nichts. Wenn Sie neue Mitteilungen erhalten, werden diese entsprechend Ihren Einstellungen hier angezeigt.", "empty_column.notifications": "Du hast noch keine Benachrichtigungen. Sobald andere Personen mit dir interagieren, wirst du hier darüber informiert.", "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Servern, um die Timeline aufzufüllen", + "error.no_hashtag_feed_access": "Registriere dich oder melde dich an, um den Hashtag anzusehen und ihm zu folgen.", "error.unexpected_crash.explanation": "Wegen eines Fehlers in unserem Code oder aufgrund einer Browser-Inkompatibilität kann diese Seite nicht korrekt angezeigt werden.", "error.unexpected_crash.explanation_addons": "Diese Seite konnte nicht korrekt angezeigt werden. Dieser Fehler wird wahrscheinlich durch ein Browser-Add-on oder automatische Übersetzungswerkzeuge verursacht.", "error.unexpected_crash.next_steps": "Versuche, die Seite neu zu laden. Wenn das nicht helfen sollte, kannst du das Webinterface von Mastodon vermutlich über einen anderen Browser erreichen – oder du verwendest eine mobile (native) App.", @@ -470,7 +471,7 @@ "ignore_notifications_modal.not_following_title": "Benachrichtigungen von Profilen ignorieren, denen du nicht folgst?", "ignore_notifications_modal.private_mentions_title": "Benachrichtigungen von unerwünschten privaten Erwähnungen ignorieren?", "info_button.label": "Hilfe", - "info_button.what_is_alt_text": "

Was ist Alt-Text?

Alt-Text bietet Bildbeschreibungen für Personen mit einer Sehschwäche, einer schlechten Internetverbindung und für alle, die zusätzlichen Kontext möchten.

Du kannst die Zugänglichkeit und die Verständlichkeit für alle verbessern, indem du eine klare, genaue und objektive Bildbeschreibung hinzufügst.

  • Erfasse wichtige Elemente
  • Fasse Text in Bildern zusammen
  • Verwende einen korrekten Satzbau
  • Vermeide unwichtige Informationen
  • Konzentriere dich bei komplexen Darstellungen (z. B. Diagramme oder Karten) auf Trends und wichtige Erkenntnisse
", + "info_button.what_is_alt_text": "

Was ist Alt-Text?

Der Alt-Text bietet Bildbeschreibungen für blinde und sehbehinderte Menschen, aber auch für alle mit einer schlechten Internetverbindung und wer einen zusätzlichen Kontext möchten.

Du kannst die Barrierefreiheit und Verständlichkeit für alle verbessern, indem du eine klare, genaue und objektive Bildbeschreibung erstellst.

  • Erfasse wichtige Elemente
  • Fasse Texte bildlich zusammen
  • Verwende einen korrekten Satzbau
  • Vermeide unwichtige und überflüssige Informationen
  • Konzentriere dich bei komplexen Darstellungen (z. B. bei Diagrammen oder Karten) auf Veränderungen und Schlüsselwörter
", "interaction_modal.action": "Melde dich auf deinem Mastodon-Server an, damit du mit dem Beitrag von {name} interagieren kannst.", "interaction_modal.go": "Los", "interaction_modal.no_account_yet": "Du hast noch kein Konto?", @@ -584,8 +585,8 @@ "navigation_bar.follows_and_followers": "Follower und Folge ich", "navigation_bar.import_export": "Importieren und exportieren", "navigation_bar.lists": "Listen", - "navigation_bar.live_feed_local": "Live-Feed (lokal)", - "navigation_bar.live_feed_public": "Live-Feed (öffentlich)", + "navigation_bar.live_feed_local": "Live-Feed (Dieser Server)", + "navigation_bar.live_feed_public": "Live-Feed (Alle Server)", "navigation_bar.logout": "Abmelden", "navigation_bar.moderation": "Moderation", "navigation_bar.more": "Mehr", @@ -693,7 +694,7 @@ "notifications.filter.follows": "Folgt", "notifications.filter.mentions": "Erwähnungen", "notifications.filter.polls": "Umfrageergebnisse", - "notifications.filter.statuses": "Neue Beiträge von Personen, denen du folgst", + "notifications.filter.statuses": "Neue Beiträge von Profilen, denen du folgst", "notifications.grant_permission": "Berechtigung erteilen.", "notifications.group": "{count} Benachrichtigungen", "notifications.mark_as_read": "Alle Benachrichtigungen als gelesen markieren", @@ -759,7 +760,7 @@ "privacy.quote.disabled": "{visibility} – niemand darf zitieren", "privacy.quote.limited": "{visibility} – eingeschränktes Zitieren", "privacy.unlisted.additional": "Das Verhalten ist wie bei „Öffentlich“, jedoch gibt es einige Einschränkungen. Der Beitrag wird nicht in „Live-Feeds“, „Erkunden“, Hashtags oder über die Mastodon-Suchfunktion auffindbar sein – selbst wenn die zugehörige Einstellung aktiviert wurde.", - "privacy.unlisted.long": "Verborgen vor Suchergebnissen, Trends und öffentlichen Timelines auf Mastodon", + "privacy.unlisted.long": "Verborgen vor Suchergebnissen, Trends und öffentlichen Timelines", "privacy.unlisted.short": "Öffentlich (still)", "privacy_policy.last_updated": "Stand: {date}", "privacy_policy.title": "Datenschutzerklärung", @@ -903,7 +904,7 @@ "status.edited_x_times": "{count, plural, one {{count}-mal} other {{count}-mal}} bearbeitet", "status.embed": "Code zum Einbetten", "status.favourite": "Favorisieren", - "status.favourites": "{count, plural, one {Mal favorisiert} other {Mal favorisiert}}", + "status.favourites_count": "{count, plural, one {{counter} Mal favorisiert} other {{counter} Mal favorisiert}}", "status.filter": "Beitrag filtern", "status.history.created": "{name} erstellte {date}", "status.history.edited": "{name} bearbeitete {date}", @@ -926,8 +927,8 @@ "status.quote_error.limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.", "status.quote_error.muted_account_hint.title": "Dieser Beitrag wurde ausgeblendet, weil du @{name} stummgeschaltet hast.", "status.quote_error.not_available": "Beitrag nicht verfügbar", - "status.quote_error.pending_approval": "Beitragsveröffentlichung ausstehend", - "status.quote_error.pending_approval_popout.body": "Auf Mastodon kann festgelegt werden, ob man zitiert werden möchte. Wir warten auf die Genehmigung des ursprünglichen Profils. Bis dahin steht deine Beitragsveröffentlichung noch aus.", + "status.quote_error.pending_approval": "Veröffentlichung ausstehend", + "status.quote_error.pending_approval_popout.body": "Auf Mastodon kannst du selbst bestimmen, ob du von anderen zitiert werden darfst oder nicht – oder nur nach individueller Genehmigung. Wir warten in diesem Fall noch auf die Genehmigung des ursprünglichen Profils. Bis dahin steht die Veröffentlichung deines Beitrags mit dem zitierten Post noch aus.", "status.quote_error.revoked": "Beitrag durch Autor*in entfernt", "status.quote_followers_only": "Nur Follower können diesen Beitrag zitieren", "status.quote_manual_review": "Zitierte*r überprüft manuell", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Ändern, wer zitieren darf", "status.quote_post_author": "Zitierter Beitrag von @{name}", "status.quote_private": "Private Beiträge können nicht zitiert werden", - "status.quotes": "{count, plural, one {Mal zitiert} other {Mal zitiert}}", "status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.", "status.quotes.local_other_disclaimer": "Durch Autor*in abgelehnte Zitate werden nicht angezeigt.", "status.quotes.remote_other_disclaimer": "Nur Zitate von {domain} werden hier garantiert angezeigt. Durch Autor*in abgelehnte Zitate werden nicht angezeigt.", + "status.quotes_count": "{count, plural, one {{counter} Mal zitiert} other {{counter} Mal zitiert}}", "status.read_more": "Gesamten Beitrag anschauen", "status.reblog": "Teilen", "status.reblog_or_quote": "Teilen oder zitieren", "status.reblog_private": "Erneut mit deinen Followern teilen", "status.reblogged_by": "{name} teilte", - "status.reblogs": "{count, plural, one {Mal geteilt} other {Mal geteilt}}", "status.reblogs.empty": "Diesen Beitrag hat bisher noch niemand geteilt. Sobald es jemand tut, wird das Profil hier erscheinen.", + "status.reblogs_count": "{count, plural, one {{counter} Mal geteilt} other {{counter} Mal geteilt}}", "status.redraft": "Löschen und neu erstellen", "status.remove_bookmark": "Lesezeichen entfernen", "status.remove_favourite": "Aus Favoriten entfernen", @@ -1031,7 +1032,7 @@ "visibility_modal.privacy_label": "Sichtbarkeit", "visibility_modal.quote_followers": "Nur Follower", "visibility_modal.quote_label": "Wer darf mich zitieren?", - "visibility_modal.quote_nobody": "Nur ich", + "visibility_modal.quote_nobody": "Nur ich selbst", "visibility_modal.quote_public": "Alle", "visibility_modal.save": "Speichern" } diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index c6770d3b5bf..82be328a229 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Όλα καθαρά! Δεν υπάρχει τίποτα εδώ. Όταν λαμβάνεις νέες ειδοποιήσεις, αυτές θα εμφανίζονται εδώ σύμφωνα με τις ρυθμίσεις σου.", "empty_column.notifications": "Δεν έχεις ειδοποιήσεις ακόμα. Όταν άλλα άτομα αλληλεπιδράσουν μαζί σου, θα το δεις εδώ.", "empty_column.public": "Δεν υπάρχει τίποτα εδώ! Γράψε κάτι δημόσιο ή ακολούθησε χειροκίνητα χρήστες από άλλους διακομιστές για να τη γεμίσεις", + "error.no_hashtag_feed_access": "Γίνετε μέλος ή συνδεθείτε για να δείτε και να ακολουθήσετε αυτήν την ετικέτα.", "error.unexpected_crash.explanation": "Είτε λόγω σφάλματος στον κώδικά μας ή λόγω ασυμβατότητας με τον περιηγητή, η σελίδα δε μπόρεσε να εμφανιστεί σωστά.", "error.unexpected_crash.explanation_addons": "Η σελίδα δεν μπόρεσε να εμφανιστεί σωστά. Το πρόβλημα οφείλεται πιθανόν σε κάποια επέκταση του φυλλομετρητή ή σε κάποιο αυτόματο εργαλείο μετάφρασης.", "error.unexpected_crash.next_steps": "Δοκίμασε να ανανεώσεις τη σελίδα. Αν αυτό δε βοηθήσει, ίσως να μπορέσεις να χρησιμοποιήσεις το Mastodon μέσω διαφορετικού περιηγητή ή κάποιας εφαρμογής.", @@ -730,7 +731,7 @@ "onboarding.profile.display_name": "Εμφανιζόμενο όνομα", "onboarding.profile.display_name_hint": "Το πλήρες ή το διασκεδαστικό σου όνομα…", "onboarding.profile.note": "Βιογραφικό", - "onboarding.profile.note_hint": "Μπορείτε να @αναφέρετε άλλα άτομα ή #hashtags…", + "onboarding.profile.note_hint": "Μπορείς να @επισημάνεις άλλα άτομα ή #ετικέτες…", "onboarding.profile.save_and_continue": "Αποθήκευση και συνέχεια", "onboarding.profile.title": "Ρύθμιση προφίλ", "onboarding.profile.upload_avatar": "Μεταφόρτωση εικόνας προφίλ", @@ -769,7 +770,7 @@ "quote_error.quote": "Επιτρέπεται μόνο μία παράθεση τη φορά.", "quote_error.unauthorized": "Δεν είστε εξουσιοδοτημένοι να παραθέσετε αυτή την ανάρτηση.", "quote_error.upload": "Η παράθεση δεν επιτρέπεται με συνημμένα πολυμέσων.", - "recommended": "Προτεινόμενα", + "recommended": "Προτείνεται", "refresh": "Ανανέωση", "regeneration_indicator.please_stand_by": "Παρακαλούμε περίμενε.", "regeneration_indicator.preparing_your_home_feed": "Ετοιμάζουμε την αρχική σου ροή…", @@ -903,7 +904,7 @@ "status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}", "status.embed": "Απόκτηση κώδικα ενσωμάτωσης", "status.favourite": "Αγαπημένα", - "status.favourites": "{count, plural, one {αγαπημένο} other {αγαπημένα}}", + "status.favourites_count": "{count, plural, one {{counter} αγαπημένο} other {{counter} αγαπημένα}}", "status.filter": "Φιλτράρισμα αυτής της ανάρτησης", "status.history.created": "{name} δημιούργησε στις {date}", "status.history.edited": "{name} επεξεργάστηκε στις {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Αλλάξτε ποιός μπορεί να κάνει παράθεση", "status.quote_post_author": "Παρατίθεται μια ανάρτηση από @{name}", "status.quote_private": "Ιδιωτικές αναρτήσεις δεν μπορούν να παρατεθούν", - "status.quotes": "{count, plural, one {# παράθεση} other {# παραθέσεις}}", "status.quotes.empty": "Κανείς δεν έχει παραθέσει αυτή την ανάρτηση ακόμα. Μόλις το κάνει, θα εμφανιστεί εδώ.", "status.quotes.local_other_disclaimer": "Οι παραθέσεις που απορρίφθηκαν από τον συντάκτη δε θα εμφανιστούν.", "status.quotes.remote_other_disclaimer": "Μόνο παραθέσεις από το {domain} είναι εγγυημένες ότι θα εμφανιστούν εδώ. Παραθέσεις που απορρίφθηκαν από τον συντάκτη δε θα εμφανιστούν.", + "status.quotes_count": "{count, plural, one {{counter} παράθεση} other {{counter} παραθέσεις}}", "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.reblogs_count": "{count, plural, one {{counter} ενίσχυση} other {{counter} ενισχύσεις}}", "status.redraft": "Σβήσε & ξαναγράψε", "status.remove_bookmark": "Αφαίρεση σελιδοδείκτη", "status.remove_favourite": "Κατάργηση από τα αγαπημένα", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 11c0c78c64b..f13bd697d1e 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -888,7 +888,6 @@ "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", "status.embed": "Get embed code", "status.favourite": "Favourite", - "status.favourites": "{count, plural, one {favourite} other {favourites}}", "status.filter": "Filter this post", "status.history.created": "{name} created {date}", "status.history.edited": "{name} edited {date}", @@ -906,7 +905,6 @@ "status.read_more": "Read more", "status.reblog": "Boost", "status.reblogged_by": "{name} boosted", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.", "status.redraft": "Delete & re-draft", "status.remove_bookmark": "Remove bookmark", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 07f80c3a51a..1108f8194eb 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "All clear! There is nothing here. When you receive new notifications, they will appear here according to your settings.", "empty_column.notifications": "You don't have any notifications yet. When other people interact with you, you will see it here.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.no_hashtag_feed_access": "Join or log in to view and follow this hashtag.", "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", "status.embed": "Get embed code", "status.favourite": "Favorite", - "status.favourites": "{count, plural, one {favorite} other {favorites}}", + "status.favourites_count": "{count, plural, one {{counter} favorite} other {{counter} favorites}}", "status.filter": "Filter this post", "status.history.created": "{name} created {date}", "status.history.edited": "{name} edited {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Change who can quote", "status.quote_post_author": "Quoted a post by @{name}", "status.quote_private": "Private posts cannot be quoted", - "status.quotes": "{count, plural, one {quote} other {quotes}}", "status.quotes.empty": "No one has quoted this post yet. When someone does, it will show up here.", "status.quotes.local_other_disclaimer": "Quotes rejected by the author will not be shown.", "status.quotes.remote_other_disclaimer": "Only quotes from {domain} are guaranteed to be shown here. Quotes rejected by the author will not be shown.", + "status.quotes_count": "{count, plural, one {{counter} quote} other {{counter} quotes}}", "status.read_more": "Read more", "status.reblog": "Boost", "status.reblog_or_quote": "Boost or quote", "status.reblog_private": "Share again with your followers", "status.reblogged_by": "{name} boosted", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.", + "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", "status.redraft": "Delete & re-draft", "status.remove_bookmark": "Remove bookmark", "status.remove_favourite": "Remove from favorites", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 7ea1c474b42..f8cfa07f9c7 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -855,7 +855,6 @@ "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}", "status.embed": "Akiri enkorpigan kodon", "status.favourite": "Ŝatata", - "status.favourites": "{count, plural, one {plej ŝatata} other {plej ŝatataj}}", "status.filter": "Filtri ĉi tiun afiŝon", "status.history.created": "{name} kreis {date}", "status.history.edited": "{name} redaktis {date}", @@ -881,14 +880,12 @@ "status.quote_policy_change": "Ŝanĝi kiu povas citi", "status.quote_post_author": "Citis afiŝon de @{name}", "status.quote_private": "Privataj afiŝoj ne povas esti cititaj", - "status.quotes": "{count, plural,one {citaĵo} other {citaĵoj}}", "status.quotes.empty": "Neniu citis ĉi tiun afiŝon ankoraŭ. Kiam iu faros tion, ĝi aperos ĉi tie.", "status.read_more": "Legi pli", "status.reblog": "Diskonigi", "status.reblog_or_quote": "Diskonigi aŭ citi", "status.reblog_private": "Kunhavigu denove kun viaj sekvantoj", "status.reblogged_by": "{name} diskonigis", - "status.reblogs": "{count, plural, one {diskonigo} other {diskonigoj}}", "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun afiŝon. Kiam iu faras tion, ri aperos ĉi tie.", "status.redraft": "Forigi kaj reskribi", "status.remove_bookmark": "Forigi legosignon", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 54578bf9ac3..f9398ca59e6 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada acá. Cuando recibás nuevas notificaciones, aparecerán acá, acorde a tu configuración.", "empty_column.notifications": "Todavía no tenés ninguna notificación. Cuando otras cuentas interactúen con vos, vas a ver la notificación acá.", "empty_column.public": "¡Naranja! Escribí algo públicamente, o seguí usuarios manualmente de otros servidores para ir llenando esta línea temporal", + "error.no_hashtag_feed_access": "Create una cuenta o iniciá sesión para seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador web, esta página no se pudo mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente es causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intentá recargar la página. Si eso no ayuda, podés usar Mastodon a través de un navegador web diferente o aplicación nativa.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}", "status.embed": "Obtener código para insertar", "status.favourite": "Marcar como favorito", - "status.favourites": "{count, plural, one {vez marcado como favorito} other {veces marcado como favorito}}", + "status.favourites_count": "{count, plural,one {{counter} favorito}other {{counter} favoritos}}", "status.filter": "Filtrar este mensaje", "status.history.created": "Creado por {name}, {date}", "status.history.edited": "Editado por {name}, {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Cambiá quién puede citar", "status.quote_post_author": "Se citó un mensaje de @{name}", "status.quote_private": "No se pueden citar los mensajes privados", - "status.quotes": "{count, plural, one {voto} other {votos}}", "status.quotes.empty": "Todavía nadie citó este mensaje. Cuando alguien lo haga, se mostrará acá.", "status.quotes.local_other_disclaimer": "Las citas rechazadas por el autor no serán mostradas.", "status.quotes.remote_other_disclaimer": "Solo las citas de {domain} están garantizadas de ser mostradas acá. Las citas rechazadas por el autor no serán mostradas.", + "status.quotes_count": "{count, plural,one {{counter} cita} other {{counter} citas}}", "status.read_more": "Leé más", "status.reblog": "Adherir", "status.reblog_or_quote": "Adherir o citar", "status.reblog_private": "Compartir de nuevo con tus seguidores", "status.reblogged_by": "{name} adhirió", - "status.reblogs": "{count, plural, one {adhesión} other {adhesiones}}", "status.reblogs.empty": "Todavía nadie adhirió a este mensaje. Cuando alguien lo haga, se mostrará acá.", + "status.reblogs_count": "{count, plural,one {{counter} adhesión} other {{counter} adhesiones}}", "status.redraft": "Eliminar mensaje original y editarlo", "status.remove_bookmark": "Quitar marcador", "status.remove_favourite": "Quitar de favoritos", @@ -1029,7 +1030,7 @@ "visibility_modal.helper.unlisted_quoting": "Cuando otras cuentas te citen, sus publicaciones también se ocultarán de las líneas temporales de tendencias.", "visibility_modal.instructions": "Controlá quién puede interactuar con este mensaje. También podés aplicar los ajustes para todos los mensajes futuros accediendo a «Configuración» > «Configuración predeterminada de mensajes».", "visibility_modal.privacy_label": "Visibilidad", - "visibility_modal.quote_followers": "Solo para seguidores", + "visibility_modal.quote_followers": "Solo seguidores", "visibility_modal.quote_label": "Quién puede citar", "visibility_modal.quote_nobody": "Solo yo", "visibility_modal.quote_public": "Cualquier cuenta", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 5bba3357960..69dcde9181e 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -174,7 +174,7 @@ "column.domain_blocks": "Dominios ocultados", "column.edit_list": "Editar lista", "column.favourites": "Favoritos", - "column.firehose": "Cronologías", + "column.firehose": "Feeds en vivo", "column.firehose_local": "Feed en vivo para este servidor", "column.firehose_singular": "Feed en vivo", "column.follow_requests": "Solicitudes de seguimiento", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada aquí. Cuando recibas nuevas notificaciones, aparecerán aquí conforme a tu configuración.", "empty_column.notifications": "No tienes ninguna notificación aún. Interactúa con otros para empezar una conversación.", "empty_column.public": "¡Aquí no hay nada! Escribe algo públicamente o sigue manualmente a usuarios de otros servidores para llenarlo", + "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador, esta página no se ha podido mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente fue causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intenta actualizar la página. Si eso no ayuda, es posible que puedas usar Mastodon a través de otro navegador o aplicación nativa.", @@ -805,7 +806,7 @@ "report.forward": "Reenviar a {target}", "report.forward_hint": "Esta cuenta es de otro servidor. ¿Enviar una copia anonimizada del informe allí también?", "report.mute": "Silenciar", - "report.mute_explanation": "No veras sus publiaciones. Todavía pueden seguirte y ver tus publicaciones y no sabrán que están silenciados.", + "report.mute_explanation": "No verás sus publicaciones. Todavía pueden seguirte y ver tus publicaciones y no sabrán que están silenciados.", "report.next": "Siguiente", "report.placeholder": "Comentarios adicionales", "report.reasons.dislike": "No me gusta", @@ -903,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}", "status.embed": "Obtener código para incrustar", "status.favourite": "Favorito", - "status.favourites": "{count, plural, one {favorito} other {favoritos}}", + "status.favourites_count": "{count, plural,one {{counter} favorito}other {{counter} favoritos}}", "status.filter": "Filtrar esta publicación", "status.history.created": "{name} creó {date}", "status.history.edited": "{name} editado {date}", @@ -920,11 +921,11 @@ "status.quote": "Citar", "status.quote.cancel": "Cancelar cita", "status.quote_error.blocked_account_hint.title": "Esta publicación se ocultó porque bloqueaste a @{name}.", - "status.quote_error.blocked_domain_hint.title": "Esta publicación está oculta porque has bloqueado @{domain}.", + "status.quote_error.blocked_domain_hint.title": "Este post está oculto porque bloqueaste {domain}.", "status.quote_error.filtered": "Oculto debido a uno de tus filtros", "status.quote_error.limited_account_hint.action": "Mostrar de todas formas", "status.quote_error.limited_account_hint.title": "Esta cuenta ha sido ocultada por los moderadores de {domain}.", - "status.quote_error.muted_account_hint.title": "Esta publicación está oculta porque has silenciado a @{name}.", + "status.quote_error.muted_account_hint.title": "Esta publicación está oculta porque silenciaste a @{name}.", "status.quote_error.not_available": "Publicación no disponible", "status.quote_error.pending_approval": "Publicación pendiente", "status.quote_error.pending_approval_popout.body": "En Mastodon, puedes controlar si alguien puede citarte. Esta publicación está pendiente mientras obtenemos la aprobación del autor original.", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Cambia quién puede citarte", "status.quote_post_author": "Ha citado una publicación de @{name}", "status.quote_private": "Las publicaciones privadas no pueden citarse", - "status.quotes": "{count, plural,one {cita} other {citas}}", "status.quotes.empty": "Nadie ha citado esta publicación todavía. Cuando alguien lo haga, aparecerá aquí.", - "status.quotes.local_other_disclaimer": "Las citas rechazadas por el autor no se mostrarán.", - "status.quotes.remote_other_disclaimer": "Solo se garantiza que se muestren las citas de {domain}. Las citas rechazadas por el autor no se mostrarán.", + "status.quotes.local_other_disclaimer": "Las citas rechazadas pro el autor no serán mostradas.", + "status.quotes.remote_other_disclaimer": "Solo las citas hechas por {domain} están garantizadas a ser vistas aquí. Las citas rechazadas por el autor no serán mostradas.", + "status.quotes_count": "{count, plural,one {{counter} cita} other {{counter} citas}}", "status.read_more": "Leer más", "status.reblog": "Impulsar", "status.reblog_or_quote": "Impulsar o citar", "status.reblog_private": "Compartir de nuevo con tus seguidores", "status.reblogged_by": "Impulsado por {name}", - "status.reblogs": "{count, plural, one {impulso} other {impulsos}}", "status.reblogs.empty": "Nadie impulsó esta publicación todavía. Cuando alguien lo haga, aparecerá aquí.", + "status.reblogs_count": "{count, plural,one {{counter} impulso} other {{counter} impulsos}}", "status.redraft": "Borrar y volver a borrador", "status.remove_bookmark": "Eliminar marcador", "status.remove_favourite": "Eliminar de favoritos", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 433976c3880..1501531fc3c 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -249,7 +249,7 @@ "confirmations.missing_alt_text.secondary": "Publicar de todos modos", "confirmations.missing_alt_text.title": "¿Deseas añadir texto alternativo?", "confirmations.mute.confirm": "Silenciar", - "confirmations.private_quote_notify.cancel": "Volver a editar", + "confirmations.private_quote_notify.cancel": "Volver a la edición", "confirmations.private_quote_notify.confirm": "Publicar", "confirmations.private_quote_notify.do_not_show_again": "No mostrar este mensaje de nuevo", "confirmations.private_quote_notify.message": "La persona a la que estás citando y otras mencionadas serán notificadas y podrán ver tu publicación, incluso si no te siguen.", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada aquí. Cuando recibas nuevas notificaciones, aparecerán aquí conforme a tu configuración.", "empty_column.notifications": "Aún no tienes ninguna notificación. Cuando otras personas interactúen contigo, aparecerán aquí.", "empty_column.public": "¡No hay nada aquí! Escribe algo públicamente, o sigue usuarios de otras instancias manualmente para llenarlo", + "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador, esta página no se ha podido mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente fue causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intenta actualizar la página. Si eso no ayuda, quizás puedas usar Mastodon desde otro navegador o aplicación nativa.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}", "status.embed": "Obtener código para incrustar", "status.favourite": "Favorito", - "status.favourites": "{count, plural, one {favorito} other {favoritos}}", + "status.favourites_count": "{count, plural,one {{counter} favorito}other {{counter} favoritos}}", "status.filter": "Filtrar esta publicación", "status.history.created": "{name} creó {date}", "status.history.edited": "{name} editó {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Cambia quién puede citarte", "status.quote_post_author": "Ha citado una publicación de @{name}", "status.quote_private": "Las publicaciones privadas no pueden ser citadas", - "status.quotes": "{count, plural,one {cita} other {citas}}", "status.quotes.empty": "Nadie ha citado esta publicación todavía. Cuando alguien lo haga, aparecerá aquí.", "status.quotes.local_other_disclaimer": "Las citas rechazadas por el autor no se mostrarán.", "status.quotes.remote_other_disclaimer": "Solo se garantiza que se muestren las citas de {domain}. Las citas rechazadas por el autor no se mostrarán.", + "status.quotes_count": "{count, plural,one {{counter} cita} other {{counter} citas}}", "status.read_more": "Leer más", "status.reblog": "Impulsar", "status.reblog_or_quote": "Impulsar o citar", "status.reblog_private": "Compartir de nuevo con tus seguidores", "status.reblogged_by": "Impulsado por {name}", - "status.reblogs": "{count, plural, one {impulso} other {impulsos}}", "status.reblogs.empty": "Nadie ha impulsado esta publicación todavía. Cuando alguien lo haga, aparecerá aquí.", + "status.reblogs_count": "{count, plural,one {{counter} impulso} other {{counter} impulsos}}", "status.redraft": "Borrar y volver a borrador", "status.remove_bookmark": "Eliminar marcador", "status.remove_favourite": "Eliminar de favoritos", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index 79083a47807..c19aa7d6b9c 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -36,7 +36,7 @@ "account.familiar_followers_two": "Jälgijateks {name1} ja {name2}", "account.featured": "Esiletõstetud", "account.featured.accounts": "Profiilid", - "account.featured.hashtags": "Sildid", + "account.featured.hashtags": "Teemaviited", "account.featured_tags.last_status_at": "Viimane postitus {date}", "account.featured_tags.last_status_never": "Postitusi pole", "account.follow": "Jälgi", @@ -126,8 +126,8 @@ "annual_report.summary.highlighted_post.by_replies": "kõige vastatum postitus", "annual_report.summary.highlighted_post.possessive": "omanik {name}", "annual_report.summary.most_used_app.most_used_app": "enim kasutatud äpp", - "annual_report.summary.most_used_hashtag.most_used_hashtag": "enim kasutatud silt", - "annual_report.summary.most_used_hashtag.none": "Pole", + "annual_report.summary.most_used_hashtag.most_used_hashtag": "enim kasutatud teemaviide", + "annual_report.summary.most_used_hashtag.none": "Puudub", "annual_report.summary.new_posts.new_posts": "uus postitus", "annual_report.summary.percentile.text": "See paneb su top {domain} kasutajate hulka.", "annual_report.summary.percentile.we_wont_tell_bernie": "Vägev.", @@ -204,7 +204,7 @@ "compose.saved.body": "Postitus salvestatud.", "compose_form.direct_message_warning_learn_more": "Vaata lisa", "compose_form.encryption_warning": "Postitused Mastodonis ei ole otsast-otsani krüpteeritud. Ära jaga mingeid delikaatseid andmeid Mastodoni kaudu.", - "compose_form.hashtag_warning": "See postitus ei ilmu ühegi märksõna all, kuna pole avalik. Vaid avalikud postitused on märksõnade kaudu leitavad.", + "compose_form.hashtag_warning": "See postitus ei ilmu ühegi teemaviite all, kuna pole avalik. Vaid avalikud postitused on teemaviidete kaudu leitavad.", "compose_form.lock_disclaimer": "Su konto ei ole {locked}. Igaüks saab sind jälgida, et näha su ainult-jälgijatele postitusi.", "compose_form.lock_disclaimer.lock": "lukus", "compose_form.placeholder": "Millest mõtled?", @@ -332,8 +332,8 @@ "emoji_button.search_results": "Otsitulemused", "emoji_button.symbols": "Sümbolid", "emoji_button.travel": "Reisimine & kohad", - "empty_column.account_featured.me": "Sa pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?", - "empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?", + "empty_column.account_featured.me": "Sa pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid teemaviiteid või sõbra kasutajakontot?", + "empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid teemaviiteid või sõbra kasutajakontot?", "empty_column.account_featured_other.unknown": "See kasutajakonto pole veel midagi esile tõstnud.", "empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks", "empty_column.account_suspended": "Konto kustutatud", @@ -349,14 +349,15 @@ "empty_column.favourited_statuses": "Pole veel lemmikpostitusi. Kui märgid mõne, näed neid siin.", "empty_column.favourites": "Keegi pole veel seda postitust lemmikuks märkinud. Kui keegi seda teeb, siis on ta nähtav siin.", "empty_column.follow_requests": "Pole hetkel ühtegi jälgimistaotlust. Kui saad mõne, näed neid siin.", - "empty_column.followed_tags": "Sa ei jälgi veel ühtegi märksõna. Kui jälgid, ilmuvad need siia.", - "empty_column.hashtag": "Selle sildi all ei ole ühtegi postitust.", + "empty_column.followed_tags": "Sa ei jälgi veel ühtegi teemaviidet. Kui jälgid, ilmuvad need siia.", + "empty_column.hashtag": "Selle teemaviite all ei ole ühtegi postitust.", "empty_column.home": "Su koduajajoon on tühi. Jälgi rohkemaid inimesi, et seda täita {suggestions}", "empty_column.list": "Siin loetelus pole veel midagi. Kui loetelu liikmed teevad uusi postitusi, näed neid siin.", "empty_column.mutes": "Sa pole veel ühtegi kasutajat summutanud.", "empty_column.notification_requests": "Kõik tühi! Siin pole mitte midagi. Kui saad uusi teavitusi, ilmuvad need siin vastavalt sinu seadistustele.", "empty_column.notifications": "Ei ole veel teateid. Kui keegi suhtleb sinuga, näed seda siin.", "empty_column.public": "Siin pole midagi! Kirjuta midagi avalikku või jälgi ise kasutajaid täitmaks seda ruumi", + "error.no_hashtag_feed_access": "Selle teemaviite jälgimiseks liitu teenusega või logi sisse oma kasutajakontoga.", "error.unexpected_crash.explanation": "Meie poolse probleemi või veebilehitseja ühilduvusprobleemi tõttu ei suutnud me seda lehekülge korrektselt näidata.", "error.unexpected_crash.explanation_addons": "Seda lehte ei suudetud õigesti kuvada. Selle vea põhjustas arvatavasti mõni lehitseja lisand või automaattõlke tööriist.", "error.unexpected_crash.next_steps": "Proovi lehekülge uuesti avada. Kui see ei aita, võib proovida kasutada Mastodoni mõne muu veebilehitseja või äpi kaudu.", @@ -367,7 +368,7 @@ "explore.title": "Populaarsust koguv", "explore.trending_links": "Uudised", "explore.trending_statuses": "Postitused", - "explore.trending_tags": "Sildid", + "explore.trending_tags": "Teemaviited", "featured_carousel.current": "Postitus {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Esiletõstetud postitus} other {Esiletõstetud postitust}}", "featured_carousel.slide": "Postitus {current, number} / {max, number}", @@ -411,7 +412,7 @@ "follow_suggestions.similar_to_recently_followed_longer": "Sarnane profiilile, mida hiljuti jälgima hakkasid", "follow_suggestions.view_all": "Vaata kõiki", "follow_suggestions.who_to_follow": "Keda jälgida", - "followed_tags": "Jälgitavad märksõnad", + "followed_tags": "Jälgitavad teemaviited", "footer.about": "Teave", "footer.about_this_server": "Serveri teave", "footer.directory": "Profiilikataloog", @@ -424,17 +425,17 @@ "generic.saved": "Salvestatud", "getting_started.heading": "Alustamine", "hashtag.admin_moderation": "Ava modereerimisliides #{name} jaoks", - "hashtag.browse": "Sirvi #{hashtag} sildiga postitusi", - "hashtag.browse_from_account": "Sirvi @{name} kasutaja #{hashtag} sildiga postitusi", + "hashtag.browse": "Sirvi #{hashtag} teemaviitega postitusi", + "hashtag.browse_from_account": "Sirvi @{name} kasutaja #{hashtag} teemaviitega postitusi", "hashtag.column_header.tag_mode.all": "ja {additional}", - "hashtag.column_header.tag_mode.any": "või {additional}", - "hashtag.column_header.tag_mode.none": "ilma {additional}", + "hashtag.column_header.tag_mode.any": "või teemaviide {additional}", + "hashtag.column_header.tag_mode.none": "ilma teemaviiteta {additional}", "hashtag.column_settings.select.no_options_message": "Soovitusi ei leitud", "hashtag.column_settings.select.placeholder": "Sisesta sildid…", "hashtag.column_settings.tag_mode.all": "Kõik need", "hashtag.column_settings.tag_mode.any": "Mõni neist", "hashtag.column_settings.tag_mode.none": "Mitte ükski neist", - "hashtag.column_settings.tag_toggle": "Kaasa lisamärked selle tulba jaoks", + "hashtag.column_settings.tag_toggle": "Kaasa lisasildid selle veeru jaoks", "hashtag.counter_by_accounts": "{count, plural, one {{counter} osalejaga} other {{counter} osalejaga}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} postitusega} other {{counter} postitusega}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} postitust} other {{counter} postitust}} täna", @@ -580,7 +581,7 @@ "navigation_bar.favourites": "Lemmikud", "navigation_bar.filters": "Summutatud sõnad", "navigation_bar.follow_requests": "Jälgimistaotlused", - "navigation_bar.followed_tags": "Jälgitavad märksõnad", + "navigation_bar.followed_tags": "Jälgitavad teemaviited", "navigation_bar.follows_and_followers": "Jälgitavad ja jälgijad", "navigation_bar.import_export": "Import ja eksport", "navigation_bar.lists": "Loetelud", @@ -730,7 +731,7 @@ "onboarding.profile.display_name": "Näidatav nimi", "onboarding.profile.display_name_hint": "Su täisnimi või naljanimi…", "onboarding.profile.note": "Elulugu", - "onboarding.profile.note_hint": "Saad @mainida teisi kasutajaid või #sildistada…", + "onboarding.profile.note_hint": "Saad @mainida teisi kasutajaid või lisada #teemaviidet…", "onboarding.profile.save_and_continue": "Salvesta ja jätka", "onboarding.profile.title": "Profiili seadistamine", "onboarding.profile.upload_avatar": "Laadi üles profiilipilt", @@ -758,7 +759,7 @@ "privacy.quote.anyone": "{visibility}, kõik võivad tsiteerida", "privacy.quote.disabled": "{visibility}, tsiteerimine pole lubatud", "privacy.quote.limited": "{visibility}, tsiteerimine on piiratud", - "privacy.unlisted.additional": "See on olemuselt küll avalik, aga postitus ei ilmu voogudes ega märksõnades, lehitsedes ega Mastodoni otsingus, isegi kui konto on seadistustes avalik.", + "privacy.unlisted.additional": "See on olemuselt küll avalik, aga postitus ei ilmu voogudes ega teemaviidetes, lehitsedes ega Mastodoni otsingus, isegi kui konto on seadistustes avalik.", "privacy.unlisted.long": "Peidetud Mastodoni otsingutulemustest, pupulaarsust koguva sisu voost ja avalikult ajajoonelt", "privacy.unlisted.short": "Vaikselt avalik", "privacy_policy.last_updated": "Viimati uuendatud {date}", @@ -845,7 +846,7 @@ "search.placeholder": "Otsi", "search.quick_action.account_search": "Sobivaid profiile {x}", "search.quick_action.go_to_account": "Mine profiili {x}", - "search.quick_action.go_to_hashtag": "Ava silt {x}", + "search.quick_action.go_to_hashtag": "Ava teemaviide {x}", "search.quick_action.open_url": "Ava URL Mastodonis", "search.quick_action.status_search": "Sobivad postitused {x}", "search.search_or_paste": "Otsi või kleebi URL", @@ -861,7 +862,7 @@ "search_results.all": "Kõik", "search_results.hashtags": "Sildid", "search_results.no_results": "Tulemusi pole.", - "search_results.no_search_yet": "Proovi otsida postitusi, profiile või silte.", + "search_results.no_search_yet": "Proovi otsida postitusi, profiile või teemaviiteid.", "search_results.see_all": "Vaata kõiki", "search_results.statuses": "Postitused", "search_results.title": "Otsi märksõna: {q}", @@ -903,7 +904,7 @@ "status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}", "status.embed": "Hangi manustamiskood", "status.favourite": "Lemmik", - "status.favourites": "{count, plural, one {lemmik} other {lemmikut}}", + "status.favourites_count": "{count, plural, one {{counter} lemmik} other {{counter} lemmikut}}", "status.filter": "Filtreeri seda postitust", "status.history.created": "{name} lõi {date}", "status.history.edited": "{name} muutis {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Muuda neid, kes võivad tsiteerida", "status.quote_post_author": "Tsiteeris kasutaja @{name} postitust", "status.quote_private": "Otsepostituste tsiteerimine pole võimalik", - "status.quotes": "{count, plural, one {# tsiteerimine} other {# tsiteerimist}}", "status.quotes.empty": "Keegi pole seda postitust veel tsiteerinud. Kui keegi seda teeb, siis on ta nähtav siin.", "status.quotes.local_other_disclaimer": "Autori poolt tagasilükatud tsitaate ei kuvata.", "status.quotes.remote_other_disclaimer": "Kui kasutaja on {domain} domeenist, siis siin on tagatud vaid tema tsitaatide näitamine. Autori poolt tagasilükatud tsitaate ei kuvata.", + "status.quotes_count": "{count, plural, one {{counter} tsiteerimine} other {{counter} tsiteerimist}}", "status.read_more": "Loe veel", "status.reblog": "Jaga", "status.reblog_or_quote": "Anna hoogu või tsiteeri", "status.reblog_private": "Jaga uuesti oma jälgijatele", "status.reblogged_by": "{name} jagas", - "status.reblogs": "{count, plural, one {jagamine} other {jagamist}}", "status.reblogs.empty": "Keegi pole seda postitust veel jaganud. Kui keegi seda teeb, siis on ta nähtav siin.", + "status.reblogs_count": "{count, plural, one {{counter} jagamine} other {{counter} jagamist}}", "status.redraft": "Kustuta & alga uuesti", "status.remove_bookmark": "Eemalda järjehoidja", "status.remove_favourite": "Eemalda lemmikute seast", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 990d4217c54..688ba097676 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -903,7 +903,6 @@ "status.edited_x_times": "{count, plural, one {behin} other {{count} aldiz}} editatua", "status.embed": "Lortu txertatzeko kodea", "status.favourite": "Gogokoa", - "status.favourites": "{count, plural, one {gogoko} other {gogoko}}", "status.filter": "Iragazi bidalketa hau", "status.history.created": "{name} erabiltzaileak sortua {date}", "status.history.edited": "{name} erabiltzaileak editatua {date}", @@ -935,7 +934,6 @@ "status.quote_policy_change": "Aldatu nork aipa zaitzakeen", "status.quote_post_author": "@{name} erabiltzailearen bidalketaren aipua", "status.quote_private": "Bidalketa pribatuak ezin dira aipatu", - "status.quotes": "{count, plural, one {aipu} other {aipu}}", "status.quotes.empty": "Momentuz inork ez du bidalketa hau aipatu. Norbaitek eginez gero, hemen agertuko da.", "status.quotes.local_other_disclaimer": "Egileak errefusatutako aipuak ez dira erakutsiko.", "status.quotes.remote_other_disclaimer": "{domain} zerbitzariko aipuak baino ez daude bermatuta hemen. Egileak errefusatutako aipuak ez dira erakutsiko.", @@ -944,7 +942,6 @@ "status.reblog_or_quote": "Bultzatu edo aipatu", "status.reblog_private": "Partekatu berriz zure jarraitzaileekin", "status.reblogged_by": "{name}(r)en bultzada", - "status.reblogs": "{count, plural, one {bultzada} other {bultzada}}", "status.reblogs.empty": "Inork ez dio bultzada eman bidalketa honi oraindik. Inork egiten badu, hemen agertuko da.", "status.redraft": "Ezabatu eta berridatzi", "status.remove_bookmark": "Kendu laster-marka", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 61bace5e2b0..093984a09e1 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,5 +1,5 @@ { - "about.blocks": "کارسازهای نظارت شده", + "about.blocks": "کارسازهای نظارت شده", "about.contact": "تماس:", "about.default_locale": "پیش‌گزیده", "about.disclaimer": "ماستودون نرم‌افزار آزاد و نشان تجاری یک شرکت غیر انتفاعی با مسئولیت محدود آلمانی است.", @@ -903,7 +903,6 @@ "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد", "status.embed": "گرفتن کد تعبیه", "status.favourite": "برگزیده‌", - "status.favourites": "{count, plural, one {برگزیده} other {برگزیده}}", "status.filter": "پالایش این فرسته", "status.history.created": "توسط {name} در {date} ایجاد شد", "status.history.edited": "توسط {name} در {date} ویرایش شد", @@ -935,7 +934,6 @@ "status.quote_policy_change": "تغییر کسانی که می‌توانند نقل کنند", "status.quote_post_author": "فرسته‌ای از @{name} نقل شد", "status.quote_private": "فرسته‌های خصوصی نمی‌توانند نقل شوند", - "status.quotes": "{count, plural, one {نقل} other {نقل}}", "status.quotes.empty": "هنوز کسی این فرسته را نقل نکرده. وقتی کسی چنین کند این‌جا نشان داده خواهد شد.", "status.quotes.local_other_disclaimer": "نقل‌هایی که به دست نگارنده رد شده باشند نشان داده نخواهند شد.", "status.quotes.remote_other_disclaimer": "تنها نقل‌ها از {domain} تضمین نمایش در این‌جا را دارند. نقل‌های رد شده به دست نگاره نشان داده نخواهند شد.", @@ -944,7 +942,6 @@ "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": "برداشتن نشانک", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index ab5e102b0b5..9b3aacb9b4a 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -233,7 +233,7 @@ "confirmations.discard_draft.edit.cancel": "Palaa muokkaamaan", "confirmations.discard_draft.edit.message": "Jatkaminen tuhoaa kaikki muutokset, joita olet tehnyt julkaisuun, jota olet parhaillaan muokkaamassa.", "confirmations.discard_draft.edit.title": "Hylätäänkö luonnosjulkaisusi muutokset?", - "confirmations.discard_draft.post.cancel": "Palaa lunnokseen", + "confirmations.discard_draft.post.cancel": "Palaa luonnokseen", "confirmations.discard_draft.post.message": "Jatkaminen tuhoaa julkaisun, jota olet parhaillaan laatimassa.", "confirmations.discard_draft.post.title": "Hylätäänkö luonnosjulkaisusi?", "confirmations.discard_edit_media.confirm": "Hylkää", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Olet ajan tasalla! Täällä ei ole mitään uutta kerrottavaa. Kun saat uusia ilmoituksia, ne näkyvät täällä asetustesi mukaisesti.", "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun muut ovat vuorovaikutuksessa kanssasi, näet sen täällä.", "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti tai seuraa muiden palvelinten käyttäjiä, niin saat sisältöä", + "error.no_hashtag_feed_access": "Liity tai kirjaudu sisään, niin voit tarkastella ja seurata tätä aihetunnistetta.", "error.unexpected_crash.explanation": "Sivua ei voida näyttää oikein ohjelmointivirheen tai selaimen yhteensopivuusvajeen vuoksi.", "error.unexpected_crash.explanation_addons": "Sivua ei voitu näyttää oikein. Tämä virhe johtuu todennäköisesti selaimen lisäosasta tai automaattisista käännöstyökaluista.", "error.unexpected_crash.next_steps": "Kokeile päivittää sivu. Jos se ei auta, voi Mastodonin käyttö ehkä onnistua eri selaimella tai natiivisovelluksella.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}", "status.embed": "Hanki upotuskoodi", "status.favourite": "Suosikki", - "status.favourites": "{count, plural, one {suosikki} other {suosikkia}}", + "status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} suosikkia}}", "status.filter": "Suodata tämä julkaisu", "status.history.created": "{name} loi {date}", "status.history.edited": "{name} muokkasi {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Vaihda, kuka voi lainata", "status.quote_post_author": "Lainaa käyttäjän @{name} julkaisua", "status.quote_private": "Yksityisiä julkaisuja ei voi lainata", - "status.quotes": "{count, plural, one {lainaus} other {lainausta}}", "status.quotes.empty": "Kukaan ei ole vielä lainannut tätä julkaisua. Kun joku tekee niin, se tulee tähän näkyviin.", "status.quotes.local_other_disclaimer": "Tekijän hylkäämiä lainauksia ei näytetä.", "status.quotes.remote_other_disclaimer": "Vain palvelimen {domain} lainaukset näkyvät taatusti tässä. Tekijän hylkäämiä lainauksia ei näytetä.", + "status.quotes_count": "{count, plural, one {{counter} lainaus} other {{counter} lainausta}}", "status.read_more": "Näytä enemmän", "status.reblog": "Tehosta", "status.reblog_or_quote": "Tehosta tai lainaa", "status.reblog_private": "Jaa uudelleen seuraajiesi kanssa", "status.reblogged_by": "{name} tehosti", - "status.reblogs": "{count, plural, one {tehostus} other {tehostusta}}", "status.reblogs.empty": "Kukaan ei ole vielä tehostanut tätä julkaisua. Kun joku tekee niin, tulee hän tähän näkyviin.", + "status.reblogs_count": "{count, plural, one {{counter} tehostus} other {{counter} tehostusta}}", "status.redraft": "Poista ja palauta muokattavaksi", "status.remove_bookmark": "Poista kirjanmerkki", "status.remove_favourite": "Poista suosikeista", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json index 37db5a1b159..82c1616ddf2 100644 --- a/app/javascript/mastodon/locales/fil.json +++ b/app/javascript/mastodon/locales/fil.json @@ -1,6 +1,7 @@ { "about.blocks": "Mga pinatimping server", "about.contact": "Kontak:", + "about.default_locale": "Default", "about.disclaimer": "Ang Mastodon ay software na malaya at bukas-na-pinagmulan, at isang tatak-pangkalakal ng Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Hindi makuha ang dahilan", "about.domain_blocks.preamble": "Sa kadalasan, hinahayaan ka ng Mastodon na makita ang mga content sa, at makipag-interact sa users ng, ibang servers sa fediverse. Narito ang exceptions na ginawa sa partikular na server na ito.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Limitado", "about.domain_blocks.suspended.explanation": "Walang data mula sa server na ito ang mapoproseso, maiimbak o maipagpapaplitan. Sa gayon. imposibleng magawa ang interaksiyon o komunikasyon sa ibang users sa server na ito.", "about.domain_blocks.suspended.title": "Suspendido", + "about.language_label": "Wika", "about.not_available": "Hindi available ang impormasyong ito.", "about.powered_by": "Decentralisadong social media na pinapagana ng {mastodon}", "about.rules": "Mga alituntunin ng server", @@ -19,21 +21,31 @@ "account.block_domain": "Hadlangan ang domain na {domain}", "account.block_short": "Hadlangan", "account.blocked": "Hinadlangan", + "account.blocking": "Pagharang", "account.cancel_follow_request": "I-kansela ang pagsunod", "account.copy": "I-sipi ang kawing sa profile", "account.direct": "Palihim banggitin si @{name}", "account.disable_notifications": "I-tigil ang pagpapaalam sa akin tuwing nagpopost si @{name}", + "account.domain_blocking": "Pag-block ng domain", "account.edit_profile": "Baguhin ang profile", + "account.edit_profile_short": "I-edit", "account.enable_notifications": "Ipaalam sa akin kapag nag-post si @{name}", "account.endorse": "I-tampok sa profile", "account.familiar_followers_many": "Sinusundan nina {name1}, {name2}, at {othersCount, plural, one {# iba pa na kilala mo} other {# na iba pa na kilala mo}}", "account.familiar_followers_one": "Sinusindan ni/ng {name1}", "account.familiar_followers_two": "Sinusindan nina {name1} at {name2}", "account.featured": "Itinatampok", + "account.featured.accounts": "Mga Profile", + "account.featured.hashtags": "Mga Hashtag", "account.featured_tags.last_status_at": "Huling post noong {date}", "account.featured_tags.last_status_never": "Walang mga post", "account.follow": "Sundan", "account.follow_back": "Sundan pabalik", + "account.follow_back_short": "I-follow back", + "account.follow_request": "Humiling na mag-follow", + "account.follow_request_cancel": "I-cancel ang request", + "account.follow_request_cancel_short": "Cancel", + "account.follow_request_short": "Request", "account.followers": "Mga tagasunod", "account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.", "account.followers_counter": "{count, plural, one {{counter} tagasunod} other {{counter} tagasunod}}", @@ -56,15 +68,29 @@ "account.mute_notifications_short": "I-mute ang mga abiso", "account.mute_short": "I-mute", "account.muted": "Naka-mute", + "account.muting": "Pag-mute", + "account.mutual": "Pina-follow nyo ang isa't-isa", "account.no_bio": "Walang nakalaan na paglalarawan.", "account.open_original_page": "Buksan ang pinagmulang pahina", "account.posts": "Mga post", + "account.posts_with_replies": "Mga Post at Reply", + "account.remove_from_followers": "Alisin si {name} sa mga follower", "account.report": "I-ulat si/ang @{name}", "account.requested_follow": "Hinihiling ni {name} na sundan ka", + "account.requests_to_follow_you": "Mga Request para i-fillow ka", "account.share": "Ibahagi ang profile ni @{name}", "account.show_reblogs": "Ipakita ang mga pagpapalakas mula sa/kay {name}", + "account.statuses_counter": "{count,plural,one {{counter} i-post} other {{counter} mga post}}", + "account.unblock": "I-unblock si @{name}", + "account.unblock_domain": "I-unblock ang domain {domain}", + "account.unblock_domain_short": "I-unblock", + "account.unblock_short": "I-unblock", "account.unendorse": "Huwag itampok sa profile", "account.unfollow": "Huwag nang sundan", + "account.unmute": "I-unmute si @{name}", + "account.unmute_notifications_short": "I-unmute ang mga notification", + "account.unmute_short": "I-unmute", + "account_note.placeholder": "I-click para magdagdag ng note", "admin.dashboard.retention.cohort_size": "Mga bagong tagagamit", "alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.", "audio.hide": "Itago ang tunog", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index c0d9a1b4f34..3197a8ccaec 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Alt er klárt! Her er einki. Tá tú fært nýggjar fráboðanir, síggjast tær her sambært tínum stillingum.", "empty_column.notifications": "Tú hevur ongar fráboðanir enn. Tá onnur samskifta við teg, so sær tú fráboðaninar her.", "empty_column.public": "Einki er her! Skriva okkurt alment ella fylg brúkarum frá øðrum ambætarum fyri at fylla tilfar í", + "error.no_hashtag_feed_access": "Melda til ella rita inn fyri at síggja og fylgja hesum frámerkinum.", "error.unexpected_crash.explanation": "Orsakað av einum feili í okkara kotu ella orsakað av at kagin hjá tær ikki er sambæriligur við skipanina, so bar ikki til at vísa hesa síðuna rætt.", "error.unexpected_crash.explanation_addons": "Hendan síðan kundi ikki vísast rætt. Orsøkin til feilin er sannlíkt vegna eina uppíbygging í kaganum hjá tær ella vegna amboð til sjálvvirkandi umseting.", "error.unexpected_crash.next_steps": "Royn at lesa síðuna inn av nýggjum. Hjálpir tað ikki, so kann vera, at tað ber til at brúka Mastodon við einum øðrum kaga ella við eini app.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Rættað {count, plural, one {{count} ferð} other {{count} ferð}}", "status.embed": "Fá kodu at seta inn", "status.favourite": "Dámdur postur", - "status.favourites": "{count, plural, one {yndispostur} other {yndispostar}}", + "status.favourites_count": "{count, plural, one {{counter} yndispostur} other {{counter} yndispostar}}", "status.filter": "Filtrera hendan postin", "status.history.created": "{name} stovnað {date}", "status.history.edited": "{name} rættað {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Broyt hvør kann sitera", "status.quote_post_author": "Siteraði ein post hjá @{name}", "status.quote_private": "Privatir postar kunnu ikki siterast", - "status.quotes": "{count, plural, one {sitat} other {sitat}}", "status.quotes.empty": "Eingin hevur siterað hendan postin enn. Tá onkur siterar postin, verður hann sjónligur her.", "status.quotes.local_other_disclaimer": "Sitatir, sum eru avvíst av høvundanum, verða ikki víst.", "status.quotes.remote_other_disclaimer": "Einans sitatir frá {domain} vera garanterað víst her. Sitatir, sum eru avvíst av høvundanum, verða ikki víst.", + "status.quotes_count": "{count, plural, one {{counter} sitat} other {{counter} sitat}}", "status.read_more": "Les meira", "status.reblog": "Stimbra", "status.reblog_or_quote": "Stimbra ella sitera", "status.reblog_private": "Deil aftur við tínum fylgjarum", "status.reblogged_by": "{name} stimbrað", - "status.reblogs": "{count, plural, one {stimbran} other {stimbranir}}", "status.reblogs.empty": "Eingin hevur stimbrað hendan postin enn. Tá onkur stimbrar postin, verður hann sjónligur her.", + "status.reblogs_count": "{count, plural, one {{counter} stimbran} other {{counter} stimbranir}}", "status.redraft": "Strika & ger nýggja kladdu", "status.remove_bookmark": "Gloym", "status.remove_favourite": "Strika í yndismerkjum", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 69e737598d9..cc896dfadce 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -21,21 +21,21 @@ "account.block_domain": "Bloquer le domaine {domain}", "account.block_short": "Bloquer", "account.blocked": "Bloqué·e", - "account.blocking": "Bloquer", + "account.blocking": "Bloqué", "account.cancel_follow_request": "Retirer cette demande d'abonnement", - "account.copy": "Copier le lien vers le profil", + "account.copy": "Copier le lien du profil", "account.direct": "Mention privée @{name}", "account.disable_notifications": "Ne plus me notifier quand @{name} publie", - "account.domain_blocking": "Bloquer domaine", + "account.domain_blocking": "Domaine bloqué", "account.edit_profile": "Modifier le profil", "account.edit_profile_short": "Modifier", "account.enable_notifications": "Me notifier quand @{name} publie", "account.endorse": "Inclure sur profil", - "account.familiar_followers_many": "Suivi par {name1},{name2}, et {othersCount, plural,one {une personne connue} other {# autres personnel connues}}", + "account.familiar_followers_many": "Suivi par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", "account.familiar_followers_one": "Suivi par {name1}", "account.familiar_followers_two": "Suivi par {name1} et {name2}", "account.featured": "En vedette", - "account.featured.accounts": "Profiles", + "account.featured.accounts": "Profils", "account.featured.hashtags": "Hashtags", "account.featured_tags.last_status_at": "Dernière publication {date}", "account.featured_tags.last_status_never": "Aucune publication", @@ -45,11 +45,11 @@ "account.follow_request": "Demande d’abonnement", "account.follow_request_cancel": "Annuler la demande", "account.follow_request_cancel_short": "Annuler", - "account.follow_request_short": "Requête", + "account.follow_request_short": "Demander à suivre", "account.followers": "abonné·e·s", "account.followers.empty": "Personne ne suit ce compte pour l'instant.", "account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}", - "account.followers_you_know_counter": "Vous connaissez {counter}", + "account.followers_you_know_counter": "{count, plural, one {{counter} suivi}, other {{counter} suivis}}", "account.following": "Abonné·e", "account.following_counter": "{count, plural, one {{counter} abonnement} other {{counter} abonnements}}", "account.follows.empty": "Ce compte ne suit personne présentement.", @@ -74,10 +74,10 @@ "account.open_original_page": "Ouvrir la page d'origine", "account.posts": "Publications", "account.posts_with_replies": "Publications et réponses", - "account.remove_from_followers": "Retirer {name} des suiveurs", + "account.remove_from_followers": "Retirer {name} des abonnés", "account.report": "Signaler @{name}", "account.requested_follow": "{name} a demandé à vous suivre", - "account.requests_to_follow_you": "Demande a vous suivre", + "account.requests_to_follow_you": "Demande à vous suivre", "account.share": "Partager le profil de @{name}", "account.show_reblogs": "Afficher les boosts de @{name}", "account.statuses_counter": "{count, plural, one {{counter} message} other {{counter} messages}}", @@ -106,7 +106,7 @@ "alert.unexpected.title": "Oups!", "alt_text_badge.title": "Texte alternatif", "alt_text_modal.add_alt_text": "Ajouter un texte alternatif", - "alt_text_modal.add_text_from_image": "Ajouter le texte provenant de l'image", + "alt_text_modal.add_text_from_image": "Extraire le texte de l'image", "alt_text_modal.cancel": "Annuler", "alt_text_modal.change_thumbnail": "Changer la vignette", "alt_text_modal.describe_for_people_with_hearing_impairments": "Décrire pour les personnes ayant des difficultés d’audition…", @@ -528,7 +528,7 @@ "limited_account_hint.action": "Afficher le profil quand même", "limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.", "link_preview.author": "Par {name}", - "link_preview.more_from_author": "Plus via {name}", + "link_preview.more_from_author": "Voir plus de {name}", "link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}", "lists.add_member": "Ajouter", "lists.add_to_list": "Ajouter à la liste", @@ -759,7 +759,7 @@ "privacy.quote.disabled": "{visibility}, citations désactivées", "privacy.quote.limited": "{visibility}, citations limitées", "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.", - "privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, aux tendances et aux échéanciers publics", + "privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, des tendances et des fils publics", "privacy.unlisted.short": "Public discret", "privacy_policy.last_updated": "Dernière mise à jour {date}", "privacy_policy.title": "Politique de confidentialité", @@ -903,7 +903,7 @@ "status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}", "status.embed": "Obtenir le code d'intégration", "status.favourite": "Ajouter aux favoris", - "status.favourites": "{count, plural, one {favori} other {favoris}}", + "status.favourites_count": "{count, plural, one {{counter} favori} other {{counter} favoris}}", "status.filter": "Filtrer cette publication", "status.history.created": "créé par {name} {date}", "status.history.edited": "modifié par {name} {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "Changer qui peut vous citer", "status.quote_post_author": "A cité un message par @{name}", "status.quote_private": "Les publications privées ne peuvent pas être citées", - "status.quotes": " {count, plural, one {quote} other {quotes}}", "status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.", "status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.", "status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.", + "status.quotes_count": "{count, plural, one {{counter} citation} other {{counter} citations}}", "status.read_more": "En savoir plus", "status.reblog": "Booster", "status.reblog_or_quote": "Boost ou citation", "status.reblog_private": "Partagez à nouveau avec vos abonnés", "status.reblogged_by": "{name} a boosté", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Personne n’a encore boosté cette publication. Lorsque quelqu’un le fera, elle apparaîtra ici.", + "status.reblogs_count": "{count, plural, one {{counter} partage} other {{counter} partages}}", "status.redraft": "Supprimer et réécrire", "status.remove_bookmark": "Retirer des signets", "status.remove_favourite": "Retirer des favoris", @@ -1026,8 +1026,8 @@ "visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.", "visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.", "visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.", - "visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché dans les calendriers tendances.", - "visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans Préférences > Valeurs par d'éfaut de publication.", + "visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché des fils tendances.", + "visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans Préférences > Valeurs par défaut de publication.", "visibility_modal.privacy_label": "Visibilité", "visibility_modal.quote_followers": "Abonné·e·s seulement", "visibility_modal.quote_label": "Autoriser les citations pour", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 92bc2d61cef..1b9a3580e36 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -4,7 +4,7 @@ "about.default_locale": "Défaut", "about.disclaimer": "Mastodon est un logiciel libre, open-source et une marque déposée de Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Raison non disponible", - "about.domain_blocks.preamble": "Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateur⋅rices de n'importe quel autre serveur dans le fédivers. Voici les exceptions qui ont été faites sur ce serveur-là.", + "about.domain_blocks.preamble": "Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateurs et utilisatrices de n'importe quel autre serveur dans le fédivers. Voici les exceptions qui ont été faites sur ce serveur.", "about.domain_blocks.silenced.explanation": "Vous ne verrez généralement pas les profils et le contenu de ce serveur, à moins que vous ne les recherchiez explicitement ou que vous ne choisissiez de les suivre.", "about.domain_blocks.silenced.title": "Limité", "about.domain_blocks.suspended.explanation": "Aucune donnée de ce serveur ne sera traitée, enregistrée ou échangée, rendant impossible toute interaction ou communication avec les comptes de ce serveur.", @@ -21,21 +21,21 @@ "account.block_domain": "Bloquer le domaine {domain}", "account.block_short": "Bloquer", "account.blocked": "Bloqué·e", - "account.blocking": "Bloquer", + "account.blocking": "Bloqué", "account.cancel_follow_request": "Annuler l'abonnement", - "account.copy": "Copier le lien vers le profil", + "account.copy": "Copier le lien du profil", "account.direct": "Mention privée @{name}", "account.disable_notifications": "Ne plus me notifier quand @{name} publie quelque chose", - "account.domain_blocking": "Bloquer domaine", + "account.domain_blocking": "Domaine bloqué", "account.edit_profile": "Modifier le profil", "account.edit_profile_short": "Modifier", "account.enable_notifications": "Me notifier quand @{name} publie quelque chose", "account.endorse": "Recommander sur votre profil", - "account.familiar_followers_many": "Suivi par {name1},{name2}, et {othersCount, plural,one {une personne connue} other {# autres personnel connues}}", + "account.familiar_followers_many": "Suivi par {name1}, {name2}, et {othersCount, plural, one {une autre personne que vous suivez} other {# autres personnes que vous suivez}}", "account.familiar_followers_one": "Suivi par {name1}", "account.familiar_followers_two": "Suivi par {name1} et {name2}", "account.featured": "En vedette", - "account.featured.accounts": "Profiles", + "account.featured.accounts": "Profils", "account.featured.hashtags": "Hashtags", "account.featured_tags.last_status_at": "Dernier message le {date}", "account.featured_tags.last_status_never": "Aucun message", @@ -45,11 +45,11 @@ "account.follow_request": "Demande d’abonnement", "account.follow_request_cancel": "Annuler la demande", "account.follow_request_cancel_short": "Annuler", - "account.follow_request_short": "Requête", + "account.follow_request_short": "Demander à suivre", "account.followers": "Abonné·e·s", "account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.", "account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}", - "account.followers_you_know_counter": "Vous connaissez {counter}", + "account.followers_you_know_counter": "{count, plural, one {{counter} suivi}, other {{counter} suivis}}", "account.following": "Abonnements", "account.following_counter": "{count, plural, one {{counter} abonnement} other {{counter} abonnements}}", "account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour l’instant.", @@ -74,10 +74,10 @@ "account.open_original_page": "Ouvrir la page d'origine", "account.posts": "Messages", "account.posts_with_replies": "Messages et réponses", - "account.remove_from_followers": "Retirer {name} des suiveurs", + "account.remove_from_followers": "Retirer {name} des abonnés", "account.report": "Signaler @{name}", "account.requested_follow": "{name} a demandé à vous suivre", - "account.requests_to_follow_you": "Demande a vous suivre", + "account.requests_to_follow_you": "Demande à vous suivre", "account.share": "Partager le profil de @{name}", "account.show_reblogs": "Afficher les partages de @{name}", "account.statuses_counter": "{count, plural, one {{counter} message} other {{counter} messages}}", @@ -106,7 +106,7 @@ "alert.unexpected.title": "Oups !", "alt_text_badge.title": "Texte alternatif", "alt_text_modal.add_alt_text": "Ajouter un texte alternatif", - "alt_text_modal.add_text_from_image": "Ajouter le texte provenant de l'image", + "alt_text_modal.add_text_from_image": "Extraire le texte de l'image", "alt_text_modal.cancel": "Annuler", "alt_text_modal.change_thumbnail": "Changer la vignette", "alt_text_modal.describe_for_people_with_hearing_impairments": "Décrire pour les personnes ayant des difficultés d’audition…", @@ -528,7 +528,7 @@ "limited_account_hint.action": "Afficher le profil quand même", "limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.", "link_preview.author": "Par {name}", - "link_preview.more_from_author": "Plus via {name}", + "link_preview.more_from_author": "Voir plus de {name}", "link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}", "lists.add_member": "Ajouter", "lists.add_to_list": "Ajouter à la liste", @@ -759,7 +759,7 @@ "privacy.quote.disabled": "{visibility}, citations désactivées", "privacy.quote.limited": "{visibility}, citations limitées", "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.", - "privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, aux tendances et aux échéanciers publics", + "privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, des tendances et des fils publics", "privacy.unlisted.short": "Public discret", "privacy_policy.last_updated": "Dernière mise à jour {date}", "privacy_policy.title": "Politique de confidentialité", @@ -903,7 +903,7 @@ "status.edited_x_times": "Modifié {count, plural, one {{count} fois} other {{count} fois}}", "status.embed": "Obtenir le code d'intégration", "status.favourite": "Ajouter aux favoris", - "status.favourites": "{count, plural, one {favori} other {favoris}}", + "status.favourites_count": "{count, plural, one {{counter} favori} other {{counter} favoris}}", "status.filter": "Filtrer ce message", "status.history.created": "créé par {name} {date}", "status.history.edited": "modifié par {name} {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "Changer qui peut vous citer", "status.quote_post_author": "A cité un message par @{name}", "status.quote_private": "Les publications privées ne peuvent pas être citées", - "status.quotes": " {count, plural, one {quote} other {quotes}}", "status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.", "status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.", "status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.", + "status.quotes_count": "{count, plural, one {{counter} citation} other {{counter} citations}}", "status.read_more": "Lire la suite", "status.reblog": "Partager", "status.reblog_or_quote": "Boost ou citation", "status.reblog_private": "Partagez à nouveau avec vos abonnés", "status.reblogged_by": "{name} a partagé", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Personne n’a encore partagé ce message. Lorsque quelqu’un le fera, il apparaîtra ici.", + "status.reblogs_count": "{count, plural, one {{counter} partage} other {{counter} partages}}", "status.redraft": "Supprimer et réécrire", "status.remove_bookmark": "Retirer des marque-pages", "status.remove_favourite": "Retirer des favoris", @@ -1026,8 +1026,8 @@ "visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.", "visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.", "visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.", - "visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché dans les calendriers tendances.", - "visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans Préférences > Valeurs par d'éfaut de publication.", + "visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché des fils tendances.", + "visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans Préférences > Valeurs par défaut de publication.", "visibility_modal.privacy_label": "Visibilité", "visibility_modal.quote_followers": "Abonné·e·s seulement", "visibility_modal.quote_label": "Autoriser les citations pour", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index e965dc645d0..e460b29de03 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -838,7 +838,6 @@ "status.edited_x_times": "{count, plural, one {{count} kear} other {{count} kearen}} bewurke", "status.embed": "Koade om op te nimmen", "status.favourite": "Favoryt", - "status.favourites": "{count, plural, one {favoryt} other {favoriten}}", "status.filter": "Dit berjocht filterje", "status.history.created": "{name} makke dit {date}", "status.history.edited": "{name} bewurke dit {date}", @@ -856,7 +855,6 @@ "status.read_more": "Mear ynfo", "status.reblog": "Booste", "status.reblogged_by": "{name} hat boost", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Net ien hat dit berjocht noch boost. Wannear’t ien dit docht, falt dat hjir te sjen.", "status.redraft": "Fuortsmite en opnij opstelle", "status.remove_bookmark": "Blêdwizer fuortsmite", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index c99a3d4b63a..ed866f32ce6 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Gach soiléir! Níl aon rud anseo. Nuair a gheobhaidh tú fógraí nua, beidh siad le feiceáil anseo de réir do shocruithe.", "empty_column.notifications": "Níl aon fógraí agat fós. Nuair a dhéanann daoine eile idirghníomhú leat, feicfear anseo é.", "empty_column.public": "Faic anseo! Scríobh rud éigin go poiblí, nó lean úsáideoirí ar fhreastalaithe eile chun é a líonadh", + "error.no_hashtag_feed_access": "Bí linn nó logáil isteach chun an haischlib seo a fheiceáil agus a leanúint.", "error.unexpected_crash.explanation": "De bharr fabht inár gcód, nó fadhb le chomhoiriúnacht brabhsálaí, níorbh fhéadfadh an leathanach seo a léiriú i gceart.", "error.unexpected_crash.explanation_addons": "Ní taispeántar an leathanach seo mar is ceart. Is dócha go gcruthaíonn breiseán brabhsálaí nó uirlisí uathaistriúcháin an fhadhb seo.", "error.unexpected_crash.next_steps": "Bain triail as an leathanach a athnuachan. Mura gcabhraíonn sé sin, seans go mbeidh tú fós in ann Mastodon a úsáid trí bhrabhsálaí nó aip dhúchais eile.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}", "status.embed": "Faigh cód leabú", "status.favourite": "Is fearr leat", - "status.favourites": "{count, plural, one {a bhfuil grá agat do} two {gráite} few {gráite} many {gráite} other {gráite}}", + "status.favourites_count": "{count, plural,\n one {{counter} cheanán}\n two {{counter} cheanáin}\n few {{counter} ceanáin}\n many {{counter} ceanán}\n other {{counter} ceanáin}\n}", "status.filter": "Déan scagadh ar an bpostáil seo", "status.history.created": "Chruthaigh {name} {date}", "status.history.edited": "Curtha in eagar ag {name} in {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Athraigh cé a fhéadann luachan a thabhairt", "status.quote_post_author": "Luaigh mé post le @{name}", "status.quote_private": "Ní féidir poist phríobháideacha a lua", - "status.quotes": "{count, plural, one {sliocht} few {sliocht} other {sliocht}}", "status.quotes.empty": "Níl an post seo luaite ag aon duine go fóill. Nuair a dhéanann duine é, taispeánfar anseo é.", "status.quotes.local_other_disclaimer": "Ní thaispeánfar sleachta ar dhiúltaigh an t-údar dóibh.", "status.quotes.remote_other_disclaimer": "Níl ráthaíocht ann go dtaispeánfar anseo ach sleachta ó {domain}. Ní thaispeánfar sleachta ar dhiúltaigh an t-údar dóibh.", + "status.quotes_count": "{count, plural,\n one {{counter} athfhriotal}\n two {{counter} athfhriotail}\n few {{counter} athfhriotail}\n many {{counter} athfhriotal}\n other {{counter} athfhriotail}\n}", "status.read_more": "Léan a thuilleadh", "status.reblog": "Treisiú", "status.reblog_or_quote": "Borradh nó luachan", "status.reblog_private": "Roinn arís le do leanúna", "status.reblogged_by": "Mhol {name}", - "status.reblogs": "{count, plural, one {buaic} other {buaic}}", "status.reblogs.empty": "Níor mhol éinne an phostáil seo fós. Nuair a mholfaidh duine éigin í, taispeánfar anseo é sin.", + "status.reblogs_count": "{count, plural,\n one {{counter} athfhriotal}\n two {{counter} athfhriotail}\n few {{counter} athfhriotail}\n many {{counter} athfhriotal}\n other {{counter} athfhriotail}\n}", "status.redraft": "Scrios ⁊ athdhréachtaigh", "status.remove_bookmark": "Bain leabharmharc", "status.remove_favourite": "Bain ó cheanáin", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index c47aaf36e70..e24a7d4608f 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -903,7 +903,6 @@ "status.edited_x_times": "Chaidh a dheasachadh {count, plural, one {{count} turas} two {{count} thuras} few {{count} tursan} other {{count} turas}}", "status.embed": "Faigh còd leabachaidh", "status.favourite": "Cuir ris na h-annsachdan", - "status.favourites": "{count, plural, one {annsachd} two {annsachd} few {annsachdan} other {annsachd}}", "status.filter": "Criathraich am post seo", "status.history.created": "Chruthaich {name} {date} e", "status.history.edited": "Dheasaich {name} {date} e", @@ -935,7 +934,6 @@ "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.", @@ -944,7 +942,6 @@ "status.reblog_or_quote": "Brosnaich no luaidh", "status.reblog_private": "Co-roinn leis an luchd-leantainn agad a-rithist", "status.reblogged_by": "’Ga bhrosnachadh le {name}", - "status.reblogs": "{count, plural, one {bhrosnachadh} two {bhrosnachadh} few {brosnachaidhean} other {brosnachadh}}", "status.reblogs.empty": "Chan deach am post seo a bhrosnachadh le duine sam bith fhathast. Nuair a bhrosnaicheas cuideigin e, nochdaidh iad an-seo.", "status.redraft": "Sguab às ⁊ dèan dreachd ùr", "status.remove_bookmark": "Thoir an comharra-lìn air falbh", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index d0839aaf0a8..06b6a5c8d60 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Todo ben! Nada por aquí. Cando recibas novas notificacións aparecerán aquí seguindo o criterio dos teus axustes.", "empty_column.notifications": "Aínda non tes notificacións. Aparecerán cando outras persoas interactúen contigo.", "empty_column.public": "Nada por aquí! Escribe algo de xeito público, ou segue de xeito manual usuarias doutros servidores para ir enchéndoo", + "error.no_hashtag_feed_access": "Crea unha conta ou accede para ver e seguir este cancelo.", "error.unexpected_crash.explanation": "Debido a un erro no noso código ou a unha compatilidade co teu navegador, esta páxina non pode ser amosada correctamente.", "error.unexpected_crash.explanation_addons": "Non se puido mostrar correctamente a páxina. Habitualmente este erro está causado por algún engadido do navegador ou ferramentas de tradución automática.", "error.unexpected_crash.next_steps": "Tenta actualizar a páxina. Se isto non axuda podes tamén empregar Mastodon noutro navegador ou aplicación nativa.", @@ -751,7 +752,7 @@ "privacy.change": "Axustar privacidade", "privacy.direct.long": "Todas as mencionadas na publicación", "privacy.direct.short": "Mención privada", - "privacy.private.long": "Só para seguidoras", + "privacy.private.long": "Só quen te segue", "privacy.private.short": "Seguidoras", "privacy.public.long": "Para todas dentro e fóra de Mastodon", "privacy.public.short": "Público", @@ -903,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}", "status.embed": "O código a incluír", "status.favourite": "Favorecer", - "status.favourites": "{count, plural, one {favorecemento} other {favorecementos}}", + "status.favourites_count": "{count, plural, one {{counter} favorecemento} other {{counter} favorecementos}}", "status.filter": "Filtrar esta publicación", "status.history.created": "{name} creouno o {date}", "status.history.edited": "{name} editouno o {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Cambia quen pode citarte", "status.quote_post_author": "Citou unha publicación de @{name}", "status.quote_private": "As publicacións privadas non se poden citar", - "status.quotes": "{count, plural, one {cita} other {citas}}", "status.quotes.empty": "Aínda ninguén citou esta publicación. Cando alguén o faga aparecerá aquí.", "status.quotes.local_other_disclaimer": "Non se mostrarán as citas rexeitadas pola autora.", "status.quotes.remote_other_disclaimer": "Só se garante que se mostren as citas do dominio {domain}. Non se mostrarán as citas rexeitadas pola persoa autora.", + "status.quotes_count": "{count, plural, one {{counter} cita} other {{counter} citas}}", "status.read_more": "Ler máis", "status.reblog": "Promover", "status.reblog_or_quote": "Promover ou citar", "status.reblog_private": "Volver a compartir coas túas seguidoras", "status.reblogged_by": "{name} promoveu", - "status.reblogs": "{count, plural, one {promoción} other {promocións}}", "status.reblogs.empty": "Aínda ninguén promoveu esta publicación. Cando alguén o faga, amosarase aquí.", + "status.reblogs_count": "{count, plural, one {{counter} promoción} other {{counter} promocións}}", "status.redraft": "Eliminar e reescribir", "status.remove_bookmark": "Eliminar marcador", "status.remove_favourite": "Retirar das favoritas", @@ -1031,7 +1032,7 @@ "visibility_modal.privacy_label": "Visibilidade", "visibility_modal.quote_followers": "Só para seguidoras", "visibility_modal.quote_label": "Quen pode citar", - "visibility_modal.quote_nobody": "Só para min", + "visibility_modal.quote_nobody": "Só eu", "visibility_modal.quote_public": "Calquera", "visibility_modal.save": "Gardar" } diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 2488d528594..cdd041df413 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "בום! אין פה כלום. כשיווצרו עוד התראות, הן יופיעו כאן על בסיס ההעדפות שלך.", "empty_column.notifications": "אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב.", "empty_column.public": "אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות", + "error.no_hashtag_feed_access": "הצטרפו או התחברו כדי לעקוב אחרי תגית זו.", "error.unexpected_crash.explanation": "עקב תקלה בקוד שלנו או בעיית תאימות דפדפן, לא ניתן להציג דף זה כראוי.", "error.unexpected_crash.explanation_addons": "לא ניתן להציג דף זה כראוי. הבעיה נגרמת כנראה עקב תוסף דפדפן או כלי תרגום אוטומטי.", "error.unexpected_crash.next_steps": "נסה/י לרענן את הדף. אם זה לא עוזר, אולי אפשר עדיין להשתמש במסטודון דרך דפדפן אחר או באמצעות אפליקציה ילידית.", @@ -903,7 +904,7 @@ "status.edited_x_times": "נערך {count, plural, one {פעם {count}} other {{count} פעמים}}", "status.embed": "העתקת קוד להטמעה", "status.favourite": "חיבוב", - "status.favourites": "{count, plural, one {חיבוב אחד} two {זוג חיבובים} other {# חיבובים}}", + "status.favourites_count": "{count, plural, one {חיבוב אחד} two {חיבוביים} many {{counter} חיבובים} other {{counter} חיבובים}}", "status.filter": "סנן הודעה זו", "status.history.created": "{name} יצר/ה {date}", "status.history.edited": "{name} ערך/ה {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "הגדרת הרשאה לציטוט הודעותיך", "status.quote_post_author": "ההודעה היא ציטוט של @{name}", "status.quote_private": "הודעות פרטיות לא ניתנות לציטוט", - "status.quotes": "{count, plural,one {ציטוט}other {ציטוטים}}", "status.quotes.empty": "עוד לא ציטטו את ההודעה הזו. כאשר זה יקרה, הציטוטים יופיעו כאן.", "status.quotes.local_other_disclaimer": "ציטוטים שיידחו על ידי המחברים המקוריים לא יוצגו.", "status.quotes.remote_other_disclaimer": "רק ציטוטים מהשרת {domain} מובטחים שיופיעו פה. ציטוטים שנדחו על ידי המצוטטים לא יופיעו.", + "status.quotes_count": "{count, plural, one {ציטוט אחד} two {שני ציטוטים} many {{counter} ציטוטים} other {{counter} ציטוטים}}", "status.read_more": "לקרוא עוד", "status.reblog": "הדהוד", "status.reblog_or_quote": "להדהד או לצטט", "status.reblog_private": "שיתוף מחדש עם העוקבות והעוקבים שלך", "status.reblogged_by": "{name} הידהד/ה:", - "status.reblogs": "{count, plural, one {הדהוד אחד} two {שני הדהודים} other {# הדהודים}}", "status.reblogs.empty": "עוד לא הידהדו את ההודעה הזו. כאשר זה יקרה, ההדהודים יופיעו כאן.", + "status.reblogs_count": "{count, plural, one {הידהוד אחד} two {שני הדהודים} many {{counter} הדהודים} other {{counter} הדהודים}}", "status.redraft": "מחיקה ועריכה מחדש", "status.remove_bookmark": "הסרת סימניה", "status.remove_favourite": "להסיר מרשימת המועדפים", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index e59dd790721..08082b53ba5 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Minden tiszta! Itt nincs semmi. Ha új értesítéseket kapsz, azok itt jelennek meg a beállításoknak megfelelően.", "empty_column.notifications": "Jelenleg még nincsenek értesítéseid. Ha mások kapcsolatba lépnek veled, ezek itt lesznek láthatóak.", "empty_column.public": "Jelenleg itt nincs semmi! Írj valamit nyilvánosan vagy kövess más kiszolgálón levő felhasználókat, hogy megtöltsd.", + "error.no_hashtag_feed_access": "Csatlakozz vagy jelentkezz be, hogy megtekintsd és kövesd ezt a hashtaget.", "error.unexpected_crash.explanation": "Egy kód- vagy böngészőkompatibilitási hiba miatt ez az oldal nem jeleníthető meg helyesen.", "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző kiegészítő vagy egy automatikus fordító okozza.", "error.unexpected_crash.next_steps": "Próbáld frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.", @@ -765,6 +766,7 @@ "privacy_policy.title": "Adatvédelmi szabályzat", "quote_error.edit": "Idézés nem adható hozzá bejegyzés szerkesztésekor.", "quote_error.poll": "Az idézés szavazások esetén nincs engedélyezve.", + "quote_error.private_mentions": "Az idézés közvetlen említések esetén nem engedélyezett.", "quote_error.quote": "Egyszerre csak egy idézet van engedélyezve.", "quote_error.unauthorized": "Nem idézheted ezt a bejegyzést.", "quote_error.upload": "Az idézés médiamellékletek esetén nem engedélyezett.", @@ -902,7 +904,7 @@ "status.edited_x_times": "{count, plural, one {{count} alkalommal} other {{count} alkalommal}} szerkesztve", "status.embed": "Beágyazási kód lekérése", "status.favourite": "Kedvenc", - "status.favourites": "{count, plural, one {kedvenc} other {kedvenc}}", + "status.favourites_count": "{count, plural, one {{counter} kedvenc} other {{counter} kedvenc}}", "status.filter": "E bejegyzés szűrése", "status.history.created": "{name} létrehozta: {date}", "status.history.edited": "{name} szerkesztette: {date}", @@ -934,17 +936,17 @@ "status.quote_policy_change": "Módosítás, hogy kik idézhetnek", "status.quote_post_author": "Idézte @{name} bejegyzését", "status.quote_private": "A privát bejegyzések nem idézhetőek", - "status.quotes": "{count, plural, one {idézés} other {idézés}}", "status.quotes.empty": "Senki sem idézte még ezt a bejegyzést. Ha valaki megteszi, itt fog megjelenni.", "status.quotes.local_other_disclaimer": "A szerző által elutasított idézések nem fognak megjelenni.", "status.quotes.remote_other_disclaimer": "Csak a(z) {domain} idézései jelennek meg itt garantáltan. A szerző által elutasított idézések nem fognak megjelenni.", + "status.quotes_count": "{count, plural, one {{counter} idézet} other {{counter} idézet}}", "status.read_more": "Bővebben", "status.reblog": "Megtolás", "status.reblog_or_quote": "Megtolás vagy idézés", "status.reblog_private": "Megosztás a követőiddel", "status.reblogged_by": "{name} megtolta", - "status.reblogs": "{count, plural, one {megtolás} other {megtolás}}", "status.reblogs.empty": "Senki sem tolta még meg ezt a bejegyzést. Ha valaki megteszi, itt fog megjelenni.", + "status.reblogs_count": "{count, plural, one {{counter} megtolás} other {{counter} megtolás}}", "status.redraft": "Törlés és újraírás", "status.remove_bookmark": "Könyvjelző eltávolítása", "status.remove_favourite": "Eltávolítás a kedvencek közül", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index eee89144d4b..a2247c6d9ef 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -157,6 +157,8 @@ "bundle_modal_error.close": "Clauder", "bundle_modal_error.message": "Un error ha occurrite durante le cargamento de iste schermo.", "bundle_modal_error.retry": "Tentar novemente", + "carousel.current": "Diapositiva {current, number} / {max, number}", + "carousel.slide": "Diapositiva {current, number} de {max, number}", "closed_registrations.other_server_instructions": "Perque Mastodon es decentralisate, tu pote crear un conto sur un altere servitor e totevia interager con iste servitor.", "closed_registrations_modal.description": "Crear un conto sur {domain} non es actualmente possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.", "closed_registrations_modal.find_another_server": "Cercar un altere servitor", @@ -173,6 +175,8 @@ "column.edit_list": "Modificar lista", "column.favourites": "Favorites", "column.firehose": "Fluxos in vivo", + "column.firehose_local": "Fluxo in vivo de iste servitor", + "column.firehose_singular": "Fluxo in vivo", "column.follow_requests": "Requestas de sequimento", "column.home": "Initio", "column.list_members": "Gerer le membros del lista", @@ -192,6 +196,7 @@ "community.column_settings.local_only": "Solmente local", "community.column_settings.media_only": "Solmente multimedia", "community.column_settings.remote_only": "A distantia solmente", + "compose.error.blank_post": "Le message non pote esser vacue.", "compose.language.change": "Cambiar le lingua", "compose.language.search": "Cercar linguas...", "compose.published.body": "Message publicate.", @@ -244,6 +249,11 @@ "confirmations.missing_alt_text.secondary": "Publicar totevia", "confirmations.missing_alt_text.title": "Adder texto alternative?", "confirmations.mute.confirm": "Silentiar", + "confirmations.private_quote_notify.cancel": "Retornar al modification", + "confirmations.private_quote_notify.confirm": "Publicar message", + "confirmations.private_quote_notify.do_not_show_again": "Non monstrar me iste message de novo", + "confirmations.private_quote_notify.message": "Le persona que tu cita e altere personas mentionate recipera un notification e potera vider tu message, etiam si illes non te seque.", + "confirmations.private_quote_notify.title": "Compartir on sequitores e usatores mentionate?", "confirmations.quiet_post_quote_info.dismiss": "Non rememorar me de novo", "confirmations.quiet_post_quote_info.got_it": "Comprendite", "confirmations.quiet_post_quote_info.message": "Quando tu cita un message public quiete, tu message non apparera in le chronologias de tendentias.", @@ -333,7 +343,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.disabled_feed": "Iste fluxo ha essite disactivate per le administratores 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.", @@ -358,7 +368,9 @@ "explore.trending_links": "Novas", "explore.trending_statuses": "Messages", "explore.trending_tags": "Hashtags", + "featured_carousel.current": "Message {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Message fixate} other {Messages fixate}}", + "featured_carousel.slide": "Message {current, number} de {max, number}", "filter_modal.added.context_mismatch_explanation": "Iste categoria de filtros non se applica al contexto in le qual tu ha accedite a iste message. Pro filtrar le message in iste contexto tamben, modifica le filtro.", "filter_modal.added.context_mismatch_title": "Contexto incoherente!", "filter_modal.added.expired_explanation": "Iste categoria de filtros ha expirate. Tu debe modificar le data de expiration pro applicar lo.", @@ -401,6 +413,7 @@ "follow_suggestions.who_to_follow": "Qui sequer", "followed_tags": "Hashtags sequite", "footer.about": "A proposito", + "footer.about_this_server": "A proposito", "footer.directory": "Directorio de profilos", "footer.get_app": "Obtener le application", "footer.keyboard_shortcuts": "Accessos directe de claviero", @@ -571,8 +584,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 vivo (local)", - "navigation_bar.live_feed_public": "Canal in vivo (public)", + "navigation_bar.live_feed_local": "Fluxo in vivo (local)", + "navigation_bar.live_feed_public": "Fluxo in vivo (public)", "navigation_bar.logout": "Clauder session", "navigation_bar.moderation": "Moderation", "navigation_bar.more": "Plus", @@ -752,6 +765,7 @@ "privacy_policy.title": "Politica de confidentialitate", "quote_error.edit": "Non es possibile adder citationes quando se modifica un message.", "quote_error.poll": "Non es permittite citar sondages.", + "quote_error.private_mentions": "Non es permittite citar con mentiones directe.", "quote_error.quote": "Solmente un citation al vice es permittite.", "quote_error.unauthorized": "Tu non es autorisate a citar iste message.", "quote_error.upload": "Non es permittite citar con annexos multimedial.", @@ -889,7 +903,7 @@ "status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}", "status.embed": "Obtener codice de incorporation", "status.favourite": "Adder al favorites", - "status.favourites": "{count, plural, one {favorite} other {favorites}}", + "status.favourites_count": "{count, plural, one {{counter} favorite} other {{counter} favorites}}", "status.filter": "Filtrar iste message", "status.history.created": "create per {name} le {date}", "status.history.edited": "modificate per {name} le {date}", @@ -918,7 +932,6 @@ "status.quote_policy_change": "Cambiar qui pote citar", "status.quote_post_author": "Ha citate un message de @{name}", "status.quote_private": "Le messages private non pote esser citate", - "status.quotes": "{count, plural, one {citation} other {citationes}}", "status.quotes.empty": "Necuno ha ancora citate iste message. Quando alcuno lo face, illo apparera hic.", "status.quotes.local_other_disclaimer": "Le citationes rejectate per le autor non essera monstrate.", "status.quotes.remote_other_disclaimer": "Solmente le citationes de {domain} se garanti de esser monstrate hic. Citationes rejectate per le autor non essera monstrate.", @@ -927,7 +940,6 @@ "status.reblog_or_quote": "Impulsar o citar", "status.reblog_private": "Re-compartir con tu sequitores", "status.reblogged_by": "Impulsate per {name}", - "status.reblogs": "{count, plural, one {impulso} other {impulsos}}", "status.reblogs.empty": "Necuno ha ancora impulsate iste message. Quando alcuno lo face, le impulsos apparera hic.", "status.redraft": "Deler e reconciper", "status.remove_bookmark": "Remover marcapagina", diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json index e978cfafc4b..1ee6784a905 100644 --- a/app/javascript/mastodon/locales/ie.json +++ b/app/javascript/mastodon/locales/ie.json @@ -612,7 +612,6 @@ "status.edited": "Ultimmen actualisat ye {date}", "status.edited_x_times": "Modificat {count, plural, one {{count} vez} other {{count} vezes}}", "status.favourite": "Favoritisar", - "status.favourites": "{count, plural, one {favorit} other {favorites}}", "status.filter": "Filtrar ti-ci posta", "status.history.created": "creat de {name} ye {date}", "status.history.edited": "modificat de {name} ye {date}", @@ -629,7 +628,6 @@ "status.read_more": "Leer plu", "status.reblog": "Boostar", "status.reblogged_by": "{name} boostat", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Ancor nequi ha boostat ti-ci posta. Quande alqui fa it, ilu va aparir ci.", "status.redraft": "Deleter & redacter", "status.remove_bookmark": "Remover marcator", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 07795897069..8ee26722501 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -787,7 +787,6 @@ "status.edited_x_times": "Redaktesis ye {count, plural, one {{count} foyo} other {{count} foyi}}", "status.embed": "Ganez adherkodexo", "status.favourite": "Favorizar", - "status.favourites": "{count, plural, one {stelumo} other {stelumi}}", "status.filter": "Filtragez ca posto", "status.history.created": "{name} kreis ye {date}", "status.history.edited": "{name} redaktis ye {date}", @@ -804,7 +803,6 @@ "status.read_more": "Lektez plu", "status.reblog": "Repetez", "status.reblogged_by": "{name} repetis", - "status.reblogs": "{count, plural, one {repeto} other {repeti}}", "status.reblogs.empty": "Nulu ja repetis ca posto. Kande ulu facas lo, lu montresos hike.", "status.redraft": "Efacez e riskisigez", "status.remove_bookmark": "Forigar lektosigno", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 70500d0b697..5af1f32ed52 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Allt hreint! Það er ekkert hér. Þegar þú færð nýjar tilkynningar, munu þær birtast hér í samræmi við stillingarnar þínar.", "empty_column.notifications": "Þú ert ekki ennþá með neinar tilkynningar. Vertu í samskiptum við aðra til að umræður fari af stað.", "empty_column.public": "Það er ekkert hér! Skrifaðu eitthvað opinberlega, eða fylgstu með notendum á öðrum netþjónum til að fylla upp í þetta", + "error.no_hashtag_feed_access": "Skráðu þig inn eða stofnaðu aðgang til að skoða og fylgjast með þessu myllumerki.", "error.unexpected_crash.explanation": "Vegna villu í kóðanum okkar eða samhæfnivandamála í vafra er ekki hægt að birta þessa síðu svo vel sé.", "error.unexpected_crash.explanation_addons": "Ekki er hægt að birta þessa síðu rétt. Þetta er líklega af völdum forritsviðbótar í vafranum eða sjálfvirkra þýðainaverkfæra.", "error.unexpected_crash.next_steps": "Prófaðu að endurlesa síðuna. Ef það hjálpar ekki til, má samt vera að þú getir notað Mastodon í gegnum annan vafra eða forrit.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Breytt {count, plural, one {{count} sinni} other {{count} sinnum}}", "status.embed": "Ná í innfellanlegan kóða", "status.favourite": "Eftirlæti", - "status.favourites": "{count, plural, one {eftirlæti} other {eftirlæti}}", + "status.favourites_count": "{count, plural, one {{counter} eftirlæti} other {{counter} eftirlæti}}", "status.filter": "Sía þessa færslu", "status.history.created": "{name} útbjó {date}", "status.history.edited": "{name} breytti {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Breyttu því hver getur tilvitnað", "status.quote_post_author": "Vitnaði í færslu frá @{name}", "status.quote_private": "Ekki er hægt að vitna í einkafærslur", - "status.quotes": "{count, plural, one {tilvitnun} other {tilvitnanir}}", "status.quotes.empty": "Enginn hefur ennþá vitnað í þessa færslu. Þegar einhver gerir það, mun það birtast hér.", "status.quotes.local_other_disclaimer": "Tilvitnanir sem höfundur hafnar verða ekki birtar.", "status.quotes.remote_other_disclaimer": "Aðeins tilvitnanir frá {domain} munu birtast hér. Tilvitnanir sem höfundur hafnar verða ekki birtar.", + "status.quotes_count": "{count, plural, one {{counter} tilvitnun} other {{counter} tilvitnanir}}", "status.read_more": "Lesa meira", "status.reblog": "Endurbirting", "status.reblog_or_quote": "Endurbirta eða vitna í færslu", "status.reblog_private": "Deildu aftur með þeim sem fylgjast með þér", "status.reblogged_by": "{name} endurbirti", - "status.reblogs": "{count, plural, one {endurbirting} other {endurbirtingar}}", "status.reblogs.empty": "Enginn hefur ennþá endurbirt þessa færslu. Þegar einhver gerir það, mun það birtast hér.", + "status.reblogs_count": "{count, plural, one {{counter} endurbirting} other {{counter} endurbirtingar}}", "status.redraft": "Eyða og endurvinna drög", "status.remove_bookmark": "Fjarlægja bókamerki", "status.remove_favourite": "Fjarlægja úr eftirlætum", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 0fe8ae6701c..9fc0be45315 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Tutto chiaro! Non c'è niente qui. Quando ricevi nuove notifiche, verranno visualizzate qui in base alle tue impostazioni.", "empty_column.notifications": "Non hai ancora nessuna notifica. Quando altre persone interagiranno con te, le vedrai qui.", "empty_column.public": "Non c'è nulla qui! Scrivi qualcosa pubblicamente o segui manualmente gli utenti dagli altri server per riempire questo spazio", + "error.no_hashtag_feed_access": "Iscriviti o accedi per visualizzare e seguire questo hashtag.", "error.unexpected_crash.explanation": "A causa di un bug nel nostro codice o di un problema di compatibilità del browser, non è stato possibile visualizzare correttamente questa pagina.", "error.unexpected_crash.explanation_addons": "Impossibile mostrare correttamente questa pagina. Questo errore è probabilmente causato da un addon del browser o da strumenti di traduzione automatica.", "error.unexpected_crash.next_steps": "Prova a ricaricare la pagina. Se non aiuta, potresti comunque utilizzare Mastodon tramite un browser differente o un'app nativa.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Modificato {count, plural, one {{count} volta} other {{count} volte}}", "status.embed": "Ottieni codice incorporato", "status.favourite": "Preferito", - "status.favourites": "{count, plural, one {preferito} other {preferiti}}", + "status.favourites_count": "{count, plural, one {{counter} apprezzamento} other {{counter} apprezzamenti}}", "status.filter": "Filtra questo post", "status.history.created": "Creato da {name} il {date}", "status.history.edited": "Modificato da {name} il {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Cambia chi può citare", "status.quote_post_author": "Citato un post di @{name}", "status.quote_private": "I post privati non possono essere citati", - "status.quotes": "{count, plural, one {citazione} other {citazioni}}", "status.quotes.empty": "Nessuno ha ancora citato questo post. Quando qualcuno lo farà, verrà visualizzato qui.", "status.quotes.local_other_disclaimer": "Le citazioni rifiutate dall'autore non verranno mostrate.", "status.quotes.remote_other_disclaimer": "Solo le citazioni provenienti da {domain} saranno mostrate qui. Le citazioni rifiutate dall'autore non saranno mostrate.", + "status.quotes_count": "{count, plural, one {{counter} citazione} other {{counter} citazioni}}", "status.read_more": "Leggi di più", "status.reblog": "Reblog", "status.reblog_or_quote": "Condividi o cita", "status.reblog_private": "Condividi di nuovo con i tuoi seguaci", "status.reblogged_by": "Rebloggato da {name}", - "status.reblogs": "{count, plural, one {boost} other {boost}}", "status.reblogs.empty": "Ancora nessuno ha rebloggato questo post. Quando qualcuno lo farà, apparirà qui.", + "status.reblogs_count": "{count, plural, one {{counter} condivisione} other {{counter} condivisioni}}", "status.redraft": "Elimina e riscrivi", "status.remove_bookmark": "Rimuovi segnalibro", "status.remove_favourite": "Rimuovi dai preferiti", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 92afa496d19..fcf7e3134ea 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -862,7 +862,6 @@ "status.edited_x_times": "{count}回編集", "status.embed": "埋め込みコードを取得", "status.favourite": "お気に入り", - "status.favourites": "{count, plural, one {お気に入り} other {お気に入り}}", "status.filter": "この投稿をフィルターする", "status.history.created": "{name}さんが{date}に作成", "status.history.edited": "{name}さんが{date}に編集", @@ -881,15 +880,14 @@ "status.quote_error.filtered": "あなたのフィルター設定によって非表示になっています", "status.quote_error.pending_approval": "承認待ちの投稿", "status.quote_noun": "引用", + "status.quote_policy_change": "引用できるユーザーの変更", "status.quote_post_author": "{name} の投稿を引用", "status.quote_private": "非公開の投稿は引用できません", - "status.quotes": "{count, plural, other {引用}}", "status.quotes.local_other_disclaimer": "投稿者が拒否した引用は表示されません。", "status.read_more": "もっと見る", "status.reblog": "ブースト", "status.reblog_or_quote": "ブーストか引用", "status.reblogged_by": "{name}さんがブースト", - "status.reblogs": "{count, plural, one {ブースト} other {ブースト}}", "status.reblogs.empty": "まだ誰もブーストしていません。ブーストされるとここに表示されます。", "status.redraft": "削除して下書きに戻す", "status.remove_bookmark": "ブックマークを削除", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index c8fcf3164fb..36ecff0b43c 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -668,7 +668,6 @@ "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}", "status.embed": "Awi-d tangalt n weslaɣ", "status.favourite": "Amenyaf", - "status.favourites": "{count, plural, one {n usmenyaf} other {n ismenyafen}}", "status.filter": "Sizdeg tassufeɣt-a", "status.history.created": "Yerna-t {name} {date}", "status.history.edited": "Ibeddel-it {name} {date}", @@ -694,7 +693,6 @@ "status.reblog": "Bḍu", "status.reblog_or_quote": "Zuzer neɣ ader-d", "status.reblogged_by": "Yebḍa-tt {name}", - "status.reblogs": "{count, plural, one {n usnerni} other {n yisnernuyen}}", "status.reblogs.empty": "Ula yiwen ur yebḍi tajewwiqt-agi ar tura. Ticki yebḍa-tt yiwen, ad d-iban da.", "status.redraft": "Kkes tɛiwdeḍ tira", "status.remove_bookmark": "Kkes tacreḍt", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index c7c659cdffe..e3552abd2d3 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -252,6 +252,7 @@ "confirmations.private_quote_notify.cancel": "편집으로 돌아가기", "confirmations.private_quote_notify.confirm": "게시", "confirmations.private_quote_notify.do_not_show_again": "이 메시지를 다시 표시하지 않음", + "confirmations.private_quote_notify.message": "인용하려는 사람과 멘션된 사람들은 나를 팔로우하지 않더라도 게시물에 대한 알림을 받으며 내용을 볼 수 있습니다.", "confirmations.quiet_post_quote_info.dismiss": "다시 보지 않기", "confirmations.quiet_post_quote_info.got_it": "알겠습니다", "confirmations.quiet_post_quote_info.message": "조용한 공개 게시물을 인용하면 그 게시물은 유행 타임라인에서 나타나지 않을 것입니다.", @@ -900,7 +901,7 @@ "status.edited_x_times": "{count, plural, other {{count}}} 번 수정됨", "status.embed": "임베드 코드 받기", "status.favourite": "좋아요", - "status.favourites": "{count, plural, other {좋아요}}", + "status.favourites_count": "{count, plural, other {{counter}}} 마음에 들어함", "status.filter": "이 게시물을 필터", "status.history.created": "{name} 님이 {date}에 게시함", "status.history.edited": "{name} 님이 {date}에 수정함", @@ -916,14 +917,14 @@ "status.pin": "고정", "status.quote": "인용", "status.quote.cancel": "인용 취소", - "status.quote_error.blocked_account_hint.title": "@{name}을 차단했기 때문에 이 게시물은 숨겨졌습니다.", + "status.quote_error.blocked_account_hint.title": "@{name} 님을 차단했기 때문에 이 게시물은 숨겨졌습니다.", "status.quote_error.blocked_domain_hint.title": "{domain}을 차단했기 때문에 이 게시물은 숨겨졌습니다.", "status.quote_error.filtered": "필터에 의해 가려짐", "status.quote_error.limited_account_hint.action": "그냥 보기", "status.quote_error.limited_account_hint.title": "이 계정은 {domain}의 중재자에 의해 숨겨진 상태입니다.", "status.quote_error.muted_account_hint.title": "@{name}을 뮤트했기 때문에 이 게시물은 숨겨졌습니다.", "status.quote_error.not_available": "게시물 사용 불가", - "status.quote_error.pending_approval": "계류 중인 게시물", + "status.quote_error.pending_approval": "게시물 대기중", "status.quote_error.pending_approval_popout.body": "Mastodon에서는 타인이 인용할 수 있는지 여부를 제어할 수 있습니다. 이 게시물은 원저자의 승인을 얻을 때까지 계류됩니다.", "status.quote_error.revoked": "원작성자에 의해 게시물 삭제됨", "status.quote_followers_only": "팔로워만 인용할 수 있는 게시물", @@ -932,17 +933,17 @@ "status.quote_policy_change": "누가 인용할 수 있는지", "status.quote_post_author": "인용된 @{name} 님의 게시물", "status.quote_private": "비공개 게시물은 인용할 수 없습니다", - "status.quotes": "{count, plural, other {인용}}", "status.quotes.empty": "아직 아무도 이 게시물을 인용하지 않았습니다. 누군가 인용한다면 여기에 표시됩니다.", "status.quotes.local_other_disclaimer": "원작자가 거부한 인용은 표시되지 않습니다.", "status.quotes.remote_other_disclaimer": "{domain}의 인용만 여기에 확정적으로 보여집니다. 원작자가 거부한 인용은 보여지지 않습니다.", + "status.quotes_count": "{count, plural, other {{counter}}} 인용", "status.read_more": "더 보기", "status.reblog": "부스트", "status.reblog_or_quote": "부스트 또는 인용", "status.reblog_private": "팔로워들에게 다시 공유", "status.reblogged_by": "{name} 님이 부스트했습니다", - "status.reblogs": "{count, plural, other {부스트}}", "status.reblogs.empty": "아직 아무도 이 게시물을 부스트하지 않았습니다. 부스트 한 사람들이 여기에 표시 됩니다.", + "status.reblogs_count": "{count, plural, other {{counter}}} 부스트", "status.redraft": "지우고 다시 쓰기", "status.remove_bookmark": "북마크 삭제", "status.remove_favourite": "즐겨찾기에서 제거", diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json index 720e940996f..a3b416a1ced 100644 --- a/app/javascript/mastodon/locales/la.json +++ b/app/javascript/mastodon/locales/la.json @@ -189,7 +189,7 @@ "notification.update": "{name} nuntium correxit", "notification_requests.accept": "Accipe", "notification_requests.confirm_accept_multiple.message": "Tu es accepturus {count, plural, one {una notitia petitionem} other {# notitia petitiones}}. Certus esne procedere vis?", - "notification_requests.confirm_dismiss_multiple.message": "Tu {count, plural, one {unam petitionem notificationis} other {# petitiones notificationum}} abrogāre prōximum es. {count, plural, one {Illa} other {Eae}} facile accessū nōn erit. Certus es tē procedere velle?", + "notification_requests.confirm_dismiss_multiple.message": "Tu {count, plural, one {unam petitionem notificationis} other {# petitiones notificationum}} abrogāre prōximum es. {count, plural, one {it} other {Eae}} facile accessū nōn erit. Certus es tē procedere velle?", "notifications.filter.all": "Omnia", "notifications.filter.polls": "Eventus electionis", "notifications.group": "{count} Notificātiōnēs", @@ -243,24 +243,105 @@ "status.delete": "Oblitterare", "status.edit": "Recolere", "status.edited_x_times": "Emendatum est {count, plural, one {{count} tempus} other {{count} tempora}}", - "status.favourites": "{count, plural, one {favoritum} other {favorita}}", "status.history.created": "{name} creatum {date}", "status.history.edited": "{name} correxit {date}", "status.open": "Expand this status", + "status.quotes.empty": "Nemo hanc commentationem adhuc citavit. Cum quis citaverit, hic apparebit.", + "status.quotes.local_other_disclaimer": "Citationes ab auctore reiec­tæ non monstrabuntur.", + "status.quotes.remote_other_disclaimer": "Tantum citae ex {domain} hic exhiberi praestantur. Citae ab auctore reiectae non exhibebuntur.", + "status.quotes_count": "{count, plural, one {{counter} citatio} other {{counter} citationes}}", + "status.read_more": "Plura lege", + "status.reblog": "Promovere", + "status.reblog_or_quote": "Promovere aut cita", + "status.reblog_private": "Iterum cum sectatoribus tuis communica", "status.reblogged_by": "{name} adiuvavit", - "status.reblogs": "{count, plural, one {auctus} other {auctūs}}", + "status.reblogs.empty": "Nemo hanc publicationem adhuc promovit. Cum quis eam promoveat, hic apparebunt.", + "status.reblogs_count": "{count, plural, one {{counter} incrementum} other {{counter} incrementa}}", + "status.redraft": "Dele et redig", + "status.remove_bookmark": "Tolle signum", + "status.remove_favourite": "Tolle ad delectis", + "status.remove_quote": "Tolle", + "status.replied_in_thread": "In filo responsum", + "status.replied_to": "{name} respondit", + "status.reply": "Respondere", + "status.replyAll": "Responde ad filum", + "status.report": "Referre @{name}", + "status.request_quote": "Pretium petere", + "status.revoke_quote": "Tolle nuntium meum ex nuntio @{name}", + "status.sensitive_warning": "Materia delicata", + "status.share": "Communica", + "status.show_less_all": "Omnibus minus monstra", + "status.show_more_all": "Omnibus plura monstra", + "status.show_original": "Monstra originalem", "status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}", + "status.translate": "Converte", + "status.translated_from_with": "Translatum ex {lang} per {provider}", + "status.uncached_media_warning": "Praevisum non praesto est", + "status.unmute_conversation": "Conversationem reserare", + "subscribed_languages.lead": "Tantum epistolae in linguis selectis in domo tua apparebunt et indices temporum post mutationem. Neminem eligatis qui epistolas in omnibus linguis recipiat.", + "subscribed_languages.save": "Servare mutationes", + "subscribed_languages.target": "Muta linguas subscriptas pro {target}", "tabs_bar.home": "Domi", + "tabs_bar.menu": "Elenchus", + "tabs_bar.notifications": "Acta Vicimediorum", + "tabs_bar.publish": "Nova publicatio", + "tabs_bar.search": "Quaere", + "terms_of_service.effective_as_of": "Valet ex {date}", + "terms_of_service.title": "Termini servitii", + "terms_of_service.upcoming_changes_on": "Mutationes venturae die {date}", "time_remaining.days": "{number, plural, one {# die} other {# dies}} restant", "time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant", "time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant", + "time_remaining.moments": "Momenta reliqua", "time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant", - "trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {diē prīdiē} other {diēbus praeteritīs {days}}}", + "trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {days} other {diēbus praeteritīs {days}}}", + "trends.trending_now": "Nunc in usu", "ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.", "units.short.billion": "{count} millia milionum", "units.short.million": "{count} milionum", "units.short.thousand": "{count} millia", - "upload_button.label": "Imaginēs, vīdeō aut fīle audītūs adde", + "upload_area.title": "Trahe et depone ad imponendum", + "upload_button.label": "Adde imagines, pelliculam, aut fasciculum sonorum.", + "upload_error.limit": "Limes onerationis superatus est.", + "upload_error.poll": "Nullis suffragiis licet fascicula imponere.", + "upload_error.quote": "Nullum oneramentum fasciculi cum citationibus permittitur.", + "upload_form.drag_and_drop.instructions": "Ad annexum mediorum tollendum, preme clavem \"Space\" aut \"Enter\". Dum traheis, utere clavibus sagittariis ad annexum mediorum in quamlibet partem movendum. Preme iterum \"Space\" aut \"Enter\" ad annexum mediorum in novo loco deponendum, aut preme \"Escape\" ad desinendum.", + "upload_form.drag_and_drop.on_drag_cancel": "Tractatio revocata est. Adiunctum medium {item} demissum est.", + "upload_form.drag_and_drop.on_drag_end": "Adhaesum medium {item} demissum est.", + "upload_form.drag_and_drop.on_drag_over": "Adhaesum medium {item} motum est.", + "upload_form.drag_and_drop.on_drag_start": "Adhaesum medium {item} sublatum est.", "upload_form.edit": "Recolere", - "upload_progress.label": "Uploading…" + "upload_progress.label": "Oneratur...", + "upload_progress.processing": "Processus…", + "username.taken": "Illud nomen usoris occupatum est. Aliud tenta.", + "video.close": "Pelliculam claude", + "video.download": "Prehendere fasciculus", + "video.exit_fullscreen": "Exitus ex plenum monitorium", + "video.expand": "Expande pelliculam", + "video.fullscreen": "Plenum monitorium", + "video.hide": "celare pellicula", + "video.mute": "Mutus", + "video.pause": "intermittere", + "video.play": "gignere", + "video.skip_backward": "Redire", + "video.skip_forward": "Progredi", + "video.unmute": "Sordes tollere", + "video.volume_down": "Volumen deminui", + "video.volume_up": "Volumen augete", + "visibility_modal.button_title": "Visibilitatem statuere", + "visibility_modal.direct_quote_warning.text": "Si praesentia configuramenta servaveris, sententia inserta in nexum convertetur.", + "visibility_modal.direct_quote_warning.title": "Citatio in mentitionibus privatis inseriri non possunt.", + "visibility_modal.header": "Visibilitas et interactio", + "visibility_modal.helper.direct_quoting": "Mentiones privatae in Mastodon scriptae ab aliis citari non possunt.", + "visibility_modal.helper.privacy_editing": "Visibilitas mutari non potest postquam nuntius publicatus est.", + "visibility_modal.helper.privacy_private_self_quote": "Citationes propriae nuntiorum privatorum publicari non possunt.", + "visibility_modal.helper.private_quoting": "Nuntii in Mastodon a sequacibus tantum scriptī ab aliis citari non possunt.", + "visibility_modal.helper.unlisted_quoting": "Cum te citant, eorum scriptum etiam ex indicibus popularibus celabitur.", + "visibility_modal.instructions": "Régula quis cum hoc scripto agere possit. Potes etiam ordinationes omnibus scriptis futuris adhibere, navigando ad Praeferentias > Regulas scriptionis praedefinitas.", + "visibility_modal.privacy_label": "Visibilitas", + "visibility_modal.quote_followers": "Sectatores tantum", + "visibility_modal.quote_label": "Quis citare potest", + "visibility_modal.quote_nobody": "Sicut me", + "visibility_modal.quote_public": "quisquis", + "visibility_modal.save": "Servare" } diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index ec8464175af..cab0580e1a4 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -785,7 +785,6 @@ "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", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index bf13549d46a..501ff11eb14 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -791,7 +791,6 @@ "status.edited": "Paskutinį kartą redaguota {date}", "status.edited_x_times": "Redaguota {count, plural, one {{count} kartą} few {{count} kartus} many {{count} karto} other {{count} kartų}}", "status.favourite": "Pamėgti", - "status.favourites": "{count, plural, one {mėgstamas} few {mėgstamai} many {mėgstamų} other {mėgstamų}}", "status.filter": "Filtruoti šį įrašą", "status.history.created": "{name} sukurta {date}", "status.history.edited": "{name} redaguota {date}", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 502092f5f70..eeda75d85d1 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -17,11 +17,11 @@ "account.add_or_remove_from_list": "Pievienot vai Noņemt no sarakstiem", "account.badges.bot": "Automatizēts", "account.badges.group": "Grupa", - "account.block": "Liegt @{name}", + "account.block": "Bloķēt @{name}", "account.block_domain": "Bloķēt domēnu {domain}", - "account.block_short": "Liegt", - "account.blocked": "Liegts", - "account.blocking": "Liegts", + "account.block_short": "Bloķēt", + "account.blocked": "Bloķēts", + "account.blocking": "Bloķēts", "account.cancel_follow_request": "Atsaukt sekošanas pieprasījumu", "account.copy": "Ievietot saiti uz profilu starpliktuvē", "account.direct": "Pieminēt @{name} privāti", @@ -41,6 +41,7 @@ "account.featured_tags.last_status_never": "Nav ierakstu", "account.follow": "Sekot", "account.follow_back": "Sekot atpakaļ", + "account.follow_back_short": "Sekot atpakaļ", "account.follow_request": "Pieprasīt sekot", "account.follow_request_cancel": "Atcelt pieprasījumu", "account.follow_request_cancel_short": "Atcelt", @@ -131,12 +132,13 @@ "annual_report.summary.thanks": "Paldies, ka esi daļa no Mastodon!", "attachments_list.unprocessed": "(neapstrādāti)", "audio.hide": "Slēpt audio", - "block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri liegšanu var apstrādāt citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.", + "block_modal.remote_users_caveat": "Mēs lūgsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr sadarbība nav garantēta, jo atsevišķi serveri bloķēšanu var apstrādāt citādi. Publiski ieraksti, iespējams, joprojām būs redzami lietotājiem, kuri nav pieteikušies.", "block_modal.show_less": "Rādīt mazāk", - "block_modal.show_more": "Parādīt mazāk", - "block_modal.they_cant_mention": "Nevar Tevi pieminēt vai sekot Tev.", - "block_modal.they_cant_see_posts": "Lietotajs nevarēs redzēt Tavus ierakstus, un Tu neredzēsi lietotāja.", - "block_modal.title": "Liegt lietotāju?", + "block_modal.show_more": "Rādīt vairāk", + "block_modal.they_cant_mention": "Lietotājs nevarēs Tevi pieminēt vai Tev sekot.", + "block_modal.they_cant_see_posts": "Lietotājs nevarēs redzēt Tavus, un Tu neredzēsi lietotāja ierakstus.", + "block_modal.they_will_know": "Lietotājs varēs redzēt, ka ir bloķēts.", + "block_modal.title": "Bloķēt lietotāju?", "block_modal.you_wont_see_mentions": "Tu neredzēsi ierakstus, kuros ir minēts šis lietotājs.", "boost_modal.combo": "Nospied {combo}, lai nākamreiz šo izlaistu", "boost_modal.reblog": "Pastiprināt ierakstu?", @@ -188,6 +190,7 @@ "community.column_settings.local_only": "Tikai vietējie", "community.column_settings.media_only": "Tikai multivide", "community.column_settings.remote_only": "Tikai attālinātie", + "compose.error.blank_post": "Ieraksts nedrīkst būt tukšs.", "compose.language.change": "Mainīt valodu", "compose.language.search": "Meklēt valodas...", "compose.published.body": "Ieraksts pievienots.", @@ -221,7 +224,9 @@ "confirmations.delete_list.message": "Vai tiešām neatgriezeniski izdzēst šo sarakstu?", "confirmations.delete_list.title": "Izdzēst sarakstu?", "confirmations.discard_draft.confirm": "Atmest un turpināt", - "confirmations.discard_draft.edit.cancel": "Atsākt labošanu", + "confirmations.discard_draft.edit.cancel": "Atgriezties pie labošanas", + "confirmations.discard_draft.edit.message": "Ja turpināsi, tiks atmestas izmaiņas ierakstā, kuru Tu šobrīd labo.", + "confirmations.discard_draft.edit.title": "Atmest ieraksta labojumus?", "confirmations.discard_draft.post.cancel": "Atsākt melnrakstu", "confirmations.discard_draft.post.message": "Turpinot tiks atmests pašreiz sastādītais ieraksts.", "confirmations.discard_draft.post.title": "Atmest melnraksta ierakstu?", @@ -238,6 +243,9 @@ "confirmations.missing_alt_text.secondary": "Vienalga iesūtīt", "confirmations.missing_alt_text.title": "Pievienot aprakstošo tekstu?", "confirmations.mute.confirm": "Apklusināt", + "confirmations.private_quote_notify.cancel": "Atgriezties pie labošanas", + "confirmations.private_quote_notify.do_not_show_again": "Nerādīt vairāk šo paziņojumu", + "confirmations.quiet_post_quote_info.got_it": "Sapratu", "confirmations.redraft.confirm": "Dzēst un pārrakstīt", "confirmations.redraft.message": "Vai tiešām vēlies izdzēst šo ierakstu un veidot jaunu tā uzmetumu? Pievienošana izlasēs un pastiprinājumi tiks zaudēti, un sākotnējā ieraksta atbildes paliks bez saiknes ar to.", "confirmations.redraft.title": "Izdzēst un rakstīt ierakstu no jauna?", @@ -247,6 +255,8 @@ "confirmations.revoke_quote.confirm": "Noņemt ierakstu", "confirmations.revoke_quote.message": "Šo darbību nevar atsaukt.", "confirmations.revoke_quote.title": "Noņemt ierakstu?", + "confirmations.unblock.confirm": "Atbloķēt", + "confirmations.unblock.title": "Atbloķēt {name}?", "confirmations.unfollow.confirm": "Pārstāt sekot", "confirmations.unfollow.title": "Pārtraukt sekot {name}?", "confirmations.withdraw_request.confirm": "Atsaukt pieprasījumu", @@ -270,10 +280,12 @@ "dismissable_banner.dismiss": "Atcelt", "dismissable_banner.public_timeline": "Šie ir jaunākie Fediverse lietotāju publiskie ieraksti, kuriem {domain} seko cilvēki.", "domain_block_modal.block": "Liegt serveri", - "domain_block_modal.block_account_instead": "Tā vietā liegt @{name}", - "domain_block_modal.they_cant_follow": "Neviens šajā serverī nevar Tev sekot.", + "domain_block_modal.block_account_instead": "Tā vietā bloķēt @{name}", + "domain_block_modal.they_cant_follow": "Neviens no šī servera nevarēs Tev sekot.", "domain_block_modal.they_wont_know": "Viņi nezinās, ka tikuši liegti.", "domain_block_modal.title": "Liegt domēnu?", + "domain_block_modal.you_will_lose_num_followers": "Tu zaudēsi {followersCount, plural, one {{followersCountDisplay} sekotāju} other {{followersCountDisplay} sekotājus}} un {followingCount, plural, one {{followingCountDisplay} sekojamo} other {{followingCountDisplay} sekojamos}}.", + "domain_block_modal.you_will_lose_relationships": "Tu zaudēsi visus sekotājus un sekojamos no šī servera.", "domain_pill.activitypub_lets_connect": "Tas ļauj savienoties un mijiedarboties ar cilvēkiem ne tikai no Mastodon, bet arī starp dažādām sabiedriskajām lietotnēm.", "domain_pill.activitypub_like_language": "ActivityPub ir kā valoda, kurā Mastodon sazināš ar citiem sabiedriskajiem tīkliem.", "domain_pill.server": "Serveris", @@ -368,6 +380,7 @@ "follow_suggestions.who_to_follow": "Kam sekot", "followed_tags": "Sekojamie tēmturi", "footer.about": "Par", + "footer.about_this_server": "Par", "footer.directory": "Profilu direktorija", "footer.get_app": "Iegūt lietotni", "footer.keyboard_shortcuts": "Īsinājumtaustiņi", @@ -525,7 +538,8 @@ "notification.favourite_pm.name_and_others_with_link": "{name} un
{count, plural, one {# cits} other {# citi}} pievienoja Tavu privāto pieminējumu izlasē", "notification.follow": "{name} uzsāka Tev sekot", "notification.follow_request": "{name} nosūtīja Tev sekošanas pieprasījumu", - "notification.mentioned_you": "{name} pieminēja jūs", + "notification.label.quote": "{name} citēja tavu ierakstu", + "notification.mentioned_you": "{name} pieminēja tevi", "notification.moderation-warning.learn_more": "Uzzināt vairāk", "notification.moderation_warning": "Ir saņemts satura pārraudzības brīdinājums", "notification.moderation_warning.action_delete_statuses": "Daži no Taviem ierakstiem tika noņemti.", @@ -536,6 +550,7 @@ "notification.moderation_warning.action_silence": "Tavs konts tika ierobežots.", "notification.moderation_warning.action_suspend": "Tava konta darbība tika apturēta.", "notification.own_poll": "Tava aptauja ir noslēgusies", + "notification.quoted_update": "{name} laboja tevis citētu ierakstu", "notification.reblog": "{name} pastiprināja Tavu ierakstu", "notification.relationships_severance_event": "Zaudēti savienojumi ar {name}", "notification.relationships_severance_event.learn_more": "Uzzināt vairāk", @@ -623,9 +638,14 @@ "privacy.direct.long": "Visi ierakstā pieminētie", "privacy.private.long": "Tikai Tavi sekotāji", "privacy.private.short": "Sekotāji", - "privacy.public.long": "Jebkurš Mastodon un ārpus tā", - "privacy.public.short": "Redzams visiem", + "privacy.public.long": "Jebkurš Mastodon platformā un ārpus tās", + "privacy.public.short": "Publisks", + "privacy.quote.anyone": "{visibility}, jebkurš var citēt", + "privacy.quote.disabled": "{visibility}, aizliegta citēšana", + "privacy.quote.limited": "{visibility}, ierobežota citēšana", "privacy.unlisted.additional": "Šis uzvedas tieši kā publisks, izņemot to, ka ieraksts neparādīsies tiešraides barotnēs vai tēmturos, izpētē vai Mastodon meklēšanā, pat ja esi to norādījis visa konta ietvaros.", + "privacy.unlisted.long": "Netiks rādīts Mastodon meklēšanas rezultātos, populārākajos, un publiskajās laikjoslās", + "privacy.unlisted.short": "Klusi publisks", "privacy_policy.last_updated": "Pēdējo reizi atjaunināta {date}", "privacy_policy.title": "Privātuma politika", "recommended": "Ieteicams", @@ -641,6 +661,7 @@ "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", "relative_time.today": "šodien", + "remove_quote_hint.button_label": "Sapratu", "reply_indicator.attachments": "{count, plural, zero{# pielikumu} one {# pielikums} other {# pielikumi}}", "reply_indicator.cancel": "Atcelt", "reply_indicator.poll": "Aptauja", @@ -726,6 +747,7 @@ "status.admin_account": "Atvērt @{name} satura pārraudzības saskarni", "status.admin_domain": "Atvērt {domain} satura pārraudzības saskarni", "status.admin_status": "Atvērt šo ziņu satura pārraudzības saskarnē", + "status.all_disabled": "Pastiprināšana un citēšana ir atspējota", "status.block": "Liegt @{name}", "status.bookmark": "Grāmatzīme", "status.cancel_reblog_private": "Nepastiprināt", @@ -740,7 +762,6 @@ "status.edited": "Pēdējoreiz labots {date}", "status.edited_x_times": "Labots {count, plural, zero {{count} reižu} one {{count} reizi} other {{count} reizes}}", "status.favourite": "Izlasē", - "status.favourites": "{count, plural, one {izlasē} other {izlasēs}}", "status.filter": "Atlasīt šo ierakstu", "status.history.created": "{name} izveidoja {date}", "status.history.edited": "{name} laboja {date}", @@ -754,10 +775,10 @@ "status.mute_conversation": "Apklusināt sarunu", "status.open": "Izvērst šo ierakstu", "status.pin": "Piespraust profilam", + "status.quote_policy_change": "Kurš var citēt", "status.read_more": "Lasīt vairāk", "status.reblog": "Pastiprināt", "status.reblogged_by": "{name} pastiprināja", - "status.reblogs": "{count, plural, zero {pastiprinājumu} one {pastiprinājums} other {pastiprinājumi}}", "status.reblogs.empty": "Neviens vēl nav pastiprinājis šo ierakstu. Kad kāds to izdarīs, šeit tiks parādīti lietotāji.", "status.redraft": "Dzēst un pārrakstīt", "status.remove_bookmark": "Noņemt grāmatzīmi", @@ -818,8 +839,9 @@ "video.volume_down": "Pagriezt klusāk", "video.volume_up": "Pagriezt skaļāk", "visibility_modal.button_title": "Iestatīt redzamību", - "visibility_modal.header": "Redzamība un mijjiedarbība", + "visibility_modal.header": "Redzamība un mijiedarbība", "visibility_modal.quote_followers": "Tikai sekotāji", + "visibility_modal.quote_label": "Kurš var citēt", "visibility_modal.quote_nobody": "Tikai es", "visibility_modal.quote_public": "Ikviens", "visibility_modal.save": "Saglabāt" diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 1530736f65b..46011b09a82 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -619,7 +619,6 @@ "status.edit": "Sunting", "status.edited_x_times": "Disunting {count, plural, other {{count} kali}}", "status.favourite": "Suka", - "status.favourites": "{count, plural, other {sukaan}}", "status.filter": "Tapiskan hantaran ini", "status.history.created": "{name} mencipta pada {date}", "status.history.edited": "{name} menyunting pada {date}", @@ -636,7 +635,6 @@ "status.read_more": "Baca lagi", "status.reblog": "Galakkan", "status.reblogged_by": "{name} galakkan", - "status.reblogs": "{count, plural, other {galakan}}", "status.reblogs.empty": "Tiada sesiapa yang galakkan hantaran ini. Apabila ada yang galakkan, hantaran akan muncul di sini.", "status.redraft": "Padam & rangka semula", "status.remove_bookmark": "Buang tanda buku", diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index 7c84e3e1596..c2b298e032f 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "清hōo空ah!內底無物件。若是lí收著新ê通知,ē根據lí ê設定,佇tsia出現。", "empty_column.notifications": "Lí iáu無收著任何通知。Nā別lâng kap lí互動,lí ē佇tsia看著。", "empty_column.public": "內底無物件!寫beh公開ê PO文,á是主動跟tuè別ê服侍器ê用者,來加添內容。", + "error.no_hashtag_feed_access": "加入á是登入,來看kap跟tuè tsit ê hashtag。", "error.unexpected_crash.explanation": "因為原始碼內底有錯誤,á是瀏覽器相容出tshê,tsit頁bē當正確顯示。", "error.unexpected_crash.explanation_addons": "Tsit頁bē當正確顯示,可能是瀏覽器附ê功能,á是自動翻譯工具所致。", "error.unexpected_crash.next_steps": "請試更新tsit頁。若是bē當改善,lí iáu是ē當改使用無kâng ê瀏覽器,á是app,來用Mastodon。", @@ -903,7 +904,7 @@ "status.edited_x_times": "有編輯 {count, plural, one {{count} kái} other {{count} kái}}", "status.embed": "The̍h相tàu ê (embed)程式碼", "status.favourite": "收藏", - "status.favourites": "{count, plural, other {# 篇收藏}}", + "status.favourites_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 收藏", "status.filter": "過濾tsit 篇 PO文", "status.history.created": "{name} 佇 {date} 建立", "status.history.edited": "{name} 佇 {date} 編輯", @@ -934,17 +935,17 @@ "status.quote_policy_change": "改通引用ê lâng", "status.quote_post_author": "引用 @{name} ê PO文ah", "status.quote_private": "私人PO文bē當引用", - "status.quotes": "{count, plural, other {# 篇引用ê PO文}}", "status.quotes.empty": "Iáu無lâng引用tsit篇PO文。Nā是有lâng引用,ē佇tsia顯示。.", "status.quotes.local_other_disclaimer": "Hōo作者拒絕引用ê引文bē當顯示。", "status.quotes.remote_other_disclaimer": "Kan-ta tuì {domain} 來ê引文tsiah保證佇tsia顯示。Hōo作者拒絕ê引文buē顯示。", + "status.quotes_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 引用", "status.read_more": "讀詳細", "status.reblog": "轉送", "status.reblog_or_quote": "轉送á是引用", "status.reblog_private": "Koh再hām跟tuè ê分享", "status.reblogged_by": "{name} kā轉送ah", - "status.reblogs": "{count, plural, other {# ê 轉送}}", "status.reblogs.empty": "Iáu無lâng轉送tsit篇PO文。Nā是有lâng轉送,ē佇tsia顯示。", + "status.reblogs_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 轉送", "status.redraft": "Thâi掉了後重寫", "status.remove_bookmark": "Thâi掉冊籤", "status.remove_favourite": "Tuì收藏內suá掉", @@ -1017,6 +1018,8 @@ "video.volume_down": "變khah細聲", "video.volume_up": "變khah大聲", "visibility_modal.button_title": "設定通看ê程度", + "visibility_modal.direct_quote_warning.text": "Nā是lí儲存目前ê設定,引用êPO文ē轉做連結。", + "visibility_modal.direct_quote_warning.title": "引用bē當tàu入去私人ê提起", "visibility_modal.header": "通看ê程度kap互動", "visibility_modal.helper.direct_quoting": "Mastodon頂發布ê私人提起bē當hōo別lâng引用。", "visibility_modal.helper.privacy_editing": "Po文發布了後,bē當改通看ê程度。", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json index c686ccfbb57..5b6b0994864 100644 --- a/app/javascript/mastodon/locales/ne.json +++ b/app/javascript/mastodon/locales/ne.json @@ -210,7 +210,6 @@ "status.mute_conversation": "कुराकानी म्यूट गर्नुहोस्", "status.reblog": "बूस्ट गर्नुहोस्", "status.reblogged_by": "{name} ले बूस्ट गर्नुभएको", - "status.reblogs": "{count, plural, one {बूस्ट} other {बूस्टहरू}}", "status.reblogs.empty": "यो पोस्टलाई अहिलेसम्म कसैले पनि बूस्ट गरेको छैन। कसैले बूस्ट गरेमा तिनीहरू यहाँ देखिनेछन्।", "status.unmute_conversation": "कुराकानी अनम्यूट गर्नुहोस्", "visibility_modal.quote_followers": "फलोअरहरु मात्र" diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 371a3c9ba18..e3497da2ff5 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -765,7 +765,7 @@ "privacy_policy.title": "Privacybeleid", "quote_error.edit": "Citaten kunnen niet tijdens het bewerken van een bericht worden toegevoegd.", "quote_error.poll": "Het is niet mogelijk om polls te citeren.", - "quote_error.private_mentions": "Citaten zijn niet toegestaan met directe vermeldingen.", + "quote_error.private_mentions": "Citaten in privéberichten zijn niet toegestaan.", "quote_error.quote": "Je kunt maar één bericht per keer citeren.", "quote_error.unauthorized": "Je bent niet gemachtigd om dit bericht te citeren.", "quote_error.upload": "Je kunt geen mediabijlage aan een bericht met een citaat toevoegen.", @@ -903,7 +903,7 @@ "status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt", "status.embed": "Embed-code verkrijgen", "status.favourite": "Favoriet", - "status.favourites": "{count, plural, one {favoriet} other {favorieten}}", + "status.favourites_count": "{count, plural, one {{counter} favoriet} other {{counter} favorieten}}", "status.filter": "Dit bericht filteren", "status.history.created": "{name} plaatste dit {date}", "status.history.edited": "{name} bewerkte dit {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "Wijzig wie jou mag citeren", "status.quote_post_author": "Citeerde een bericht van @{name}", "status.quote_private": "Citeren van berichten aan alleen volgers is niet mogelijk", - "status.quotes": "{count, plural, one {citaat} other {citaten}}", "status.quotes.empty": "Niemand heeft dit bericht nog geciteerd. Wanneer iemand dat doet, wordt dat hier getoond.", "status.quotes.local_other_disclaimer": "Citaten afgewezen door de auteur worden niet getoond.", "status.quotes.remote_other_disclaimer": "Alleen citaten van {domain} worden hier gegarandeerd weergegeven. Citaten afgewezen door de auteur worden niet getoond.", + "status.quotes_count": "{count, plural, one {{counter} citaat} other {{counter} citaten}}", "status.read_more": "Meer lezen", "status.reblog": "Boosten", "status.reblog_or_quote": "Boosten of citeren", "status.reblog_private": "Opnieuw met je volgers delen", "status.reblogged_by": "{name} boostte", - "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "Niemand heeft dit bericht nog geboost. Wanneer iemand dit doet, valt dat hier te zien.", + "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", "status.redraft": "Verwijderen en herschrijven", "status.remove_bookmark": "Bladwijzer verwijderen", "status.remove_favourite": "Verwijderen uit favorieten", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 5bdbee9d353..17c526140ac 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -903,7 +903,7 @@ "status.edited_x_times": "Redigert {count, plural, one {{count} gong} other {{count} gonger}}", "status.embed": "Få innbyggingskode", "status.favourite": "Favoritt", - "status.favourites": "{count, plural, one {favoritt} other {favorittar}}", + "status.favourites_count": "{count, plural, one {{counter} favoritt} other {{counter} favorittar}}", "status.filter": "Filtrer dette innlegget", "status.history.created": "{name} oppretta {date}", "status.history.edited": "{name} redigerte {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "Byt kven som kan sitera", "status.quote_post_author": "Siterte eit innlegg av @{name}", "status.quote_private": "Du kan ikkje sitera private innlegg", - "status.quotes": "{count, plural, one {sitat} other {sitat}}", "status.quotes.empty": "Ingen har sitert dette innlegget enno. Når nokon gje det, vil det dukka opp her.", "status.quotes.local_other_disclaimer": "Sitat som skribenten avviser, vil ikkje syna.", "status.quotes.remote_other_disclaimer": "Berre sitat frå {domain} er garanterte å syna her. Sitat som skribenten har avvist, vil ikkje syna.", + "status.quotes_count": "{count, plural, one {{counter} sitat} other {{counter} sitat}}", "status.read_more": "Les meir", "status.reblog": "Framhev", "status.reblog_or_quote": "Framhev eller siter", "status.reblog_private": "Del på nytt med fylgjarane dine", "status.reblogged_by": "{name} framheva", - "status.reblogs": "{count, plural, one {framheving} other {framhevingar}}", "status.reblogs.empty": "Ingen har framheva dette tutet enno. Om nokon gjer, så dukkar det opp her.", + "status.reblogs_count": "{count, plural, one {{counter} framheving} other {{counter} framhevingar}}", "status.redraft": "Slett & skriv på nytt", "status.remove_bookmark": "Fjern bokmerke", "status.remove_favourite": "Fjern frå favorittar", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 64ef92dc9e7..c1e39907113 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -23,6 +23,7 @@ "account.disable_notifications": "ਜਦੋਂ {name} ਕੋਈ ਪੋਸਟ ਕਰੇ ਤਾਂ ਮੈਨੂੰ ਸੂਚਨਾ ਨਾ ਦਿਓ", "account.domain_blocking": "ਡੋਮੇਨ ਉੱਤੇ ਪਾਬੰਦੀ", "account.edit_profile": "ਪਰੋਫਾਈਲ ਨੂੰ ਸੋਧੋ", + "account.edit_profile_short": "ਸੋਧੋ", "account.enable_notifications": "ਜਦੋਂ {name} ਪੋਸਟ ਕਰੇ ਤਾਂ ਮੈਨੂੰ ਸੂਚਨਾ ਦਿਓ", "account.endorse": "ਪਰੋਫਾਇਲ ਉੱਤੇ ਫ਼ੀਚਰ", "account.familiar_followers_one": "{name1} ਵਲੋਂ ਫ਼ਾਲੋ ਕੀਤਾ", @@ -34,6 +35,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, one {{counter} ਫ਼ਾਲੋਅਰ} other {{counter} ਫ਼ਾਲੋਅਰ}}", @@ -55,8 +61,10 @@ "account.open_original_page": "ਅਸਲ ਸਫ਼ੇ ਨੂੰ ਖੋਲ੍ਹੋ", "account.posts": "ਪੋਸਟਾਂ", "account.posts_with_replies": "ਪੋਸਟਾਂ ਅਤੇ ਜਵਾਬ", + "account.remove_from_followers": "{name} ਨੂੰ ਫ਼ਾਲੋਅਰ ਵਿੱਚੋਂ ਹਟਾਓ", "account.report": "{name} ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ", "account.requested_follow": "{name} ਨੇ ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰਨ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ", + "account.requests_to_follow_you": "ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰਨ ਦੀਆਂ ਬੇਨਤੀਆਂ", "account.share": "{name} ਦਾ ਪਰੋਫ਼ਾਇਲ ਸਾਂਝਾ ਕਰੋ", "account.statuses_counter": "{count, plural, one {{counter} ਪੋਸਟ} other {{counter} ਪੋਸਟਾਂ}}", "account.unblock": "@{name} ਤੋਂ ਪਾਬੰਦੀ ਹਟਾਓ", @@ -73,6 +81,7 @@ "admin.dashboard.retention.cohort_size": "ਨਵੇਂ ਵਰਤੋਂਕਾਰ", "alert.unexpected.title": "ਓਹੋ!", "alt_text_badge.title": "ਬਦਲੀ ਲਿਖਤ", + "alt_text_modal.add_alt_text": "ਬਦਲਵੀ ਲਿਖਤ ਜੋੜੋ", "alt_text_modal.cancel": "ਰੱਦ ਕਰੋ", "alt_text_modal.done": "ਮੁਕੰਮਲ", "announcement.announcement": "ਹੋਕਾ", @@ -99,6 +108,8 @@ "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "ਬੰਦ ਕਰੋ", "bundle_modal_error.retry": "ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰੋ", + "carousel.current": "ਸਲਾਈਡ {current, number} / {max, number}", + "carousel.slide": "{max, number} ਵਿੱਚੋਂ {current, number} ਸਲਾਈਡ", "closed_registrations_modal.title": "Mastodon ਲਈ ਸਾਈਨ ਅੱਪ ਕਰੋ", "column.about": "ਸਾਡੇ ਬਾਰੇ", "column.blocks": "ਪਾਬੰਦੀ ਲਾਏ ਵਰਤੋਂਕਾਰ", @@ -111,6 +122,7 @@ "column.edit_list": "ਸੂਚੀ ਨੂੰ ਸੋਧੋ", "column.favourites": "ਮਨਪਸੰਦ", "column.firehose": "ਲਾਈਵ ਫੀਡ", + "column.firehose_singular": "ਲਾਈਵ ਫੀਡ", "column.follow_requests": "ਫ਼ਾਲੋ ਦੀਆਂ ਬੇਨਤੀਆਂ", "column.home": "ਮੁੱਖ ਸਫ਼ਾ", "column.list_members": "ਸੂਚੀ ਦੇ ਮੈਂਬਰ ਦਾ ਇੰਤਜ਼ਾਮ ਕਰੋ", @@ -129,6 +141,7 @@ "community.column_settings.local_only": "ਸਿਰਫ ਲੋਕਲ ਹੀ", "community.column_settings.media_only": "ਸਿਰਫ ਮੀਡੀਆ ਹੀ", "community.column_settings.remote_only": "ਸਿਰਫ਼ ਰਿਮੋਟ ਹੀ", + "compose.error.blank_post": "ਪੋਸਟ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦੀ ਹੈ।", "compose.language.change": "ਭਾਸ਼ਾ ਬਦਲੋ", "compose.language.search": "ਭਾਸ਼ਾਵਾਂ ਦੀ ਖੋਜ...", "compose.published.body": "ਪੋਸਟ ਪ੍ਰਕਾਸ਼ਿਤ ਕੀਤੀ।", @@ -140,6 +153,8 @@ "compose_form.lock_disclaimer": "ਤੁਹਾਡਾ ਖਾਤਾ {locked} ਨਹੀਂ ਹੈ। ਕੋਈ ਵੀ ਤੁਹਾਡੀਆਂ ਸਿਰਫ਼-ਫ਼ਾਲੋਅਰ ਪੋਸਟਾਂ ਵੇਖਣ ਵਾਸਤੇ ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰ ਸਕਦਾ ਹੈ।", "compose_form.lock_disclaimer.lock": "ਲਾਕ ਹੈ", "compose_form.placeholder": "ਤੁਹਾਡੇ ਮਨ ਵਿੱਚ ਕੀ ਹੈ?", + "compose_form.poll.duration": "ਵੋਟ ਲਈ ਸਮਾਂ", + "compose_form.poll.multiple": "ਕਈ ਚੋਣਾਂ", "compose_form.poll.option_placeholder": "{number} ਚੋਣ", "compose_form.poll.single": "ਇਕੱਲੀ ਚੋਣ", "compose_form.poll.type": "ਸਟਾਈਲ", @@ -165,10 +180,21 @@ "confirmations.logout.title": "ਲਾਗ ਆਉਟ ਕਰਨਾ ਹੈ?", "confirmations.missing_alt_text.secondary": "ਕਿਵੇਂ ਵੀ ਪੋਸਟ ਕਰੋ", "confirmations.mute.confirm": "ਮੌਨ ਕਰੋ", + "confirmations.private_quote_notify.do_not_show_again": "ਮੈਨੂੰ ਇਹ ਸੁਨੇਹਾ ਫੇਰ ਨਾ ਦਿਖਾਓ", + "confirmations.quiet_post_quote_info.dismiss": "ਮੈਨੂੰ ਮੁੜ ਕੇ ਯਾਦ ਨਾ ਕਰਵਾਓ", + "confirmations.quiet_post_quote_info.got_it": "ਸਮਝ ਗਏ", "confirmations.redraft.confirm": "ਹਟਾਓ ਤੇ ਮੁੜ-ਡਰਾਫਟ", + "confirmations.redraft.title": "ਪੋਸਟ ਨੂੰ ਹਟਾ ਕੇ ਮੁੜ-ਡਰਾਫਟ ਕਰਨਾ ਹੈ?", "confirmations.remove_from_followers.confirm": "ਫ਼ਾਲੋਅਰ ਨੂੰ ਹਟਾਓ", "confirmations.remove_from_followers.title": "ਫ਼ਾਲੋਅਰ ਨੂੰ ਹਟਾਉਣਾ ਹੈ?", + "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": "ਹੋਰ ਵੇਖਾਓ", @@ -194,6 +220,7 @@ "domain_pill.username": "ਵਰਤੋਂਕਾਰ-ਨਾਂ", "domain_pill.whats_in_a_handle": "ਹੈਂਡਲ ਕੀ ਹੁੰਦਾ ਹੈ?", "domain_pill.your_handle": "ਤੁਹਾਡਾ ਹੈਂਡਲ:", + "dropdown.empty": "ਕੋਈ ਚੋਣ ਕਰੋ", "embed.instructions": "ਹੇਠਲੇ ਕੋਡ ਨੂੰ ਕਾਪੀ ਕਰਕੇ ਆਪਣੀ ਵੈੱਬਸਾਈਟ ਉੱਤੇ ਇਸ ਪੋਸਟ ਨੂੰ ਇੰਬੈੱਡ ਕਰੋ।", "emoji_button.activity": "ਗਤੀਵਿਧੀ", "emoji_button.clear": "ਮਿਟਾਓ", @@ -246,6 +273,7 @@ "follow_suggestions.who_to_follow": "ਕਿਸ ਨੂੰ ਫ਼ਾਲੋ ਕਰੀਏ", "followed_tags": "ਫ਼ਾਲੋ ਕੀਤੇ ਹੈਸ਼ਟੈਗ", "footer.about": "ਸਾਡੇ ਬਾਰੇ", + "footer.about_this_server": "ਇਸ ਬਾਰੇ", "footer.directory": "ਪਰੋਫਾਇਲ ਡਾਇਰੈਕਟਰੀ", "footer.get_app": "ਐਪ ਲਵੋ", "footer.keyboard_shortcuts": "ਕੀਬੋਰਡ ਸ਼ਾਰਟਕੱਟ", @@ -255,6 +283,7 @@ "footer.terms_of_service": "ਸੇਵਾ ਦੀਆਂ ਸ਼ਰਤਾਂ", "generic.saved": "ਸਾਂਭੀ ਗਈ", "getting_started.heading": "ਸ਼ੁਰੂ ਕਰੀਏ", + "hashtag.browse": "#{hashtag} ਵਿੱਚ ਪੋਸਟਾਂ ਨੂੰ ਵੇਖੋ", "hashtag.column_header.tag_mode.all": "ਅਤੇ {additional}", "hashtag.column_header.tag_mode.any": "ਜਾਂ {additional}", "hashtag.column_header.tag_mode.none": "{additional} ਬਿਨਾਂ", @@ -319,6 +348,8 @@ "keyboard_shortcuts.translate": "ਪੋਸਟ ਨੂੰ ਅਨੁਵਾਦ ਕਰਨ ਲਈ", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "ਸੂਚੀ ਵਿੱਚ ਉੱਤੇ ਭੇਜੋ", + "learn_more_link.got_it": "ਸਮਝ ਗਏ", + "learn_more_link.learn_more": "ਹੋਰ ਜਾਣੋ", "lightbox.close": "ਬੰਦ ਕਰੋ", "lightbox.next": "ਅਗਲੀ", "lightbox.previous": "ਪਿਛਲੀ", @@ -412,6 +443,7 @@ "notifications.column_settings.group": "ਗਰੁੱਪ", "notifications.column_settings.mention": "ਜ਼ਿਕਰ:", "notifications.column_settings.poll": "ਪੋਲ ਦੇ ਨਤੀਜੇ:", + "notifications.column_settings.quote": "ਹਵਾਲੇ:", "notifications.column_settings.reblog": "ਬੂਸਟ:", "notifications.column_settings.show": "ਕਾਲਮ ਵਿੱਚ ਵੇਖਾਓ", "notifications.column_settings.sound": "ਆਵਾਜ਼ ਚਲਾਓ", @@ -468,7 +500,9 @@ "relative_time.minutes": "{number}ਮਿੰ", "relative_time.seconds": "{number}ਸ", "relative_time.today": "ਅੱਜ", + "remove_quote_hint.button_label": "ਸਮਝ ਗਏ", "reply_indicator.cancel": "ਰੱਦ ਕਰੋ", + "reply_indicator.poll": "ਵੋਟਾਂ", "report.block": "ਬਲਾਕ", "report.categories.legal": "ਕਨੂੰਨੀ", "report.categories.other": "ਬਾਕੀ", @@ -523,8 +557,11 @@ "status.admin_status": "", "status.block": "@{name} ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਓ", "status.bookmark": "ਬੁੱਕਮਾਰਕ", + "status.context.retry": "ਮੁੜ-ਕੋਸ਼ਿਸ਼", + "status.context.show": "ਵੇਖਾਓ", "status.copy": "ਪੋਸਟ ਲਈ ਲਿੰਕ ਕਾਪੀ ਕਰੋ", "status.delete": "ਹਟਾਓ", + "status.delete.success": "ਪੋਸਟ ਨੂੰ ਹਟਾਇਆ", "status.direct": "{name} ਪ੍ਰਾਈਵੇਟ ਜ਼ਿਕਰ", "status.direct_indicator": "ਪ੍ਰਾਈਵੇਟ ਜ਼ਿਕਰ", "status.edit": "ਸੋਧ", @@ -543,6 +580,8 @@ "status.mute_conversation": "ਗੱਲਬਾਤ ਨੂੰ ਮੌਨ ਕਰੋ", "status.open": "ਇਹ ਪੋਸਟ ਨੂੰ ਫੈਲਾਓ", "status.pin": "ਪਰੋਫਾਈਲ ਉੱਤੇ ਟੰਗੋ", + "status.quote": "ਹਵਾਲਾ", + "status.quote.cancel": "ਹਵਾਲੇ ਨੂੰ ਰੱਦ ਕਰੋ", "status.read_more": "ਹੋਰ ਪੜ੍ਹੋ", "status.reblog": "ਵਧਾਓ", "status.reblogged_by": "{name} ਨੇ ਬੂਸਟ ਕੀਤਾ", @@ -550,6 +589,7 @@ "status.redraft": "ਹਟਾਓ ਤੇ ਮੁੜ-ਡਰਾਫਟ", "status.remove_bookmark": "ਬੁੱਕਮਾਰਕ ਨੂੰ ਹਟਾਓ", "status.remove_favourite": "ਮਨਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਓ", + "status.remove_quote": "ਹਟਾਓ", "status.replied_in_thread": "ਮਾਮਲੇ ਵਿੱਚ ਜਵਾਬ ਦਿਓ", "status.replied_to": "{name} ਨੂੰ ਜਵਾਬ ਦਿੱਤਾ", "status.reply": "ਜਵਾਬ ਦੇਵੋ", @@ -594,6 +634,13 @@ "video.mute": "ਮੌਨ", "video.pause": "ਠਹਿਰੋ", "video.play": "ਚਲਾਓ", + "video.skip_backward": "ਛੱਡ ਕੇ ਪਿੱਛੇ ਜਾਓ", + "video.skip_forward": "ਛੱਡ ਕੇ ਅੱਗੇ ਜਾਓ", "video.volume_down": "ਅਵਾਜ਼ ਘਟਾਓ", - "video.volume_up": "ਅਵਾਜ਼ ਵਧਾਓ" + "video.volume_up": "ਅਵਾਜ਼ ਵਧਾਓ", + "visibility_modal.quote_followers": "ਸਿਰਫ਼ ਫ਼ਾਲੋਅਰ", + "visibility_modal.quote_label": "ਕੌਣ ਹਵਾਲਾ ਦੇ ਸਕਦਾ ਹੈ", + "visibility_modal.quote_nobody": "ਸਿਰਫ਼ ਮੈਂ", + "visibility_modal.quote_public": "ਕੋਈ ਵੀ", + "visibility_modal.save": "ਸੰਭਾਲੋ" } diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 13a38fa3e9f..f88f4b66815 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -889,7 +889,6 @@ "status.edited_x_times": "Edytowano {count, plural, one {{count} raz} other {{count} razy}}", "status.embed": "Kod osadzenia", "status.favourite": "Dodaj do ulubionych", - "status.favourites": "{count, plural, one {polubienie} few {polubienia} other {polubień}}", "status.filter": "Filtruj ten wpis", "status.history.created": "{name} utworzone {date}", "status.history.edited": "{name} edytowane {date}", @@ -918,7 +917,6 @@ "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.", @@ -927,7 +925,6 @@ "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ę", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index af1ee37b935..d60d1561524 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -4,7 +4,7 @@ "about.default_locale": "Padrão", "about.disclaimer": "Mastodon é um software de código aberto e livre, e uma marca registrada de Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Razão não disponível", - "about.domain_blocks.preamble": "O Mastodon geralmente permite que você veja o conteúdo e interaja com usuários de qualquer outro servidor no fediverso. Estas são as exceções deste servidor em específico.", + "about.domain_blocks.preamble": "O \"Mastodon\" geralmente permite que você veja o conteúdo e interaja com usuários de qualquer outro servidor no \"fediverso\". Estas são as exceções deste servidor em específico.", "about.domain_blocks.silenced.explanation": "Você geralmente não verá perfis e conteúdo deste servidor, a menos que você o procure explicitamente ou opte por seguir.", "about.domain_blocks.silenced.title": "Limitado", "about.domain_blocks.suspended.explanation": "Nenhum dado desse servidor será processado, armazenado ou trocado, impossibilitando qualquer interação ou comunicação com os usuários deste servidor.", @@ -36,7 +36,7 @@ "account.familiar_followers_two": "Seguido por {name1} e {name2}", "account.featured": "Em destaque", "account.featured.accounts": "Perfis", - "account.featured.hashtags": "Hashtags", + "account.featured.hashtags": "\"Hashtags\"", "account.featured_tags.last_status_at": "Última publicação em {date}", "account.featured_tags.last_status_never": "Sem publicações", "account.follow": "Seguir", @@ -49,7 +49,7 @@ "account.followers": "Seguidores", "account.followers.empty": "Nada aqui.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", - "account.followers_you_know_counter": "{counter} que você sabe", + "account.followers_you_know_counter": "{counter} que você conhece", "account.following": "Seguindo", "account.following_counter": "{count, plural, one {{counter} seguindo} other {{counter} seguindo}}", "account.follows.empty": "Nada aqui.", @@ -249,6 +249,11 @@ "confirmations.missing_alt_text.secondary": "Postar mesmo assim", "confirmations.missing_alt_text.title": "Adicionar texto alternativo?", "confirmations.mute.confirm": "Silenciar", + "confirmations.private_quote_notify.cancel": "Voltar à edição", + "confirmations.private_quote_notify.confirm": "Publicar postagem", + "confirmations.private_quote_notify.do_not_show_again": "Não me mostre esta mensagem novamente", + "confirmations.private_quote_notify.message": "A pessoa que está citando e outras menções serão notificadas e poderão ver sua postagem, mesmo que não sigam você.", + "confirmations.private_quote_notify.title": "Compartilhar com seguidores e usuários mencionados?", "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.", @@ -352,6 +357,7 @@ "empty_column.notification_requests": "Tudo limpo! Não há nada aqui. Quando você receber novas notificações, elas aparecerão aqui de acordo com suas configurações.", "empty_column.notifications": "Interaja com outros usuários para começar a conversar.", "empty_column.public": "Publique algo ou siga manualmente usuários de outros servidores", + "error.no_hashtag_feed_access": "Se cadastre ou faça login para ver e seguir esta hashtag.", "error.unexpected_crash.explanation": "Esta página não pôde ser mostrada corretamente. Este erro provavelmente é devido a um bug em nosso código ou um problema de compatibilidade de navegador.", "error.unexpected_crash.explanation_addons": "Esta página não pôde ser mostrada corretamente. Este erro provavelmente é causado por um complemento do navegador ou ferramentas de tradução automática.", "error.unexpected_crash.next_steps": "Tente atualizar a página. Se isso não ajudar, você ainda poderá usar o Mastodon por meio de um navegador diferente ou de um aplicativo nativo.", @@ -760,6 +766,7 @@ "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.private_mentions": "Citações não são permitidas com menções diretas.", "quote_error.quote": "Apenas uma citação por vez é permitido.", "quote_error.unauthorized": "Você não é autorizado a citar essa publicação.", "quote_error.upload": "Citações não são permitidas com mídias anexadas.", @@ -897,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}", "status.embed": "Obter código de incorporação", "status.favourite": "Favorita", - "status.favourites": "{count, plural, one {favorite} other {favorites}}", + "status.favourites_count": "{count, plural, one {{counter} favorito} other {{counter} favoritos}}", "status.filter": "Filtrar esta publicação", "status.history.created": "{name} criou {date}", "status.history.edited": "{name} editou {date}", @@ -929,17 +936,17 @@ "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.quotes_count": "{count, plural, one {{counter} mencionar} other {{counter} menções}}", "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.reblogs_count": "{count, plural, one {{counter} impulsionar} other {{counter} impulsionados}}", "status.redraft": "Excluir e rascunhar", "status.remove_bookmark": "Remover do Salvos", "status.remove_favourite": "Remover dos favoritos", @@ -1013,6 +1020,8 @@ "video.volume_down": "Diminuir o volume", "video.volume_up": "Aumentar o volume", "visibility_modal.button_title": "Selecionar Visibilidade", + "visibility_modal.direct_quote_warning.text": "Se salvar as configurações atuais, a cotação incorporada será convertida em um link.", + "visibility_modal.direct_quote_warning.title": "Cotações não podem ser incorporadas em menções privadas", "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.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index c67124a70d8..101a4bbd78b 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Tudo limpo! Não há nada aqui. Quando receberes novas notificações, elas aparecerão aqui conforme as tuas configurações.", "empty_column.notifications": "Ainda não tens quaisquer notificações. Quando outras pessoas interagirem contigo, verás isso aqui.", "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para veres aqui os conteúdos públicos", + "error.no_hashtag_feed_access": "Inscreva-se ou inicie sessão para ver e seguir esta etiqueta.", "error.unexpected_crash.explanation": "Devido a um erro no nosso código ou a um problema de compatibilidade do navegador, esta página não pode ser apresentada corretamente.", "error.unexpected_crash.explanation_addons": "Esta página não pode ser mostrada corretamente. Este erro provavelmente é causado por um complemento do navegador ou ferramentas de tradução automática.", "error.unexpected_crash.next_steps": "Tenta atualizar a página. Se isso não ajudar, podes usar o Mastodon através de um navegador diferente ou uma aplicação nativa.", @@ -903,7 +904,7 @@ "status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}", "status.embed": "Obter código de incorporação", "status.favourite": "Adicionar aos favoritos", - "status.favourites": "{count, plural, one {favorito} other {favoritos}}", + "status.favourites_count": "{count, plural, one {{counter} favorito} other {{counter} favoritos}}", "status.filter": "Filtrar esta publicação", "status.history.created": "{name} criado em {date}", "status.history.edited": "{name} editado em {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Alterar quem pode citar", "status.quote_post_author": "Citou uma publicação de @{name}", "status.quote_private": "Publicações privadas não podem ser citadas", - "status.quotes": "{count, plural, one {citação} other {citações}}", "status.quotes.empty": "Ainda ninguém citou esta publicação. Quando alguém o fizer, aparecerá aqui.", "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.quotes_count": "{count, plural, one {{counter} citação} other {{counter} citações}}", "status.read_more": "Ler mais", "status.reblog": "Partilhar", "status.reblog_or_quote": "Partilhe ou cite", "status.reblog_private": "Partilhe novamente com os seus seguidores", "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.reblogs_count": "{count, plural, one {{counter} partilha} other {{counter} partilhas}}", "status.redraft": "Eliminar e reescrever", "status.remove_bookmark": "Retirar dos marcadores", "status.remove_favourite": "Remover dos favoritos", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 5b55483c912..2461dcf7e4c 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -157,6 +157,7 @@ "bundle_modal_error.close": "Закрыть", "bundle_modal_error.message": "Кое-что пошло не так при загрузке этой страницы.", "bundle_modal_error.retry": "Попробовать снова", + "carousel.slide": "Слайд {current, number} из {max, number}", "closed_registrations.other_server_instructions": "Благодаря тому что Mastodon децентрализован, вы можете взаимодействовать с этим сервером, даже если зарегистрируетесь на другом сервере.", "closed_registrations_modal.description": "Зарегистрироваться на {domain} сейчас не выйдет, но имейте в виду, что вам не нужна учётная запись именно на {domain}, чтобы использовать Mastodon.", "closed_registrations_modal.find_another_server": "Найти другой сервер", @@ -355,6 +356,7 @@ "empty_column.notification_requests": "Здесь ничего нет! Когда вы получите новые уведомления, они здесь появятся согласно вашим настройкам.", "empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.", "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других серверов, чтобы заполнить ленту", + "error.no_hashtag_feed_access": "Зарегистрируйтесь или войдите, чтобы читать и подписаться на этот хэштег.", "error.unexpected_crash.explanation": "Из-за несовместимого браузера или ошибки в нашем коде эта страница не может быть корректно отображена.", "error.unexpected_crash.explanation_addons": "Эта страница не может быть корректно отображена. Скорее всего, ошибка вызвана расширением браузера или инструментом автоматического перевода.", "error.unexpected_crash.next_steps": "Попробуйте обновить страницу. Если это не поможет, вы, возможно, всё ещё сможете использовать Mastodon в другом браузере или приложении.", @@ -628,12 +630,12 @@ "notification.moderation_warning.action_disable": "Ваша учётная запись была отключена.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Некоторые ваши посты были отмечены как содержимое деликатного характера.", "notification.moderation_warning.action_none": "Модераторы вынесли вам предупреждение.", - "notification.moderation_warning.action_sensitive": "С этого момента все ваши новые посты будут отмечены как содержимое деликатного характера.", + "notification.moderation_warning.action_sensitive": "С этого момента все ваши посты будут отмечены как содержимое деликатного характера.", "notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.", "notification.moderation_warning.action_suspend": "Ваша учётная запись была заблокирована.", "notification.own_poll": "Ваш опрос завершился", "notification.poll": "Опрос, в котором вы приняли участие, завершился", - "notification.quoted_update": "{name} отредактировал процитированное вами сообщение", + "notification.quoted_update": "{name} отредактировал(а) процитированный вами пост", "notification.reblog": "{name} продвинул(а) ваш пост", "notification.reblog.name_and_others_with_link": "{name} и ещё {count, plural, one {# пользователь} few {# пользователя} other {# пользователей}} продвинули ваш пост", "notification.relationships_severance_event": "Разорвана связь с {name}", @@ -763,9 +765,10 @@ "privacy_policy.title": "Политика конфиденциальности", "quote_error.edit": "Нельзя добавить цитирование к уже опубликованному посту.", "quote_error.poll": "Цитирование не допускается при голосовании.", + "quote_error.private_mentions": "Цитирование в личных упоминаниях не позволяется.", "quote_error.quote": "Одновременно допускается только одна цитата.", "quote_error.unauthorized": "Вы не имеете права цитировать этот пост.", - "quote_error.upload": "Цитирование с использованием медиа вложений запрещено.", + "quote_error.upload": "Цитирование не позволяется, если к посту прикреплены медиавложения.", "recommended": "Рекомендуется", "refresh": "Обновить", "regeneration_indicator.please_stand_by": "Пожалуйста, подождите.", @@ -782,7 +785,8 @@ "relative_time.seconds": "{number} с.", "relative_time.today": "сегодня", "remove_quote_hint.button_label": "Понятно", - "remove_quote_hint.message": "Вы можете сделать это из меню настроек {icon}.", + "remove_quote_hint.message": "Вы можете сделать это из {icon} меню поста.", + "remove_quote_hint.title": "Хотите убрать цитирование вашего поста?", "reply_indicator.attachments": "{count, plural, one {# вложение} few {# вложения} other {# вложений}}", "reply_indicator.cancel": "Отмена", "reply_indicator.poll": "Опрос", @@ -880,6 +884,7 @@ "status.cancel_reblog_private": "Отменить продвижение", "status.cannot_quote": "Вы не можете процитировать этот пост", "status.cannot_reblog": "Этот пост не может быть продвинут", + "status.contains_quote": "Цитирует другой пост", "status.context.loading": "Загрузка ответов", "status.context.loading_error": "Не удалось загрузить новые ответы", "status.context.loading_success": "Загружены новые ответы", @@ -898,7 +903,7 @@ "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}", "status.embed": "Встроить на свой сайт", "status.favourite": "Добавить в избранное", - "status.favourites": "{count, plural, one {звёздочка} few {звёздочки} other {звёздочек}}", + "status.favourites_count": "{count, plural, one {{counter} звёздочка} few {{counter} звёздочки} other {{counter} звёздочек}}", "status.filter": "Фильтровать этот пост", "status.history.created": "{name} создал(а) пост {date}", "status.history.edited": "{name} отредактировал(а) пост {date}", @@ -929,16 +934,16 @@ "status.quote_noun": "Цитата", "status.quote_policy_change": "Изменить настройки цитирования", "status.quote_post_author": "Процитировал(а) пост @{name}", - "status.quote_private": "Приватные записи не могут быть цитированы", - "status.quotes": "{count, plural, one {цитирование} few {цитирования} other {цитирований}}", + "status.quote_private": "Приватные посты не могут быть процитированы", "status.quotes.empty": "Никто еще не процитировал этот пост. Все цитирования этого поста будут показаны здесь.", + "status.quotes_count": "{count, plural, one {{counter} цитирование} few {{counter} цитирования} other {{counter} цитирований}}", "status.read_more": "Читать далее", "status.reblog": "Продвинуть", "status.reblog_or_quote": "Продвинуть или процитировать", "status.reblog_private": "Поделиться со своими подписчиками ещё раз", "status.reblogged_by": "{name} продвинул(а)", - "status.reblogs": "{count, plural, one {продвижение} few {продвижения} other {продвижений}}", "status.reblogs.empty": "Никто ещё не продвинул этот пост. Все пользователи, которые продвинут этот пост, будут показаны здесь.", + "status.reblogs_count": "{count, plural, one {{counter} продвижение} few {{counter} продвижения} other {{counter} продвижений}}", "status.redraft": "Удалить и исправить", "status.remove_bookmark": "Убрать из закладок", "status.remove_favourite": "Убрать из избранного", @@ -1015,12 +1020,12 @@ "visibility_modal.direct_quote_warning.text": "Если вы сохраните эти настройки, вложенное цитирование будет преобразовано в ссылку.", "visibility_modal.direct_quote_warning.title": "Нельзя добавить цитирование к личным упоминаниям", "visibility_modal.header": "Видимость и взаимодействие", - "visibility_modal.helper.direct_quoting": "Частные упоминания, созданные на Mastodon не могут быть цитированы другими.", + "visibility_modal.helper.direct_quoting": "Посты, созданные в Mastodon как личные упоминания, не могут быть процитированы другими пользователями.", "visibility_modal.helper.privacy_editing": "Видимость поста невозможно изменить после публикации.", - "visibility_modal.helper.privacy_private_self_quote": "Цитаты из личных сообщений не могут быть опубликованы публично.", - "visibility_modal.helper.private_quoting": "Публикации на Mastodon, доступные исключительно подписчикам, не подлежат цитированию со стороны других пользователей.", - "visibility_modal.helper.unlisted_quoting": "Когда люди цитируют вас, их посты также скрываются из ленты трендов.", - "visibility_modal.instructions": "Регулируйте, кто сможет взаимодействовать с этим сообщением. Также можно установить параметры для всех будущих публикаций, перейдя в Настройки > Стандартные параметры публикации.", + "visibility_modal.helper.privacy_private_self_quote": "Посты, которые цитируют ваши приватные посты, не могут быть публичными.", + "visibility_modal.helper.private_quoting": "Посты, созданные в Mastodon с видимостью только для подписчиков, не могут быть процитированы другими пользователями.", + "visibility_modal.helper.unlisted_quoting": "Если кто-нибудь процитирует вас, его пост тоже будет скрыт из алгоритмических лент.", + "visibility_modal.instructions": "Определите, кто сможет взаимодействовать с этим постом. Чтобы применить настройки ко всем будущим постам, перейдите в Настройки > Предустановки для новых постов.", "visibility_modal.privacy_label": "Видимость", "visibility_modal.quote_followers": "Только подписчики", "visibility_modal.quote_label": "Кто может цитировать вас", diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 4e2d40fe5c6..8b4f4cc3d5f 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -689,7 +689,6 @@ "status.detailed_status": "Visualizatzione de detàlliu de arresonada", "status.edit": "Modìfica", "status.edited_x_times": "Modificadu {count, plural, one {{count} # borta} other {{count} bortas}}", - "status.favourites": "{count, plural, one {preferidu} other {preferidos}}", "status.load_more": "Càrriga·nde àteros", "status.media_hidden": "Elementos multimediales cuados", "status.mention": "Mèntova a @{name}", diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json index e326d3fa6a7..8b6ab568832 100644 --- a/app/javascript/mastodon/locales/si.json +++ b/app/javascript/mastodon/locales/si.json @@ -809,7 +809,6 @@ "status.edited_x_times": "සංශෝධිතයි {count, plural, one {වාර {count}} other {වාර {count}}}", "status.embed": "කාවැද්දූ කේතය ලබා ගන්න", "status.favourite": "ප්‍රියතම", - "status.favourites": "{count, plural, one {ප්‍රියතම} other {ප්‍රියතම}}", "status.filter": "මෙම ලිපිය පෙරන්න", "status.history.created": "{name} නිර්මාණය {date}", "status.history.edited": "{name} සංස්කරණය {date}", @@ -826,7 +825,6 @@ "status.read_more": "තව කියවන්න", "status.reblog": "බූස්ට් කරන්න", "status.reblogged_by": "{name} වැඩි කරන ලදී", - "status.reblogs": "{count, plural, one {බූස්ට්} other {බූස්ට්}}", "status.reblogs.empty": "මෙම සටහන තවම කිසිවෙකු බූස්ට් කර නැත. යමෙකු එසේ කළ විට, ඔවුන් මෙහි පෙන්වනු ඇත.", "status.redraft": "& නැවත කෙටුම්පත මකන්න", "status.remove_bookmark": "පොත්යොමුව ඉවතලන්න", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 76987b906bf..c440c786237 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -227,7 +227,9 @@ "confirmations.delete_list.title": "Vymazať zoznam?", "confirmations.discard_draft.confirm": "Zahodiť a pokračovať", "confirmations.discard_draft.edit.cancel": "Pokračovať v úpravách", + "confirmations.discard_draft.edit.title": "Zahodiť zmeny v tvojom príspevku?", "confirmations.discard_draft.post.cancel": "Pokračuj v rozpísanom", + "confirmations.discard_draft.post.title": "Zahodiť tvoj rozpísaný príspevok?", "confirmations.discard_edit_media.confirm": "Zahodiť", "confirmations.discard_edit_media.message": "Máte neuložené zmeny v popise alebo náhľade média, zahodiť ich aj tak?", "confirmations.follow_to_list.confirm": "Nasleduj a pridaj do zoznamu", @@ -244,7 +246,9 @@ "confirmations.private_quote_notify.cancel": "Späť na úpravu", "confirmations.private_quote_notify.confirm": "Zverejni príspevok", "confirmations.private_quote_notify.do_not_show_again": "Neukazuj mi túto spravu znova", + "confirmations.private_quote_notify.title": "Zdieľať z nasledovateľmi a spomenutými užívateľmi?", "confirmations.quiet_post_quote_info.dismiss": "Nepripomínaj mi znova", + "confirmations.quiet_post_quote_info.got_it": "Mám to", "confirmations.redraft.confirm": "Vymazať a prepísať", "confirmations.redraft.message": "Určite chcete tento príspevok vymazať a prepísať? Prídete o jeho zdieľania a ohviezdičkovania a odpovede na pôvodný príspevok budú odlúčené.", "confirmations.redraft.title": "Vymazať a prepísať príspevok?", @@ -817,7 +821,6 @@ "status.reblog": "Zdieľať", "status.reblog_or_quote": "Zdieľať alebo citovať", "status.reblogged_by": "{name} zdieľa", - "status.reblogs": "{count, plural, one {zdieľanie} few {zdieľania} many {} other {zdieľaní}}", "status.reblogs.empty": "Nikto ešte tento príspevok nezdieľal. Keď tak niekto urobí, zobrazí sa to tu.", "status.redraft": "Vymazať a prepísať", "status.remove_bookmark": "Odstrániť záložku", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 43f6c511d37..7f7d8bd9910 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -1,6 +1,7 @@ { "about.blocks": "Moderirani strežniki", "about.contact": "Stik:", + "about.default_locale": "Privzeto", "about.disclaimer": "Mastodon je prosto, odprtokodno programje in blagovna znamka podjetja Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Razlog ni na voljo", "about.domain_blocks.preamble": "Mastodon vam na splošno omogoča ogled vsebin in interakcijo z uporabniki z vseh drugih strežnikov v fediverzumu. Tu so navedene izjeme, ki jih postavlja ta strežnik.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Omejeno", "about.domain_blocks.suspended.explanation": "Nobeni podatki s tega strežnika ne bodo obdelani, shranjeni ali izmenjani, zaradi česar je nemogoča kakršna koli interakcija ali komunikacija z uporabniki s tega strežnika.", "about.domain_blocks.suspended.title": "Suspendiran", + "about.language_label": "Jezik", "about.not_available": "Ti podatki še niso na voljo na tem strežniku.", "about.powered_by": "Decentraliziran družabni medij, ki ga poganja {mastodon}", "about.rules": "Pravila strežnika", @@ -24,18 +26,27 @@ "account.direct": "Zasebno omeni @{name}", "account.disable_notifications": "Ne obveščaj me več, ko ima @{name} novo objavo", "account.edit_profile": "Uredi profil", + "account.edit_profile_short": "Uredi", "account.enable_notifications": "Obvesti me, ko ima @{name} novo objavo", "account.endorse": "Izpostavi v profilu", + "account.familiar_followers_one": "Sledi {name1}", + "account.featured": "Izpostavljeni", + "account.featured.accounts": "Profili", + "account.featured.hashtags": "Ključniki", "account.featured_tags.last_status_at": "Zadnja objava {date}", "account.featured_tags.last_status_never": "Ni objav", "account.follow": "Sledi", "account.follow_back": "Sledi nazaj", + "account.follow_back_short": "Sledi nazaj", + "account.follow_request_cancel": "Prekliči zahtevo", + "account.follow_request_cancel_short": "Prekliči", "account.followers": "Sledilci", "account.followers.empty": "Nihče še ne sledi temu uporabniku.", "account.followers_counter": "{count, plural, one {{counter} sledilec} two {{counter} sledilca} few {{counter} sledilci} other {{counter} sledilcev}}", "account.following": "Sledim", "account.following_counter": "{count, plural, one {{counter} sleden} two {{counter} sledena} few {{counter} sledeni} other {{counter} sledenih}}", "account.follows.empty": "Ta uporabnik še ne sledi nikomur.", + "account.follows_you": "Vam sledi", "account.go_to_profile": "Pojdi na profil", "account.hide_reblogs": "Skrij izpostavitve od @{name}", "account.in_memoriam": "V spomin.", @@ -50,6 +61,7 @@ "account.mute_notifications_short": "Utišaj obvestila", "account.mute_short": "Utišaj", "account.muted": "Utišan", + "account.mutual": "Drug drugemu sledita", "account.no_bio": "Ni opisa.", "account.open_original_page": "Odpri izvirno stran", "account.posts": "Objave", @@ -61,6 +73,7 @@ "account.statuses_counter": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objave} other {{counter} objav}}", "account.unblock": "Odblokiraj @{name}", "account.unblock_domain": "Odblokiraj domeno {domain}", + "account.unblock_domain_short": "Odblokiraj", "account.unblock_short": "Odblokiraj", "account.unendorse": "Ne vključi v profil", "account.unfollow": "Ne sledi več", @@ -150,6 +163,8 @@ "column.edit_list": "Uredi seznam", "column.favourites": "Priljubljeni", "column.firehose": "Viri v živo", + "column.firehose_local": "Vir v živo za ta strežnik", + "column.firehose_singular": "Vir v živo", "column.follow_requests": "Prošnje za sledenje", "column.home": "Domov", "column.list_members": "Upravljaj člane seznama", @@ -169,6 +184,7 @@ "community.column_settings.local_only": "Samo krajevno", "community.column_settings.media_only": "Samo predstavnosti", "community.column_settings.remote_only": "Samo oddaljeno", + "compose.error.blank_post": "Objava ne sme biti prazna", "compose.language.change": "Spremeni jezik", "compose.language.search": "Poišči jezike ...", "compose.published.body": "Objavljeno.", @@ -201,6 +217,8 @@ "confirmations.delete_list.confirm": "Izbriši", "confirmations.delete_list.message": "Ali ste prepričani, da želite trajno izbrisati ta seznam?", "confirmations.delete_list.title": "Želite izbrisati seznam?", + "confirmations.discard_draft.edit.cancel": "Nadaljuj z urejanjem", + "confirmations.discard_draft.post.cancel": "Nadaljuj na osnutku", "confirmations.discard_edit_media.confirm": "Opusti", "confirmations.discard_edit_media.message": "Spremenjenega opisa predstavnosti ali predogleda niste shranili. Želite spremembe kljub temu opustiti?", "confirmations.follow_to_list.confirm": "Sledi in dodaj na seznam", @@ -214,10 +232,24 @@ "confirmations.missing_alt_text.secondary": "Vseeno objavi", "confirmations.missing_alt_text.title": "Dodam nadomestno besedilo?", "confirmations.mute.confirm": "Utišaj", + "confirmations.private_quote_notify.cancel": "Nazaj k urejanju", + "confirmations.private_quote_notify.confirm": "Objavi objavo", + "confirmations.private_quote_notify.do_not_show_again": "Ne prikazuj mi več tega sporočila", + "confirmations.quiet_post_quote_info.dismiss": "Ne opominjaj me več", + "confirmations.quiet_post_quote_info.got_it": "Razumem", "confirmations.redraft.confirm": "Izbriši in preoblikuj", "confirmations.redraft.message": "Ali ste prepričani, da želite izbrisati to objavo in jo preoblikovati? Izkazi priljubljenosti in izpostavitve bodo izgubljeni, odgovori na izvirno objavo pa bodo osiroteli.", "confirmations.redraft.title": "Želite izbrisati in preoblikovati objavo?", + "confirmations.remove_from_followers.confirm": "Odstrani sledilca", + "confirmations.remove_from_followers.title": "Ali želite odstraniti sledilca?", + "confirmations.revoke_quote.confirm": "Odstrani objavo", + "confirmations.revoke_quote.message": "Tega dejanja ni možno povrniti.", + "confirmations.revoke_quote.title": "Ali želite odstraniti objavo?", + "confirmations.unblock.confirm": "Odblokiraj", + "confirmations.unblock.title": "Ali želite odblokirati {name}?", "confirmations.unfollow.confirm": "Ne sledi več", + "confirmations.unfollow.title": "Ali želite prenehati slediti {name}?", + "confirmations.withdraw_request.confirm": "Umakni zahtevo", "content_warning.hide": "Skrij objavo", "content_warning.show": "Vseeno pokaži", "content_warning.show_more": "Pokaži več", @@ -304,6 +336,7 @@ "errors.unexpected_crash.copy_stacktrace": "Kopiraj sled sklada na odložišče", "errors.unexpected_crash.report_issue": "Prijavi težavo", "explore.suggested_follows": "Ljudje", + "explore.title": "V trendu", "explore.trending_links": "Novice", "explore.trending_statuses": "Objave", "explore.trending_tags": "Ključniki", @@ -371,7 +404,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} udeleženec} two {{counter} udeleženca} few {{counter} udeležencev} other {{counter} udeležencev}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} objava} two {{counter} posts} few {{counter} objavi} other {{counter} objav}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objav} other {{counter} objav}}", + "hashtag.feature": "Izpostavi v profilu", "hashtag.follow": "Sledi ključniku", + "hashtag.mute": "Utišaj #{hashtag}", + "hashtag.unfeature": "Ne izpostavljaj v profilu", "hashtag.unfollow": "Nehaj slediti ključniku", "hashtags.and_other": "… in še {count, plural, other {#}}", "hints.profiles.followers_may_be_missing": "Sledilci za ta profil morda manjkajo.", @@ -380,6 +416,7 @@ "hints.profiles.see_more_followers": "Pokaži več sledilcev na {domain}", "hints.profiles.see_more_follows": "Pokaži več sledenih ljudi na zbirališču {domain}", "hints.profiles.see_more_posts": "Pokaži več objav na {domain}", + "home.column_settings.show_quotes": "Pokaži citate", "home.column_settings.show_reblogs": "Pokaži izpostavitve", "home.column_settings.show_replies": "Pokaži odgovore", "home.hide_announcements": "Skrij obvestila", @@ -404,6 +441,7 @@ "interaction_modal.no_account_yet": "Še nimate računa?", "interaction_modal.on_another_server": "Na drugem strežniku", "interaction_modal.on_this_server": "Na tem strežniku", + "interaction_modal.title": "Za nadaljevanje se prijavite", "interaction_modal.username_prompt": "Npr. {example}", "intervals.full.days": "{number, plural, one {# dan} two {# dni} few {# dni} other {# dni}}", "intervals.full.hours": "{number, plural, one {# ura} two {# uri} few {# ure} other {# ur}}", @@ -432,6 +470,7 @@ "keyboard_shortcuts.open_media": "Odpri predstavnost", "keyboard_shortcuts.pinned": "Odpri seznam pripetih objav", "keyboard_shortcuts.profile": "Odpri avtorjev profil", + "keyboard_shortcuts.quote": "Citiraj objavo", "keyboard_shortcuts.reply": "Odgovori na objavo", "keyboard_shortcuts.requests": "Odpri seznam s prošnjami za sledenje", "keyboard_shortcuts.search": "Pozornost na iskalno vrstico", @@ -443,6 +482,8 @@ "keyboard_shortcuts.translate": "za prevod objave", "keyboard_shortcuts.unfocus": "Odstrani pozornost z območja za sestavljanje besedila/iskanje", "keyboard_shortcuts.up": "Premakni navzgor po seznamu", + "learn_more_link.got_it": "Razumem", + "learn_more_link.learn_more": "Več o tem", "lightbox.close": "Zapri", "lightbox.next": "Naslednji", "lightbox.previous": "Prejšnji", @@ -492,8 +533,10 @@ "mute_modal.you_wont_see_mentions": "Objav, ki jih omenjajo, ne boste videli.", "mute_modal.you_wont_see_posts": "Še vedno vidijo vaše objave, vi pa ne njihovih.", "navigation_bar.about": "O Mastodonu", + "navigation_bar.account_settings": "Geslo in varnost", "navigation_bar.administration": "Upravljanje", "navigation_bar.advanced_interface": "Odpri v naprednem spletnem vmesniku", + "navigation_bar.automated_deletion": "Samodejno brisanje objav", "navigation_bar.blocks": "Blokirani uporabniki", "navigation_bar.bookmarks": "Zaznamki", "navigation_bar.direct": "Zasebne omembe", @@ -503,12 +546,15 @@ "navigation_bar.follow_requests": "Prošnje za sledenje", "navigation_bar.followed_tags": "Sledeni ključniki", "navigation_bar.follows_and_followers": "Sledenja in sledilci", + "navigation_bar.import_export": "Uvoz in izvoz", "navigation_bar.lists": "Seznami", "navigation_bar.logout": "Odjava", "navigation_bar.moderation": "Moderiranje", + "navigation_bar.more": "Več", "navigation_bar.mutes": "Utišani uporabniki", "navigation_bar.opened_in_classic_interface": "Objave, računi in druge specifične strani se privzeto odprejo v klasičnem spletnem vmesniku.", "navigation_bar.preferences": "Nastavitve", + "navigation_bar.privacy_and_reach": "Zasebnost in dosegljivost", "navigation_bar.search": "Iskanje", "not_signed_in_indicator.not_signed_in": "Za dostop do tega vira se morate prijaviti.", "notification.admin.report": "{name} je prijavil/a {target}", @@ -531,6 +577,7 @@ "notification.label.mention": "Omemba", "notification.label.private_mention": "Zasebna omemba", "notification.label.private_reply": "Zasebni odgovor", + "notification.label.quote": "{name} je citiral/a vašo objavo", "notification.label.reply": "Odgovori", "notification.mention": "Omemba", "notification.mentioned_you": "{name} vas je omenil/a", @@ -588,6 +635,7 @@ "notifications.column_settings.mention": "Omembe:", "notifications.column_settings.poll": "Rezultati ankete:", "notifications.column_settings.push": "Potisna obvestila", + "notifications.column_settings.quote": "Citati:", "notifications.column_settings.reblog": "Izpostavitve:", "notifications.column_settings.show": "Pokaži v stolpcu", "notifications.column_settings.sound": "Predvajaj zvok", @@ -682,6 +730,7 @@ "relative_time.minutes": "{number} m", "relative_time.seconds": "{number} s", "relative_time.today": "danes", + "remove_quote_hint.button_label": "Razumem", "reply_indicator.attachments": "{count, plural, one {# priloga} two {# prilogi} few {# priloge} other {# prilog}}", "reply_indicator.cancel": "Prekliči", "reply_indicator.poll": "Anketa", @@ -735,6 +784,7 @@ "report_notification.categories.violation": "Kršitev pravila", "report_notification.categories.violation_sentence": "kršitev pravila", "report_notification.open": "Odpri prijavo", + "search.clear": "Počisti iskanje", "search.no_recent_searches": "Ni nedavnih iskanj", "search.placeholder": "Iskanje", "search.quick_action.account_search": "Profili, ki se ujemajo z {x}", @@ -776,9 +826,13 @@ "status.bookmark": "Dodaj med zaznamke", "status.cancel_reblog_private": "Prekliči izpostavitev", "status.cannot_reblog": "Te objave ni mogoče izpostaviti", + "status.contains_quote": "Vsebuje citat", + "status.context.retry": "Poskusi znova", + "status.context.show": "Pokaži", "status.continued_thread": "Nadaljevanje niti", "status.copy": "Kopiraj povezavo do objave", "status.delete": "Izbriši", + "status.delete.success": "Objava je izbrisana", "status.detailed_status": "Podroben pogled pogovora", "status.direct": "Zasebno omeni @{name}", "status.direct_indicator": "Zasebna omemba", @@ -787,7 +841,6 @@ "status.edited_x_times": "Urejeno {count, plural, one {#-krat} two {#-krat} few {#-krat} other {#-krat}}", "status.embed": "Pridobite kodo za vgradnjo", "status.favourite": "Priljubljen/a", - "status.favourites": "{count, plural, one {priljubitev} two {priljubitvi} few {priljubitve} other {priljubitev}}", "status.filter": "Filtriraj to objavo", "status.history.created": "{name}: ustvarjeno {date}", "status.history.edited": "{name}: urejeno {date}", @@ -801,14 +854,22 @@ "status.mute_conversation": "Utišaj pogovor", "status.open": "Razširi to objavo", "status.pin": "Pripni na profil", + "status.quote.cancel": "Prekliči citat", + "status.quote_error.filtered": "Skrito zaradi enega od vaših filtrov", + "status.quote_error.limited_account_hint.action": "Vseeno pokaži", + "status.quote_error.limited_account_hint.title": "Ta račun so moderatorji {domain} skrili.", + "status.quote_error.not_available": "Objava ni na voljo", + "status.quote_followers_only": "Samo sledilci lahko citirajo to objavo", + "status.quote_policy_change": "Spremenite, kdo lahko citira", + "status.quote_private": "Zasebnih objav ni možno citirati", "status.read_more": "Preberi več", "status.reblog": "Izpostavi", "status.reblogged_by": "{name} je izpostavil/a", - "status.reblogs": "{count, plural, one {izpostavitev} two {izpostavitvi} few {izpostavitve} other {izpostavitev}}", "status.reblogs.empty": "Nihče še ni izpostavil te objave. Ko se bo to zgodilo, se bodo pojavile tukaj.", "status.redraft": "Izbriši in preoblikuj", "status.remove_bookmark": "Odstrani zaznamek", "status.remove_favourite": "Odstrani iz priljubljenih", + "status.remove_quote": "Odstrani", "status.replied_in_thread": "Odgovor iz niti", "status.replied_to": "Odgovoril/a {name}", "status.reply": "Odgovori", @@ -829,7 +890,10 @@ "subscribed_languages.save": "Shrani spremembe", "subscribed_languages.target": "Spremeni naročene jezike za {target}", "tabs_bar.home": "Domov", + "tabs_bar.menu": "Meni", "tabs_bar.notifications": "Obvestila", + "tabs_bar.publish": "Nova objava", + "tabs_bar.search": "Išči", "terms_of_service.effective_as_of": "Veljavno od {date}", "terms_of_service.title": "Pogoji uporabe", "terms_of_service.upcoming_changes_on": "Spremembe začnejo veljati {date}", @@ -863,6 +927,19 @@ "video.expand": "Razširi video", "video.fullscreen": "Celozaslonski način", "video.hide": "Skrij video", + "video.mute": "Utišaj", "video.pause": "Premor", - "video.play": "Predvajaj" + "video.play": "Predvajaj", + "video.skip_backward": "Preskoči nazaj", + "video.skip_forward": "Preskoči naprej", + "video.unmute": "Odtišaj", + "video.volume_down": "Zmanjšaj glasnost", + "video.volume_up": "Povečaj glasnost", + "visibility_modal.header": "Vidnost in interakcija", + "visibility_modal.privacy_label": "Vidnost", + "visibility_modal.quote_followers": "Samo sledilci", + "visibility_modal.quote_label": "Kdo lahko citira", + "visibility_modal.quote_nobody": "Samo jaz", + "visibility_modal.quote_public": "Vsi", + "visibility_modal.save": "Shrani" } diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index c4224d58131..bb43bbdbbc3 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -353,6 +353,7 @@ "empty_column.notification_requests": "Gjithçka si duhet! S’ka ç’bëhet këtu. Kur merrni njoftime të reja, do të shfaqen këtu, në përputhje me rregullimet tuaja.", "empty_column.notifications": "Ende s’keni ndonjë njoftim. Ndërveproni me të tjerët që të nisë biseda.", "empty_column.public": "S’ka gjë këtu! Shkruani diçka publikisht, ose ndiqni dorazi përdorues prej instancash të tjera, që kjo të mbushet", + "error.no_hashtag_feed_access": "Që të shihni dhe ndiqni hashtag-ë, regjistrohuni, ose bëni hyrjen.", "error.unexpected_crash.explanation": "Për shkak të një të mete në kodin tonë ose të një problemi përputhshmërie të shfletuesit, kjo faqe s’mund të shfaqet saktë.", "error.unexpected_crash.explanation_addons": "Kjo faqe s’u shfaq dot saktë. Ky gabim ka gjasa të jetë shkaktuar nga një shtesë shfletuesi ose një mjet përkthimi të automatizuar.", "error.unexpected_crash.next_steps": "Provoni të freskoni faqen. Nëse kjo s’bën punë, mundeni ende të jeni në gjendje të përdorni Mastodon-in që nga një shfletues tjetër ose nga ndonjë aplikacion origjinal prej projektit.", @@ -899,7 +900,7 @@ "status.edited_x_times": "Përpunuar {count, plural, one {{count} herë} other {{count} herë}}", "status.embed": "Merrni kod trupëzimi", "status.favourite": "I vini shenjë si të parapëlqyer", - "status.favourites": "{count, plural, one {i parapëlqyer} other {të parapëlqyer}}", + "status.favourites_count": "{count, plural, one {{counter} i parapëlqyer} other {{counter} të parapëlqyer}}", "status.filter": "Filtroje këtë postim", "status.history.created": "{name} u krijua më {date}", "status.history.edited": "{name} u përpunua më {date}", @@ -931,17 +932,17 @@ "status.quote_policy_change": "Ndryshoni cilët mund të citojnë", "status.quote_post_author": "U citua një postim nga @{name}", "status.quote_private": "Postimet private s’mund të citohen", - "status.quotes": "{count, plural, one {citim} other {citime}}", "status.quotes.empty": "Këtë postim ende s’e ka cituar kush. Kur dikush ta bëjë, do të shfaqet këtu.", "status.quotes.local_other_disclaimer": "Citimet e hedhura poshtë nga autori s’do të shfaqen.", "status.quotes.remote_other_disclaimer": "Këtu garantohet të shfaqen vetëm citime nga {domain}. Citime të hedhura poshtë nga autori s’do të shfaqen.", + "status.quotes_count": "{count, plural, one {{counter} citim} other {{counter} citime}}", "status.read_more": "Lexoni më tepër", "status.reblog": "Përforcojeni", "status.reblog_or_quote": "Përforconi ose citoni", "status.reblog_private": "Rindajeni me ndjekësit tuaj", "status.reblogged_by": "{name} përforcoi", - "status.reblogs": "{count, plural, one {përforcim} other {përforcime}}", "status.reblogs.empty": "Këtë mesazh s’e ka përforcuar njeri deri tani. Kur ta bëjë dikush, kjo do të duket këtu.", + "status.reblogs_count": "{count, plural, one {{counter} përforcim} other {{counter} përforcime}}", "status.redraft": "Fshijeni & rihartojeni", "status.remove_bookmark": "Hiqe faqerojtësin", "status.remove_favourite": "Hiqe nga të parapëlqyerat", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index f91044899e1..25091d8ab1e 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -620,7 +620,6 @@ "status.edited": "Poslednje uređivanje {date}", "status.edited_x_times": "Uređeno {count, plural, one {{count} put} other {{count} puta}}", "status.favourite": "Omiljeno", - "status.favourites": "{count, plural, one {# omiljeno} few {# omiljena} other {# omiljenih}}", "status.filter": "Filtriraj ovu objavu", "status.history.created": "{name} napisao/la {date}", "status.history.edited": "{name} uredio/la {date}", @@ -637,7 +636,6 @@ "status.read_more": "Pročitajte više", "status.reblog": "Podrži", "status.reblogged_by": "{name} je podržao/la", - "status.reblogs": "{count, plural, one {# podržavanje} few {# podržavanja} other {# podržavanja}}", "status.reblogs.empty": "Još uvek niko nije podržao ovu objavu. Kada bude podržana, pojaviće se ovde.", "status.redraft": "Izbriši i preoblikuj", "status.remove_bookmark": "Ukloni obeleživač", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 117c25949de..b273bb2ff9d 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -620,7 +620,6 @@ "status.edited": "Последње уређивање {date}", "status.edited_x_times": "Уређено {count, plural, one {{count} пут} other {{count} пута}}", "status.favourite": "Омиљено", - "status.favourites": "{count, plural, one {# омиљено} few {# омиљена} other {# омиљених}}", "status.filter": "Филтрирај ову објаву", "status.history.created": "{name} написао/ла {date}", "status.history.edited": "{name} уредио/ла {date}", @@ -637,7 +636,6 @@ "status.read_more": "Прочитајте више", "status.reblog": "Подржи", "status.reblogged_by": "{name} је подржао/ла", - "status.reblogs": "{count, plural, one {# подржавање} few {# подржавања} other {# подржавања}}", "status.reblogs.empty": "Још увек нико није подржао ову објаву. Када буде подржана, појавиће се овде.", "status.redraft": "Избриши и преобликуј", "status.remove_bookmark": "Уклони обележивач", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index ff601edf16a..bd48262ab3c 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -28,6 +28,7 @@ "account.disable_notifications": "Sluta meddela mig när @{name} skriver ett inlägg", "account.domain_blocking": "Blockerad domän", "account.edit_profile": "Redigera profil", + "account.edit_profile_short": "Redigera", "account.enable_notifications": "Notifiera mig när @{name} gör inlägg", "account.endorse": "Visa på profil", "account.familiar_followers_many": "Följs av {name1},{name2} och {othersCount, plural, one {en till du känner} other {# andra du känner}}", @@ -40,6 +41,11 @@ "account.featured_tags.last_status_never": "Inga inlägg", "account.follow": "Följ", "account.follow_back": "Följ tillbaka", + "account.follow_back_short": "Följ tillbaka", + "account.follow_request": "Begär att följa", + "account.follow_request_cancel": "Avbryt begäran", + "account.follow_request_cancel_short": "Avbryt", + "account.follow_request_short": "Begär", "account.followers": "Följare", "account.followers.empty": "Ingen följer denna användare än.", "account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}", @@ -167,6 +173,8 @@ "column.edit_list": "Redigera lista", "column.favourites": "Favoriter", "column.firehose": "Direktflöden", + "column.firehose_local": "Direktflöde för den här servern", + "column.firehose_singular": "Direktflöde", "column.follow_requests": "Följarförfrågningar", "column.home": "Hem", "column.list_members": "Hantera listmedlemmar", @@ -186,6 +194,7 @@ "community.column_settings.local_only": "Endast lokalt", "community.column_settings.media_only": "Endast media", "community.column_settings.remote_only": "Endast fjärr", + "compose.error.blank_post": "Inlägget kan inte vara tomt.", "compose.language.change": "Ändra språk", "compose.language.search": "Sök språk...", "compose.published.body": "Inlägget publicerat.", @@ -238,7 +247,15 @@ "confirmations.missing_alt_text.secondary": "Posta ändå", "confirmations.missing_alt_text.title": "Lägg till alt-text?", "confirmations.mute.confirm": "Tysta", + "confirmations.private_quote_notify.cancel": "Tillbaka till redigering", + "confirmations.private_quote_notify.confirm": "Publicera inlägg", + "confirmations.private_quote_notify.do_not_show_again": "Visa inte mig detta meddelande igen", + "confirmations.private_quote_notify.message": "Personen du citerar och andra omnämnda kommer att meddelas och kommer att kunna se ditt inlägg, även om de inte följer dig.", + "confirmations.private_quote_notify.title": "Dela med följare och omnämnda användare?", "confirmations.quiet_post_quote_info.dismiss": "Påminn mig inte igen", + "confirmations.quiet_post_quote_info.got_it": "Jag förstår", + "confirmations.quiet_post_quote_info.message": "När du citerar en tyst offentlig post, kommer ditt inlägg att döljas från trendande tidslinjer.", + "confirmations.quiet_post_quote_info.title": "Citerar tysta offentliga inlägg", "confirmations.redraft.confirm": "Radera & gör om", "confirmations.redraft.message": "Är du säker på att du vill radera detta inlägg och göra om det? Favoritmarkeringar, boostar och svar till det ursprungliga inlägget kommer förlora sitt sammanhang.", "confirmations.redraft.title": "Ta bort & gör om inlägget?", @@ -248,7 +265,12 @@ "confirmations.revoke_quote.confirm": "Ta bort inlägg", "confirmations.revoke_quote.message": "Denna åtgärd kan inte ångras.", "confirmations.revoke_quote.title": "Ta bort inlägg?", + "confirmations.unblock.confirm": "Avblockera", + "confirmations.unblock.title": "Avblockera {name}?", "confirmations.unfollow.confirm": "Avfölj", + "confirmations.unfollow.title": "Avfölj {name}?", + "confirmations.withdraw_request.confirm": "Återkalla förfrågan", + "confirmations.withdraw_request.title": "Återkalla begäran att följa {name}?", "content_warning.hide": "Dölj inlägg", "content_warning.show": "Visa ändå", "content_warning.show_more": "Visa mer", @@ -318,10 +340,11 @@ "empty_column.blocks": "Du har ännu ej blockerat några användare.", "empty_column.bookmarked_statuses": "Du har inte bokmärkt några inlägg än. När du bokmärker ett inlägg kommer det synas här.", "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att sätta bollen i rullning!", - "empty_column.direct": "Du har inga privata omnämninande. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", + "empty_column.direct": "Du har inga privata omnämnanden. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", + "empty_column.disabled_feed": "Detta flöde har inaktiverats av dina serveradministratörer.", "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.", "empty_column.explore_statuses": "Ingenting är trendigt just nu. Kom tillbaka senare!", - "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg ännu. När du favoritmärker ett så kommer det att dyka upp här.", + "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg ännu. När du favoritmarkerar ett så kommer det att dyka upp här.", "empty_column.favourites": "Ingen har favoritmarkerat detta inlägg än. När någon gör det kommer de synas här.", "empty_column.follow_requests": "Du har inga följarförfrågningar än. När du får en kommer den visas här.", "empty_column.followed_tags": "Du följer inga hashtaggar ännu. När du gör det kommer de att dyka upp här.", @@ -332,6 +355,7 @@ "empty_column.notification_requests": "Allt klart! Det finns inget mer här. När du får nya meddelanden visas de här enligt dina inställningar.", "empty_column.notifications": "Du har inga meddelanden än. Interagera med andra för att starta konversationen.", "empty_column.public": "Det finns inget här! Skriv något offentligt, eller följ manuellt användarna från andra instanser för att fylla på det", + "error.no_hashtag_feed_access": "Gå med eller logga in för att visa och följa denna hashtag.", "error.unexpected_crash.explanation": "På grund av en bugg i vår kod eller kompatiblitetsproblem i webbläsaren kan den här sidan inte visas korrekt.", "error.unexpected_crash.explanation_addons": "Denna sida kunde inte visas korrekt. Detta beror troligen på ett webbläsartillägg eller ett automatiskt översättningsverktyg.", "error.unexpected_crash.next_steps": "Prova att ladda om sidan. Om det inte hjälper kan du försöka använda Mastodon med en annan webbläsare eller app.", @@ -343,7 +367,9 @@ "explore.trending_links": "Nyheter", "explore.trending_statuses": "Inlägg", "explore.trending_tags": "Hashtaggar", + "featured_carousel.current": "Inlägg {current, number} / {max, number}", "featured_carousel.header": "{count, plural,one {Fäst inlägg} other {Fästa inlägg}}", + "featured_carousel.slide": "Inlägg {current, number} av {max, number}", "filter_modal.added.context_mismatch_explanation": "Denna filterkategori gäller inte för det sammanhang där du har tillgång till det här inlägget. Om du vill att inlägget ska filtreras även i detta sammanhang måste du redigera filtret.", "filter_modal.added.context_mismatch_title": "Misspassning av sammanhang!", "filter_modal.added.expired_explanation": "Denna filterkategori har utgått, du måste ändra utgångsdatum för att den ska kunna tillämpas.", @@ -444,10 +470,12 @@ "ignore_notifications_modal.private_mentions_title": "Vill du ignorera aviseringar från oombedda privata omnämnanden?", "info_button.label": "Hjälp", "info_button.what_is_alt_text": "

Vad är alt-text?

alt-text ger bildbeskrivningar för personer med synnedsättning, anslutningar med låg bandbredd eller de som söker extra sammanhang.

Du kan förbättra tillgängligheten och förståelsen för alla genom att skriva en tydlig, koncis och objektiv alt-text.

  • Fånga viktiga element
  • Sammanfatta text i bilder
  • Använd vanlig meningsstruktur
  • Undvik överflödig information
  • Fokus på trender och viktiga resultat i komplexa bilder (som diagram eller kartor)
", + "interaction_modal.action": "För att interagera med {name}s inlägg måste du logga in på ditt konto på vilken Mastodon-server du än använder.", "interaction_modal.go": "Vidare", "interaction_modal.no_account_yet": "Har du inget konto än?", "interaction_modal.on_another_server": "På en annan server", "interaction_modal.on_this_server": "På denna server", + "interaction_modal.title": "Logga in för att fortsätta", "interaction_modal.username_prompt": "T.ex. {example}", "intervals.full.days": "{number, plural, one {# dag} other {# dagar}}", "intervals.full.hours": "{number, plural, one {# timme} other {# timmar}}", @@ -468,6 +496,7 @@ "keyboard_shortcuts.home": "Öppna Hemtidslinjen", "keyboard_shortcuts.hotkey": "Kommando", "keyboard_shortcuts.legend": "Visa denna översikt", + "keyboard_shortcuts.load_more": "Fokusera \"Ladda mer\"-knappen", "keyboard_shortcuts.local": "Öppna lokal tidslinje", "keyboard_shortcuts.mention": "Nämna skaparen", "keyboard_shortcuts.muted": "Öppna listan över tystade användare", @@ -476,6 +505,7 @@ "keyboard_shortcuts.open_media": "Öppna media", "keyboard_shortcuts.pinned": "Öppna listan över fästa inlägg", "keyboard_shortcuts.profile": "Öppna författarens profil", + "keyboard_shortcuts.quote": "Citera inlägg", "keyboard_shortcuts.reply": "Svara på inlägg", "keyboard_shortcuts.requests": "Öppna följförfrågningar", "keyboard_shortcuts.search": "Fokusera sökfältet", @@ -604,6 +634,7 @@ "notification.moderation_warning.action_suspend": "Ditt konto har stängts av.", "notification.own_poll": "Din röstning har avslutats", "notification.poll": "En enkät som du röstat i har avslutats", + "notification.quoted_update": "{name} redigerade ett inlägg du har citerat", "notification.reblog": "{name} boostade ditt inlägg", "notification.reblog.name_and_others_with_link": "{name} och {count, plural, one {# annan} other {# andra}} har förhöjt ditt inlägg", "notification.relationships_severance_event": "Förlorade kontakter med {name}", @@ -723,10 +754,20 @@ "privacy.private.short": "Följare", "privacy.public.long": "Alla på och utanför Mastodon", "privacy.public.short": "Offentlig", + "privacy.quote.anyone": "{visibility}, vem som helst kan citera", + "privacy.quote.disabled": "{visibility}, citat inaktiverade", + "privacy.quote.limited": "{visibility}, citat begränsade", "privacy.unlisted.additional": "Detta fungerar precis som offentlig, förutom att inlägget inte visas i liveflöden eller hashtaggar, utforska eller Mastodon-sökning, även om du har valt detta för hela kontot.", + "privacy.unlisted.long": "Dold från Mastodon-sökresultat, trendar och offentliga tidslinjer", "privacy.unlisted.short": "Offentlig (begränsad)", "privacy_policy.last_updated": "Senast uppdaterad {date}", "privacy_policy.title": "Integritetspolicy", + "quote_error.edit": "Citat kan inte läggas till när du redigerar ett inlägg.", + "quote_error.poll": "Citat är inte tillåtet med omröstningar.", + "quote_error.private_mentions": "Citering är inte tillåtet med direkta omnämnanden.", + "quote_error.quote": "Endast ett citat åt gången är tillåtet.", + "quote_error.unauthorized": "Du har inte behörighet att citera detta inlägg.", + "quote_error.upload": "Citering är inte tillåtet med mediebilagor.", "recommended": "Rekommenderas", "refresh": "Läs om", "regeneration_indicator.please_stand_by": "Vänligen vänta.", @@ -742,6 +783,9 @@ "relative_time.minutes": "{number}min", "relative_time.seconds": "{number}sek", "relative_time.today": "idag", + "remove_quote_hint.button_label": "Jag förstår", + "remove_quote_hint.message": "Du kan göra det från {icon} alternativmenyn.", + "remove_quote_hint.title": "Vill du ta bort ditt citerade inlägg?", "reply_indicator.attachments": "{count, plural, one {# bilaga} other {# bilagor}}", "reply_indicator.cancel": "Ångra", "reply_indicator.poll": "Omröstning", @@ -833,10 +877,19 @@ "status.admin_account": "Öppet modereringsgränssnitt för @{name}", "status.admin_domain": "Öppet modereringsgränssnitt för @{domain}", "status.admin_status": "Öppna detta inlägg i modereringsgränssnittet", + "status.all_disabled": "Booster och citat är inaktiverade", "status.block": "Blockera @{name}", "status.bookmark": "Bokmärk", "status.cancel_reblog_private": "Sluta boosta", + "status.cannot_quote": "Du har inte tillåtelse att citera detta inlägg", "status.cannot_reblog": "Detta inlägg kan inte boostas", + "status.contains_quote": "Innehåller citat", + "status.context.loading": "Laddar fler svar", + "status.context.loading_error": "Kunde inte ladda nya svar", + "status.context.loading_success": "Nya svar laddade", + "status.context.more_replies_found": "Fler svar hittades", + "status.context.retry": "Försök igen", + "status.context.show": "Visa", "status.continued_thread": "Fortsatt tråd", "status.copy": "Kopiera inläggslänk", "status.delete": "Radera", @@ -849,7 +902,6 @@ "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}", "status.embed": "Hämta kod för inbäddning", "status.favourite": "Favoritmarkera", - "status.favourites": "{count, plural, one {favorit} other {favoriter}}", "status.filter": "Filtrera detta inlägg", "status.history.created": "{name} skapade {date}", "status.history.edited": "{name} redigerade {date}", @@ -863,20 +915,34 @@ "status.mute_conversation": "Tysta konversation", "status.open": "Utvidga detta inlägg", "status.pin": "Fäst i profil", + "status.quote": "Citera ", "status.quote.cancel": "Återkalla citering", + "status.quote_error.blocked_account_hint.title": "Detta inlägg är dolt eftersom du har blockerat @{name}.", + "status.quote_error.blocked_domain_hint.title": "Detta inlägg är dolt eftersom du har blockerat {domain}.", "status.quote_error.filtered": "Dolt på grund av ett av dina filter", + "status.quote_error.limited_account_hint.action": "Visa ändå", + "status.quote_error.limited_account_hint.title": "Detta konto har dolts av {domain}s moderatorer.", + "status.quote_error.muted_account_hint.title": "Detta inlägg är dolt eftersom du har tystat @{name}.", "status.quote_error.not_available": "Inlägg ej tillgängligt", "status.quote_error.pending_approval": "Väntande inlägg", + "status.quote_error.pending_approval_popout.body": "På Mastodon kan du styra om någon kan citera dig. Det här inlägget väntar medan vi får den ursprungliga författarens godkännande.", + "status.quote_error.revoked": "Inlägg borttaget av författaren", + "status.quote_followers_only": "Detta inlägg kan bara citeras av följare", + "status.quote_manual_review": "Författaren kommer att granska manuellt", "status.quote_policy_change": "Ändra vem som kan citera", "status.quote_post_author": "Citerade ett inlägg av @{name}", + "status.quote_private": "Privata inlägg kan inte citeras", + "status.quotes.empty": "Ingen har citerat detta inlägg än. När någon gör det kommer det att synas här.", + "status.quotes.local_other_disclaimer": "Citat som avvisats av författaren kommer inte att visas.", "status.read_more": "Läs mer", "status.reblog": "Boosta", + "status.reblog_private": "Dela igen med dina följare", "status.reblogged_by": "{name} boostade", - "status.reblogs": "{count, plural, one {# röst} other {# röster}}", "status.reblogs.empty": "Ingen har boostat detta inlägg än. När någon gör det kommer de synas här.", "status.redraft": "Radera & gör om", "status.remove_bookmark": "Ta bort bokmärke", "status.remove_favourite": "Ta bort från Favoriter", + "status.remove_quote": "Ta bort", "status.replied_in_thread": "Svarade i tråden", "status.replied_to": "Svarade på {name}", "status.reply": "Svara", @@ -920,6 +986,7 @@ "upload_button.label": "Lägg till media", "upload_error.limit": "Filöverföringsgränsen överskriden.", "upload_error.poll": "Filuppladdning tillåts inte med omröstningar.", + "upload_error.quote": "Filuppladdning tillåts inte med citat.", "upload_form.drag_and_drop.instructions": "För att plocka upp en mediebilaga, tryck på mellanslag eller enter. Använd piltangenterna för att flytta mediebilagan. Tryck på mellanslag eller enter igen för att släppa mediebilagan i sin nya position, eller tryck på escape för att avbryta.", "upload_form.drag_and_drop.on_drag_cancel": "Flytten avbröts. Mediebilagan {item} släpptes.", "upload_form.drag_and_drop.on_drag_end": "Mediebilagan {item} släpptes.", @@ -944,9 +1011,19 @@ "video.volume_down": "Volym ned", "video.volume_up": "Volym upp", "visibility_modal.button_title": "Ange synlighet", + "visibility_modal.direct_quote_warning.text": "Om du sparar de nuvarande inställningarna kommer det inbäddade citatet bli konverterat till en länk.", + "visibility_modal.direct_quote_warning.title": "Citat kan inte bäddas in i privata omnämnanden", "visibility_modal.header": "Synlighet och interaktion", + "visibility_modal.helper.direct_quoting": "Privata omnämnanden som författats på Mastodon kan inte citeras av andra.", + "visibility_modal.helper.privacy_editing": "Synligheten kan inte ändras efter att ett inlägg är publicerat.", + "visibility_modal.helper.privacy_private_self_quote": "Självcitat av privata inlägg kan inte göras publika.", + "visibility_modal.helper.private_quoting": "Inlägg som endast är för följare och som författats på Mastodon kan inte citeras av andra.", "visibility_modal.helper.unlisted_quoting": "När folk citerar dig, deras inlägg kommer också att döljas från trendiga tidslinjer.", + "visibility_modal.instructions": "Kontrollera vem som kan interagera med detta inlägg. Du kan också använda inställningar för alla framtida inlägg genom att navigera till Preferences > Posting defaults.", + "visibility_modal.privacy_label": "Synlighet", "visibility_modal.quote_followers": "Endast följare", + "visibility_modal.quote_label": "Vem kan citera", + "visibility_modal.quote_nobody": "Bara jag", "visibility_modal.quote_public": "Alla", "visibility_modal.save": "Spara" } diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 4cba56bd9f0..22d0c22795a 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -836,7 +836,6 @@ "status.edited_x_times": "แก้ไข {count, plural, other {{count} ครั้ง}}", "status.embed": "รับโค้ดฝังตัว", "status.favourite": "ชื่นชอบ", - "status.favourites": "{count, plural, other {รายการโปรด}}", "status.filter": "กรองโพสต์นี้", "status.history.created": "{name} ได้สร้างเมื่อ {date}", "status.history.edited": "{name} ได้แก้ไขเมื่อ {date}", @@ -853,7 +852,6 @@ "status.read_more": "อ่านเพิ่มเติม", "status.reblog": "ดัน", "status.reblogged_by": "{name} ได้ดัน", - "status.reblogs": "{count, plural, other {การดัน}}", "status.reblogs.empty": "ยังไม่มีใครดันโพสต์นี้ เมื่อใครสักคนดัน เขาจะปรากฏที่นี่", "status.redraft": "ลบแล้วร่างใหม่", "status.remove_bookmark": "เอาที่คั่นหน้าออก", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 221c2158876..2f560424624 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -28,6 +28,7 @@ "account.disable_notifications": "@{name} li toki la o mu ala e mi", "account.domain_blocking": "mi len e ma ni", "account.edit_profile": "o ante e lipu mi", + "account.edit_profile_short": "o ante", "account.enable_notifications": "@{name} li toki la o toki e toki ona tawa mi", "account.endorse": "lipu jan la o suli e ni", "account.familiar_followers_many": "{name1} en {name2} en {othersCount, plural, other {jan ante #}} li kute e jan ni", @@ -40,6 +41,10 @@ "account.featured_tags.last_status_never": "toki ala li lon", "account.follow": "o kute", "account.follow_back": "jan ni li kute e sina. o kute", + "account.follow_back_short": "jan ni li kute e sina. o kute", + "account.follow_request": "toki e wile kute", + "account.follow_request_cancel": "toki ala e wile kute", + "account.follow_request_cancel_short": "ala", "account.followers": "jan kute", "account.followers.empty": "jan ala li kute e jan ni", "account.followers_counter": "{count, plural, other {jan {counter} li kute e ona}}", @@ -151,6 +156,8 @@ "bundle_modal_error.close": "o pini", "bundle_modal_error.message": "ilo li wile kama e ijo ni, taso pakala li lon.", "bundle_modal_error.retry": "o alasa sin", + "carousel.current": "lipu {current, number} / {max, number}", + "carousel.slide": "lipu {current, number} lon {max, number}", "closed_registrations.other_server_instructions": "kulupu Mastodon la lawa mute li lon. sina ken pali e sijelo lon ma ante la sina awen ken lukin e ijo pi ma ni.", "closed_registrations_modal.description": "tenpo ni la, sina ken ala pali e jan lon ma {domain}. taso sina wile kepeken ilo Mastodon la, sina ken pali e jan lon ma ante lon ala ma {domain}.", "closed_registrations_modal.find_another_server": "o alasa e ma ante", @@ -167,6 +174,8 @@ "column.edit_list": "o ante e kulupu", "column.favourites": "ijo pona", "column.firehose": "toki pi tenpo ni", + "column.firehose_local": "toki pi tenpo ni pi ma ni", + "column.firehose_singular": "toki pi tenpo ni", "column.follow_requests": "wile alasa pi jan ante", "column.home": "lipu open", "column.list_members": "o ante e kulupu jan", @@ -186,6 +195,7 @@ "community.column_settings.local_only": "toki tan ni taso", "community.column_settings.media_only": "sitelen taso", "community.column_settings.remote_only": "toki tan ante taso", + "compose.error.blank_post": "toki ken ala jo e ala.", "compose.language.change": "o ante e nasin toki", "compose.language.search": "o alasa e nasin toki...", "compose.published.body": "toki li pana.", @@ -237,6 +247,9 @@ "confirmations.missing_alt_text.secondary": "o pana a", "confirmations.missing_alt_text.title": "o pana ala pana e toki pi sona lukin?", "confirmations.mute.confirm": "o len", + "confirmations.private_quote_notify.confirm": "o pana e toki ni tawa ale", + "confirmations.private_quote_notify.do_not_show_again": "o toki ala e toki ni", + "confirmations.quiet_post_quote_info.dismiss": "o toki ala e ni tawa mi", "confirmations.quiet_post_quote_info.got_it": "sona", "confirmations.redraft.confirm": "o weka o pali sin e toki", "confirmations.redraft.message": "pali sin e toki ni la sina wile ala wile weka e ona? sina ni la suli pi toki ni en wawa pi toki ni li weka. kin la toki lon toki ni li jo e mama ala.", @@ -246,7 +259,10 @@ "confirmations.remove_from_followers.title": "o kama ala kama kute ala e jan?", "confirmations.revoke_quote.confirm": "o weka e toki tan lipu Mastodon", "confirmations.revoke_quote.title": "sina wile weka ala weka e toki?", + "confirmations.unblock.confirm": "o len ala", + "confirmations.unblock.title": "o len ala e {name}?", "confirmations.unfollow.confirm": "o kute ala", + "confirmations.unfollow.title": "o kute ala e {name}?", "content_warning.hide": "o len", "content_warning.show": "o lukin a", "content_warning.show_more": "o lukin", @@ -287,10 +303,12 @@ "domain_pill.your_handle": "nimi sina:", "domain_pill.your_server": "ni li ma sina lon ilo. toki ale sina li lon ma ni. ma li ike tawa sina la, sina ken tawa ma ante. ni la jan kute sina li tawa sama.", "domain_pill.your_username": "ni li nimi sina. ma sina la, sina taso li jo e ona. jan mute li lon ma ante la, ona li ken jo e nimi sama.", + "dropdown.empty": "o wile e ijo wan", "embed.instructions": "o pana e toki ni la, toki li lon lipu ante. ", "embed.preview": "ni li jo e sitelen ni:", "emoji_button.activity": "musi", "emoji_button.clear": "o weka", + "emoji_button.custom": "pali sin", "emoji_button.flags": "len ma", "emoji_button.food": "moku", "emoji_button.label": "o pana e sitelen pilin", @@ -326,7 +344,10 @@ "explore.trending_links": "sin", "explore.trending_statuses": "toki", "explore.trending_tags": "kulupu pi lipu suli", + "featured_carousel.current": "toki{current, number} / {max, number}", "featured_carousel.header": "{count, plural, other {toki sewi}}", + "featured_carousel.slide": "toki {current, number} lon {max, number}", + "filter_modal.added.review_and_configure_title": "o alasa e lawa", "filter_modal.added.settings_link": "lipu lawa", "filter_modal.select_filter.expired": "tenpo pini", "filter_modal.select_filter.search": "o alasa anu pali", @@ -367,8 +388,10 @@ "hashtag.counter_by_accounts": "{count, plural, other {jan {counter}}}", "hashtag.counter_by_uses": "{count, plural, other {toki {counter}}}", "hashtag.counter_by_uses_today": "{count, plural, other {toki poka {counter}}}", + "hashtag.feature": "lipu jan la o suli e ni", "hashtag.follow": "o kute e kulupu lipu", "hashtag.mute": "o kute ala e kulupu #{hashtag}", + "hashtag.unfeature": "lipu jan la o suli ala e ni", "hashtag.unfollow": "o kute ala e kulupu lipu", "hints.profiles.followers_may_be_missing": "jan kute li ken weka.", "hints.profiles.see_more_followers": "o lukin e jan ni lon ma {domain}: ona li kute e jan ni.", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 05a3d4c92cd..b7b4a08cdb6 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -55,7 +55,7 @@ "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.", "account.follows_you": "Seni takip ediyor", "account.go_to_profile": "Profile git", - "account.hide_reblogs": "@{name} kişisinin boostlarını gizle", + "account.hide_reblogs": "@{name} kişisinin yeniden paylaşımlarını gizle", "account.in_memoriam": "Hatırasına.", "account.joined_short": "Katıldı", "account.languages": "Abone olunan dilleri değiştir", @@ -903,7 +903,7 @@ "status.edited_x_times": "{count, plural, one {{count} kez} other {{count} kez}} düzenlendi", "status.embed": "Gömme kodunu al", "status.favourite": "Favori", - "status.favourites": "{count, plural, one {beğeni} other {beğeni}}", + "status.favourites_count": "{count, plural, one {{counter} favori} other {{counter} favori}}", "status.filter": "Bu gönderiyi süzgeçle", "status.history.created": "{name} oluşturdu {date}", "status.history.edited": "{name} düzenledi {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "Kimin alıntı yapabileceğini değiştirin", "status.quote_post_author": "@{name} adlı kullanıcının bir gönderisini alıntıladı", "status.quote_private": "Özel gönderiler alıntılanamaz", - "status.quotes": "{count, plural, one {# alıntı} other {# alıntı}}", "status.quotes.empty": "Henüz hiç kimse bu gönderiyi alıntılamadı. Herhangi bir kullanıcı alıntıladığında burada görüntülenecek.", "status.quotes.local_other_disclaimer": "Yazar tarafından reddedilen alıntılar gösterilmez.", "status.quotes.remote_other_disclaimer": "Yalnızca {domain} adresinden gelen alıntılar burada gösterilir. Yazar tarafından reddedilen alıntılar gösterilmez.", + "status.quotes_count": "{count, plural, one {{counter} alıntı} other {{counter} alıntı}}", "status.read_more": "Devamını okuyun", "status.reblog": "Yeniden paylaş", "status.reblog_or_quote": "Yükselt veya alıntıla", "status.reblog_private": "Takipçilerinizle tekrar paylaşın", "status.reblogged_by": "{name} yeniden paylaştı", - "status.reblogs": "{count, plural, one {yeniden paylaşım} other {yeniden paylaşım}}", "status.reblogs.empty": "Henüz hiç kimse bu gönderiyi yeniden paylaşmadı. Herhangi bir kullanıcı yeniden paylaştığında burada görüntülenecek.", + "status.reblogs_count": "{count, plural, one {{counter} yeniden paylaşım} other {{counter} yeniden paylaşım}}", "status.redraft": "Sil,Düzenle ve yeniden-paylaş", "status.remove_bookmark": "Yer işaretini kaldır", "status.remove_favourite": "Favorilerden kaldır", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 39260765c28..dc48e9dafd0 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -26,8 +26,12 @@ "account.direct": "Особиста згадка @{name}", "account.disable_notifications": "Не повідомляти мене про дописи @{name}", "account.edit_profile": "Редагувати профіль", + "account.edit_profile_short": "Редагувати", "account.enable_notifications": "Повідомляти мене про дописи @{name}", "account.endorse": "Рекомендувати у моєму профілі", + "account.familiar_followers_many": "Має серед підисників {name1}, {name2} та ще {othersCount, plural, one {# відомого вам користувача} few {# відомих вам користувачів} many {# відомих вам користувачів} other {# відомих вам користувачів}}", + "account.familiar_followers_one": "Має серед підписників {name1}", + "account.familiar_followers_two": "Має серед підписників {name1} та {name2}", "account.featured": "Рекомендоване", "account.featured.accounts": "Профілі", "account.featured.hashtags": "Хештеги", @@ -35,10 +39,16 @@ "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, one {{counter} підписник} few {{counter} підписники} many {{counter} підписників} other {{counter} підписник}}", - "account.following": "Ви стежите", + "account.followers_you_know_counter": "{count, plural, one {{counter} відомий вам} few {{counter} відомі вам} many {{counter} відомих вам} other {{counter} відомі вам}}", + "account.following": "Підписки", "account.following_counter": "{count, plural, one {{counter} підписка} few {{counter} підписки} many {{counter} підписок} other {{counter} підписка}}", "account.follows.empty": "Цей користувач ще ні на кого не підписався.", "account.follows_you": "Підписаний(-а) на вас", @@ -56,12 +66,15 @@ "account.mute_notifications_short": "Не сповіщати", "account.mute_short": "Ігнорувати", "account.muted": "Приховується", + "account.mutual": "Підписані навзаєм", "account.no_bio": "Немає опису.", "account.open_original_page": "Відкрити оригінальну сторінку", "account.posts": "Дописи", "account.posts_with_replies": "Дописи й відповіді", + "account.remove_from_followers": "Вилучити {name} із підписників", "account.report": "Поскаржитися на @{name}", "account.requested_follow": "{name} надсилає запит на стеження", + "account.requests_to_follow_you": "Просить дозвіл на вас підписатись", "account.share": "Поділитися профілем @{name}", "account.show_reblogs": "Показати поширення від @{name}", "account.statuses_counter": "{count, plural, one {{counter} допис} few {{counter} дописи} many {{counter} дописів} other {{counter} допис}}", @@ -176,6 +189,7 @@ "community.column_settings.local_only": "Лише локальні", "community.column_settings.media_only": "Лише з медіа", "community.column_settings.remote_only": "Лише віддалені", + "compose.error.blank_post": "Допис не може бути порожнім.", "compose.language.change": "Змінити мову", "compose.language.search": "Шукати мови...", "compose.published.body": "Допис опубліковано.", @@ -223,13 +237,30 @@ "confirmations.missing_alt_text.secondary": "Все одно опублікувати", "confirmations.missing_alt_text.title": "Додати альтернативний текст?", "confirmations.mute.confirm": "Приховати", + "confirmations.private_quote_notify.cancel": "Назад до редагування", + "confirmations.private_quote_notify.confirm": "Оприлюднити допис", + "confirmations.private_quote_notify.do_not_show_again": "Більше не показувати цього повідомлення", + "confirmations.private_quote_notify.message": "Ті, кого ви цитуєте чи згадуєте, отримають сповіщення й зможуть переглянути ваш допис, навіть якщо не підписані на вас.", + "confirmations.private_quote_notify.title": "Поширити підписникам і згаданим користувачам?", + "confirmations.quiet_post_quote_info.dismiss": "Більше не нагадувати", + "confirmations.quiet_post_quote_info.got_it": "Зрозуміло", + "confirmations.quiet_post_quote_info.message": "Цитати тихого публічного допису не буде показано в стрічках трендів.", + "confirmations.quiet_post_quote_info.title": "Цитування тихих публічних дописів", "confirmations.redraft.confirm": "Видалити та виправити", "confirmations.redraft.message": "Ви впевнені, що хочете видалити цей допис та переписати його? Додавання у вибране та поширення буде втрачено, а відповіді на оригінальний допис залишаться без першоджерела.", "confirmations.redraft.title": "Видалити та переробити допис?", + "confirmations.remove_from_followers.confirm": "Вилучити підписника", + "confirmations.remove_from_followers.message": "Точно відписати {name} від вас?", + "confirmations.remove_from_followers.title": "Вилучити підписника?", "confirmations.revoke_quote.confirm": "Видалити публікацію", "confirmations.revoke_quote.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": "Показати більше", @@ -364,6 +395,7 @@ "follow_suggestions.who_to_follow": "На кого підписатися", "followed_tags": "Відстежувані хештеґи", "footer.about": "Про проєкт", + "footer.about_this_server": "Про сервер", "footer.directory": "Каталог профілів", "footer.get_app": "Завантажити застосунок", "footer.keyboard_shortcuts": "Комбінації клавіш", @@ -396,6 +428,7 @@ "hints.profiles.see_more_followers": "Переглянути більше підписників на {domain}", "hints.profiles.see_more_follows": "Переглянути більше підписок на {domain}", "hints.profiles.see_more_posts": "Переглянути більше дописів на {domain}", + "home.column_settings.show_quotes": "Показувати цитати", "home.column_settings.show_reblogs": "Показувати поширення", "home.column_settings.show_replies": "Показувати відповіді", "home.hide_announcements": "Приховати оголошення", @@ -442,6 +475,7 @@ "keyboard_shortcuts.home": "Відкрити домашню стрічку", "keyboard_shortcuts.hotkey": "Гаряча клавіша", "keyboard_shortcuts.legend": "Показати легенду", + "keyboard_shortcuts.load_more": "Фокусуватися на кнопці «Завантажити більше»", "keyboard_shortcuts.local": "Відкрити локальну стрічку", "keyboard_shortcuts.mention": "Згадати автора", "keyboard_shortcuts.muted": "Відкрити список прихованих користувачів", @@ -450,7 +484,7 @@ "keyboard_shortcuts.open_media": "Відкрити медіа", "keyboard_shortcuts.pinned": "Відкрити список закріплених дописів", "keyboard_shortcuts.profile": "Відкрити профіль автора", - "keyboard_shortcuts.quote": "Цитувати пост", + "keyboard_shortcuts.quote": "Цитувати допис", "keyboard_shortcuts.reply": "Відповісти", "keyboard_shortcuts.requests": "Відкрити список охочих підписатися", "keyboard_shortcuts.search": "Сфокусуватися на пошуку", @@ -564,6 +598,7 @@ "notification.label.mention": "Згадка", "notification.label.private_mention": "Особиста згадка", "notification.label.private_reply": "Приватна відповідь", + "notification.label.quote": "{name} цитує ваш допис", "notification.label.reply": "Відповідь", "notification.mention": "Згадка", "notification.mentioned_you": "{name} згадує вас", @@ -578,6 +613,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 {# інший} few {# інших} many {# інших} other {# інший}} поширили ваш допис", "notification.relationships_severance_event": "Втрачено з'єднання з {name}", @@ -621,6 +657,7 @@ "notifications.column_settings.mention": "Згадки:", "notifications.column_settings.poll": "Результати опитування:", "notifications.column_settings.push": "Push-сповіщення", + "notifications.column_settings.quote": "Цитати:", "notifications.column_settings.reblog": "Поширення:", "notifications.column_settings.show": "Показати в стовпчику", "notifications.column_settings.sound": "Відтворювати звуки", @@ -696,10 +733,20 @@ "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.long": "Сховано з результатів пошуку Mastodon, трендових і публічних стрічок", "privacy.unlisted.short": "Без додавання до стрічки", "privacy_policy.last_updated": "Оновлено {date}", "privacy_policy.title": "Політика приватності", + "quote_error.edit": "Цитат не можна додавати під час редагування допису.", + "quote_error.poll": "Одночасно цитувати й опитувати не можна.", + "quote_error.private_mentions": "Одночасно цитувати й особисто когось згадувати не можна.", + "quote_error.quote": "Дозволено лише одну цитату.", + "quote_error.unauthorized": "Вам не дозволено цитувати цей допис.", + "quote_error.upload": "Одночасно цитувати й додавати мультимедіа не можна.", "recommended": "Рекомендовано", "refresh": "Оновити", "regeneration_indicator.please_stand_by": "Будь ласка, очікуйте.", @@ -715,6 +762,9 @@ "relative_time.minutes": "{number}х", "relative_time.seconds": "{number}с", "relative_time.today": "сьогодні", + "remove_quote_hint.button_label": "Зрозуміло", + "remove_quote_hint.message": "Це можна зробити за допомогою {icon} меню параметрів.", + "remove_quote_hint.title": "Хочете вилучити допис із цитати?", "reply_indicator.attachments": "{count, plural, one {# вкладення} few {# вкладення} many {# вкладень} other {# вкладення}}", "reply_indicator.cancel": "Скасувати", "reply_indicator.poll": "Опитування", @@ -792,7 +842,7 @@ "search_results.no_search_yet": "Спробуйте пошукати дописи, профілі або хештеґи.", "search_results.see_all": "Показати все", "search_results.statuses": "Дописів", - "search_results.title": "Шукати \"{q}\"", + "search_results.title": "Пошук «{q}»", "server_banner.about_active_users": "Люди, які використовують цей сервер протягом останніх 30 днів (Щомісячні Активні Користувачі)", "server_banner.active_users": "активні користувачі", "server_banner.administered_by": "Адміністратор:", @@ -806,13 +856,23 @@ "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.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": "Видалити", + "status.delete.success": "Допис видалено", "status.detailed_status": "Детальний вигляд бесіди", "status.direct": "Особиста згадка @{name}", "status.direct_indicator": "Особиста згадка", @@ -821,7 +881,7 @@ "status.edited_x_times": "Відредаговано {count, plural, one {{count} раз} few {{count} рази} many {{counter} разів} other {{counter} разів}}", "status.embed": "Отримати код вставки", "status.favourite": "Уподобане", - "status.favourites": "{count, plural, one {вподобання} few {вподобання} many {вподобань} other {вподобання}}", + "status.favourites_count": "{count, plural, one {{counter} вподобання} few {{counter} вподобання} many {{counter} вподобань} other {{counter} вподобання}}", "status.filter": "Фільтрувати цей допис", "status.history.created": "{name} створює {date}", "status.history.edited": "{name} змінює {date}", @@ -835,27 +895,50 @@ "status.mute_conversation": "Ігнорувати розмову", "status.open": "Розгорнути допис", "status.pin": "Закріпити у профілі", + "status.quote": "Цитувати", + "status.quote.cancel": "Скасувати цитування", + "status.quote_error.blocked_account_hint.title": "Допис сховано, бо ви блокуєте @{name}.", + "status.quote_error.blocked_domain_hint.title": "Допис сховано, бо ви блокуєте {domain}.", "status.quote_error.filtered": "Приховано через один з ваших фільтрів", - "status.quote_error.not_available": "Пост недоступний", + "status.quote_error.limited_account_hint.action": "Усе одно показати", + "status.quote_error.limited_account_hint.title": "Обліковий запис сховали модератори {domain}.", + "status.quote_error.muted_account_hint.title": "Допис сховано, бо ви приховуєте @{name}.", + "status.quote_error.not_available": "Допис недоступний", + "status.quote_error.pending_approval": "Цитати ще не схвалено", + "status.quote_error.pending_approval_popout.body": "Mastodon дає вам змогу контролювати, чи можна вас цитувати. Допис буде показано, коли автор схвалить його цитування.", + "status.quote_error.revoked": "Автор допису не дозволив його цитувати", + "status.quote_followers_only": "Цитувати можна лише підписникам", + "status.quote_manual_review": "Автор матиме схвалити вручну", + "status.quote_noun": "Цитата", + "status.quote_policy_change": "Змінити, кому можна цитувати", "status.quote_post_author": "Цитований допис @{name}", + "status.quote_private": "Приватних повідомлень цитувати не можна", + "status.quotes.empty": "Допису ще не цитували. Цитати буде показано тут.", + "status.quotes.local_other_disclaimer": "Відхилених автором цитат показано не буде.", + "status.quotes.remote_other_disclaimer": "Цитати за межами {domain} показуються не завжди. Відхилених автором цитат показано не буде.", + "status.quotes_count": "{count, plural, one {{counter} цитата} few {{counter} цитати} many {{counter} цитат} other {{counter} цитати}}", "status.read_more": "Дізнатися більше", "status.reblog": "Поширити", + "status.reblog_or_quote": "Поширити чи цитувати", + "status.reblog_private": "Поширити підписникам ще раз", "status.reblogged_by": "{name} поширює", - "status.reblogs": "{count, plural, one {поширення} few {поширення} many {поширень} other {поширення}}", "status.reblogs.empty": "Ніхто ще не поширив цей допис. Коли хтось це зроблять, вони будуть зображені тут.", + "status.reblogs_count": "{count, plural, one {{counter} поширення} few {{counter} поширення} many {{counter} поширень} other {{counter} поширення}}", "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": "Поділитися", - "status.show_less_all": "Згорнути для всіх", - "status.show_more_all": "Розгорнути для всіх", + "status.show_less_all": "Згорнути всі", + "status.show_more_all": "Розгорнути всі", "status.show_original": "Показати оригінал", "status.title.with_attachments": "{user} розміщує {{attachmentCount, plural, one {вкладення} few {{attachmentCount} вкладення} many {{attachmentCount} вкладень} other {{attachmentCount} вкладень}}", "status.translate": "Перекласти", @@ -889,6 +972,7 @@ "upload_button.label": "Додати зображення, відео або аудіо", "upload_error.limit": "Ви перевищили ліміт завантаження файлів.", "upload_error.poll": "Не можна завантажувати файли до опитувань.", + "upload_error.quote": "Додавати файли й цитувати одночасно не можна.", "upload_form.drag_and_drop.instructions": "Щоб вибрати медіавкладення, натисніть пробіл або Enter. Під час перетягування, використайте клавіші зі стрілками для переміщення вкладення в будь-якому напрямку. Натисніть пробіл або Enter знову, щоб залишити медіавкладення в новому положенні, або натисніть клавішу Escape, щоб скасувати.", "upload_form.drag_and_drop.on_drag_cancel": "Перетягування скасовано. Медіавкладення {item} прибрано.", "upload_form.drag_and_drop.on_drag_end": "Медіавкладення {item} прибрано.", @@ -904,10 +988,28 @@ "video.expand": "Розгорнути відео", "video.fullscreen": "На весь екран", "video.hide": "Приховати відео", + "video.mute": "Вимкнути звук", "video.pause": "Призупинити", "video.play": "Програвати", + "video.skip_backward": "Перемотати назад", + "video.skip_forward": "Перемотати вперед", + "video.unmute": "Увімкнути звук", + "video.volume_down": "Тихіше", + "video.volume_up": "Гучніше", + "visibility_modal.button_title": "Налаштувати видимість", + "visibility_modal.direct_quote_warning.text": "При збереженні поточних налаштувань цитату буде замінено на посилання.", + "visibility_modal.direct_quote_warning.title": "Цитат не можна вкладати в особисті згадки", + "visibility_modal.header": "Видимість і взаємодія", + "visibility_modal.helper.direct_quoting": "Mastodon не дозволяє цитувати особисті згадки.", "visibility_modal.helper.privacy_editing": "Видимість не може бути змінена після публікації повідомлення.", + "visibility_modal.helper.privacy_private_self_quote": "Цитати власних приватних повідомлень не можна робити публічними.", + "visibility_modal.helper.private_quoting": "Mastodon не дозволяє цитувати дописи, адресовані лише підписникам.", + "visibility_modal.helper.unlisted_quoting": "Цитати вашого допису також буде сховано зі стрічок трендів.", + "visibility_modal.instructions": "Налаштуйте, кому можна взаємодіяти з цим дописом. Для майбутніх дописів це можна налаштувати, відкривши Налаштування > Усталені налаштування дописів.", + "visibility_modal.privacy_label": "Видимість", "visibility_modal.quote_followers": "Тільки для підписників", + "visibility_modal.quote_label": "Кому можна цитувати", + "visibility_modal.quote_nobody": "Лише мені", "visibility_modal.quote_public": "Будь-хто", "visibility_modal.save": "Зберегти" } diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index a8c687fbd20..d6437549fe9 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -332,8 +332,8 @@ "emoji_button.search_results": "Kết quả tìm kiếm", "emoji_button.symbols": "Biểu tượng", "emoji_button.travel": "Du lịch", - "empty_column.account_featured.me": "Bạn chưa nêu bật gì. Bạn có biết rằng, bạn có thể giới thiệu hashtag thường dùng, lẫn hồ sơ của bạn bè trên trang cá nhân của mình không?", - "empty_column.account_featured.other": "{acct} chưa nêu bật gì. Bạn có biết rằng, bạn có thể giới thiệu hashtag thường dùng, lẫn hồ sơ của bạn bè trên trang cá nhân của mình không?", + "empty_column.account_featured.me": "Bạn chưa nêu bật gì. Bạn có biết rằng, bạn có thể giới thiệu hashtag thường dùng và hồ sơ của bạn bè trên trang cá nhân của mình không?", + "empty_column.account_featured.other": "{acct} chưa nêu bật gì. Bạn có biết rằng, bạn có thể giới thiệu hashtag thường dùng và hồ sơ của bạn bè trên trang cá nhân của mình không?", "empty_column.account_featured_other.unknown": "Người này chưa nêu bật nội dung gì.", "empty_column.account_hides_collections": "Người này đã chọn ẩn thông tin", "empty_column.account_suspended": "Tài khoản vô hiệu hóa", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Sạch sẽ! Không còn gì ở đây. Khi bạn nhận được thông báo mới, chúng sẽ xuất hiện ở đây theo cài đặt của bạn.", "empty_column.notifications": "Bạn chưa có thông báo nào. Hãy thử theo dõi hoặc nhắn riêng cho ai đó.", "empty_column.public": "Trống trơn! Bạn hãy viết gì đó hoặc bắt đầu theo dõi những người khác", + "error.no_hashtag_feed_access": "Tham gia hoặc đăng nhập để xem và theo dõi hashtag này.", "error.unexpected_crash.explanation": "Trang này có thể không hiển thị chính xác do lỗi lập trình Mastodon hoặc vấn đề tương thích trình duyệt.", "error.unexpected_crash.explanation_addons": "Trang này không thể hiển thị do xung khắc với add-on của trình duyệt hoặc công cụ tự động dịch ngôn ngữ.", "error.unexpected_crash.next_steps": "Hãy thử làm mới trang. Nếu vẫn không được, bạn hãy vào Mastodon bằng một ứng dụng di động hoặc trình duyệt khác.", @@ -408,7 +409,7 @@ "follow_suggestions.personalized_suggestion": "Gợi ý cá nhân hóa", "follow_suggestions.popular_suggestion": "Người nổi tiếng", "follow_suggestions.popular_suggestion_longer": "Nổi tiếng trên {domain}", - "follow_suggestions.similar_to_recently_followed_longer": "Tương đồng những người mà bạn theo dõi", + "follow_suggestions.similar_to_recently_followed_longer": "Tương tự những người mà bạn theo dõi", "follow_suggestions.view_all": "Xem tất cả", "follow_suggestions.who_to_follow": "Gợi ý theo dõi", "followed_tags": "Hashtag theo dõi", @@ -903,7 +904,7 @@ "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}", "status.embed": "Nhúng", "status.favourite": "Thích", - "status.favourites": "{count, plural, other {thích}}", + "status.favourites_count": "{count, plural, other {{counter} thích}}", "status.filter": "Lọc tút này", "status.history.created": "{name} đăng {date}", "status.history.edited": "{name} đã sửa {date}", @@ -935,17 +936,17 @@ "status.quote_policy_change": "Thay đổi người có thể trích dẫn", "status.quote_post_author": "Trích dẫn từ tút của @{name}", "status.quote_private": "Không thể trích dẫn nhắn riêng", - "status.quotes": "{count, plural, other {trích dẫn}}", "status.quotes.empty": "Tút này chưa có ai trích dẫn. Nếu có, nó sẽ hiển thị ở đây.", "status.quotes.local_other_disclaimer": "Những trích dẫn bị tác giả từ chối sẽ không được hiển thị.", "status.quotes.remote_other_disclaimer": "Chỉ những trích dẫn từ {domain} mới được hiển thị ở đây. Những trích dẫn bị tác giả từ chối sẽ không được hiển thị.", + "status.quotes_count": "{count, plural, other {{counter} trích dẫn}}", "status.read_more": "Đọc tiếp", "status.reblog": "Đăng lại", "status.reblog_or_quote": "Đăng lại hoặc trích dẫn", "status.reblog_private": "Chia sẻ lại với người theo dõi của bạn", "status.reblogged_by": "{name} đăng lại", - "status.reblogs": "{count, plural, other {đăng lại}}", "status.reblogs.empty": "Tút này chưa có ai đăng lại. Nếu có, nó sẽ hiển thị ở đây.", + "status.reblogs_count": "{count, plural, other {{counter} đăng lại}}", "status.redraft": "Xóa và viết lại", "status.remove_bookmark": "Bỏ lưu", "status.remove_favourite": "Bỏ thích", @@ -1022,12 +1023,12 @@ "visibility_modal.direct_quote_warning.text": "Nếu bạn lưu cài đặt hiện tại, trích dẫn được nhúng sẽ được chuyển đổi thành liên kết.", "visibility_modal.direct_quote_warning.title": "Không thể nhúng trích dẫn vào Nhắn Riêng", "visibility_modal.header": "Hiển thị và tương tác", - "visibility_modal.helper.direct_quoting": "Nhắn riêng trên Mastodon không thể được người khác trích dẫn.", + "visibility_modal.helper.direct_quoting": "Không thể trích dẫn tút Nhắn riêng trên Mastodon.", "visibility_modal.helper.privacy_editing": "Không thể thay đổi chế độ hiển thị sau khi một tút đã đăng.", "visibility_modal.helper.privacy_private_self_quote": "Không thể công khai trích dẫn tút của bản thân hoặc tút riêng tư.", - "visibility_modal.helper.private_quoting": "Tút chỉ dành cho người theo dõi trên Mastodon không thể được người khác trích dẫn.", + "visibility_modal.helper.private_quoting": "Không thể trích dẫn tút chỉ dành cho người theo dõi trên Mastodon.", "visibility_modal.helper.unlisted_quoting": "Khi ai đó trích dẫn bạn, tút của họ cũng sẽ bị ẩn khỏi bảng tin công khai.", - "visibility_modal.instructions": "Kiểm soát những ai có thể tương tác với tút này. Bạn cũng có thể áp dụng cài đặt cho tất cả các tút trong tương lai bằng cách điều hướng đến Thiết lập > Đăng.", + "visibility_modal.instructions": "Kiểm soát những ai có thể tương tác với tút này. Bạn cũng có thể đặt sẵn cho tất cả tút trong tương lai bằng cách truy cập Thiết lập > Mặc định cho tút.", "visibility_modal.privacy_label": "Hiển thị", "visibility_modal.quote_followers": "Chỉ người theo dõi", "visibility_modal.quote_label": "Ai có thể trích dẫn", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 13fbc569cc3..a5d475f0e4b 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -252,7 +252,7 @@ "confirmations.private_quote_notify.cancel": "返回编辑", "confirmations.private_quote_notify.confirm": "发布嘟文", "confirmations.private_quote_notify.do_not_show_again": "不再显示此消息", - "confirmations.private_quote_notify.message": "您所引用与提及的用户将收到通知且能够查看你的嘟文,即便这些用户没有关注您。", + "confirmations.private_quote_notify.message": "你所引用与提及的用户将收到通知且能够查看你的嘟文,即便这些用户没有关注你。", "confirmations.private_quote_notify.title": "是否和关注者和提及的用户分享此内容?", "confirmations.quiet_post_quote_info.dismiss": "不再提醒", "confirmations.quiet_post_quote_info.got_it": "明白了", @@ -903,7 +903,7 @@ "status.edited_x_times": "共编辑 {count, plural, other {{count} 次}}", "status.embed": "获取嵌入代码", "status.favourite": "喜欢", - "status.favourites": "{count, plural, other {次喜欢}}", + "status.favourites_count": "{count, plural, other {{counter} 次喜欢}}", "status.filter": "过滤此嘟文", "status.history.created": "{name} 创建于 {date}", "status.history.edited": "{name} 编辑于 {date}", @@ -935,17 +935,17 @@ "status.quote_policy_change": "更改谁可以引用", "status.quote_post_author": "引用了 @{name} 的嘟文", "status.quote_private": "不能引用私人嘟文", - "status.quotes": "{count, plural, other {引用嘟文}}", "status.quotes.empty": "还没有人引用过此条嘟文。引用此嘟文的人会显示在这里。", "status.quotes.local_other_disclaimer": "遭作者拒绝的引用将不会显示。", "status.quotes.remote_other_disclaimer": "此处仅保证会显示来自 {domain} 的引用。遭作者拒绝的引用将不会显示。", + "status.quotes_count": "{count, plural, other {{counter} 次引用}}", "status.read_more": "查看更多", "status.reblog": "转嘟", "status.reblog_or_quote": "转嘟或引用", "status.reblog_private": "向关注者再次分享", "status.reblogged_by": "{name} 转嘟了", - "status.reblogs": "{count, plural, other {次转嘟}}", "status.reblogs.empty": "没有人转嘟过此条嘟文。如果有人转嘟了,就会显示在这里。", + "status.reblogs_count": "{count, plural, other {{counter} 次转嘟}}", "status.redraft": "删除并重新编辑", "status.remove_bookmark": "移除书签", "status.remove_favourite": "从喜欢列表中移除", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index fd3eb665ab4..dbb7752e97b 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -692,7 +692,6 @@ "status.edited": "最後編輯於 {date}", "status.edited_x_times": "Edited {count, plural, one {{count} 次} other {{count} 次}}", "status.favourite": "最愛", - "status.favourites": "{count, plural, one {則最愛} other {則最愛}}", "status.filter": "篩選此帖文", "status.history.created": "{name} 於 {date} 建立", "status.history.edited": "{name} 於 {date} 編輯", @@ -709,7 +708,6 @@ "status.read_more": "閱讀更多", "status.reblog": "轉推", "status.reblogged_by": "{name} 轉推", - "status.reblogs": "{count, plural, one {則轉推} other {則轉推}}", "status.reblogs.empty": "還未有人轉推。有的話會顯示在這裡。", "status.redraft": "刪除並編輯", "status.remove_bookmark": "移除書籤", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index c75f359a546..cbe6a1e56fb 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -1,43 +1,43 @@ { - "about.blocks": "被限制的伺服器", + "about.blocks": "受管制的伺服器", "about.contact": "聯絡我們:", "about.default_locale": "預設", "about.disclaimer": "Mastodon 是一個自由的開源軟體,是 Mastodon gGmbH 之註冊商標。", "about.domain_blocks.no_reason_available": "無法存取的原因", - "about.domain_blocks.preamble": "Mastodon 基本上允許您瀏覽聯邦宇宙中任何伺服器的內容並與使用者互動。以下是在本伺服器上設定的例外。", - "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案和內容,除非您明確搜尋或主動跟隨對方。", + "about.domain_blocks.preamble": "Mastodon 基本上允許您瀏覽聯邦宇宙中任何伺服器的內容並與使用者互動。以下是於本伺服器上設定之例外。", + "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案與內容,除非您明確地打開或著跟隨此個人檔案。", "about.domain_blocks.silenced.title": "已受限", "about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法和此伺服器上的使用者互動與交流。", "about.domain_blocks.suspended.title": "已停權", "about.language_label": "語言", "about.not_available": "無法於本伺服器上使用此資訊。", - "about.powered_by": "由 {mastodon} 提供的去中心化社群媒體", + "about.powered_by": "由 {mastodon} 提供之去中心化社群媒體", "about.rules": "伺服器規則", "account.account_note_header": "個人備註", "account.add_or_remove_from_list": "自列表中新增或移除", "account.badges.bot": "機器人", "account.badges.group": "群組", "account.block": "封鎖 @{name}", - "account.block_domain": "封鎖來自 {domain} 網域的所有內容", + "account.block_domain": "封鎖來自 {domain} 網域之所有內容", "account.block_short": "封鎖", "account.blocked": "已封鎖", "account.blocking": "封鎖中", "account.cancel_follow_request": "收回跟隨請求", "account.copy": "複製個人檔案連結", - "account.direct": " @{name}", - "account.disable_notifications": "取消來自 @{name} 嘟文的通知", + "account.direct": "私訊 @{name}", + "account.disable_notifications": "取消來自 @{name} 嘟文之通知", "account.domain_blocking": "封鎖中網域", "account.edit_profile": "編輯個人檔案", "account.edit_profile_short": "編輯", - "account.enable_notifications": "當 @{name} 嘟文時通知我", - "account.endorse": "於個人檔案推薦對方", + "account.enable_notifications": "當 @{name} 發嘟時通知我", + "account.endorse": "於個人檔案推薦", "account.familiar_followers_many": "被 {name1}、{name2}、及{othersCount, plural, other {其他您認識的 # 人}} 跟隨", "account.familiar_followers_one": "被 {name1} 跟隨", "account.familiar_followers_two": "被 {name1} 與 {name2} 跟隨", "account.featured": "精選內容", "account.featured.accounts": "個人檔案", "account.featured.hashtags": "主題標籤", - "account.featured_tags.last_status_at": "上次嘟文於 {date}", + "account.featured_tags.last_status_at": "上次發嘟於 {date}", "account.featured_tags.last_status_never": "沒有嘟文", "account.follow": "跟隨", "account.follow_back": "跟隨回去", @@ -47,20 +47,20 @@ "account.follow_request_cancel_short": "取消", "account.follow_request_short": "跟隨請求", "account.followers": "跟隨者", - "account.followers.empty": "尚未有人跟隨這位使用者。", + "account.followers.empty": "尚未有人跟隨此使用者。", "account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨", - "account.followers_you_know_counter": "{counter} 位您知道的跟隨者", + "account.followers_you_know_counter": "{counter} 位您認識的跟隨者", "account.following": "跟隨中", "account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}", - "account.follows.empty": "這位使用者尚未跟隨任何人。", + "account.follows.empty": "此使用者尚未跟隨任何人。", "account.follows_you": "已跟隨您", "account.go_to_profile": "前往個人檔案", - "account.hide_reblogs": "隱藏來自 @{name} 的轉嘟", + "account.hide_reblogs": "隱藏來自 @{name} 之轉嘟", "account.in_memoriam": "謹此悼念。", "account.joined_short": "加入時間", "account.languages": "變更訂閱的語言", "account.link_verified_on": "已於 {date} 檢查此連結的擁有者權限", - "account.locked_info": "此帳號的隱私狀態設定為鎖定。該擁有者會手動審核能跟隨此帳號的人。", + "account.locked_info": "此帳號的隱私狀態被設為鎖定。該擁有者會手動審核能跟隨此帳號的人。", "account.media": "媒體", "account.mention": "提及 @{name}", "account.moved_to": "{name} 目前的新帳號為:", @@ -104,9 +104,9 @@ "alert.rate_limited.title": "已限速", "alert.unexpected.message": "發生非預期的錯誤。", "alert.unexpected.title": "哎呀!", - "alt_text_badge.title": "替代文字", - "alt_text_modal.add_alt_text": "新增替代文字", - "alt_text_modal.add_text_from_image": "自圖片新增替代文字", + "alt_text_badge.title": "ALT 說明文字", + "alt_text_modal.add_alt_text": "新增 ALT 說明文字", + "alt_text_modal.add_text_from_image": "自圖片新增 ALT 說明文字", "alt_text_modal.cancel": "取消", "alt_text_modal.change_thumbnail": "變更預覽圖", "alt_text_modal.describe_for_people_with_hearing_impairments": "替聽覺障礙人士描述...", @@ -119,9 +119,9 @@ "annual_report.summary.archetype.pollster": "民調專家", "annual_report.summary.archetype.replier": "社交菁英", "annual_report.summary.followers.followers": "跟隨者", - "annual_report.summary.followers.total": "總共 {count}", + "annual_report.summary.followers.total": "總共 {count} 人", "annual_report.summary.here_it_is": "以下是您的 {year} 年度回顧:", - "annual_report.summary.highlighted_post.by_favourites": "最多被加到最愛的嘟文", + "annual_report.summary.highlighted_post.by_favourites": "最多被加至最愛的嘟文", "annual_report.summary.highlighted_post.by_reblogs": "最多轉嘟的嘟文", "annual_report.summary.highlighted_post.by_replies": "最多回覆的嘟文", "annual_report.summary.highlighted_post.possessive": "{name} 的", @@ -132,40 +132,40 @@ "annual_report.summary.percentile.text": "這讓您成為前{domain} 的使用者。", "annual_report.summary.percentile.we_wont_tell_bernie": "我們不會告訴 Bernie。", "annual_report.summary.thanks": "感謝您成為 Mastodon 的一份子!", - "attachments_list.unprocessed": "(未經處理)", + "attachments_list.unprocessed": "(未處理)", "audio.hide": "隱藏音訊", "block_modal.remote_users_caveat": "我們會要求 {domain} 伺服器尊重您的決定。然而,我們無法保證所有伺服器皆會遵守,某些伺服器可能以不同方式處理封鎖。未登入之使用者仍可能看見您的公開嘟文。", "block_modal.show_less": "減少顯示", "block_modal.show_more": "顯示更多", "block_modal.they_cant_mention": "他們無法提及或跟隨您。", - "block_modal.they_cant_see_posts": "他們無法讀取您的嘟文,且您不會見到他們的。", + "block_modal.they_cant_see_posts": "他們無法讀取您的嘟文,且您不會見到他們。", "block_modal.they_will_know": "他們能見到他們已被封鎖。", "block_modal.title": "是否封鎖該使用者?", "block_modal.you_wont_see_mentions": "您不會見到提及他們的嘟文。", - "boost_modal.combo": "下次您可以按 {combo} 跳過", + "boost_modal.combo": "您下次可以按 {combo} 跳過", "boost_modal.reblog": "是否要轉嘟?", "boost_modal.undo_reblog": "是否要取消轉嘟?", "bundle_column_error.copy_stacktrace": "複製錯誤報告", - "bundle_column_error.error.body": "無法繪製請求的頁面。這可能是因為我們程式碼中的臭蟲或是瀏覽器的相容問題。", + "bundle_column_error.error.body": "無法繪製請求的頁面。這可能是因為我們程式碼中的臭蟲或是瀏覽器相容問題。", "bundle_column_error.error.title": "糟糕!", "bundle_column_error.network.body": "嘗試載入此頁面時發生錯誤。這可能是因為您的網際網路連線或此伺服器有暫時性的問題。", "bundle_column_error.network.title": "網路錯誤", "bundle_column_error.retry": "重試", "bundle_column_error.return": "返回首頁", - "bundle_column_error.routing.body": "找不到請求的頁面。您確定網址列中的 URL 是正確的嗎?", + "bundle_column_error.routing.body": "找不到請求的頁面。您是否確定網址列中的 URL 是正確的?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "關閉", "bundle_modal_error.message": "載入此畫面時發生錯誤。", "bundle_modal_error.retry": "重試", "carousel.current": "頁面 {current, number} / {max, number}", "carousel.slide": "{max, number} 頁中之第 {current, number} 頁", - "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。", + "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與此伺服器互動。", "closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon。", "closed_registrations_modal.find_another_server": "尋找另一個伺服器", "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架設自己的伺服器!", "closed_registrations_modal.title": "註冊 Mastodon", "column.about": "關於", - "column.blocks": "已封鎖的使用者", + "column.blocks": "已封鎖使用者", "column.bookmarks": "書籤", "column.community": "本站時間軸", "column.create_list": "建立列表", @@ -244,10 +244,10 @@ "confirmations.logout.confirm": "登出", "confirmations.logout.message": "您確定要登出嗎?", "confirmations.logout.title": "您確定要登出嗎?", - "confirmations.missing_alt_text.confirm": "新增替代文字", - "confirmations.missing_alt_text.message": "您的嘟文中的多媒體內容未附上替代文字。添加描述能幫助更多人存取您的內容。", + "confirmations.missing_alt_text.confirm": "新增 ALT 說明文字", + "confirmations.missing_alt_text.message": "您的嘟文中的多媒體內容未附上 ALT 說明文字。添加說明文字描述能幫助更多人存取您的內容。", "confirmations.missing_alt_text.secondary": "仍要發嘟", - "confirmations.missing_alt_text.title": "是否新增替代文字?", + "confirmations.missing_alt_text.title": "是否新增 ALT 說明文字?", "confirmations.mute.confirm": "靜音", "confirmations.private_quote_notify.cancel": "返回至編輯", "confirmations.private_quote_notify.confirm": "發表嘟文", @@ -357,6 +357,7 @@ "empty_column.notification_requests": "清空啦!已經沒有任何推播通知。當您收到新推播通知時,它們將依照您的設定於此顯示。", "empty_column.notifications": "您還沒有收到任何推播通知,當您與別人開始互動時,它將於此顯示。", "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或者跟隨其他伺服器的使用者後,就會有嘟文出現了", + "error.no_hashtag_feed_access": "加入或登入 Mastodon 以檢視與跟隨此主題標籤。", "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。", "error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。", "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式以檢視來使用 Mastodon。", @@ -470,7 +471,7 @@ "ignore_notifications_modal.not_following_title": "忽略來自您未跟隨帳號之推播通知?", "ignore_notifications_modal.private_mentions_title": "忽略來自不請自來私訊之推播通知?", "info_button.label": "幫助", - "info_button.what_is_alt_text": "

何謂替代文字?

替代文字為視覺障礙者、低網路頻寬或尋求額外上下文語境的人們提供圖片描述。

您可以透過撰寫清晰、簡潔及客觀的替代文字以替所有人改善無障礙特性與協助理解。

  • 掌握幾個重要元素
  • 替圖片提供文字摘要
  • 使用常規行文結構
  • 避免冗贅資訊
  • 聚焦於趨勢與複雜視覺中之關鍵(如圖表或地圖)
", + "info_button.what_is_alt_text": "

何謂 ALT 說明文字?

ALT 說明文字為視覺障礙者、低網路頻寬或尋求額外上下文語境的人們提供圖片描述。

您可以透過撰寫清晰、簡潔及客觀的說明文字以替所有人改善無障礙特性與協助理解。

  • 掌握幾個重要元素
  • 替圖片提供文字摘要
  • 使用常規行文結構
  • 避免冗贅資訊
  • 聚焦於趨勢與複雜視覺中之關鍵(如圖表或地圖)
", "interaction_modal.action": "若欲與 {name} 之嘟文互動,您必須登入您帳號所註冊之 Mastodon 伺服器。", "interaction_modal.go": "Go!", "interaction_modal.no_account_yet": "仍尚未有帳號嗎?", @@ -903,7 +904,7 @@ "status.edited_x_times": "已編輯 {count, plural, other {{count} 次}}", "status.embed": "取得嵌入程式碼", "status.favourite": "最愛", - "status.favourites": "{count, plural, other {則最愛}}", + "status.favourites_count": "{count, plural, other {{counter} 則最愛}}", "status.filter": "過濾此嘟文", "status.history.created": "{name} 於 {date} 建立", "status.history.edited": "{name} 於 {date} 修改", @@ -935,17 +936,17 @@ "status.quote_policy_change": "變更可以引用的人", "status.quote_post_author": "已引用 @{name} 之嘟文", "status.quote_private": "無法引用私人嘟文", - "status.quotes": "{count, plural, other {則引用嘟文}}", "status.quotes.empty": "目前尚無人引用此嘟文。當有人引用時,它將於此顯示。", "status.quotes.local_other_disclaimer": "被作者拒絕之引用嘟文將不被顯示。", "status.quotes.remote_other_disclaimer": "僅有來自 {domain} 之引用嘟文保證將於此顯示。被作者拒絕之引用嘟文將不被顯示。", + "status.quotes_count": "{count, plural, other {{counter} 則引用嘟文}}", "status.read_more": "閱讀更多", "status.reblog": "轉嘟", "status.reblog_or_quote": "轉嘟或引用", "status.reblog_private": "再次與跟隨者分享", "status.reblogged_by": "{name} 已轉嘟", - "status.reblogs": "{count, plural, other {則轉嘟}}", "status.reblogs.empty": "還沒有人轉嘟過這則嘟文。當有人轉嘟時,它們將於此顯示。", + "status.reblogs_count": "{count, plural, other {{counter} 則轉嘟}}", "status.redraft": "刪除並重新編輯", "status.remove_bookmark": "自書籤中移除", "status.remove_favourite": "自最愛中移除", diff --git a/app/javascript/mastodon/main.tsx b/app/javascript/mastodon/main.tsx index f89baf66cd6..249baf65fc0 100644 --- a/app/javascript/mastodon/main.tsx +++ b/app/javascript/mastodon/main.tsx @@ -9,7 +9,6 @@ import { me, reduceMotion } from 'mastodon/initial_state'; import ready from 'mastodon/ready'; import { store } from 'mastodon/store'; -import { initializeEmoji } from './features/emoji'; import { isProduction, isDevelopment } from './utils/environment'; function main() { @@ -30,6 +29,7 @@ function main() { }); } + const { initializeEmoji } = await import('./features/emoji/index'); initializeEmoji(); const root = createRoot(mountNode); diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index d4ed1bd5059..784050d9429 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -341,8 +341,8 @@ export const composeReducer = (state = initialState, action) => { const isDirect = state.get('privacy') === 'direct'; return state .set('quoted_status_id', isDirect ? null : status.get('id')) - .set('spoiler', status.get('sensitive')) - .set('spoiler_text', status.get('spoiler_text')) + .update('spoiler', spoiler => (spoiler) || !!status.get('spoiler_text')) + .update('spoiler_text', (spoiler_text) => spoiler_text || status.get('spoiler_text')) .update('privacy', (visibility) => { if (['public', 'unlisted'].includes(visibility) && status.get('visibility') === 'private') { return 'private'; diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index 1e68d49aa69..471c7af4114 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -32,7 +32,11 @@ function getStatusResultFunction( }; } - if (statusBase.get('isLoading')) { + // When a status is loading, a `isLoading` property is set + // A status can be loading because it is not known yet (in which case it will only contain `isLoading`) + // or because it is being re-fetched; in the latter case, `visibility` will always be set to a non-empty + // string. + if (statusBase.get('isLoading') && !statusBase.get('visibility')) { return { status: null, loadingState: 'loading', @@ -74,7 +78,7 @@ function getStatusResultFunction( map.set('matched_filters', filtered); map.set('matched_media_filters', mediaFiltered); }), - loadingState: 'complete' + loadingState: statusBase.get('isLoading') ? 'loading' : 'complete' }; } diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index b5cb56d4254..c3a14a4fbde 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -327,9 +327,9 @@ $content-width: 840px; font-weight: 700; color: $primary-text-color; text-transform: none; - padding-bottom: 0; + padding-top: 0; margin-bottom: 0; - border-bottom: 0; + border-top: 0; .comment { display: block; @@ -1024,10 +1024,6 @@ a.name-tag, margin-top: 15px; } -.user-role { - color: var(--user-role-accent); -} - .applications-list { .icon { vertical-align: middle; diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss index 266a9ca9307..f3ddf341d27 100644 --- a/app/javascript/styles/mastodon/widgets.scss +++ b/app/javascript/styles/mastodon/widgets.scss @@ -47,10 +47,6 @@ overflow: hidden; text-overflow: ellipsis; - .fa { - color: $darker-text-color; - } - small { display: block; font-weight: 400; @@ -62,7 +58,6 @@ &.active h4 { &, - .fa, small, .trends__item__current { color: $primary-text-color; diff --git a/app/javascript/styles_new/application.scss b/app/javascript/styles_new/application.scss new file mode 100644 index 00000000000..e16e5368e7d --- /dev/null +++ b/app/javascript/styles_new/application.scss @@ -0,0 +1,7 @@ +@use 'mastodon/css_variables'; +@use 'mastodon/variables'; +@use 'common'; + +html { + color-scheme: dark; +} diff --git a/app/javascript/styles_new/common.scss b/app/javascript/styles_new/common.scss new file mode 100644 index 00000000000..d8a7f90a327 --- /dev/null +++ b/app/javascript/styles_new/common.scss @@ -0,0 +1,24 @@ +@use 'mastodon/mixins'; +@use 'fonts/roboto'; +@use 'fonts/roboto-mono'; + +@use 'mastodon/reset'; +@use 'mastodon/basics'; +@use 'mastodon/branding'; +@use 'mastodon/containers'; +@use 'mastodon/lists'; +@use 'mastodon/widgets'; +@use 'mastodon/forms'; +@use 'mastodon/accounts'; +@use 'mastodon/components'; +@use 'mastodon/polls'; +@use 'mastodon/modal'; +@use 'mastodon/emoji_picker'; +@use 'mastodon/annual_reports'; +@use 'mastodon/about'; +@use 'mastodon/tables'; +@use 'mastodon/admin'; +@use 'mastodon/dashboard'; +@use 'mastodon/rtl'; +@use 'mastodon/accessibility'; +@use 'mastodon/rich_text'; diff --git a/app/javascript/styles_new/contrast.scss b/app/javascript/styles_new/contrast.scss new file mode 100644 index 00000000000..af73c88fef6 --- /dev/null +++ b/app/javascript/styles_new/contrast.scss @@ -0,0 +1,8 @@ +@use 'mastodon/css_variables'; +@use 'mastodon/variables'; +@use 'common'; +@use 'contrast/diff'; + +html { + color-scheme: dark; +} diff --git a/app/javascript/styles_new/contrast/diff.scss b/app/javascript/styles_new/contrast/diff.scss new file mode 100644 index 00000000000..f809c7cdc3a --- /dev/null +++ b/app/javascript/styles_new/contrast/diff.scss @@ -0,0 +1,54 @@ +:root { + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-50); + --color-text-secondary: var(--color-grey-300); + --color-text-tertiary: var(--color-grey-400); + --color-text-brand: var(--color-indigo-300); + --color-text-status-links: var(--color-text-brand); + + /* BORDER TOKENS */ + + --border-strength-primary: 18%; +} + +.status__content a, +.reply-indicator__content a, +.edit-indicator__content a, +.link-footer a, +.status__content__read-more-button, +.status__content__translate-button { + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + + &.mention { + text-decoration: none; + + span { + text-decoration: underline; + } + + &:hover, + &:focus, + &:active { + span { + text-decoration: none; + } + } + } +} + +.link-button:disabled { + cursor: not-allowed; + + &:hover, + &:focus, + &:active { + text-decoration: none !important; + } +} diff --git a/app/javascript/styles_new/entrypoints/inert.scss b/app/javascript/styles_new/entrypoints/inert.scss new file mode 100644 index 00000000000..a60045d7be8 --- /dev/null +++ b/app/javascript/styles_new/entrypoints/inert.scss @@ -0,0 +1,14 @@ +/* This is needed for the wicg-inert polyfill */ + +[inert] { + pointer-events: none; + cursor: default; +} + +[inert], +[inert] * { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} diff --git a/app/javascript/styles_new/entrypoints/mailer.scss b/app/javascript/styles_new/entrypoints/mailer.scss new file mode 100644 index 00000000000..fcbbd66f4c7 --- /dev/null +++ b/app/javascript/styles_new/entrypoints/mailer.scss @@ -0,0 +1,1030 @@ +@use '../fonts/inter'; + +body { + accent-color: #6364ff; + overflow-wrap: anywhere; + margin: 0; + background-color: #f3f2f5; + padding: 0; + -webkit-text-size-adjust: none; + text-size-adjust: none; +} + +p, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + background-color: transparent; + padding: 0; + border: none; + font-family: Inter, 'Lucida Grande', sans-serif; +} + +img { + max-width: 100%; + height: auto; + border: none; + text-indent: 0; + vertical-align: middle; + color: inherit; + font-family: inherit; +} + +table { + border: none; +} + +table + p { + margin-top: 16px; +} + +.email { + min-width: 280px; + font-family: Inter, 'Lucida Grande', sans-serif; + overflow-wrap: anywhere; + color: #17063b; + background-color: #f3f2f5; +} + +.email-container { + max-width: 740px; + margin: 0 auto; + width: 100%; +} + +// Outer email card +.email-card-table { + border-collapse: collapse; + width: 100%; +} + +.email-card-td { + overflow: hidden; + box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 4%); + background-color: #fff; +} + +// Inner email card +.email-inner-card-table { + border-collapse: separate; + width: 100%; + border-radius: 12px; +} + +.email-inner-card-td-without-padding, +.email-inner-card-td { + border-radius: 12px; + overflow: hidden; + box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 8%); + background-color: #fff; + border: 1px solid #dfdee3; +} + +.email-inner-card-td { + padding: 24px; +} + +.email-inner-nested-card-td { + border-radius: 12px; + padding: 18px; + overflow: hidden; + background-color: #fff; + border: 1px solid #dfdee3; +} + +// Account +.email-account-banner-table { + background-color: #f3f2f5; + border-top-left-radius: 12px; + border-top-right-radius: 12px; +} + +.email-account-banner-td { + border-top-left-radius: 12px; + border-top-right-radius: 12px; + height: 140px; + vertical-align: bottom; + background-position: center !important; + background-size: cover !important; +} + +.email-account-banner-inner-td { + padding: 24px 24px 0; + mso-padding-alt: 24px; +} + +.email-account-banner-overlap-div { + max-height: 42px; +} + +.email-account-banner-icon-table { + width: auto; + margin: 0; + overflow: hidden; + border-radius: 8px; + border-collapse: separate; + background-color: #fff; + border: 2px solid #fff; + + img { + display: block; + max-width: 100%; + border: none; + border-radius: 6px; + } +} + +.email-account-body-td { + padding: 56px 24px 24px; + mso-padding-alt: 24px; +} + +.email-account-name { + font-size: 16px; + font-weight: 600; + line-height: 24px; + color: #17063b; +} + +.email-account-handle { + font-size: 14px; + line-height: 20px; + color: #746a89; +} + +.email-account-stats-table { + td { + padding-right: 16px; + font-size: 14px; + line-height: 20px; + color: #746a89; + } + + b { + font-weight: 600; + color: #17063b; + } + + span { + white-space: nowrap; + } +} + +// Utility classes +.email-w-full { + table-layout: fixed; + width: 100%; +} + +.email-prose { + p, + ul, + ol { + color: #17063b; + font-size: 14px; + line-height: 20px; + + &:not(:last-child) { + margin-bottom: 16px; + } + + a:not([class]) { + color: #6364ff; + text-decoration: none; + + &:hover { + color: #563acc !important; + } + } + } +} + +.email-dir-rtl { + direction: rtl; + + [dir='rtl'] & { + direction: ltr; + } +} + +.email-dir-ltr { + direction: ltr; +} + +.email-padding-24 { + padding: 24px; +} + +.email-padding-top-24 { + padding-top: 24px; +} + +.email-padding-top-16 { + padding-top: 16px; +} + +.email-padding-top-0 { + padding-top: 0; +} + +.email-border-top { + border-top: 1px solid #dfdee3; +} + +.email-border-bottom { + border-bottom: 1px solid #dfdee3; +} + +.email-desktop-flex { + font-size: 0; + max-width: 740px; + margin-left: auto; + margin-right: auto; + + &.email-dir-rtl > .email-desktop-column { + direction: ltr; + + [dir='rtl'] & { + direction: rtl; + } + } +} + +.email-desktop-column { + display: inline-block; + width: 100%; + max-width: none; + text-align: start; + vertical-align: top; + font-size: 16px; +} + +// Header +.email-header-td { + padding: 16px 32px; + background-color: #1b001f; + background-image: url('../../images/mailer-new/common/header-bg-start.png'); + background-position: left top; + background-repeat: repeat; +} + +.email-header-logo-table { + width: auto; + margin: 0; +} + +.email-header-logo-td { + padding: 16px 0; + font-size: 0; + + img { + color: #fff; + font-size: 16px; + font-weight: bold; + max-height: 40px; + } +} + +.email-header-logo-a { + display: inline-block; + + img { + display: inline-block; + color: #fff; + } +} + +.email-header-logo-div { + max-height: 0; +} + +.email-header-logo-p { + word-break: break-all; + padding-left: 40px; + padding-top: 26px; + font-size: 11px; + line-height: 13px; + color: #8d808f; + text-align: left; +} + +.email-header-logo-span { + display: block; + text-align: right; +} + +.email-header-heading-td { + padding: 16px 0; +} + +.email-header-heading-img-td { + width: 56px; + text-align: left; + vertical-align: top; + + img { + width: 56px; + height: 56px; + border-radius: 12px; + } +} + +.email-header-heading-txt-td { + vertical-align: middle; + padding-left: 16px; + padding-right: 16px; + + h1 { + margin-bottom: 5px; + color: #fff; + font-size: 24px; + line-height: 28px; + font-weight: 600; + } + + p { + color: #a399a5; + font-size: 18px; + line-height: 21.6px; + font-weight: 500; + } + + &:only-child { + padding-left: 0; + padding-right: 0; + } +} + +.email-header-card-table { + width: 100%; + border-collapse: separate; + overflow: hidden; + border-radius: 12px; + background-color: #fff; + border: 2px solid #fff; + box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 8%); +} + +.email-header-card { + position: relative; + max-height: 100px; +} + +.email-header-card-banner-td { + border-radius: 12px 12px 0 0; + width: 236px; + height: 80px; + background-color: #f3f2f5 !important; + background-position: center !important; + background-size: cover !important; +} + +.email-header-card-body-td { + padding: 12px; + + .email-btn-table { + width: 100%; + max-width: 212px; + } +} + +.email-header-card-instance { + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + color: #17063b; + font-size: 14px; + line-height: 20px; + font-weight: 600; + + &:only-of-type { + margin-bottom: 12px; + } +} + +.email-header-card-description { + margin-bottom: 12px; + color: #746a89; + font-size: 12px; + line-height: 16px; + max-height: 32px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +// To make the design work with images off +// we create an empty div that overlaps with +// the rest of the content with a dark background. +.email-header-after-div { + max-height: 0; +} + +.email-header-after-inside-div { + height: 30px; + background-color: #1b001f; +} + +// Body content +.email-body-td { + background-image: url('../../images/mailer-new/common/header-bg-end.png'); + background-position: left top; + background-repeat: no-repeat; +} + +.email-body-padding-td { + padding: 0 32px 32px; + mso-padding-alt: 32px; +} + +.email-body-columns-td { + border-top: 1px solid #dfdee3; + padding: 32px 24px 8px; +} + +.email-body-huge-padding-td { + padding: 110px 32px 32px; + mso-padding-alt: 32px; +} + +.email-body-padding-td { + & > p { + font-size: 14px; + line-height: 20px; + color: #17063b; + + a { + color: #6364ff; + text-decoration: none; + + &:hover { + color: #563acc !important; + } + } + } +} + +// Texts +.email-h2 { + margin-bottom: 4px; + color: #17063b; + font-size: 18px; + font-weight: 600; + line-height: 28px; +} + +.email-h-sub { + margin-bottom: 16px; + color: #746a89; + font-size: 14px; + line-height: 16px; +} + +.email-p { + margin-bottom: 16px; + color: #746a89; + font-size: 14px; + font-weight: 400; + line-height: 20px; +} + +// Footer +.email-footer-td { + padding: 28px 32px 32px; + text-align: center; +} + +.email-footer-logo-a { + display: inline-block; +} + +.email-footer-p { + color: #9b94ab; + text-align: center; + font-size: 12px; + line-height: 20px; + + a { + color: #9b94ab; + text-decoration: underline; + } + + &:first-child { + margin-bottom: 12px; + } +} + +// Button +.email-btn-table { + margin: 0; + max-width: 100%; + border-collapse: separate; + border-radius: 8px; + background-color: #6364ff; +} + +.email-btn-td { + height: 40px; + text-align: center; + mso-padding-alt: 0 35px; + word-break: normal; +} + +.email-btn-a { + display: block; + border-radius: 8px; + padding-left: 35px; + padding-right: 35px; + padding-top: 10px; + padding-bottom: 10px; + text-align: center; + font-family: Inter, 'Lucida Grande', sans-serif; + font-size: 14px; + font-weight: 600; + line-height: 20px; + color: #fff; + text-decoration: none; + transition: background-color 0.3s ease-in-out; +} + +// Status +.email-status-header-img { + vertical-align: top; + width: 48px; + + img { + width: 48px; + height: 48px; + border-radius: 8px; + overflow: hidden; + } +} + +.email-quote-header-img { + width: 34px; + + img { + width: 34px; + height: 34px; + border-radius: 8px; + overflow: hidden; + } +} + +.email-status-header-text { + padding-left: 16px; + padding-right: 16px; + vertical-align: middle; +} + +.email-quote-header-text { + padding-left: 14px; + padding-right: 14px; + vertical-align: middle; +} + +.email-status-header-name { + font-size: 16px; + font-weight: 600; + line-height: 24px; + color: #17063b; +} + +.email-status-header-handle { + font-size: 14px; + line-height: 20px; + color: #746a89; +} + +.email-quote-header-name { + font-size: 14px; + font-weight: 600; + line-height: 18px; + color: #17063b; +} + +.email-quote-header-handle { + font-size: 13px; + line-height: 18px; + color: #746a89; +} + +.email-status-content { + padding-top: 24px; +} + +.email-status-spoiler { + color: #746a89; + font-style: italic; + margin-bottom: 8px; +} + +.email-status-prose { + .quote-inline { + display: none; + } + + p { + font-size: 14px; + line-height: 20px; + margin-bottom: 20px; + color: #17063b; + white-space: pre-wrap; + unicode-bidi: plaintext; + } + + a { + color: #6364ff; + text-decoration: none; + + &:hover { + color: #563acc !important; + } + } + + .invisible { + font-size: 0; + line-height: 0; + display: inline-block; + width: 0; + height: 0; + position: absolute; + } + + .ellipsis { + &::after { + content: '…'; + } + } +} + +.email-status-media { + margin-top: 16px; + font-size: 14px; + line-height: 20px; + color: #17063b; + + img { + border-radius: 8px; + } + + a { + color: #6364ff; + text-decoration: none; + + &:hover { + color: #563acc !important; + } + } +} + +.email-status-footer { + margin-top: 16px; + font-size: 12px; + line-height: 16px; + color: #746a89; + + a { + color: #746a89; + } + + a:hover { + color: #746a89 !important; + text-decoration: underline !important; + } +} + +// Purple frame for emphasis +.email-frame-table { + background-color: #efefff; + border-radius: 8px; +} + +.email-frame-td { + padding: 16px; +} + +.email-frame-wrapper-td { + padding-bottom: 16px; +} + +.email-frame-td > p { + text-align: center; + font-size: 16px; + line-height: 24px; +} + +// Checklist item +.email-checklist-wrapper-td { + padding: 4px 0; +} + +.email-checklist-table { + border-radius: 12px; + border-width: 1px; + border-style: solid; + border-color: #efefff; + background-color: #fff; +} + +.email-checklist-checked { + border-color: #c4e6d7; + background-color: #eaf6f1; +} + +.email-checklist-td { + padding: 16px 16px 6px; +} + +.email-checklist-icons-td { + width: 84px; + vertical-align: top; +} + +.email-checklist-icons-checkbox-td { + width: 20px; + vertical-align: middle; + + img { + max-width: 100%; + width: 20px; + } +} + +.email-checklist-icons-step-td { + width: 64px; + text-align: center; + vertical-align: middle; + + img { + max-width: 100%; + width: 40px; + } +} + +.email-checklist-text-td { + h3 { + margin: 0 0 4px; + color: #17063b; + font-size: 14px; + font-weight: 600; + line-height: 16.8px; + + .email-checklist-checked & { + color: #746a89; + text-decoration: line-through; + } + } + + p { + margin: 0 0 12px; + color: #746a89; + font-size: 14px; + line-height: 16.8px; + } + + .email-btn-table { + width: 100px; + } + + .email-btn-td { + mso-padding-alt: 10px; + } + + .email-btn-a { + padding-left: 10px; + padding-right: 10px; + } + + div + div { + margin-inline-start: auto; + margin-bottom: 12px; + } +} + +// Welcome email +.email-welcome-apps-btns { + font-size: 12px; + line-height: 44px; +} + +.email-column-td { + padding: 0 8px; + vertical-align: top; +} + +.email-link-with-arrow { + color: #6364ff; + font-size: 14px; + font-weight: 600; + line-height: 16.8px; + + &:hover { + color: #563acc !important; + } + + span { + font-size: 12px; + font-weight: 400; + } +} + +.email-column-action-td { + padding: 24px 0; + color: #6364ff; + font-size: 14px; + font-weight: 600; + line-height: 16.8px; + text-align: center; +} + +// Follow and hashtags +.email-mini-wrapper-td { + padding: 4px 0; + + table { + table-layout: fixed; + } +} + +.email-mini-td { + border-radius: 12px; + border: 1px solid #e8e6eb; + background-color: #fff; + padding: 15px 16px; +} + +.email-mini-follow-img-td { + width: 40px; + vertical-align: top; + + img { + border-radius: 8px; + } +} + +.email-mini-follow-text-td { + padding-left: 8px; + padding-right: 16px; + vertical-align: top; + + h3 { + color: #17063b; + font-size: 14px; + font-weight: 600; + line-height: 20px; + } + + p { + color: #746a89; + font-size: 12px; + font-weight: 400; + line-height: 16px; + } +} + +.email-mini-follow-btn-td { + width: 68px; + vertical-align: top; + + .email-btn-table { + width: 100%; + } + + .email-btn-td { + mso-padding-alt: 10px; + } + + .email-btn-a { + padding-left: 10px; + padding-right: 10px; + } +} + +.email-mini-hashtag-td { + height: 40px; + + td { + vertical-align: middle; + } + + h3 { + color: #17063b; + font-size: 14px; + font-weight: 600; + line-height: 20px; + } + + p { + color: #746a89; + font-size: 12px; + font-weight: 400; + line-height: 16px; + word-break: break-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.email-mini-hashtag-img-td { + width: 40px; + height: 20px; + white-space: nowrap; + text-indent: -2px; + font-size: 0; + + & + td { + padding-left: 8px; + } +} + +.email-mini-hashtag-img-span { + display: inline-block; + max-width: 12px; + font-size: 12px; + + img { + width: 16px; + height: 16px; + border-radius: 50%; + max-width: none; + border: 2px solid #fff; + vertical-align: middle; + } +} + +// Extra content on light purple background +.email-extra-wave { + height: 42px; + background-image: url('../../images/mailer-new/welcome/purple-extra-soft-wave.png'); + background-position: bottom center; + background-repeat: no-repeat; +} + +.email-extra-td { + padding: 32px 32px 24px; + background-color: #f0f0ff; + background-image: url('../../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes + + .email-column-td { + padding-top: 8px; + padding-bottom: 8px; + } +} + +// Feature card +.email-feature-wrapper-td { + padding: 8px 0; +} + +.email-feature-td { + padding: 24px; + background-color: #fff; + border: 1px solid #e8e6eb; + border-radius: 12px; +} + +// Responsive +/* stylelint-disable-next-line media-feature-range-notation -- Basic media queries have better support across email clients. */ +@media only screen and (min-width: 740px) { + .email-desktop-p-8 { + padding: 32px !important; + } + + .email-desktop-rounded-16px { + border-radius: 16px !important; + } + + .email-header-td { + border-radius: 16px 16px 0 0 !important; + } + + .email-desktop-flex { + display: flex; + } + + .email-header-left { + padding-right: 32px; + } + + .email-header-right { + width: 240px; + margin-inline-start: auto; + } + + .email-desktop-column { + max-width: 346px !important; + } + + .email-desktop-text-right { + text-align: right; + } +} diff --git a/app/javascript/styles_new/fonts/inter.scss b/app/javascript/styles_new/fonts/inter.scss new file mode 100644 index 00000000000..816fc75b85a --- /dev/null +++ b/app/javascript/styles_new/fonts/inter.scss @@ -0,0 +1,8 @@ +@font-face { + font-family: Inter; + src: url('../../fonts/inter/inter-variable-font-slnt-wght.woff2') + format('woff2-variations'); + font-weight: 100 900; + font-style: normal; + mso-generic-font-family: swiss; +} diff --git a/app/javascript/styles_new/fonts/roboto-mono.scss b/app/javascript/styles_new/fonts/roboto-mono.scss new file mode 100644 index 00000000000..d51cb01c91f --- /dev/null +++ b/app/javascript/styles_new/fonts/roboto-mono.scss @@ -0,0 +1,13 @@ +@font-face { + font-family: mastodon-font-monospace; + src: + local('Roboto Mono'), + url('@/fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'), + url('@/fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'), + url('@/fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'), + url('@/fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') + format('svg'); + font-weight: 400; + font-display: swap; + font-style: normal; +} diff --git a/app/javascript/styles_new/fonts/roboto.scss b/app/javascript/styles_new/fonts/roboto.scss new file mode 100644 index 00000000000..ea4b842b0c4 --- /dev/null +++ b/app/javascript/styles_new/fonts/roboto.scss @@ -0,0 +1,55 @@ +@font-face { + font-family: mastodon-font-sans-serif; + src: + local('Roboto Italic'), + url('@/fonts/roboto/roboto-italic-webfont.woff2') format('woff2'), + url('@/fonts/roboto/roboto-italic-webfont.woff') format('woff'), + url('@/fonts/roboto/roboto-italic-webfont.ttf') format('truetype'), + url('@/fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont') + format('svg'); + font-weight: normal; + font-display: swap; + font-style: italic; +} + +@font-face { + font-family: mastodon-font-sans-serif; + src: + local('Roboto Bold'), + url('@/fonts/roboto/roboto-bold-webfont.woff2') format('woff2'), + url('@/fonts/roboto/roboto-bold-webfont.woff') format('woff'), + url('@/fonts/roboto/roboto-bold-webfont.ttf') format('truetype'), + url('@/fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont') + format('svg'); + font-weight: bold; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: mastodon-font-sans-serif; + src: + local('Roboto Medium'), + url('@/fonts/roboto/roboto-medium-webfont.woff2') format('woff2'), + url('@/fonts/roboto/roboto-medium-webfont.woff') format('woff'), + url('@/fonts/roboto/roboto-medium-webfont.ttf') format('truetype'), + url('@/fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont') + format('svg'); + font-weight: 500; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: mastodon-font-sans-serif; + src: + local('Roboto'), + url('@/fonts/roboto/roboto-regular-webfont.woff2') format('woff2'), + url('@/fonts/roboto/roboto-regular-webfont.woff') format('woff'), + url('@/fonts/roboto/roboto-regular-webfont.ttf') format('truetype'), + url('@/fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont') + format('svg'); + font-weight: normal; + font-display: swap; + font-style: normal; +} diff --git a/app/javascript/styles_new/mastodon-light.scss b/app/javascript/styles_new/mastodon-light.scss new file mode 100644 index 00000000000..494efdbbde9 --- /dev/null +++ b/app/javascript/styles_new/mastodon-light.scss @@ -0,0 +1,9 @@ +@use 'mastodon-light/css_variables'; +@use 'mastodon/variables' with ( + $emojis-requiring-inversion: 'chains' +); +@use 'common'; + +html { + color-scheme: light; +} diff --git a/app/javascript/styles_new/mastodon-light/css_variables.scss b/app/javascript/styles_new/mastodon-light/css_variables.scss new file mode 100644 index 00000000000..70745ec071a --- /dev/null +++ b/app/javascript/styles_new/mastodon-light/css_variables.scss @@ -0,0 +1,214 @@ +@use '../mastodon/theme_utils' as utils; + +:root { + --color-black: #000; + --color-grey-950: #181821; + --color-grey-800: #292938; + --color-grey-700: #444664; + --color-grey-600: #545778; + --color-grey-500: #696d91; + --color-grey-400: #8b8dac; + --color-grey-300: #b4b6cb; + --color-grey-200: #d8d9e3; + --color-grey-100: #f0f0f5; + --color-grey-50: #f0f1ff; + --color-white: #fff; + --color-indigo-600: #6147e6; + --color-indigo-400: #8886ff; + --color-indigo-300: #a5abfd; + --color-indigo-200: #c8cdfe; + --color-indigo-100: #e0e3ff; + --color-indigo-50: #f0f1ff; + --color-red-500: #ff637e; + --color-red-600: #ec003f; + --color-yellow-400: #ffb900; + --color-yellow-600: #e17100; + --color-green-400: #05df72; + --color-green-600: #00a63e; + + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-950); + --color-text-secondary: var(--color-grey-600); + --color-text-tertiary: var(--color-grey-500); + --color-text-on-inverted: var(--color-white); + --color-text-brand: var(--color-indigo-600); + --color-text-brand-soft: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-brand) + ); + --color-text-on-brand-base: var(--color-white); + --color-text-error: var(--color-red-600); + --color-text-on-error-base: var(--color-white); + --color-text-warning: var(--color-yellow-600); + --color-text-on-warning-base: var(--color-white); + --color-text-success: var(--color-green-600); + --color-text-on-success-base: var(--color-white); + --color-text-disabled: var(--color-grey-300); + --color-text-on-disabled: var(--color-grey-200); + --color-text-bookmark-highlight: var(--color-text-error); + --color-text-favourite-highlight: var(--color-text-warning); + --color-text-on-media: var(--color-white); + --color-text-status-links: var(--color-text-brand); + + /* BACKGROUND TOKENS */ + + // Neutrals + --color-bg-primary: var(--color-white); + --overlay-strength-secondary: 5%; + --color-bg-secondary-base: var(--color-grey-600); + --color-bg-secondary: #{color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + )}; + --color-bg-secondary-solid: #{color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + )}; + --color-bg-tertiary: #{color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) + )}; + + // Utility + --color-bg-ambient: var(--color-bg-primary); + --color-bg-elevated: var(--color-bg-primary); + --color-bg-inverted: var(--color-grey-950); + --color-bg-media-base: var(--color-black); + --color-bg-media-strength: 65%; + --color-bg-media: #{utils.css-alpha( + var(--color-bg-media-base), + var(--color-bg-media-strength) + )}; + --color-bg-overlay: var(--color-bg-primary); + --color-bg-disabled: var(--color-grey-400); + + // Brand + --overlay-strength-brand: 8%; + --color-bg-brand-base: var(--color-indigo-600); + --color-bg-brand-base-hover: color-mix( + in oklab, + var(--color-bg-brand-base), + black var(--overlay-strength-brand) + ); + --color-bg-brand-soft: #{utils.css-alpha( + var(--color-bg-brand-base), + calc(var(--overlay-strength-brand) * 1.5) + )}; + --color-bg-brand-softer: #{utils.css-alpha( + var(--color-bg-brand-base), + var(--overlay-strength-brand) + )}; + + // Error + --overlay-strength-error: 12%; + --color-bg-error-base: var(--color-red-600); + --color-bg-error-base-hover: color-mix( + in oklab, + var(--color-bg-error-base), + black var(--overlay-strength-error) + ); + --color-bg-error-soft: #{utils.css-alpha( + var(--color-bg-error-base), + calc(var(--overlay-strength-error) * 1.5) + )}; + --color-bg-error-softer: #{utils.css-alpha( + var(--color-bg-error-base), + var(--overlay-strength-error) + )}; + + // Warning + --overlay-strength-warning: 10%; + --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base-hover: color-mix( + in oklab, + var(--color-bg-warning-base), + black var(--overlay-strength-warning) + ); + --color-bg-warning-soft: #{utils.css-alpha( + var(--color-bg-warning-base), + calc(var(--overlay-strength-warning) * 1.5) + )}; + --color-bg-warning-softer: #{utils.css-alpha( + var(--color-bg-warning-base), + var(--overlay-strength-warning) + )}; + + // Success + --overlay-strength-success: 15%; + --color-bg-success-base: var(--color-green-600); + --color-bg-success-base-hover: color-mix( + in oklab, + var(--color-bg-success-base), + black var(--overlay-strength-success) + ); + --color-bg-success-soft: #{utils.css-alpha( + var(--color-bg-success-base), + calc(var(--overlay-strength-success) * 1.5) + )}; + --color-bg-success-softer: #{utils.css-alpha( + var(--color-bg-success-base), + var(--overlay-strength-success) + )}; + + /* BORDER TOKENS */ + + --border-strength-primary: 15%; + --color-border-primary: color-mix( + in oklab, + var(--color-bg-primary), + var(--color-grey-950) var(--border-strength-primary) + ); + --color-border-media: rgb(252 248 255 / 15%); + --color-border-on-bg-secondary: var(--color-grey-200); + --color-border-on-bg-brand-softer: var(--color-indigo-200); + --color-border-on-bg-error-softer: #{utils.css-alpha( + var(--color-text-error), + 50% + )}; + --color-border-on-bg-warning-softer: #{utils.css-alpha( + var(--color-text-warning), + 50% + )}; + --color-border-on-bg-success-softer: #{utils.css-alpha( + var(--color-text-success), + 50% + )}; + --color-border-on-bg-inverted: var(--color-border-primary); + + /* SHADOW TOKENS */ + + --shadow-strength-primary: 30%; + --color-shadow-primary: #{utils.css-alpha( + var(--color-black), + var(--shadow-strength-primary) + )}; + --dropdown-shadow: + 0 20px 25px -5px var(--color-shadow-primary), + 0 8px 10px -6px var(--color-shadow-primary); + --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); + + /* GRAPHS/CHARTS TOKENS */ + + --color-graph-primary-stroke: var(--color-text-brand); + --color-graph-primary-fill: var(--color-bg-brand-softer); + --color-graph-warning-stroke: var(--color-text-warning); + --color-graph-warning-fill: var(--color-bg-warning-softer); + --color-graph-disabled-stroke: var(--color-text-disabled); + --color-graph-disabled-fill: var(--color-bg-disabled); + + /* LEGACY TOKENS */ + + --rich-text-container-color: rgb(255 216 231 / 100%); + --rich-text-text-color: rgb(114 47 83 / 100%); + --rich-text-decorations-color: rgb(255 175 212 / 100%); + + /* MISCELLANEOUS */ + + --outline-focus-default: 2px solid var(--color-text-brand); + --avatar-border-radius: 8px; +} diff --git a/app/javascript/styles_new/mastodon/_mixins.scss b/app/javascript/styles_new/mastodon/_mixins.scss new file mode 100644 index 00000000000..effbe82c3d8 --- /dev/null +++ b/app/javascript/styles_new/mastodon/_mixins.scss @@ -0,0 +1,45 @@ +@mixin search-input { + outline: 0; + box-sizing: border-box; + width: 100%; + box-shadow: none; + font-family: inherit; + background: var(--color-bg-secondary); + color: var(--color-text-primary); + border-radius: 4px; + border: 1px solid var(--color-border-on-bg-secondary); + font-size: 17px; + line-height: normal; + margin: 0; +} + +@mixin search-popout { + background: var(--color-bg-elevated); + border-radius: 4px; + padding: 10px 14px; + padding-bottom: 14px; + margin-top: 10px; + color: var(--color-text-secondary); + box-shadow: 2px 4px 15px var(--color-shadow-primary); + + h4 { + text-transform: uppercase; + color: var(--color-text-secondary); + font-size: 13px; + font-weight: 500; + margin-bottom: 10px; + } + + li { + padding: 4px 0; + } + + ul { + margin-bottom: 10px; + } + + em { + font-weight: 500; + color: var(--color-text-primary); + } +} diff --git a/app/javascript/styles_new/mastodon/_theme_utils.scss b/app/javascript/styles_new/mastodon/_theme_utils.scss new file mode 100644 index 00000000000..1c03c2f65cd --- /dev/null +++ b/app/javascript/styles_new/mastodon/_theme_utils.scss @@ -0,0 +1,3 @@ +@function css-alpha($base-color, $amount) { + @return #{rgb(from $base-color r g b / $amount)}; +} diff --git a/app/javascript/styles_new/mastodon/_variables.scss b/app/javascript/styles_new/mastodon/_variables.scss new file mode 100644 index 00000000000..a948dbc41c5 --- /dev/null +++ b/app/javascript/styles_new/mastodon/_variables.scss @@ -0,0 +1,27 @@ +// Keep this filter a SCSS variable rather than +// a CSS Custom Property due to this Safari bug: +// https://github.com/mdn/browser-compat-data/issues/25914#issuecomment-2676190245 +$backdrop-blur-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); + +// Language codes that uses CJK fonts +$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; + +// Variables for components +$media-modal-media-max-width: 100%; + +// put margins on top and bottom of image to avoid the screen covered by image. +$media-modal-media-max-height: 80%; + +$no-gap-breakpoint: 1175px; +$mobile-menu-breakpoint: 760px; +$mobile-breakpoint: 630px; +$no-columns-breakpoint: 600px; + +$font-sans-serif: 'mastodon-font-sans-serif' !default; +$font-display: 'mastodon-font-display' !default; +$font-monospace: 'mastodon-font-monospace' !default; + +$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange' + 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' + 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on' + 'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default; diff --git a/app/javascript/styles_new/mastodon/about.scss b/app/javascript/styles_new/mastodon/about.scss new file mode 100644 index 00000000000..0bb2c8c9eb2 --- /dev/null +++ b/app/javascript/styles_new/mastodon/about.scss @@ -0,0 +1,130 @@ +@use 'variables' as *; + +$maximum-width: 1235px; +$fluid-breakpoint: $maximum-width + 20px; + +.container { + box-sizing: border-box; + max-width: $maximum-width; + margin: 0 auto; + position: relative; + + @media screen and (max-width: $fluid-breakpoint) { + width: 100%; + padding: 0 10px; + } +} + +.brand { + position: relative; + text-decoration: none; +} + +.rules-list { + font-size: 15px; + line-height: 22px; + counter-reset: list-counter; + + li { + position: relative; + border-bottom: 1px solid var(--color-border-primary); + padding: 1em 1.75em; + padding-inline-start: 3em; + font-weight: 500; + counter-increment: list-counter; + min-height: 4ch; + + button { + background: transparent; + border: 0; + padding: 0; + margin: 0; + text-align: start; + font: inherit; + + &:hover, + &:focus, + &:active { + background: transparent; + } + + &[aria-expanded='false'] .rules-list__hint { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + @supports (-webkit-line-clamp: 2) { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + } + } + } + + &::before { + content: counter(list-counter); + position: absolute; + inset-inline-start: 0; + top: 1em; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + border-radius: 50%; + width: 4ch; + height: 4ch; + font-weight: 500; + display: flex; + justify-content: center; + align-items: center; + } + + &:last-child { + border-bottom: 0; + } + } + + &__text { + color: var(--color-text-primary); + } + + &__hint { + font-size: 14px; + font-weight: 400; + color: var(--color-text-secondary); + } +} + +.rules-languages { + display: flex; + gap: 1rem; + align-items: center; + position: relative; + + > label { + font-size: 14px; + font-weight: 600; + color: var(--color-text-primary); + } + + select { + appearance: none; + box-sizing: border-box; + font-size: 14px; + color: var(--color-text-primary); + display: block; + width: 100%; + outline: 0; + font-family: inherit; + resize: vertical; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + padding-inline-start: 10px; + padding-inline-end: 30px; + height: 41px; + + @media screen and (width <= 600px) { + font-size: 16px; + } + } +} diff --git a/app/javascript/styles_new/mastodon/accessibility.scss b/app/javascript/styles_new/mastodon/accessibility.scss new file mode 100644 index 00000000000..7cd2d4eae39 --- /dev/null +++ b/app/javascript/styles_new/mastodon/accessibility.scss @@ -0,0 +1,13 @@ +@use 'variables' as *; + +%emoji-color-inversion { + filter: invert(1); +} + +.emojione { + @each $emoji in $emojis-requiring-inversion { + &[title=':#{$emoji}:'] { + @extend %emoji-color-inversion; + } + } +} diff --git a/app/javascript/styles_new/mastodon/accounts.scss b/app/javascript/styles_new/mastodon/accounts.scss new file mode 100644 index 00000000000..dab604be2ad --- /dev/null +++ b/app/javascript/styles_new/mastodon/accounts.scss @@ -0,0 +1,411 @@ +@use 'sass:color'; +@use 'variables' as *; + +.card { + & > a { + display: block; + text-decoration: none; + color: inherit; + overflow: hidden; + border-radius: 4px; + + &:hover, + &:active, + &:focus { + .card__bar { + background: var(--color-bg-brand-softer); + } + } + } + + &__img { + height: 130px; + position: relative; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-bottom: none; + + img { + display: block; + width: 100%; + height: 100%; + margin: 0; + object-fit: cover; + } + + @media screen and (width <= 600px) { + height: 200px; + } + } + + &__bar { + position: relative; + padding: 15px; + display: flex; + justify-content: flex-start; + align-items: center; + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-top: none; + + .avatar { + flex: 0 0 auto; + width: 48px; + height: 48px; + padding-top: 2px; + + img { + width: 100%; + height: 100%; + display: block; + margin: 0; + border-radius: 4px; + background: var(--color-bg-secondary); + object-fit: cover; + } + } + + .display-name { + margin-inline-start: 15px; + text-align: start; + + svg[data-hidden] { + display: none; + } + + strong { + font-size: 15px; + color: var(--color-text-primary); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + } + + span { + display: block; + font-size: 14px; + color: var(--color-text-secondary); + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + +.pagination { + padding: 30px 0; + text-align: center; + overflow: hidden; + + a, + .current, + .newer, + .older, + .page, + .gap { + font-size: 14px; + color: var(--color-text-primary); + font-weight: 500; + display: inline-block; + padding: 6px 10px; + text-decoration: none; + } + + .current { + color: var(--color-bg-inverted); + background: var(--color-text-on-inverted); + border-radius: 100px; + cursor: default; + margin: 0 10px; + } + + .gap { + cursor: default; + } + + .older, + .newer { + text-transform: uppercase; + color: var(--color-text-primary); + } + + .older { + float: left; + padding-inline-start: 0; + } + + .newer { + float: right; + padding-inline-end: 0; + } + + .disabled { + cursor: default; + color: var(--color-text-disabled); + } + + @media screen and (width <= 700px) { + padding: 30px 20px; + + .page { + display: none; + } + + .newer, + .older { + display: inline-block; + } + } +} + +.nothing-here { + color: var(--color-text-secondary); + background: var(--color-bg-primary); + font-size: 14px; + font-weight: 500; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + cursor: default; + border-radius: 4px; + padding: 20px; + min-height: 30vh; + border: 1px solid var(--color-border-primary); + + @media screen and (min-width: ($no-gap-breakpoint - 1)) { + border-top: 0; + } + + &--no-toolbar { + border-top: 1px solid var(--color-border-primary); + } + + &--under-tabs { + border-radius: 0 0 4px 4px; + } + + &--flexible { + box-sizing: border-box; + min-height: 100%; + } +} + +.information-badge, +.simple_form .overridden, +.simple_form .recommended, +.simple_form .not_recommended { + display: inline-block; + padding: 4px 6px; + cursor: default; + border-radius: 4px; + font-size: 12px; + line-height: 12px; + font-weight: 500; + color: var(--color-text-primary); + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.information-badge, +.simple_form .overridden, +.simple_form .recommended, +.simple_form .not_recommended { + background-color: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); +} + +.information-badge { + &.superapp { + color: var(--color-text-success); + background-color: var(--color-bg-success-softer); + border-color: var(--color-border-on-bg-success-softer); + } +} + +.account-role { + display: inline-flex; + padding: 4px; + padding-inline-end: 8px; + border: 1px solid var(--color-text-brand); + color: var(--color-text-brand); + font-weight: 500; + font-size: 12px; + letter-spacing: 0.5px; + line-height: 16px; + gap: 4px; + border-radius: 6px; + align-items: center; + + svg { + width: auto; + height: 15px; + opacity: 0.85; + fill: currentColor; + } + + &__domain { + font-weight: 400; + opacity: 0.75; + letter-spacing: 0; + } +} + +.simple_form .not_recommended { + color: var(--color-text-error); + background-color: var(--color-bg-error-softer); + border-color: var(--color-border-on-bg-error-softer); +} + +.account__header__fields { + max-width: 100vw; + padding: 0; + margin: 15px -15px -15px; + border: 0 none; + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + font-size: 14px; + line-height: 20px; + + dl { + display: flex; + border-bottom: 1px solid var(--color-border-primary); + } + + dt, + dd { + box-sizing: border-box; + padding: 14px; + text-align: center; + max-height: 48px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + dt { + font-weight: 500; + width: 120px; + flex: 0 0 auto; + color: var(--color-text-primary); + background: var(--color-bg-secondary); + } + + dd { + flex: 1 1 auto; + color: var(--color-text-secondary); + } + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + + .verified { + border: 1px solid var(--color-border-on-bg-success-softer); + background: var(--color-bg-success-softer); + + a { + color: var(--color-text-success); + font-weight: 500; + } + + &__mark { + color: var(--color-text-success); + } + } + + dl:last-child { + border-bottom: 0; + } +} + +.directory__tag .trends__item__current { + width: auto; +} + +.pending-account { + &__header { + color: var(--color-text-secondary); + + a { + color: var(--color-text-primary); + text-decoration: none; + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } + + strong { + color: var(--color-text-primary); + font-weight: 700; + } + + .warning-hint { + font-weight: normal !important; + } + } + + &__body { + margin-top: 10px; + } +} + +.batch-table__row--muted { + color: var(--color-text-tertiary); +} + +.batch-table__row--muted .pending-account__header, +.batch-table__row--muted .accounts-table, +.batch-table__row--muted .name-tag { + &, + a, + strong { + color: var(--color-text-tertiary); + } +} + +.batch-table__row--muted .name-tag .avatar { + opacity: 0.5; +} + +.batch-table__row--muted .accounts-table { + tbody td.accounts-table__extra, + &__count, + &__count small { + color: var(--color-text-tertiary); + } +} + +.batch-table__row--attention { + color: var(--color-text-warning); +} + +.batch-table__row--attention .pending-account__header, +.batch-table__row--attention .accounts-table, +.batch-table__row--attention .name-tag { + &, + a, + strong { + color: var(--color-text-warning); + } +} + +.batch-table__row--attention .accounts-table { + tbody td.accounts-table__extra, + &__count, + &__count small { + color: var(--color-text-warning); + } +} diff --git a/app/javascript/styles_new/mastodon/admin.scss b/app/javascript/styles_new/mastodon/admin.scss new file mode 100644 index 00000000000..d0cd3c51374 --- /dev/null +++ b/app/javascript/styles_new/mastodon/admin.scss @@ -0,0 +1,2173 @@ +@use 'sass:color'; +@use 'sass:math'; +@use 'variables' as *; + +$no-columns-breakpoint: 890px; +$sidebar-width: 300px; +$content-width: 840px; + +.admin-wrapper { + display: flex; + justify-content: center; + box-sizing: border-box; + width: 100%; + min-height: 100vh; + min-height: 100dvh; + padding: env(safe-area-inset-top) env(safe-area-inset-right) + env(safe-area-inset-bottom) env(safe-area-inset-left); + + .icon { + width: 16px; + height: 16px; + vertical-align: top; + margin: 0 2px; + } + + .sidebar-wrapper { + min-height: 100vh; + min-height: 100dvh; + overflow: hidden; + pointer-events: none; + flex: 1 1 auto; + + &__inner { + display: flex; + justify-content: flex-end; + height: 100%; + } + } + + .sidebar { + width: $sidebar-width; + padding: 0; + pointer-events: auto; + + &__toggle { + display: none; + background: var(--color-bg-primary); + border-bottom: 1px solid var(--color-border-primary); + align-items: center; + + &__logo { + flex: 1 1 auto; + + a { + display: block; + padding: 15px; + } + } + + &__icon { + display: block; + color: var(--color-text-secondary); + text-decoration: none; + flex: 0 0 auto; + font-size: 18px; + padding: 10px; + margin: 5px 10px; + border-radius: 4px; + + &:focus { + background: var(--color-bg-brand-softer); + } + + .material-close { + display: none; + } + + &.active { + .material-close { + display: block; + } + + .material-menu { + display: none; + } + } + } + } + + .logo { + display: block; + margin: 40px auto; + width: 100px; + height: 100px; + } + + .logo--wordmark { + display: inherit; + margin: inherit; + width: inherit; + height: 25px; + } + + @media screen and (max-width: $no-columns-breakpoint) { + & > a:first-child { + display: none; + } + } + + ul { + list-style: none; + overflow: hidden; + margin-bottom: 20px; + + @media screen and (max-width: $no-columns-breakpoint) { + margin-bottom: 0; + } + + a { + font-size: 14px; + display: flex; + align-items: center; + gap: 6px; + padding: 15px; + color: var(--color-text-secondary); + text-decoration: none; + transition: all 200ms linear; + transition-property: color, background-color; + + &:hover { + color: var(--color-text-primary); + transition: all 100ms linear; + transition-property: color, background-color; + } + } + + ul { + margin: 0; + + a { + border: 0; + padding: 15px 35px; + } + } + + .warning a { + color: var(--color-text-warning); + font-weight: 700; + } + + .simple-navigation-active-leaf a { + color: var(--color-text-brand); + border-bottom: 0; + } + } + } + + .content-wrapper { + box-sizing: border-box; + width: 100%; + max-width: $content-width; + flex: 1 1 auto; + } + + @media screen and (max-width: ($content-width + $sidebar-width)) { + .sidebar-wrapper--empty { + display: none; + } + + .sidebar-wrapper { + width: $sidebar-width; + flex: 0 0 auto; + } + } + + @media screen and (max-width: $no-columns-breakpoint) { + .sidebar-wrapper { + width: 100%; + } + } + + .content { + padding-top: 55px; + padding-bottom: 20px; + padding-inline-start: 25px; + padding-inline-end: 15px; + + @media screen and (max-width: $no-columns-breakpoint) { + max-width: none; + padding: 15px; + padding-top: 30px; + } + + &__heading { + margin-bottom: 45px; + + &__row { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + margin-top: -15px; + margin-inline-end: -15px; + + & > * { + margin-top: 15px; + margin-inline-end: 15px; + } + } + + &__tabs { + margin-top: 30px; + width: 100%; + + & > div { + display: flex; + flex-wrap: wrap; + gap: 5px; + } + + a { + font-size: 14px; + display: inline-flex; + align-items: center; + padding: 7px 10px; + border-radius: 4px; + color: var(--color-text-secondary); + text-decoration: none; + font-weight: 500; + gap: 5px; + white-space: nowrap; + + @media screen and (max-width: $mobile-breakpoint) { + flex: 1 0 50%; + } + + &:hover, + &:focus, + &:active { + background: var(--color-bg-secondary); + } + + &.selected { + font-weight: 700; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + } + } + } + + &__actions { + display: inline-flex; + flex-flow: wrap; + gap: 5px; + align-items: center; + + .time-period { + padding: 0 10px; + } + + .back-link { + margin-bottom: 0; + } + } + + h2 small { + font-size: 12px; + display: block; + font-weight: 500; + color: var(--color-text-secondary); + line-height: 18px; + } + + @media screen and (max-width: $no-columns-breakpoint) { + border-bottom: 0; + padding-bottom: 0; + } + } + + h2 { + color: var(--color-text-primary); + font-size: 24px; + line-height: 36px; + font-weight: 700; + } + + h3 { + color: var(--color-text-primary); + font-size: 20px; + line-height: 28px; + font-weight: 400; + margin-bottom: 30px; + } + + h4 { + text-transform: uppercase; + font-size: 13px; + font-weight: 700; + color: var(--color-text-secondary); + padding-top: 24px; + margin-bottom: 8px; + border-top: 1px solid var(--color-border-primary); + } + + h6 { + font-size: 16px; + color: var(--color-text-primary); + line-height: 28px; + font-weight: 500; + } + + .fields-group h6 { + color: var(--color-text-primary); + font-weight: 500; + } + + .directory__tag h4 { + font-size: 18px; + font-weight: 700; + color: var(--color-text-primary); + text-transform: none; + padding-top: 0; + margin-bottom: 0; + border-top: 0; + + .comment { + display: block; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 4px; + + &.private-comment { + display: block; + color: var(--color-text-secondary); + } + + &.public-comment { + display: block; + color: var(--color-text-primary); + } + } + } + + & > p { + font-size: 14px; + line-height: 21px; + color: var(--color-text-primary); + margin-bottom: 20px; + + strong { + color: var(--color-text-primary); + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + } + + hr { + width: 100%; + height: 0; + border: 0; + border-bottom: 1px solid var(--color-border-primary); + margin: 20px 0; + + &.spacer { + height: 1px; + border: 0; + } + } + } + + @media screen and (max-width: $no-columns-breakpoint) { + display: block; + + .sidebar-wrapper { + min-height: 0; + } + + .sidebar { + width: 100%; + padding: 0; + height: auto; + + &__toggle { + display: flex; + } + + & > ul { + display: none; + + &.visible { + display: block; + position: fixed; + z-index: 10; + width: 100%; + height: calc(100% - 56px); + inset-inline-start: 0; + bottom: 0; + overflow-y: auto; + background: var(--color-bg-primary); + } + } + + ul a, + ul ul a { + font-size: 16px; + border-radius: 0; + transition: none; + + &:hover { + transition: none; + } + } + + ul ul { + border-radius: 0; + } + + ul .simple-navigation-active-leaf a { + border-bottom-color: var(--color-text-brand); + } + } + } +} + +hr.spacer { + width: 100%; + border: 0; + margin: 20px 0; + height: 1px; +} + +body, +.admin-wrapper .content { + .muted-hint { + color: var(--color-text-secondary); + + a { + color: var(--color-text-brand); + } + } + + .positive-hint, + .negative-hint, + .neutral-hint { + a { + color: inherit; + text-decoration: underline; + + &:focus, + &:hover, + &:active { + text-decoration: none; + } + } + } + + .positive-hint { + color: var(--color-text-success); + font-weight: 500; + } + + .negative-hint { + color: var(--color-text-error); + font-weight: 500; + } + + .neutral-hint { + color: var(--color-text-primary); + font-weight: 500; + } + + .warning-hint { + color: var(--color-text-warning); + font-weight: 500; + } +} + +kbd { + font-family: Courier, monospace; + background-color: var(--color-bg-brand-softer); + padding: 4px; + padding-bottom: 2px; + border-radius: 5px; +} + +.filters { + display: flex; + flex-wrap: wrap; + gap: 40px; + + .filter-subset { + flex: 0 0 auto; + margin-bottom: 20px; + + &:last-child { + margin-bottom: 30px; + } + + ul { + margin-top: 5px; + list-style: none; + + li { + display: inline-block; + margin-inline-end: 5px; + } + } + + & > div { + display: flex; + gap: 5px; + } + + strong { + font-weight: 500; + text-transform: uppercase; + font-size: 12px; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + &--with-select strong { + display: block; + margin-bottom: 10px; + } + + a { + display: inline-block; + color: var(--color-text-secondary); + text-decoration: none; + text-transform: uppercase; + font-size: 12px; + font-weight: 500; + border-bottom: 2px solid var(--color-bg-secondary); + + &:hover { + color: var(--color-text-primary); + border-bottom: 2px solid var(--color-bg-tertiary); + } + + &.selected { + color: var(--color-text-brand); + border-bottom: 2px solid var(--color-text-brand); + } + } + } +} + +.report-accounts { + display: flex; + flex-wrap: wrap; + margin-bottom: 20px; +} + +.report-accounts__item { + display: flex; + flex: 250px; + flex-direction: column; + margin: 0 5px; + + & > strong { + display: block; + margin: 0 0 10px -5px; + font-weight: 500; + font-size: 14px; + line-height: 18px; + color: var(--color-text-primary); + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + .account-card { + flex: 1 1 auto; + } +} + +.report-status, +.account-status { + display: flex; + margin-bottom: 10px; +} + +.report-status__actions, +.account-status__actions { + flex: 0 0 auto; + display: flex; + flex-direction: column; + + .icon-button { + font-size: 24px; + width: 24px; + text-align: center; + margin-bottom: 10px; + } +} + +.simple_form.new_report_note, +.simple_form.new_account_moderation_note { + max-width: 100%; +} + +.batch-form-box { + display: flex; + flex-wrap: wrap; + margin-bottom: 5px; + + #form_status_batch_action { + margin: 0 5px 5px 0; + font-size: 14px; + } + + input.button { + margin: 0 5px 5px 0; + } +} + +.back-link { + margin-bottom: 10px; + font-size: 14px; + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +.special-action-button, +.back-link { + text-align: end; + flex: 1 1 auto; +} + +.action-buttons { + display: flex; + overflow: hidden; + justify-content: space-between; +} + +.spacer { + flex: 1 1 auto; +} + +.log-entry { + display: block; + line-height: 20px; + padding: 15px; + padding-inline-start: 15px * 2 + 40px; + background: var(--color-bg-primary); + border-right: 1px solid var(--color-border-primary); + border-left: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + position: relative; + text-decoration: none; + color: var(--color-text-secondary); + font-size: 14px; + + &:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-top: 1px solid var(--color-border-primary); + } + + &:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: 1px solid var(--color-border-primary); + } + + &__avatar { + position: absolute; + inset-inline-start: 15px; + top: 15px; + + .avatar { + border-radius: var(--avatar-border-radius); + width: 40px; + height: 40px; + } + } + + &__title { + overflow-wrap: break-word; + } + + &__timestamp { + color: var(--color-text-tertiary); + } + + a, + .username, + .target { + color: var(--color-text-primary); + text-decoration: none; + font-weight: 500; + } + + a { + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } +} + +.strike-entry { + display: block; + line-height: 20px; + padding: 15px; + padding-inline-start: 15px * 2 + 40px; + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + position: relative; + text-decoration: none; + color: var(--color-text-secondary); + font-size: 14px; + margin-bottom: 15px; + + &__avatar { + position: absolute; + inset-inline-start: 15px; + top: 15px; + + .avatar { + border-radius: var(--avatar-border-radius); + width: 40px; + height: 40px; + } + } + + &__title { + overflow-wrap: break-word; + } + + &__timestamp { + color: var(--color-text-secondary); + } + + &:hover, + &:focus, + &:active { + background: var(--color-bg-primary); + } +} + +a.name-tag, +.name-tag, +a.inline-name-tag, +.inline-name-tag { + text-decoration: none; + color: var(--color-text-primary); + + &:hover { + color: var(--color-text-brand); + } + + .username { + font-weight: 500; + } + + &.suspended { + .username { + text-decoration: line-through; + color: var(--color-text-error); + } + + .avatar { + filter: grayscale(100%); + opacity: 0.8; + } + } +} + +a.name-tag, +.name-tag { + display: inline-flex; + align-items: center; + vertical-align: top; + + .avatar { + display: block; + margin: 0; + margin-inline-end: 5px; + border-radius: 50%; + } + + &.suspended { + .avatar { + filter: grayscale(100%); + opacity: 0.8; + } + } +} + +.speech-bubble { + margin-bottom: 20px; + border-inline-start: 4px solid var(--color-text-brand); + + &.positive { + border-color: var(--color-text-success); + } + + &.negative { + border-color: var(--color-text-error); + } + + &.warning { + border-color: var(--color-text-warning); + } + + &__bubble { + padding: 16px; + padding-inline-start: 14px; + font-size: 15px; + line-height: 20px; + border-radius: 4px 4px 4px 0; + position: relative; + font-weight: 500; + + a { + color: var(--color-text-secondary); + } + } + + &__owner { + padding: 8px; + padding-inline-start: 12px; + } + + time { + color: var(--color-text-tertiary); + } +} + +.report-card { + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + margin-bottom: 20px; + + &__profile { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px; + + .account { + padding: 0; + border: none; + + &__avatar-wrapper { + margin-inline-start: 0; + } + } + + &__stats { + flex: 0 0 auto; + font-weight: 500; + color: var(--color-text-secondary); + text-transform: uppercase; + text-align: end; + + a { + color: inherit; + text-decoration: none; + + &:focus, + &:hover, + &:active { + color: var(--color-text-brand); + } + } + + .red { + color: var(--color-text-error); + } + } + } + + &__summary { + &__item { + display: flex; + justify-content: flex-start; + border-top: 1px solid var(--color-border-primary); + + &__reported-by, + &__assigned { + padding: 15px; + flex: 0 0 auto; + box-sizing: border-box; + width: 150px; + color: var(--color-text-secondary); + + &, + .username { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + &__content { + flex: 1 1 auto; + max-width: calc(100% - 300px); + + &__icon { + margin-inline-end: 4px; + font-weight: 500; + } + } + + &__content a { + display: block; + box-sizing: border-box; + width: 100%; + padding: 15px; + text-decoration: none; + color: var(--color-text-secondary); + + &:hover { + color: var(--color-text-brand); + } + } + } + } +} + +.one-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ellipsized-ip { + display: inline-block; + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; +} + +.admin-account-bio { + display: flex; + flex-wrap: wrap; + margin: 0 -5px; + margin-top: 20px; + + > div { + box-sizing: border-box; + padding: 0 5px; + margin-bottom: 10px; + flex: 1 0 50%; + max-width: 100%; + } + + .account__header__fields, + .account__header__content { + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + height: 100%; + } + + .account__header__fields { + margin: 0; + border: 1px solid var(--color-border-primary); + + a { + color: var(--color-text-brand); + } + + dl:first-child .verified { + border-radius: 0 4px 0 0; + } + + .verified a { + color: var(--color-text-success); + } + } + + .account__header__content { + box-sizing: border-box; + padding: 20px; + color: var(--color-text-primary); + } +} + +.center-text { + text-align: center; +} + +.applications-list__item, +.filters-list__item { + padding: 15px 0; + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + margin-top: 15px; +} + +.applications-list { + .icon { + vertical-align: middle; + } +} + +.announcements-list, +.filters-list { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + border-bottom: none; + + &__item { + padding: 15px 0; + border-bottom: 1px solid var(--color-border-primary); + + &__title { + padding: 0 15px; + display: block; + font-weight: 500; + font-size: 18px; + line-height: 1.5; + color: var(--color-text-primary); + text-decoration: none; + margin-bottom: 10px; + + &:hover { + color: var(--color-text-brand); + } + + .account-role { + vertical-align: middle; + } + } + + .icon { + vertical-align: middle; + } + + a.announcements-list__item__title { + &:hover, + &:focus, + &:active { + color: var(--color-text-primary); + } + } + + &__action-bar { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + + &:not(.no-wrap) { + flex-wrap: wrap; + } + } + + &__meta { + padding: 0 15px; + color: var(--color-text-tertiary); + + a { + color: inherit; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + } + + &__actions { + margin-inline-start: auto; + } + + &__permissions { + margin-top: 10px; + } + } +} + +.filters-list__item { + &__title { + display: flex; + justify-content: space-between; + margin-bottom: 0; + overflow-wrap: anywhere; + } + + &__permissions { + margin-top: 0; + margin-bottom: 10px; + } + + .expiration { + font-size: 13px; + } + + &.expired { + .expiration { + color: var(--color-text-error); + } + + .permissions-list__item__icon { + color: var(--color-text-secondary); + } + } +} + +.rule-actions { + display: flex; + flex-direction: column; + + a.table-action-link { + padding-inline-start: 0; + } +} + +.dashboard__counters.admin-account-counters { + margin-top: 10px; +} + +.account-badges { + margin: -2px 0; +} + +.retention { + overflow: auto; + + > h4 { + position: sticky; + inset-inline-start: 0; + } + + &__table { + &__number { + color: var(--color-bg-primary); + padding: 10px; + } + + &__date { + white-space: nowrap; + padding: 10px 0; + text-align: start; + min-width: 120px; + + &.retention__table__average { + font-weight: 700; + } + } + + &__size { + text-align: center; + padding: 10px; + } + + &__label { + font-weight: 700; + color: var(--color-text-secondary); + } + + &__box { + box-sizing: border-box; + width: 52px; + margin: 1px; + padding: 10px; + font-weight: 500; + color: var(--color-text-primary); + background: var(--color-bg-primary); + + @for $i from 0 through 10 { + &--#{10 * $i} { + @if $i > 5 { + color: var(--color-text-on-brand-base); + } + + background-color: rgb( + from var(--color-bg-brand-base) r g b / #{math.div(max(1, $i), 10)} + ); + } + } + } + } +} + +.sparkline { + display: block; + text-decoration: none; + background: var(--color-bg-primary); + border-radius: 4px; + border: 1px solid var(--color-border-primary); + padding: 0; + position: relative; + padding-bottom: 55px + 20px; + overflow: hidden; + + &__value { + display: flex; + line-height: 33px; + align-items: flex-end; + padding: 20px; + padding-bottom: 10px; + + &__total { + display: block; + margin-inline-end: 10px; + font-weight: 500; + font-size: 28px; + color: var(--color-text-primary); + } + + &__change { + display: block; + font-weight: 500; + font-size: 18px; + color: var(--color-text-secondary); + margin-bottom: -3px; + + &.positive { + color: var(--color-text-success); + } + + &.negative { + color: var(--color-text-error); + } + } + } + + &__label { + padding: 0 20px; + padding-bottom: 10px; + text-transform: uppercase; + color: var(--color-text-secondary); + font-weight: 500; + } + + &__graph { + position: absolute; + bottom: 0; + width: 100%; + + svg { + display: block; + margin: 0; + } + + path:first-child { + fill: var(--color-graph-primary-fill) !important; + fill-opacity: 1 !important; + } + + path:last-child { + stroke: var(--color-graph-primary-stroke) !important; + fill: none !important; + } + } +} + +a.sparkline { + &:hover, + &:focus, + &:active { + background: var(--color-bg-brand-softer); + } +} + +.skeleton { + background-color: var(--color-bg-primary); + background-image: linear-gradient( + 90deg, + var(--color-bg-primary), + var(--color-bg-secondary), + var(--color-bg-primary) + ); + background-size: 200px 100%; + background-repeat: no-repeat; + border-radius: 4px; + display: inline-block; + line-height: 1; + width: 100%; + animation: skeleton 1.2s ease-in-out infinite; + + .reduce-motion & { + animation: none; + } +} + +@keyframes skeleton { + 0% { + background-position: -200px 0; + } + + 100% { + background-position: calc(200px + 100%) 0; + } +} + +.dimension { + table { + width: 100%; + } + + &__item { + border-bottom: 1px solid var(--color-border-primary); + + &__key { + font-weight: 500; + padding: 11px 10px; + } + + &__value { + text-align: end; + color: var(--color-text-secondary); + padding: 11px 10px; + } + + &__indicator { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-text-brand); + margin-inline-end: 10px; + + @for $i from 0 through 10 { + &--#{10 * $i} { + background-color: rgb( + from var(--color-text-brand) r g b / #{math.div(max(1, $i), 10)} + ); + } + } + } + + &:last-child { + border-bottom: 0; + } + + &.negative { + color: var(--color-text-error); + font-weight: 700; + + .dimension__item__value { + color: var(--color-text-error); + } + } + } +} + +.report-reason-selector { + border-radius: 4px; + background: var(--color-bg-primary); + margin-bottom: 20px; + + &__category { + cursor: pointer; + border-bottom: 1px solid var(--color-border-primary); + + &__label { + padding: 15px; + display: flex; + gap: 8px; + align-items: center; + } + + &__rules { + margin-inline-start: 30px; + } + } + + &__rule { + cursor: pointer; + padding: 15px; + display: flex; + gap: 8px; + align-items: center; + } +} + +.report-header { + display: grid; + gap: 15px; + grid-template-columns: minmax(0, 1fr) 300px; + + &__details { + &__item { + border-bottom: 1px solid var(--color-border-primary); + padding: 15px 0; + + &:last-child { + border-bottom: 0; + } + + &__header { + font-weight: 600; + padding: 4px 0; + } + } + + &--horizontal { + display: grid; + grid-auto-columns: minmax(0, 1fr); + grid-auto-flow: column; + + .report-header__details__item { + border-bottom: 0; + } + } + } + + @media screen and (width <= 930px) { + grid-template-columns: minmax(0, 1fr); + } +} + +.account-card { + border-radius: 4px; + border: 1px solid var(--color-border-primary); + position: relative; + + &__warning-badge { + position: absolute; + padding: 4px 10px; + top: 10px; + inset-inline-start: 10px; + border-radius: 4px; + background: + url('@/images/warning-stripes.svg') repeat-y left, + url('@/images/warning-stripes.svg') repeat-y right, + var(--color-bg-primary); + } + + &__permalink { + color: inherit; + text-decoration: none; + } + + &__header { + padding: 4px; + border-radius: 4px; + height: 128px; + + img { + display: block; + margin: 0; + width: 100%; + height: 100%; + object-fit: cover; + background: var(--color-bg-secondary); + } + } + + &__title { + margin-top: -(15px + 8px); + display: flex; + align-items: flex-end; + + &__avatar { + padding: 14px; + + img, + .account__avatar { + display: block; + margin: 0; + width: 56px; + height: 56px; + background-color: var(--color-bg-secondary); + border-radius: 8px; + border: 1px solid var(--color-border-media); + } + } + + .display-name { + color: var(--color-text-secondary); + padding-bottom: 15px; + font-size: 15px; + line-height: 20px; + + bdi { + display: block; + color: var(--color-text-primary); + font-weight: 700; + } + } + } + + &__bio { + padding: 0 15px; + margin: 8px 0; + overflow: hidden; + text-overflow: ellipsis; + overflow-wrap: break-word; + max-height: 21px * 2; + position: relative; + font-size: 15px; + line-height: 21px; + + &::after { + display: block; + content: ''; + width: 50px; + height: 21px; + position: absolute; + bottom: 0; + inset-inline-end: 15px; + pointer-events: none; + } + + a { + color: var(--color-text-primary); + text-decoration: none; + unicode-bidi: isolate; + + &:hover { + text-decoration: underline; + } + + &.mention { + &:hover { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + } + } + + &__actions { + display: flex; + justify-content: space-between; + align-items: center; + + &__button { + flex-shrink: 1; + padding: 0 15px; + overflow: hidden; + + .button { + min-width: 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + max-width: 100%; + } + } + } + + &__counters { + flex: 1 1 auto; + display: grid; + grid-auto-columns: minmax(0, 1fr); + grid-auto-flow: column; + max-width: 340px; + min-width: 65px * 3; + + &__item { + padding: 15px 0; + text-align: center; + color: var(--color-text-primary); + font-weight: 600; + font-size: 15px; + line-height: 21px; + + small { + display: block; + color: var(--color-text-secondary); + font-weight: 400; + font-size: 13px; + line-height: 18px; + } + } + } +} + +.report-notes { + margin-bottom: 20px; + + &__item { + background: var(--color-bg-primary); + position: relative; + padding: 15px; + padding-inline-start: 15px * 2 + 40px; + border: 1px solid var(--color-border-primary); + + &:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + + &:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } + + &__avatar { + position: absolute; + inset-inline-start: 15px; + top: 15px; + border-radius: var(--avatar-border-radius); + width: 40px; + height: 40px; + } + + &__header { + color: var(--color-text-secondary); + font-size: 15px; + line-height: 20px; + margin-bottom: 4px; + + .username { + color: var(--color-text-primary); + font-weight: 500; + margin-inline-end: 5px; + + a { + color: inherit; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + + a.timestamp { + color: var(--color-text-secondary); + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + + time { + margin-inline-start: 5px; + vertical-align: baseline; + } + } + + &__content { + font-size: 15px; + line-height: 20px; + overflow-wrap: break-word; + font-weight: 400; + color: var(--color-text-primary); + + p { + margin-bottom: 20px; + white-space: pre-wrap; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } + + &__actions { + position: absolute; + top: 15px; + inset-inline-end: 15px; + text-align: end; + } + } +} + +.report-actions { + &__item { + display: flex; + align-items: center; + line-height: 18px; + + &:last-child { + border-bottom: 0; + } + + &__button { + box-sizing: border-box; + flex: 0 0 auto; + width: 200px; + padding: 15px; + padding-inline-end: 0; + + .button { + display: block; + width: 100%; + } + } + + &__description { + padding: 15px; + font-size: 14px; + color: var(--color-text-tertiary); + } + } + + @media screen and (width <= 800px) { + border: 0; + + &__item { + flex-direction: column; + border: 0; + + &__button { + width: 100%; + padding: 15px 0; + } + + &__description { + padding: 0; + padding-bottom: 15px; + } + } + } +} + +.section-skip-link { + float: right; + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } +} + +.strike-card { + padding: 15px; + font-size: 15px; + line-height: 20px; + overflow-wrap: break-word; + font-weight: 400; + color: var(--color-text-primary); + box-sizing: border-box; + min-height: 100%; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + p { + margin-bottom: 20px; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + + strong { + font-weight: 700; + } + } + + &__rules { + list-style: disc; + padding-inline-start: 15px; + margin-bottom: 20px; + color: var(--color-text-secondary); + + &:last-child { + margin-bottom: 0; + } + + &__text { + color: var(--color-text-primary); + } + } + + &__statuses-list { + border-radius: 4px; + border: 1px solid var(--color-border-primary); + font-size: 13px; + line-height: 18px; + overflow: hidden; + + &__item { + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + + &__meta { + color: var(--color-text-secondary); + } + + a { + color: inherit; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + } +} + +.availability-indicator { + display: flex; + align-items: center; + margin-bottom: 30px; + font-size: 14px; + line-height: 21px; + + &__hint { + padding: 0 15px; + } + + &__graphic { + display: flex; + margin: 0 -2px; + + &__item { + display: block; + flex: 0 0 auto; + width: 4px; + height: 21px; + background: var(--color-bg-secondary); + margin: 0 2px; + border-radius: 2px; + + &.positive { + background: var(--color-bg-success-base); + } + + &.negative { + background: var(--color-bg-error-base); + } + } + } +} + +.history { + counter-reset: step 0; + font-size: 15px; + line-height: 22px; + + > li { + counter-increment: step 1; + padding-inline-start: 2.5rem; + padding-bottom: 8px; + position: relative; + margin-bottom: 8px; + + &::before { + position: absolute; + content: counter(step); + font-size: 0.625rem; + font-weight: 500; + inset-inline-start: 0; + display: flex; + justify-content: center; + align-items: center; + width: calc(1.375rem + 1px); + height: calc(1.375rem + 1px); + background: var(--color-bg-primary); + border: 1px solid var(--color-text-brand); + color: var(--color-text-brand); + border-radius: 8px; + } + + &::after { + position: absolute; + content: ''; + width: 1px; + background: var(--color-text-brand); + bottom: 0; + top: calc(1.875rem + 1px); + inset-inline-start: 0.6875rem; + } + + &:last-child { + margin-bottom: 0; + + &::after { + display: none; + } + } + } + + &__entry { + h5 { + font-weight: 500; + color: var(--color-text-primary); + line-height: 25px; + margin-bottom: 16px; + } + + .status { + border: 1px solid var(--color-border-primary); + background: var(--color-bg-secondary); + border-radius: 4px; + } + } +} + +.status__card { + padding: 15px; + border-radius: 4px; + font-size: 15px; + line-height: 20px; + overflow-wrap: break-word; + font-weight: 400; + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); + box-sizing: border-box; + min-height: 100%; + + &.status--has-quote { + .quote-inline { + display: none; + } + } + + .status__quote & { + // Remove the border from the .status__card within .status__quote + border: none; + + .display-name__account { + line-height: inherit; + } + + .status__avatar, + .status__avatar .account__avatar { + width: 32px; + height: 32px; + } + } + + .status__prepend { + padding: 0 0 15px; + gap: 4px; + align-items: center; + } + + > details { + summary { + display: block; + box-sizing: border-box; + color: var(--color-text-primary); + background: var(--color-bg-brand-softer); + border: 1px solid var(--color-border-on-bg-brand-softer); + border-radius: 8px; + padding: 8px 13px; + position: relative; + font-size: 15px; + line-height: 22px; + cursor: pointer; + + &::after { + content: attr(data-show, 'Show more'); + margin-top: 8px; + display: block; + font-size: 15px; + line-height: 20px; + color: var(--color-text-brand); + cursor: pointer; + border: 0; + background: transparent; + padding: 0; + text-decoration: none; + font-weight: 500; + } + + &:hover, + &:focus-visible { + &::after { + text-decoration: underline !important; + } + } + } + + &[open] summary { + margin-bottom: 16px; + + &::after { + content: attr(data-hide, 'Hide post'); + } + } + } + + .preview-card { + position: relative; + max-width: 566px; + + .status-card__image { + &--video { + aspect-ratio: 16 / 9; + } + + &--large { + aspect-ratio: 1.91 / 1; + } + + aspect-ratio: 1; + } + + .spoiler-button__overlay__label { + outline: 1px solid var(--color-border-media); + } + + .hide-button { + // Toggled to appear when the preview-card is unblurred: + display: none; + position: absolute; + top: 5px; + right: 5px; + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + outline: 1px solid var(--color-border-media); + border: 0; + padding: 3px 12px; + border-radius: 99px; + font-size: 14px; + font-weight: 700; + line-height: 20px; + + &:hover, + &:focus { + background-color: rgb(from var(--color-bg-media-base) r g b / 90%); + } + } + + &.preview-card--image-visible { + .hide-button { + display: block; + } + + .spoiler-button__overlay, + .status-card__image-preview { + display: none; + } + } + } + + .detailed-status__meta { + .detailed-status__application, + .detailed-status__datetime, + .detailed-status__link { + color: inherit; + } + } +} + +.admin { + &__terms-of-service { + &__container { + background: var(--color-bg-tertiary); + border-radius: 8px; + border: 1px solid var(--color-border-primary); + overflow: hidden; + + &__header { + padding: 16px; + font-size: 14px; + line-height: 20px; + color: var(--color-text-primary); + display: flex; + align-items: center; + gap: 12px; + } + + &__body { + background: var(--color-bg-primary); + padding: 16px; + overflow-y: scroll; + height: 30vh; + } + } + + &__history { + & > li { + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + } + + &__item { + padding: 16px 0; + padding-bottom: 8px; + + h5 { + font-size: 14px; + line-height: 20px; + font-weight: 600; + margin-bottom: 16px; + + a { + color: inherit; + text-decoration: none; + } + } + } + } + } +} + +.dot-indicator { + display: inline-flex; + align-items: center; + gap: 8px; + font-weight: 500; + + &__indicator { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-text-tertiary); + } + + &.success { + color: var(--color-text-success); + + .dot-indicator__indicator { + background-color: var(--color-bg-success-base); + } + } +} diff --git a/app/javascript/styles_new/mastodon/annual_reports.scss b/app/javascript/styles_new/mastodon/annual_reports.scss new file mode 100644 index 00000000000..a9b7e0ddee0 --- /dev/null +++ b/app/javascript/styles_new/mastodon/annual_reports.scss @@ -0,0 +1,342 @@ +@use 'variables' as *; + +:root { + --indigo-1: #17063b; + --indigo-2: #2f0c7a; + --indigo-3: #562cfc; + --indigo-5: #858afa; + --indigo-6: #cccfff; + --lime: #baff3b; + --goldenrod-2: #ffc954; +} + +.annual-report { + flex: 0 0 auto; + background: var(--indigo-1); + padding: 24px; + + &__header { + margin-bottom: 16px; + + h1 { + font-size: 25px; + font-weight: 600; + line-height: 30px; + color: var(--lime); + margin-bottom: 8px; + } + + p { + font-size: 16px; + font-weight: 600; + line-height: 20px; + color: var(--indigo-6); + } + } + + &__bento { + display: grid; + gap: 8px; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); + grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto) minmax( + 0, + auto + ); + + &__box { + padding: 16px; + border-radius: 8px; + background: var(--indigo-2); + color: var(--indigo-5); + } + } + + &__summary { + &__most-boosted-post { + grid-column: span 2; + grid-row: span 2; + padding: 0; + + .status__content, + .content-warning { + color: var(--indigo-6); + } + + .detailed-status { + border: 0; + } + + .content-warning { + border: 0; + background: var(--indigo-1); + + .link-button { + color: var(--indigo-5); + } + } + + .detailed-status__meta__line { + border-bottom-color: var(--indigo-3); + } + + .detailed-status__meta { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .detailed-status__meta, + .poll__footer, + .poll__link, + .detailed-status .logo, + .detailed-status__display-name { + color: var(--indigo-5); + } + + .detailed-status__meta .animated-number, + .detailed-status__display-name strong { + color: var(--indigo-6); + } + + .poll__chart { + background-color: var(--indigo-3); + + &.leading { + background-color: var(--goldenrod-2); + } + } + + .status-card, + .hashtag-bar { + display: none; + } + } + + &__followers { + grid-column: span 1; + text-align: center; + position: relative; + overflow: hidden; + padding-block-start: 24px; + padding-block-end: 24px; + + --sparkline-gradient-top: rgba(86, 44, 252, 50%); + --sparkline-gradient-bottom: rgba(86, 44, 252, 0%); + + &__foreground { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + position: relative; + z-index: 1; + } + + &__number { + font-size: 31px; + font-weight: 600; + line-height: 37px; + color: var(--lime); + } + + &__label { + font-size: 14px; + font-weight: 600; + line-height: 17px; + color: var(--indigo-6); + } + + &__footnote { + display: block; + font-weight: 400; + opacity: 0.5; + } + + svg { + position: absolute; + bottom: 0; + inset-inline-end: 0; + pointer-events: none; + z-index: 0; + height: 70%; + width: auto; + + path:first-child { + fill: url('#gradient') !important; + fill-opacity: 1 !important; + } + + path:last-child { + stroke: var(--color-graph-primary-stroke) !important; + fill: none !important; + } + } + } + + &__archetype { + grid-column: span 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 8px; + padding: 0; + + img { + display: block; + width: 100%; + height: auto; + border-radius: 8px; + } + + &__label { + padding: 16px; + padding-bottom: 8px; + font-size: 14px; + line-height: 17px; + font-weight: 600; + color: var(--lime); + } + } + + &__most-used-app { + grid-column: span 1; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + box-sizing: border-box; + + &__label { + font-size: 14px; + line-height: 17px; + font-weight: 600; + color: var(--indigo-6); + } + + &__icon { + font-size: 14px; + line-height: 17px; + font-weight: 600; + color: var(--goldenrod-2); + } + } + + &__percentile { + grid-row: span 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + text-align: center; + text-wrap: balance; + padding: 16px 8px; + + &__label { + font-size: 14px; + line-height: 17px; + } + + &__number { + font-size: 54px; + font-weight: 600; + line-height: 73px; + color: var(--goldenrod-2); + } + + &__footnote { + font-size: 11px; + line-height: 14px; + opacity: 0.5; + } + } + + &__new-posts { + grid-column: span 2; + text-align: center; + position: relative; + overflow: hidden; + + &__label { + font-size: 20px; + font-weight: 600; + line-height: 24px; + color: var(--indigo-6); + z-index: 1; + position: relative; + } + + &__number { + font-size: 76px; + font-weight: 600; + line-height: 91px; + color: var(--goldenrod-2); + z-index: 1; + position: relative; + } + + svg { + position: absolute; + inset-inline-start: -7px; + top: -4px; + z-index: 0; + } + } + + &__most-used-hashtag { + grid-column: span 2; + text-align: center; + overflow: hidden; + + &__hashtag { + font-size: 42px; + font-weight: 600; + line-height: 58px; + color: var(--indigo-6); + margin-inline-start: -100%; + margin-inline-end: -100%; + } + + &__label { + font-size: 14px; + font-weight: 600; + line-height: 17px; + } + } + } +} + +.annual-report-modal { + max-width: 600px; + background: var(--indigo-1); + border-radius: 16px; + display: flex; + flex-direction: column; + overflow-y: auto; + + .loading-indicator .circular-progress { + color: var(--lime); + } + + @media screen and (max-width: $no-columns-breakpoint) { + border-bottom: 0; + border-radius: 16px 16px 0 0; + } +} + +.notification-group--annual-report { + .notification-group__icon { + color: var(--lime); + } + + .notification-group__main .link-button { + font-weight: 500; + color: var(--lime); + } +} diff --git a/app/javascript/styles_new/mastodon/basics.scss b/app/javascript/styles_new/mastodon/basics.scss new file mode 100644 index 00000000000..6298409d157 --- /dev/null +++ b/app/javascript/styles_new/mastodon/basics.scss @@ -0,0 +1,300 @@ +@use 'variables' as *; + +html.has-modal { + &, + body { + touch-action: none; + overscroll-behavior: none; + -webkit-overflow-scrolling: auto; + scrollbar-gutter: stable; + } + + body { + overflow: hidden !important; + } +} + +body { + font-family: $font-sans-serif, sans-serif; + background: var(--color-bg-ambient); + font-size: 13px; + line-height: 18px; + font-weight: 400; + color: var(--color-text-primary); + text-rendering: optimizelegibility; + + // Disable kerning for Japanese text to preserve monospaced alignment for readability + &:not(:lang(ja)) { + font-feature-settings: 'kern'; + } + + text-size-adjust: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0%); + -webkit-tap-highlight-color: transparent; + + &.system-font { + // system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+) + // -apple-system => Safari <11 specific + // BlinkMacSystemFont => Chrome <56 on macOS specific + // Segoe UI => Windows 7/8/10 + // Oxygen => KDE + // Ubuntu => Unity/Ubuntu + // Cantarell => GNOME + // Fira Sans => Firefox OS + // Droid Sans => Older Androids (<4.0) + // Helvetica Neue => Older macOS <10.11 + // $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0) + font-family: + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + $font-sans-serif, + sans-serif; + } + + &.app-body { + padding: 0; + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + box-sizing: border-box; + + &.layout-single-column { + height: auto; + min-height: 100vh; + min-height: 100dvh; + overflow-y: scroll; + } + + &.layout-multiple-columns { + position: absolute; + width: 100%; + height: 100%; + padding-bottom: env(safe-area-inset-bottom); + } + } + + &.player { + padding: 0; + margin: 0; + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; + + & > div { + height: 100%; + } + + .video-player video { + width: 100%; + height: 100%; + max-height: 100vh; + } + + .media-gallery { + margin-top: 0; + height: 100% !important; + border-radius: 0; + } + + .media-gallery__item { + border-radius: 0; + } + } + + &.embed { + margin: 0; + padding-bottom: 0; + overflow: hidden; + } + + &.admin { + padding: 0; + background: var(--color-bg-primary); + } + + &.error { + position: absolute; + text-align: center; + width: 100%; + height: 100%; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + + .dialog { + vertical-align: middle; + margin: 20px; + + &__illustration { + img { + display: block; + max-width: 470px; + width: 100%; + height: auto; + margin-top: -120px; + margin-bottom: -45px; + } + } + + h1 { + font-size: 20px; + line-height: 28px; + font-weight: 400; + } + } + } +} + +a { + &:focus { + border-radius: 4px; + outline: var(--outline-focus-default); + } + + &:focus:not(:focus-visible) { + outline: none; + } +} + +button { + font-family: inherit; + cursor: pointer; + + &:focus:not(:focus-visible) { + outline: none; + } +} + +.app-holder { + &, + & > div, + & > noscript { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + outline: 0 !important; + } + + & > noscript { + min-height: 100vh; + min-height: 100dvh; + } +} + +.layout-single-column .app-holder { + &, + & > div { + min-height: 100vh; + min-height: 100dvh; + } +} + +.layout-multiple-columns .app-holder { + &, + & > div { + height: 100%; + } +} + +.error-boundary, +.app-holder noscript { + flex-direction: column; + font-size: 16px; + font-weight: 400; + line-height: 1.7; + color: var(--color-text-error); + text-align: center; + + & > div { + max-width: 500px; + } + + p { + margin-bottom: 0.85em; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--color-text-brand); + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + + &__footer { + color: var(--color-text-secondary); + font-size: 13px; + + a { + color: var(--color-text-secondary); + } + } + + button { + display: inline; + border: 0; + background: transparent; + color: var(--color-text-secondary); + font: inherit; + padding: 0; + margin: 0; + line-height: inherit; + cursor: pointer; + outline: 0; + transition: color 300ms linear; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + + &.copied { + color: var(--mas-status-success-color); + transition: none; + } + } +} + +.logo-resources { + // Not using display: none because of https://bugs.chromium.org/p/chromium/issues/detail?id=258029 + visibility: hidden; + user-select: none; + pointer-events: none; + width: 0; + height: 0; + overflow: hidden; + position: absolute; + top: 0; + inset-inline-start: 0; + z-index: -1000; +} + +// NoScript adds a __ns__pop2top class to the full ancestry of blocked elements, +// to set the z-index to a high value, which messes with modals and dropdowns. +// Blocked elements can in theory only be media and frames/embeds, so they +// should only appear in statuses, under divs and articles. +body, +div, +article { + .__ns__pop2top { + z-index: unset !important; + } +} diff --git a/app/javascript/styles_new/mastodon/branding.scss b/app/javascript/styles_new/mastodon/branding.scss new file mode 100644 index 00000000000..a7cc9c500e2 --- /dev/null +++ b/app/javascript/styles_new/mastodon/branding.scss @@ -0,0 +1,5 @@ +@use 'variables' as *; + +.logo { + color: var(--color-text-primary); +} diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss new file mode 100644 index 00000000000..970e3d128f2 --- /dev/null +++ b/app/javascript/styles_new/mastodon/components.scss @@ -0,0 +1,11433 @@ +@use 'sass:color'; +@use 'variables' as *; +@use 'mixins' as *; + +.app-body { + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.animated-number { + display: inline-flex; + flex-direction: column; + align-items: stretch; + overflow: hidden; + position: relative; +} + +.inline-alert { + color: var(--color-text-success); + font-weight: 400; + + .no-reduce-motion & { + transition: opacity 200ms ease; + } +} + +.link-button { + display: block; + font-size: 15px; + line-height: 20px; + color: var(--color-text-brand); + border: 0; + background: transparent; + padding: 0; + cursor: pointer; + text-decoration: none; + + &--destructive { + color: var(--color-text-error); + } + + &:hover, + &:active { + text-decoration: underline; + } + + &:disabled { + color: var(--color-text-primary); + cursor: default; + } + + &:focus-visible { + outline: var(--outline-focus-default); + } +} + +.help-button { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border: 0; + border-radius: 20px; + cursor: pointer; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + + &:active, + &:focus, + &:hover { + background: var(--color-bg-brand-base-hover); + } + + &:focus-visible { + outline: var(--outline-focus-default); + } + + .icon { + width: 14px; + height: 14px; + } +} + +.button { + background-color: var(--color-bg-brand-base); + border: 10px none; + border-radius: 4px; + box-sizing: border-box; + color: var(--color-text-on-brand-base); + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + font-family: inherit; + font-size: 15px; + font-weight: 500; + letter-spacing: 0; + line-height: 22px; + overflow: hidden; + padding: 7px 18px; + position: relative; + text-align: center; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + width: auto; + + &:active, + &:focus, + &:hover { + background-color: var(--color-bg-brand-base-hover); + } + + &:focus-visible { + outline: 2px solid var(--color-bg-brand-base); + outline-offset: 2px; + } + + &--compact { + font-size: 14px; + line-height: normal; + font-weight: 700; + padding: 5px 12px; + border-radius: 4px; + } + + &--dangerous { + background-color: var(--color-bg-error-base); + color: var(--color-text-on-error-base); + + &:active, + &:focus, + &:hover { + background-color: var(--color-bg-error-base-hover); + transition: none; + } + } + + &--destructive { + &:active, + &:focus, + &:hover { + color: var(--color-text-on-error-base); + background-color: var(--color-bg-error-base); + transition: none; + } + } + + &:disabled, + &.disabled { + color: var(--color-text-on-disabled); + background-color: var(--color-bg-disabled); + cursor: not-allowed; + } + + &.copyable { + transition: background 300ms linear; + } + + &.copied { + color: var(--color-text-on-success-base); + background-color: var(--color-bg-success-base); + transition: none; + } + + &.button-secondary { + color: var(--color-text-brand); + background: transparent; + padding: 6px 17px; + border: 1px solid var(--color-text-brand); + + &:active, + &:focus, + &:hover { + border-color: var(--color-text-brand); + color: var(--color-text-brand); + background-color: transparent; + text-decoration: none; + } + + &.button--destructive { + &:active, + &:focus, + &:hover { + border-color: var(--color-text-error); + color: var(--color-text-error); + } + } + + &:disabled, + &.disabled { + border-color: var(--color-text-disabled); + color: var(--color-text-disabled); + + &:active, + &:focus, + &:hover { + border-color: var(--color-text-disabled); + color: var(--color-text-disabled); + } + } + } + + &.button--plain { + color: var(--color-text-brand); + background: transparent; + padding: 6px; + + // The button has no outline, so we use negative margin to + // visually align its label with its surroundings while maintaining + // a generous click target + margin-inline: -6px; + border: 1px solid transparent; + + &:active, + &:focus, + &:hover { + border-color: transparent; + color: var(--color-text-brand-soft); + background-color: transparent; + text-decoration: none; + } + + &:disabled, + &.disabled { + opacity: 0.7; + border-color: transparent; + color: var(--color-text-disabled); + + &:active, + &:focus, + &:hover { + border-color: transparent; + color: var(--color-text-disabled); + } + } + } + + &.button--block { + width: 100%; + } + + &.loading { + cursor: wait; + + .button__label-wrapper { + // Hide the label only visually, so that + // it keeps its layout and accessibility + opacity: 0; + } + + .loading-indicator { + position: absolute; + inset: 0; + } + } + + .icon { + width: 18px; + height: 18px; + } +} + +.column__wrapper { + display: flex; + flex: 1 1 auto; + position: relative; +} + +.icon { + flex: 0 0 auto; + width: 24px; + height: 24px; + aspect-ratio: 1; + + path { + fill: currentColor; + } +} + +.icon-button { + --default-icon-color: var(--color-text-secondary); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-primary); + --hover-bg-color: var(--color-bg-brand-softer); + + display: inline-flex; + color: var(--default-icon-color); + border: 0; + padding: 0; + border-radius: 4px; + background: var(--default-bg-color); + cursor: pointer; + align-items: center; + justify-content: center; + text-decoration: none; + gap: 4px; + flex: 0 0 auto; + + a { + display: flex; + color: inherit; + text-decoration: none; + } + + &:hover, + &:active, + &:focus-visible { + color: var(--hover-icon-color); + background-color: var(--hover-bg-color); + } + + &:focus-visible { + outline: 2px solid var(--color-text-brand); + } + + &.disabled { + color: var(--color-text-disabled); + background-color: var(--default-bg-color); + cursor: default; + } + + &.inverted { + --default-icon-color: var(--color-text-primary); + --hover-icon-color: var(--color-text-secondary); + } + + &.active { + --default-icon-color: var(--color-text-brand); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: transparent; + } + + &.overlayed { + --default-icon-color: rgb(from var(--color-text-on-media) r g b / 70%); + --default-bg-color: var(--color-bg-media); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: rgb(from var(--color-bg-media-base) r g b / 90%); + + box-sizing: content-box; + backdrop-filter: $backdrop-blur-filter; + border-radius: 4px; + padding: 2px; + } + + &--with-counter { + padding-inline-end: 4px; + } + + &__counter { + display: block; + width: auto; + font-size: 12px; + font-weight: 500; + } + + &.copyable { + transition: all 300ms linear; + } + + &.copied { + color: var(--color-text-success); + transition: none; + background-color: var(--color-bg-success-softer); + border-color: var(--color-border-on-bg-brand-softer); + } +} + +body > [data-popper-placement] { + z-index: 9999; +} + +.invisible { + font-size: 0; + line-height: 0; + display: inline-block; + width: 0; + height: 0; + position: absolute; + + img, + svg { + margin: 0 !important; + border: 0 !important; + padding: 0 !important; + width: 0 !important; + height: 0 !important; + } +} + +.ellipsis { + &::after { + content: '…'; + } +} + +.autosuggest-textarea { + &__textarea { + background: transparent; + min-height: 100px; + padding-bottom: 0; + resize: none; + scrollbar-color: initial; + + &::-webkit-scrollbar { + all: unset; + } + } + + &__suggestions { + box-shadow: var(--dropdown-shadow); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); + border-radius: 0 0 4px 4px; + color: var(--color-text-primary); + font-size: 14px; + padding: 0; + + &__item { + box-sizing: border-box; + display: flex; + align-items: center; + height: 48px; + cursor: pointer; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + color: var(--color-text-primary); + + &:last-child { + border-radius: 0 0 4px 4px; + } + + &:hover, + &:focus, + &:active { + background: var(--color-bg-secondary); + + .autosuggest-account .display-name__account { + color: inherit; + } + } + + &.selected { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + + .autosuggest-account .display-name__account { + color: inherit; + } + } + } + } +} + +.autosuggest-account, +.autosuggest-emoji, +.autosuggest-hashtag { + flex: 1 0 0; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 12px; + padding: 8px 12px; + overflow: hidden; + text-overflow: ellipsis; +} + +.autosuggest-account { + .display-name { + font-weight: 400; + display: flex; + flex-direction: column; + flex: 1 0 0; + } + + .display-name__account { + display: block; + line-height: 16px; + font-size: 12px; + color: var(--color-text-secondary); + } +} + +.autosuggest-hashtag { + justify-content: space-between; + + &__name { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__uses { + flex: 0 0 auto; + text-align: end; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.autosuggest-emoji { + &__name { + flex: 1 0 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.autosuggest-account .account__avatar, +.autosuggest-emoji img { + display: block; + width: 24px; + height: 24px; + flex: 0 0 auto; +} + +.compose-form { + display: flex; + flex-direction: column; + gap: 32px; + + .layout-multiple-columns &, + .column & { + padding: 15px; + } + + &__highlightable { + display: flex; + flex-direction: column; + flex: 0 1 auto; + border-radius: 4px; + border: 1px solid var(--color-border-on-bg-secondary); + transition: border-color 300ms linear; + position: relative; + background: var(--color-bg-secondary); + + &.active { + transition: none; + border-color: var(--color-text-brand); + } + } + + &__warning { + color: var(--color-text-primary); + background: var(--color-bg-warning-softer); + border: 1px solid var(--color-border-on-bg-warning-softer); + padding: 8px 10px; + border-radius: 4px; + font-size: 13px; + font-weight: 400; + + strong { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + a { + color: var(--color-text-brand); + font-weight: 500; + text-decoration: underline; + + &:hover, + &:active, + &:focus { + text-decoration: none; + } + } + } + + .spoiler-input { + display: flex; + align-items: stretch; + + &__border { + background: url('@/images/warning-stripes.svg') repeat-y; + width: 5px; + flex: 0 0 auto; + } + + .autosuggest-input { + flex: 1 1 auto; + border: 1px solid var(--color-border-primary); + border-width: 1px 0; + } + } + + .autosuggest-textarea__textarea, + .spoiler-input__input { + display: block; + box-sizing: border-box; + width: 100%; + margin: 0; + color: var(--color-text-primary); + background: transparent; + font-family: inherit; + font-size: 14px; + padding: 12px; + line-height: normal; + border: 0; + outline: 0; + + &:focus { + outline: 0; + } + } + + .spoiler-input__input { + padding: 12px 12px - 5px; + background: var(--color-bg-brand-softer); + color: var(--color-text-brand); + } + + &__dropdowns { + display: flex; + align-items: center; + gap: 8px; + margin: 8px; + flex-wrap: wrap; + + & > div { + overflow: hidden; + display: flex; + } + } + + &__uploads { + padding: 0 12px; + aspect-ratio: 3/2; + flex-shrink: 0; + } + + .media-gallery { + gap: 8px; + } + + &__upload { + position: relative; + + &.draggable { + will-change: transform, opacity; + touch-action: none; + cursor: grab; + } + + &.dragging { + opacity: 0; + } + + &.overlay { + height: 100%; + border-radius: 8px; + pointer-events: none; + } + + &__actions { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 8px; + } + + &__preview, + &__visualizer { + position: absolute; + width: 100%; + height: 100%; + z-index: -1; + top: 0; + } + + &__preview { + border-radius: 6px; + inset-inline-start: 0; + } + + &__visualizer { + padding: 16px; + box-sizing: border-box; + + .audio-player__visualizer { + margin: 0 auto; + display: block; + height: 100%; + } + + .icon { + position: absolute; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + opacity: 0.75; + color: var(--player-foreground-color); + filter: var(--overlay-icon-shadow); + width: 48px; + height: 48px; + } + } + + &__thumbnail { + width: 100%; + height: 100%; + background-position: center; + background-size: cover; + background-repeat: no-repeat; + overflow: hidden; + } + + .icon-button { + flex: 0 0 auto; + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border-radius: 6px; + font-size: 12px; + line-height: 16px; + font-weight: 500; + padding: 4px 8px; + font-family: inherit; + + .icon { + width: 15px; + height: 15px; + } + } + + .icon-button.compose-form__upload__delete { + padding: 2px; + border-radius: 50%; + + .icon { + width: 20px; + height: 20px; + } + } + + &__warning { + position: absolute; + z-index: 2; + bottom: 0; + inset-inline-start: 0; + inset-inline-end: 0; + padding: 8px; + + .icon-button.active { + color: var(--color-text-on-warning-base); + background: var(--color-bg-warning-base); + } + } + } + + &__footer { + display: flex; + flex-direction: column; + gap: 12px; + padding: 12px; + } + + &__submit { + display: flex; + align-items: center; + flex: 1 1 auto; + max-width: 100%; + overflow: hidden; + } + + &__buttons { + display: flex; + gap: 8px; + align-items: center; + flex: 1 1 auto; + + & > div { + display: flex; + } + + .icon-button { + padding: 2px; + } + + .icon-button .icon { + width: 20px; + height: 20px; + } + } + + &__actions { + display: flex; + align-items: center; + flex: 0 0 auto; + gap: 12px; + flex-wrap: wrap; + + .icon-button { + box-sizing: content-box; + color: var(--color-text-brand); + + &:hover, + &:focus, + &:active { + color: var(--color-text-brand); + } + + &.disabled { + color: var(--color-text-disabled); + } + + &.active { + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + } + } + } + + &__poll { + margin-top: 8px; + display: flex; + flex-direction: column; + align-self: stretch; + gap: 8px; + + .poll__option { + padding: 0 12px; + gap: 8px; + + &.empty:not(:focus-within) { + opacity: 0.5; + } + } + + .poll__input { + width: 17px; + height: 17px; + border-color: var(--color-text-secondary); + } + + &__footer { + display: flex; + align-items: center; + gap: 16px; + padding-inline-start: 37px; + padding-inline-end: 40px; + + &__sep { + width: 1px; + height: 22px; + background: var(--color-border-primary); + flex: 0 0 auto; + } + } + + &__select { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1 1 auto; + min-width: 0; + + &__label { + flex: 0 0 auto; + font-size: 11px; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.5px; + color: var(--color-text-secondary); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + &__value { + flex: 0 0 auto; + appearance: none; + background: transparent; + border: none; + padding: 0; + font-size: 14px; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.1px; + color: var(--color-text-brand); + background-color: var(--color-bg-secondary-solid); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + + .status__quote { + margin: 0 8px; + max-height: 220px; + + // Override .status__content .status__content__text.status__content__text--visible + .status__content__text.status__content__text { + display: -webkit-box; + } + + .status__content__text { + -webkit-line-clamp: 4; + line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + } + } +} + +.dropdown-button { + display: flex; + align-items: center; + gap: 4px; + color: var(--color-text-brand); + background: transparent; + border: 1px solid var(--color-text-brand); + border-radius: 6px; + padding: 4px 8px; + font-size: 13px; + line-height: normal; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &[disabled] { + cursor: default; + color: var(--color-text-disabled); + border-color: var(--color-text-disabled); + } + + .icon { + width: 15px; + height: 15px; + flex: 0 0 auto; + } + + &__label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 1 auto; + } + + &.active { + color: var(--color-text-on-brand-base); + border-color: var(--color-bg-brand-base); + background: var(--color-bg-brand-base); + } + + &.warning { + color: var(--color-text-warning); + border-color: var(--color-text-warning); + + &.active { + color: var(--color-text-on-warning-base); + border-color: var(--color-bg-warning-base); + background-color: var(--color-bg-warning-base); + } + } +} + +.character-counter { + cursor: default; + font-family: $font-sans-serif, sans-serif; + font-size: 14px; + font-weight: 400; + line-height: normal; + color: var(--color-text-secondary); + flex: 1 0 auto; + text-align: end; + + &.character-counter--over { + color: var(--color-text-error); + } +} + +.no-reduce-motion .spoiler-input { + transition: + height 0.4s ease, + opacity 0.4s ease; +} + +.sign-in-banner { + padding: 10px; + + p { + font-size: 15px; + line-height: 22px; + color: var(--color-text-primary); + margin-bottom: 20px; + + strong { + font-weight: 700; + } + + a { + color: var(--color-text-primary); + text-decoration: none; + unicode-bidi: isolate; + + &:hover { + text-decoration: underline; + } + } + } + + .button { + margin-bottom: 10px; + } +} + +.emojione { + font-size: inherit; + vertical-align: middle; + object-fit: contain; + margin: -0.2ex 0.15em 0.2ex; + width: 16px; + height: 16px; + + img { + width: auto; + } +} + +.status__content--with-action { + cursor: pointer; +} + +.status__content { + clear: both; +} + +.status__content, +.edit-indicator__content, +.reply-indicator__content { + position: relative; + overflow-wrap: break-word; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + font-size: 15px; + line-height: 22px; + padding-top: 2px; + color: var(--color-text-primary); + + &:focus { + outline: 0; + } + + &.status__content--with-spoiler { + white-space: normal; + + .status__content__text { + white-space: pre-wrap; + } + } + + .emojione { + width: 20px; + height: 20px; + margin: -3px 0 0; + } + + p { + margin-bottom: 22px; + white-space: pre-wrap; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--color-text-status-links); + text-decoration: none; + unicode-bidi: isolate; + + &:hover { + text-decoration: underline; + } + + &.mention { + &:hover { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + } + + a.unhandled-link { + color: var(--color-text-brand); + } + + .status__content__text { + display: none; + + &.status__content__text--visible { + display: block; + } + } +} + +.reply-indicator { + display: grid; + grid-template-columns: 46px minmax(0, 1fr); + grid-template-rows: 46px max-content; + gap: 0 10px; + + .detailed-status__display-name { + margin-bottom: 4px; + } + + .detailed-status__display-avatar { + grid-column-start: 1; + grid-row-start: 1; + grid-row-end: span 1; + } + + &__main { + grid-column-start: 2; + grid-row-start: 1; + grid-row-end: span 2; + } + + .display-name { + font-size: 14px; + line-height: 16px; + + &__account { + display: none; + } + } + + &__line { + grid-column-start: 1; + grid-row-start: 2; + grid-row-end: span 1; + position: relative; + + &::before { + display: block; + content: ''; + position: absolute; + inset-inline-start: 50%; + top: 4px; + transform: translateX(-50%); + background: var(--color-border-primary); + width: 2px; + height: calc(100% + 32px - 8px); // Account for gap to next element + } + } + + &__content { + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + display: -webkit-box; + line-clamp: 4; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + padding: 0; + max-height: 4 * 20px; + overflow: hidden; + color: var(--color-text-secondary); + } + + &__attachments { + margin-top: 4px; + color: var(--color-text-secondary); + font-size: 12px; + line-height: 16px; + display: flex; + align-items: center; + gap: 4px; + + .icon { + width: 18px; + height: 18px; + } + } +} + +.edit-indicator { + border-radius: 4px 4px 0 0; + background: var(--color-bg-tertiary); + padding: 12px; + overflow-y: auto; + flex: 0 0 auto; + border-bottom: 1px solid var(--color-border-primary); + display: flex; + flex-direction: column; + gap: 4px; + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + color: var(--color-text-secondary); + font-size: 12px; + line-height: 16px; + overflow: hidden; + text-overflow: ellipsis; + } + + &__cancel { + display: flex; + + .icon { + width: 18px; + height: 18px; + } + } + + &__display-name { + display: flex; + gap: 4px; + + a { + color: inherit; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + + &__content { + color: var(--color-text-primary); + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + padding-top: 0 !important; + display: -webkit-box; + line-clamp: 4; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + max-height: 4 * 20px; + overflow: hidden; + + a { + color: var(--color-text-brand); + } + } + + &__attachments { + color: var(--color-text-secondary); + font-size: 12px; + line-height: 16px; + opacity: 0.75; + display: flex; + align-items: center; + gap: 4px; + + .icon { + width: 18px; + height: 18px; + } + } +} + +.edit-indicator__content, +.reply-indicator__content { + .emojione { + width: 18px; + height: 18px; + margin: -3px 0 0; + } +} + +.announcements__content { + overflow-wrap: break-word; + overflow-y: auto; + + .emojione { + width: 20px; + height: 20px; + margin: -3px 0 0; + } + + p { + margin-bottom: 10px; + white-space: pre-wrap; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--color-text-primary); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &.mention { + &:hover { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + + &.unhandled-link { + color: var(--color-text-brand); + } + } +} + +.status__content.status__content--collapsed { + max-height: 22px * 15; // 15 lines is roughly above 500 characters +} + +.status__content__read-more-button, +.status__content__translate-button { + display: flex; + align-items: center; + font-size: 15px; + line-height: 22px; + color: var(--color-text-brand); + border: 0; + background: transparent; + padding: 0; + margin-top: 16px; + text-decoration: none; + text-wrap: nowrap; + + .status--is-quote & { + // Needed to prevent buttons from stretching across whole + // status width in Safari due to line-clamp + width: min-content; + white-space: nowrap; + } + + &:hover, + &:active { + text-decoration: underline; + } + + .icon { + width: 15px; + height: 15px; + } +} + +.translate-button { + margin-top: 16px; + font-size: 15px; + line-height: 22px; + display: flex; + justify-content: space-between; + color: var(--color-text-tertiary); +} + +.status__wrapper--filtered { + color: var(--color-text-tertiary); + border: 0; + font-size: inherit; + text-align: center; + line-height: inherit; + margin: 0; + padding: 15px; + box-sizing: border-box; + width: 100%; + clear: both; + border-bottom: 1px solid var(--color-border-primary); + + &__button { + display: inline; + color: var(--color-text-brand); + border: 0; + background: transparent; + padding: 0; + font-size: inherit; + line-height: inherit; + + &:hover, + &:active { + text-decoration: underline; + } + } +} + +.focusable { + &:focus-visible { + outline: 2px solid var(--color-text-brand); + outline-offset: -2px; + background: var(--color-bg-brand-softer); + } +} + +.status { + padding: 16px; + min-height: 54px; + border-bottom: 1px solid var(--color-border-primary); + cursor: auto; + opacity: 1; + animation: fade 150ms linear; + + @keyframes fade { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } + } + + .content-warning { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + + .media-gallery, + .video-player, + .audio-player, + .attachment-list { + margin-top: 16px; + } + + &--in-thread { + --thread-margin: calc(46px + 8px); + + border-bottom: 0; + + & > .status__content, + & > .status__action-bar, + & > .media-gallery, + & > div > .video-player, + & > .audio-player, + & > .attachment-list, + & > .picture-in-picture-placeholder, + & > .more-from-author, + & > .status-card, + & > .hashtag-bar, + & > .content-warning, + & > .filter-warning, + & > .status__quote { + margin-inline-start: var(--thread-margin); + width: calc(100% - var(--thread-margin)); + } + + .more-from-author { + width: calc(100% - var(--thread-margin) + 2px); + } + + .status__content__read-more-button { + margin-inline-start: var(--thread-margin); + } + } + + &__action-bar__button-wrapper { + flex-basis: 0; + flex-grow: 1; + + &:last-child { + flex-grow: 0; + } + } + + &--first-in-thread { + border-top: 1px solid var(--color-border-primary); + } + + &__line { + --thread-line-color: var(--color-border-primary); + + height: 16px - 4px; + border-inline-start: 2px solid var(--thread-line-color); + width: 0; + position: absolute; + top: 0; + inset-inline-start: 16px + ((46px - 2px) * 0.5); + + &--full { + top: 0; + height: 100%; + + &::before { + content: ''; + display: block; + position: absolute; + top: 16px - 4px; + height: 46px + 4px + 4px; + width: 2px; + background: var(--thread-line-color); + inset-inline-start: -2px; + } + } + + &--first { + top: 16px + 46px + 4px; + height: calc(100% - (16px + 46px + 4px)); + + &::before { + display: none; + } + } + } + + .no-reduce-motion &--highlighted-entry::before { + content: ''; + position: absolute; + inset: 0; + background: var(--color-bg-brand-softer); + opacity: 0; + animation: fade 0.7s reverse both 0.3s; + pointer-events: none; + } +} + +.status__relative-time { + display: block; + font-size: 15px; + line-height: 22px; + height: 40px; + order: 2; + flex: 0 0 auto; + color: var(--color-text-secondary); +} + +.notification__relative_time { + color: var(--color-text-tertiary); + float: right; + font-size: 14px; + padding-bottom: 1px; +} + +.status__visibility-icon { + padding: 0 4px; + + .icon { + width: 1em; + height: 1em; + margin-bottom: -2px; + } +} + +.status__display-name { + color: var(--color-text-secondary); +} + +.status__info .status__display-name { + max-width: 100%; + display: flex; + font-size: 15px; + line-height: 22px; + align-items: center; + gap: 10px; + overflow: hidden; + margin-inline-end: auto; + + .display-name { + bdi { + overflow: hidden; + text-overflow: ellipsis; + } + + &__account { + white-space: nowrap; + display: block; + overflow: hidden; + text-overflow: ellipsis; + } + } +} + +.status__quote-cancel { + align-self: self-start; + order: 5; +} + +.status__info { + font-size: 15px; + padding-bottom: 10px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + cursor: pointer; +} + +.status-check-box__status { + display: block; + box-sizing: border-box; + width: 100%; + padding: 0 10px; + + .detailed-status__display-name { + color: var(--color-text-tertiary); + + span { + display: inline; + } + + &:hover strong { + text-decoration: none; + } + } + + .media-gallery, + .audio-player, + .video-player { + margin-top: 15px; + max-width: 250px; + } + + .status__content { + padding: 0; + white-space: normal; + } + + .media-gallery__item-thumbnail { + cursor: default; + } +} + +.status__prepend { + padding: 16px; + padding-bottom: 0; + display: flex; + align-items: center; + gap: 8px; + font-size: 15px; + line-height: 22px; + font-weight: 500; + color: var(--color-text-secondary); + + &__icon { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + + .icon { + width: 16px; + height: 16px; + } + } + + a { + color: inherit; + text-decoration: none; + } + + > span { + display: block; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.status__wrapper-direct, +.notification-ungrouped--direct, +.notification-group--direct, +.notification-group--annual-report { + background: var(--color-bg-brand-softer); + + &:focus { + background: var(--color-bg-brand-soft); + } +} + +.status__wrapper-direct, +.notification-ungrouped--direct { + .status__prepend, + .notification-ungrouped__header { + color: var(--color-text-brand); + } +} + +.status__action-bar { + display: flex; + justify-content: space-between; + align-items: center; + gap: 18px; + margin-top: 16px; +} + +.detailed-status__action-bar-dropdown { + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.detailed-status { + padding: 16px; + border-top: 1px solid var(--color-border-primary); + + .status__content { + font-size: 19px; + line-height: 24px; + + .emojione { + width: 24px; + height: 24px; + margin: -1px 0 0; + } + } + + .media-gallery, + .video-player, + .audio-player { + margin-top: 16px; + } + + .status__prepend { + padding: 0; + margin-bottom: 16px; + } + + .content-warning { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + } + + .logo { + width: 40px; + height: 40px; + color: var(--color-text-tertiary); + } +} + +.embed { + position: relative; + + &__overlay { + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + .detailed-status { + border-top: 0; + } +} + +.scrollable > div:first-child .detailed-status { + border-top: 0; +} + +.detailed-status__meta { + margin-top: 24px; + color: var(--color-text-tertiary); + font-size: 14px; + line-height: 18px; + + &__line { + border-bottom: 1px solid var(--color-border-primary); + padding: 8px 0; + display: flex; + align-items: center; + gap: 8px; + + &:first-child { + padding-top: 0; + } + + &:last-child { + padding-bottom: 0; + border-bottom: 0; + } + } + + .icon { + width: 18px; + height: 18px; + } + + .animated-number { + color: var(--color-text-primary); + font-weight: 500; + } +} + +.detailed-status__action-bar { + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + display: flex; + flex-direction: row; + padding: 10px 0; +} + +.detailed-status__wrapper-direct { + .detailed-status, + .detailed-status__action-bar { + background: var(--color-bg-brand-softer); + } + + .status__prepend { + color: var(--color-text-brand); + } +} + +.status__quote { + // --status-gutter-width is currently only set inside of + // .notification-ungrouped, so everywhere else this will fall back + // to the pixel values + --quote-margin: var(--status-gutter-width); + + position: relative; + overflow: hidden; + margin-block-start: 16px; + margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px)); + border-radius: 12px; + color: var(--color-text-primary); + border: 1px solid var(--color-border-primary); +} + +.status__quote--error { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 12px; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + min-height: 56px; + + .link-button { + font-size: inherit; + line-height: inherit; + letter-spacing: inherit; + } +} + +.status__quote-author-button { + position: relative; + overflow: hidden; + display: flex; + margin-top: 8px; + padding: 8px 12px; + align-items: center; + font-family: inherit; + font-size: 14px; + font-weight: 400; + line-height: 20px; + letter-spacing: 0.25px; + color: var(--color-text-secondary); + background: var(--color-bg-brand-softer); + border-radius: 8px; + cursor: default; +} + +.status--is-quote { + border: none; + padding: 12px; + + .status__info { + padding-bottom: 8px; + } + + .display-name, + .status__relative-time { + font-size: 14px; + line-height: 20px; + letter-spacing: 0.1px; + } + + .display-name__account { + font-size: 12px; + line-height: 16px; + letter-spacing: 0.5px; + } + + .status__content { + display: -webkit-box; + font-size: 14px; + letter-spacing: 0.25px; + line-height: 20px; + -webkit-line-clamp: 4; + line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + + p { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + } + } + + .media-gallery, + .video-player, + .audio-player, + .attachment-list, + .poll { + margin-top: 8px; + } +} + +.detailed-status__link { + display: inline-flex; + align-items: center; + color: inherit; + text-decoration: none; + gap: 6px; +} + +.domain { + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + display: flex; + align-items: center; + gap: 8px; + + &__domain-name { + flex: 1 1 auto; + color: var(--color-text-primary); + font-size: 15px; + line-height: 21px; + font-weight: 500; + } +} + +.account { + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + + .account__display-name { + flex: 1 1 auto; + display: flex; + align-items: center; + gap: 10px; + color: var(--color-text-secondary); + overflow: hidden; + text-decoration: none; + font-size: 14px; + + .display-name { + margin-bottom: 4px; + } + + .display-name strong { + display: inline; + color: var(--color-text-primary); + } + } + + &--minimal { + .account__display-name { + .display-name { + margin-bottom: 0; + } + + .display-name strong { + display: block; + } + } + } + + &__avatar-wrapper .account__avatar { + @container (width < 360px) { + width: 35px !important; + height: 35px !important; + } + } + + &__domain-pill { + display: inline-flex; + background: var(--color-bg-brand-softer); + border-radius: 4px; + border: 0; + color: var(--color-text-brand); + font-weight: 500; + font-size: 12px; + line-height: 16px; + padding: 4px 8px; + + &.active { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + } + + &__popout { + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + box-shadow: var(--dropdown-shadow); + max-width: 320px; + padding: 16px; + border-radius: 8px; + display: flex; + flex-direction: column; + gap: 24px; + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + + .link-button { + display: inline; + font-size: inherit; + line-height: inherit; + } + + &__header { + display: flex; + align-items: center; + gap: 12px; + + &__icon { + width: 40px; + height: 40px; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + flex-shrink: 0; + } + + h3 { + font-size: 17px; + line-height: 22px; + color: var(--color-text-primary); + } + } + + &__handle { + border: 2px dashed var(--color-border-on-brand-softer); + background: var(--color-bg-brand-softer); + padding: 12px 8px; + color: var(--color-text-brand); + border-radius: 4px; + + &__label { + font-size: 11px; + line-height: 16px; + font-weight: 500; + } + + &__handle { + overflow: hidden; + text-overflow: ellipsis; + user-select: all; + } + } + + &__parts { + display: flex; + flex-direction: column; + gap: 8px; + font-size: 12px; + line-height: 16px; + + & > div { + display: flex; + align-items: flex-start; + gap: 12px; + } + + &__icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + color: var(--color-text-brand); + } + + h6 { + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: var(--color-text-primary); + } + } + } + } + + &__note { + font-size: 14px; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + line-clamp: 1; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + margin-top: 10px; + color: var(--color-text-secondary); + + &--missing { + color: var(--color-text-tertiary); + } + + p { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: inherit; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + } +} + +.learn-more__popout { + gap: 8px; + + &__content { + display: flex; + flex-direction: column; + gap: 4px; + } + + h6 { + font-size: inherit; + font-weight: 500; + line-height: inherit; + letter-spacing: 0.1px; + } + + .link-button { + font-weight: 500; + } +} + +.account__wrapper { + display: flex; + gap: 10px; + align-items: center; + justify-content: end; +} + +.account__wrapper--with-bio { + align-items: start; +} + +.account__info-wrapper { + flex: 1 1 auto; + min-width: 0; +} + +.account__avatar { + display: block; + position: relative; + border-radius: var(--avatar-border-radius); + background: var(--color-bg-tertiary); + flex-shrink: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: var(--avatar-border-radius); + display: inline-block; // to not show broken images + } + + &--loading { + background-color: var(--color-bg-tertiary); + } + + &--inline { + display: inline-block; + vertical-align: middle; + margin-inline-end: 5px; + } + + &-composite { + border-radius: 50%; + overflow: hidden; + position: relative; + + & > div { + float: left; + position: relative; + box-sizing: border-box; + } + + .account__avatar { + width: 100% !important; + height: 100% !important; + } + + &__label { + display: block; + position: absolute; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + color: var(--color-text-primary); + text-shadow: 1px 1px 2px + rgb(from var(--color-shadow-primary) r g b / 100%); + font-weight: 700; + font-size: 15px; + } + } + + &__counter { + $height: 16px; + $h-padding: 5px; + + position: absolute; + bottom: -3px; + inset-inline-end: -3px; + padding-left: $h-padding; + padding-right: $h-padding; + height: $height; + border-radius: $height; + min-width: $height - 2 * $h-padding; // to ensure that it is never narrower than a circle + line-height: $height + 1px; // to visually center the numbers + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border-width: 1px; + border-style: solid; + border-color: var(--color-bg-primary); + font-size: 11px; + font-weight: 500; + text-align: center; + } +} + +a .account__avatar { + cursor: pointer; +} + +.avatar-group { + display: flex; + + --avatar-height: 28px; + + &:not(.avatar-group--compact) { + gap: 8px; + flex-wrap: wrap; + height: var(--avatar-height); + overflow-y: clip; + } +} + +.avatar-group--compact { + & > :not(:first-child) { + margin-inline-start: -12px; + } + + & > :first-child { + transform: rotate(-4deg); + } + + & > :nth-child(2) { + transform: rotate(-2deg); + } + + .account__avatar { + box-shadow: 0 0 0 2px var(--color-bg-primary); + } +} + +.account__avatar-overlay { + position: relative; + + &-overlay { + position: absolute; + bottom: 0; + inset-inline-end: 0; + z-index: 1; + } +} + +.account__relationship { + white-space: nowrap; + display: flex; + align-items: center; + gap: 8px; +} + +.account__relationship, +.explore-suggestions-card { + .icon-button { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + box-sizing: content-box; + padding: 5px; + + .icon { + width: 24px; + height: 24px; + } + } +} + +.account-authorize { + padding: 14px 10px; + + .detailed-status__display-name { + display: block; + margin-bottom: 15px; + overflow: hidden; + } +} + +.account-authorize__avatar { + float: left; + margin-inline-end: 10px; +} + +.status__display-name, +.status__relative-time, +.detailed-status__display-name, +.detailed-status__datetime, +.detailed-status__application, +.detailed-status__link, +.account__display-name { + text-decoration: none; +} + +.status__display-name, +.account__display-name { + .display-name strong { + color: var(--color-text-primary); + } +} + +.muted { + .emojione { + opacity: 0.5; + } +} + +.status__display-name, +.detailed-status__display-name, +a.account__display-name { + &:hover .display-name strong { + text-decoration: underline; + } +} + +.account__display-name .display-name strong { + display: block; + overflow: hidden; + text-overflow: ellipsis; +} + +.detailed-status__application, +.detailed-status__datetime, +.detailed-status__link { + color: inherit; +} + +.detailed-status__display-name { + color: var(--color-text-secondary); + display: flex; + align-items: center; + gap: 10px; + font-size: 15px; + line-height: 22px; + margin-bottom: 16px; + overflow: hidden; + + strong, + span { + display: block; + text-overflow: ellipsis; + overflow: hidden; + } + + strong { + color: var(--color-text-primary); + } +} + +.muted { + .status__content, + .status__content p, + .status__content a { + color: var(--color-text-tertiary); + } + + .status__display-name strong { + color: var(--color-text-tertiary); + } + + .status__avatar { + opacity: 0.5; + } +} + +.notification__report { + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + display: flex; + gap: 10px; + + &__avatar { + flex: 0 0 auto; + } + + &__details { + flex: 1 1 auto; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--color-text-secondary); + gap: 10px; + font-size: 15px; + line-height: 22px; + white-space: nowrap; + overflow: hidden; + + & > div { + overflow: hidden; + text-overflow: ellipsis; + } + + strong { + font-weight: 500; + } + } + + &__actions { + flex: 0 0 auto; + } +} + +.notification-group--link { + color: var(--color-text-primary); + text-decoration: none; + + .notification-group__main { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + flex-grow: 1; + font-size: 15px; + line-height: 22px; + + strong, + bdi { + font-weight: 700; + } + + .link-button { + font-size: inherit; + line-height: inherit; + font-weight: inherit; + } + } +} + +.notification__message { + padding: 16px; + padding-bottom: 0; + cursor: default; + color: var(--color-text-secondary); + font-size: 15px; + line-height: 22px; + font-weight: 500; + display: flex; + align-items: center; + gap: 10px; + + .icon { + color: var(--color-text-brand); + width: 18px; + height: 18px; + } + + .icon-star { + color: var(--color-text-favourite-highlight); + } + + > span { + display: inline; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.icon-button.star-icon.active { + color: var(--color-text-favourite-highlight); +} + +.icon-button.bookmark-icon.active { + color: var(--color-text-bookmark-highlight); +} + +.no-reduce-motion .icon-button.star-icon { + &.activate { + & > .icon { + animation: spring-rotate-in 1s linear; + transform-origin: 50% 52%; + } + } + + &.deactivate { + & > .icon { + animation: spring-rotate-out 1s linear; + transform-origin: 50% 52%; + } + } +} + +.notification__display-name { + color: inherit; + font-weight: 500; + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} + +.display-name { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &__account { + text-overflow: ellipsis; + overflow: hidden; + } +} + +.display-name__html { + font-weight: 500; +} + +.status__relative-time, +.detailed-status__datetime, +.detailed-status__link { + &:is(a):hover { + text-decoration: underline; + } +} + +.zoomable-image { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + scrollbar-width: none; + overflow: hidden; + user-select: none; + + img { + max-width: $media-modal-media-max-width; + max-height: $media-modal-media-max-height; + width: auto; + height: auto; + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + border-radius: 8px; + touch-action: none; + user-select: none; + } + + &--zoomed-in { + z-index: 9999; + cursor: grab; + + img { + outline: none; + border-radius: 0; + } + } + + &--dragging { + cursor: grabbing; + } + + &--error img { + visibility: hidden; + } + + &__preview { + max-width: $media-modal-media-max-width; + max-height: $media-modal-media-max-height; + position: absolute; + z-index: 1; + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + border-radius: 8px; + overflow: hidden; + + canvas { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + z-index: -1; + } + } + + .loading-indicator { + z-index: 2; + mix-blend-mode: luminosity; + } +} + +.navigation-bar { + display: flex; + align-items: center; + flex-shrink: 0; + cursor: default; + gap: 10px; + + .column > & { + padding: 15px; + } + + .account { + border-bottom: 0; + padding: 0; + flex: 1 1 auto; + min-width: 0; + + &__display-name { + font-size: 14px; + line-height: 20px; + font-weight: 500; + + .display-name__account { + font-size: 14px; + font-weight: 400; + } + } + } + + .icon-button { + padding: 8px; + color: var(--color-text-primary); + } + + .icon-button .icon { + width: 24px; + height: 24px; + } +} + +.dropdown-animation { + animation: dropdown 250ms cubic-bezier(0.1, 0.7, 0.1, 1); + + @keyframes dropdown { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + .reduce-motion & { + animation: none; + } +} + +.dropdown { + display: inline-block; +} + +.dropdown__content { + display: none; + position: absolute; +} + +.dropdown-menu__separator { + border-bottom: 1px solid var(--color-border-primary); + margin: 5px 0; + height: 0; +} + +.dropdown-menu { + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + padding: 4px; + border-radius: 4px; + box-shadow: var(--dropdown-shadow); + z-index: 9999; + + &__text-button { + display: inline-flex; + align-items: center; + color: inherit; + background: transparent; + border: 0; + margin: 0; + padding: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; + + &:focus-visible { + outline: 1px dotted; + } + + &:hover { + text-decoration: underline; + } + + .icon { + width: 15px; + height: 15px; + } + } + + &__container { + &__header { + border-bottom: 1px solid var(--color-border-primary); + padding: 10px 14px; + padding-bottom: 14px; + margin-bottom: 4px; + font-size: 13px; + line-height: 18px; + color: var(--color-text-secondary); + } + + &__list { + list-style: none; + + &--scrollable { + max-height: 300px; + overflow-y: scroll; + } + } + + &--loading { + display: flex; + align-items: center; + justify-content: center; + padding: 30px 45px; + } + } + + &.left { + transform-origin: 100% 50%; + } + + &.top { + transform-origin: 50% 100%; + } + + &.bottom { + transform-origin: 50% 0; + } + + &.right { + transform-origin: 0 50%; + } +} + +.dropdown-menu__item { + font-size: 13px; + line-height: 18px; + font-weight: 500; + display: block; + + &--dangerous { + color: var(--color-text-error); + } + + &--highlighted { + color: var(--color-text-brand); + } + + &-content { + display: flex; + flex-direction: column; + } + + &-subtitle { + font-weight: 400; + } + + a, + button { + font: inherit; + display: flex; + align-items: center; + gap: 8px; + white-space: inherit; + width: 100%; + padding: 10px 14px; + border: 0; + margin: 0; + background: transparent; + box-sizing: border-box; + text-decoration: none; + color: inherit; + text-align: inherit; + border-radius: 4px; + + &:focus, + &:hover, + &:active { + &:not(:disabled, [aria-disabled='true']) { + background: var(--color-bg-secondary); + outline: 0; + } + } + } + + button:disabled, + button[aria-disabled='true'] { + color: var(--color-text-disabled); + cursor: default; + + &:focus { + color: rgb(from var(--color-text-disabled) r g b / 70%); + background: var(--color-bg-disabled); + outline: 0; + } + } +} + +.reblog-menu-item { + max-width: 360px; +} + +.inline-account { + display: inline-flex; + align-items: center; + vertical-align: top; + + .account__avatar { + margin-inline-end: 5px; + border-radius: 50%; + } + + strong { + font-weight: 600; + } +} + +.columns-area { + display: flex; + flex: 1 1 auto; + flex-direction: row; + justify-content: flex-start; + position: relative; + + .layout-multiple-columns & { + overflow-x: auto; + + &.unscrollable { + overflow-x: hidden; + } + } + + &__panels { + box-sizing: border-box; + display: flex; + justify-content: center; + gap: 16px; + width: 100%; + height: 100%; + min-height: 100vh; + min-height: 100dvh; + padding-inline: 10px; + padding-bottom: env(safe-area-inset-bottom); + + &__pane { + height: 100%; + overflow: hidden; + display: flex; + justify-content: flex-end; + min-width: 285px; + + &--start { + justify-content: flex-start; + } + + &__inner { + position: fixed; + width: 285px; + height: 100%; + } + } + + &__main { + --column-header-height: 62px; + + box-sizing: border-box; + width: 100%; + flex: 0 1 auto; + display: flex; + flex-direction: column; + contain: inline-size layout paint style; + container: column / inline-size; + color: var(--color-text-primary); + background-color: var(--color-bg-primary); + + @media screen and (min-width: $no-gap-breakpoint) { + max-width: 600px; + } + } + } +} + +.ui__navigation-bar { + position: fixed; + bottom: 0; + z-index: 3; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + gap: 8px; + padding-bottom: env(safe-area-inset-bottom); + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + border-top: 1px solid var(--color-border-primary); + box-sizing: border-box; + + .layout-multiple-columns & { + display: none; + } + + &__items { + display: grid; + grid-auto-columns: minmax(0, 1fr); + grid-auto-flow: column; + padding: 0 16px; + + &.active { + flex: 1; + padding: 0; + } + } + + &__sign-up { + display: flex; + align-items: center; + gap: 4px; + padding-inline-start: 16px; + } + + &__item { + display: flex; + flex-direction: column; + align-items: center; + background: transparent; + border: none; + gap: 8px; + font-size: 12px; + font-weight: 500; + line-height: 16px; + padding-top: 11px; + padding-bottom: 15px; + border-top: 4px solid transparent; + text-decoration: none; + color: inherit; + + &.active { + color: var(--color-text-brand); + } + + &:focus { + outline: 0; + } + + &:focus-visible { + border-top-color: var(--color-text-brand); + border-radius: 0; + } + } +} + +.tabs-bar__wrapper { + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + position: sticky; + top: 0; + z-index: 2; + border-top: 0; + + @media screen and (min-width: $no-gap-breakpoint) { + border-top: 10px solid var(--color-bg-ambient); + } +} + +.react-swipeable-view-container { + &, + .columns-area, + .drawer, + .column { + height: 100%; + } +} + +.react-swipeable-view-container > * { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +.column { + width: clamp(380px, calc((100% - 350px) / 4), 400px); + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: column; + + > .scrollable { + border: 1px solid var(--color-border-primary); + border-top: 0; + border-radius: 0 0 4px 4px; + + &.about, + &.privacy-policy { + border-top: 1px solid var(--color-border-primary); + border-radius: 4px; + + @media screen and (max-width: $no-gap-breakpoint) { + border-top: 0; + border-bottom: 0; + } + } + } +} + +.column__alert { + --alert-height: 54px; + + position: sticky; + bottom: 0; + z-index: 10; + box-sizing: border-box; + display: grid; + grid-template-rows: minmax(var(--alert-height), max-content); + align-items: end; + width: 100%; + max-width: 360px; + padding: 1rem; + margin: auto auto 0; + overflow: clip; + pointer-events: none; + + @media (max-width: #{$mobile-menu-breakpoint - 1}) { + // Compensate for mobile menubar + bottom: var(--mobile-bottom-nav-height); + } + + & > * { + // Make all nested alerts occupy the same space + // rather than stack + grid-area: 1 / 1; + pointer-events: initial; + } +} + +.ui { + --mobile-bottom-nav-height: 55px; + --last-content-item-border-width: 2px; + + flex: 0 0 auto; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + + @media (max-width: #{$mobile-menu-breakpoint - 1}) { + padding-bottom: calc( + var(--mobile-bottom-nav-height) - var(--last-content-item-border-width) + ); + } +} + +.drawer { + width: 350px; + box-sizing: border-box; + display: flex; + flex-direction: column; + overflow-y: hidden; +} + +.drawer__tab { + display: flex; + flex: 1 1 auto; + padding: 13px 3px 11px; + color: var(--color-text-secondary); + text-decoration: none; + text-align: center; + font-size: 16px; + align-items: center; + justify-content: center; +} + +.column, +.drawer { + flex: 1 1 100%; +} + +@media screen and (width > $mobile-breakpoint) { + .columns-area { + padding: 0; + } + + .column, + .drawer { + flex: 0 0 auto; + padding: 10px; + padding-inline-start: 5px; + padding-inline-end: 5px; + + &:first-child { + padding-inline-start: 10px; + } + + &:last-child { + padding-inline-end: 10px; + } + } + + .columns-area > div { + .column, + .drawer { + padding-inline-start: 5px; + padding-inline-end: 5px; + } + } +} + +.columns-area--mobile { + flex-direction: column; + width: 100%; + height: 100%; + margin: 0 auto; + + .column, + .drawer { + width: 100%; + height: 100%; + padding: 0; + } + + .account-card { + margin-bottom: 0; + } + + .filter-form { + display: flex; + flex-wrap: wrap; + } + + .autosuggest-textarea__textarea { + font-size: 16px; + } + + .search__input { + line-height: 18px; + font-size: 16px; + padding-block: 15px; + padding-inline-end: 30px; + } + + .scrollable { + overflow: visible; + + @supports (display: grid) { + contain: content; + } + } + + @media screen and (min-width: $no-gap-breakpoint) { + padding: 10px 0; + padding-top: 0; + } +} + +@media screen and (min-width: $no-gap-breakpoint) { + .react-swipeable-view-container .columns-area--mobile { + height: calc(100% - 10px) !important; + } + + .getting-started__wrapper { + margin-bottom: 10px; + } + + .search-page .search { + display: none; + } + + .navigation-panel__legal, + .navigation-panel__compose-button, + .navigation-panel .navigation-bar { + display: none !important; + } +} + +@media screen and (max-width: ($no-gap-breakpoint - 1px)) { + $sidebar-width: 285px; + + .columns-area__panels__main { + width: calc(100% - $sidebar-width); + } + + .columns-area__panels { + min-height: 100vh; + min-height: 100dvh; + gap: 0; + padding-inline: 0; + } + + .columns-area__panels__pane--navigational { + min-width: $sidebar-width; + + .columns-area__panels__pane__inner { + width: $sidebar-width; + } + + .navigation-panel { + margin: 0; + border-inline-start: 1px solid var(--color-border-primary); + height: 100dvh; + } + + .navigation-panel__banner, + .navigation-panel__logo, + .getting-started__trends { + display: none; + } + } + + .layout-single-column { + .column > .scrollable, + .tabs-bar__wrapper .column-header, + .tabs-bar__wrapper .column-back-button, + .explore__search-header, + .column-search-header { + border-left: 0; + border-right: 0; + } + + .column-header, + .column-back-button, + .scrollable, + .error-column { + border-radius: 0 !important; + } + + .column-header, + .column-back-button { + border-top: 0; + } + } +} + +@media screen and (width <= 759px) { + .columns-area__panels__main { + width: 100%; + } + + .columns-area__panels__pane--navigational { + position: fixed; + inset-inline-end: 0; + width: 100%; + height: 100%; + pointer-events: none; + } + + .columns-area__panels__pane--navigational .columns-area__panels__pane__inner { + pointer-events: auto; + background: var(--color-bg-primary); + position: fixed; + width: 284px + 70px; + inset-inline-end: -70px; + touch-action: pan-y; + + .navigation-panel { + width: 284px; + overflow-y: auto; + scrollbar-width: thin; + + &__menu { + flex-shrink: 0; + min-height: none; + overflow: hidden; + padding-bottom: calc(65px + env(safe-area-inset-bottom)); + } + + &__logo { + display: none; + } + } + } +} + +.columns-area__panels__pane--navigational { + transition: background 500ms; +} + +.columns-area__panels__pane--overlay { + pointer-events: auto; + background: rgb(from var(--color-bg-overlay) r g b / 50%); + z-index: 3; + + .columns-area__panels__pane__inner { + box-shadow: var(--dropdown-shadow); + } +} + +@media screen and (width >= 760px) { + .ui__navigation-bar { + display: none; + } +} + +.explore-suggestions-card { + padding: 12px 16px; + gap: 8px; + display: flex; + flex-direction: column; + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + + &__source { + font-size: 13px; + line-height: 16px; + color: var(--color-text-tertiary); + + @container (width >= 400px) { + padding-inline-start: 60px; + } + } + + &__body { + display: flex; + gap: 12px; + align-items: center; + justify-content: end; + } + + &__avatar { + flex-shrink: 0; + + @container (width < 360px) { + width: 35px !important; + height: 35px !important; + } + } + + &__link { + flex: 1 1 auto; + display: flex; + gap: 12px; + align-items: center; + text-decoration: none; + min-width: 0; + + &:hover, + &:focus-visible { + .display-name__html { + text-decoration: underline; + } + } + + .display-name { + font-size: 15px; + line-height: 20px; + color: var(--color-text-primary); + + strong { + font-weight: 700; + } + + &__account { + color: var(--color-text-secondary); + display: block; + } + } + } + + &__actions { + display: flex; + align-items: center; + gap: 8px; + justify-content: end; + + .button { + min-width: 80px; + } + } + + &__dismiss-button { + @container (width < 400px) { + display: none; + } + } +} + +@media screen and (max-width: ($no-gap-breakpoint - 1px)) { + .columns-area__panels__pane--compositional { + display: none; + } +} + +.icon-with-badge { + display: inline-flex; + position: relative; + + &__badge { + position: absolute; + inset-inline-start: 9px; + top: -13px; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border: 2px solid var(--color-bg-primary); + padding: 1px 6px; + border-radius: 6px; + font-size: 10px; + font-weight: 500; + line-height: 14px; + } + + &__issue-badge { + position: absolute; + inset-inline-start: 11px; + bottom: 1px; + display: block; + background: var(--color-text-error); + border-radius: 50%; + width: 0.625rem; + height: 0.625rem; + } +} + +.column-link--transparent .icon-with-badge__badge { + border-color: var(--color-bg-primary); +} + +.column-title { + text-align: center; + padding-bottom: 32px; + + h3 { + font-size: 24px; + line-height: 1.5; + font-weight: 700; + margin-bottom: 10px; + } + + p { + font-size: 16px; + line-height: 24px; + font-weight: 400; + color: var(--color-text-secondary); + } + + @media screen and (width >= 600px) { + padding: 40px; + } +} + +.copy-paste-text { + background: var(--color-bg-secondary); + border-radius: 8px; + border: 1px solid var(--color-border-primary); + padding: 16px; + color: var(--color-text-primary); + font-size: 15px; + line-height: 22px; + display: flex; + flex-direction: column; + align-items: flex-end; + transition: border-color 300ms linear; + margin-bottom: 30px; + + &:focus, + &.focused { + transition: none; + outline: 0; + border-color: var(--color-text-brand); + } + + &.copied { + border-color: var(--color-text-success); + transition: none; + } + + textarea { + width: 100%; + height: auto; + background: transparent; + color: inherit; + font: inherit; + border: 0; + padding: 0; + margin-bottom: 30px; + resize: none; + + &:focus { + outline: 0; + } + } +} + +.onboarding__profile { + position: relative; + margin-bottom: 40px + 20px; + margin-top: -20px; + + .app-form__avatar-input { + border: 2px solid var(--color-bg-primary); + position: absolute; + inset-inline-start: -2px; + bottom: -40px; + z-index: 2; + } + + .app-form__header-input { + margin: 0 -20px; + border-radius: 0; + + img { + border-radius: 0; + } + } +} + +.compose-panel { + width: 285px; + margin-top: 10px; + display: flex; + flex-direction: column; + height: calc(100% - 10px); + overflow-y: auto; + scrollbar-width: thin; + + .compose-form { + flex: 1 1 auto; + } +} + +.navigation-panel { + margin-top: 10px; + margin-bottom: 10px; + height: calc(100% - 20px); + overflow: hidden; + display: flex; + flex-direction: column; + + &__menu { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + scrollbar-width: thin; + } + + &__list-panel { + &__header { + display: flex; + align-items: center; + padding-inline-end: 4px; + + &__sep { + width: 0; + height: 24px; + border-left: 1px solid var(--color-border-primary); + } + + .column-link { + flex: 1 1 auto; + } + + .icon-button { + padding: 8px; + } + } + + &__items { + padding-inline-start: 24px + 8px; + + .icon { + display: none; + } + } + } + + &__compose-button { + display: flex; + justify-content: flex-start; + padding-top: 8px; + padding-bottom: 8px; + padding-inline-start: 12px - 7px; + padding-inline-end: 12px; + gap: 8px; + margin: 12px; + border-radius: 6px; + font-size: 16px; + font-weight: 600; + line-height: 18px; + + .icon { + width: 24px; + height: 24px; + } + } + + .navigation-bar { + padding: 16px; + } + + .search { + margin: 16px; + margin-bottom: 12px; + } + + .logo { + height: 30px; + width: auto; + } + + &__logo { + margin-bottom: 12px; + } + + .getting-started__trends h4 { + padding: 10px 12px; + padding-inline-start: 16px; + } + + .getting-started__trends .trends__item { + padding: 10px 12px; + padding-inline-start: 16px; + } + + @media screen and (height <= 930px) { + &__portal .trends__item:nth-child(n + 5) { + display: none; + } + } + + @media screen and (height <= (930px - 56px)) { + &__portal .trends__item:nth-child(n + 4) { + display: none; + } + } + + @media screen and (height <= (930px - 56px * 2)) { + &__portal .trends__item:nth-child(n + 3) { + display: none; + } + } + + @media screen and (height <= (930px - 56px * 3)) { + &__portal { + display: none; + } + } +} + +.navigation-panel, +.compose-panel { + hr { + flex: 0 0 auto; + border: 0; + background: transparent; + border-top: 1px solid var(--color-border-primary); + margin: 10px 0; + } + + .flex-spacer { + background: transparent; + } +} + +.drawer__pager { + box-sizing: border-box; + padding: 0; + flex-grow: 1; + position: relative; + overflow: hidden; + display: flex; + border-radius: 4px; + border: 1px solid var(--color-border-primary); +} + +.drawer__inner { + position: absolute; + top: 0; + inset-inline-start: 0; + background: var(--color-bg-primary); + box-sizing: border-box; + padding: 0; + display: flex; + flex-direction: column; + overflow: hidden; + overflow-y: auto; + width: 100%; + height: 100%; + z-index: 0; +} + +.drawer__inner__mastodon { + position: relative; + background: var(--color-bg-primary); + flex: 1; + min-height: 47px; + display: none; + contain: content; + + > img { + display: block; + object-fit: contain; + object-position: bottom left; + width: 85%; + height: 100%; + pointer-events: none; + user-select: none; + } + + @media screen and (height >= 640px) { + display: block; + } +} + +.drawer__header { + flex: 0 0 auto; + font-size: 16px; + border: 1px solid var(--color-border-primary); + margin-bottom: 10px; + display: flex; + flex-direction: row; + border-radius: 4px; + overflow: hidden; + + a:hover, + a:focus, + a:active { + color: var(--color-text-primary); + } +} + +.scrollable { + overflow-y: scroll; + overflow-x: hidden; + flex: 1 1 auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + + &.optionally-scrollable { + overflow-y: auto; + } + + @supports (display: grid) { + // hack to fix Chrome <57 + contain: strict; + } + + &--flex { + display: flex; + flex-direction: column; + } + + &__append { + flex: 1 1 auto; + position: relative; + min-height: 120px; + } + + .scrollable { + flex: 1 1 auto; + } +} + +.scrollable.fullscreen { + @supports (display: grid) { + // hack to fix Chrome <57 + contain: none; + } +} + +.column-back-button { + box-sizing: border-box; + width: 100%; + background: transparent; + border: 1px solid var(--color-border-primary); + border-radius: 4px 4px 0 0; + color: var(--color-text-brand); + cursor: pointer; + flex: 0 0 auto; + font-size: 16px; + line-height: inherit; + text-align: unset; + padding: 13px; + margin: 0; + z-index: 3; + outline: 0; + display: flex; + align-items: center; + gap: 5px; + + &:hover { + text-decoration: underline; + } +} + +.column-header__back-button { + display: flex; + align-items: center; + gap: 5px; + background: transparent; + border: 0; + font-family: inherit; + color: var(--color-text-brand); + cursor: pointer; + white-space: nowrap; + font-size: 16px; + padding: 13px; + z-index: 3; + + &:hover { + text-decoration: underline; + } + + &.compact { + padding-inline-end: 5px; + flex: 0 0 auto; + } +} + +.react-toggle { + display: inline-block; + position: relative; + cursor: pointer; + background-color: transparent; + border: 0; + border-radius: 10px; + padding: 0; + user-select: none; + -webkit-tap-highlight-color: transparent; +} + +.react-toggle--focus, +.react-toggle:focus-within { + outline: var(--outline-focus-default); + outline-offset: 2px; + + .react-toggle-track { + border-color: transparent; + } +} + +.react-toggle-screenreader-only, +.sr-only { + border: 0; + clip-path: inset(50%); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.react-toggle--disabled { + cursor: not-allowed; + opacity: 0.5; + transition: opacity 0.25s; +} + +.react-toggle-track { + width: 32px; + height: 20px; + padding: 0; + border-radius: 10px; + background-color: rgb(from var(--color-bg-brand-softer) r g b / 50%); + border: 1px solid rgb(from var(--color-text-brand) r g b / 50%); + box-sizing: border-box; + + .react-toggle:hover:not(.react-toggle--disabled) & { + background-color: rgb( + from var(--color-bg-brand-softer) r g b / + calc(50% + var(--overlay-strength-brand)) + ); + } + + .react-toggle--checked & { + background-color: var(--color-bg-brand-base); + border-color: var(--color-bg-brand-base); + } + + .react-toggle--checked:not(.react-toggle--disabled):hover & { + background-color: var(--color-bg-brand-base-hover); + } +} + +.react-toggle-track-check, +.react-toggle-track-x { + display: none; +} + +.react-toggle-thumb { + position: absolute; + top: 2px; + inset-inline-start: 2px; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: var(--color-text-on-brand-base); + box-sizing: border-box; + transition: all 0.25s ease; + transition-property: border-color, left; + + .react-toggle--checked & { + inset-inline-start: 32px - 16px - 2px; + border-color: var(--color-bg-brand-base); + } +} + +.follow_requests-unlocked_explanation, +.switch-to-advanced { + color: var(--color-text-secondary); + background-color: var(--color-bg-secondary); + padding: 15px; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 12px; + font-size: 13px; + line-height: 18px; + + a { + color: var(--color-text-brand); + font-weight: bold; + } +} + +.column-link { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 12px; + font-size: 16px; + font-weight: 400; + text-decoration: none; + overflow: hidden; + white-space: nowrap; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + background: transparent; + border: 0; + border-left: 4px solid transparent; + box-sizing: border-box; + + &:hover, + &:focus, + &:active { + color: var(--color-text-primary); + } + + &.active { + color: var(--color-text-brand); + } + + &:focus { + outline: 0; + } + + &:focus-visible { + border-color: var(--color-text-brand); + border-radius: 0; + } + + &--logo { + background: transparent; + padding: 10px; + + &:hover, + &:focus, + &:active { + background: transparent; + } + } +} + +.column-link__badge { + display: inline-block; + border-radius: 4px; + font-size: 12px; + line-height: 19px; + font-weight: 500; + background: var(--color-bg-primary); + padding: 4px 8px; + margin: -6px 10px; +} + +.column-subheading { + background: var(--color-bg-secondary); + color: var(--color-text-secondary); + padding: 8px 20px; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + cursor: default; +} + +.getting-started__wrapper { + flex: 0 0 auto; +} + +.flex-spacer { + flex: 1 1 auto; +} + +.getting-started { + color: var(--color-text-tertiary); + overflow: auto; + border: 1px solid var(--color-border-primary); + border-top: 0; + + &__trends { + flex: 0 1 auto; + opacity: 1; + animation: fade 150ms linear; + margin-top: 10px; + + h4 { + border-bottom: 1px solid var(--color-border-primary); + padding: 10px; + font-size: 12px; + text-transform: uppercase; + font-weight: 500; + + a { + color: var(--color-text-secondary); + text-decoration: none; + } + } + + .trends__item { + border-bottom: 0; + padding: 10px; + + &__current { + color: var(--color-text-secondary); + } + } + } +} + +.keyboard-shortcuts { + padding: 8px 0 0; + overflow: hidden; + + thead { + position: absolute; + inset-inline-start: -9999px; + } + + td { + padding: 0 10px 8px; + } + + kbd { + display: inline-block; + } +} + +.status-card { + display: flex; + align-items: center; + position: relative; + font-size: 14px; + color: var(--color-text-secondary); + margin-top: 14px; + text-decoration: none; + overflow: hidden; + border: 1px solid var(--color-border-primary); + border-radius: 8px; + contain: inline-size layout paint style; + + &.bottomless { + border-radius: 8px 8px 0 0; + } + + &__actions { + bottom: 0; + inset-inline-start: 0; + position: absolute; + inset-inline-end: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + + & > div { + background: rgb(from var(--color-shadow-primary) r g b / 60%); + border-radius: 8px; + padding: 12px 9px; + backdrop-filter: $backdrop-blur-filter; + flex: 0 0 auto; + display: flex; + justify-content: center; + align-items: center; + } + + button, + a { + display: inline; + color: var(--color-text-primary); + background: transparent; + border: 0; + padding: 0 8px; + text-decoration: none; + font-size: 18px; + line-height: 18px; + + &:hover, + &:active, + &:focus { + color: var(--color-text-primary); + } + } + + a { + font-size: 19px; + position: relative; + bottom: -1px; + } + } +} + +a.status-card { + cursor: pointer; + + &:hover, + &:focus, + &:active { + .status-card__title, + .status-card__host, + .status-card__author, + .status-card__description { + color: var(--color-text-brand); + } + } +} + +.status-card a { + color: inherit; + text-decoration: none; + + &:hover, + &:focus, + &:active { + .status-card__title, + .status-card__host, + .status-card__author, + .status-card__description { + color: var(--color-text-brand); + } + } +} + +.status-card-photo { + cursor: zoom-in; + display: block; + text-decoration: none; + width: 100%; + height: auto; + margin: 0; +} + +.status-card-video { + // Firefox has a bug where frameborder=0 iframes add some extra blank space + // see https://bugzilla.mozilla.org/show_bug.cgi?id=155174 + overflow: hidden; + + iframe { + width: 100%; + height: 100%; + } +} + +.status-card__title { + display: block; + font-weight: 700; + font-size: 19px; + line-height: 24px; + color: var(--color-text-primary); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.status-card.expanded .status-card__title { + white-space: normal; + display: -webkit-box; + line-clamp: 2; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.status-card__content { + flex: 1 1 auto; + overflow: hidden; + padding: 15px; + box-sizing: border-box; + max-width: 100%; +} + +.status-card__host { + display: block; + font-size: 14px; + margin-bottom: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.status-card__author { + display: block; + margin-top: 8px; + font-size: 14px; + color: var(--color-text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + strong { + font-weight: 500; + } +} + +.status-card__description { + display: block; + margin-top: 8px; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.status-card__image { + flex: 0 0 auto; + width: 120px; + aspect-ratio: 1; + background: var(--color-bg-secondary); + position: relative; + + & > .icon { + width: 18px; + height: 18px; + position: absolute; + transform-origin: 50% 50%; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + } +} + +.status-card__image-image { + display: block; + margin: 0; + width: 100%; + height: 100%; + object-fit: cover; + background-size: cover; + background-position: center center; +} + +.status-card__image-preview { + display: block; + margin: 0; + width: 100%; + height: 100%; + object-fit: fill; + position: absolute; + top: 0; + inset-inline-start: 0; + z-index: 0; + background: var(--color-bg-primary); + + &--hidden { + display: none; + } +} + +.status-card.expanded { + flex-direction: column; + align-items: flex-start; +} + +.status-card.expanded .status-card__image { + width: 100%; + aspect-ratio: auto; +} + +.status-card__image, +.status-card__image-image, +.status-card__image-preview { + border-start-start-radius: 8px; + border-start-end-radius: 0; + border-end-end-radius: 0; + border-end-start-radius: 8px; +} + +.status-card.expanded .status-card__image, +.status-card.expanded .status-card__image-image, +.status-card.expanded .status-card__image-preview { + border-start-end-radius: 8px; + border-end-end-radius: 0; + border-end-start-radius: 0; +} + +.status-card.bottomless .status-card__image, +.status-card.bottomless .status-card__image-image, +.status-card.bottomless .status-card__image-preview { + border-end-end-radius: 0; + border-end-start-radius: 0; +} + +.status-card.expanded > a { + width: 100%; +} + +.load-more { + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-primary); + background-color: transparent; + border: 0; + font-size: inherit; + line-height: inherit; + width: 100%; + padding: 15px; + box-sizing: border-box; + text-decoration: none; + + &--large { + padding-block: 32px; + } + + &:is(button) { + &:hover { + background: var(--color-bg-secondary); + } + + &:focus-visible { + outline: 2px solid var(--color-text-brand); + outline-offset: -2px; + } + } + + .icon { + width: 22px; + height: 22px; + } +} + +.load-gap { + border-bottom: 1px solid var(--color-border-primary); +} + +.timeline-hint { + text-align: center; + color: var(--color-text-secondary); + padding: 16px; + box-sizing: border-box; + width: 100%; + font-size: 14px; + line-height: 21px; + + strong { + font-weight: 500; + } + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + color: var(--color-text-brand-soft); + } + } + + &--with-descendants { + border-top: 1px solid var(--color-border-primary); + } +} + +.regeneration-indicator { + color: var(--color-text-secondary); + border: 1px solid var(--color-border-primary); + border-top: 0; + cursor: default; + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; + + &__figure { + display: block; + width: 100%; + height: auto; + max-width: 350px; + margin-top: -50px; + } + + &__label { + text-align: center; + font-size: 16px; + + strong { + font-weight: 500; + display: block; + margin-bottom: 10px; + color: var(--color-text-secondary); + } + + span { + font-size: 15px; + } + } +} + +.column-header__wrapper { + position: relative; + flex: 0 0 auto; + z-index: 1; + + &.active { + box-shadow: 0 1px 0 var(--color-bg-brand-softer); + + &::before { + display: block; + content: ''; + position: absolute; + bottom: -13px; + inset-inline-start: 0; + inset-inline-end: 0; + margin: 0 auto; + width: 60%; + pointer-events: none; + height: 28px; + z-index: 1; + background: radial-gradient( + ellipse, + rgb(from var(--color-bg-brand-softer-base) r g b / 23%) 0%, + transparent 60% + ); + } + } + + .announcements { + z-index: 1; + position: relative; + } +} + +.column-header__select-row { + border-width: 0 1px 1px; + border-style: solid; + border-color: var(--color-border-primary); + padding: 15px; + display: flex; + align-items: center; + gap: 8px; + + &__checkbox .check-box { + display: flex; + } + + &__select-menu:disabled { + visibility: hidden; + } + + &__mode-button { + margin-left: auto; + color: var(--color-text-brand); + font-weight: bold; + font-size: 14px; + + &:hover { + color: var(--color-text-brand-soft); + } + } +} + +.column-header { + display: flex; + font-size: 16px; + border: 1px solid var(--color-border-primary); + border-radius: 4px 4px 0 0; + flex: 0 0 auto; + cursor: pointer; + position: relative; + z-index: 2; + outline: 0; + + &__title { + display: flex; + align-items: center; + gap: 5px; + margin: 0; + border: 0; + padding: 13px; + padding-inline-end: 0; + color: inherit; + background: transparent; + font: inherit; + text-align: start; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + flex: 1; + + &:focus-visible { + outline: var(--outline-focus-default); + } + + .logo { + height: 24px; + } + } + + .column-header__back-button + &__title { + padding-inline-start: 0; + } + + .column-header__back-button { + flex: 1; + color: var(--color-text-brand); + + &.compact { + flex: 0 0 auto; + color: var(--color-text-primary); + } + } + + &.active { + .column-header__icon { + color: var(--color-text-brand); + text-shadow: 0 0 10px var(--color-bg-brand-softer); + } + } + + &:focus, + &:active { + outline: 0; + } + + &__advanced-buttons { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + padding-top: 0; + + &:first-child { + padding-top: 16px; + } + } +} + +.column-header__buttons { + height: 48px; + display: flex; +} + +.column-header__links { + margin-bottom: 14px; +} + +.column-header__links .text-btn { + margin-inline-end: 10px; +} + +.column-header__button { + display: flex; + justify-content: center; + align-items: center; + border: 0; + color: var(--color-text-primary); + background: transparent; + cursor: pointer; + font-size: 16px; + padding: 0 15px; + + &:last-child { + border-start-end-radius: 4px; + } + + &:hover { + color: var(--color-text-secondary); + } + + &:focus-visible { + outline: var(--outline-focus-default); + } + + &.active { + color: var(--color-text-brand); + + &:hover { + color: var(--color-text-brand); + } + } + + &:disabled { + color: var(--color-text-disabled); + cursor: default; + } +} + +.no-reduce-motion .column-header__button .icon-sliders { + transition: transform 150ms ease-in-out; +} + +.column-header__collapsible { + max-height: 70vh; + overflow: hidden; + overflow-y: auto; + color: var(--color-text-secondary); + transition: + max-height 150ms ease-in-out, + opacity 300ms linear; + opacity: 1; + z-index: 1; + position: relative; + border-left: 1px solid var(--color-border-primary); + border-right: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + + @media screen and (max-width: $no-gap-breakpoint) { + border-left: 0; + border-right: 0; + } + + &.collapsed { + max-height: 0; + opacity: 0.5; + border-bottom: 0; + } + + &.animating { + overflow-y: hidden; + } + + hr { + height: 0; + background: transparent; + border: 0; + border-top: 1px solid var(--color-border-primary); + margin: 10px 0; + } +} + +.column-header__collapsible-inner { + border-top: 0; +} + +.column-header__setting-btn { + &:hover, + &:focus { + color: var(--color-text-secondary); + text-decoration: underline; + } +} + +.column-header__collapsible__extra + .column-header__setting-btn { + padding-top: 5px; +} + +.column-header__permission-btn { + display: inline; + font-weight: inherit; + text-decoration: underline; +} + +.column-header__setting-arrows { + display: flex; + align-items: center; +} + +.text-btn { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 0; + font-family: inherit; + font-size: inherit; + font-weight: inherit; + color: inherit; + border: 0; + background: transparent; + cursor: pointer; + text-decoration: none; + + .icon { + width: 13px; + height: 13px; + } +} + +.column-header__issue-btn { + color: var(--color-text-error); + + &:hover { + text-decoration: underline; + } +} + +.loading-indicator { + color: var(--color-text-secondary); + font-size: 12px; + font-weight: 400; + text-transform: uppercase; + overflow: visible; + position: absolute; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + display: flex; + align-items: center; + justify-content: center; +} + +.load-more .loading-indicator, +.button .loading-indicator, +.icon-button .loading-indicator { + position: static; + transform: none; + color: inherit; + + .circular-progress { + color: inherit; + width: 22px; + height: 22px; + } +} + +.button--compact .loading-indicator .circular-progress { + width: 17px; + height: 17px; +} + +.icon-button .loading-indicator .circular-progress { + color: var(--color-text-tertiary); + width: 12px; + height: 12px; + margin: 6px; +} + +.load-more .loading-indicator .circular-progress { + color: var(--color-text-tertiary); +} + +.circular-progress { + color: var(--color-text-tertiary); + animation: 1.4s linear 0s infinite normal none running simple-rotate; + + circle { + stroke: currentColor; + stroke-dasharray: 80px, 200px; + stroke-dashoffset: 0; + animation: circular-progress 1.4s ease-in-out infinite; + } +} + +@keyframes circular-progress { + 0% { + stroke-dasharray: 1px, 200px; + stroke-dashoffset: 0; + } + + 50% { + stroke-dasharray: 100px, 200px; + stroke-dashoffset: -15px; + } + + 100% { + stroke-dasharray: 100px, 200px; + stroke-dashoffset: -125px; + } +} + +@keyframes simple-rotate { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +@keyframes spring-rotate-in { + 0% { + transform: rotate(0deg); + } + + 30% { + transform: rotate(-484.8deg); + } + + 60% { + transform: rotate(-316.7deg); + } + + 90% { + transform: rotate(-375deg); + } + + 100% { + transform: rotate(-360deg); + } +} + +@keyframes spring-rotate-out { + 0% { + transform: rotate(-360deg); + } + + 30% { + transform: rotate(124.8deg); + } + + 60% { + transform: rotate(-43.27deg); + } + + 90% { + transform: rotate(15deg); + } + + 100% { + transform: rotate(0deg); + } +} + +.video-error-cover { + align-items: center; + background: var(--color-bg-primary); + color: var(--color-text-primary); + cursor: pointer; + display: flex; + flex-direction: column; + height: 100%; + justify-content: center; + margin-top: 8px; + position: relative; + text-align: center; + z-index: 100; +} + +.spoiler-button { + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + position: absolute; + z-index: 100; + + &--hidden { + display: none; + } + + &--click-thru { + pointer-events: none; + } + + &__overlay { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + border: 0; + color: var(--color-text-on-media); + line-height: 20px; + font-size: 14px; + + &__label { + background-color: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border-radius: 8px; + padding: 12px 16px; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + flex-direction: column; + font-weight: 600; + } + + &__action { + font-weight: 400; + font-size: 13px; + } + + &:hover, + &:focus { + .spoiler-button__overlay__label { + background-color: rgb(from var(--color-bg-media-base) r g b / 90%); + } + } + } +} + +.account--panel { + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + display: flex; + flex-direction: row; + padding: 10px 0; +} + +.account--panel__button, +.detailed-status__button { + flex: 1 1 auto; + text-align: center; +} + +.column-settings { + display: flex; + flex-direction: column; + + &__section { + // FIXME: Legacy + color: var(--color-text-secondary); + cursor: default; + display: block; + font-weight: 500; + } + + .column-header__links { + margin: 0; + } + + section { + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + } + + h3 { + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + font-weight: 500; + color: var(--color-text-primary); + margin-bottom: 16px; + } + + &__row { + display: flex; + flex-direction: column; + gap: 12px; + } + + .app-form__toggle { + &__toggle > div { + border: 0; + } + } +} + +.column-settings__hashtags { + margin-top: 15px; + + .column-settings__row { + margin-bottom: 15px; + } + + .column-select { + &__control { + @include search-input; + + &::placeholder { + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + } + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + + &:focus { + background: var(--color-bg-secondary); + } + + @media screen and (width <= 600px) { + font-size: 16px; + } + } + + &__placeholder { + color: var(--color-text-tertiary); + padding-inline-start: 2px; + font-size: 12px; + } + + &__value-container { + padding-inline-start: 6px; + } + + &__multi-value { + background: var(--color-bg-secondary); + + &__remove { + cursor: pointer; + + &:hover, + &:active, + &:focus { + background: var(--color-bg-brand-softer); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + } + } + } + + &__multi-value__label, + &__input, + &__input-container { + color: var(--color-text-secondary); + } + + &__clear-indicator, + &__dropdown-indicator { + cursor: pointer; + transition: none; + color: var(--color-text-tertiary); + + &:hover, + &:active, + &:focus { + color: var(--color-text-secondary); + } + } + + &__indicator-separator { + background-color: var(--color-border-primary); + } + + &__menu { + @include search-popout; + + padding: 0; + background: var(--color-bg-elevated); + } + + &__menu-list { + padding: 6px; + } + + &__option { + color: var(--color-text-primary); + border-radius: 4px; + font-size: 14px; + + &--is-focused, + &--is-selected { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + } + } + } +} + +.column-settings__row { + .text-btn:not(.column-header__permission-btn) { + margin-bottom: 15px; + } +} + +.setting-toggle { + display: flex; + align-items: center; + gap: 8px; +} + +.setting-toggle__label { + color: var(--color-text-secondary); +} + +.limited-account-hint { + p { + color: var(--color-text-primary); + font-size: 15px; + font-weight: 500; + margin-bottom: 20px; + } +} + +.empty-column-indicator { + color: var(--color-text-secondary); + text-align: center; + padding: 20px; + font-size: 14px; + line-height: 20px; + font-weight: 400; + cursor: default; + display: flex; + flex: 1 1 auto; + align-items: center; + justify-content: center; + + & > span { + max-width: 500px; + } + + a { + color: var(--color-text-brand); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +.empty-column-indicator { + &__arrow { + position: absolute; + top: 50%; + inset-inline-start: 50%; + pointer-events: none; + transform: translate(100%, -100%) rotate(12deg); + transform-origin: center; + } +} + +.follow_requests-unlocked_explanation { + margin: 16px; + margin-bottom: 0; +} + +.error-column { + padding: 20px; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + display: flex; + flex: 1 1 auto; + align-items: center; + justify-content: center; + flex-direction: column; + cursor: default; + + &__image { + width: 70%; + max-width: 350px; + margin-top: -50px; + } + + &__message { + text-align: center; + color: var(--color-text-secondary); + font-size: 15px; + line-height: 22px; + + h1 { + font-size: 28px; + line-height: 33px; + font-weight: 700; + margin-bottom: 15px; + color: var(--color-text-primary); + } + + p { + max-width: 48ch; + } + + &__actions { + margin-top: 30px; + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + } + } +} + +@keyframes heartbeat { + 0% { + transform: scale(1); + animation-timing-function: ease-out; + } + + 10% { + transform: scale(0.91); + animation-timing-function: ease-in; + } + + 17% { + transform: scale(0.98); + animation-timing-function: ease-out; + } + + 33% { + transform: scale(0.87); + animation-timing-function: ease-in; + } + + 45% { + transform: scale(1); + animation-timing-function: ease-out; + } +} + +.no-reduce-motion .pulse-loading { + transform-origin: center center; + animation: heartbeat 1.5s ease-in-out infinite both; +} + +.emoji-picker-dropdown__menu { + position: relative; + margin-top: 5px; + z-index: 2; + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + box-shadow: var(--dropdown-shadow); + border-radius: 5px; + + .emoji-mart-scroll { + transition: opacity 200ms ease; + } + + &.selecting .emoji-mart-scroll { + opacity: 0.5; + } +} + +.emoji-picker-dropdown__modifiers { + position: absolute; + top: 60px; + inset-inline-end: 11px; + cursor: pointer; +} + +.emoji-picker-dropdown__modifiers__menu { + position: absolute; + z-index: 4; + top: -5px; + inset-inline-start: -9px; + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + box-shadow: var(--dropdown-shadow); + overflow: hidden; + + button { + display: block; + cursor: pointer; + border: 0; + padding: 4px 8px; + background: transparent; + + &:hover, + &:focus, + &:active { + background: var(--color-border-primary); + } + } + + .emoji-mart-emoji { + height: 22px; + } +} + +.emoji-mart-emoji { + span { + background-repeat: no-repeat; + } +} + +.upload-area { + align-items: center; + background: rgb(from var(--color-bg-overlay) r g b / 80%); + display: flex; + height: 100vh; + justify-content: center; + inset-inline-start: 0; + opacity: 0; + position: fixed; + top: 0; + visibility: hidden; + width: 100vw; + z-index: 2000; + + * { + pointer-events: none; + } +} + +.upload-area__drop { + width: 320px; + height: 160px; + display: flex; + box-sizing: border-box; + position: relative; + padding: 8px; +} + +.upload-area__background { + position: absolute; + top: 0; + inset-inline-end: 0; + bottom: 0; + inset-inline-start: 0; + z-index: -1; + border-radius: 4px; + background: var(--color-bg-elevated); + box-shadow: 0 0 5px var(--color-shadow-primary); +} + +.upload-area__content { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + color: var(--color-text-primary); + font-size: 18px; + font-weight: 500; + border: 2px dashed var(--color-border-primary); + border-radius: 4px; +} + +.upload-progress { + color: var(--color-text-secondary); + overflow: hidden; + display: flex; + gap: 8px; + align-items: center; + padding: 0 12px; + + .icon { + width: 24px; + height: 24px; + color: var(--color-text-brand); + } + + span { + font-size: 12px; + text-transform: uppercase; + font-weight: 500; + display: block; + } +} + +.upload-progress__message { + flex: 1 1 auto; +} + +.upload-progress__backdrop { + width: 100%; + height: 6px; + border-radius: 6px; + background: var(--color-bg-primary); + position: relative; + margin-top: 5px; +} + +.upload-progress__tracker { + position: absolute; + inset-inline-start: 0; + top: 0; + height: 6px; + background: var(--color-text-brand); + border-radius: 6px; +} + +.emoji-button { + display: block; + padding-top: 5px; + padding-bottom: 2px; + padding-inline-start: 2px; + padding-inline-end: 5px; + outline: 0; + cursor: pointer; + + img { + filter: grayscale(100%); + opacity: 0.8; + display: block; + margin: 0; + width: 22px; + height: 22px; + } + + &:hover, + &:active, + &:focus { + img { + opacity: 1; + filter: none; + border-radius: 100%; + } + } + + &:focus-visible { + img { + outline: var(--outline-focus-default); + } + } +} + +.dropdown--active .emoji-button img { + opacity: 1; + filter: none; +} + +.privacy-dropdown__dropdown, +.language-dropdown__dropdown, +.visibility-dropdown__dropdown { + box-shadow: var(--dropdown-shadow); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); + padding: 4px; + border-radius: 4px; + overflow: hidden; + z-index: 2; + + &.top { + transform-origin: 50% 100%; + } + + &.bottom { + transform-origin: 50% 0; + } +} + +.modal-root__container .privacy-dropdown { + flex-grow: 0; +} + +.modal-root__container .privacy-dropdown__dropdown { + pointer-events: auto; + z-index: 9999; +} + +.privacy-dropdown__option, +.visibility-dropdown__option { + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + padding: 8px 12px; + cursor: pointer; + display: flex; + align-items: center; + gap: 12px; + border-radius: 4px; + color: var(--color-text-primary); + + // Make sure adjacent hover/active states don't have a meeting radius. + &:hover + &:is(:focus, .active), + &:is(:focus, .active) + &:hover, + &:is(:focus, .active) + &:is(:focus, .active) { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &:hover:has(+ :focus, .active), + &:is(:focus, .active):has(+ :hover), + &:is(:focus, .active):has(+ :is(:focus, .active)) { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + &:hover, + &:active { + background: var(--color-bg-secondary); + } + + &:focus, + &.active { + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + outline: 0; + + .privacy-dropdown__option__content, + .privacy-dropdown__option__content strong, + .privacy-dropdown__option__additional, + .visibility-dropdown__option__content, + .visibility-dropdown__option__content strong, + .visibility-dropdown__option__additional { + color: var(--color-text-on-brand-base); + } + } + + &__additional { + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-secondary); + cursor: help; + } +} + +.privacy-dropdown__option__icon, +.visibility-dropdown__option__icon { + display: flex; + align-items: center; + justify-content: center; +} + +.privacy-dropdown__option__content, +.visibility-dropdown__option__content { + flex: 1 1 auto; + color: var(--color-text-secondary); + + strong { + color: var(--color-text-primary); + font-weight: 500; + display: block; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } +} + +.language-dropdown { + &__dropdown { + width: 300px; + padding: 0; + + .emoji-mart-search { + padding: 10px; + background: var(--color-bg-elevated); + + input { + padding: 8px 12px; + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + color: var(--color-text-secondary); + + @media screen and (width <= 600px) { + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + } + } + } + + .emoji-mart-search-icon { + inset-inline-end: 15px; + opacity: 1; + color: var(--color-text-primary); + + .icon { + width: 18px; + height: 18px; + } + + &:disabled { + color: var(--color-text-secondary); + } + } + + .emoji-mart-scroll { + padding: 0 10px 10px; + background: var(--color-bg-elevated); + } + + &__results { + &__item { + display: flex; + align-items: center; + gap: 0.5em; + cursor: pointer; + color: var(--color-text-primary); + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + font-weight: 500; + padding: 8px 12px; + border-radius: 4px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + &__common-name { + color: var(--color-text-secondary); + font-weight: 400; + } + + &:active, + &:hover { + background: var(--color-bg-secondary); + } + + &:focus, + &.active { + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + outline: 0; + + .language-dropdown__dropdown__results__item__common-name { + color: var(--color-text-on-brand-base); + } + } + } + } + } +} + +.visibility-modal { + &__quote-warning { + color: var(--color-text-primary); + background: var(--color-bg-warning-softer); + padding: 16px; + border-radius: 4px; + + h3 { + font-weight: 500; + margin-bottom: 4px; + } + + p { + font-size: 0.8em; + } + } +} + +.visibility-dropdown { + &__overlay[data-popper-placement] { + z-index: 9999; + } + + &.disabled { + opacity: 0.6; + cursor: default; + } + + &__label { + display: block; + font-weight: 500; + margin-bottom: 8px; + } + + &__button { + display: flex; + align-items: center; + color: var(--color-text-primary); + background: var(--color-bg-secondary-solid); + border: 1px solid var(--color-border-primary); + padding: 8px 12px; + width: 100%; + text-align: left; + border-radius: 4px; + font-size: 14px; + height: 40px; + + &:disabled { + cursor: default; + } + } + + &__icon { + margin-inline: auto -4px; + width: 18px; + height: 18px; + opacity: 0.5; + } + + &__helper { + margin-top: 4px; + font-size: 0.8em; + color: var(--color-text-tertiary); + } +} + +.search { + margin-bottom: 32px; + position: relative; + + .layout-multiple-columns & { + margin-bottom: 10px; + } + + &__popout { + box-sizing: border-box; + display: none; + position: absolute; + inset-inline-start: 0; + margin-top: -2px; + width: 100%; + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); + border-radius: 0 0 4px 4px; + box-shadow: var(--dropdown-shadow); + z-index: 99; + font-size: 13px; + padding: 15px 5px; + + h4 { + text-transform: uppercase; + color: var(--color-text-secondary); + font-weight: 500; + padding: 0 10px; + margin-bottom: 10px; + } + + .icon-button { + padding: 0; + color: var(--color-text-secondary); + } + + .icon { + width: 18px; + height: 18px; + } + + &__menu { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + + &__message { + color: var(--color-text-secondary); + padding: 0 10px; + } + + &__item { + display: block; + box-sizing: border-box; + width: 100%; + border: 0; + font: inherit; + background: transparent; + color: var(--color-text-secondary); + padding: 10px; + cursor: pointer; + border-radius: 4px; + text-align: start; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + + &--flex { + display: flex; + justify-content: space-between; + } + + .icon-button { + transition: none; + } + + &:hover, + &:focus, + &:active, + &.selected { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + + .icon-button { + color: inherit; + } + } + + mark { + background: transparent; + font-weight: 700; + color: var(--color-text-primary); + } + + span { + overflow: inherit; + text-overflow: inherit; + } + } + } + } + + &.active { + .search__input { + border-radius: 4px 4px 0 0; + } + + .search__popout { + display: block; + } + } +} + +.search__input { + @include search-input; + + display: block; + padding: 12px 16px; + padding-inline-start: 16px + 15px + 8px; + line-height: normal; + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } +} + +.search__icon-wrapper { + position: absolute; + top: 14px; + display: grid; + margin-inline-start: 16px - 2px; + width: 20px; + height: 20px; + + .icon { + width: 100%; + height: 100%; + } + + &:not(.has-value) { + pointer-events: none; + } +} + +.search__icon { + grid-area: 1 / 1; + transition: all 100ms linear; + transition-property: transform, opacity; + color: var(--color-text-secondary); +} + +.search__icon.icon-search { + .has-value & { + pointer-events: none; + opacity: 0; + transform: rotate(90deg); + } +} + +.search__icon--clear-button { + background: transparent; + border: 0; + padding: 0; + width: 20px; + height: 20px; + border-radius: 100%; + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus { + outline: 0 !important; + } + + &:focus-visible { + box-shadow: 0 0 0 2px var(--color-text-brand); + } + + &[aria-hidden='true'] { + pointer-events: none; + opacity: 0; + transform: rotate(-90deg); + } +} + +.search-results__section { + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + + &__header { + border-bottom: 1px solid var(--color-border-primary); + background: var(--color-bg-tertiary); + padding: 15px; + font-weight: 500; + font-size: 14px; + color: var(--color-text-secondary); + display: flex; + justify-content: space-between; + + h3 { + display: flex; + align-items: center; + gap: 5px; + } + + button { + color: var(--color-text-brand); + padding: 0; + border: 0; + background: 0; + font: inherit; + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } + } + + .account:last-child, + & > div:last-child .status { + border-bottom: 0; + } +} + +.search-results__info { + padding: 20px; + color: var(--color-text-secondary); + text-align: center; +} + +.modal-root { + position: relative; + z-index: 9998; +} + +.modal-root__overlay { + position: fixed; + top: 0; + inset-inline-start: 0; + inset-inline-end: 0; + bottom: 0; + opacity: 0.9; + background: var(--color-bg-overlay); + transition: background 0.5s; +} + +.modal-root__container { + position: fixed; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + max-width: 100vw; + max-height: 100vh; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + align-content: space-around; + z-index: 9999; + pointer-events: none; + user-select: none; + overscroll-behavior: none; +} + +.modal-root__modal { + pointer-events: auto; + user-select: text; + display: flex; + max-width: 100vw; + + @media screen and (width <= $mobile-breakpoint) { + margin-top: auto; + } +} + +.video-modal .video-player { + max-height: 80vh; + max-width: 100vw; +} + +.audio-modal__container { + width: 50vw; +} + +.media-modal { + width: 100%; + height: 100%; + position: relative; + touch-action: pan-y; + + &__buttons { + position: absolute; + inset-inline-end: 8px; + top: 8px; + z-index: 100; + display: flex; + gap: 8px; + align-items: center; + + .icon-button { + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-on-media); + --hover-bg-color: rgb( + from var(--color-text-on-media) r g b / + var(--overlay-strength-secondary) + ); + + padding: 8px; + + .icon { + width: 24px; + height: 24px; + filter: var(--overlay-icon-shadow); + } + } + } +} + +.media-modal__closer { + display: flex; + position: absolute; + top: 0; + inset-inline-start: 0; + inset-inline-end: 0; + bottom: 0; + align-items: center; + justify-content: space-around; // If set to center, the fullscreen image overlay is misaligned. + + > div { + flex-shrink: 0; + overflow: auto; + } +} + +.media-modal__navigation { + position: absolute; + top: 0; + inset-inline-start: 0; + inset-inline-end: 0; + bottom: 0; + pointer-events: none; + transition: opacity 0.3s linear; + will-change: opacity; + + * { + pointer-events: auto; + } + + &.media-modal__navigation--hidden { + opacity: 0; + + * { + pointer-events: none; + } + } +} + +.media-modal__nav { + background: transparent; + box-sizing: border-box; + border: 0; + color: var(--color-text-on-media); + cursor: pointer; + display: flex; + align-items: center; + font-size: 24px; + height: 20vmax; + margin: auto 0; + padding: 30px 5px; + position: absolute; + top: 0; + bottom: 0; + transform: scaleX(var(--text-x-direction)); + + .icon { + border-radius: 5px; + padding: 10px; + } + + &:hover .icon, + &:focus .icon, + &:active .icon { + background: rgb( + from var(--color-text-on-media) r g b / var(--overlay-strength-secondary) + ); + } +} + +.media-modal__nav--prev { + inset-inline-start: 0; +} + +.media-modal__nav--next { + inset-inline-end: 0; +} + +.media-modal__overlay { + max-width: 600px; + position: absolute; + inset-inline-start: 0; + inset-inline-end: 0; + bottom: 0; + margin: 0 auto; + + .picture-in-picture__footer { + border-radius: 0; + border: none; + background: transparent; + backdrop-filter: none; + padding: 16px; + + .icon-button { + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-on-media); + --hover-bg-color: rgb( + from var(--color-text-on-media) r g b / var(--overlay-strength-brand) + ); + + .icon { + filter: var(--overlay-icon-shadow); + } + + &.active { + --default-icon-color: var(--color-text-brand); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: var(--color-bg-brand-soft); + } + + &.star-icon.active { + --default-icon-color: var(--color-text-favourite-highlight); + --hover-icon-color: var(--color-text-favourite-highlight); + --hover-bg-color: rgb( + from var(--color-text-favourite-highlight) r g b / + var(--overlay-strength-brand) + ); + } + + &.disabled { + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + + cursor: default; + opacity: 0.4; + } + } + } +} + +.media-modal__pagination { + display: flex; + justify-content: center; + margin-bottom: 20px; +} + +.media-modal__page-dot { + flex: 0 0 auto; + background-color: rgb(from var(--color-text-on-media) r g b / 40%); + filter: var(--overlay-icon-shadow); + height: 6px; + width: 6px; + border-radius: 50%; + margin: 0 4px; + padding: 0; + border: 0; + font-size: 0; + transition: background-color 0.2s ease-in-out; + + &.active { + background-color: var(--color-text-on-media); + } + + &:focus { + opacity: 1; + outline: 2px solid var(--color-text-on-media); + outline-offset: 2px; + } +} + +.modal-placeholder { + width: 588px; + min-height: 478px; + flex-direction: column; + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + border-radius: 16px; + + &__error { + padding: 24px; + display: flex; + align-items: center; + flex-direction: column; + + &__image { + width: 70%; + max-width: 350px; + } + + &__message { + text-align: center; + text-wrap: balance; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + + &__actions { + margin-top: 24px; + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + } + } + } +} + +.safety-action-modal { + width: 600px; + flex-direction: column; + + &__top, + &__bottom { + display: flex; + gap: 8px; + padding: 24px; + flex-direction: column; + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + } + + &__top { + border-radius: 16px 16px 0 0; + border-bottom: 0; + gap: 16px; + } + + &__bottom { + border-radius: 0 0 16px 16px; + border-top: 0; + + @media screen and (max-width: $mobile-breakpoint) { + border-radius: 0; + border-bottom: 0; + padding-bottom: 32px; + } + } + + &__header { + display: flex; + gap: 16px; + align-items: center; + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + + &__icon { + border-radius: 64px; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + flex-shrink: 0; + + .icon { + width: 24px; + height: 24px; + } + } + + h1 { + font-size: 22px; + line-height: 28px; + color: var(--color-text-primary); + } + } + + &__confirmation { + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + + h1 { + font-size: 16px; + line-height: 24px; + color: var(--color-text-primary); + font-weight: 500; + + &:not(:only-child) { + margin-bottom: 8px; + } + } + + strong { + font-weight: 700; + color: var(--color-text-primary); + } + } + + &__status { + border: 1px solid var(--color-border-primary); + border-radius: 8px; + padding: 8px; + cursor: pointer; + + &__account { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 8px; + color: var(--color-text-tertiary); + + bdi { + color: inherit; + } + } + + &__content { + display: -webkit-box; + font-size: 15px; + line-height: 22px; + color: var(--color-text-tertiary); + line-clamp: 4; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + max-height: 4 * 22px; + overflow: hidden; + + p, + a { + color: inherit; + } + } + + .reply-indicator__attachments { + margin-top: 0; + font-size: 15px; + line-height: 22px; + color: var(--color-text-tertiary); + } + } + + &__bullet-points { + display: flex; + flex-direction: column; + gap: 8px; + font-size: 16px; + line-height: 24px; + + & > div { + display: flex; + gap: 16px; + align-items: center; + + strong { + font-weight: 700; + } + } + + &--deemphasized { + color: var(--color-text-primary); + } + + &__icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + .icon { + width: 24px; + height: 24px; + } + } + } + + &__field-group { + display: flex; + flex-direction: column; + + label { + display: flex; + gap: 16px; + align-items: center; + font-size: 16px; + line-height: 24px; + height: 32px; + padding: 0 12px; + } + } + + &__caveats { + font-size: 14px; + padding: 0 12px; + + strong { + font-weight: 500; + } + } + + &__bottom { + padding-top: 0; + + &__collapsible { + display: none; + flex-direction: column; + gap: 16px; + } + + &.active { + background: var(--color-bg-secondary); + padding-top: 24px; + + .safety-action-modal__bottom__collapsible { + display: flex; + } + } + } + + &__actions { + display: flex; + align-items: center; + gap: 8px; + justify-content: flex-end; + + &__hint { + font-size: 14px; + line-height: 20px; + color: var(--color-text-tertiary); + } + + .link-button { + padding: 10px 12px; + font-weight: 600; + } + } +} + +.dialog-modal { + width: 588px; + max-height: 80vh; + flex-direction: column; + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + border-radius: 16px; + + &__header { + box-sizing: border-box; + border-bottom: 1px solid var(--color-border-primary); + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: row-reverse; + padding: 12px 24px; + min-height: 61px; + + &__title { + font-size: 16px; + line-height: 24px; + font-weight: 500; + letter-spacing: 0.15px; + } + } + + &__content { + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + overflow-y: auto; + + &__description { + margin: 24px 24px 0; + color: var(--color-text-secondary); + + a { + color: inherit; + } + } + + &__form { + display: flex; + flex-direction: column; + gap: 16px; + padding: 24px; + } + + &__preview { + display: flex; + flex-direction: column; + gap: 16px; + align-items: center; + justify-content: center; + padding: 24px; + background: var(--color-bg-media-base); + + img { + display: block; + } + + img, + .gifv video { + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + border-radius: 8px; + } + + img, + .gifv video, + .video-player, + .audio-player { + max-width: 360px; + max-height: 45vh; + } + } + + &__actions { + display: flex; + align-items: center; + gap: 8px; + justify-content: flex-end; + padding: 0 24px 24px; + } + } + + &__popout { + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + box-shadow: var(--dropdown-shadow); + max-width: 320px; + padding: 16px; + border-radius: 8px; + z-index: 9999 !important; + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + } + + .copy-paste-text { + margin-bottom: 0; + } +} + +.hotkey-combination { + display: inline-flex; + align-items: center; + gap: 4px; +} + +.boost-modal, +.report-modal, +.actions-modal, +.compare-history-modal { + background: var(--color-bg-primary); + color: var(--color-text-primary); + border-radius: 4px; + border: 1px solid var(--color-border-primary); + overflow: hidden; + max-width: 90vw; + width: 480px; + position: relative; + flex-direction: column; + + @media screen and (max-width: $no-columns-breakpoint) { + border-bottom: 0; + border-radius: 4px 4px 0 0; + padding-bottom: env(safe-area-inset-bottom); + } +} + +.boost-modal__container { + overflow-y: auto; + padding: 10px; + + .status { + user-select: text; + border-bottom: 0; + } +} + +.report-modal { + width: 90vw; + max-width: 700px; + border: 1px solid var(--color-border-primary); +} + +.report-dialog-modal { + max-width: 90vw; + width: 480px; + height: 80vh; + background: var(--color-bg-primary); + color: var(--color-text-primary); + border-radius: 4px; + border: 1px solid var(--color-border-primary); + overflow: hidden; + position: relative; + flex-direction: column; + display: flex; + + &__container { + box-sizing: border-box; + border-top: 1px solid var(--color-border-primary); + padding: 20px; + flex-grow: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: auto; + } + + &__title { + font-size: 28px; + line-height: 33px; + font-weight: 700; + margin-bottom: 15px; + + @media screen and (height <= 800px) { + font-size: 22px; + } + } + + &__subtitle { + font-size: 17px; + font-weight: 600; + line-height: 22px; + margin-bottom: 4px; + } + + &__lead { + font-size: 17px; + line-height: 22px; + color: var(--color-text-primary); + margin-bottom: 30px; + + a { + text-decoration: none; + color: var(--color-text-brand); + font-weight: 500; + + &:hover { + text-decoration: underline; + } + } + } + + &__actions { + margin-top: 30px; + display: flex; + + .button { + flex: 1 1 auto; + } + } + + &__statuses { + flex-grow: 1; + min-height: 0; + overflow: auto; + } + + .status__content a { + color: var(--color-text-brand); + } + + .status__content, + .status__content p { + color: var(--color-text-primary); + } + + .dialog-option { + align-items: center; + gap: 12px; + } + + .dialog-option .poll__input { + color: var(--color-text-secondary); + display: inline-flex; + align-items: center; + justify-content: center; + + svg { + width: 15px; + height: 15px; + } + + &:active, + &:focus, + &:hover { + border-color: var(--color-text-success); + border-width: 4px; + } + + &.active { + color: var(--color-text-on-success-base); + background: var(--color-bg-success-base); + border-color: var(--color-bg-success-base); + } + } + + .poll__option.dialog-option { + padding: 15px 0; + flex: 0 0 auto; + border-bottom: 1px solid var(--color-border-primary); + + &:last-child { + border-bottom: 0; + } + + & > .poll__option__text { + font-size: 13px; + color: var(--color-text-primary); + + strong { + font-size: 17px; + font-weight: 500; + line-height: 22px; + color: var(--color-text-primary); + display: block; + margin-bottom: 4px; + + &:last-child { + margin-bottom: 0; + } + } + } + } + + .flex-spacer { + background: transparent; + } + + &__textarea { + display: block; + box-sizing: border-box; + width: 100%; + color: var(--color-text-primary); + background: var(--color-bg-secondary); + padding: 10px; + font-family: inherit; + font-size: 17px; + line-height: 22px; + resize: vertical; + border: 0; + border: 1px solid var(--color-border-primary); + outline: 0; + border-radius: 4px; + margin: 20px 0; + + &:focus { + outline: 0; + } + } + + &__toggle { + display: flex; + align-items: center; + margin-bottom: 16px; + gap: 8px; + + & > span { + display: block; + font-size: 14px; + font-weight: 500; + line-height: 20px; + } + } + + .button.button-secondary { + border-color: var(--color-text-error); + color: var(--color-text-error); + flex: 0 0 auto; + + &:hover, + &:focus, + &:active { + color: var(--color-text-on-error-base); + background: var(--color-bg-error-base); + border-color: var(--color-bg-error-base); + } + } + + hr { + border: 0; + background: transparent; + margin: 15px 0; + } + + .emoji-mart-search { + padding-inline-end: 10px; + } + + .emoji-mart-search-icon { + inset-inline-end: 10px + 5px; + } +} + +.setting-divider { + background: transparent; + border: 0; + margin: 0; + width: 100%; + height: 1px; + margin-bottom: 29px; +} + +.actions-modal { + border-radius: 8px 8px 0 0; + background: var(--color-bg-elevated); + backdrop-filter: $backdrop-blur-filter; + border-color: var(--color-border-primary); + box-shadow: var(--dropdown-shadow); + max-height: 80vh; + max-width: 80vw; + + ul { + overflow-y: auto; + padding-bottom: 8px; + } + + a, + button { + color: inherit; + display: flex; + padding: 16px; + font-size: 15px; + line-height: 21px; + background: transparent; + border: none; + align-items: center; + text-decoration: none; + width: 100%; + box-sizing: border-box; + + &:hover, + &:active, + &:focus { + background: var(--color-bg-brand-softer); + } + } +} + +.report-modal__target { + padding: 30px; + font-size: 16px; + + strong { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } +} + +.report-modal__target { + text-align: center; +} + +.report-modal__target { + padding: 15px; + + .report-modal__close { + position: absolute; + top: 10px; + inset-inline-end: 10px; + } +} + +.compare-history-modal { + .report-modal__target { + border-bottom: 1px solid var(--color-border-primary); + } + + &__container { + padding: 30px; + pointer-events: all; + overflow-y: auto; + } + + .status__content { + color: var(--color-text-primary); + font-size: 19px; + line-height: 24px; + + .emojione { + width: 24px; + height: 24px; + margin: -1px 0 0; + } + + a { + color: var(--color-text-brand); + } + + hr { + height: 0.25rem; + padding: 0; + background-color: var(--color-text-primary); + border: 0; + margin: 20px 0; + } + } + + .media-gallery, + .audio-player, + .video-player { + margin-top: 15px; + } +} + +.loading-bar { + background-color: var(--color-bg-brand-base); + height: 3px; + position: fixed; + top: 0; + inset-inline-start: 0; + z-index: 9999; +} + +.media-gallery__actions { + position: absolute; + top: 6px; + inset-inline-end: 6px; + display: flex; + gap: 2px; + z-index: 2; + + &__pill { + display: block; + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border: 0; + padding: 3px 12px; + border-radius: 99px; + font-size: 14px; + font-weight: 700; + line-height: 20px; + } +} + +.media-gallery__item__badges { + position: absolute; + bottom: 8px; + inset-inline-end: 8px; + display: flex; + gap: 2px; + pointer-events: none; +} + +.media-gallery__alt__label, +.relationship-tag { + display: block; + text-align: center; + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border: 0; + padding: 3px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 700; + z-index: 1; + line-height: 20px; + cursor: pointer; + pointer-events: auto; + + &--non-interactive { + pointer-events: none; + } +} + +.relationship-tag { + text-transform: uppercase; + cursor: default; +} + +.info-tooltip { + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border-radius: 4px; + box-shadow: var(--dropdown-shadow); + padding: 16px; + min-width: 16em; + min-height: 2em; + max-width: 22em; + max-height: 30em; + overflow-y: auto; + z-index: 10; + + &--solid { + color: var(--color-text-primary); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); + } + + h4 { + font-size: 15px; + line-height: 20px; + font-weight: 500; + margin-bottom: 8px; + } + + p { + font-size: 15px; + line-height: 20px; + opacity: 0.85; + white-space: pre-line; + } + + .button { + margin-block-start: 8px; + } +} + +.attachment-list { + display: flex; + font-size: 14px; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + margin-top: 16px; + overflow: hidden; + + &__icon { + flex: 0 0 auto; + color: var(--color-text-tertiary); + padding: 8px 18px; + cursor: default; + border-inline-end: 1px solid var(--color-border-primary); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 26px; + } + + &__list { + list-style: none; + padding: 4px 0; + padding-inline-start: 8px; + display: flex; + flex-direction: column; + justify-content: center; + + li { + display: block; + padding: 4px 0; + } + + a { + text-decoration: none; + color: var(--color-text-tertiary); + font-weight: 500; + + &:hover { + text-decoration: underline; + } + } + } + + &.compact { + border: 0; + + .attachment-list__list { + padding: 0; + display: block; + } + + .icon { + color: var(--color-text-tertiary); + vertical-align: middle; + } + } +} + +/* Media Gallery */ +.media-gallery { + box-sizing: border-box; + margin-top: 8px; + overflow: hidden; + border-radius: 8px; + position: relative; + width: 100%; + min-height: 64px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 2px; + + &--layout-2 { + & > .media-gallery__item:nth-child(1) { + border-end-end-radius: 0; + border-start-end-radius: 0; + } + + & > .media-gallery__item:nth-child(2) { + border-start-start-radius: 0; + border-end-start-radius: 0; + } + } + + &--layout-3 { + min-height: calc(64px * 2 + 8px); + + & > .media-gallery__item:nth-child(1) { + border-end-end-radius: 0; + border-start-end-radius: 0; + } + + & > .media-gallery__item:nth-child(2) { + border-start-start-radius: 0; + border-end-start-radius: 0; + border-end-end-radius: 0; + } + + & > .media-gallery__item:nth-child(3) { + border-start-start-radius: 0; + border-end-start-radius: 0; + border-start-end-radius: 0; + } + } + + &--layout-4 { + min-height: calc(64px * 2 + 8px); + + & > .media-gallery__item:nth-child(1) { + border-end-end-radius: 0; + border-start-end-radius: 0; + border-end-start-radius: 0; + } + + & > .media-gallery__item:nth-child(2) { + border-start-start-radius: 0; + border-end-start-radius: 0; + border-end-end-radius: 0; + } + + & > .media-gallery__item:nth-child(3) { + border-start-start-radius: 0; + border-start-end-radius: 0; + border-end-start-radius: 0; + border-end-end-radius: 0; + } + + & > .media-gallery__item:nth-child(4) { + border-start-start-radius: 0; + border-end-start-radius: 0; + border-start-end-radius: 0; + } + } +} + +.media-gallery__item { + border: 0; + box-sizing: border-box; + display: block; + position: relative; + border-radius: 8px; + overflow: hidden; + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + z-index: 1; + + &--tall { + grid-row: span 2; + } + + &--wide { + grid-column: span 2; + } + + &--square { + aspect-ratio: 1; + } + + &__overlay { + position: absolute; + top: 0; + inset-inline-start: 0; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 100%; + height: 100%; + pointer-events: none; + padding: 8px; + z-index: 1; + + &--corner { + align-items: flex-start; + justify-content: flex-end; + } + + .icon { + color: var(--color-text-on-media); + filter: drop-shadow(0 0 6px var(--color-bg-media-base)); + } + } + + &--error img { + visibility: hidden; + } +} + +.media-gallery__item-thumbnail { + cursor: pointer; + display: block; + text-decoration: none; + color: var(--color-text-primary); + position: relative; + z-index: -1; + + &, + img { + height: 100%; + width: 100%; + } + + img { + object-fit: cover; + } +} + +.media-gallery__preview { + width: 100%; + height: 100%; + object-fit: cover; + position: absolute; + top: 0; + inset-inline-start: 0; + z-index: -2; + background: var(--color-bg-overlay); + + &--hidden { + display: none; + } +} + +.media-gallery__gifv { + height: 100%; + overflow: hidden; + position: relative; + width: 100%; + z-index: -1; +} + +.media-gallery__item-gifv-thumbnail { + cursor: pointer; + height: 100%; + object-fit: cover; + width: 100%; +} + +/* End Media Gallery */ + +.detailed, +.fullscreen { + .video-player__volume__current, + .video-player__volume::before { + bottom: 27px; + } + + .video-player__volume__handle { + bottom: 23px; + } +} + +.audio-player { + /* These are only fallback values, the AudioPlayer component inserts + * the real colours dynamically as inline styles */ + --player-foreground-color: var(--color-text-on-media); + --player-background-color: var(--color-bg-media-base); + --player-accent-color: var(--color-text-brand); + + box-sizing: border-box; + container: audio-player / inline-size; + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + width: 100%; + aspect-ratio: 16 / 9; + color: var(--player-foreground-color); + background: var(--player-background-color); + border-radius: 8px; + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + + &__controls { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + height: 100%; + + &__play { + display: flex; + align-items: center; + justify-content: center; + position: relative; + + .player-button { + position: absolute; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + } + + .icon { + filter: var(--overlay-icon-shadow); + } + } + + .player-button { + display: inline-block; + outline: 0; + padding: 5px; + flex: 0 0 auto; + background: transparent; + border: 0; + color: var(--player-foreground-color); + opacity: 0.75; + + .icon { + width: 48px; + height: 48px; + } + + &:active, + &:hover, + &:focus { + opacity: 1; + } + } + } + + &__visualizer { + width: 100%; + max-width: 200px; + } + + .video-player__seek { + position: absolute; + inset: 0 0 auto; + height: 24px; + z-index: 1; /* Ensure this renders on top of audio player controls */ + } + + &.inactive { + .video-player__seek, + .audio-player__controls, + .video-player__controls { + visibility: hidden; + } + } + + .video-player__volume::before, + .video-player__seek::before { + background: currentColor; + opacity: 0.15; + } + + .video-player__seek__buffer { + background: currentColor; + opacity: 0.2; + } + + .video-player__seek__progress, + .video-player__seek__handle, + .video-player__volume__current, + .video-player__volume__handle { + background-color: var(--player-accent-color); + } + + .video-player__buttons button, + .video-player__buttons a { + color: currentColor; + opacity: 0.75; + + &:active, + &:hover, + &:focus { + color: currentColor; + opacity: 1; + } + } + + .video-player__time-sep, + .video-player__time-total, + .video-player__time-current { + color: currentColor; + } + + @container audio-player (max-width: 400px) { + .video-player__time, + .player-button.video-player__download__icon { + display: none; + } + } + + .video-player__seek::before, + .video-player__seek__buffer, + .video-player__seek__progress { + top: 0; + } + + .video-player__seek__handle { + top: -4px; + } + + .video-player__controls { + padding-top: 10px; + background: transparent; + z-index: 1; + } +} + +.video-player { + overflow: hidden; + position: relative; + color: var(--color-text-on-media); + background: var(--color-bg-media); + max-width: 100%; + border-radius: 8px; + box-sizing: border-box; + display: flex; + align-items: center; + outline: 1px solid var(--color-border-media); + outline-offset: -1px; + z-index: 2; + + video { + display: block; + z-index: -2; + } + + &.fullscreen { + width: 100% !important; + height: 100% !important; + margin: 0; + aspect-ratio: auto !important; + outline: none; + border-radius: 0; + + video { + width: 100% !important; + height: 100% !important; + outline: 0; + } + } + + &__controls { + position: absolute; + direction: ltr; + z-index: -1; + bottom: 0; + inset-inline-start: 0; + inset-inline-end: 0; + box-sizing: border-box; + background: linear-gradient( + 0deg, + rgb(from var(--color-bg-media-base) r g b / 85%) 0, + rgb(from var(--color-bg-media-base) r g b / 45%) 60%, + transparent + ); + padding: 0 15px; + opacity: 0; + pointer-events: none; + transition: opacity 0.1s ease; + will-change: opacity, pointer-events; + + &.active { + opacity: 1; + pointer-events: auto; + } + } + + .media-gallery__actions { + opacity: 0; + transition: opacity 0.1s ease; + + &.active { + opacity: 1; + } + } + + &.inactive { + video, + .video-player__controls { + visibility: hidden; + } + } + + &__spoiler { + display: none; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + z-index: 4; + border: 0; + background: var(--color-bg-primary); + color: var(--color-text-secondary); + transition: none; + pointer-events: none; + + &.active { + display: block; + pointer-events: auto; + + &:hover, + &:active, + &:focus { + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + } + } + + &__title { + display: block; + font-size: 14px; + } + + &__subtitle { + display: block; + font-size: 11px; + font-weight: 500; + } + } + + &__buttons-bar { + display: flex; + justify-content: space-between; + padding-bottom: 8px; + margin: 0 -5px; + + .video-player__download__icon { + color: inherit; + } + } + + &__buttons { + display: flex; + flex: 0 1 auto; + min-width: 30px; + align-items: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + gap: 5px; + + .player-button { + display: inline-block; + outline: 0; + padding: 5px; + flex: 0 0 auto; + background: transparent; + border: 0; + color: rgb(from var(--color-text-on-media) r g b / 75%); + font-weight: 500; + + &:active, + &:hover, + &:focus { + color: var(--color-text-on-media); + } + } + } + + &__time { + display: inline; + flex: 0 1 auto; + overflow: hidden; + text-overflow: ellipsis; + margin: 0 5px; + + // Ensure digits maintain a consistent width + font-variant-numeric: tabular-nums; + } + + &__time-sep, + &__time-total, + &__time-current { + color: var(--color-text-on-media); + font-size: 14px; + font-weight: 500; + } + + &__time-sep { + display: inline-block; + margin: 0 6px; + } + + &__volume { + flex: 0 0 auto; + display: inline-flex; + cursor: pointer; + height: 24px; + position: relative; + overflow: hidden; + + .no-reduce-motion & { + transition: all 100ms linear; + } + + &.active { + overflow: visible; + width: 50px; + margin-inline-end: 16px; + } + + &::before { + content: ''; + width: 50px; + background: rgb(from var(--color-text-on-media) r g b / 35%); + border-radius: 4px; + display: block; + position: absolute; + height: 4px; + inset-inline-start: 0; + top: 50%; + transform: translate(0, -50%); + } + + &__current { + display: block; + position: absolute; + height: 4px; + border-radius: 4px; + inset-inline-start: 0; + top: 50%; + transform: translate(0, -50%); + background: var(--color-text-on-media); + } + + &__handle { + position: absolute; + z-index: 3; + border-radius: 50%; + width: 12px; + height: 12px; + top: 50%; + inset-inline-start: 0; + margin-inline-start: -6px; + transform: translate(0, -50%); + background: var(--color-text-on-media); + box-shadow: 1px 2px 6px var(--color-shadow-primary); + opacity: 0; + + .no-reduce-motion & { + transition: opacity 100ms linear; + } + } + + &.active &__handle { + opacity: 1; + } + } + + &__link { + padding: 2px 10px; + + a { + text-decoration: none; + font-size: 14px; + font-weight: 500; + color: var(--color-text-on-media); + + &:hover, + &:active, + &:focus { + text-decoration: underline; + } + } + } + + &__seek { + cursor: pointer; + height: 24px; + position: relative; + + &::before { + content: ''; + width: 100%; + background: rgb(from var(--color-text-on-media) r g b / 35%); + border-radius: 4px; + display: block; + position: absolute; + height: 4px; + top: 14px; + } + + &__progress, + &__buffer { + display: block; + position: absolute; + height: 4px; + border-radius: 4px; + top: 14px; + background: var(--color-text-on-media); + } + + &__buffer { + background: rgb(from var(--color-text-on-media) r g b / 20%); + } + + &__handle { + position: absolute; + z-index: 3; + opacity: 0; + border-radius: 50%; + width: 12px; + height: 12px; + top: 10px; + margin-inline-start: -6px; + background: var(--color-text-on-media); + box-shadow: 1px 2px 6px var(--color-shadow-primary); + + .no-reduce-motion & { + transition: opacity 0.1s ease; + } + + &.active { + opacity: 1; + cursor: grabbing; + } + } + + &:hover { + .video-player__seek__handle { + opacity: 1; + } + } + } + + &__hotkey-indicator { + position: absolute; + top: 50%; + inset-inline-start: 50%; + transform: translate(-50%, -50%); + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; + border-radius: 8px; + padding: 16px 24px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + + &__label { + font-size: 15px; + font-weight: 500; + } + } + + &.detailed, + &.fullscreen { + .video-player__buttons { + .player-button { + padding-top: 10px; + padding-bottom: 10px; + } + } + } +} + +.gifv { + position: relative; + + canvas { + position: absolute; + width: 100%; + height: 100%; + } + + video { + max-width: 100vw; + max-height: 80vh; + } +} + +.scrollable .account-card { + margin: 10px; +} + +.scrollable .account-card__title__avatar { + img { + border: 2px solid var(--color-bg-primary); + } + + .account__avatar { + border: none; + } +} + +.scrollable .account-card__header { + img { + border-radius: 4px; + } +} + +.scrollable .account-card__bio::after { + background: linear-gradient(to left, var(--color-bg-primary), transparent); +} + +.account-gallery__container { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 2px; + + .media-gallery__item { + border-radius: 0; + } + + .load-more, + .timeline-hint { + grid-column: span 3; + } +} + +.notification__filter-bar, +.account__section-headline { + border: 1px solid var(--color-border-primary); + border-top: 0; + cursor: default; + display: flex; + flex-shrink: 0; + + @media screen and (max-width: ($no-gap-breakpoint - 1px)) { + border-right: 0; + border-left: 0; + } + + button { + background: transparent; + border: 0; + margin: 0; + } + + button, + a { + display: block; + flex: 1 1 auto; + color: var(--color-text-secondary); + padding: 15px 0; + font-size: 14px; + font-weight: 500; + text-align: center; + text-decoration: none; + position: relative; + width: 100%; + white-space: nowrap; + + &.active { + color: var(--color-text-primary); + + &::before { + display: block; + content: ''; + position: absolute; + bottom: -1px; + left: 50%; + transform: translateX(-50%); + width: 40px; + height: 3px; + border-radius: 4px 4px 0 0; + background: var(--color-text-brand); + } + } + } + + .scrollable & { + border-left: 0; + border-right: 0; + } +} + +.filter-form { + border-bottom: 1px solid var(--color-border-primary); + + &__column { + display: flex; + flex-direction: column; + gap: 15px; + padding: 15px; + } + + .radio-button { + display: flex; + } +} + +.column-settings__row .radio-button { + display: flex; +} + +.radio-button, +.check-box { + font-size: 14px; + position: relative; + display: inline-flex; + align-items: center; + line-height: 18px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + gap: 10px; + color: var(--color-text-primary); + + input[type='radio'], + input[type='checkbox'] { + display: none; + } + + &__input { + display: flex; + align-items: center; + justify-content: center; + position: relative; + border: 2px solid var(--color-text-primary); + box-sizing: border-box; + width: 20px; + height: 20px; + flex: 0 0 auto; + border-radius: 50%; + + &.checked, + &.indeterminate { + border-color: var(--color-text-brand); + } + + .icon { + width: 18px; + height: 18px; + } + } +} + +.radio-button__input.checked::before { + content: ''; + display: block; + border-radius: 50%; + width: calc(100% - 4px); + height: calc(100% - 4px); + background: var(--color-text-brand); +} + +.check-box { + &__input { + width: 18px; + height: 18px; + border-radius: 2px; + + &.checked, + &.indeterminate { + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + } + } +} + +noscript { + text-align: center; + + img { + width: 200px; + opacity: 0.5; + animation: flicker 4s infinite; + } + + div { + font-size: 14px; + margin: 30px auto; + color: var(--color-text-primary); + max-width: 400px; + + a { + color: var(--color-text-brand); + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } + } +} + +@keyframes flicker { + 0% { + opacity: 1; + } + + 30% { + opacity: 0.75; + } + + 100% { + opacity: 1; + } +} + +.moved-account-banner, +.follow-request-banner, +.account-memorial-banner { + padding: 20px; + background: var(--color-bg-tertiary); + display: flex; + align-items: center; + flex-direction: column; + + &__message { + color: var(--color-text-secondary); + padding: 8px 0; + padding-top: 0; + padding-bottom: 4px; + font-size: 14px; + font-weight: 500; + text-align: center; + margin-bottom: 16px; + } + + &__action { + display: flex; + justify-content: space-between; + align-items: center; + gap: 15px; + width: 100%; + } + + .detailed-status__display-name { + margin-bottom: 0; + } +} + +.follow-request-banner .button { + width: 100%; +} + +.account-memorial-banner__message { + margin-bottom: 0; +} + +.column-inline-form { + padding: 15px; + display: flex; + justify-content: flex-start; + gap: 15px; + align-items: center; + border: 1px solid var(--color-border-primary); + border-top: 0; + + label { + flex: 1 1 auto; + + input { + width: 100%; + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + border-left: 0; + border-right: 0; + } +} + +.drawer__backdrop { + cursor: pointer; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + background: rgb(from var(--color-bg-overlay) r g b / 50%); +} + +.focal-point { + position: relative; + cursor: grab; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + + &.dragging { + cursor: grabbing; + } + + &__reticle { + position: absolute; + width: 100px; + height: 100px; + transform: translate(-50%, -50%); + border: 2px solid var(--color-text-on-media); + border-radius: 50%; + box-shadow: 0 0 0 9999em var(--color-shadow-primary); + pointer-events: none; + } +} + +.account__header__content { + color: var(--color-text-secondary); + font-size: 14px; + font-weight: 400; + overflow: hidden; + word-break: normal; + overflow-wrap: break-word; + + p { + margin-bottom: 20px; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: inherit; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + } +} + +.account__header { + overflow: hidden; + container: account-header / inline-size; + + &.inactive { + opacity: 0.5; + + .account__header__image, + .account__avatar { + filter: grayscale(100%); + } + } + + &__info { + position: absolute; + top: 20px; + inset-inline-end: 20px; + display: flex; + flex-wrap: wrap; + gap: 2px; + } + + &__image { + overflow: hidden; + height: 145px; + position: relative; + background: var(--color-bg-tertiary); + border-bottom: 1px solid var(--color-border-primary); + + img { + object-fit: cover; + display: block; + width: 100%; + height: 100%; + margin: 0; + } + } + + &__bar { + position: relative; + padding: 0 20px; + border-bottom: 1px solid var(--color-border-primary); + + .avatar { + display: block; + flex: 0 0 auto; + + .account__avatar { + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: var(--avatar-border-radius); + } + } + } + + &__badges { + display: flex; + flex-wrap: wrap; + gap: 8px; + + .account-role { + line-height: unset; + } + } + + &__tabs { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-top: -55px; + padding-top: 10px; + gap: 8px; + overflow: hidden; + margin-inline-start: -2px; // aligns the pfp with content below + + &__name { + margin-top: 16px; + margin-bottom: 16px; + + .emojione { + width: 22px; + height: 22px; + } + + h1 { + font-size: 17px; + line-height: 22px; + color: var(--color-text-primary); + font-weight: 600; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + small { + display: flex; + align-items: center; + gap: 4px; + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + + span { + overflow: hidden; + text-overflow: ellipsis; + user-select: all; + } + + .icon-lock { + height: 18px; + width: 18px; + } + } + } + } + + .spacer { + flex: 1 1 auto; + } + } + + &__follow-button { + flex-grow: 1; + } + + &__buttons { + display: flex; + align-items: center; + gap: 8px; + + $button-breakpoint: 420px; + $button-fallback-breakpoint: #{$button-breakpoint} + 55px; + + &--desktop { + margin-top: 55px; + + @container (width < #{$button-breakpoint}) { + display: none; + } + + @supports (not (container-type: inline-size)) { + @media (max-width: #{$button-fallback-breakpoint}) { + display: none; + } + } + } + + &--mobile { + margin-block: 16px; + + @container (width >= #{$button-breakpoint}) { + display: none; + } + + @supports (not (container-type: inline-size)) { + @media (min-width: (#{$button-fallback-breakpoint} + 1px)) { + display: none; + } + } + } + + .button { + flex-shrink: 1; + white-space: nowrap; + min-width: 80px; + } + + .icon-button { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + box-sizing: content-box; + padding: 5px; + + .icon { + width: 24px; + height: 24px; + } + + &.copied { + border-color: var(--color-text-success); + } + } + } + + &__bio { + .account__header__content { + color: var(--color-text-primary); + } + + .account__header__fields { + margin: 0; + margin-top: 16px; + border-radius: 4px; + border: 1px solid var(--color-border-primary); + + dl { + display: block; + padding: 11px 16px; + border-bottom-color: var(--color-border-primary); + } + + dd, + dt { + font-size: 13px; + line-height: 18px; + padding: 0; + text-align: initial; + } + + dt { + width: auto; + background: transparent; + text-transform: uppercase; + color: var(--color-text-tertiary); + } + + dd { + color: var(--color-text-secondary); + } + + a { + color: var(--color-text-brand); + } + + .icon { + width: 18px; + height: 18px; + } + + .verified { + border: 1px solid var(--color-text-success); + margin-top: -1px; + margin-inline: -1px; + + &:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + + &:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + margin-bottom: -1px; + } + + dt, + dd { + color: var(--color-text-success); + } + + dd { + display: flex; + align-items: center; + gap: 4px; + + span { + display: flex; + } + } + + a { + color: var(--color-text-success); + } + } + } + } + + &__extra { + margin-top: 16px; + + &__links { + font-size: 14px; + color: var(--color-text-secondary); + margin: 0 -10px; + padding-top: 16px; + padding-bottom: 10px; + + a { + display: inline-block; + color: var(--color-text-secondary); + text-decoration: none; + padding: 5px 10px; + font-weight: 500; + + strong { + font-weight: 700; + color: var(--color-text-primary); + } + } + } + } + + &__account-note { + color: var(--color-text-primary); + font-size: 14px; + font-weight: 400; + margin-bottom: 10px; + + &__loading-indicator-wrapper { + position: relative; + height: 37px; + + .loading-indicator { + left: 10px; + } + + .circular-progress { + width: 14px; + height: 14px; + } + } + + label { + display: block; + font-size: 12px; + font-weight: 500; + color: var(--color-text-secondary); + text-transform: uppercase; + margin-bottom: 5px; + } + + textarea { + display: block; + box-sizing: border-box; + width: calc(100% + 20px); + color: var(--color-text-primary); + background: transparent; + padding: 10px; + margin: 0 -10px; + font-family: inherit; + font-size: 14px; + resize: none; + border: 0; + outline: 0; + border-radius: 4px; + + &::placeholder { + color: var(--color-text-tertiary); + opacity: 1; + } + + &:focus { + background: var(--color-bg-brand-softer); + } + } + } + + &__familiar-followers { + display: flex; + align-items: center; + gap: 10px; + margin-block: 16px; + color: var(--color-text-secondary); + + a:any-link { + font-weight: 500; + text-decoration: none; + color: var(--color-text-primary); + } + } +} + +.account__contents { + overflow: hidden; +} + +.account__details { + display: flex; + flex-wrap: wrap; + column-gap: 1em; +} + +.verified-badge { + display: inline-flex; + align-items: center; + color: var(--color-text-success); + gap: 4px; + overflow: hidden; + white-space: nowrap; + + > span { + overflow: hidden; + text-overflow: ellipsis; + } + + a { + color: inherit; + font-weight: 500; + text-decoration: none; + } + + .icon { + width: 16px; + height: 16px; + } +} + +.trends { + &__item { + display: flex; + align-items: center; + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + gap: 8px; + + &__name { + flex: 1 1 auto; + color: var(--color-text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + strong { + font-weight: 500; + } + + a { + color: var(--color-text-primary); + text-decoration: none; + font-size: 14px; + font-weight: 500; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &:hover, + &:focus, + &:active { + span { + text-decoration: underline; + } + } + } + } + + &__current { + flex: 0 0 auto; + font-size: 24px; + font-weight: 500; + text-align: end; + color: var(--color-text-primary); + text-decoration: none; + } + + &__sparkline { + flex: 0 0 auto; + width: 50px; + + path:first-child { + fill: var(--color-graph-primary-fill) !important; + fill-opacity: 1 !important; + } + + path:last-child { + stroke: var(--color-graph-primary-stroke) !important; + fill: none !important; + } + } + + &--requires-review { + .trends__item__name { + color: var(--color-text-warning); + + a { + color: var(--color-text-warning); + } + } + + .trends__item__current { + color: var(--color-text-warning); + } + + .trends__item__sparkline { + path:first-child { + fill: var(--color-graph-warning-fill) !important; + } + + path:last-child { + stroke: var(--color-graph-warning-stroke) !important; + } + } + } + + &--disabled { + .trends__item__name { + color: var(--color-text-disabled); + + a { + color: var(--color-text-disabled); + } + } + + .trends__item__current { + color: var(--color-text-disabled); + } + + .trends__item__sparkline { + path:first-child { + fill: var(--color-graph-disabled-fill) !important; + } + + path:last-child { + stroke: var(--color-graph-disabled-stroke) !important; + } + } + } + } + + &--compact &__item { + padding: 12px; + } +} + +.conversation { + display: flex; + border-bottom: 1px solid var(--color-border-primary); + padding: 5px; + padding-bottom: 0; + + &:focus { + background: var(--color-bg-secondary); + outline: 0; + } + + &__avatar { + flex: 0 0 auto; + padding: 10px; + padding-top: 12px; + position: relative; + cursor: pointer; + } + + &__unread { + display: inline-block; + background: var(--color-text-brand); + border-radius: 50%; + width: 0.625rem; + height: 0.625rem; + margin: -0.1ex 0.15em 0.1ex; + } + + &__content { + flex: 1 1 auto; + padding: 10px 5px; + padding-inline-end: 15px; + overflow: hidden; + + &__info { + overflow: hidden; + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + } + + &__relative-time { + font-size: 15px; + color: var(--color-text-secondary); + padding-inline-start: 15px; + } + + &__names { + color: var(--color-text-secondary); + font-size: 15px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 4px; + flex-basis: 90px; + flex-grow: 1; + + a { + color: var(--color-text-primary); + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + + a { + overflow-wrap: anywhere; + } + } +} + +.announcements { + width: calc(100% - 124px); + flex: 0 0 auto; + position: relative; + overflow: hidden; + + .layout-multiple-columns & { + width: 100%; + } + + @media screen and (max-width: (124px + 300px)) { + width: 100%; + } + + &__root { + background: var(--color-bg-brand-softer); + font-size: 13px; + display: flex; + align-items: flex-end; + } + + &__mastodon { + width: 124px; + flex: 0 0 auto; + + @media screen and (max-width: (124px + 300px)) { + display: none; + } + } + + &__slides { + display: flex; + flex-wrap: nowrap; + align-items: start; + } + + &__slide { + box-sizing: border-box; + width: 100%; + flex: 0 0 100%; + padding: 15px; + position: relative; + font-size: 15px; + line-height: 20px; + overflow-wrap: break-word; + font-weight: 400; + max-height: 50vh; + overflow: hidden; + flex-direction: column; + } + + &__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: var(--color-text-brand); + border-radius: 50%; + width: 0.625rem; + height: 0.625rem; + } + + &__pagination { + padding: 15px; + color: var(--color-text-secondary); + position: absolute; + bottom: 3px; + inset-inline-end: 0; + display: flex; + align-items: center; + z-index: 1; + } +} + +.layout-multiple-columns .announcements__mastodon { + display: none; +} + +.layout-multiple-columns .announcements__container { + width: 100%; +} + +.reactions-bar { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-top: 15px; + margin-inline-start: -2px; + width: calc(100% - (90px - 33px)); + + &__item { + flex-shrink: 0; + background: var(--color-bg-brand-softer); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + border: 1px solid var(--color-border-on-bg-brand-softer); + border-radius: 3px; + box-sizing: border-box; + margin: 2px; + cursor: pointer; + user-select: none; + padding: 0 6px; + display: flex; + align-items: center; + transition: all 100ms ease-in; + transition-property: background-color, color; + + &__emoji { + display: block; + margin: 3px 0; + width: 16px; + height: 16px; + + img { + display: block; + margin: 0; + width: 100%; + height: 100%; + min-width: auto; + min-height: auto; + object-fit: contain; + } + } + + &__count { + display: block; + min-width: 9px; + font-size: 13px; + font-weight: 500; + text-align: center; + margin-inline-start: 6px; + } + + &:hover, + &:focus, + &:active { + color: var(--color-text-primary); + background: var(--color-bg-brand-soft); + transition: all 200ms ease-out; + transition-property: background-color, color; + } + + &.active { + color: var(--color-text-brand); + background-color: var(--color-bg-brand-softer); + border-color: var(--color-text-brand); + transition: all 100ms ease-in; + transition-property: background-color, color; + } + } + + .emoji-picker-dropdown { + display: flex; + margin: 2px; + } + + &:hover .emoji-button { + opacity: 0.85; + } + + .emoji-button { + color: var(--color-text-secondary); + margin: 0; + font-size: 16px; + width: auto; + flex-shrink: 0; + padding: 0 6px; + height: 22px; + display: flex; + align-items: center; + opacity: 0.5; + transition: all 100ms ease-in; + transition-property: background-color, color; + + &:hover, + &:active, + &:focus { + opacity: 1; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + transition: all 200ms ease-out; + transition-property: background-color, color; + } + } + + &--empty { + .emoji-button { + padding: 0; + } + } +} + +.notification, +.status__wrapper, +.conversation { + position: relative; + + // When scrolling these elements into view, take into account + // the column header height + scroll-margin-top: var(--column-header-height, 0); + + &.unread { + &::before { + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + border-inline-start: 4px solid var(--color-text-brand); + pointer-events: none; + } + } +} + +.picture-in-picture { + position: fixed; + bottom: 20px; + inset-inline-end: 20px; + width: 300px; + box-shadow: var(--dropdown-shadow); + + &__footer { + border-radius: 0 0 4px 4px; + background: var(--color-bg-secondary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + border-top: 0; + padding: 12px; + display: flex; + justify-content: space-between; + } + + &__header { + border-radius: 4px 4px 0 0; + background: var(--color-bg-secondary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + border-bottom: 0; + padding: 12px; + display: flex; + justify-content: space-between; + + .icon-button { + padding: 6px; + } + + &__account { + display: flex; + text-decoration: none; + overflow: hidden; + } + + .account__avatar { + margin-inline-end: 8px; + } + + .display-name { + color: var(--color-text-primary); + text-decoration: none; + + strong, + span { + display: block; + text-overflow: ellipsis; + overflow: hidden; + } + + span { + color: var(--color-text-secondary); + } + } + } + + .video-player, + .audio-player { + border-radius: 0; + } +} + +.picture-in-picture-placeholder { + border-radius: 8px; + box-sizing: border-box; + border: 1px dashed var(--color-border-primary); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: 16px; + font-size: 15px; + line-height: 21px; + font-weight: 500; + cursor: pointer; + color: var(--color-text-tertiary); + aspect-ratio: 16 / 9; + + .icon { + width: 48px; + height: 48px; + margin-bottom: 8px; + } + + &:hover, + &:active, + &:focus { + color: var(--color-text-secondary); + } + + &:focus-visible { + outline: var(--outline-focus-default); + border-color: transparent; + } +} + +.notifications-permission-banner { + padding: 30px; + border-bottom: 1px solid var(--color-border-primary); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + + &__close { + position: absolute; + top: 10px; + inset-inline-end: 10px; + } + + h2 { + font-size: 16px; + font-weight: 500; + margin-bottom: 15px; + text-align: center; + } + + p { + color: var(--color-text-secondary); + margin-bottom: 15px; + text-align: center; + + .icon { + width: 20px; + height: 20px; + vertical-align: middle; + } + } +} + +.explore__search-header { + justify-content: center; + align-items: center; + border: 1px solid var(--color-border-primary); + border-top: 0; + border-bottom: 0; + padding: 16px; + padding-bottom: 8px; + + .search { + width: 100%; + margin-bottom: 0; + } + + .search__input { + padding-block: 12px; + padding-inline-end: 30px; + } + + .search__popout { + border: 1px solid var(--color-border-primary); + } + + .search__icon { + top: 12px; + inset-inline-end: 12px; + color: var(--color-text-tertiary); + } +} + +.layout-single-column .explore__search-header { + display: none; + + @media screen and (max-width: ($no-gap-breakpoint - 1px)) { + display: flex; + } +} + +.explore__search-results { + flex: 1 1 auto; + display: flex; + flex-direction: column; + + @media screen and (min-width: $no-gap-breakpoint) { + border: 1px solid var(--color-border-primary); + border-top: 0; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + } +} + +.story { + display: flex; + align-items: center; + color: var(--color-text-primary); + padding: 16px; + border-bottom: 1px solid var(--color-border-primary); + gap: 16px; + + &:last-child { + border-bottom: 0; + } + + &__details { + flex: 1 1 auto; + + &__publisher { + color: var(--color-text-secondary); + margin-bottom: 8px; + font-size: 14px; + line-height: 20px; + } + + &__title { + display: block; + font-size: 19px; + line-height: 24px; + font-weight: 500; + margin-bottom: 8px; + text-decoration: none; + color: var(--color-text-primary); + + &:hover, + &:active, + &:focus { + color: var(--color-text-brand); + } + } + + &__shared { + display: flex; + align-items: baseline; + color: var(--color-text-secondary); + gap: 8px; + justify-content: space-between; + font-size: 14px; + line-height: 20px; + + &__pill { + background: var(--color-bg-tertiary); + border-radius: 4px; + color: inherit; + text-decoration: none; + padding: 4px 12px; + font-size: 12px; + font-weight: 500; + line-height: 16px; + flex-shrink: 0; + } + + &__author-link { + display: inline-flex; + align-items: center; + gap: 4px; + color: var(--color-text-primary); + font-weight: 500; + text-decoration: none; + + &:hover, + &:active, + &:focus { + color: var(--color-text-brand); + } + } + } + + strong { + font-weight: 500; + } + } + + &__thumbnail { + flex: 0 0 auto; + position: relative; + width: 120px; + aspect-ratio: 1; + + .skeleton { + width: 100%; + height: 100%; + } + + img { + border-radius: 8px; + display: block; + margin: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + + &__preview { + border-radius: 8px; + display: block; + margin: 0; + width: 100%; + height: 100%; + object-fit: fill; + position: absolute; + top: 0; + inset-inline-start: 0; + z-index: 0; + + &--hidden { + display: none; + } + } + } + + &.expanded { + flex-direction: column; + + .story__thumbnail { + order: 1; + width: 100%; + height: auto; + aspect-ratio: 1.91 / 1; + } + + .story__details { + order: 2; + width: 100%; + flex: 0 0 auto; + } + } +} + +.server-banner { + &__introduction { + font-size: 15px; + line-height: 22px; + color: var(--color-text-secondary); + margin-bottom: 20px; + + strong { + font-weight: 700; + } + + a { + color: inherit; + text-decoration: underline; + + &:hover, + &:active, + &:focus { + text-decoration: none; + } + } + } + + &__hero { + display: block; + border-radius: 4px; + width: 100%; + height: auto; + margin-bottom: 20px; + aspect-ratio: 1.9; + border: 0; + background: var(--color-bg-tertiary); + object-fit: cover; + } + + &__description { + font-size: 15px; + line-height: 22px; + color: var(--color-text-secondary); + margin-bottom: 20px; + } + + &__meta { + display: flex; + gap: 10px; + max-width: 100%; + + &__column { + flex: 0 0 auto; + width: calc(50% - 5px); + overflow: hidden; + } + } + + &__number { + font-weight: 600; + color: var(--color-text-primary); + font-size: 14px; + } + + &__number-label { + color: var(--color-text-secondary); + font-weight: 500; + font-size: 14px; + } + + h4 { + text-transform: uppercase; + color: var(--color-text-secondary); + margin-bottom: 10px; + font-weight: 600; + } + + .account { + padding: 0; + border: 0; + } + + .account__avatar-wrapper { + margin-inline-start: 0; + } + + .spacer { + margin: 10px 0; + } +} + +.safety-action-modal, +.interaction-modal { + max-width: 100vw; + width: 600px; + overflow-y: auto; +} + +.interaction-modal { + overflow: visible; + position: relative; + display: block; + border-radius: 16px; + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + padding: 24px; + box-sizing: border-box; + + @media screen and (max-width: $mobile-breakpoint) { + border-radius: 16px 16px 0 0; + border-bottom: 0; + padding-bottom: 32px; + } + + h3 { + font-size: 22px; + line-height: 33px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + } + + p { + text-align: center; + font-size: 17px; + line-height: 22px; + color: var(--color-text-secondary); + + strong { + color: var(--color-text-primary); + font-weight: 700; + } + } + + p.hint { + margin-bottom: 14px; + font-size: 14px; + } + + &__lead { + margin-bottom: 20px; + + h3 { + margin-bottom: 15px; + } + } + + &__login { + position: relative; + margin-bottom: 20px; + + &__input { + @include search-input; + + border: 1px solid var(--color-border-primary); + padding: 4px 6px; + color: var(--color-text-primary); + font-size: 16px; + line-height: 18px; + display: flex; + align-items: center; + + input { + background: transparent; + color: inherit; + font: inherit; + border: 0; + padding: 15px - 4px 15px - 6px; + flex: 1 1 auto; + min-width: 0; + + &::placeholder { + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + } + + &:focus { + outline: 0; + } + } + + .button { + flex: 0 0 auto; + } + } + + .search__popout { + margin-top: -1px; + padding-top: 5px; + padding-bottom: 5px; + border: 1px solid var(--color-border-primary); + } + + &.invalid &__input { + border-color: var(--color-text-error); + } + + &.expanded .search__popout { + display: block; + } + + &.expanded &__input { + border-radius: 4px 4px 0 0; + } + } + + &__choices { + display: flex; + gap: 40px; + + &__choice { + flex: 1; + box-sizing: border-box; + + h3 { + margin-bottom: 20px; + } + + p { + color: var(--color-text-secondary); + margin-bottom: 20px; + font-size: 15px; + } + + .button { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + } + } + + @media screen and (max-width: ($no-gap-breakpoint - 1px)) { + &__choices { + flex-direction: column; + + &__choice { + margin-top: 40px; + } + } + } + + .link-button { + font-size: inherit; + display: inline; + } +} + +.privacy-policy { + padding: 20px; + + @media screen and (min-width: $no-gap-breakpoint) { + border-radius: 4px; + } + + &__body { + margin-top: 20px; + } +} + +.prose { + color: var(--color-text-primary); + font-size: 15px; + line-height: 22px; + + p, + ul, + ol { + margin-top: 1.25em; + margin-bottom: 1.25em; + } + + img { + margin-top: 2em; + margin-bottom: 2em; + max-width: 100%; + } + + video { + margin-top: 2em; + margin-bottom: 2em; + max-width: 100%; + } + + figure { + margin-top: 2em; + margin-bottom: 2em; + + figcaption { + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; + } + } + + figure > * { + margin-top: 0; + margin-bottom: 0; + } + + h1 { + font-size: 1.5em; + margin-top: 0; + margin-bottom: 1em; + line-height: 1.33; + } + + h2 { + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; + } + + h3, + h4, + h5, + h6 { + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; + } + + ol { + counter-reset: list-counter; + } + + li { + margin-top: 0.5em; + margin-bottom: 0.5em; + } + + ol > li { + counter-increment: list-counter; + + &::before { + content: counter(list-counter) '.'; + position: absolute; + inset-inline-start: 0; + } + } + + ul > li::before { + content: ''; + position: absolute; + background-color: var(--color-text-secondary); + border-radius: 50%; + width: 0.375em; + height: 0.375em; + top: 0.5em; + inset-inline-start: 0.25em; + } + + ul > li, + ol > li { + position: relative; + padding-inline-start: 1.75em; + } + + & > ul > li p { + margin-top: 0.75em; + margin-bottom: 0.75em; + } + + & > ul > li > *:first-child { + margin-top: 1.25em; + } + + & > ul > li > *:last-child { + margin-bottom: 1.25em; + } + + & > ol > li > *:first-child { + margin-top: 1.25em; + } + + & > ol > li > *:last-child { + margin-bottom: 1.25em; + } + + ul ul, + ul ol, + ol ul, + ol ol { + margin-top: 0.75em; + margin-bottom: 0.75em; + } + + h1, + h2, + h3, + h4, + h5, + h6, + strong, + b { + color: var(--color-text-primary); + font-weight: 700; + } + + em, + i { + font-style: italic; + } + + a { + color: var(--color-text-brand); + text-decoration: underline; + + &:focus, + &:hover, + &:active { + text-decoration: none; + } + } + + code { + font-size: 0.875em; + background: var(--color-bg-secondary); + border-radius: 4px; + padding: 0.2em 0.3em; + } + + hr { + border: 0; + border-top: 1px solid var(--color-border-primary); + margin-top: 3em; + margin-bottom: 3em; + } + + hr + * { + margin-top: 0; + } + + h2 + * { + margin-top: 0; + } + + h3 + * { + margin-top: 0; + } + + h4 + *, + h5 + *, + h6 + * { + margin-top: 0; + } + + & > :first-child { + margin-top: 0; + } + + & > :last-child { + margin-bottom: 0; + } +} + +.dismissable-banner, +.warning-banner { + position: relative; + margin: 10px; + margin-bottom: 5px; + border-radius: 8px; + border: 1px solid var(--color-border-on-bg-brand-softer); + background: var(--color-bg-brand-softer); + overflow: hidden; + flex-shrink: 0; + + &__background-image { + width: 125%; + position: absolute; + bottom: -25%; + inset-inline-end: -25%; + z-index: -1; + opacity: 0.15; + mix-blend-mode: luminosity; + } + + &__message { + flex: 1 1 auto; + padding: 15px; + font-size: 15px; + line-height: 22px; + font-weight: 500; + color: var(--color-text-primary); + + p { + margin-bottom: 15px; + + &:last-child { + margin-bottom: 0; + } + } + + h1 { + color: var(--color-text-brand); + font-size: 22px; + line-height: 33px; + font-weight: 700; + margin-bottom: 15px; + } + + &__actions { + display: flex; + flex-wrap: wrap; + gap: 4px; + + &__wrapper { + display: flex; + margin-top: 30px; + } + + .button { + display: block; + flex-grow: 1; + } + } + } + + &__action { + float: right; + padding: 15px 10px; + + .icon-button { + color: var(--color-text-brand); + } + } +} + +.warning-banner { + border: 1px solid var(--color-border-on-bg-error-softer); + background: var(--color-bg-error-softer); + + &__message { + h1 { + color: var(--color-text-error); + } + + a { + color: var(--color-text-primary); + } + } +} + +.image { + position: relative; + overflow: hidden; + + &__preview { + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + + &.loaded &__preview { + display: none; + } + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border: 0; + background: transparent; + opacity: 0; + } + + &.loaded img { + opacity: 1; + } +} + +.link-footer { + flex: 0 0 auto; + padding-top: 20px; + z-index: 1; + font-size: 13px; + + .column & { + padding: 15px; + } + + p { + color: var(--color-text-secondary); + margin-bottom: 20px; + + .version { + white-space: nowrap; + } + + strong { + font-weight: 500; + } + + a { + color: var(--color-text-secondary); + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + } +} + +.about { + padding: 20px; + border-top: 1px solid var(--color-border-primary); + + @media screen and (min-width: $no-gap-breakpoint) { + border-radius: 4px; + } + + &__footer { + color: var(--color-text-tertiary); + text-align: center; + font-size: 15px; + line-height: 22px; + margin-top: 20px; + } + + &__header { + margin-bottom: 30px; + + &__hero { + width: 100%; + height: auto; + aspect-ratio: 1.9; + background: var(--color-bg-tertiary); + border-radius: 8px; + margin-bottom: 30px; + } + + h1, + p { + text-align: center; + } + + h1 { + font-size: 24px; + line-height: 1.5; + font-weight: 700; + margin-bottom: 10px; + } + + p { + font-size: 16px; + line-height: 24px; + font-weight: 400; + color: var(--color-text-secondary); + } + } + + &__meta { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + display: flex; + margin-bottom: 30px; + font-size: 15px; + + &__column { + box-sizing: border-box; + width: 50%; + padding: 20px; + } + + &__divider { + width: 0; + border: 0; + border-style: solid; + border-color: var(--color-border-primary); + border-left-width: 1px; + min-height: calc(100% - 60px); + flex: 0 0 auto; + } + + h4 { + font-size: 15px; + text-transform: uppercase; + color: var(--color-text-secondary); + font-weight: 500; + margin-bottom: 20px; + } + + @media screen and (width <= 600px) { + display: block; + + h4 { + text-align: center; + } + + &__column { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + &__divider { + min-height: 0; + width: 100%; + border-left-width: 0; + border-top-width: 1px; + } + } + + .layout-multiple-columns & { + display: block; + + h4 { + text-align: center; + } + + &__column { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + &__divider { + min-height: 0; + width: 100%; + border-left-width: 0; + border-top-width: 1px; + } + } + } + + &__mail { + color: var(--color-text-primary); + text-decoration: none; + font-weight: 500; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + + .link-footer { + padding: 0; + margin-top: 60px; + text-align: center; + font-size: 15px; + line-height: 22px; + + @media screen and (min-width: $no-gap-breakpoint) { + display: none; + } + } + + .account { + padding: 0; + border: 0; + } + + .account__avatar-wrapper { + margin-inline-start: 0; + } + + .account__relationship { + display: none; + } + + &__section { + margin-bottom: 10px; + + &__title { + display: flex; + align-items: center; + gap: 6px; + font-size: 17px; + font-weight: 600; + line-height: 22px; + padding: 20px; + border-radius: 4px; + border: 1px solid var(--color-border-primary); + color: var(--color-text-brand); + cursor: pointer; + width: 100%; + background: none; + } + + &.active &__title { + border-radius: 4px 4px 0 0; + } + + &__body { + border: 1px solid var(--color-border-primary); + border-top: 0; + padding: 20px; + font-size: 15px; + line-height: 22px; + } + } + + &__domain-blocks { + margin-top: 30px; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + + &__domain { + border-bottom: 1px solid var(--color-border-primary); + padding: 10px; + font-size: 15px; + color: var(--color-text-secondary); + + &:nth-child(2n) { + background: var(--color-bg-secondary); + } + + &:last-child { + border-bottom: 0; + } + + &__header { + display: flex; + gap: 10px; + justify-content: space-between; + font-weight: 500; + margin-bottom: 4px; + } + + h6 { + color: var(--color-text-primary); + font-size: inherit; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + p { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + +.notification-list { + position: fixed; + bottom: 2rem; + inset-inline-start: 1rem; + z-index: 9999; + display: flex; + flex-direction: column; + gap: 4px; +} + +.notification-bar { + --alert-edge-spacing: 1rem; + + display: flex; + gap: 10px; + flex: 0 0 auto; + width: auto; + padding: 15px; + margin: 0; + color: var(--color-text-on-inverted); + background: var(--color-bg-inverted); + backdrop-filter: blur(8px); + border: 1px solid var(--color-border-on-bg-inverted); + border-radius: 8px; + box-shadow: + 0 10px 15px -3px var(--color-shadow-primary), + 0 4px 6px -4px var(--color-shadow-primary); + cursor: default; + font-size: 15px; + line-height: 21px; + + &.from-side { + translate: calc( + -1 * (100% + var(--alert-edge-spacing)) * var(--text-x-direction) + ); + } + + &.from-below { + translate: 0 calc(100% + var(--alert-edge-spacing)); + } + + &.notification-bar--active { + translate: none; + } + + .no-reduce-motion & { + transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1); + will-change: translate; + } +} + +.notification-bar__content { + margin-inline-end: auto; +} + +.notification-bar__title { + margin-inline-end: 5px; + font-weight: 700; +} + +.notification-bar__action { + display: inline-block; + border: 0; + background: transparent; + text-transform: uppercase; + cursor: pointer; + color: var(--color-text-brand); + font-weight: 700; + border-radius: 4px; + padding: 0 4px; + + &:hover, + &:focus, + &:active { + background: var(--color-bg-brand-softer); + } +} + +.notification-bar__dismiss-button { + margin-top: -2px; + color: rgb(from currentColor r g b / 85%); + + &:hover, + &:focus, + &:active { + color: currentColor; + } +} + +.notification-bar__loading-indicator { + --spinner-size: 22px; + + position: relative; + height: var(--spinner-size); + width: var(--spinner-size); + margin-inline-start: 2px; + + svg { + color: var(--color-text-on-media); + height: var(--spinner-size); + width: var(--spinner-size); + } +} + +.hashtag-header { + border-bottom: 1px solid var(--color-border-primary); + padding: 15px; + font-size: 17px; + line-height: 22px; + color: var(--color-text-secondary); + + strong { + font-weight: 700; + } + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + gap: 15px; + + h1 { + color: var(--color-text-primary); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + font-size: 22px; + line-height: 33px; + font-weight: 700; + } + + &__buttons { + display: flex; + align-items: center; + gap: 8px; + + .button { + flex-shrink: 1; + white-space: nowrap; + min-width: 80px; + } + + .icon-button { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + box-sizing: content-box; + padding: 5px; + + .icon { + width: 24px; + height: 24px; + } + } + } + } +} + +.hashtag-bar { + margin-top: 16px; + display: flex; + flex-wrap: wrap; + font-size: 12px; + line-height: 16px; + gap: 6px; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + + a { + display: inline-flex; + color: inherit; + text-decoration: none; + padding: 4px 12px; + background: var(--color-bg-brand-softer); + border-radius: 4px; + font-weight: 500; + + &:hover, + &:focus, + &:active { + background: var(--color-bg-brand-soft); + } + } + + .link-button { + color: inherit; + font-size: inherit; + line-height: inherit; + padding: 0; + } +} + +.inline-follow-suggestions { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 0; + padding-bottom: 0; + border-bottom: 1px solid var(--color-border-primary); + background: var(--color-bg-brand-softer); + + &.focusable:focus-visible { + background: var(--color-bg-brand-softer); + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 16px; + + h3 { + font-size: 15px; + line-height: 22px; + font-weight: 500; + } + + &__actions { + display: flex; + align-items: center; + gap: 24px; + } + + .link-button { + font-size: 13px; + font-weight: 500; + } + } + + &__body { + position: relative; + + &__scroll-button { + position: absolute; + height: 100%; + background: transparent; + border: none; + cursor: pointer; + top: 0; + color: var(--color-text-primary); + opacity: 0.5; + + &.left { + left: 0; + } + + &.right { + right: 0; + } + + &__icon { + border-radius: 50%; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + display: flex; + align-items: center; + justify-content: center; + aspect-ratio: 1; + padding: 8px; + + .icon { + width: 24px; + height: 24px; + } + } + + &:hover, + &:focus, + &:active { + opacity: 1; + } + } + + &__scrollable { + box-sizing: border-box; + display: flex; + flex-wrap: nowrap; + min-height: 228px; + gap: 16px; + padding: 16px; + scroll-snap-type: x mandatory; + scroll-padding: 16px; + scroll-behavior: smooth; + overflow-x: scroll; + scrollbar-width: none; + + &__card { + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + display: flex; + flex-direction: column; + gap: 12px; + align-items: center; + padding: 12px; + scroll-snap-align: start; + flex: 0 0 auto; + width: 200px; + box-sizing: border-box; + position: relative; + + a { + text-decoration: none; + } + + & > .icon-button { + position: absolute; + inset-inline-end: 8px; + top: 8px; + opacity: 0.75; + } + + &__avatar { + height: 48px; + display: flex; + + a { + display: flex; + text-decoration: none; + } + } + + .account__avatar { + flex-shrink: 0; + align-self: flex-end; + border: 1px solid var(--color-border-primary); + background-color: var(--color-bg-tertiary); + } + + &__text-stack { + display: flex; + flex-direction: column; + gap: 4px; + align-items: center; + max-width: 100%; + + a { + max-width: 100%; + } + + &__source { + display: inline-flex; + align-items: center; + color: var(--color-text-tertiary); + gap: 4px; + overflow: hidden; + white-space: nowrap; + cursor: help; + + > span { + overflow: hidden; + text-overflow: ellipsis; + } + + .icon { + width: 16px; + height: 16px; + } + } + } + + .display-name { + display: flex; + flex-direction: column; + gap: 4px; + align-items: center; + + & > * { + max-width: 100%; + } + + &__html { + font-size: 15px; + font-weight: 500; + color: var(--color-text-primary); + } + + &__account { + font-size: 14px; + color: var(--color-text-secondary); + } + } + + .verified-badge { + font-size: 14px; + max-width: 100%; + } + + .button { + display: block; + width: 100%; + } + } + } + } +} + +.filtered-notifications-banner { + display: flex; + align-items: center; + border-bottom: 1px solid var(--color-border-primary); + padding: 16px 24px; + gap: 8px; + color: var(--color-text-secondary); + text-decoration: none; + + &:hover, + &:active, + &:focus { + color: var(--color-text-primary); + } + + .notification-group__icon { + color: inherit; + } + + &__text { + flex: 1 1 auto; + font-size: 14px; + line-height: 20px; + + strong { + font-size: 16px; + line-height: 24px; + display: block; + } + } + + &__badge { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border-radius: 100px; + padding: 2px 8px; + } +} + +.notification-request { + $padding: 15px; + + display: flex; + padding: $padding; + gap: 8px; + position: relative; + border-bottom: 1px solid var(--color-border-primary); + + &__checkbox { + position: absolute; + inset-inline-start: $padding; + top: 50%; + transform: translateY(-50%); + width: 0; + overflow: hidden; + opacity: 0; + + .check-box { + display: flex; + } + } + + &__link { + display: flex; + align-items: center; + gap: 12px; + flex: 1 1 auto; + text-decoration: none; + color: inherit; + overflow: hidden; + + .account__avatar { + flex-shrink: 0; + } + } + + &__name { + flex: 1 1 auto; + color: var(--color-text-secondary); + font-size: 14px; + line-height: 20px; + overflow: hidden; + text-overflow: ellipsis; + + &__display-name { + display: flex; + align-items: center; + gap: 6px; + font-size: 16px; + letter-spacing: 0.5px; + line-height: 24px; + color: var(--color-text-primary); + + bdi { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + .filtered-notifications-banner__badge { + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border-radius: 4px; + padding: 1px 6px; + } + } + + &__actions { + display: flex; + align-items: center; + gap: 8px; + + .icon-button { + border-radius: 4px; + border: 1px solid var(--color-border-primary); + padding: 5px; + } + } + + .notification-request__link { + transition: padding-inline-start 0.1s ease-in-out; + } + + &--forced-checkbox { + cursor: pointer; + + &:hover { + background: var(--color-bg-secondary); + } + + .notification-request__checkbox { + opacity: 1; + width: 30px; + } + + .notification-request__link { + padding-inline-start: 30px; + } + + .notification-request__actions { + display: none; + } + } +} + +.more-from-author { + box-sizing: border-box; + font-size: 14px; + color: var(--color-text-secondary); + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border-primary); + border-top: 0; + border-radius: 0 0 8px 8px; + padding: 15px; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 4px 8px; + + .logo { + width: 16px; + height: 16px; + color: var(--color-text-secondary); + } + + & > span { + display: flex; + align-items: center; + gap: 8px; + } + + a { + display: inline-flex; + align-items: center; + gap: 4px; + font-weight: 500; + color: var(--color-text-primary); + text-decoration: none; + min-width: 0; + + &:hover, + &:focus, + &:active { + color: var(--color-text-brand); + } + } +} + +.notification-group { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 16px 24px; + border-bottom: 1px solid var(--color-border-primary); + + &__icon { + width: 40px; + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + color: var(--color-text-tertiary); + + .icon { + width: 28px; + height: 28px; + } + } + + &--follow &__icon, + &--follow-request &__icon { + color: var(--color-text-brand); + } + + &--favourite &__icon { + color: var(--color-text-favourite-highlight); + } + + &--reblog &__icon { + color: var(--color-text-success); + } + + &--relationships-severance-event &__icon, + &--admin-report &__icon, + &--admin-sign-up &__icon { + color: var(--color-text-tertiary); + } + + &--moderation-warning &__icon { + color: var(--color-text-bookmark-highlight); + } + + &--follow-request &__actions { + align-items: center; + display: flex; + gap: 8px; + + .icon-button { + border: 1px solid var(--color-border-primary); + border-radius: 50%; + padding: 1px; + } + } + + &__main { + display: flex; + flex-direction: column; + gap: 8px; + flex: 1 1 auto; + overflow: hidden; + container-type: inline-size; + + &__header { + display: flex; + flex-direction: column; + gap: 8px; + + &__wrapper { + display: flex; + justify-content: space-between; + } + + &__label { + display: flex; + flex-wrap: wrap; + gap: 2px 8px; + font-size: 15px; + line-height: 22px; + color: var(--color-text-secondary); + + a { + color: inherit; + text-decoration: none; + } + + bdi { + font-weight: 700; + color: var(--color-text-primary); + } + + time { + color: var(--color-text-tertiary); + } + + @container (width < 350px) { + time, + &-separator { + display: none; + } + } + } + } + + &__status { + border: 1px solid var(--color-border-primary); + border-radius: 8px; + padding: 8px; + } + + &__additional-content { + color: var(--color-text-tertiary); + margin-top: -8px; // to offset the parent's `gap` property + font-size: 15px; + line-height: 22px; + } + } + + .status { + padding: 0; + border: 0; + } + + &__embedded-status { + display: flex; + flex-direction: column; + gap: 8px; + cursor: pointer; + + &__account { + display: flex; + align-items: center; + gap: 4px; + color: var(--color-text-tertiary); + font-size: 15px; + line-height: 22px; + + bdi { + color: var(--color-text-secondary); + } + + .account__avatar { + flex: 0 0 auto; + } + } + + &__content { + display: -webkit-box; + font-size: 15px; + line-height: 22px; + color: var(--color-text-secondary); + -webkit-line-clamp: 4; + line-clamp: 4; + -webkit-box-orient: vertical; + max-height: none; + overflow: hidden; + + p, + a { + color: inherit; + } + + p { + margin-bottom: 8px; + } + } + + .reply-indicator__attachments { + margin-top: 0; + font-size: 15px; + line-height: 22px; + color: var(--color-text-tertiary); + } + } +} + +.notification-group__actions, +.compose-form__actions { + .button { + display: block; // Otherwise text-ellipsis doesn't work + flex: 1 1 auto; + } +} + +.notification-ungrouped { + padding: 16px 24px; + border-bottom: 1px solid var(--color-border-primary); + + &__header { + display: flex; + align-items: center; + gap: 8px; + color: var(--color-text-tertiary); + font-size: 15px; + line-height: 22px; + font-weight: 500; + padding-inline-start: 24px; + margin-bottom: 16px; + + &__icon { + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + + .icon { + width: 16px; + height: 16px; + } + } + + a { + color: inherit; + text-decoration: none; + } + } + + .status:not(.status--is-quote) { + border: 0; + padding: 0; + } + + .status__wrapper-direct { + background: transparent; + } + + .status { + // 40px avatar + 8px gap + --status-gutter-width: 48px; + } + + .status--is-quote { + --status-gutter-width: 0; + } + + .status__content, + .status__action-bar, + .media-gallery, + .video-player, + .audio-player, + .attachment-list, + .picture-in-picture-placeholder, + .more-from-author, + .status-card, + .hashtag-bar, + .content-warning, + .filter-warning { + margin-inline-start: var(--status-gutter-width); + width: calc(100% - var(--status-gutter-width)); + } + + .more-from-author { + width: calc(100% - var(--status-gutter-width) + 2px); + } + + .status__content__read-more-button { + margin-inline-start: var(--status-gutter-width); + } + + .notification__report { + border: 0; + padding: 0; + } +} + +.notification-group--unread, +.notification-ungrouped--unread { + position: relative; + + &::before { + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + border-inline-start: 4px solid var(--color-text-brand); + pointer-events: none; + } +} + +.hover-card-controller[data-popper-reference-hidden='true'] { + opacity: 0; + pointer-events: none; +} + +.hover-card { + box-shadow: var(--dropdown-shadow); + background: var(--color-bg-primary); + backdrop-filter: $backdrop-blur-filter; + border: 1px solid var(--color-border-primary); + border-radius: 8px; + padding: 16px; + width: 270px; + display: flex; + flex-direction: column; + gap: 12px; + + &--loading { + position: relative; + min-height: 100px; + } + + &__name { + display: flex; + gap: 12px; + text-decoration: none; + color: inherit; + } + + &__numbers, + &__familiar-followers { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 2px 10px; + } + + &__numbers { + font-size: 15px; + line-height: 22px; + color: var(--color-text-primary); + + strong { + font-weight: 700; + } + } + + &__text-row { + display: flex; + flex-direction: column; + gap: 8px; + } + + &__bio { + color: var(--color-text-primary); + font-size: 14px; + line-height: 20px; + display: -webkit-box; + line-clamp: 2; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + max-height: 2 * 20px; + overflow: hidden; + + p { + margin-bottom: 0; + } + + a { + color: inherit; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + } + + &__note { + &-label { + color: var(--color-text-tertiary); + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + } + + dd { + white-space: pre-line; + color: var(--color-text-primary); + overflow: hidden; + line-clamp: 3; // Not yet supported in browers + display: -webkit-box; // The next 3 properties are needed + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + } + } + + &__limited-account-note { + text-align: center; + font-weight: 500; + } + + .display-name { + font-size: 15px; + line-height: 22px; + + bdi { + font-weight: 500; + color: var(--color-text-primary); + } + + &__account { + display: block; + color: var(--color-text-tertiary); + } + } + + .account-fields { + color: var(--color-text-primary); + font-size: 14px; + line-height: 20px; + + a { + color: inherit; + text-decoration: none; + + &:focus, + &:hover, + &:active { + text-decoration: underline; + } + } + + dl { + display: flex; + align-items: center; + gap: 4px; + + dt { + flex: 0 1 auto; + color: var(--color-text-tertiary); + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + dd { + flex: 1 1 auto; + font-weight: 500; + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + text-align: end; + } + + &.verified { + dd { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 4px; + overflow: hidden; + white-space: nowrap; + color: var(--color-text-success); + + & > span { + overflow: hidden; + text-overflow: ellipsis; + } + + a { + font-weight: 500; + } + + .icon { + width: 16px; + height: 16px; + } + } + } + } + } +} + +.content-warning { + display: block; + box-sizing: border-box; + color: var(--color-text-primary); + background: var(--color-bg-brand-softer); + border: 1px solid var(--color-border-on-bg-brand-softer); + border-radius: 8px; + padding: 8px (5px + 8px); + position: relative; + font-size: 15px; + line-height: 22px; + cursor: pointer; + + p { + margin-bottom: 8px; + font-weight: 500; + } + + .link-button { + font-size: inherit; + line-height: inherit; + font-weight: 500; + } + + &--filter { + color: var(--color-text-secondary); + + p { + font-weight: normal; + } + + .filter-name { + font-weight: 500; + color: var(--color-text-primary); + } + } +} + +.lists__item { + display: flex; + align-items: center; + gap: 16px; + padding-inline-end: 13px; + border-bottom: 1px solid var(--color-border-primary); + + &__title { + display: flex; + align-items: center; + gap: 5px; + padding: 16px 13px; + flex: 1 1 auto; + font-size: 16px; + line-height: 24px; + color: var(--color-text-secondary); + text-decoration: none; + + &:is(a):hover, + &:is(a):focus, + &:is(a):active { + color: var(--color-text-primary); + } + + input { + display: block; + width: 100%; + background: transparent; + border: 0; + padding: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; + color: inherit; + + &::placeholder { + color: var(--color-text-secondary); + opacity: 1; + } + + &:focus { + outline: 0; + } + } + } +} + +.column-search-header { + display: flex; + gap: 12px; + align-items: center; + border: 1px solid var(--color-border-primary); + border-top: 0; + border-bottom: 0; + padding: 16px; + padding-bottom: 8px; + + input { + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); + padding: 12px; + font-size: 16px; + line-height: normal; + border-radius: 4px; + display: block; + flex: 1 1 auto; + + &::placeholder { + color: var(--color-text-secondary); + opacity: 1; + } + + &:focus { + outline: 0; + } + } +} + +.column-footer { + padding: 16px; +} + +.lists-scrollable { + min-height: 50vh; +} + +.featured-carousel { + overflow: hidden; + flex-shrink: 0; + border-bottom: 1px solid var(--color-border-primary); + touch-action: pan-y; + + &__slides { + display: flex; + flex-wrap: nowrap; + align-items: start; + } + + &__slide { + flex: 0 0 auto; + flex-basis: 100%; + width: 100%; + overflow: hidden; + } + + .status { + border-bottom: 0; + } + + &__header { + padding: 8px 16px; + color: var(--color-text-secondary); + inset-inline-end: 0; + display: flex; + align-items: center; + gap: 4px; + } + + &__title { + flex-grow: 1; + font-size: 12px; + font-weight: 500; + text-transform: uppercase; + display: flex; + align-items: center; + gap: 4px; + + .icon { + width: 16px; + height: 16px; + } + } + + &__pagination { + display: flex; + align-items: center; + gap: 4px; + } +} diff --git a/app/javascript/styles_new/mastodon/containers.scss b/app/javascript/styles_new/mastodon/containers.scss new file mode 100644 index 00000000000..57c62a29e35 --- /dev/null +++ b/app/javascript/styles_new/mastodon/containers.scss @@ -0,0 +1,166 @@ +@use 'variables' as *; + +.container-alt { + width: 700px; + margin: 0 auto; + + @media screen and (width <= 740px) { + width: 100%; + margin: 0; + } +} + +.logo-container { + margin: 50px auto; + + h1 { + display: flex; + justify-content: center; + align-items: center; + + .logo { + height: 42px; + margin-inline-end: 10px; + } + + a { + display: flex; + justify-content: center; + align-items: center; + color: var(--color-text-primary); + text-decoration: none; + outline: 0; + padding: 12px 16px; + line-height: 32px; + font-weight: 500; + font-size: 14px; + } + } +} + +.compose-standalone { + .compose-form { + width: 400px; + margin: 0 auto; + padding: 10px 0; + padding-bottom: 20px; + box-sizing: border-box; + + @media screen and (width <= 400px) { + width: 100%; + padding: 20px; + } + } +} + +.account-header { + width: 400px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 10px; + font-size: 14px; + line-height: 20px; + box-sizing: border-box; + padding: 20px 0; + margin-top: 40px; + margin-bottom: 10px; + border-bottom: 1px solid var(--color-border-primary); + + @media screen and (width <= 440px) { + width: 100%; + margin: 0; + padding: 20px; + } + + .avatar { + width: 48px; + height: 48px; + flex: 0 0 auto; + + img { + width: 100%; + height: 100%; + display: block; + margin: 0; + border-radius: var(--avatar-border-radius); + } + } + + .name { + flex: 1 1 auto; + color: var(--color-text-primary); + + .username { + display: block; + font-size: 16px; + line-height: 24px; + text-overflow: ellipsis; + overflow: hidden; + color: var(--color-text-primary); + } + } + + .logout-link { + display: block; + font-size: 32px; + line-height: 40px; + flex: 0 0 auto; + } +} + +.redirect { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + font-size: 14px; + line-height: 18px; + + &__logo { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 30px; + + img { + height: 48px; + } + } + + &__message { + text-align: center; + + h1 { + font-size: 17px; + line-height: 22px; + font-weight: 700; + margin-bottom: 30px; + } + + p { + margin-bottom: 30px; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: var(--color-text-brand); + font-weight: 500; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + + &__link { + margin-top: 15px; + } +} diff --git a/app/javascript/styles_new/mastodon/css_variables.scss b/app/javascript/styles_new/mastodon/css_variables.scss new file mode 100644 index 00000000000..ba3319a6b48 --- /dev/null +++ b/app/javascript/styles_new/mastodon/css_variables.scss @@ -0,0 +1,228 @@ +@use 'theme_utils' as utils; + +:root { + --color-black: #000; + --color-grey-950: #181821; + --color-grey-800: #292938; + --color-grey-700: #444664; + --color-grey-600: #545778; + --color-grey-500: #696d91; + --color-grey-400: #8b8dac; + --color-grey-300: #b4b6cb; + --color-grey-200: #d8d9e3; + --color-grey-100: #f0f0f5; + --color-grey-50: #f0f1ff; + --color-white: #fff; + --color-indigo-600: #6147e6; + --color-indigo-400: #8886ff; + --color-indigo-300: #a5abfd; + --color-indigo-200: #c8cdfe; + --color-indigo-100: #e0e3ff; + --color-indigo-50: #f0f1ff; + --color-red-500: #ff637e; + --color-red-600: #ec003f; + --color-yellow-400: #ffb900; + --color-yellow-600: #e17100; + --color-green-400: #05df72; + --color-green-600: #00a63e; + + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-50); + --color-text-secondary: var(--color-grey-400); + --color-text-tertiary: var(--color-grey-500); + --color-text-on-inverted: var(--color-grey-950); + --color-text-brand: var(--color-indigo-400); + --color-text-brand-soft: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-brand) + ); + --color-text-on-brand-base: var(--color-white); + --color-text-error: var(--color-red-500); + --color-text-on-error-base: var(--color-white); + --color-text-warning: var(--color-yellow-400); + --color-text-on-warning-base: var(--color-white); + --color-text-success: var(--color-green-400); + --color-text-on-success-base: var(--color-white); + --color-text-disabled: var(--color-grey-600); + --color-text-on-disabled: var(--color-grey-400); + --color-text-bookmark-highlight: var(--color-text-error); + --color-text-favourite-highlight: var(--color-text-warning); + --color-text-on-media: var(--color-white); + --color-text-status-links: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + + /* BACKGROUND TOKENS */ + + // Neutrals + --color-bg-primary: var(--color-grey-950); + --overlay-strength-secondary: 10%; + --color-bg-secondary-base: var(--color-indigo-200); + --color-bg-secondary: #{utils.css-alpha( + var(--color-bg-secondary-base), + var(--overlay-strength-secondary) + )}; + --color-bg-secondary-solid: color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + ); + --color-bg-tertiary: color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) + ); + + // Utility + --color-bg-ambient: var(--color-bg-primary); + --color-bg-elevated: var(--color-grey-800); + --color-bg-inverted: var(--color-grey-50); + --color-bg-media-base: var(--color-black); + --color-bg-media-strength: 65%; + --color-bg-media: #{utils.css-alpha( + var(--color-bg-media-base), + var(--color-bg-media-strength) + )}; + --color-bg-overlay: var(--color-bg-primary); + --color-bg-disabled: var(--color-grey-700); + + // Brand + --overlay-strength-brand: 10%; + --color-bg-brand-base: var(--color-indigo-600); + --color-bg-brand-base-hover: color-mix( + in oklab, + var(--color-bg-brand-base), + black var(--overlay-strength-brand) + ); + --color-bg-brand-soft: #{utils.css-alpha( + var(--color-bg-brand-base), + calc(var(--overlay-strength-brand) * 1.5) + )}; + --color-bg-brand-softer: #{utils.css-alpha( + var(--color-bg-brand-base), + var(--overlay-strength-brand) + )}; + + // Error + --overlay-strength-error: 12%; + --color-bg-error-base: var(--color-red-600); + --color-bg-error-base-hover: color-mix( + in oklab, + var(--color-bg-error-base), + black var(--overlay-strength-error) + ); + --color-bg-error-soft: #{utils.css-alpha( + var(--color-bg-error-base), + calc(var(--overlay-strength-error) * 1.5) + )}; + --color-bg-error-softer: #{utils.css-alpha( + var(--color-bg-error-base), + var(--overlay-strength-error) + )}; + + // Warning + --overlay-strength-warning: 10%; + --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base-hover: color-mix( + in oklab, + var(--color-bg-warning-base), + black var(--overlay-strength-warning) + ); + --color-bg-warning-soft: #{utils.css-alpha( + var(--color-bg-warning-base), + calc(var(--overlay-strength-warning) * 1.5) + )}; + --color-bg-warning-softer: #{utils.css-alpha( + var(--color-bg-warning-base), + var(--overlay-strength-warning) + )}; + + // Success + --overlay-strength-success: 15%; + --color-bg-success-base: var(--color-green-600); + --color-bg-success-base-hover: color-mix( + in oklab, + var(--color-bg-success-base), + black var(--overlay-strength-success) + ); + --color-bg-success-soft: #{utils.css-alpha( + var(--color-bg-success-base), + calc(var(--overlay-strength-success) * 1.5) + )}; + --color-bg-success-softer: #{utils.css-alpha( + var(--color-bg-success-base), + var(--overlay-strength-success) + )}; + + /* BORDER TOKENS */ + + --border-strength-primary: 18%; + --color-border-primary: #{utils.css-alpha( + var(--color-indigo-200), + var(--border-strength-primary) + )}; + --color-border-media: rgb(252 248 255 / 15%); + --color-border-on-bg-secondary: #{utils.css-alpha( + var(--color-indigo-200), + calc(var(--border-strength-primary) / 1.5) + )}; + --color-border-on-bg-brand-softer: var(--color-border-primary); + --color-border-on-bg-error-softer: #{utils.css-alpha( + var(--color-text-error), + 50% + )}; + --color-border-on-bg-warning-softer: #{utils.css-alpha( + var(--color-text-warning), + 50% + )}; + --color-border-on-bg-success-softer: #{utils.css-alpha( + var(--color-text-success), + 50% + )}; + --color-border-on-bg-inverted: var(--color-border-primary); + + /* SHADOW TOKENS */ + + --shadow-strength-primary: 80%; + --color-shadow-primary: #{utils.css-alpha( + var(--color-black), + var(--shadow-strength-primary) + )}; + --dropdown-shadow: + 0 20px 25px -5px var(--color-shadow-primary), + 0 8px 10px -6px var(--color-shadow-primary); + --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); + + /* GRAPHS/CHARTS TOKENS */ + + --color-graph-primary-stroke: var(--color-text-brand); + --color-graph-primary-fill: var(--color-bg-brand-softer); + --color-graph-warning-stroke: var(--color-text-warning); + --color-graph-warning-fill: var(--color-bg-warning-softer); + --color-graph-disabled-stroke: var(--color-text-disabled); + --color-graph-disabled-fill: var(--color-bg-disabled); + + /* LEGACY TOKENS */ + + --rich-text-container-color: rgb(87 24 60 / 100%); + --rich-text-text-color: rgb(255 175 212 / 100%); + --rich-text-decorations-color: rgb(128 58 95 / 100%); + + /* MISCELLANEOUS */ + + --outline-focus-default: 2px solid var(--color-text-brand); + --avatar-border-radius: 8px; +} + +body { + // Variable for easily inverting directional UI elements, + --text-x-direction: 1; + + &.rtl { + --text-x-direction: -1; + } +} diff --git a/app/javascript/styles_new/mastodon/dashboard.scss b/app/javascript/styles_new/mastodon/dashboard.scss new file mode 100644 index 00000000000..db3f0e8a842 --- /dev/null +++ b/app/javascript/styles_new/mastodon/dashboard.scss @@ -0,0 +1,120 @@ +@use 'variables' as *; + +.dashboard__counters { + display: flex; + flex-wrap: wrap; + margin: 0 -5px; + margin-bottom: 20px; + + & > div { + box-sizing: border-box; + flex: 0 0 33.333%; + padding: 0 5px; + margin-bottom: 10px; + + & > div, + & > a { + padding: 20px; + background: var(--color-bg-primary); + border-radius: 4px; + border: 1px solid var(--color-border-primary); + box-sizing: border-box; + height: 100%; + } + + & > a { + text-decoration: none; + color: inherit; + display: block; + + &:hover, + &:focus, + &:active { + background: var(--color-bg-brand-softer); + } + } + } + + &__num, + &__text { + text-align: center; + font-weight: 500; + font-size: 24px; + color: var(--color-text-primary); + margin-bottom: 20px; + line-height: 30px; + } + + &__text { + font-size: 18px; + } + + &__label { + font-size: 14px; + color: var(--color-text-secondary); + text-align: center; + font-weight: 500; + } +} + +.dashboard { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); + gap: 10px; + + @media screen and (width <= 1350px) { + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + } + + &__item { + &--span-double-column { + grid-column: span 2; + } + + &--span-double-row { + grid-row: span 2; + } + + h4 { + padding-top: 20px; + } + } + + &__quick-access { + display: flex; + align-items: baseline; + border-radius: 4px; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + transition: all 100ms ease-in; + font-size: 14px; + padding: 8px 16px; + text-decoration: none; + margin-bottom: 4px; + + &:active, + &:focus, + &:hover { + background-color: var(--color-bg-brand-base-hover); + transition: all 200ms ease-out; + } + + &.positive { + background: var(--color-bg-success-softer); + color: var(--color-text-success); + } + + &.negative { + background: var(--color-bg-error-softer); + color: var(--color-text-error); + } + + span { + flex: 1 1 auto; + } + + strong { + font-weight: 700; + } + } +} diff --git a/app/javascript/styles_new/mastodon/emoji_picker.scss b/app/javascript/styles_new/mastodon/emoji_picker.scss new file mode 100644 index 00000000000..ad2f2f630db --- /dev/null +++ b/app/javascript/styles_new/mastodon/emoji_picker.scss @@ -0,0 +1,248 @@ +@use 'variables' as *; + +.emoji-mart { + font-size: 13px; + display: inline-block; + + &, + * { + box-sizing: border-box; + line-height: 1.15; + } + + .emoji-mart-emoji { + padding: 6px; + } +} + +.emoji-mart-bar { + &:first-child { + background: var(--color-bg-tertiary); + border-bottom: 1px solid var(--color-border-primary); + } +} + +.emoji-mart-anchors { + display: flex; + justify-content: space-between; + padding: 0 6px; + line-height: 0; +} + +.emoji-mart-anchor { + position: relative; + flex: 1; + text-align: center; + padding: 12px 4px; + overflow: hidden; + transition: color 0.1s ease-out; + cursor: pointer; + background: transparent; + border: 0; + color: var(--color-text-secondary); + + &:hover { + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + } +} + +.emoji-mart-anchor-selected { + color: var(--color-text-brand); + + &:hover { + color: var(--color-text-brand-soft); + } + + .emoji-mart-anchor-bar { + bottom: -1px; + } +} + +.emoji-mart-anchor-bar { + position: absolute; + bottom: -5px; + inset-inline-start: 0; + width: 100%; + height: 4px; + background-color: var(--color-text-brand); +} + +.emoji-mart-anchors { + i { + display: inline-block; + width: 100%; + max-width: 22px; + } + + svg { + fill: currentColor; + max-height: 18px; + } +} + +.emoji-mart-scroll { + overflow-y: scroll; + height: 270px; + max-height: 35vh; + padding: 0 6px 6px; + will-change: transform; +} + +.emoji-mart-search { + padding: 10px; + padding-inline-end: 45px; + position: relative; + + input { + font-size: 16px; + font-weight: 400; + padding: 7px 9px; + padding-inline-end: 25px; + font-family: inherit; + display: block; + width: 100%; + background: var(--color-bg-secondary); + color: var(--color-text-secondary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + + &::-moz-focus-inner { + border: 0; + } + + &:active, + &:focus { + outline: none !important; + border-width: 1px !important; + } + + &::-webkit-search-cancel-button { + display: none; + } + } +} + +.emoji-mart-search-icon { + position: absolute; + top: 18px; + inset-inline-end: 45px + 5px; + z-index: 2; + padding: 2px 5px 1px; + border: 0; + background: none; + transition: all 100ms linear; + transition-property: opacity; + pointer-events: auto; + + &:disabled { + cursor: default; + pointer-events: none; + } + + svg { + fill: currentColor; + } +} + +.emoji-mart-category .emoji-mart-emoji { + cursor: pointer; + + span { + z-index: 1; + position: relative; + text-align: center; + display: inline-flex !important; + align-items: center; + justify-content: center; + } + + &:hover::before { + z-index: -1; + content: ''; + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100%; + background-color: var(--color-bg-brand-softer); + border-radius: 100%; + } +} + +.emoji-mart-category-label { + z-index: 2; + position: relative; + position: -webkit-sticky; + position: sticky; + top: 0; + + span { + display: block; + width: 100%; + font-weight: 500; + padding: 5px 6px; + } +} + +/* For screenreaders only, via https://stackoverflow.com/a/19758620 */ +.emoji-mart-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip-path: inset(50%); + border: 0; +} + +.emoji-mart-category-list { + margin: 0; + padding: 0; +} + +.emoji-mart-category-list li { + list-style: none; + margin: 0; + padding: 0; + display: inline-block; +} + +.emoji-mart-emoji { + position: relative; + display: inline-block; + background: transparent; + border: 0; + padding: 0; + font-size: 0; + + span { + width: 22px; + height: 22px; + } +} + +.emoji-mart-no-results { + font-size: 14px; + color: var(--color-text-tertiary); + text-align: center; + padding: 5px 6px; + padding-top: 70px; + + .emoji-mart-no-results-label { + margin-top: 0.2em; + } + + .emoji-mart-emoji:hover::before { + cursor: default; + content: none; + } +} + +.emoji-mart-preview { + display: none; +} diff --git a/app/javascript/styles_new/mastodon/forms.scss b/app/javascript/styles_new/mastodon/forms.scss new file mode 100644 index 00000000000..9f716c2126d --- /dev/null +++ b/app/javascript/styles_new/mastodon/forms.scss @@ -0,0 +1,1449 @@ +@use 'sass:color'; +@use 'variables' as *; + +code { + font-family: $font-monospace, monospace; + font-weight: 400; +} + +.form-container { + max-width: 450px; + padding: 20px; + padding-bottom: 50px; + margin: 50px auto; +} + +.form-section { + border-radius: 8px; + background: var(--color-bg-secondary); + padding: 24px; + margin-bottom: 24px; +} + +.fade-out-top { + position: relative; + overflow: hidden; + height: 160px; + max-width: 566px; + margin-inline: auto; + + &::after { + content: ''; + display: block; + background: linear-gradient( + to bottom, + var(--color-bg-secondary-solid), + transparent + ); + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + height: 100px; + pointer-events: none; + } + + & > div { + position: absolute; + inset-inline-start: 0; + bottom: 0; + } +} + +.indicator-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + color: var(--color-text-primary); + + &.success { + color: var(--color-text-on-success-base); + background: var(--color-bg-success-base); + } + + &.failure { + color: var(--color-text-on-error-base); + background: var(--color-bg-error-base); + } +} + +.simple_form { + &.hidden { + display: none; + } + + .input { + margin-bottom: 16px; + overflow: hidden; + + &:last-child { + margin-bottom: 0; + } + + &__toolbar { + margin-top: 16px; + display: flex; + align-items: center; + gap: 16px; + + .character-counter { + flex: 0 0 auto; + } + } + + &.hidden { + margin: 0; + } + + &.radio_buttons { + .radio { + margin-bottom: 15px; + + &:last-child { + margin-bottom: 0; + } + } + + .radio > label { + position: relative; + padding-inline-start: 28px; + + input { + position: absolute; + top: -2px; + inset-inline-start: 0; + } + } + } + + &.boolean { + position: relative; + margin-bottom: 0; + + .label_input > label { + font-family: inherit; + font-size: 14px; + padding-top: 5px; + color: var(--color-text-primary); + display: block; + width: auto; + } + + .label_input, + .hint { + padding-inline-start: 28px; + } + + .label_input__wrapper { + position: static; + } + + label.checkbox { + position: absolute; + top: 2px; + inset-inline-start: 0; + } + + label a { + color: var(--color-text-brand); + text-decoration: underline; + + &:hover, + &:active, + &:focus { + text-decoration: none; + } + } + + .overridden, + .recommended, + .not_recommended { + position: absolute; + margin: 0 4px; + margin-top: -2px; + } + } + } + + .row { + display: flex; + margin: 0 -5px; + + .input { + box-sizing: border-box; + flex: 1 1 auto; + width: 50%; + padding: 0 5px; + } + } + + .title { + font-size: 28px; + line-height: 33px; + font-weight: 700; + margin-bottom: 15px; + } + + .lead { + font-size: 17px; + line-height: 22px; + color: var(--color-text-primary); + margin-bottom: 30px; + + &.invited-by { + margin-bottom: 15px; + } + + a { + color: var(--color-text-brand); + } + } + + .rules-list { + font-size: 17px; + line-height: 22px; + margin-bottom: 30px; + } + + .hint { + color: var(--color-text-secondary); + + a { + color: var(--color-text-brand); + } + + code { + border-radius: 3px; + padding: 0.2em 0.4em; + background: var(--color-bg-secondary); + } + + li { + list-style: disc; + margin-inline-start: 18px; + } + + .icon { + vertical-align: -3px; + } + } + + ul.hint { + margin-bottom: 15px; + } + + span.hint { + display: block; + font-size: 12px; + margin-top: 4px; + } + + p.hint { + margin-bottom: 15px; + color: var(--color-text-secondary); + + &.subtle-hint { + text-align: center; + font-size: 12px; + line-height: 18px; + margin-top: 15px; + margin-bottom: 0; + } + } + + .authentication-hint { + margin-bottom: 25px; + } + + .card { + margin-bottom: 15px; + } + + strong { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + .input.with_floating_label { + .label_input { + display: flex; + + & > label { + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + font-weight: 500; + min-width: 150px; + flex: 0 0 auto; + } + + input, + select { + flex: 1 1 auto; + } + } + + &.select .hint { + margin-top: 6px; + margin-inline-start: 150px; + } + } + + .input.with_label { + .label_input > label { + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + display: block; + margin-bottom: 8px; + overflow-wrap: break-word; + font-weight: 500; + } + + .hint { + margin-top: 6px; + } + + ul { + flex: 390px; + } + } + + .input.with_block_label { + max-width: none; + + & > label { + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + display: block; + font-weight: 600; + line-height: 20px; + } + + .hint { + line-height: 16px; + margin-bottom: 12px; + } + + ul { + columns: 2; + + @media screen and (max-width: $mobile-breakpoint) { + columns: 1; + } + } + } + + .input.with_block_label.user_role_permissions_as_keys ul { + columns: unset; + } + + .input.datetime .label_input, + .input.date .label_input { + display: flex; + gap: 4px; + align-items: center; + + select { + display: inline-block; + width: auto; + flex: 0; + } + } + + .input.date_of_birth .label_input { + display: flex; + gap: 8px; + align-items: center; + + input { + box-sizing: content-box; + width: 32px; + flex: 0; + + &:last-child { + width: 64px; + } + } + } + + .input.select.select--languages { + min-width: 32ch; + } + + .required abbr { + text-decoration: none; + color: var(--color-text-error); + } + + .fields-group { + margin-bottom: 25px; + + .input:last-child { + margin-bottom: 0; + } + + &__thumbnail { + display: block; + margin: 0; + margin-bottom: 10px; + max-width: 100%; + height: auto; + border-radius: var(--avatar-border-radius); + background: url('@/images/void.png'); + + &[src$='missing.png'] { + visibility: hidden; + } + + &:last-child { + margin-bottom: 0; + } + + &#account_avatar-preview { + width: 90px; + height: 90px; + object-fit: cover; + } + } + } + + .fields-row { + display: flex; + margin: 0 -10px; + padding-top: 5px; + margin-bottom: 25px; + + .input { + max-width: none; + } + + &__column { + box-sizing: border-box; + padding: 0 10px; + flex: 1 1 auto; + min-height: 1px; + + &-6 { + max-width: 50%; + } + + .actions { + margin-top: 27px; + } + } + + .fields-group:last-child, + .fields-row__column.fields-group { + margin-bottom: 0; + } + + @media screen and (max-width: $no-columns-breakpoint) { + display: block; + margin-bottom: 0; + + &__column { + max-width: none; + } + + .fields-group:last-child, + .fields-row__column.fields-group, + .fields-row__column { + margin-bottom: 25px; + } + } + + .fields-group.invited-by { + margin-bottom: 30px; + + .hint { + text-align: center; + } + } + } + + .input.radio_buttons .radio label { + margin-bottom: 5px; + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + display: block; + width: auto; + } + + .check_boxes { + .checkbox { + label { + font-family: inherit; + font-size: 14px; + color: var(--color-text-primary); + display: inline-block; + width: auto; + position: relative; + padding-top: 5px; + padding-inline-start: 25px; + flex: 1 1 auto; + } + + input[type='checkbox'] { + position: absolute; + inset-inline-start: 0; + top: 5px; + margin: 0; + } + } + } + + .input.static .label_input__wrapper { + font-size: 14px; + padding: 10px; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + } + + input[type='text'], + input[type='number'], + input[type='email'], + input[type='password'], + input[type='url'], + input[type='datetime-local'], + textarea { + box-sizing: border-box; + font-size: 14px; + line-height: 20px; + color: var(--color-text-primary); + display: block; + width: 100%; + outline: 0; + font-family: inherit; + resize: vertical; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + padding: 10px 16px; + + &:invalid { + box-shadow: none; + } + + &:required:valid { + border-color: var(--color-text-success); + } + + @media screen and (width <= 600px) { + font-size: 16px; + } + } + + input[type='text'], + input[type='number'], + input[type='email'], + input[type='password'], + input[type='datetime-local'] { + &:focus:invalid:not(:placeholder-shown), + &:required:invalid:not(:placeholder-shown) { + border-color: var(--color-text-error); + } + } + + .input.field_with_errors { + label { + color: var(--color-text-error); + } + + input[type='text'], + input[type='number'], + input[type='email'], + input[type='password'], + input[type='datetime-local'], + textarea, + select { + border-color: var(--color-text-error); + } + + .error { + display: block; + font-weight: 500; + color: var(--color-text-error); + margin-top: 4px; + } + } + + .input.disabled { + opacity: 0.5; + } + + .actions { + margin-top: 30px; + display: flex; + gap: 10px; + + &.actions--top { + margin-top: 0; + margin-bottom: 30px; + } + } + + .stacked-actions { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 30px; + margin-bottom: 15px; + } + + .btn { + display: block; + width: 100%; + border: 0; + border-radius: 4px; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); + font-size: 15px; + line-height: 22px; + height: auto; + padding: 7px 18px; + text-decoration: none; + text-align: center; + box-sizing: border-box; + cursor: pointer; + font-weight: 500; + outline: 0; + + &:last-child { + margin-inline-end: 0; + } + + &:active, + &:focus, + &:hover { + background: var(--color-bg-brand-base-hover); + } + + &:disabled, + &:disabled:hover { + color: var(--color-text-on-disabled); + background: var(--color-bg-disabled); + } + + &.negative { + background: var(--color-bg-error-base); + + &:hover, + &:active, + &:focus { + background: var(--color-bg-error-base-hover); + } + } + } + + select { + appearance: none; + box-sizing: border-box; + font-size: 14px; + color: var(--color-text-primary); + display: block; + width: 100%; + outline: 0; + font-family: inherit; + resize: vertical; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + padding-inline-start: 10px; + padding-inline-end: 30px; + height: 41px; + + @media screen and (width <= 600px) { + font-size: 16px; + } + } + + h4 { + margin-bottom: 15px !important; + } + + .label_input { + position: relative; + + &__loading-indicator { + box-sizing: border-box; + position: absolute; + top: 0; + inset-inline-start: 0; + border: 1px solid transparent; + padding: 10px 16px; + width: 100%; + } + + &__wrapper { + position: relative; + } + + &__append { + position: absolute; + inset-inline-end: 3px; + top: 1px; + padding: 10px; + font-size: 14px; + color: var(--color-text-tertiary); + font-family: inherit; + pointer-events: none; + cursor: default; + max-width: 50%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &::after { + content: ''; + display: block; + position: absolute; + top: 0; + inset-inline-end: 0; + bottom: 1px; + width: 5px; + } + } + } + + .status-card { + contain: unset; + } +} + +/* Double-chevron icon for custom select components */ +.select-wrapper, +.select .label_input__wrapper { + width: 100%; + + &::after { + --icon-size: 11px; + + content: ''; + position: absolute; + top: 0; + bottom: 0; + inset-inline-end: 9px; + width: var(--icon-size); + background-color: var(--color-text-tertiary); + pointer-events: none; + mask-image: url("data:image/svg+xml;utf8,"); + mask-position: right center; + mask-size: var(--icon-size); + mask-repeat: no-repeat; + } +} + +.block-icon { + display: block; + margin: 0 auto; + margin-bottom: 10px; + font-size: 24px; +} + +.flash-message { + color: var(--color-text-brand); + background: transparent; + border: 1px solid var(--color-text-brand); + border-radius: 4px; + padding: 15px 10px; + margin-bottom: 30px; + text-align: center; + + &.notice { + border: 1px solid var(--color-border-on-bg-success-softer); + background: var(--color-bg-success-softer); + color: var(--color-text-success); + } + + &.warning { + border: 1px solid var(--color-border-on-bg-warning-softer); + background: var(--color-bg-warning-softer); + color: var(--color-text-warning); + } + + &.alert { + border: 1px solid var(--color-border-on-bg-error-softer); + background: var(--color-bg-error-softer); + color: var(--color-text-error); + } + + &.hidden { + display: none; + } + + &.hidden-on-touch-devices { + @media screen and (pointer: coarse) { + display: none; + } + } + + a { + display: inline-block; + color: var(--color-text-secondary); + text-decoration: none; + + &:hover { + color: var(--color-text-primary); + text-decoration: underline; + } + } + + &.warning a { + font-weight: 700; + color: inherit; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + color: inherit; + } + } + + p { + margin-bottom: 15px; + } + + .oauth-code { + outline: 0; + box-sizing: border-box; + display: block; + width: 100%; + border: 0; + padding: 10px; + font-family: $font-monospace, monospace; + background: var(--color-bg-secondary); + color: var(--color-text-primary); + font-size: 14px; + margin: 0; + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + + &:focus { + background: var(--color-bg-brand-softer); + } + } + + strong { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + @media screen and (440px < width <= 740px) { + margin-top: 40px; + } + + &.translation-prompt { + text-align: unset; + color: unset; + + a { + text-decoration: underline; + } + } +} + +.flash-message-stack { + margin-bottom: 30px; + + .flash-message { + border-radius: 0; + margin-bottom: 0; + border-top-width: 0; + + &:first-child { + border-radius: 4px 4px 0 0; + border-top-width: 1px; + } + + &:last-child { + border-radius: 0 0 4px 4px; + + &:first-child { + border-radius: 4px; + } + } + } +} + +.form-footer { + margin-top: 30px; + text-align: center; + + a { + color: var(--color-text-secondary); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} + +.quick-nav { + list-style: none; + margin-bottom: 25px; + font-size: 14px; + + li { + display: inline-block; + margin-inline-end: 10px; + } + + a { + color: var(--color-text-brand); + text-transform: uppercase; + text-decoration: none; + font-weight: 700; + + &:hover, + &:focus, + &:active { + color: var(--color-text-brand-soft); + } + } +} + +.oauth-prompt, +.follow-prompt { + margin-bottom: 30px; + color: var(--color-text-secondary); + + h2 { + font-size: 16px; + margin-bottom: 30px; + text-align: center; + } + + strong { + color: var(--color-text-primary); + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } +} + +.oauth-prompt { + h3 { + color: var(--color-text-primary); + font-size: 17px; + line-height: 22px; + font-weight: 500; + margin-bottom: 30px; + } + + p { + font-size: 14px; + line-height: 18px; + margin-bottom: 30px; + } + + .permissions-list { + border: 1px solid var(--color-border-primary); + border-radius: 4px; + background: var(--color-bg-secondary); + margin-bottom: 30px; + } + + .actions { + margin: 0 -10px; + display: flex; + + form { + box-sizing: border-box; + padding: 0 10px; + flex: 1 1 auto; + min-height: 1px; + width: 50%; + } + } +} + +.qr-wrapper { + display: flex; + flex-wrap: wrap; + align-items: flex-start; +} + +.qr-code { + flex: 0 0 auto; + background: white; + padding: 4px; + margin: 0 10px 20px 0; + box-shadow: 0 0 15px var(--color-shadow-primary); + display: inline-block; + + svg { + display: block; + margin: 0; + } +} + +.qr-alternative { + margin-bottom: 20px; + color: var(--color-text-primary); + flex: 150px; + + samp { + display: block; + font-size: 14px; + } +} + +.action-pagination { + display: flex; + flex-wrap: wrap; + align-items: center; + + .actions, + .pagination { + flex: 1 1 auto; + } + + .actions { + padding: 30px 0; + padding-inline-end: 20px; + flex: 0 0 auto; + } +} + +.post-follow-actions { + text-align: center; + color: var(--color-text-secondary); + + div { + margin-bottom: 4px; + } +} + +.alternative-login { + margin-top: 20px; + margin-bottom: 20px; + + h4 { + font-size: 16px; + color: var(--color-text-primary); + text-align: center; + margin-bottom: 20px; + border: 0; + padding: 0; + } + + .button { + display: block; + } +} + +.scope-danger { + color: var(--color-text-error); +} + +.form_admin_settings_site_short_description, +.form_admin_settings_site_description, +.form_admin_settings_site_extended_description, +.form_admin_settings_site_terms, +.form_admin_settings_custom_css, +.form_admin_settings_closed_registrations_message { + textarea { + font-family: $font-monospace, monospace; + } +} + +.input-copy { + color: var(--color-text-primary); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: 4px; + display: flex; + align-items: center; + padding-inline-end: 4px; + position: relative; + top: 1px; + transition: border-color 300ms linear; + + &__wrapper { + flex: 1 1 auto; + } + + input[type='text'] { + background: transparent; + border: 0; + padding: 10px; + font-size: 14px; + font-family: $font-monospace, monospace; + } + + button { + flex: 0 0 auto; + margin: 4px; + text-transform: none; + font-weight: 400; + font-size: 14px; + padding: 7px 18px; + padding-bottom: 6px; + width: auto; + transition: background 300ms linear; + } + + &.copied { + border-color: var(--color-text-success); + transition: none; + + button { + background: var(--color-bg-success-base); + transition: none; + } + } +} + +.input.user_confirm_password, +.input.user_website { + &:not(.field_with_errors) { + display: none; + } +} + +.simple_form .h-captcha { + display: flex; + justify-content: center; + margin-bottom: 30px; +} + +.permissions-list { + &__item { + padding: 15px; + color: var(--color-text-primary); + border-bottom: 1px solid var(--color-border-primary); + display: flex; + align-items: center; + + &__text { + flex: 1 1 auto; + + &__title { + font-weight: 500; + } + + &__type { + color: var(--color-text-secondary); + overflow-wrap: anywhere; + } + } + + &__icon { + flex: 0 0 auto; + font-size: 18px; + width: 30px; + color: var(--color-text-success); + display: flex; + align-items: center; + } + + &:last-child { + border-bottom: 0; + } + } +} + +// Only remove padding when listing applications, to prevent styling issues on +// the Authorization page. +.applications-list { + .permissions-list__item:last-child { + padding-bottom: 0; + } +} + +.keywords-table { + thead { + th { + white-space: nowrap; + } + + th:first-child { + width: 100%; + } + } + + tfoot { + td { + border: 0; + } + } + + .input.string { + margin-bottom: 0; + } + + .label_input__wrapper { + margin-top: 10px; + } + + .table-action-link { + margin-top: 10px; + white-space: nowrap; + } +} + +.progress-tracker { + display: flex; + align-items: center; + padding-bottom: 30px; + margin-bottom: 30px; + + li { + flex: 0 0 auto; + position: relative; + } + + .separator { + height: 2px; + background: var(--color-border-primary); + flex: 1 1 auto; + + &.completed { + background: var(--color-text-brand); + } + } + + .circle { + box-sizing: border-box; + position: relative; + width: 30px; + height: 30px; + border-radius: 50%; + border: 2px solid var(--color-border-primary); + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 16px; + } + } + + .label { + position: absolute; + font-size: 14px; + font-weight: 500; + color: var(--color-text-primary); + padding-top: 10px; + text-align: center; + width: 100px; + left: 50%; + transform: translateX(-50%); + } + + li:first-child .label { + inset-inline-start: 0; + inset-inline-end: auto; + text-align: start; + transform: none; + } + + li:last-child .label { + inset-inline-start: auto; + inset-inline-end: 0; + text-align: end; + transform: none; + } + + .active .circle { + border-color: var(--color-text-brand); + + &::before { + content: ''; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--color-text-brand); + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } + } + + .completed .circle { + border-color: var(--color-text-brand); + background: var(--color-text-brand); + } +} + +.app-form { + padding: 16px; + + &__avatar-input, + &__header-input { + display: block; + border-radius: 8px; + background: var(--color-bg-secondary); + position: relative; + cursor: pointer; + + &:hover { + background-color: var(--color-bg-brand-softer); + } + + img { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; + z-index: 0; + } + + .icon { + position: absolute; + inset-inline-start: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: var(--color-text-secondary); + z-index: 3; + } + + &.selected .icon { + color: var(--color-text-primary); + transform: none; + inset-inline-start: auto; + inset-inline-end: 8px; + top: auto; + bottom: 8px; + } + + &.invalid img { + outline: 1px solid var(--color-text-error); + outline-offset: -1px; + } + + &.invalid::before { + display: block; + content: ''; + width: 100%; + height: 100%; + position: absolute; + background: var(--color-bg-error-softer); + z-index: 2; + border-radius: 8px; + } + } + + &__avatar-input { + width: 80px; + height: 80px; + } + + &__header-input { + aspect-ratio: 580/193; + } + + &__toggle { + display: flex; + align-items: center; + gap: 16px; + color: var(--color-text-secondary); + font-size: 14px; + line-height: 20px; + + .icon { + flex: 0 0 auto; + } + + .icon { + width: 24px; + height: 24px; + } + + &__label { + flex: 1 1 auto; + + strong { + color: var(--color-text-primary); + font-weight: 600; + } + + .hint { + display: block; + font-size: 14px; + color: var(--color-text-secondary); + } + + .recommended { + position: absolute; + margin: 0 4px; + margin-top: -2px; + } + } + + &__toggle { + flex: 0 0 auto; + display: flex; + align-items: center; + } + + &__toggle > div { + display: flex; + border-inline-start: 1px solid var(--color-border-primary); + padding-inline-start: 16px; + } + } + + &__link { + display: flex; + gap: 16px; + padding: 8px 0; + align-items: center; + text-decoration: none; + color: var(--color-text-primary); + margin-bottom: 16px; + + &__text { + flex: 1 1 auto; + font-size: 14px; + line-height: 20px; + color: var(--color-text-secondary); + + strong { + font-weight: 600; + display: block; + color: var(--color-text-primary); + } + + .icon { + vertical-align: -5px; + width: 20px; + height: 20px; + } + } + } +} diff --git a/app/javascript/styles_new/mastodon/lists.scss b/app/javascript/styles_new/mastodon/lists.scss new file mode 100644 index 00000000000..6019cd80028 --- /dev/null +++ b/app/javascript/styles_new/mastodon/lists.scss @@ -0,0 +1,19 @@ +.no-list { + list-style: none; + + li { + display: inline-block; + margin: 0 5px; + } +} + +.recovery-codes { + list-style: none; + margin: 0 auto; + + li { + font-size: 125%; + line-height: 1.5; + letter-spacing: 1px; + } +} diff --git a/app/javascript/styles_new/mastodon/modal.scss b/app/javascript/styles_new/mastodon/modal.scss new file mode 100644 index 00000000000..6af2a182b63 --- /dev/null +++ b/app/javascript/styles_new/mastodon/modal.scss @@ -0,0 +1,53 @@ +@use 'variables' as *; + +.modal-layout { + background: var(--color-bg-brand-softer); + display: flex; + flex-direction: column; + height: 100vh; + padding: 0; +} + +.modal-layout__mastodon { + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-end; + + > div { + flex: 1; + max-height: 235px; + position: relative; + + img { + max-height: 100%; + max-width: 100%; + height: 100%; + position: absolute; + bottom: 0; + inset-inline-start: 0; + } + } +} + +@media screen and (width <= 600px) { + .account-header { + margin-top: 0; + } +} + +.with-zig-zag-decoration { + &::after { + content: ''; + position: absolute; + inset: auto 0 0; + height: 32px; + background-color: var(--color-bg-brand-softer); + + /* Decorative zig-zag pattern at the bottom of the page */ + mask-image: url('data:image/svg+xml;utf8,'); + mask-position: bottom; + mask-repeat: repeat-x; + z-index: -1; + } +} diff --git a/app/javascript/styles_new/mastodon/polls.scss b/app/javascript/styles_new/mastodon/polls.scss new file mode 100644 index 00000000000..19fb8dd505f --- /dev/null +++ b/app/javascript/styles_new/mastodon/polls.scss @@ -0,0 +1,232 @@ +@use 'sass:color'; +@use 'variables' as *; + +.poll { + margin-top: 16px; + font-size: 14px; + + li { + margin-bottom: 10px; + position: relative; + } + + &__chart { + border-radius: 4px; + display: block; + background: rgb(from var(--color-text-brand) r g b / 60%); + height: 5px; + min-width: 1%; + + &.leading { + background: var(--color-text-brand); + } + } + + progress { + border: 0; + display: block; + width: 100%; + height: 5px; + appearance: none; + background: transparent; + + &::-webkit-progress-bar { + background: transparent; + } + + // Those rules need to be entirely separate or they won't work, hence the + // duplication + &::-moz-progress-bar { + border-radius: 4px; + background: rgb(from var(--color-text-brand) r g b / 60%); + } + + &::-webkit-progress-value { + border-radius: 4px; + background: rgb(from var(--color-text-brand) r g b / 60%); + } + } + + &__option { + position: relative; + display: flex; + align-items: flex-start; + gap: 8px; + padding: 6px 0; + line-height: 18px; + cursor: default; + overflow: hidden; + + &__text { + display: inline-block; + overflow-wrap: break-word; + max-width: calc(100% - 45px - 25px); + } + + input[type='radio'], + input[type='checkbox'] { + display: none; + } + + .autosuggest-input { + flex: 1 1 auto; + } + + input[type='text'] { + display: block; + box-sizing: border-box; + width: 100%; + font-size: 14px; + color: var(--color-text-primary); + outline: 0; + font-family: inherit; + background: var(--color-bg-primary); + border: 1px solid var(--color-text-secondary); + border-radius: 4px; + padding: 8px 12px; + + &:focus { + border-color: var(--color-text-brand); + } + + @media screen and (width <= 600px) { + font-size: 16px; + line-height: 24px; + letter-spacing: 0.5px; + } + } + + &.selectable { + cursor: pointer; + } + + &.editable, + &.disabled { + align-items: center; + overflow: visible; + } + } + + &__input { + display: block; + position: relative; + border: 1px solid var(--color-text-secondary); + box-sizing: border-box; + width: 17px; + height: 17px; + border-radius: 50%; + flex: 0 0 auto; + + &.checkbox { + border-radius: 4px; + } + + &:active, + &:focus, + &:hover { + border-color: var(--color-text-success); + border-width: 4px; + } + + &.active { + background-color: var(--color-bg-success-base); + border-color: var(--color-text-success); + } + + &::-moz-focus-inner { + outline: 0 !important; + border: 0; + } + + &:focus, + &:active { + outline: 0 !important; + } + + &.disabled { + border-color: var(--color-text-disabled); + + &.active { + background: var(--color-text-disabled); + } + + &:active, + &:focus, + &:hover { + border-color: var(--color-text-disabled); + border-width: 1px; + } + } + } + + &__option.editable &__input, + &__option.disabled &__input { + &:active, + &:focus, + &:hover { + border-color: var(--color-text-primary); + border-width: 1px; + } + } + + &__number { + display: inline-block; + width: 45px; + font-weight: 700; + flex: 0 0 45px; + } + + &__voted { + padding: 0 5px; + display: inline-block; + + &__mark { + font-size: 18px; + } + } + + &__footer { + padding-top: 6px; + padding-bottom: 5px; + color: var(--color-text-tertiary); + } + + &__link { + display: inline; + background: transparent; + padding: 0; + margin: 0; + border: 0; + color: var(--color-text-tertiary); + text-decoration: underline; + font-size: inherit; + + &:hover { + text-decoration: none; + } + + &:active, + &:focus { + background-color: var(--color-bg-secondary); + } + } + + .button { + height: 36px; + padding: 0 16px; + margin-inline-end: 10px; + font-size: 14px; + } +} + +.muted .poll { + color: var(--color-text-tertiary); + + &__chart { + background: rgb(from var(--color-text-brand) r g b / 40%); + + &.leading { + background: rgb(from var(--color-text-brand) r g b / 60%); + } + } +} diff --git a/app/javascript/styles_new/mastodon/reset.scss b/app/javascript/styles_new/mastodon/reset.scss new file mode 100644 index 00000000000..3644b94cdf6 --- /dev/null +++ b/app/javascript/styles_new/mastodon/reset.scss @@ -0,0 +1,58 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + line-height: 1; +} + +ol, ul { + list-style: none; +} + +blockquote, q { + quotes: none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +html:has(body.custom-scrollbars) { + scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); +} diff --git a/app/javascript/styles_new/mastodon/rich_text.scss b/app/javascript/styles_new/mastodon/rich_text.scss new file mode 100644 index 00000000000..4ac3f025fec --- /dev/null +++ b/app/javascript/styles_new/mastodon/rich_text.scss @@ -0,0 +1,116 @@ +.status__content__text, +.e-content, +.edit-indicator__content, +.reply-indicator__content { + code { + background: var(--rich-text-container-color); + padding: 4px; + border-radius: 4px; + color: var(--rich-text-text-color); + font-size: 0.85em; + } + + pre { + background: var(--rich-text-container-color); + padding: 8px; + border-radius: 4px; + color: var(--rich-text-text-color); + + code { + padding: 0; + background: transparent; + } + } + + pre, + blockquote { + margin-bottom: 22px; + white-space: pre-wrap; + unicode-bidi: plaintext; + + &:last-child { + margin-bottom: 0; + } + } + + blockquote { + padding-inline-start: 32px; + color: var(--rich-text-text-color); + white-space: normal; + position: relative; + + &::before { + display: block; + content: ''; + width: 24px; + height: 20px; + mask-image: url('@/images/quote.svg'); + background-color: var(--rich-text-decorations-color); + position: absolute; + inset-inline-start: 0; + top: 0; + } + + blockquote { + margin-top: 4px; + border-inline-start: 3px solid var(--rich-text-decorations-color); + padding-inline-start: 16px; + + &::before { + display: none; + } + } + + p:last-of-type { + margin-bottom: 0; + } + } + + & > ul, + & > ol { + margin-bottom: 22px; + + &:last-child { + margin-bottom: 0; + } + } + + b, + strong { + font-weight: 700; + } + + em, + i { + font-style: italic; + } + + ul, + ol { + padding-inline-start: 24px; + + li { + padding-inline-start: 8px; + + &::marker { + text-align: end; + } + } + + p { + margin: 0; + } + } + + ul { + list-style-type: '•'; + + li::marker { + text-align: start; + } + } + + ol { + list-style-type: decimal; + } +} diff --git a/app/javascript/styles_new/mastodon/rtl.scss b/app/javascript/styles_new/mastodon/rtl.scss new file mode 100644 index 00000000000..48935b75d74 --- /dev/null +++ b/app/javascript/styles_new/mastodon/rtl.scss @@ -0,0 +1,50 @@ +@use 'variables' as *; + +body.rtl { + direction: rtl; + + .reactions-bar { + direction: rtl; + } + + .announcements__mastodon, + .drawer__inner__mastodon > img { + transform: scaleX(-1); + } + + .compose-form .autosuggest-textarea__textarea { + padding-right: 10px; + padding-left: 10px + 22px; + } + + .columns-area { + direction: rtl; + } + + .account__avatar-wrapper { + float: right; + } + + .column-header__setting-arrows { + float: left; + } + + .admin-wrapper { + direction: rtl; + } + + .react-swipeable-view-container > * { + direction: rtl; + } + + .column-back-button__icon { + transform: scale(-1, 1); + } + + .dismissable-banner, + .warning-banner { + &__action { + float: left; + } + } +} diff --git a/app/javascript/styles_new/mastodon/tables.scss b/app/javascript/styles_new/mastodon/tables.scss new file mode 100644 index 00000000000..8e303aff685 --- /dev/null +++ b/app/javascript/styles_new/mastodon/tables.scss @@ -0,0 +1,375 @@ +@use 'variables' as *; + +.table { + width: 100%; + max-width: 100%; + border-spacing: 0; + border-collapse: collapse; + + th, + td { + padding: 8px; + line-height: 18px; + vertical-align: top; + border-bottom: 1px solid var(--color-border-primary); + text-align: start; + background: var(--color-bg-primary); + + &.critical { + font-weight: 700; + color: var(--color-text-warning); + } + } + + & > thead > tr > th { + vertical-align: bottom; + font-weight: 500; + } + + & > tbody > tr > th { + font-weight: 500; + } + + & > tbody > tr:nth-child(odd) > td, + & > tbody > tr:nth-child(odd) > th { + background: var(--color-bg-primary); + } + + & > tbody > tr:last-child > td, + & > tbody > tr:last-child > th { + border-bottom: 0; + } + + a { + color: var(--color-text-secondary); + text-decoration: none; + + &:hover { + color: var(--color-text-brand); + } + } + + strong { + font-weight: 500; + + @each $lang in $cjk-langs { + &:lang(#{$lang}) { + font-weight: 700; + } + } + } + + &.inline-table { + & > tbody > tr:nth-child(odd) { + & > td, + & > th { + background: transparent; + } + } + + & > tbody > tr:first-child { + & > td, + & > th { + border-top: 0; + } + } + } + + &.horizontal-table { + border-collapse: collapse; + border-style: hidden; + + & > tbody > tr > th, + & > tbody > tr > td { + padding: 11px 10px; + background: transparent; + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); + } + + & > tbody > tr > th { + color: var(--color-text-secondary); + font-weight: 600; + } + } + + &.batch-table { + & > thead > tr > th { + background: var(--color-bg-primary); + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); + + &:first-child { + border-radius: 4px 0 0; + border-inline-start: 1px solid var(--color-border-primary); + } + + &:last-child { + border-radius: 0 4px 0 0; + border-inline-end: 1px solid var(--color-border-primary); + } + } + } + + &--invites tbody td { + vertical-align: middle; + } +} + +.table-wrapper { + overflow: auto; + margin-bottom: 20px; +} + +samp { + font-family: $font-monospace, monospace; +} + +button.table-action-link { + background: transparent; + border: 0; + font: inherit; +} + +button.table-action-link, +a.table-action-link { + text-decoration: none; + display: inline-block; + margin-inline-end: 5px; + padding: 0 10px; + color: var(--color-text-secondary); + font-weight: 500; + white-space: nowrap; + + &:hover { + color: var(--color-text-brand); + } + + &:first-child { + padding-inline-start: 0; + } +} + +.batch-table { + &--no-toolbar { + .batch-table__toolbar { + position: static; + height: 4px; + border-bottom: none; + } + } + + &__toolbar, + &__row { + display: flex; + + &__select { + box-sizing: border-box; + padding: 8px 16px; + cursor: pointer; + min-height: 100%; + + input { + margin-top: 8px; + } + + &--aligned { + display: flex; + align-items: center; + + input { + margin-top: 0; + } + } + } + + &__actions, + &__content { + padding: 8px 0; + padding-inline-end: 16px; + flex: 1 1 auto; + } + } + + &__toolbar { + position: sticky; + top: 0; + z-index: 200; + border: 1px solid var(--color-border-primary); + background: var(--color-bg-primary); + border-radius: 4px 4px 0 0; + height: 47px; + align-items: center; + + &__actions { + text-align: end; + padding-inline-end: 16px - 5px; + + .table-action-link { + padding: 0; + } + } + } + + &__select-all { + background: var(--color-bg-primary); + height: 47px; + align-items: center; + justify-content: center; + border: 1px solid var(--color-border-primary); + border-top: 0; + color: var(--color-text-primary); + display: none; + + &.active { + display: flex; + } + + .selected, + .not-selected { + display: none; + + &.active { + display: block; + } + } + + strong { + font-weight: 700; + } + + span { + padding: 8px; + display: inline-block; + } + + button { + background: transparent; + border: 0; + font: inherit; + color: var(--color-text-brand); + border-radius: 4px; + font-weight: 700; + padding: 8px; + + &:hover, + &:focus, + &:active { + background: var(--color-bg-secondary); + } + } + } + + &__form { + padding: 16px; + border: 1px solid var(--color-border-primary); + border-top: 0; + background: var(--color-bg-primary); + + .fields-row { + padding-top: 0; + margin-bottom: 0; + } + } + + &__row { + border: 1px solid var(--color-border-primary); + border-top: 0; + background: var(--color-bg-primary); + + @media screen and (max-width: $no-gap-breakpoint) { + .optional &:first-child { + border-top: 1px solid var(--color-border-primary); + } + } + + &:last-child { + border-radius: 0 0 4px 4px; + } + + &__content { + padding-top: 12px; + padding-bottom: 16px; + overflow: hidden; + + &--unpadded { + padding: 0; + } + + &--padded { + padding: 12px 16px 16px; + } + + &--with-image { + display: flex; + align-items: center; + } + + &__image { + flex: 0 0 auto; + display: flex; + justify-content: center; + align-items: center; + margin-inline-end: 10px; + + .emojione { + width: 32px; + height: 32px; + } + } + + &__text { + flex: 1 1 auto; + } + + &__quote { + padding: 12px; + padding-top: 0; + } + + &__extra { + flex: 0 0 auto; + text-align: end; + color: var(--color-text-secondary); + font-weight: 500; + } + } + + .directory__tag { + margin: 0; + width: 100%; + + a { + background: transparent; + border-radius: 0; + } + } + } + + &.optional .batch-table__toolbar, + &.optional .batch-table__row__select { + @media screen and (max-width: $no-gap-breakpoint) { + display: none; + } + } + + // Reset the status card to not have borders, background or padding when + // inline in the table of statuses + .batch-table__row__content > .status__card { + border: none; + background: none; + padding: 0; + } + + @media screen and (width <= 870px) { + .accounts-table tbody td.optional { + display: none; + } + } +} + +.one-liner { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/app/javascript/styles_new/mastodon/widgets.scss b/app/javascript/styles_new/mastodon/widgets.scss new file mode 100644 index 00000000000..69c79cd1e6f --- /dev/null +++ b/app/javascript/styles_new/mastodon/widgets.scss @@ -0,0 +1,178 @@ +@use 'sass:color'; +@use 'variables' as *; + +.directory { + &__tag { + box-sizing: border-box; + margin-bottom: 10px; + + & > a, + & > div { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid var(--color-border-primary); + border-radius: 4px; + padding: 15px; + text-decoration: none; + color: inherit; + box-shadow: 0 0 15px var(--color-shadow-primary); + } + + & > a { + &:hover, + &:active, + &:focus { + background: var(--color-bg-primary); + } + } + + &.active > a { + background: var(--color-bg-brand-base); + cursor: default; + } + + &.disabled > div { + opacity: 0.5; + cursor: default; + } + + h4 { + flex: 1 1 auto; + font-size: 18px; + font-weight: 700; + color: var(--color-text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + small { + display: block; + font-weight: 400; + font-size: 15px; + margin-top: 8px; + color: var(--color-text-secondary); + } + } + + &.active h4 { + &, + small, + .trends__item__current { + color: var(--color-text-primary); + } + } + + .avatar-stack { + flex: 0 0 auto; + width: (36px + 4px) * 3; + } + + &.active .avatar-stack .account__avatar { + border-color: var(--color-text-brand); + } + + .trends__item__current { + padding-inline-end: 0; + } + } +} + +.accounts-table { + width: 100%; + + .account { + max-width: calc(56px + 30ch); + padding: 0; + border: 0; + } + + strong { + font-weight: 700; + } + + thead th { + text-align: center; + text-transform: uppercase; + color: var(--color-text-secondary); + font-weight: 700; + padding: 10px; + + &:first-child { + text-align: start; + } + } + + tbody td { + padding: 15px 0; + vertical-align: middle; + border-bottom: 1px solid var(--color-border-primary); + } + + tbody tr:last-child td { + border-bottom: 0; + } + + &__count { + width: 120px; + text-align: center; + font-size: 15px; + font-weight: 500; + color: var(--color-text-primary); + + small { + display: block; + color: var(--color-text-secondary); + font-weight: 400; + font-size: 14px; + } + } + + tbody td.accounts-table__extra { + width: 120px; + text-align: end; + color: var(--color-text-secondary); + padding-inline-end: 16px; + + a { + text-decoration: none; + color: inherit; + + &:focus, + &:hover, + &:active { + color: var(--color-text-brand); + } + } + } + + &__comment { + width: 50%; + vertical-align: initial !important; + } + + tbody td.accounts-table__interrelationships { + width: 21px; + padding-inline-end: 16px; + } + + .icon { + &.active { + color: var(--color-text-brand); + } + + &.passive { + color: var(--color-text-warning); + } + + &.active.passive { + color: var(--color-text-success); + } + } + + @media screen and (max-width: $no-gap-breakpoint) { + tbody td.optional { + display: none; + } + } +} diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index f158626dbbf..d94f8767618 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class ActivityPub::Activity::Update < ActivityPub::Activity + # Updates to unknown objects older than that are ignored + OBJECT_AGE_THRESHOLD = 1.day + def perform @account.schedule_refresh_if_stale! @@ -26,6 +29,9 @@ class ActivityPub::Activity::Update < ActivityPub::Activity @status = Status.find_by(uri: object_uri, account_id: @account.id) + # Ignore updates for old unknown objects, since those are updates we are not interested in + return if @status.nil? && object_too_old? + # We may be getting `Create` and `Update` out of order @status ||= ActivityPub::Activity::Create.new(@json, @account, **@options).perform @@ -33,4 +39,10 @@ class ActivityPub::Activity::Update < ActivityPub::Activity ActivityPub::ProcessStatusUpdateService.new.call(@status, @json, @object, request_id: @options[:request_id]) end + + def object_too_old? + @object['published'].present? && @object['published'].to_datetime < OBJECT_AGE_THRESHOLD.ago + rescue Date::Error + false + end end diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb index 963cc0d1c43..10f007ff370 100644 --- a/app/lib/text_formatter.rb +++ b/app/lib/text_formatter.rb @@ -31,7 +31,7 @@ class TextFormatter end def to_s - return ''.html_safe if text.blank? + return add_quote_fallback('').html_safe if text.blank? # rubocop:disable Rails/OutputSafety html = rewrite do |entity| if entity[:url] diff --git a/app/models/collection.rb b/app/models/collection.rb new file mode 100644 index 00000000000..320933ea606 --- /dev/null +++ b/app/models/collection.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: collections +# +# id :bigint(8) not null, primary key +# description :text not null +# discoverable :boolean not null +# local :boolean not null +# name :string not null +# original_number_of_items :integer +# sensitive :boolean not null +# uri :string +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) not null +# tag_id :bigint(8) +# +class Collection < ApplicationRecord + MAX_ITEMS = 25 + + belongs_to :account + belongs_to :tag, optional: true + + has_many :collection_items, dependent: :delete_all + + validates :name, presence: true + validates :description, presence: true + validates :uri, presence: true, if: :remote? + validates :original_number_of_items, + presence: true, + numericality: { greater_than_or_equal: 0 }, + if: :remote? + validate :tag_is_usable + validate :items_do_not_exceed_limit + + def remote? + !local? + end + + private + + def tag_is_usable + return if tag.blank? + + errors.add(:tag, :unusable) unless tag.usable? + end + + def items_do_not_exceed_limit + errors.add(:collection_items, :too_many, count: MAX_ITEMS) if collection_items.size > MAX_ITEMS + end +end diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb new file mode 100644 index 00000000000..0ea50e69142 --- /dev/null +++ b/app/models/collection_item.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: collection_items +# +# id :bigint(8) not null, primary key +# activity_uri :string +# approval_last_verified_at :datetime +# approval_uri :string +# object_uri :string +# position :integer default(1), not null +# state :integer default("pending"), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) +# collection_id :bigint(8) not null +# +class CollectionItem < ApplicationRecord + belongs_to :collection + belongs_to :account, optional: true + + enum :state, + { pending: 0, accepted: 1, rejected: 2, revoked: 3 }, + validate: true + + delegate :local?, :remote?, to: :collection + + validates :position, numericality: { only_integer: true, greater_than: 0 } + validates :activity_uri, presence: true, if: :local_item_with_remote_account? + validates :approval_uri, absence: true, unless: :local? + validates :account, presence: true, if: :accepted? + validates :object_uri, presence: true, if: -> { account.nil? } + + scope :ordered, -> { order(position: :asc) } + + def local_item_with_remote_account? + local? && account&.remote? + end +end diff --git a/app/models/concerns/account/associations.rb b/app/models/concerns/account/associations.rb index 62c55da5de1..e1684d25607 100644 --- a/app/models/concerns/account/associations.rb +++ b/app/models/concerns/account/associations.rb @@ -13,6 +13,8 @@ module Account::Associations has_many :account_warnings has_many :aliases, class_name: 'AccountAlias' has_many :bookmarks + has_many :collections + has_many :collection_items has_many :conversations, class_name: 'AccountConversation' has_many :custom_filters has_many :favourites diff --git a/app/models/ip_block.rb b/app/models/ip_block.rb index 4c95ac38dea..b3b678a6a16 100644 --- a/app/models/ip_block.rb +++ b/app/models/ip_block.rb @@ -31,9 +31,10 @@ class IpBlock < ApplicationRecord after_commit :reset_cache - def to_log_human_identifier + def to_cidr "#{ip}/#{ip.prefix}" end + alias to_log_human_identifier to_cidr class << self def blocked?(remote_ip) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 13ca0d7e3ab..2615eed4e39 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -4,29 +4,30 @@ # # Table name: media_attachments # -# id :bigint(8) not null, primary key -# status_id :bigint(8) -# file_file_name :string -# file_content_type :string -# file_file_size :integer -# file_updated_at :datetime -# remote_url :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# shortcode :string -# type :integer default("image"), not null -# file_meta :json -# account_id :bigint(8) -# description :text -# scheduled_status_id :bigint(8) -# blurhash :string -# processing :integer -# file_storage_schema_version :integer -# thumbnail_file_name :string -# thumbnail_content_type :string -# thumbnail_file_size :integer -# thumbnail_updated_at :datetime -# thumbnail_remote_url :string +# id :bigint(8) not null, primary key +# blurhash :string +# description :text +# file_content_type :string +# file_file_name :string +# file_file_size :integer +# file_meta :json +# file_storage_schema_version :integer +# file_updated_at :datetime +# processing :integer +# remote_url :string default(""), not null +# shortcode :string +# thumbnail_content_type :string +# thumbnail_file_name :string +# thumbnail_file_size :integer +# thumbnail_remote_url :string +# thumbnail_storage_schema_version :integer +# thumbnail_updated_at :datetime +# type :integer default("image"), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) +# scheduled_status_id :bigint(8) +# status_id :bigint(8) # class MediaAttachment < ApplicationRecord diff --git a/app/models/user.rb b/app/models/user.rb index ccd96bdc92a..be5dc8e517c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -131,11 +131,12 @@ class User < ApplicationRecord delegate :can?, to: :role - attr_reader :invite_code, :date_of_birth + attr_reader :invite_code attr_writer :current_account attribute :external, :boolean, default: false attribute :bypass_registration_checks, :boolean, default: false + attribute :date_of_birth, :date def self.those_who_can(*any_of_privileges) matching_role_ids = UserRole.that_can(*any_of_privileges).map(&:id) @@ -151,17 +152,6 @@ class User < ApplicationRecord Rails.env.local? end - def date_of_birth=(hash_or_string) - @date_of_birth = begin - if hash_or_string.is_a?(Hash) - day, month, year = hash_or_string.values_at(1, 2, 3) - "#{day}.#{month}.#{year}" - else - hash_or_string - end - end - end - def role if role_id.nil? UserRole.everyone diff --git a/app/serializers/rest/admin/ip_block_serializer.rb b/app/serializers/rest/admin/ip_block_serializer.rb index 6a38f8b566f..4b782da53a7 100644 --- a/app/serializers/rest/admin/ip_block_serializer.rb +++ b/app/serializers/rest/admin/ip_block_serializer.rb @@ -9,6 +9,6 @@ class REST::Admin::IpBlockSerializer < ActiveModel::Serializer end def ip - "#{object.ip}/#{object.ip.prefix}" + object.to_cidr end end diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index fa2c728e579..8d0986adcb9 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -61,10 +61,6 @@ class NotifyService < BaseService NotificationPermission.exists?(account: @recipient, from_account: @sender) end - def from_limited? - @sender.silenced? && not_following? - end - def message? @notification.type == :mention end diff --git a/app/views/admin/domain_blocks/confirm_suspension.html.haml b/app/views/admin/domain_blocks/confirm_suspension.html.haml index e82026d3a2b..5b56c1903f6 100644 --- a/app/views/admin/domain_blocks/confirm_suspension.html.haml +++ b/app/views/admin/domain_blocks/confirm_suspension.html.haml @@ -18,5 +18,5 @@ domain: @domain_block.domain .actions - = link_to t('.cancel'), admin_instances_path, class: 'button button-tertiary' + = link_to t('.cancel'), admin_instances_path, class: 'button button-secondary' = f.button :submit, t('.confirm'), class: 'button button--dangerous', name: :confirm diff --git a/app/views/admin/ip_blocks/_ip_block.html.haml b/app/views/admin/ip_blocks/_ip_block.html.haml index 3dc6f8f8e57..0a1b864c6e2 100644 --- a/app/views/admin/ip_blocks/_ip_block.html.haml +++ b/app/views/admin/ip_blocks/_ip_block.html.haml @@ -3,7 +3,7 @@ = f.check_box :ip_block_ids, { multiple: true, include_hidden: false }, ip_block.id .batch-table__row__content.pending-account .pending-account__header - %samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}") + %samp= link_to ip_block.to_cidr, admin_accounts_path(ip: ip_block.to_cidr) - if ip_block.comment.present? · = ip_block.comment diff --git a/app/views/admin/reports/actions/preview.html.haml b/app/views/admin/reports/actions/preview.html.haml index 5a90bf1b8f6..bfd7fd5a409 100644 --- a/app/views/admin/reports/actions/preview.html.haml +++ b/app/views/admin/reports/actions/preview.html.haml @@ -76,7 +76,7 @@ %hr.spacer/ .actions - = link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-tertiary' + = link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-secondary' = form.button t('admin.reports.confirm'), name: :confirm, class: 'button', diff --git a/app/views/admin/roles/_role.html.haml b/app/views/admin/roles/_role.html.haml index ddaca5d8a9f..df813bbaf43 100644 --- a/app/views/admin/roles/_role.html.haml +++ b/app/views/admin/roles/_role.html.haml @@ -1,7 +1,7 @@ .announcements-list__item - if can?(:update, role) = link_to edit_admin_role_path(role), class: 'announcements-list__item__title' do - %span.user-role + %span = material_symbol 'group' - if role.everyone? @@ -10,13 +10,12 @@ = role.name - else %span.announcements-list__item__title - %span.user-role - = material_symbol 'group' + = material_symbol 'group' - - if role.everyone? - = t('admin.roles.everyone') - - else - = role.name + - if role.everyone? + = t('admin.roles.everyone') + - else + = role.name .announcements-list__item__action-bar .announcements-list__item__meta diff --git a/app/views/auth/registrations/rules.html.haml b/app/views/auth/registrations/rules.html.haml index 59e7c9072fb..a5a96c57378 100644 --- a/app/views/auth/registrations/rules.html.haml +++ b/app/views/auth/registrations/rules.html.haml @@ -27,4 +27,4 @@ .stacked-actions - accept_path = @invite_code.present? ? public_invite_url(invite_code: @invite_code, accept: @accept_token) : new_user_registration_path(accept: @accept_token) = link_to t('auth.rules.accept'), accept_path, class: 'button' - = link_to t('auth.rules.back'), root_path, class: 'button button-tertiary' + = link_to t('auth.rules.back'), root_path, class: 'button button-secondary' diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml index d25dd22d375..26a03472fe2 100644 --- a/app/views/layouts/modal.html.haml +++ b/app/views/layouts/modal.html.haml @@ -1,7 +1,7 @@ - content_for :header_tags do = vite_typescript_tag 'public.tsx', crossorigin: 'anonymous' -- content_for :body_classes, 'modal-layout compose-standalone' +- content_for :body_classes, 'modal-layout with-zig-zag-decoration compose-standalone' - content_for :content do - if user_signed_in? && !@hide_header diff --git a/app/views/settings/imports/show.html.haml b/app/views/settings/imports/show.html.haml index dd18ac2168c..40b06e6efb1 100644 --- a/app/views/settings/imports/show.html.haml +++ b/app/views/settings/imports/show.html.haml @@ -11,5 +11,5 @@ .simple_form .actions - = link_to t('generic.cancel'), settings_import_path(@bulk_import), method: :delete, class: 'button button-tertiary' + = link_to t('generic.cancel'), settings_import_path(@bulk_import), method: :delete, class: 'button button-secondary' = link_to t('generic.confirm'), confirm_settings_import_path(@bulk_import), method: :post, class: 'button' diff --git a/app/views/statuses/_og_image.html.haml b/app/views/statuses/_og_image.html.haml index 1ae97adff67..1f7f57f1562 100644 --- a/app/views/statuses/_og_image.html.haml +++ b/app/views/statuses/_og_image.html.haml @@ -1,6 +1,6 @@ -- if activity.is_a?(Status) && (activity.non_sensitive_with_media? || (activity.with_media? && Setting.preview_sensitive_media)) +- if status.non_sensitive_with_media? || (status.with_media? && Setting.preview_sensitive_media) - player_card = false - - activity.ordered_media_attachments.each do |media| + - status.ordered_media_attachments.each do |media| - if media.image? = opengraph 'og:image', full_asset_url(media.file.url(:original)) = opengraph 'og:image:type', media.file_content_type diff --git a/app/views/statuses/show.html.haml b/app/views/statuses/show.html.haml index ca2628bbcda..7478562ea2e 100644 --- a/app/views/statuses/show.html.haml +++ b/app/views/statuses/show.html.haml @@ -20,6 +20,6 @@ = opengraph 'profile:username', acct(@account)[1..] = render 'og_description', activity: @status - = render 'og_image', activity: @status, account: @account + = render 'og_image', status: @status, account: @account = render 'shared/web_app' diff --git a/config/locales/activerecord.be.yml b/config/locales/activerecord.be.yml index 3ddb3e328ed..97f95a0e730 100644 --- a/config/locales/activerecord.be.yml +++ b/config/locales/activerecord.be.yml @@ -32,6 +32,12 @@ be: attributes: url: invalid: нядзейсны URL + collection: + attributes: + collection_items: + too_many: занадта шмат, дазволена не больш за %{count} + tag: + unusable: нельга выкарыстаць doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.cs.yml b/config/locales/activerecord.cs.yml index 38708713d2c..44390fb32c4 100644 --- a/config/locales/activerecord.cs.yml +++ b/config/locales/activerecord.cs.yml @@ -32,6 +32,12 @@ cs: attributes: url: invalid: není platná URL + collection: + attributes: + collection_items: + too_many: je příliš mnoho, ne více než %{count} je povoleno + tag: + unusable: nesmí být použito doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.cy.yml b/config/locales/activerecord.cy.yml index c201016be61..cfb20eb9d20 100644 --- a/config/locales/activerecord.cy.yml +++ b/config/locales/activerecord.cy.yml @@ -32,6 +32,12 @@ cy: attributes: url: invalid: nid yw'n URL dilys + collection: + attributes: + collection_items: + too_many: yn ormod, does dim caniatâd i fwy na %{count} + tag: + unusable: does dim caniatâd i'w ddefnyddio doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.da.yml b/config/locales/activerecord.da.yml index 8c405fba13b..ff4e9d97eac 100644 --- a/config/locales/activerecord.da.yml +++ b/config/locales/activerecord.da.yml @@ -32,6 +32,12 @@ da: attributes: url: invalid: er ikke en gyldig URL + collection: + attributes: + collection_items: + too_many: er for mange, ikke mere end %{count} er tilladt + tag: + unusable: må ikke anvendes doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.de.yml b/config/locales/activerecord.de.yml index 4ae7aec5dd6..b31317ad36f 100644 --- a/config/locales/activerecord.de.yml +++ b/config/locales/activerecord.de.yml @@ -32,6 +32,12 @@ de: attributes: url: invalid: ist keine gültige URL + collection: + attributes: + collection_items: + too_many: zu viele, mehr als %{count} sind nicht erlaubt + tag: + unusable: darf nicht verwendet werden doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.el.yml b/config/locales/activerecord.el.yml index 4bb344bc6db..d6e6bf570d2 100644 --- a/config/locales/activerecord.el.yml +++ b/config/locales/activerecord.el.yml @@ -32,6 +32,12 @@ el: attributes: url: invalid: δεν είναι έγκυρο URL + collection: + attributes: + collection_items: + too_many: είναι πολλά, δεν επιτρέπονται περισσότερα από %{count} + tag: + unusable: δεν μπορεί να χρησιμοποιηθεί doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 6940d589cab..fb5ccce89e2 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -32,6 +32,12 @@ en: attributes: url: invalid: is not a valid URL + collection: + attributes: + collection_items: + too_many: are too many, no more than %{count} are allowed + tag: + unusable: may not be used doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.eo.yml b/config/locales/activerecord.eo.yml index 96b7ee89fda..c4bee952fbf 100644 --- a/config/locales/activerecord.eo.yml +++ b/config/locales/activerecord.eo.yml @@ -32,6 +32,12 @@ eo: attributes: url: invalid: ne estas valida URL + collection: + attributes: + collection_items: + too_many: estas tro multaj, ne pli ol %{count} estas permesataj + tag: + unusable: ne povus esti uzata doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es-AR.yml b/config/locales/activerecord.es-AR.yml index 62a409a3535..927f9e4742a 100644 --- a/config/locales/activerecord.es-AR.yml +++ b/config/locales/activerecord.es-AR.yml @@ -32,6 +32,12 @@ es-AR: attributes: url: invalid: no es una dirección web válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: no podrán ser usadas doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es-MX.yml b/config/locales/activerecord.es-MX.yml index 02384f1c715..94419778345 100644 --- a/config/locales/activerecord.es-MX.yml +++ b/config/locales/activerecord.es-MX.yml @@ -32,6 +32,12 @@ es-MX: attributes: url: invalid: no es una dirección URL válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: podría no utilizarse doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 94f29365e91..cb425fecdde 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -32,6 +32,12 @@ es: attributes: url: invalid: no es una URL válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: podría no utilizarse doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.et.yml b/config/locales/activerecord.et.yml index 4e49ae96837..a1d4a0c38b8 100644 --- a/config/locales/activerecord.et.yml +++ b/config/locales/activerecord.et.yml @@ -32,6 +32,12 @@ et: attributes: url: invalid: pole sobiv URL + collection: + attributes: + collection_items: + too_many: on liiga palju, pole lubatud enam, kui %{count} + tag: + unusable: pole võimalik kasutada doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fo.yml b/config/locales/activerecord.fo.yml index ce84a1fffd4..3f069cd072f 100644 --- a/config/locales/activerecord.fo.yml +++ b/config/locales/activerecord.fo.yml @@ -32,6 +32,12 @@ fo: attributes: url: invalid: er ikki eitt rætt leinki + collection: + attributes: + collection_items: + too_many: er ov nógvir, einans %{count} eru loyvdir + tag: + unusable: kann ikki brúkast doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.ga.yml b/config/locales/activerecord.ga.yml index 853c705663f..bc8bbf2fb3b 100644 --- a/config/locales/activerecord.ga.yml +++ b/config/locales/activerecord.ga.yml @@ -32,6 +32,12 @@ ga: attributes: url: invalid: nach URL bailí é + collection: + attributes: + collection_items: + too_many: an iomarca, níl cead níos mó ná %{count} a fháil + tag: + unusable: ní fhéadfar a úsáid doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.gl.yml b/config/locales/activerecord.gl.yml index f4e67255655..b836c3e9773 100644 --- a/config/locales/activerecord.gl.yml +++ b/config/locales/activerecord.gl.yml @@ -32,6 +32,12 @@ gl: attributes: url: invalid: non é un URL válido + collection: + attributes: + collection_items: + too_many: son demasiados, non se permiten máis de %{count} + tag: + unusable: non se debería usar doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.he.yml b/config/locales/activerecord.he.yml index 7dff17493b3..27bfe360d99 100644 --- a/config/locales/activerecord.he.yml +++ b/config/locales/activerecord.he.yml @@ -32,6 +32,12 @@ he: attributes: url: invalid: כתובת לא חוקית + collection: + attributes: + collection_items: + too_many: יש יותר מדי, מותרות %{count} לכל היותר + tag: + unusable: לא שמישות doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml index cf2f50a9f9b..5db36592c81 100644 --- a/config/locales/activerecord.hu.yml +++ b/config/locales/activerecord.hu.yml @@ -32,6 +32,12 @@ hu: attributes: url: invalid: nem érvényes URL + collection: + attributes: + collection_items: + too_many: túl sok, legfeljebb %{count} engedélyezett + tag: + unusable: nem használható doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.is.yml b/config/locales/activerecord.is.yml index cff90a3476d..7fe6835a1d7 100644 --- a/config/locales/activerecord.is.yml +++ b/config/locales/activerecord.is.yml @@ -32,6 +32,12 @@ is: attributes: url: invalid: er ekki gild vefslóð + collection: + attributes: + collection_items: + too_many: eru of mörg, ekki fleiri en %{count} eru leyfileg + tag: + unusable: gæti verið ekki notað doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.nan.yml b/config/locales/activerecord.nan.yml index b1e9dc67bfa..60f5d107176 100644 --- a/config/locales/activerecord.nan.yml +++ b/config/locales/activerecord.nan.yml @@ -14,3 +14,9 @@ nan: username: 用者ê名 user/invite_request: text: 原因 + errors: + models: + terms_of_service: + attributes: + effective_date: + too_soon: 傷緊ah,著khah uànn佇 %{date} diff --git a/config/locales/activerecord.pt-BR.yml b/config/locales/activerecord.pt-BR.yml index 8b77ed7e834..7d4223725a7 100644 --- a/config/locales/activerecord.pt-BR.yml +++ b/config/locales/activerecord.pt-BR.yml @@ -32,6 +32,12 @@ pt-BR: attributes: url: invalid: não é uma URL válida + collection: + attributes: + collection_items: + too_many: é demais, não é permitido mais que %{count} + tag: + unusable: não pode ser usado doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.pt-PT.yml b/config/locales/activerecord.pt-PT.yml index 397ed492e52..76baf556acb 100644 --- a/config/locales/activerecord.pt-PT.yml +++ b/config/locales/activerecord.pt-PT.yml @@ -32,6 +32,12 @@ pt-PT: attributes: url: invalid: não é um URL válido + collection: + attributes: + collection_items: + too_many: são demasiadas, não são permitidas mais do que %{count} + tag: + unusable: não pode ser utilizada doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.ru.yml b/config/locales/activerecord.ru.yml index 84616eb7673..5b0f46e382a 100644 --- a/config/locales/activerecord.ru.yml +++ b/config/locales/activerecord.ru.yml @@ -32,6 +32,12 @@ ru: attributes: url: invalid: не является действительным URL + collection: + attributes: + collection_items: + too_many: слишком много, не разрешено более чем %{count} + tag: + unusable: может не быть использованным doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.sq.yml b/config/locales/activerecord.sq.yml index 2683dd014bf..428283a0ef0 100644 --- a/config/locales/activerecord.sq.yml +++ b/config/locales/activerecord.sq.yml @@ -32,6 +32,12 @@ sq: attributes: url: invalid: s’është URL e vlefshme + collection: + attributes: + collection_items: + too_many: janë shumë, nuk lejohen më tepër se %{count} + tag: + unusable: mund të mos përdoret dot doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.sv.yml b/config/locales/activerecord.sv.yml index 74b939fda48..0b96c16a5c3 100644 --- a/config/locales/activerecord.sv.yml +++ b/config/locales/activerecord.sv.yml @@ -32,6 +32,12 @@ sv: attributes: url: invalid: är inte en giltig URL + collection: + attributes: + collection_items: + too_many: är för många, fler än %{count} är inte tillåtet + tag: + unusable: får inte användas doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.vi.yml b/config/locales/activerecord.vi.yml index fe810d94e50..4a52fbce24a 100644 --- a/config/locales/activerecord.vi.yml +++ b/config/locales/activerecord.vi.yml @@ -32,6 +32,12 @@ vi: attributes: url: invalid: không phải là một URL hợp lệ + collection: + attributes: + collection_items: + too_many: quá nhiều, không được hơn %{count} + tag: + unusable: không được phép dùng doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.zh-TW.yml b/config/locales/activerecord.zh-TW.yml index f8f630ba3cb..3b61e5ab883 100644 --- a/config/locales/activerecord.zh-TW.yml +++ b/config/locales/activerecord.zh-TW.yml @@ -32,6 +32,12 @@ zh-TW: attributes: url: invalid: 不是有效的 URL + collection: + attributes: + collection_items: + too_many: 數量過多,無法多過 %{count} 個 + tag: + unusable: 無法使用 doorkeeper/application: attributes: website: diff --git a/config/locales/da.yml b/config/locales/da.yml index b2a078b4846..ab6abedd352 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -477,7 +477,7 @@ da: no_file: Ingen fil valgt export_domain_blocks: import: - description_html: En liste over domæneblokeringer er ved at blive importeret. Gennemgå listen meget nøje, især hvis man ikke selv har oprettet den. + description_html: Du er ved at importere en liste over domæneblokeringer. Gennemgå denne liste meget omhyggeligt, især hvis du ikke selv har udarbejdet den. existing_relationships_warning: Eksisterende følge-relationer private_comment_description_html: 'For at man lettere kan holde styr på, hvorfra importerede blokeringer kommer, oprettes disse med flg. private kommentar: %{comment}' private_comment_template: Importeret fra %{source} d. %{date} @@ -514,7 +514,7 @@ da: select_capabilities: Vælg kapaciteter sign_in: Log ind status: Status - title: Fediverse Auxiliary Service Providers + title: Udbydere af Fediverse-hjælpetjenester title: FASP follow_recommendations: description_html: "Følg-anbefalinger hjælpe nye brugere til hurtigt at finde interessant indhold. Når en bruger ikke har interageret nok med andre til at generere personlige følg-anbefalinger, anbefales disse konti i stedet. De revurderes dagligt baseret på en blanding af konti med de flest nylige engagementer og fleste lokale følger-antal for et givet sprog." @@ -1186,7 +1186,7 @@ da: new_trending_statuses: title: Indlæg, der trender new_trending_tags: - title: Hashtags, der trender + title: Populære hashtags subject: Nye tendenser klar til gennemgang på %{instance} aliases: add_new: Opret alias @@ -1901,7 +1901,7 @@ da: user_domain_block: "%{target_name} blev blokeret" lost_followers: Tabte følgere lost_follows: Mistet følger - preamble: Der kan mistes fulgte objekter og følgere, når et domæne blokeres eller moderatorerne beslutter at suspendere en ekstern server. Når det sker, kan der downloades lister over afbrudte relationer til inspektion og mulig import på anden server. + preamble: Du kan miste fulgte og følgere, når du blokerer et domæne, eller når dine moderatorer beslutter at suspendere en fjernserver. Når det sker, kan du downloade lister over afbrudte forhold til inspektion og eventuelt import til en anden server. purged: Oplysninger om denne server er blevet renset af serveradministratoreren. type: Begivenhed statuses: @@ -1957,12 +1957,12 @@ da: enabled: Slet automatisk gamle indlæg enabled_hint: Sletter automatisk dine indlæg, når disse når en bestemt alder, medmindre de matcher en af undtagelserne nedenfor exceptions: Undtagelser - explanation: Sletning af indlæg er en ressourcekrævende operation, hvorfor dette sker gradvist over tid, når serveren ellers ikke er optaget. Indlæg kan derfor blive slettet efter, at de reelt har passeret aldersgrænsen. + explanation: Da sletning af indlæg er en kostbar operation, foregår dette langsomt over tid, når serveren ikke er optaget af andre opgaver. Af denne grund kan dine indlæg blive slettet et stykke tid efter, at de har nået alderstærsklen. ignore_favs: Ignorér favoritter ignore_reblogs: Ignorér fremhævelser interaction_exceptions: Undtagelser baseret på interaktioner interaction_exceptions_explanation: Bemærk, at det ikke garanteres, at indlæg slettes, hvis de når under favorit- eller fremhævelses-tærsklerne efter én gang at været nået over dem. - keep_direct: Behold direkte besked + keep_direct: Behold direkte beskeder keep_direct_hint: Sletter ingen af dine direkte beskeder keep_media: Behold indlæg med medievedhæftninger keep_media_hint: Sletter ingen af dine indlæg med medievedhæftninger @@ -2004,7 +2004,7 @@ da: title: Tjenestevilkårene for %{domain} ændres themes: contrast: Mastodon (høj kontrast) - default: Mastodont (mørkt) + default: Mastodon (mørkt) mastodon-light: Mastodon (lyst) system: Automatisk (benyt systemtema) time: @@ -2024,8 +2024,8 @@ da: edit: Redigér enabled: Tofaktorgodkendelse aktiveret enabled_success: Tofaktorgodkendelse aktiveret - generate_recovery_codes: Generere gendannelseskoder - lost_recovery_codes: Gendannelseskoder muliggør adgang til din konto, hvis du mister din mobil. Ved mistet gendannelseskoder, kan disse regenerere her. Dine gamle gendannelseskoder ugyldiggøres. + generate_recovery_codes: Generer gendannelseskoder + lost_recovery_codes: Gendannelseskoder giver dig mulighed for at få adgang til din konto igen, hvis du mister din telefon. Hvis du har mistet dine gendannelseskoder, kan du generere dem igen her. Dine gamle gendannelseskoder vil blive ugyldige. methods: Tofaktormetoder otp: Godkendelses-app recovery_codes: Sikkerhedskopieret gendannelseskoder diff --git a/config/locales/de.yml b/config/locales/de.yml index d5c35ccfa21..ccd44a0c8d9 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -364,7 +364,7 @@ de: overwrite: Überschreiben shortcode: Shortcode shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche - title: Eigene Emojis + title: Emojis uncategorized: Unkategorisiert unlist: Nicht anzeigen unlisted: Nicht sichtbar @@ -1089,16 +1089,16 @@ de: tag_servers_measure: Server tag_uses_measure: insgesamt description_html: Diese Hashtags werden derzeit in vielen Beiträgen verwendet, die dein Server sieht. Dies kann deinen Nutzer*innen helfen, herauszufinden, worüber die Leute im Moment am meisten schreiben. Hashtags werden erst dann öffentlich angezeigt, wenn du sie genehmigst. - listable: Kann vorgeschlagen werden + listable: Darf empfohlen werden no_tag_selected: Keine Hashtags wurden geändert, da keine ausgewählt wurden - not_listable: Wird nicht vorgeschlagen - not_trendable: Wird in den Trends nicht angezeigt - not_usable: Kann nicht verwendet werden + not_listable: Darf nicht vorgeschlagen werden + not_trendable: In Trends nicht erlaubt + not_usable: In Beiträgen nicht erlaubt peaked_on_and_decaying: In den Trends am %{date}, jetzt absteigend title: Angesagte Hashtags - trendable: Darf in den Trends erscheinen + trendable: In Trends erlaubt trending_rank: Platz %{rank} - usable: Darf verwendet werden + usable: In Beiträgen erlaubt usage_comparison: Heute %{today}-mal und gestern %{yesterday}-mal verwendet used_by_over_week: one: In den vergangenen 7 Tagen von einem Profil verwendet @@ -1122,7 +1122,7 @@ de: title: Neue Regel für Profilnamen erstellen no_username_block_selected: Keine Regeln für Profilnamen wurden geändert, weil keine ausgewählt wurde(n) not_permitted: Nicht gestattet - title: Regeln für Profilnamen + title: Profilnamen updated_msg: Regel für Profilnamen erfolgreich aktualisiert warning_presets: add_new: Neu hinzufügen @@ -1279,13 +1279,13 @@ de: security: Sicherheit set_new_password: Neues Passwort einrichten setup: - email_below_hint_html: Überprüfe deinen Spam-Ordner oder lass dir den Bestätigungslink erneut zusenden. Falls die angegebene E-Mail-Adresse falsch ist, kannst du sie auch korrigieren. - email_settings_hint_html: Klicke auf den Link, den wir an %{email} gesendet haben, um mit Mastodon loszulegen. Wir warten hier solange auf dich. + email_below_hint_html: Überprüfe deinen Spam-/Junk-Ordner oder lass dir den Bestätigungslink erneut zusenden. Falls du die vorherige E-Mail-Adresse falsch eingegeben hast, kannst du sie hier korrigieren. + email_settings_hint_html: Klicke auf den Link, den wir dir an %{email} geschickt haben, um mit Mastodon zu starten. Wir warten hier solange auf dich. link_not_received: Keinen Bestätigungslink erhalten? new_confirmation_instructions_sent: In wenigen Minuten wirst du eine neue E-Mail mit dem Bestätigungslink erhalten! title: Überprüfe dein E-Mail-Postfach sign_in: - preamble_html: Melde dich mit deinen Zugangsdaten für %{domain} an. Falls dein Konto auf einem anderen Server erstellt wurde, ist eine Anmeldung hier nicht möglich. + preamble_html: Melde dich mit deinen Zugangsdaten für %{domain} an. Falls dein Konto auf einem anderen Mastodon-Server erstellt wurde, ist eine Anmeldung hier nicht möglich. title: Bei %{domain} anmelden sign_up: manual_review: Registrierungen für den Server %{domain} werden manuell durch unsere Moderator*innen überprüft. Um uns dabei zu unterstützen, schreibe etwas über dich und sage uns, weshalb du ein Konto auf %{domain} anlegen möchtest. @@ -1738,8 +1738,8 @@ de: code_hint: Gib den Code ein, den deine 2FA- bzw. TOTP-App generiert hat, um den Vorgang zu bestätigen description_html: Wenn du die Zwei-Faktor-Authentisierung (2FA) mit einer Authentifizierungs-App deines Smartphones aktivierst, benötigst du neben dem regulären Passwort zusätzlich auch den zeitbasierten Code der 2FA-App, um dich anmelden zu können. enable: Aktivieren - instructions_html: "Scanne diesen QR-Code mit einer beliebigen Authentisierungs-App (TOTP). Diese App generiert dann zeitbasierte Codes, die du beim Anmelden zusätzlich zum regulären Passwort eingeben musst." - manual_instructions: Wenn du den QR-Code nicht einscannen kannst, sondern die Zahlenfolge manuell eingeben musst, ist hier der geheime Token für deine 2FA-App. + instructions_html: "Scanne diesen QR-Code mit einer beliebigen Authentisierungs-App (TOTP) ein. Die App generiert dann zeitbasierte Codes, die du beim Anmelden zusätzlich zum regulären Passwort eingeben musst." + manual_instructions: 'Wenn du den QR-Code nicht einscannen kannst, sondern die Zahlenfolge manuell eingeben musst, ist hier der geheime Code für deine 2FA-App:' setup: Einrichten wrong_code: Der eingegebene Code ist ungültig! Laufen Serverzeit und Gerätezeit synchron? pagination: @@ -1941,7 +1941,7 @@ de: pending_approval: Veröffentlichung ausstehend revoked: Beitrag durch Autor*in entfernt quote_policies: - followers: Nur Follower + followers: Nur Follower und ich nobody: Nur ich public: Alle quote_post_author: Zitierte %{acct} @@ -2116,7 +2116,7 @@ de: edit_profile_action: Personalisieren edit_profile_step: Mit einem vollständigen Profil interagieren andere eher mit dir. edit_profile_title: Personalisiere dein Profil - explanation: Hier sind ein paar Tipps, um loszulegen + explanation: Hier sind ein paar Tipps für den Start feature_action: Mehr erfahren feature_audience: Mastodon bietet dir eine einzigartige Möglichkeit, deine Reichweite ohne Mittelsperson zu verwalten. Auf deiner eigenen Infrastruktur bereitgestellt, ermöglicht Mastodon es dir, jedem anderen Mastodon-Server zu folgen und von jedem anderen Server aus gefolgt zu werden. Ebenso steht Mastodon unter deiner Kontrolle. feature_audience_title: Baue deine Reichweite mit Vertrauen auf @@ -2150,7 +2150,7 @@ de: follow_limit_reached: Du kannst nicht mehr als %{limit} Profilen folgen go_to_sso_account_settings: Kontoeinstellungen des Identitätsanbieters aufrufen invalid_otp_token: Ungültiger Code der Zwei-Faktor-Authentisierung - otp_lost_help_html: Wenn du beides nicht mehr weißt, melde dich bitte bei uns unter der E-Mail-Adresse %{email} + otp_lost_help_html: Wenn du sowohl die E-Mail-Adresse als auch das Passwort nicht mehr weißt, melde dich bitte bei uns unter %{email} rate_limited: Zu viele Authentisierungsversuche. Bitte versuche es später noch einmal. seamless_external_login: Du bist über einen externen Dienst angemeldet, daher sind Passwort- und E-Mail-Einstellungen nicht verfügbar. signed_in_as: 'Angemeldet als:' diff --git a/config/locales/devise.es-MX.yml b/config/locales/devise.es-MX.yml index f37b1ab5f9e..de2a4a8d3b9 100644 --- a/config/locales/devise.es-MX.yml +++ b/config/locales/devise.es-MX.yml @@ -7,7 +7,7 @@ es-MX: send_paranoid_instructions: Si su dirección de correo electrónico existe en nuestra base de datos, recibirá un correo electrónico con instrucciones sobre cómo confirmar su dirección de correo en pocos minutos. failure: already_authenticated: Usted ya está registrado. - closed_registrations: Su intento de registro ha sido bloqueado debido a una política de red. Si cree que esto es un error, póngase en contacto con %{email}. + closed_registrations: Su intento de registro ha sido bloqueado debido a una política de red. Si cree que se trata de un error, póngase en contacto con %{email}. inactive: Su cuenta no ha sido activada aún. invalid: "%{authentication_keys} o contraseña inválida." last_attempt: Tiene un intento más antes de que tu cuenta sea bloqueada. diff --git a/config/locales/devise.nan.yml b/config/locales/devise.nan.yml index 3a8933e37ce..885e509fdea 100644 --- a/config/locales/devise.nan.yml +++ b/config/locales/devise.nan.yml @@ -7,6 +7,8 @@ nan: pending: Lí ê口座iáu teh審查。 timeout: Lí ê作業階段kàu期ah。請koh登入,繼續完成。 mailer: + confirmation_instructions: + extra_html: Mā請斟酌讀服侍器規定kap服務規定。 two_factor_disabled: title: 雙因素認證關掉ah two_factor_enabled: diff --git a/config/locales/devise.ru.yml b/config/locales/devise.ru.yml index cc95f730615..60d73189813 100644 --- a/config/locales/devise.ru.yml +++ b/config/locales/devise.ru.yml @@ -7,6 +7,7 @@ ru: send_paranoid_instructions: В течение нескольких минут вы получите письмо с инструкциями по его подтверждению, при условии что на ваш адрес электронной почты зарегистрирована учётная запись. Если письмо не приходит, проверьте папку «Спам». failure: already_authenticated: Вы уже авторизованы. + closed_registrations: Ваша попытка регистрации была заблокирована в связи с сетевой политикой. Если вы считаете, что произошла ошибка, свяжитесь с %{email}. inactive: Ваша учётная запись ещё не активирована. invalid: "%{authentication_keys} или пароль введён неверно." last_attempt: У вас осталась последняя попытка ввода пароля до блокировки учётной записи. diff --git a/config/locales/devise.sl.yml b/config/locales/devise.sl.yml index 8e708f4ebf5..cb369c19106 100644 --- a/config/locales/devise.sl.yml +++ b/config/locales/devise.sl.yml @@ -7,6 +7,7 @@ sl: send_paranoid_instructions: Če vaš e-poštni naslov obstaja v naši zbirki podatkov, boste v nekaj minutah prejeli e-poštno sporočilo z navodili za potrditev vašega e-poštnega naslova. Če niste prejeli e-poštnega sporočila, preverite mapo neželena pošta. failure: already_authenticated: Ste že prijavljeni. + closed_registrations: Vaš poskus registracije je blokiran zaradi omrežnih pravil. Če ste mnenja, da gre za napako, stopite v stik s/z %{email}. inactive: Vaš račun še ni aktiviran. invalid: Neveljavno %{authentication_keys} ali geslo. last_attempt: Pred zaklepom računa imate še en poskus. diff --git a/config/locales/el.yml b/config/locales/el.yml index 397c03b402e..47a558ada79 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1044,7 +1044,7 @@ el: confirm_allow_provider: Σίγουρα θες να επιτρέψεις τους επιλεγμένους παρόχους; confirm_disallow: Σίγουρα θες να απορρίψεις τους επιλεγμένους συνδέσμους; confirm_disallow_provider: Σίγουρα θες να απορρίψεις τους επιλεγμένους παρόχους; - description_html: Αυτοί οι σύνδεσμοι μοιράζονται αρκετά από λογαριασμούς των οποίων τις δημοσιεύσεις, βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συμβαίνει στον κόσμο. Οι σύνδεσμοι δεν εμφανίζονται δημόσια μέχρι να εγκρίνετε τον εκδότη. Μπορείς επίσης να επιτρέψεις ή να απορρίψεις μεμονωμένους συνδέσμους. + description_html: Αυτοί οι σύνδεσμοι κοινοποιούνται αρκετά από λογαριασμούς των οποίων τις αναρτήσεις, βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συμβαίνει στον κόσμο. Οι σύνδεσμοι δεν εμφανίζονται δημόσια μέχρι να εγκρίνετε τον εκδότη. Μπορείτε επίσης να επιτρέψετε ή να απορρίψετε μεμονωμένους συνδέσμους. disallow: Να μην επιτρέπεται ο σύνδεσμος disallow_provider: Να μην επιτρέπεται ο εκδότης no_link_selected: Κανένας σύνδεσμος δεν άλλαξε αφού κανείς δεν επιλέχθηκε @@ -1060,7 +1060,7 @@ el: pending_review: Εκκρεμεί αξιολόγηση preview_card_providers: allowed: Σύνδεσμοι από αυτόν τον εκδότη μπορούν να γίνουν δημοφιλείς - description_html: Αυτοί είναι τομείς από τους οποίους οι σύνδεσμοι συχνά μοιράζονται στον διακομιστή σας. Σύνδεσμοι δεν γίνουν δημοφιλείς δημοσίως εκτός και αν ο τομέας του συνδέσμου εγκριθεί. Η έγκρισή σας (ή απόρριψη) περιλαμβάνει και τους υποτομείς. + description_html: Αυτοί είναι τομείς από τους οποίους οι σύνδεσμοι συχνά κοινοποιούνται στον διακομιστή σας. Οι σύνδεσμοι δεν θα γίνουν δημοφιλείς δημοσίως εκτός και αν ο τομέας του συνδέσμου εγκριθεί. Η έγκρισή σας (ή απόρριψη) περιλαμβάνει και τους υποτομείς. rejected: Σύνδεσμοι από αυτόν τον εκδότη δε θα γίνουν δημοφιλείς title: Εκδότες rejected: Απορρίφθηκε @@ -1077,8 +1077,8 @@ el: no_status_selected: Καμία δημοφιλής ανάρτηση δεν άλλαξε αφού καμία δεν επιλέχθηκε not_discoverable: Ο συντάκτης δεν έχει επιλέξει να είναι ανακαλύψιμος shared_by: - one: Μοιράστηκε ή προστέθηκε στα αγαπημένα μία φορά - other: Μοιράστηκε και προστέθηκε στα αγαπημένα %{friendly_count} φορές + one: Κοινοποιήθηκε ή προστέθηκε στα αγαπημένα μία φορά + other: Κοινοποιήθηκε και προστέθηκε στα αγαπημένα %{friendly_count} φορές title: Αναρτήσεις σε τάση tags: current_score: Τρέχουσα βαθμολογία %{score} @@ -1305,7 +1305,7 @@ el: user_privacy_agreement_html: Έχω διαβάσει και συμφωνώ με την πολιτική απορρήτου author_attribution: example_title: Δείγμα κειμένου - hint_html: Γράφεις ειδήσεις ή blog άρθρα εκτός του Mastodon; Έλεγξε πώς μπορείς να πάρεις τα εύσημα όταν μοιράζονται στο Mastodon. + hint_html: Γράφεις ειδήσεις ή άρθρα blog εκτός του Mastodon; Έλεγξε πώς μπορείς να πάρεις τα εύσημα όταν κοινοποιούνται στο Mastodon. instructions: 'Βεβαιώσου ότι ο κώδικας αυτός είναι στο HTML του άρθρου σου:' more_from_html: Περισσότερα από %{name} s_blog: Ιστολόγιο του/της %{name} @@ -1593,9 +1593,9 @@ el: invalid: Αυτή η πρόσκληση δεν είναι έγκυρη invited_by: 'Σε προσκάλεσε ο/η:' max_uses: - one: 1 χρήσης - other: "%{count} χρήσεων" - max_uses_prompt: Απεριόριστη + one: 1 χρήση + other: "%{count} χρήσεις" + max_uses_prompt: Απεριόριστες prompt: Φτιάξε και μοίρασε συνδέσμους με τρίτους για να δώσεις πρόσβαση σε αυτόν τον διακομιστή table: expires_at: Λήγει @@ -1880,7 +1880,7 @@ el: development: Ανάπτυξη edit_profile: Επεξεργασία προφίλ export: Εξαγωγή - featured_tags: Παρεχόμενες ετικέτες + featured_tags: Προβεβλημένες ετικέτες import: Εισαγωγή import_and_export: Εισαγωγή και εξαγωγή migrate: Μετακόμιση λογαριασμού @@ -2142,7 +2142,7 @@ el: post_step: Πες γεια στον κόσμο με κείμενο, φωτογραφίες, βίντεο ή δημοσκοπήσεις. post_title: Κάνε την πρώτη σου ανάρτηση share_step: Πες στους φίλους σου πώς να σε βρουν στο Mastodon. - share_title: Μοιραστείτε το προφίλ σας στο Mastodon + share_title: Κοινοποίησε το προφίλ σου στο Mastodon sign_in_action: Σύνδεση subject: Καλώς ήρθες στο Mastodon title: Καλώς όρισες, %{name}! @@ -2159,7 +2159,7 @@ el: here_is_how: Δείτε πώς hint_html: Η επαλήθευση της ταυτότητας στο Mastodon είναι για όλους. Βασισμένο σε ανοιχτά πρότυπα ιστού, τώρα και για πάντα δωρεάν. Το μόνο που χρειάζεσαι είναι μια προσωπική ιστοσελίδα που ο κόσμος να σε αναγνωρίζει από αυτή. Όταν συνδέεσαι σε αυτήν την ιστοσελίδα από το προφίλ σου, θα ελέγξουμε ότι η ιστοσελίδα συνδέεται πίσω στο προφίλ σου και θα δείξει μια οπτική ένδειξη σε αυτό. instructions_html: Αντέγραψε και επικόλλησε τον παρακάτω κώδικα στην HTML της ιστοσελίδας σου. Στη συνέχεια, πρόσθεσε τη διεύθυνση της ιστοσελίδας σου σε ένα από τα επιπλέον πεδία στο προφίλ σου από την καρτέλα "Επεξεργασία προφίλ" και αποθήκευσε τις αλλαγές. - verification: Πιστοποίηση + verification: Επαλήθευση verified_links: Οι επαληθευμένοι σύνδεσμοι σας website_verification: Επαλήθευση ιστοτόπου webauthn_credentials: diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 08f1b33c7a0..b39844b1945 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -796,8 +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 + view_feeds: Ver temas y feed en vivo + view_feeds_description: Permitir a los usuarios acceder a temas y feeds en vivo sin importar la configuración de los servidores title: Roles rules: add_new: Añadir norma @@ -839,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 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. + preamble: Mostrar contenido interesante es fundamental para atraer a nuevos usuarios que quizá no conozcan a nadie en Mastodon. Controla cómo funcionan las distintas funciones de descubrimiento en tu servidor. privacy: Privacidad profile_directory: Directorio de perfiles public_timelines: Lineas de tiempo públicas @@ -852,13 +852,13 @@ es-MX: users: Para los usuarios locales que han iniciado sesión feed_access: modes: - authenticated: Solo usuarios autenticados + authenticated: Solo usuarios registrados disabled: Requerir un rol de usuario específico public: Todos landing_page: values: about: Acerca de - local_feed: Cronología local + local_feed: Feed 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!" @@ -1196,7 +1196,7 @@ es-MX: hint_html: Si deseas migrar de otra cuenta a esta, aquí puedes crear un alias, que es necesario para poder mover seguidores de la cuenta anterior a esta. Esta acción por sí misma es inofensiva y reversible. La migración de la cuenta se inicia desde la cuenta anterior. remove: Desvincular alias appearance: - advanced_settings: Ajustes avanzados + advanced_settings: Configuración avanzada animations_and_accessibility: Animaciones y accesibilidad boosting_preferences: Preferencias de impulso boosting_preferences_info_html: "Consejo: Sin importar los ajustes, pulsar Mayus al hacer clic en el icono de %{icon} Impulsar, dará impulso inmediatamente." @@ -1604,8 +1604,8 @@ es-MX: link_preview: author_html: Por %{name} potentially_sensitive_content: - action: Pulsa para mostrar - confirm_visit: "¿Seguro que quieres abrir este enlace?" + action: Haz clic para mostrar + confirm_visit: "¿Estás seguro de que deseas abrir este enlace?" hide_button: Ocultar label: Contenido potencialmente sensible lists: @@ -1672,7 +1672,7 @@ es-MX: disabled_account: Tu cuenta actual no será completamente utilizable después. Sin embargo, tendrás acceso a la exportación de datos así como a la reactivación. followers: Esta acción migrará a todos los seguidores de la cuenta actual a la nueva cuenta only_redirect_html: Alternativamente, solo puedes poner una redirección en tu perfil. - other_data: Ningún otro dato se moverá automáticamente (esto incluye tus publicaciones y la lista de cuentas que sigues) + other_data: No se transferirán automáticamente otros datos (incluidas tus publicaciones y la lista de cuentas que sigues) redirect: El perfil de tu cuenta actual se actualizará con un aviso de redirección y será excluido de las búsquedas moderation: title: Moderación @@ -1929,7 +1929,7 @@ es-MX: errors: in_reply_not_found: La publicación a la que estás intentando responder no existe. quoted_status_not_found: La publicación que intentas citar no parece existir. - quoted_user_not_mentioned: No se puede citar a un usuario no mencionado en una Mención Privada. + quoted_user_not_mentioned: No se puede citar a un usuario no mencionado en una mención privada. over_character_limit: Límite de caracteres de %{max} superado pin_errors: direct: Las publicaciones que son visibles solo para los usuarios mencionados no pueden fijarse @@ -1939,12 +1939,12 @@ es-MX: quote_error: not_available: Publicación no disponible pending_approval: Publicación pendiente - revoked: Publicación borrada por el autor + revoked: Publicación eliminada por el autor quote_policies: followers: Solo seguidores nobody: Solo yo public: Cualquiera - quote_post_author: Citando una publicación de %{acct} + quote_post_author: Cita de una publicación de %{acct} title: "%{name}: «%{quote}»" visibilities: direct: Mención privada diff --git a/config/locales/et.yml b/config/locales/et.yml index 99643e60859..0bb6f5acc4d 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -384,8 +384,8 @@ et: one: "%{count} ootel raport" other: "%{count} ootel raportit" pending_tags_html: - one: "%{count} ootel silt" - other: "%{count} ootel silti" + one: "%{count} ootel teemaviide" + other: "%{count} ootel teemaviidet" pending_users_html: one: "%{count} ootel kasutaja" other: "%{count} ootel kasutajat" @@ -783,7 +783,7 @@ et: manage_settings: Halda sätteid manage_settings_description: Lubab kasutajatel muuta lehekülje sätteid manage_taxonomies: Halda taksonoomiaid - manage_taxonomies_description: Luba kasutajatel populaarset sisu üle vaadata ning uuendada siltide sätteid + manage_taxonomies_description: Luba kasutajatel populaarset sisu üle vaadata ning uuendada teemaviidete seadistusi manage_user_access: Halda kasutajate ligipääsu manage_user_access_description: Võimaldab kasutajatel keelata teiste kasutajate kaheastmelise autentimise, muuta oma e-posti aadressi ja lähtestada oma parooli manage_users: Kasutajate haldamine @@ -998,8 +998,8 @@ et: reset: Lähtesta review: Vaata olek üle search: Otsi - title: Märksõnad - updated_msg: Sildi sätted edukalt uuendatud + title: Teemaviited + updated_msg: Teemaviite seadistused on uuendatud terms_of_service: back: Tagasi teenuse tingimustesse changelog: Mis on muutunud @@ -1088,14 +1088,14 @@ et: tag_servers_dimension: Populaarseimad serverid tag_servers_measure: erinevat serverit tag_uses_measure: kasutajaid kokku - description_html: Need sildid ilmuvad praegu paljudes postitutes mida su server näeb. See võib aidata su kasutajatel leida seda millest kõige rohkem parajasti räägitakse. Ühtegi silti ei näidata avalikult, enne nende heaks kiitmist. + description_html: Need teemaviited ilmuvad praegu paljudes postitutes, mida su server näeb. See võib aidata su kasutajatel leida seda millest kõige rohkem parajasti räägitakse. Ühtegi teemaviidet ei näidata enne nende heaks kiitmist avalikult. listable: Võib olla soovitatud no_tag_selected: Silte ei muudetud, kuna ühtegi polnud valitud not_listable: Ei soovitata not_trendable: Ei ilmu trendides not_usable: Ei saa kasutada peaked_on_and_decaying: Populaarseim %{date}, nüüd langemas - title: Trendikad sildid + title: Trendikad teemaviited trendable: Võib ilmuda trendides trending_rank: 'Trendides #%{rank}' usable: Kasutatav @@ -1186,7 +1186,7 @@ et: new_trending_statuses: title: Trendikad postitused new_trending_tags: - title: Trendikad sildid + title: Trendikad teemaviited subject: Uued %{instance} trendid ülevaatuseks aliases: add_new: Pane kolimiseks valmis @@ -1428,8 +1428,8 @@ et: featured_tags: add_new: Lisa uus errors: - limit: Oled jõudnud siltide lubatud maksimumarvuni - hint_html: "Mis on esiletõstetud sildid? Neid silte näidatakse su avalikul profiilil esiletõstetult ning need aitavad teistel leida postitusi, millel on selline silt. See on hea viis, kuidas hoida järge loovtöödel või pikaajalistel projektidel." + limit: Oled jõudnud teemaviidete lubatud maksimumarvuni + hint_html: "Mis on esiletõstetud teemaviited? Neid teemaviiteid näidatakse su avalikul profiilil esiletõstetult ning need aitavad teistel leida postitusi, millel on selline teemaviide. See on hea viis, kuidas hoida järge loovtöödel või pikaajalistel projektidel." filters: contexts: account: Profiilid @@ -1815,7 +1815,7 @@ et: content_warning: 'Sisuhoiatus:' descriptions: account: "@%{acct} avalikud postitused" - tag: 'Avalikud postitused sildiga #%{hashtag}' + tag: 'Avalikud postitused teemaviitega #%{hashtag}' scheduled_statuses: over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit} @@ -1880,7 +1880,7 @@ et: development: Arendus edit_profile: Muuda profiili export: Eksport - featured_tags: Esile toodud sildid + featured_tags: Esile toodud teemaviited import: Impordi import_and_export: Import / eksport migrate: Konto kolimine @@ -1923,8 +1923,8 @@ et: show: Näita rohkem default_language: Kasutajaliidese keelega sama disallowed_hashtags: - one: 'sisaldab ebasobivat silti: %{tags}' - other: 'sisaldab ebasobivaid silte: %{tags}' + one: 'sisaldab ebasobivat teemaviidet: %{tags}' + other: 'sisaldab ebasobivaid teemaviiteid: %{tags}' edited_at_html: Muudetud %{date} errors: in_reply_not_found: Postitus, millele üritad vastata, ei näi enam eksisteerivat. @@ -2139,7 +2139,7 @@ et: other: "%{people} inimest viimase 2 päeva jooksul" hashtags_subtitle: Avasta, mis viimase 2 päeva jooksul on toimunud hashtags_title: Populaarsed märksõnad - hashtags_view_more: Vaata teisi trendikaid märksõnu + hashtags_view_more: Vaata teisi trendikaid teemaviiteid post_action: Postita post_step: Tervita maailma teksti, fotode, videote või küsitlustega. post_title: Tee oma esimene postitus diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 01976436977..f73a1284d88 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -563,6 +563,7 @@ eu: create: Gehitu Moderazio Oharra created_msg: Instantziako moderazio oharra ongi sortu da! description_html: Ikusi eta idatzi oharrak beste moderatzaileentzat eta zuretzat etorkizunerako + title: Moderazio oharrak private_comment: Iruzkin pribatua public_comment: Iruzkin publikoa purge: Ezabatu betiko @@ -771,6 +772,8 @@ eu: description_html: Gehienek erabilera baldintzak irakurri eta onartu dituztela baieztatzen badute ere, orokorrean arazoren bat dagoen arte ez dituzte irakurtzen. Zerbitzariaren arauak begirada batean ikustea errazteko buletadun zerrenda batean bildu. Saiatu arauak labur eta sinple idazten, baina elementu askotan banatu gabe. edit: Editatu araua empty: Ez da zerbitzariko araurik definitu oraindik. + move_down: Behera mugitu + move_up: Mugitu gora title: Zerbitzariaren arauak translation: Itzulpena translations: Itzulpenak @@ -808,6 +811,14 @@ eu: all: Guztiei disabled: Inori ez users: Saioa hasita duten erabiltzaile lokalei + feed_access: + modes: + public: Edonork + landing_page: + values: + about: Honi buruz + local_feed: Jario lokala + trends: Joerak registrations: moderation_recommandation: Mesedez, ziurtatu moderazio-talde egokia eta erreaktiboa duzula erregistroak guztiei ireki aurretik! preamble: Kontrolatu nork sortu dezakeen kontua zerbitzarian. @@ -860,9 +871,14 @@ eu: no_status_selected: Ez da bidalketarik aldatu ez delako bat ere hautatu open: Ireki bidalketa original_status: Jatorrizko bidalketa + quotes: Aipuak reblogs: Bultzadak + replied_to_html: "%{acct_link}(r)i erantzuten" status_changed: Bidalketa aldatuta + status_title: "%{name} erabiltzailearen bidalketa" trending: Joera + view_publicly: Publikoki ikusi + view_quoted_post: Aipatutako bidalketa ikusi visibility: Ikusgaitasuna with_media: Multimediarekin strikes: @@ -907,6 +923,7 @@ eu: sidekiq_process_check: message_html: Ez da ari Sidekiq prozesurik exekutatzen %{value} ilad(et)an. Egiaztatu Sidekiq konfigurazioa software_version_check: + action: Ikusi eguneraketa eskuragarriak message_html: Mastodon eguneratze bat eskuragarri dago. software_version_critical_check: action: Ikusi eguneraketa eskuragarriak @@ -1021,6 +1038,8 @@ eu: one: Pertsona batek erabilia azken astean other: "%{count} pertsonak erabilia azken astean" trending: Joerak + username_blocks: + add_new: Gehitu berria warning_presets: add_new: Gehitu berria delete: Ezabatu diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 85230296b22..eec1dd5048a 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -2089,7 +2089,7 @@ fa: silence: همچنان می‌توانید از حساب خود استفاده کنید، اما فقط افرادی که از قبل شما را دنبال می‌کنند، پست‌های شما را در این سرور می‌بینند و ممکن است از ویژگی‌های مختلف کشف مستثنی شوید. با این حال، دیگران ممکن است همچنان به صورت دستی شما را دنبال کنند. suspend: دیگر نمی توانید از حساب خود استفاده کنید و نمایه و سایر داده های شما دیگر در دسترس نیستند. هنوز هم می‌توانید برای درخواست پشتیبان‌گیری از داده‌های خود وارد شوید تا زمانی که داده‌ها در حدود 30 روز به طور کامل حذف شوند، اما ما برخی از داده‌های اولیه را حفظ می‌کنیم تا از تعلیق فرار نکنید. reason: 'دلیل:' - statuses: 'پست های ذکر شده:' + statuses: 'فرسته‌های ارجاع داده:' subject: delete_statuses: فرسته‌هایتان روی %{acct} برداشته شده‌اند disable: حساب %{acct} شما متوقف شده است diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index 7e1c7632255..a0e3f1d584f 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -496,6 +496,7 @@ fr-CA: delete: Supprimer ip: Adresse IP request_body: Corps de la demande + title: Déboguer les rappels providers: active: Activé base_url: URL de base @@ -819,6 +820,8 @@ fr-CA: preamble: Fournissez des informations détaillées sur le fonctionnement, la modération et le financement du serveur. rules_hint: Il y a un espace dédié pour les règles auxquelles vos utilisateurs sont invités à adhérer. title: À propos + allow_referrer_origin: + title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. title: Apparence @@ -857,7 +860,7 @@ fr-CA: local_feed: Fil local trends: Tendances registrations: - moderation_recommandation: Veuillez vous assurer d'avoir une équipe de modération adéquate et réactive avant d'ouvrir les inscriptions à tout le monde! + moderation_recommandation: Veuillez vous assurer d'avoir une équipe de modération adéquate et réactive avant d'ouvrir les inscriptions à tout le monde ! preamble: Affecte qui peut créer un compte sur votre serveur. title: Inscriptions registrations_mode: @@ -1939,6 +1942,7 @@ fr-CA: public: Publique public_long: Tout le monde sur et en dehors de Mastodon unlisted: Public discret + unlisted_long: Caché des résultats de recherche, des tendances et des fils d'actualité publics statuses_cleanup: enabled: Supprimer automatiquement vos anciens messages enabled_hint: Supprime automatiquement vos messages une fois qu'ils ont atteint un seuil d'ancienneté défini, à moins qu'ils ne correspondent à l'une des exceptions ci-dessous @@ -1984,6 +1988,7 @@ fr-CA: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: contrast: Mastodon (Contraste élevé) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4ed43673650..9d43b6efefa 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -496,6 +496,7 @@ fr: delete: Supprimer ip: Adresse IP request_body: Corps de la demande + title: Déboguer les rappels providers: active: Activé base_url: URL de base @@ -819,6 +820,8 @@ fr: preamble: Fournissez des informations détaillées sur le fonctionnement, la modération et le financement du serveur. rules_hint: Il y a un espace dédié pour les règles auxquelles vos utilisateurs sont invités à adhérer. title: À propos + allow_referrer_origin: + title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. title: Apparence @@ -857,7 +860,7 @@ fr: local_feed: Fil local trends: Tendances registrations: - moderation_recommandation: Veuillez vous assurer d'avoir une équipe de modération adéquate et réactive avant d'ouvrir les inscriptions à tout le monde! + moderation_recommandation: Veuillez vous assurer d'avoir une équipe de modération adéquate et réactive avant d'ouvrir les inscriptions à tout le monde ! preamble: Affecte qui peut créer un compte sur votre serveur. title: Inscriptions registrations_mode: @@ -1939,6 +1942,7 @@ fr: public: Publique public_long: Tout le monde sur et en dehors de Mastodon unlisted: Public discret + unlisted_long: Caché des résultats de recherche, des tendances et des fils d'actualité publics statuses_cleanup: enabled: Supprimer automatiquement vos anciens messages enabled_hint: Supprime automatiquement vos messages une fois qu'ils ont atteint un seuil d'ancienneté défini, à moins qu'ils ne correspondent à l'une des exceptions ci-dessous @@ -1984,6 +1988,7 @@ fr: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: contrast: Mastodon (Contraste élevé) diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 87798f8bae3..e2954e5538d 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1672,6 +1672,7 @@ gl: disabled_account: Tras o cambio a túa conta actual non será totalmente usable, pero terás acceso a exportar os datos e tamén a reactivación. followers: Esta acción moverá todas as túas seguidoras desde a conta actual a nova conta only_redirect_html: De xeito alternativo, podes simplemente pór unha redirección no perfil. + other_data: Ningún outro dato se moverá de xeito automático (incluíndo as túas publicacións e a lista das contas que segues) redirect: O perfil da túa conta actualizarase cun aviso de redirección e será excluído das buscas moderation: title: Moderación diff --git a/config/locales/hu.yml b/config/locales/hu.yml index c1d20eadeb9..a957165f269 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1672,6 +1672,7 @@ hu: disabled_account: A jelenlegi fiókod nem lesz teljesen használható ezután. Viszont elérhető lesz majd az adatexport funkció, valamint a reaktiválás is. followers: Ez a művelet az összes követődet a jelenlegi fiókról az újra fogja költöztetni only_redirect_html: Az is lehetséges, hogy csak átirányítást raksz a profilodra. + other_data: Semmilyen más adat (beleértve a bejegyzéseket és a követett fiókokat) nem lesz automatikusan áthelyezve redirect: A jelenlegi fiókod profiljára átirányításról szóló figyelmeztetést rakunk, valamint már nem fogjuk mutatni a keresésekben moderation: title: Moderáció @@ -1928,6 +1929,7 @@ hu: errors: in_reply_not_found: Már nem létezik az a bejegyzés, melyre válaszolni szeretnél. quoted_status_not_found: Már nem létezik az a bejegyzés, amelyből idézni szeretnél. + quoted_user_not_mentioned: Nem idézhet meg nem említett felhasználót egy privát említési bejegyzésben. over_character_limit: túllépted a maximális %{max} karakteres keretet pin_errors: direct: A csak a megemlített felhasználók számára látható bejegyzések nem tűzhetők ki diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 7b624f55fb7..4f2e8a53850 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1181,7 +1181,7 @@ ko: advanced_settings: 고급 설정 animations_and_accessibility: 애니메이션과 접근성 boosting_preferences: 부스팅 설정 - boosting_preferences_info_html: "팁: 설정에 관계 없이 %{icon}을 쉬프트+클릭하여 곧바로 부스트할 수 있습니다." + boosting_preferences_info_html: "팁: 설정에 관계 없이 %{icon}을 시프트+클릭하여 곧바로 부스트할 수 있습니다." discovery: 발견하기 localization: body: 마스토돈은 자원봉사자들에 의해 번역되었습니다. diff --git a/config/locales/lv.yml b/config/locales/lv.yml index ba8ec50efca..3ad4eb5e4ea 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -902,6 +902,7 @@ lv: title: Konta ieraksti - @%{name} trending: Aktuāli view_publicly: Skatīt publiski + view_quoted_post: Skatīt citēto ierakstu visibility: Redzamība with_media: Ar multividi strikes: @@ -1152,7 +1153,7 @@ lv: new_trending_statuses: title: Populārākās ziņas new_trending_tags: - title: Izplatīti tēmturi + title: Populārākie tēmturi subject: Tiek pārskatītas jaunas tendences %{instance} aliases: add_new: Izveidot aizstājvārdu @@ -1244,7 +1245,7 @@ lv: setup: email_below_hint_html: Jāpārbauda sava surogātpasta mape vai jāpieprasa vēl vienu! Savu e-pasta adresi var labot, ja tā ir nepareiza. email_settings_hint_html: Jāatver saite, kuru mēs nosūtījām uz %{email}, lai sāktu izmantot Mastodon. Mēs gaidīsim šeit pat. - link_not_received: Vai nesaņēmi sati? + link_not_received: Vai nesaņēmi saiti? new_confirmation_instructions_sent: Pēc dažām minūtēm saņemsi jaunu e-pasta ziņojumu ar apstiprinājuma saiti. title: Pārbaudi savu iesūtni sign_in: @@ -1679,6 +1680,8 @@ lv: title: Jauna pieminēšana poll: subject: "%{name} aptauja ir beigusies" + quote: + body: 'Tavu ierakstu citēja %{name}:' reblog: body: 'Tavu ziņu izcēla %{name}:' subject: "%{name} izcēla tavu ziņu" @@ -1893,12 +1896,15 @@ lv: edited_at_html: Labots %{date} errors: in_reply_not_found: Šķiet, ka ziņa, uz kuru tu mēģini atbildēt, nepastāv. + quoted_status_not_found: Šķiet, ka ieraksts, kuru tu mēģini citēt, nepastāv. over_character_limit: pārsniegts %{max} rakstzīmju ierobežojums pin_errors: direct: Ierakstus, kas ir redzami tikai pieminētajiem lietotājiem, nevar piespraust limit: Jau ir piesprausts lielākais iespējamais ierakstu skaits ownership: Kāda cita ierakstu nevar piespraust reblog: Pastiprinātu ierakstu nevar piespraust + quote_error: + not_available: Ieraksts nav pieejams quote_policies: followers: Tikai sekotāji nobody: Tikai es @@ -1906,6 +1912,8 @@ lv: title: "%{name}: “%{quote}”" visibilities: public: Publisks + unlisted: Klusi publisks + unlisted_long: Netiks rādīts Mastodon meklēšanas rezultātos, populārākajos, un publiskajās laikjoslās statuses_cleanup: enabled: Automātiski dzēst vecās ziņas enabled_hint: Automātiski izdzēš Tavus ierakstus, tiklīdz tie sasniedz noteiktu vecuma slieksni, ja vien tie neatbilst kādam no zemāk norādītajiem izņēmumiem @@ -2084,7 +2092,7 @@ lv: other: "%{people} cilvēki pēdējās 2 dienās" zero: "%{people} cilvēku pēdējās divās dienās" hashtags_subtitle: Izpēti, kas pēdējās divās dienās ir piesasitījis cilvēku uzmanību - hashtags_title: Izplatīti tēmturi + hashtags_title: Populārākie tēmturi hashtags_view_more: Skatīt vairāk izplatītu tēmturu post_action: Rakstīt post_step: Pasveicini pasauli ar tekstu, fotoattēliem, video vai aptaujām! diff --git a/config/locales/nan.yml b/config/locales/nan.yml index 508cc46665d..b9e5c09159f 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -1258,10 +1258,116 @@ nan: title: Tsi̍t-kuá基本規定。 title_invited: Lí受邀請ah。 security: 安全 + set_new_password: 設新ê密碼 + setup: + email_below_hint_html: 請檢查lí ê pùn-sò phue資料giap-á,á是請求koh送tsi̍t改。Lí通改電子phue地址,nā是有出入。 + email_settings_hint_html: 請tshi̍h guán送kàu %{email} ê連結,來開始用 Mastodon。Guán ē佇tsia等。 + link_not_received: Kám iáu bē收著連結? + new_confirmation_instructions_sent: Lí ē tī幾分鐘後收著新ê電子phue,有確認連結! + title: 請檢查lí ê收件giap-á + sign_in: + preamble_html: 請用lí佇 %{domain} ê口座kap密碼登入。若是lí ê口座tī別ê服侍器,lí bē當tī tsia登入。 + title: 登入 %{domain} + sign_up: + manual_review: 佇 %{domain} ê註冊ē經過guán ê管理員人工審查。為著tsān guán 進行lí ê註冊,寫tsi̍t點á關係lí家己kap想beh tī %{domain}註冊ê理由。 + preamble: Nā是lí tī tsit ê服侍器有口座,lí ē當tuè別ê佇聯邦宇宙ê lâng,無論伊ê口座tī tó-tsi̍t站。 + title: Lán做伙設定 %{domain}。 + status: + account_status: 口座ê狀態 + confirming: Teh等電子phue確認完成。 + functional: Lí ê口座完全ē當用。 + pending: Lán ê人員teh處理lí ê申請。Tse可能愛一段時間。若是申請允准,lí ē收著e-mail。 + redirecting_to: Lí ê口座無活動,因為tann轉kàu %{acct}。 + self_destruct: 因為 %{domain} teh-beh關掉,lí kan-ta ē當有限接近使用lí ê口座。 + view_strikes: 看早前tuì lí ê口座ê警告 + too_fast: 添表ê速度傷緊,請koh試。 + use_security_key: 用安全鎖 + user_agreement_html: 我有讀而且同意 服務規定 kap 隱私權政策 + user_privacy_agreement_html: 我有讀而且同意 隱私權政策 + author_attribution: + example_title: 見本文字 + hint_html: Lí kám佇Mastodon外口寫新聞á是blog文章?控制當in佇Mastodon分享ê時陣,án-tsuánn表示作者資訊。 + instructions: 請確認lí ê文章HTML內底有tsit ê code: + more_from_html: 看 %{name} ê其他內容 + s_blog: "%{name} ê Blog" + then_instructions: 了後,kā公開ê所在ê域名加kàu下kha ê格á: + title: 作者ê落名 + challenge: + confirm: 繼續 + hint_html: "提醒:Guán佇一點鐘內底,bē koh問密碼。" + invalid_password: 無效ê密碼 + prompt: 確認密碼來繼續 + crypto: + errors: + invalid_key: m̄是有效ê Ed25519 á是 Curve25519 鎖 + date: + formats: + default: "%Y年%b月%d日" + with_month_name: "%Y年%B月%d日" + datetime: + distance_in_words: + about_x_hours: "%{count}點鐘前" + about_x_months: "%{count}kò月前" + about_x_years: "%{count}年前" + almost_x_years: 接近%{count}年前 + half_a_minute: 頭tú-á + less_than_x_minutes: "%{count}分鐘前以內" + less_than_x_seconds: 頭tú-á + over_x_years: "%{count}年" + x_days: "%{count}kang" + x_minutes: "%{count}分" + x_months: "%{count}個月" + x_seconds: "%{count}秒" + deletes: + challenge_not_passed: Lí輸入ê資訊毋著 + confirm_password: 輸入lí tsit-má ê密碼,驗證lí ê身份 + confirm_username: 輸入lí ê口座ê名,來確認程序 + proceed: Thâi掉口座 + success_msg: Lí ê口座thâi掉成功 + warning: + before: 佇繼續進前,請斟酌讀下kha ê說明: + caches: 已經hōo其他ê站the̍h著cache ê內容,可能iáu ē有資料 + data_removal: Lí ê PO文kap其他ê資料ē永永thâi掉。 + email_change_html: Lí ē當改lí ê電子phue地址,suah毋免thâi掉lí ê口座。 + email_contact_html: 若是iáu buē收著phue,lí通寄kàu %{email} tshuē幫tsān + email_reconfirmation_html: Nā是lí iáu bē收著驗證phue,lí通koh請求 + irreversible: Lí bē當復原á是重頭啟用lí ê口座 + more_details_html: 其他資訊,請看隱私政策。 + username_available: Lí ê用者名別lâng ē當申請 + username_unavailable: Lí ê口座ē保留,而且別lâng bē當用 + disputes: + strikes: + action_taken: 行ê行動 + appeal: 投訴 + appeal_approved: Tsit ê警告已經投訴成功,bē koh有效。 + appeal_rejected: Tsit ê投訴已經受拒絕 + appeal_submitted_at: 投訴送出去ah + appealed_msg: Lí ê投訴送出去ah。Nā受允准,ē通知lí。 + appeals: + submit: 送投訴 + approve_appeal: 允准投訴 + associated_report: 關聯ê報告 + created_at: 過時ê + description_html: Tsiah ê是 %{instance} ê人員送hōo lí ê,tuì lí ê口座行ê行動kap警告。 + recipient: 送kàu + reject_appeal: 拒絕投訴 + status: 'PO文 #%{id}' + status_removed: 嘟文已經tuì系統thâi掉 + title: "%{action} tuì %{date}" + title_actions: + delete_statuses: Thâi掉PO文 + disable: 冷凍口座 + mark_statuses_as_sensitive: Kā PO文標做敏感ê + none: 警告 + sensitive: Kā 口座文標做敏感ê + silence: 口座制限 + suspend: 停止口座權限 scheduled_statuses: too_soon: Tio̍h用未來ê日期。 statuses: default_language: Kap界面ê語言sio kâng + terms_of_service: + title: 服務規定 two_factor_authentication: disable: 停止用雙因素認證 user_mailer: diff --git a/config/locales/nn.yml b/config/locales/nn.yml index 51ba5f6b2fc..aad56c92e91 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1672,6 +1672,7 @@ nn: disabled_account: Din nåværende konto vil ikke være fullt brukbar etterpå. Men du vil ha tilgang til dataeksportering såvel som reaktivering. followers: Denne handlinga flyttar alle fylgjarar frå denne kontoen til den nye only_redirect_html: Alternativt kan du velge å bare legge ut en omdirigering på profilen din. + other_data: Ingen andre data blir flytte automatisk (inkludert innlegga dine og lista over folk du fylgjer) redirect: Profilen til din nåværende konto vil bli oppdatert med en omdirigeringsnotis og bli fjernet fra søk moderation: title: Moderasjon diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 1ae00ddc934..9bda819e7b4 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1672,6 +1672,7 @@ pt-BR: disabled_account: Sua conta não estará totalmente funcional ao término deste processo. Entretanto, você terá acesso à exportação de dados bem como à reativação. followers: Esta ação moverá todos os seguidores da conta atual para a nova conta only_redirect_html: Alternativamente, você pode apenas colocar um redirecionamento no seu perfil. + other_data: Nenhum outro dado será movido automaticamente (isto inclui suas postagens e a lista de conta que segue) redirect: O perfil atual da sua conta será atualizado com um aviso de redirecionamento e também será excluído das pesquisas moderation: title: Moderação @@ -1928,6 +1929,7 @@ pt-BR: errors: in_reply_not_found: A publicação que você quer responder parece não existir. quoted_status_not_found: A publicação que você quer responder parece não existir. + quoted_user_not_mentioned: Não é possível citar um usuário não mencionado em uma postagem de Menção Privada. over_character_limit: limite de caracteres de %{max} excedido pin_errors: direct: Publicações visíveis apenas para usuários mencionados não podem ser fixadas diff --git a/config/locales/ru.yml b/config/locales/ru.yml index b7ca4eed33e..fa81b360fa0 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -30,94 +30,94 @@ ru: account_actions: action: Выполнить действие already_silenced: Эта учетная запись уже ограничена. - already_suspended: Действие этой учетной записи уже приостановлено. - title: Произвести модерацию учётной записи %{acct} + already_suspended: Эта учетная запись уже заблокирована. + title: Осуществить модерацию в отношении учётной записи %{acct} account_moderation_notes: - create: Создать - created_msg: Заметка модератора успешно создана! - destroyed_msg: Заметка модератора успешно удалена! + create: Создать заметку + created_msg: Заметка для модераторов добавлена + destroyed_msg: Заметка для модераторов удалена accounts: - add_email_domain_block: Забанить email домен - approve: Подтвердить - approved_msg: Успешно одобрена заявка на регистрацию %{username} + add_email_domain_block: " Добавить блокировку по домену эл. почты" + approve: Одобрить + approved_msg: Заявка на регистрацию %{username} одобрена are_you_sure: Вы уверены? - avatar: Аватар + avatar: Фото профиля by_domain: Домен change_email: - changed_msg: Адрес эл. почты успешно изменен! - current_email: Текущий e-mail - label: Сменить e-mail - new_email: Новый e-mail - submit: Сменить e-mail - title: Сменить e-mail для %{username} + changed_msg: Адрес электронной почты изменён + current_email: Текущий адрес электронной почты + label: Изменить адрес эл. почты + new_email: Новый адрес электронной почты + submit: Изменить адрес электронной почты + title: Изменить адрес электронной почты пользователя %{username} change_role: - changed_msg: Роль успешно изменена! + changed_msg: Роль изменена edit_roles: Управление ролями пользователей label: Изменить роль - no_role: Нет роли - title: Изменить роль %{username} + no_role: Без роли + title: Изменить роль пользователя %{username} confirm: Подтвердить - confirmed: Подтверждено - confirming: Подтверждение + confirmed: Подтверждена + confirming: Ожидает подтверждения custom: Другое delete: Удалить данные - deleted: Удалён + deleted: Удалена demote: Разжаловать destroyed_msg: Данные %{username} поставлены в очередь на удаление - disable: Заморозка + disable: Отключить disable_sign_in_token_auth: Отключить аутентификацию по e-mail кодам disable_two_factor_authentication: Отключить 2FA - disabled: Отключено + disabled: Отключена display_name: Отображаемое имя domain: Домен - edit: Изменить - email: E-mail - email_status: Статус e-mail + edit: Редактировать + email: Адрес электронной почты + email_status: Подтверждение учётной записи enable: Включить enable_sign_in_token_auth: Включить аутентификацию по e-mail кодам enabled: Включен enabled_msg: Учётная запись %{username} успешно разморожена followers: Подписчики follows: Подписки - header: Шапка + header: Обложка профиля inbox_url: URL входящих invite_request_text: Причины для присоединения invited_by: Приглашение выдал(а) - ip: IP + ip: IP-адрес joined: Дата регистрации location: all: Все local: Локальные - remote: Удаленные - title: Размещение - login_status: Статус учётной записи - media_attachments: Медиафайлы + remote: С других серверов + title: По расположению + login_status: Состояние учётной записи + media_attachments: Медиавложения memorialize: Сделать мемориалом - memorialized: Превращён в памятник + memorialized: In memoriam memorialized_msg: "%{username} успешно превращён в памятник" moderation: active: Действующие all: Все - disabled: Отключено - pending: В ожидании + disabled: Отключённые + pending: Ожидают одобрения silenced: Ограниченные suspended: Заблокированные - title: Модерация + title: По состоянию moderation_notes: Заметки модератора most_recent_activity: Последняя активность - most_recent_ip: Последний IP + most_recent_ip: Недавние IP-адреса no_account_selected: Ничего не изменилось, так как ни одна учётная запись не была выделена - no_limits_imposed: Без ограничений + no_limits_imposed: Ограничения отсутствуют no_role_assigned: Роль не присвоена not_subscribed: Не подписаны - pending: Ожидает рассмотрения - perform_full_suspension: Блокировка - previous_strikes: Предыдущие замечания + pending: Ожидает одобрения + perform_full_suspension: Заблокировать + previous_strikes: Зафиксированные нарушения previous_strikes_description_html: - few: У этой учётной записи %{count} замечания. - many: У этой учётной записи %{count} замечаний. - one: У этой учётной записи одно замечание. - other: У этой учетной записи %{count} замечание. + few: За этой учётной записью числится %{count} нарушения. + many: За этой учётной записью числится %{count} нарушений. + one: За этой учётной записью числится %{count} нарушение. + other: За этой учётной записью числится %{count} нарушений. promote: Повысить protocol: Протокол public: Публичный @@ -125,13 +125,13 @@ ru: redownload: Обновить аватар redownloaded_msg: Профиль %{username} успешно обновлен из оригинала reject: Отклонить - rejected_msg: Успешно отклонено приложение для регистрации %{username} + rejected_msg: Заявка на регистрацию %{username} отклонена remote_suspension_irreversible: Данные этого аккаунта были необратимо удалены. remote_suspension_reversible_hint_html: Учётная запись была заблокирована, и данные будут полностью удалены %{date}. До этого момента её можно восстановить без каких-либо неприятных последствий. Если вы хотите немедленно удалить все данные учётной записи, вы можете сделать это ниже. - remove_avatar: Удалить аватар - remove_header: Убрать шапку - removed_avatar_msg: Аватар %{username} успешно удален - removed_header_msg: Успешно удалено изображение заголовка %{username} + remove_avatar: Удалить фото профиля + remove_header: Удалить обложку профиля + removed_avatar_msg: Фото профиля %{username} удалено + removed_header_msg: Обложка профиля %{username} удалена resend_confirmation: already_confirmed: Этот пользователь уже подтвержден send: Повторно отправить ссылку подтверждения @@ -141,69 +141,69 @@ ru: resubscribe: Переподписаться role: Роль search: Поиск - search_same_email_domain: Другие пользователи с тем же почтовым доменом - search_same_ip: Другие пользователи с таким же IP + search_same_email_domain: Другие пользователи с тем же доменом эл. почты + search_same_ip: Другие пользователи с тем же IP-адресом security: Безопасность security_measures: only_password: Только пароль password_and_2fa: Пароль и 2FA - sensitive: Деликатный + sensitive: Скрыть медиа sensitized: отмечено как деликатный контент shared_inbox_url: URL общих входящих show: created_reports: Жалобы, отправленные с этой учётной записи targeted_reports: Жалобы на эту учётную запись - silence: Скрытие - silenced: Заглушен + silence: Ограничить + silenced: Ограничена statuses: Посты - strikes: Предыдущие замечания + strikes: Зафиксированные нарушения subscribe: Подписаться suspend: Заблокировать - suspended: Заморожен + suspended: Заблокирована suspension_irreversible: Данные этой учётной записи были необратимо удалены. Вы можете разблокировать учетную запись, чтобы сделать её доступной, но это не восстановит ранее имевшиеся в ней данные. suspension_reversible_hint_html: Учётная запись была заблокирована, и данные будут полностью удалены %{date}. До этого момента её можно восстановить без каких-либо неприятных последствий. Если вы хотите немедленно удалить все данные учётной записи, вы можете сделать это ниже. title: Учётные записи unblock_email: Разблокировать e-mail адрес unblocked_email_msg: E-mail адрес %{username} разблокирован unconfirmed_email: Неподтверждённый e-mail - undo_sensitized: Снять отметку "деликатный" - undo_silenced: Отменить скрытие - undo_suspension: Снять блокировку + undo_sensitized: Не скрывать медиа + undo_silenced: Не ограничивать + undo_suspension: Разблокировать unsilenced_msg: Ограничения с учётной записи %{username} сняты успешно unsubscribe: Отписаться unsuspended_msg: Учётная запись %{username} успешно разморожена username: Имя пользователя view_domain: Просмотр сводки по домену - warn: Предупреждение + warn: Предупредить web: Веб whitelisted: В белом списке action_logs: action_types: - approve_appeal: Одобрение обжалований - approve_user: Утверждение регистраций + approve_appeal: Удовлетворение апелляции + approve_user: Одобрение регистрации assigned_to_self_report: Присвоение жалоб - change_email_user: Смена e-mail пользователей - change_role_user: Смена ролей пользователей - confirm_user: Подтверждение пользователей - create_account_warning: Выдача предупреждения - create_announcement: Создание объявлений - create_canonical_email_block: Создание блокировок e-mail + change_email_user: Смена адреса эл. почты пользователя + change_role_user: Изменение роли пользователя + confirm_user: Подтверждение пользователя + create_account_warning: Вынесение предупреждения + create_announcement: Создание объявления + create_canonical_email_block: Добавление блокировки по адресу эл. почты create_custom_emoji: Добавление эмодзи create_domain_allow: Разрешение доменов create_domain_block: Блокировка доменов - create_email_domain_block: Создание доменных блокировок e-mail + create_email_domain_block: Добавление блокировки по домену эл. почты create_ip_block: Создание правил для IP-адресов create_relay: Создание ретранслятора create_unavailable_domain: Добавление домена в список недоступных - create_user_role: Создание ролей + create_user_role: Создание роли create_username_block: Создать правило имени пользователя demote_user: Разжалование пользователей - destroy_announcement: Удаление объявлений - destroy_canonical_email_block: Удаление блокировок e-mail + destroy_announcement: Удаление объявления + destroy_canonical_email_block: Удаление блокировки по адресу эл. почты destroy_custom_emoji: Удаление эмодзи destroy_domain_allow: Отзыв разрешений для доменов destroy_domain_block: Разблокировка доменов - destroy_email_domain_block: Удаление доменных блокировок e-mail + destroy_email_domain_block: Удаление блокировки по домену эл. почты destroy_instance: Очистить домен destroy_ip_block: Удаление правил для IP-адресов destroy_relay: Удаление ретранслятора @@ -222,7 +222,7 @@ ru: enable_user: Разморозка пользователей memorialize_account: Присвоение пользователям статуса «мемориала» promote_user: Повышение пользователей - publish_terms_of_service: Опубликование пользовательского соглашения + publish_terms_of_service: Публикация пользовательского соглашения reject_appeal: Отклонение обжалований reject_user: Отклонение регистраций remove_avatar_user: Удаление аватаров @@ -247,8 +247,8 @@ ru: update_user_role: Изменение ролей update_username_block: Обновить правило имени пользователя actions: - approve_appeal_html: "%{name} одобрил(а) обжалование действий модерации от %{target}" - approve_user_html: "%{name} утвердил(а) регистрацию %{target}" + approve_appeal_html: "%{name} удовлетворил(а) апелляцию %{target}" + approve_user_html: "%{name} одобрил(а) заявку на регистрацию %{target}" assigned_to_self_report_html: "%{name} назначил(а) себя для решения жалобы %{target}" change_email_user_html: "%{name} cменил(а) e-mail адрес пользователя %{target}" change_role_user_html: "%{name} изменил(а) роль %{target}" @@ -322,26 +322,26 @@ ru: unavailable_instance: "(доменное имя недоступно)" announcements: back: Вернуться к объявлениям - destroyed_msg: Объявление удалено. + destroyed_msg: Объявление удалено edit: title: Редактировать объявление - empty: Объявления не найдены. - live: В эфире + empty: Объявлений не найдено. + live: Опубликованные new: - create: Создать объявление - title: Новое объявление + create: Опубликовать объявление + title: Создать объявление preview: disclaimer: Так как пользователи не могут отказаться от получения уведомлений по электронной почте, их следует использовать только для действительно важных объявлений, например, чтобы сообщить об утечке персональных данных или о закрытии сервера. - explanation_html: 'Сообщение будет отравлено %{display_count} пользователям. В теле письма будет указан следующий текст:' - title: Предпросмотр объявления по электронной почте + explanation_html: 'Сообщение будет отравлено %{display_count} пользователям. Письмо будет содержать следующий текст:' + title: Предпросмотр уведомления о новом объявлении publish: Опубликовать - published_msg: Объявление опубликовано. + published_msg: Объявление опубликовано scheduled_for: Запланировано на %{time} scheduled_msg: Объявление добавлено в очередь публикации. title: Объявления - unpublish: Отменить публикацию - unpublished_msg: Объявление скрыто. - updated_msg: Объявление обновлено. + unpublish: Скрыть + unpublished_msg: Объявление скрыто + updated_msg: Объявление отредактировано critical_update_pending: Ожидается обновление критического уровня custom_emojis: assign_category: Задать категорию @@ -365,7 +365,7 @@ ru: listed: В списке new: title: Добавить новый эмодзи - no_emoji_selected: Не было изменено ни одного эмодзи + no_emoji_selected: Ничего не изменилось, так как ни один эмодзи не был выделен not_permitted: У вас недостаточно прав для выполнения этого действия overwrite: Заменить shortcode: Краткий код @@ -413,8 +413,8 @@ ru: website: Веб-сайт disputes: appeals: - empty: Обжалования не найдены. - title: Обжалования + empty: Апелляций не найдено. + title: Апелляции domain_allows: add_new: Внести в белый список created_msg: Домен добавлен в белый список @@ -465,7 +465,7 @@ ru: undo: Отменить блокировку домена view: Посмотреть доменные блокировки email_domain_blocks: - add_new: Добавить новую + add_new: Добавить allow_registrations_with_approval: Разрешить регистрацию с одобрением attempts_over_week: few: "%{count} попытки за последнюю неделю" @@ -486,7 +486,7 @@ ru: not_permitted: Не разрешено resolved_dns_records_hint_html: Доменное имя указывает на следующие MX-домены, которые в конечном итоге отвечают за прием электронной почты. Блокировка MX-домена будет блокировать регистрации с любого адреса электронной почты, который использует тот же MX-домен, даже если видимое доменное имя отличается от него. Будьте осторожны, чтобы не заблокировать основных провайдеров электронной почты resolved_through_html: Разрешено через %{domain} - title: Заблокированные e-mail домены + title: Блокировки по домену эл. почты export_domain_allows: new: title: Импорт домена разрешён @@ -758,16 +758,16 @@ ru: one: "%{count} пользователь" other: "%{count} пользователей" categories: - administration: Администрация + administration: Администрирование devops: DevOps invites: Приглашения - moderation: Модерация - special: Особые + moderation: Модерирование + special: Особые разрешения delete: Удалить - description_html: С помощью ролей пользователей вы можете настроить, к каким функциям и областям Mastodon у ваших пользователей будет доступ. - edit: Изменить роль '%{name}' ' + description_html: С помощью пользовательских ролей вы можете настроить, к каким функциям и разделам Mastodon у ваших пользователей будет доступ. + edit: Редактировать роль '%{name}' everyone: Разрешения по умолчанию - everyone_full_description_html: Это базовая роль, касающаяся всех пользователей, даже тех, кто не имеет назначенной роли. Все другие роли наследуют разрешения от нее. + everyone_full_description_html: Это базовая роль, которая распространяется на всех пользователей, включая тех, кому не присвоена никакая роль. Все другие роли наследуют разрешения от неё. permissions_count: few: "%{count} разрешения" many: "%{count} разрешений" @@ -775,107 +775,121 @@ ru: other: "%{count} разрешений" privileges: administrator: Администратор - administrator_description: Пользователи с этим разрешением будут обходить все права - delete_user_data: Удалить пользовательские данные - delete_user_data_description: Позволяет пользователям удалять данные других пользователей без задержки - invite_users: Пригласить пользователей - invite_users_description: Позволяет пользователям приглашать новых людей на сервер + administrator_description: Пользователи с этим разрешением смогут обойти любую проверку разрешений + delete_user_data: Удаление пользовательских данных + delete_user_data_description: Разрешить пользователям моментально удалять данные других пользователей + invite_users: Приглашение пользователей + invite_users_description: Разрешить пользователям приглашать на этот сервер новых людей manage_announcements: Управление объявлениями - manage_announcements_description: Позволяет пользователям управлять объявлениями на сервере + manage_announcements_description: Разрешить пользователям управлять объявлениями этого сервера manage_appeals: Управление апелляциями - manage_appeals_description: Позволяет пользователям просматривать апелляции на действия модерации - manage_blocks: Управление блоками + manage_appeals_description: Разрешить пользователям рассматривать апелляции на действия модераторов + manage_blocks: Управление блокировками manage_blocks_description: Позволить пользователям блокировать провайдеров электронной почты и IP-адреса - manage_custom_emojis: Управление смайлами - manage_custom_emojis_description: Позволяет пользователям управлять пользовательскими эмодзи на сервере - manage_federation: Управление Федерацией + manage_custom_emojis: Управление эмодзи + manage_custom_emojis_description: Разрешить пользователям управлять эмодзи этого сервера + manage_federation: Управление федерацией с другими серверами manage_federation_description: Позволяет пользователям блокировать или разрешить объединение с другими доменами и контролировать возможность доставки manage_invites: Управление приглашениями manage_invites_description: Позволяет пользователям просматривать и деактивировать пригласительные ссылки manage_reports: Управление жалобами manage_reports_description: Позволяет пользователям просматривать жалобы и осуществлять модерацию по ним manage_roles: Управление ролями - manage_roles_description: Разрешает пользователям управлять ролями и назначать их + manage_roles_description: Разрешить пользователям управлять нижестоящими ролями, а также присваивать эти роли другим пользователям manage_rules: Управление правилами - manage_rules_description: Разрешает пользователям изменять правила на сервере + manage_rules_description: Разрешить пользователям менять правила сервера manage_settings: Управление параметрами manage_settings_description: Разрешает пользователям управлять параметрами сайта manage_taxonomies: Управление трендами manage_taxonomies_description: Позволяет пользователям модерировать список популярного контента и менять настройки хэштегов - manage_user_access: Управление правами пользователей - manage_user_access_description: Разрешает пользователям отключать двухэтапную аутентификацию другим пользователям, менять их e-mail и сбрасывать их пароль + manage_user_access: Управление доступом в учётную запись + manage_user_access_description: Разрешить пользователям отключать двухфакторную аутентификацию другим пользователям, менять адреса электронной почты и сбрасывать пароли manage_users: Управление пользователями manage_users_description: Разрешает пользователям просматривать информацию о других пользователях и применять против них модерацию - manage_webhooks: Управление веб-хуками + manage_webhooks: Управление вебхуками manage_webhooks_description: Разрешает пользователям настраивать веб-хуки для административных событий - view_audit_log: Посмотреть журнал аудита - view_audit_log_description: Позволяет пользователям просматривать историю административных действий на сервере - view_dashboard: Открыть панель управления - view_dashboard_description: Разрешает пользователям просматривать статистику сервера + view_audit_log: Просмотр журнала аудита + view_audit_log_description: Разрешить пользователям просмотр истории административных действий на этом сервере + view_dashboard: Просмотр панели мониторинга + view_dashboard_description: Разрешить пользователям доступ к панели мониторинга и различным метрикам view_devops: DevOps - view_devops_description: Разрешить пользователям доступ к панелям Sidekiq и pgHero + view_devops_description: Разрешить пользователям доступ к панелям управления Sidekiq и pgHero + view_feeds: Просмотр живых и тематических лент + view_feeds_description: Разрешить пользователям доступ к живым и тематическим лентам вне зависимости от настроек сервера title: Роли rules: add_new: Добавить правило add_translation: Добавить перевод delete: Удалить - description_html: Хотя большинство утверждает, что прочитали и согласны с условиями обслуживания, обычно люди не читают их до тех пор, пока не возникнет проблема. Упростите просмотр правил вашего сервера с первого взгляда, предоставив их в виде простого маркированного списка. Старайтесь, чтобы отдельные правила были краткими и простыми, но старайтесь не разбивать их на множество отдельных элементов. + description_html: Хотя большинство людей утверждают, что они прочитали условия и согласились с ними, обычно никто не читает текст соглашений до тех пор, пока не возникнет проблема. Составьте правила вашего сервера в виде одноуровневого маркированного списка, чтобы было легче увидеть их сразу. Старайтесь, чтобы отдельные правила были простыми и лаконичными, но старайтесь не разбивать их на множество отдельных элементов. edit: Редактировать правило - empty: Правила сервера еще не определены. - move_down: Переместить вниз - move_up: Переместить вверх + empty: Правила сервера еще не добавлены. + move_down: Опустить вниз + move_up: Поднять вверх title: Правила сервера translation: Перевод - translations: Переводы + translations: Перевод + translations_explanation: При желании можно перевести правила на другие языки. Если перевод отсутствует, будет показан текст по умолчанию. Всегда необходимо следить за тем, чтобы перевод не расходится с текстом по умолчанию. settings: about: - manage_rules: Управление правилами на сервере - preamble: Предоставьте подробную информацию как сервер работает, модерируется, финансируется. - rules_hint: Это отдельное место для правил, которыми должны руководствоваться пользователи. - title: О нас + manage_rules: Перейти к правилам сервера + preamble: Предоставьте подробную информацию о том, как сервер функционирует, модерируется, финансируется. + rules_hint: Для правил, которых должны придерживаться пользователи, есть отдельный раздел. + title: О сервере + allow_referrer_origin: + desc: Когда ваши пользователи переходят по ссылкам на внешние сайты, их браузеры могут отправлять адрес вашего сервера Mastodon в заголовке Referer. Снимите этот флажок, если такое поведение позволит отследить уникальных пользователей, например если это ваш личный сервер Mastodon. + title: Позволить внешним сайтам видеть ваш сервер Mastodon как источник трафика appearance: - preamble: Настройте веб-интерфейс Мастодона. + preamble: Настройте веб-интерфейс Mastodon. title: Внешний вид branding: - preamble: Брендинг вашего сервера отличает его от других серверов сети. Эта информация может отображаться в различных средах, таких как веб-интерфейс Mastodon, нативные приложения, в виде предпросмотра ссылок на других веб-сайтах, в почтовых приложениях и так далее. По этой причине лучше держать эту информацию ясной, короткой и краткой. + preamble: 'Брендинг вашего сервера отличает его от других серверов сети. Эти сведения могут отображаться в различных средах: веб-интерфейсе Mastodon, нативных приложениях, предпросмотре ссылки на других веб-сайтах и в мессенджерах и так далее. По этой причине лучше формулировать информацию в ясной, лаконичной и понятной форме.' title: Брендинг - captcha_enabled: - desc_html: Это зависит от внешних скриптов из hCaptcha, которые могут представлять интерес для безопасности и конфиденциальности. Кроме того, это может сделать процесс регистрации значительно менее доступным для некоторых (особенно отключенных) людей. По этим причинам просьба рассмотреть альтернативные меры, такие, как регистрация, основанная на официальном утверждении или на приглашении. - title: Запрашивать новых пользователей для решения CAPTCHA для подтверждения учетной записи content_retention: - danger_zone: Осторожно! - preamble: Управление сохранением пользовательского контента в Mastodon. - title: Хранение контента + danger_zone: Небезопасные настройки + preamble: Управляйте тем, как Mastodon сохраняет пользовательские данные. + title: Хранение данных default_noindex: - desc_html: Влияет на всех пользователей, которые не изменили эту настройку сами - title: Исключить пользователей из индексации поисковиками по умолчанию + desc_html: Применяется к тем пользователям, которые не изменили эту настройку сами + title: Не индексировать профили пользователей по умолчанию discovery: - follow_recommendations: Рекомендации подписок - privacy: Конфиденциальность + follow_recommendations: Рекомендации профилей + preamble: Возможность найти интересный контент или получить его в рекомендациях играет важную роль в вовлечении новых пользователей, которые могут ещё не знать никого в Mastodon. Управляйте тем, как различные функции обзора работают на вашем сервере. + privacy: Приватность profile_directory: Каталог профилей public_timelines: Публичные ленты - publish_statistics: Опубликовать стаитстику + publish_statistics: Публикация статистики title: Обзор - trends: Популярное + trends: Актуальное domain_blocks: - all: Всем + all: Кому угодно disabled: Никому - users: Залогиненным локальным пользователям + users: Авторизованным пользователям этого сервера + feed_access: + modes: + authenticated: Только авторизованные пользователи + disabled: Только пользователи с особой ролью + public: Кто угодно + landing_page: + values: + about: О сервере + local_feed: Локальная лента + trends: Актуальное registrations: - moderation_recommandation: Убедитесь, что у вас есть адекватная и оперативная команда модераторов, прежде чем открывать регистрацию для всех желающих! - preamble: Контролируйте, кто может создать учетную запись на вашем сервере. + moderation_recommandation: Прежде чем открывать регистрацию для всех желающих, убедитесь, что у вас есть компетентная команда модераторов, способная на быструю реакцию! + preamble: Контролируйте, кто может создать учётную запись на вашем сервере. title: Регистрации registrations_mode: modes: - approved: Для регистрации требуется подтверждение - none: Никто не может регистрироваться - open: Все могут регистрироваться - warning_hint: Мы рекомендуем использовать "Требуется одобрение для регистрации", если вы не уверены, что ваша команда модераторов сможет своевременно справиться со спамом и злоумышленными регистрациями. + approved: Регистрация требует подтверждения + none: Никто не может зарегистрироваться + open: Кто угодно может зарегистрироваться + warning_hint: Мы рекомендуем использовать опцию «Регистрация требует подтверждения», если вы не уверены, что ваша команда модераторов сможет своевременно справиться со спамом и злоумышленными регистрациями. security: - authorized_fetch: Требовать аутентификацию от федеративных серверов - authorized_fetch_hint: Требование аутентификации от федеративных серверов позволяет более строго соблюдать блокировки как на уровне пользователя, так и на уровне сервера. Однако при этом снижается производительность, уменьшается охват ваших ответов и могут возникнуть проблемы совместимости с некоторыми федеративными сервисами. Кроме того, это не помешает специальным исполнителям получать ваши публичные сообщения и учётные записи. - authorized_fetch_overridden_hint: В настоящее время вы не можете изменить этот параметр, поскольку он переопределяется переменной среды. - federation_authentication: Принудительная аутентификация федерации + authorized_fetch: Требовать аутентификацию для межсерверного взаимодействия + authorized_fetch_hint: Требование аутентификации для сервер-серверного взаимодействия позволяет более строго соблюдать блокировки как на уровне пользователя, так и на уровне сервера. Однако при этом снижается производительность, уменьшается охват ваших ответов, и могут возникнуть проблемы совместимости с некоторыми федеративными сервисами. Кроме того, это не остановит заинтересованных лиц от получения публичных постов и учётных записей с вашего сервера. + authorized_fetch_overridden_hint: В данный момент вы не можете изменить этот параметр, поскольку он переопределён переменной среды. + federation_authentication: Обязательная аутентификация межсерверного взаимодействия title: Настройки сервера site_uploads: delete: Удалить загруженный файл @@ -914,6 +928,7 @@ ru: no_status_selected: Ничего не изменилось, так как ни один пост не был выделен open: Открыть запись original_status: Оригинальный пост + quotes: Цитаты reblogs: Продвинули replied_to_html: Ответ пользователю %{acct_link} status_changed: Пост изменен @@ -921,6 +936,7 @@ ru: title: Посты пользователя - @%{name} trending: Популярное view_publicly: Открыть по публичной ссылке + view_quoted_post: Просмотр цитируемого сообщения visibility: Видимость with_media: С файлами strikes: @@ -1005,9 +1021,9 @@ ru: draft: Черновик generate: Использовать шаблон generates: - action: Генерировать + action: Сгенерировать chance_to_review_html: "Сгенерированное пользовательское соглашение не будет опубликовано автоматически. У вас будет возможность просмотреть результат. Введите все необходимые сведения, чтобы продолжить." - explanation_html: Шаблон пользовательского соглашения приводится исключительно в ознакомительных целях, и не может рассматриваться как юридическая консультация по тому или иному вопросу. Обратитесь к своему юрисконсульту насчёт вашей ситуации и имеющихся правовых вопросов. + explanation_html: Шаблон пользовательского соглашения приводится исключительно в ознакомительных целях и не может рассматриваться как юридическая консультация по тому или иному вопросу. Обратитесь к своему юрисконсульту насчёт вашей ситуации и имеющихся правовых вопросов. title: Создание пользовательского соглашения going_live_on_html: Вступило в силу с %{date} history: История @@ -1017,8 +1033,8 @@ ru: notified_on_html: 'Дата уведомления пользователей: %{date}' notify_users: Уведомить пользователей preview: - explanation_html: 'Сообщение будет отравлено %{display_count} пользователям, которые зарегистрировались до %{date}. В теле письма будет указан следующий текст:' - send_preview: Отправить предпросмотр на %{email} + explanation_html: 'Сообщение будет отравлено %{display_count} пользователям, которые зарегистрировались до %{date}. Письмо будет содержать следующий текст:' + send_preview: Отправить тестовое уведомление на %{email} send_to_all: few: Отправить %{display_count} сообщения many: Отправить %{display_count} сообщений @@ -1130,17 +1146,17 @@ ru: warning_presets: add_new: Добавить delete: Удалить - edit_preset: Удалить шаблон предупреждения - empty: Вы еще не определили пресеты предупреждений. + edit_preset: Редактировать шаблон предупреждения + empty: Вы ещё не создали ни одного шаблона предупреждения. title: Шаблоны предупреждений webhooks: - add_new: Добавить конечную точку + add_new: Добавить вебхук delete: Удалить description_html: "Вебхуки позволяют Mastodon отправлять вашим приложениям уведомления в реальном времени о выбранных происходящих событиях, а они могут обрабатывать их в автоматическом режиме." disable: Отключить - disabled: Отключено + disabled: Деактивирован edit: Редактировать вебхук - empty: У вас пока нет настроенных конечных точек вебхуков. + empty: Вы ещё не настроили ни одного вебхука. enable: Включить enabled: Активен enabled_events: @@ -1149,9 +1165,9 @@ ru: one: "%{count} событие включено" other: "%{count} событий включено" events: События - new: Новый вебхук + new: Создать вебхук rotate_secret: Сгенерировать новый - secret: Ключ подписи + secret: Секретный ключ status: Состояние title: Вебхуки webhook: Вебхук @@ -1300,7 +1316,7 @@ ru: pending: Ваша заявка ожидает одобрения администраторами, это может занять немного времени. Вы получите письмо, как только заявку одобрят. redirecting_to: Ваша учётная запись деактивированна, потому что вы настроили перенаправление на %{acct}. self_destruct: Поскольку %{domain} закрывается, вы получите ограниченный доступ к вашей учетной записи. - view_strikes: Просмотр предупреждений, которые модераторы выносили вам ранее + view_strikes: Просмотр нарушений, зафиксированных ранее при модерации вашей учётной записи too_fast: Форма отправлена слишком быстро, попробуйте еще раз. use_security_key: Использовать электронный ключ user_agreement_html: Мной прочитаны и приняты пользовательское соглашение и политика конфиденциальности @@ -1365,7 +1381,7 @@ ru: appeal_submitted_at: Апелляция отправлена appealed_msg: Ваша апелляция была отправлена. Если она будет одобрена, вы будете уведомлены. appeals: - submit: Подать обжалование + submit: Подать апелляцию approve_appeal: Одобрить обжалование associated_report: Связанные обращения created_at: Дата @@ -1374,15 +1390,15 @@ ru: reject_appeal: Отклонить обжалование status: 'Пост #%{id}' status_removed: Пост уже удален из системы - title: "%{action} от %{date}" + title: "%{action} (%{date})" title_actions: delete_statuses: Удаление поста - disable: Заморозка аккаунта + disable: Отключение учётной записи mark_statuses_as_sensitive: Помечать посты как деликатные - none: Требующие внимания + none: Предупреждение sensitive: Отметить учетную запись как деликатную - silence: Ограничение учетной записи - suspend: Приостановка Аккаунта + silence: Ограничение учётной записи + suspend: Блокировка учётной записи your_appeal_approved: Ваша апелляция одобрена your_appeal_pending: Вы подали апелляцию your_appeal_rejected: Ваша апелляция отклонена @@ -2020,7 +2036,7 @@ ru: terms_of_service_interstitial: past_preamble_html: Мы изменили наше пользовательское соглашение с момента вашего последнего посещения. Мы рекомендуем вам ознакомиться с обновленным соглашением. review_link: Посмотреть пользовательское соглашение - title: Изменяется пользовательское соглашение %{domain} + title: Изменяется пользовательское соглашение на сервере %{domain} themes: contrast: Mastodon (высококонтрастная) default: Mastodon (тёмная) @@ -2082,17 +2098,17 @@ ru: change_password: сменить пароль details: 'Подробности о новом входе:' explanation: Мы заметили вход в вашу учётную запись с нового IP-адреса. - further_actions_html: Если это были не вы, рекомендуем вам немедленно %{action} и включить двухфакторную авторизацию, чтобы обезопасить свою учётную запись. + further_actions_html: Если это были не вы, рекомендуем немедленно %{action} и включить двухфакторную аутентификацию, чтобы обезопасить свою учётную запись. subject: В вашу учётную запись был выполнен вход с нового IP-адреса title: Выполнен вход terms_of_service_changed: agreement: Продолжая использовать %{domain}, вы соглашаетесь с этими условиями. Если вы не согласны с новыми условиями, вы в любой момент можете удалить вашу учётную запись на %{domain}. - changelog: 'Вот что обновление условий будет значит для вас в общих чертах:' - description: 'Вы получили это сообщение, потому что мы внесли некоторые изменения в пользовательское соглашение %{domain}. Эти изменения вступят в силу %{date}. Рекомендуем вам ознакомиться с обновлёнными условиями по ссылке:' - description_html: Вы получили это сообщение, потому что мы внесли некоторые изменения в пользовательское соглашение %{domain}. Эти изменения вступят в силу %{date}. Рекомендуем вам ознакомиться с обновлёнными условиями. + changelog: 'Вот что обновление условий будет значить для вас в общих чертах:' + description: 'Вы получили это сообщение, потому что мы внесли некоторые изменения в пользовательское соглашение сервера %{domain}. Эти изменения вступят в силу %{date}. Рекомендуем вам ознакомиться с обновлёнными условиями по ссылке:' + description_html: Вы получили это сообщение, потому что мы внесли некоторые изменения в пользовательское соглашение сервера %{domain}. Эти изменения вступят в силу %{date}. Рекомендуем вам ознакомиться с обновлёнными условиями. sign_off: Ваш %{domain} - subject: Обновления наших условий использования - subtitle: На %{domain} изменилось пользовательское соглашение + subject: Мы обновляем наше пользовательское соглашение + subtitle: Изменяется пользовательское соглашение на сервере %{domain} title: Важное обновление warning: appeal: Обжаловать diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml index 79d5f033f16..27d16dcff64 100644 --- a/config/locales/simple_form.ar.yml +++ b/config/locales/simple_form.ar.yml @@ -347,9 +347,7 @@ ar: jurisdiction: الاختصاص القانوني min_age: الحد الإدنى للعمر user: - date_of_birth_1i: يوم date_of_birth_2i: شهر - date_of_birth_3i: سنة role: الدور time_zone: النطاق الزمني user_role: diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml index eac024fa703..dbc556bf85e 100644 --- a/config/locales/simple_form.be.yml +++ b/config/locales/simple_form.be.yml @@ -374,9 +374,9 @@ be: jurisdiction: Юрысдыкцыя min_age: Мінімальны ўзрост user: - date_of_birth_1i: Дзень + date_of_birth_1i: Год date_of_birth_2i: Месяц - date_of_birth_3i: Год + date_of_birth_3i: Дзень role: Роля time_zone: Часавы пояс user_role: diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 2306109b6bd..dc6baffb154 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -231,6 +231,7 @@ bg: setting_default_language: Език на публикуване setting_default_quote_policy: Кой може да цитира setting_default_sensitive: Все да се бележи мултимедията като деликатна + setting_delete_modal: Предупреждение преди изтриване на публикация setting_disable_hover_cards: Изключване на прегледа на профила, премествайки показалеца отгоре setting_disable_swiping: Деактивиране на бързо плъзгащи движения setting_display_media: Показване на мултимедия @@ -240,6 +241,7 @@ bg: setting_emoji_style: Стил на емоджито setting_expand_spoilers: Винаги разширяване на публикации, отбелязани с предупреждения за съдържание setting_hide_network: Скриване на социалния ви свързан граф + setting_missing_alt_text_modal: Предупреждение преди публикуване на мултимедия без алтернативен текст setting_quick_boosting: Включване на бързо подсилване setting_reduce_motion: Обездвижване на анимациите setting_system_font_ui: Употреба на стандартния шрифт на системата @@ -274,6 +276,7 @@ bg: content_cache_retention_period: Период на запазване на отдалечено съдържание custom_css: Персонализиран CSS favicon: Сайтоикона + landing_page: Целева страница за нови посетители mascot: Плашило талисман по избор (остаряло) media_cache_retention_period: Период на запазване на мултимедийния кеш min_age: Минимално възрастово изискване @@ -354,9 +357,9 @@ bg: jurisdiction: Законова юрисдикция min_age: Минимална възраст user: - date_of_birth_1i: Ден + date_of_birth_1i: Година date_of_birth_2i: Месец - date_of_birth_3i: Година + date_of_birth_3i: Ден role: Роля time_zone: Часова зона user_role: diff --git a/config/locales/simple_form.br.yml b/config/locales/simple_form.br.yml index 87065f9a0b6..6be3f3d178c 100644 --- a/config/locales/simple_form.br.yml +++ b/config/locales/simple_form.br.yml @@ -109,9 +109,7 @@ br: domain: Domani jurisdiction: Barnadurezh user: - date_of_birth_1i: Devezh date_of_birth_2i: Mizvezh - date_of_birth_3i: Bloavezh role: Roll time_zone: Gwerzhid eur user_role: diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 776465c62c0..d7ced9c6f58 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -353,9 +353,9 @@ ca: jurisdiction: Jurisdicció min_age: Edat mínima user: - date_of_birth_1i: Dia + date_of_birth_1i: Any date_of_birth_2i: Mes - date_of_birth_3i: Any + date_of_birth_3i: Dia role: Rol time_zone: Zona horària user_role: diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index 8570d6ea02b..ff3f56a0d46 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -374,9 +374,9 @@ cs: jurisdiction: Právní příslušnost min_age: Věková hranice user: - date_of_birth_1i: Den + date_of_birth_1i: Rok date_of_birth_2i: Měsíc - date_of_birth_3i: Rok + date_of_birth_3i: Den role: Role time_zone: Časové pásmo user_role: diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index f53e4f16ca9..5a723d20f7e 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -376,9 +376,9 @@ cy: jurisdiction: Awdurdodaeth gyfreithiol min_age: Isafswm oedran user: - date_of_birth_1i: Dydd + date_of_birth_1i: Blwyddyn date_of_birth_2i: Mis - date_of_birth_3i: Blwyddyn + date_of_birth_3i: Diwrnod role: Rôl time_zone: Cylchfa amser user_role: diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index c4b539010b6..b442e5add0c 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -5,7 +5,7 @@ da: account: attribution_domains: Ét pr. linje. Beskytter mod falske tilskrivninger. discoverable: Dine offentlige indlæg og profil kan blive fremhævet eller anbefalet i forskellige områder af Mastodon, og profilen kan blive foreslået til andre brugere. - display_name: Dit fulde navn eller dit sjove navn. + display_name: Dit fulde navn eller et kaldenavn. fields: Din hjemmeside, dine pronominer, din alder, eller hvad du har lyst til. indexable: Dine offentlige indlæg vil kunne vises i Mastodon-søgeresultater. Folk, som har interageret med dem, vil kunne finde dem uanset. note: 'Du kan @omtale andre personer eller #hashtags.' @@ -179,7 +179,7 @@ da: name: Etiket value: Indhold indexable: Medtag offentlige indlæg i søgeresultater - show_collections: Vis følger og følgere på profil + show_collections: Vis fulgte og følgere på profil unlocked: Acceptér automatisk nye følgere account_alias: acct: Brugernavn på den gamle konto @@ -372,9 +372,9 @@ da: jurisdiction: Juridisk jurisdiktion min_age: Minimumsalder user: - date_of_birth_1i: Dag + date_of_birth_1i: År date_of_birth_2i: Måned - date_of_birth_3i: År + date_of_birth_3i: Dag role: Rolle time_zone: Tidszone user_role: diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index c3539839adc..392d7076d17 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -353,10 +353,10 @@ de: indexable: Profilseite in Suchmaschinen einbeziehen show_application: App anzeigen, über die ich einen Beitrag veröffentlicht habe tag: - listable: Erlaube, dass dieser Hashtag in Suchen und Empfehlungen erscheint + listable: Dieser Hashtag darf in Suchen und Empfehlungen erscheinen name: Hashtag - trendable: Erlaube, dass dieser Hashtag in den Trends erscheint - usable: Beiträge dürfen diesen Hashtag lokal verwenden + trendable: Dieser Hashtag darf in den Trends erscheinen + usable: Dieser Hashtag darf lokal in Beiträgen verwendet werden terms_of_service: changelog: Was hat sich geändert? effective_date: Datum des Inkrafttretens @@ -372,9 +372,9 @@ de: jurisdiction: Gerichtsstand min_age: Mindestalter user: - date_of_birth_1i: Tag + date_of_birth_1i: Jahr date_of_birth_2i: Monat - date_of_birth_3i: Jahr + date_of_birth_3i: Tag role: Rolle time_zone: Zeitzone user_role: diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index a4c600ede2b..f033d90dcfa 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -77,7 +77,7 @@ el: domain: Αυτό μπορεί να είναι το όνομα τομέα που εμφανίζεται στη διεύθυνση email ή η εγγραφή MX που χρησιμοποιεί. Θα ελέγχονται κατά την εγγραφή. with_dns_records: Θα γίνει απόπειρα ανάλυσης των εγγραφών DNS του τομέα και τα αποτελέσματα θα μπουν και αυτά σε μαύρη λίστα featured_tag: - name: 'Εδώ είναι μερικά από τα hashtags που χρησιμοποιήσατε περισσότερο πρόσφατα:' + name: 'Εδώ είναι μερικές από τις ετικέτες που χρησιμοποιήσατε περισσότερο πρόσφατα:' filters: action: Επιλέξτε ποια ενέργεια θα εκτελεστεί όταν μια ανάρτηση ταιριάζει με το φίλτρο actions: @@ -114,7 +114,7 @@ el: form_challenge: current_password: Μπαίνεις σε ασφαλή περιοχή imports: - data: Αρχείο CSV που έχει εξαχθεί από διαφορετικό κόμβο Mastodon + data: Αρχείο CSV που έχει εξαχθεί από διαφορετικό διακομιστή Mastodon invite_request: text: Αυτό θα μας βοηθήσει να επιθεωρήσουμε την αίτησή σου ip_block: @@ -173,7 +173,7 @@ el: url: Πού θα σταλούν τα γεγονότα labels: account: - attribution_domains: Ιστοσελίδες επιτρέπεται να σας αναφέρουν + attribution_domains: Ιστοσελίδες που επιτρέπεται να σας αναφέρουν discoverable: Παροχή προφίλ και αναρτήσεων σε αλγορίθμους ανακάλυψης fields: name: Περιγραφή @@ -210,7 +210,7 @@ el: text: Εξηγήστε γιατί αυτή η απόφαση πρέπει να αντιστραφεί defaults: autofollow: Προσκάλεσε για να ακολουθήσουν το λογαριασμό σου - avatar: Αβατάρ + avatar: Άβαταρ bot: Αυτός είναι ένας αυτοματοποιημένος λογαριασμός (bot) chosen_languages: Φιλτράρισμα γλωσσών confirm_new_password: Επιβεβαίωσε νέο συνθηματικό @@ -372,9 +372,9 @@ el: jurisdiction: Νομική δικαιοδοσία min_age: Ελάχιστη ηλικία user: - date_of_birth_1i: Ημέρα + date_of_birth_1i: Έτος date_of_birth_2i: Μήνας - date_of_birth_3i: Έτος + date_of_birth_3i: Ημέρα role: Ρόλος time_zone: Ζώνη ώρας user_role: diff --git a/config/locales/simple_form.en-GB.yml b/config/locales/simple_form.en-GB.yml index 7ab3d887e78..cb2c485f778 100644 --- a/config/locales/simple_form.en-GB.yml +++ b/config/locales/simple_form.en-GB.yml @@ -346,9 +346,7 @@ en-GB: jurisdiction: Legal jurisdiction min_age: Minimum age user: - date_of_birth_1i: Day date_of_birth_2i: Month - date_of_birth_3i: Year role: Role time_zone: Time Zone user_role: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 43d8e55567f..077f53cbcde 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -372,9 +372,9 @@ en: jurisdiction: Legal jurisdiction min_age: Minimum age user: - date_of_birth_1i: Day + date_of_birth_1i: Year date_of_birth_2i: Month - date_of_birth_3i: Year + date_of_birth_3i: Day role: Role time_zone: Time zone user_role: diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index f2973d7bb36..f2ae3be28ee 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -353,9 +353,9 @@ eo: jurisdiction: Laŭleĝa jurisdikcio min_age: Minimuma aĝo user: - date_of_birth_1i: Tago + date_of_birth_1i: Jaro date_of_birth_2i: Monato - date_of_birth_3i: Jaro + date_of_birth_3i: Tago role: Rolo time_zone: Horzono user_role: diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml index eb277888afb..4689dd0d79b 100644 --- a/config/locales/simple_form.es-AR.yml +++ b/config/locales/simple_form.es-AR.yml @@ -372,9 +372,9 @@ es-AR: jurisdiction: Jurisdicción legal min_age: Edad mínima user: - date_of_birth_1i: Día + date_of_birth_1i: Año date_of_birth_2i: Mes - date_of_birth_3i: Año + date_of_birth_3i: Día role: Rol time_zone: Zona horaria user_role: diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 551192480a0..ae46605da65 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -54,10 +54,10 @@ es-MX: password: Usa al menos 8 caracteres phrase: Se aplicará sin importar las mayúsculas o los avisos de contenido de una publicación scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. - setting_advanced_layout: Mostrar Mastodon con vista de varias columnas, permitiéndote ver tu cronología, notificaciones y una tercera columna que tú elijas. No recomendado para pantallas pequeñas. + setting_advanced_layout: Mostrar Mastodon en un diseño de varias columnas, lo que te permite ver la cronología, las notificaciones y una tercera columna de tu elección. No se recomienda para pantallas pequeñas. setting_aggregate_reblogs: No mostrar nuevos impulsos para las publicaciones que han sido recientemente impulsadas (sólo afecta a las publicaciones recibidas recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente - setting_boost_modal: Si está activado, impulsar una publicación abrirá una ventana donde podrás cambiar la visibilidad de tu impulso. + setting_boost_modal: Cuando está habilitado, impulsar abrirá primero un cuadro de confirmación en el que podrás cambiar la visibilidad de tu impulso. setting_default_quote_policy_private: Las publicaciones solo para seguidores hechas en Mastodon no pueden ser citadas por otros usuarios. setting_default_quote_policy_unlisted: Cuando las personas te citen, su publicación también se ocultará en las cronologías públicas. setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un clic @@ -79,7 +79,7 @@ es-MX: featured_tag: name: 'Aquí están algunas de las etiquetas que más has usado recientemente:' filters: - action: Elige qué acción realizar cuando una publicación coincide con el filtro + action: Elige qué acción realizar cuando una publicación coincida con el filtro actions: blur: Ocultar contenido multimedia detrás de una advertencia, sin ocultar el propio texto hide: Ocultar completamente el contenido filtrado, comportándose como si no existiera @@ -88,7 +88,7 @@ es-MX: activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos, y nuevos registros en periodos semanales app_icon: WEBP, PNG, GIF o JPG. Reemplaza el icono de aplicación predeterminado en dispositivos móviles con un icono personalizado. backups_retention_period: Los usuarios tienen la posibilidad de generar archivos de sus mensajes para descargarlos más adelante. Cuando se establece en un valor positivo, estos archivos se eliminarán automáticamente del almacenamiento después del número especificado de días. - bootstrap_timeline_accounts: Estas cuentas se colocarán en la parte superior de las recomendaciones de cuentas a las que seguir para los nuevos usuarios. Proporciona una lista de cuentas separadas por comas. + bootstrap_timeline_accounts: Estas cuentas aparecerán en la parte superior de las recomendaciones de seguimiento de los nuevos usuarios. Proporciona una lista de cuentas separadas por comas. closed_registrations_message: Mostrado cuando los registros están cerrados 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. @@ -372,9 +372,9 @@ es-MX: jurisdiction: Jurisdicción legal min_age: Edad mínima user: - date_of_birth_1i: Día + date_of_birth_1i: Año date_of_birth_2i: Mes - date_of_birth_3i: Año + date_of_birth_3i: Día role: Rol time_zone: Zona horaria user_role: diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index 0dfd10f4071..b56fbff243a 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -372,9 +372,9 @@ es: jurisdiction: Jurisdicción legal min_age: Edad mínima user: - date_of_birth_1i: Día + date_of_birth_1i: Año date_of_birth_2i: Mes - date_of_birth_3i: Año + date_of_birth_3i: Día role: Rol time_zone: Zona horaria user_role: diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml index 9cf4a9ad48e..92efc6dbadf 100644 --- a/config/locales/simple_form.et.yml +++ b/config/locales/simple_form.et.yml @@ -8,7 +8,7 @@ et: display_name: Su täisnimi või naljanimi. fields: Su koduleht, sugu, vanus. Mistahes, mida soovid. indexable: Sinu avalikud postitused võivad ilmuda Mastodoni otsingutulemustes. Inimesed, kes on sinu postitustele reageerinud, saavad neid otsida nii või naa. - note: 'Saad @mainida teisi inimesi või #silte.' + note: 'Saad @mainida teisi inimesi või #teemaviiteid.' show_collections: Inimesed saavad sirvida su jälgijaid ja jälgitavaid. Inimesed, keda sa jälgid, näevad seda sõltumata häälestuse valikust. unlocked: Teised kasutajad saavad sind jälgima hakata nõusolekut küsimata. Eemalda märge, kui soovid jälgimistaotlusi üle vaadata ja valida, kas nõustuda või keelduda uute jälgijatega. account_alias: @@ -16,7 +16,7 @@ et: account_migration: acct: Sisesta kasutajanimi@domeen, kuhu soovid konto siit kolida account_warning_preset: - text: Saab kasutada postituse süntaksi, näiteks URLe, silte ja mainimisi + text: Saad kasutada postituse süntaksi, näiteks võrguaadresse, teemaviiteid ja mainimisi title: Valikuline. Ei ole nähtav saajale admin_account_action: include_statuses: Kasutaja näeb, millised postitused on põhjustanud moderaatori otsuse või hoiatuse @@ -77,7 +77,7 @@ et: domain: See võib olla e-postiaadressis näha olev domeen või MX-kirje, mida aadress kasutab. Kontroll toimub liitumise käigus. with_dns_records: Püütakse lahendada selle domeeni DNS-kirjed ja ühtlasi blokeerida ka selle tulemused featured_tag: - name: 'Siin on mõned nendest siltidest, mida oled viimati kasutanud:' + name: 'Siin on mõned nendest teemaviiteid, mida oled viimati kasutanud:' filters: action: Vali tegevus, kui postitus vastab filtrile actions: @@ -110,7 +110,7 @@ et: theme: Teema, mida näevad sisenemata ning uued kasutajad. 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: Trendid näitavad, millised postitused, teemaviited ja uudislood koguvad sinu serveris tähelepanu. form_challenge: current_password: Turvalisse alasse sisenemine imports: @@ -272,7 +272,7 @@ et: email_domain_block: with_dns_records: Kaasa domeeni MX-kirjed ning IP-aadressid featured_tag: - name: Silt + name: Teemaviide filters: actions: blur: Peida hoiatusega meedia @@ -353,10 +353,10 @@ et: indexable: Kaasa profiilileht otsimootoritesse show_application: Näita, millisest äpist postituse saatsid tag: - listable: Luba sellel sildil ilmuda profiilide kataloogis - name: Silt - trendable: Luba sellel sildil trendida - usable: Luba seda märksõna postitustes kasutada lokaalselt + listable: Luba sellel teemaviitel ilmuda profiilide kataloogis + name: Teemaviide + trendable: Luba sellel teemaviitel olla nähtav viimaste trendide all + usable: Luba seda teemaviidet postitustes kasutada lokaalselt terms_of_service: changelog: Mis on muutunud? effective_date: Jõustumise kuupäev @@ -372,9 +372,9 @@ et: jurisdiction: Jurisdiktsioon min_age: Vanuse alampiir user: - date_of_birth_1i: Päev + date_of_birth_1i: Aasta date_of_birth_2i: Kuu - date_of_birth_3i: Aasta + date_of_birth_3i: Päev role: Roll time_zone: Ajavöönd user_role: diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml index 3a0a8b6741d..26888fd24f2 100644 --- a/config/locales/simple_form.eu.yml +++ b/config/locales/simple_form.eu.yml @@ -325,9 +325,7 @@ eu: terms_of_service_generator: domain: Domeinua user: - date_of_birth_1i: Eguna date_of_birth_2i: Hilabetea - date_of_birth_3i: Urtea role: Rola time_zone: Ordu zona user_role: diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml index 497c67b4164..20afb20faef 100644 --- a/config/locales/simple_form.fa.yml +++ b/config/locales/simple_form.fa.yml @@ -54,8 +54,10 @@ fa: password: دست‌کم باید ۸ نویسه داشته باشد phrase: مستقل از کوچکی و بزرگی حروف، با متن اصلی یا هشدار محتوای فرسته‌ها مقایسه می‌شود scopes: واسط‌های برنامه‌نویسی که این برنامه به آن دسترسی دارد. اگر بالاترین سطح دسترسی را انتخاب کنید، دیگر نیازی به انتخاب سطح‌های پایینی ندارید. + setting_advanced_layout: نمایش ماستودون به شکل چندستونه که می‌گذارد خط زمانی، آگاهی‌ها رو ستون سومی را به دلخواه ببینید. برای صفحه‌های کوچک‌تر توصیه نمی‌شود. setting_aggregate_reblogs: برای تقویت‌هایی که به تازگی برایتان نمایش داده شده‌اند، تقویت‌های بیشتر را نمایش نده (فقط روی تقویت‌های اخیر تأثیر می‌گذارد) setting_always_send_emails: در حالت عادی آگاهی‌های رایانامه‌ای هنگامی که فعّالانه از ماستودون استفاده می‌کنید فرستاده نمی‌شوند + setting_boost_modal: هنگام به کار افتادن تقویت نخست گفت‌وگوی تأییدی خواهد گشود که می‌توانید نمایانی تقویتتان را تغییر دهید. setting_default_quote_policy_private: فرسته‌های فقط پی‌گیران روی ماستودون نمی‌توانند به دست دیگران نقل شوند. setting_default_quote_policy_unlisted: هنگامی که کسی نقلتان می‌کند هم فرسته‌اش از خط زمانی‌های داغ پنهان خواهد بود. setting_default_sensitive: تصاویر حساس به طور پیش‌فرض پنهان هستند و می‌توانند با یک کلیک آشکار شوند @@ -63,6 +65,7 @@ fa: 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: به جای پیش‌رفتن خودکار در فهرست، به‌روزرسانی فهرست نوشته‌ها را پشت یک کلیک پنهان کن @@ -76,6 +79,7 @@ fa: featured_tag: name: 'این‌ها برخی از برچسب‌هایی هستند که به تازگی استفاده کرده‌اید:' filters: + action: گزینش کنشی که هنگام تطابق فرسته‌ای با پالایه انجام شود actions: blur: نهفتن رسانه پشت هشدار بدون نهفتن خود متن hide: نهفتن کامل محتوای پالوده، گویی وجود ندارد @@ -84,10 +88,12 @@ fa: activity_api_enabled: تعداد بوق‌های منتشرهٔ محلی، کاربران فعال، و کاربران تازه در هر هفته app_icon: WEBP، PNG، GIF یا JPG. با یک نماد سفارشی، نماد برنامه پیش‌فرض را در دستگاه‌های تلفن همراه لغو می‌کند. backups_retention_period: کاربران می توانند بایگانی هایی از پست های خود ایجاد کنند تا بعدا دانلود کنند. وقتی روی مقدار مثبت تنظیم شود، این بایگانی‌ها پس از تعداد روزهای مشخص شده به‌طور خودکار از فضای ذخیره‌سازی شما حذف می‌شوند. + bootstrap_timeline_accounts: این حساب‌ها به بالای توصیه‌های پی‌گیری کاربران جدید سنجاق خواهند شد. سیاهه‌ای از حساب‌ها جدا شده با کاما فراهم کنید. closed_registrations_message: نمایش داده هنگام بسته بودن ثبت‌نام‌ها - content_cache_retention_period: همه پست‌های سرورهای دیگر (از جمله تقویت‌کننده‌ها و پاسخ‌ها) پس از چند روز مشخص شده، بدون توجه به هرگونه تعامل کاربر محلی با آن پست‌ها، حذف خواهند شد. این شامل پست هایی می شود که یک کاربر محلی آن را به عنوان نشانک یا موارد دلخواه علامت گذاری کرده است. ذکر خصوصی بین کاربران از نمونه های مختلف نیز از بین خواهد رفت و بازیابی آنها غیرممکن است. استفاده از این تنظیم برای موارد با هدف خاص در نظر گرفته شده است و بسیاری از انتظارات کاربر را هنگامی که برای استفاده عمومی اجرا می شود، از بین می برد. + content_cache_retention_period: پس از تعداد روزهای مشخّص شده تمامی فرسته‌ها از دیگر کارسازها (از جمله تقویت‌ها و پاسخ‌ها) بدون توجّه به هر تعامل کاربری محلی با آن‌ها حذف خواهند شد. این موضوع شامل حال فرسته‌هایی که کاربران محلی به عنوان نشانک یا برگزیده علامت زده‌اند نیز می‌شود. اشاره‌های خصوصی بین کاربران نمونه‌های مختلف نیزی از دست رفته و قابل بازگردانی نخواهند بود. استفاده از این تنظیم برای نمونه‌هایی با هدف خاص در نظر گرفته شده و برای استفادهٔ عمومی بسیاری از انتظارات کاربر را زیر پا می‌گذارد. custom_css: می‌توانیدروی نگارش وب ماستودون سبک‌های سفارشی اعمال کنید. favicon: WEBP، PNG، GIF یا JPG. فاویکون پیش‌فرض ماستودون را با یک نماد سفارشی لغو می‌کند. + landing_page: گزینش صفحه‌ای که بینندگان هنگام نخستین بازدید از کارسازتان می‌بینند. اگر «داغ‌ها» را بگزینید باید داغ‌ها در تنظمیات کشف به کار افتاده باشند. اگر «خوراک محلی» را بگزینید باید «دسترسی به خوراک‌های محلی» در تنظیمات کشف روی «هرکسی» تنظیم شده باشد. mascot: نقش میانای وب پیش‌رفته را پایمال می‌کند. media_cache_retention_period: فایل های رسانه ای از پست های ارسال شده توسط کاربران راه دور در سرور شما ذخیره می شوند. وقتی روی مقدار مثبت تنظیم شود، رسانه پس از تعداد روزهای مشخص حذف می شود. اگر داده‌های رسانه پس از حذف درخواست شود، در صورتی که محتوای منبع هنوز در دسترس باشد، مجدداً بارگیری می‌شود. با توجه به محدودیت‌هایی که در مورد تعداد دفعات نظرسنجی کارت‌های پیش‌نمایش پیوند از سایت‌های شخص ثالث وجود دارد، توصیه می‌شود این مقدار را حداقل 14 روز تنظیم کنید، در غیر این صورت کارت‌های پیش‌نمایش پیوند قبل از آن زمان به‌روزرسانی نمی‌شوند. min_age: در طول ثبت‌نام از کاربران خواسته خواهد شد که تاریخ تولَدشان را تأیید کنند @@ -366,9 +372,7 @@ fa: jurisdiction: صلاحیت قانونی min_age: کمینهٔ زمان user: - date_of_birth_1i: روز date_of_birth_2i: ماه - date_of_birth_3i: سال role: نقش time_zone: منطقهٔ زمانی user_role: diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 99c1b79bbe0..63d63b32917 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -372,9 +372,9 @@ fi: jurisdiction: Lainkäyttöalue min_age: Vähimmäisikä user: - date_of_birth_1i: Päivä + date_of_birth_1i: Vuosi date_of_birth_2i: Kuukausi - date_of_birth_3i: Vuosi + date_of_birth_3i: Päivä role: Rooli time_zone: Aikavyöhyke user_role: diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index de563ec5279..15465fff9ce 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -372,9 +372,9 @@ fo: jurisdiction: Løgdømi min_age: Lægsti aldur user: - date_of_birth_1i: Dagur + date_of_birth_1i: Ár date_of_birth_2i: Mánaði - date_of_birth_3i: Ár + date_of_birth_3i: Dagur role: Leiklutur time_zone: Tíðarsona user_role: diff --git a/config/locales/simple_form.fr-CA.yml b/config/locales/simple_form.fr-CA.yml index e064ceeed16..2fc9fd53145 100644 --- a/config/locales/simple_form.fr-CA.yml +++ b/config/locales/simple_form.fr-CA.yml @@ -54,6 +54,7 @@ fr-CA: password: Utilisez au moins 8 caractères phrase: Sera filtré peu importe la casse ou l’avertissement de contenu du message scopes: À quelles APIs l’application sera autorisée à accéder. Si vous sélectionnez une permission générale, vous n’avez pas besoin de sélectionner les permissions plus précises. + setting_advanced_layout: Afficher Mastodon avec une mise en page multicolonnes, vous permettant de visualiser le flux, les notifications et une troisième colonne de votre choix. Non recommandé pour les écrans plus petits. setting_aggregate_reblogs: Ne pas afficher les nouveaux partages pour les messages déjà récemment partagés (n’affecte que les partages futurs) setting_always_send_emails: Normalement, les notifications par courriel ne seront pas envoyées lorsque vous utilisez Mastodon activement setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. @@ -152,6 +153,8 @@ fr-CA: name: Nom public du rôle, si le rôle est configuré pour être affiché avec un badge permissions_as_keys: Les utilisateur·rice·s ayant ce rôle auront accès à … position: Dans certaines situations, un rôle supérieur peut trancher la résolution d'un conflit. Mais certaines opérations ne peuvent être effectuées que sur des rôles ayant une priorité inférieure + username_block: + comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles webhook: events: Sélectionnez les événements à envoyer template: Écrivez votre propre bloc JSON avec la possibilité d’utiliser de l’interpolation de variables. Laissez vide pour le bloc JSON par défaut. @@ -353,9 +356,8 @@ fr-CA: jurisdiction: Juridiction min_age: Âge minimum user: - date_of_birth_1i: Jour date_of_birth_2i: Mois - date_of_birth_3i: Année + date_of_birth_3i: Jour role: Rôle time_zone: Fuseau horaire user_role: diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index dda65ff2886..2b996ce94a3 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -31,7 +31,7 @@ fr: suspend: Empêcher toute interaction depuis ou vers ce compte et supprimer son contenu. Réversible dans les 30 jours. Cloture tous les signalements concernant ce compte. warning_preset_id: Facultatif. Vous pouvez toujours ajouter un texte personnalisé à la fin de la présélection announcement: - all_day: Coché, seules les dates de l’intervalle de temps seront affichées + all_day: Lorsque coché, seules les dates de l’intervalle de temps seront affichées ends_at: Facultatif. La fin de l'annonce surviendra automatiquement à ce moment scheduled_at: Laisser vide pour publier l’annonce immédiatement starts_at: Facultatif. Si votre annonce est liée à une période spécifique @@ -54,6 +54,7 @@ fr: password: Utilisez au moins 8 caractères phrase: Sera filtré peu importe la casse ou l’avertissement de contenu du message scopes: À quelles APIs l’application sera autorisée à accéder. Si vous sélectionnez une permission générale, vous n’avez pas besoin de sélectionner les permissions plus précises. + setting_advanced_layout: Afficher Mastodon avec une mise en page multicolonnes, vous permettant de visualiser le flux, les notifications et une troisième colonne de votre choix. Non recommandé pour les écrans plus petits. setting_aggregate_reblogs: Ne pas afficher les nouveaux partages pour les messages déjà récemment partagés (n’affecte que les partages futurs) setting_always_send_emails: Normalement, les notifications par courriel ne seront pas envoyées lorsque vous utilisez Mastodon activement setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. @@ -152,6 +153,8 @@ fr: name: Nom public du rôle, si le rôle est configuré pour être affiché avec un badge permissions_as_keys: Les utilisateur·rice·s ayant ce rôle auront accès à … position: Dans certaines situations, un rôle supérieur peut trancher la résolution d'un conflit. Mais certaines opérations ne peuvent être effectuées que sur des rôles ayant une priorité inférieure + username_block: + comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles webhook: events: Sélectionnez les événements à envoyer template: Écrivez votre propre bloc JSON avec la possibilité d’utiliser de l’interpolation de variables. Laissez vider pour utiliser le bloc JSON par défaut. @@ -353,9 +356,8 @@ fr: jurisdiction: Juridiction min_age: Âge minimum user: - date_of_birth_1i: Jour date_of_birth_2i: Mois - date_of_birth_3i: Année + date_of_birth_3i: Jour role: Rôle time_zone: Fuseau horaire user_role: diff --git a/config/locales/simple_form.fy.yml b/config/locales/simple_form.fy.yml index 6507b94ab87..7b88eee24f6 100644 --- a/config/locales/simple_form.fy.yml +++ b/config/locales/simple_form.fy.yml @@ -349,9 +349,7 @@ fy: jurisdiction: Rjochtsgebiet min_age: Minimum leeftiid user: - date_of_birth_1i: Dei date_of_birth_2i: Moanne - date_of_birth_3i: Jier role: Rol time_zone: Tiidsône user_role: diff --git a/config/locales/simple_form.ga.yml b/config/locales/simple_form.ga.yml index d5e70f08929..b3d6081ffd7 100644 --- a/config/locales/simple_form.ga.yml +++ b/config/locales/simple_form.ga.yml @@ -375,9 +375,9 @@ ga: jurisdiction: Dlínse dhlíthiúil min_age: Aois íosta user: - date_of_birth_1i: Lá + date_of_birth_1i: Bliain date_of_birth_2i: Mí - date_of_birth_3i: Bliain + date_of_birth_3i: Lá role: Ról time_zone: Crios ama user_role: diff --git a/config/locales/simple_form.gd.yml b/config/locales/simple_form.gd.yml index 54d1be91b79..e11b300b33a 100644 --- a/config/locales/simple_form.gd.yml +++ b/config/locales/simple_form.gd.yml @@ -373,9 +373,7 @@ gd: jurisdiction: Uachdranas laghail min_age: An aois as lugha user: - date_of_birth_1i: Latha date_of_birth_2i: Mìos - date_of_birth_3i: Bliadhna role: Dreuchd time_zone: Roinn-tìde user_role: diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 3a91a26c114..b8969680571 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -88,6 +88,7 @@ gl: activity_api_enabled: Conta do número de publicacións locais, usuarias activas, e novos rexistros en acumulados semanais app_icon: WEBP, PNG, GIF ou JPG. Sobrescribe a icona por defecto da aplicación nos dispositivos móbiles cunha icona personalizada. backups_retention_period: As usuarias poden crear arquivos das súas publicacións para descargalos. Cando se establece un valor positivo, estes arquivos serán borrados automáticamente da túa almacenaxe despois do número de días establecido. + bootstrap_timeline_accounts: Vas fixar estas contas na parte superior das recomendacións de seguimento. Escribe unha lista de contas separadas por vírgulas. closed_registrations_message: Móstrase cando non se admiten novas usuarias 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. @@ -371,9 +372,9 @@ gl: jurisdiction: Xurisdición legal min_age: Idade mínima user: - date_of_birth_1i: Día + date_of_birth_1i: Ano date_of_birth_2i: Mes - date_of_birth_3i: Ano + date_of_birth_3i: Día role: Rol time_zone: Fuso horario user_role: diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 492d27bea0e..cdf1300a4fb 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -374,9 +374,9 @@ he: jurisdiction: איזור השיפוט min_age: גיל מינימלי user: - date_of_birth_1i: יום + date_of_birth_1i: שנה date_of_birth_2i: חודש - date_of_birth_3i: שנה + date_of_birth_3i: יום role: תפקיד time_zone: אזור זמן user_role: diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index f5dcf3b96a5..aa118bf5d6c 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -79,6 +79,7 @@ hu: featured_tag: name: 'Itt vannak azok a hashtagek, melyeket legutóbb használtál:' filters: + action: Válassza ki a végrehajtandó műveletet, ha a bejegyzés megfelel a szűrőnek actions: blur: Média elrejtése figyelmeztetéssel, a szöveg elrejtése nélkül hide: A szűrt tartalom teljes elrejtése, mintha nem is létezne @@ -87,6 +88,7 @@ hu: activity_api_enabled: Helyi bejegyzések, aktív felhasználók és új regisztrációk száma heti bontásban app_icon: WEBP, PNG, GIF vagy JPG. Mobileszközökön az alkalmazás alapértelmezett ikonját felülírja egy egyéni ikonnal. backups_retention_period: A felhasználók archívumokat állíthatnak elő a bejegyzéseikből, hogy később letöltsék azokat. Ha pozitív értékre van állítva, akkor a megadott számú nap után automatikusan törölve lesznek a tárhelyedről. + bootstrap_timeline_accounts: Ezek a fiókok rögzítve lesznek az új felhasználók követési ajánlásai tetején. Add meg a fiókok vesszővel elválasztott listáját. closed_registrations_message: Akkor jelenik meg, amikor a regisztráció le van zárva content_cache_retention_period: Minden más kiszolgálóról származó bejegyzés (megtolásokkal és válaszokkal együtt) törölve lesz a megadott számú nap elteltével, függetlenül a helyi felhasználók ezekkel a bejegyzésekkel történő interakcióitól. Ebben azok a bejegyzések is benne vannak, melyeket a helyi felhasználó könyvjelzőzött vagy kedvencnek jelölt. A különböző kiszolgálók felhasználói közötti privát üzenetek is el fognak veszni visszaállíthatatlanul. Ennek a beállításnak a használata különleges felhasználási esetekre javasolt, mert számos felhasználói elvárás fog eltörni, ha általános céllal használják. custom_css: A Mastodon webes verziójában használhatsz egyéni stílusokat. @@ -370,9 +372,9 @@ hu: jurisdiction: Joghatóság min_age: Minimális életkor user: - date_of_birth_1i: Nap + date_of_birth_1i: Év date_of_birth_2i: Hónap - date_of_birth_3i: Év + date_of_birth_3i: Nap role: Szerep time_zone: Időzóna user_role: diff --git a/config/locales/simple_form.ia.yml b/config/locales/simple_form.ia.yml index b8231e2e14d..39992a569e9 100644 --- a/config/locales/simple_form.ia.yml +++ b/config/locales/simple_form.ia.yml @@ -370,9 +370,7 @@ ia: jurisdiction: Jurisdiction min_age: Etate minime user: - date_of_birth_1i: Die date_of_birth_2i: Mense - date_of_birth_3i: Anno role: Rolo time_zone: Fuso horari user_role: diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml index 9795077ca13..b4f034bda4b 100644 --- a/config/locales/simple_form.is.yml +++ b/config/locales/simple_form.is.yml @@ -372,9 +372,9 @@ is: jurisdiction: Lögsagnarumdæmi min_age: Lágmarksaldur user: - date_of_birth_1i: Dagur + date_of_birth_1i: Ár date_of_birth_2i: Mánuður - date_of_birth_3i: Ár + date_of_birth_3i: Dagur role: Hlutverk time_zone: Tímabelti user_role: diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 8ce108ba960..9dd05272b73 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -372,9 +372,9 @@ it: jurisdiction: Giurisdizione legale min_age: Età minima user: - date_of_birth_1i: Giorno + date_of_birth_1i: Anno date_of_birth_2i: Mese - date_of_birth_3i: Anno + date_of_birth_3i: Giorno role: Ruolo time_zone: Fuso orario user_role: diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 3dc39069974..9025e16b18a 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -348,9 +348,7 @@ ja: jurisdiction: 裁判管轄 min_age: 登録可能な最低年齢 user: - date_of_birth_1i: 日 date_of_birth_2i: 月 - date_of_birth_3i: 年 role: ロール time_zone: タイムゾーン user_role: diff --git a/config/locales/simple_form.kab.yml b/config/locales/simple_form.kab.yml index 6f3acf712b7..cf66a051306 100644 --- a/config/locales/simple_form.kab.yml +++ b/config/locales/simple_form.kab.yml @@ -158,9 +158,7 @@ kab: terms_of_service_generator: domain: Taɣult user: - date_of_birth_1i: Ass date_of_birth_2i: Ayyur - date_of_birth_3i: Aseggas role: Tamlilt time_zone: Tamnaḍt tasragant user_role: diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index be7a506333e..2f0e09bc94d 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -78,6 +78,7 @@ ko: featured_tag: name: '이것들은 최근에 많이 쓰인 해시태그들입니다:' filters: + action: 게시물이 필터에 걸러질 때 어떤 동작을 수행할 지 고르세요 actions: blur: 텍스트는 숨기지 않고 그대로 둔 채 경고 뒤에 미디어를 숨김니다 hide: 필터에 걸러진 글을 처음부터 없었던 것처럼 완전히 가리기 @@ -366,9 +367,9 @@ ko: jurisdiction: 법적 관할권 min_age: 최소 연령 user: - date_of_birth_1i: 일 + date_of_birth_1i: 년 date_of_birth_2i: 월 - date_of_birth_3i: 년 + date_of_birth_3i: 일 role: 역할 time_zone: 시간대 user_role: diff --git a/config/locales/simple_form.ku.yml b/config/locales/simple_form.ku.yml index 52e9d6b234c..a2f7b2b9b12 100644 --- a/config/locales/simple_form.ku.yml +++ b/config/locales/simple_form.ku.yml @@ -260,6 +260,8 @@ ku: name: Hashtag trendable: Bihêle ku ev hashtag werê xuyakirin di bin rojevê de user: + date_of_birth_1i: Sal + date_of_birth_3i: Roj role: Rol user_role: color: Rengê nîşanê diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index 604f5173b88..35fb5fb9f42 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -313,9 +313,7 @@ lad: domain: Domeno min_age: Edad minima user: - date_of_birth_1i: Diya date_of_birth_2i: Mez - date_of_birth_3i: Anyo role: Rolo time_zone: Zona de tiempo user_role: diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index f5f7b582c28..4e88520f846 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -246,9 +246,7 @@ lt: jurisdiction: Teisinis teismingumas min_age: Mažiausias amžius user: - date_of_birth_1i: Diena date_of_birth_2i: Mėnuo - date_of_birth_3i: Metai role: Vaidmuo time_zone: Laiko juosta user_role: diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 41d879a3743..8475de9f5b8 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -54,7 +54,7 @@ lv: password: Izmanto vismaz 8 rakstzīmes phrase: Tiks saskaņots neatkarīgi no ziņas teksta reģistra vai satura brīdinājuma scopes: Kuriem API lietotnei būs ļauts piekļūt. Ja atlasa augstākā līmeņa tvērumu, nav nepieciešamas atlasīt atsevišķus. - setting_aggregate_reblogs: Nerādīt jaunus izcēlumus ziņām, kas nesen tika palielinātas (ietekmē tikai nesen saņemtos palielinājumus) + setting_aggregate_reblogs: Nerādīt jaunus pastiprinājumus ierakstiem, kas nesen tikuši pastiprināti (ietekmēs tikai turpmāk saņemtos pastiprinājumus) setting_always_send_emails: Parasti e-pasta paziņojumi netiek sūtīti, kad aktīvi izmantojat Mastodon setting_default_sensitive: Pēc noklusējuma jūtīgi informācijas nesēji ir paslēpti, un tos var atklāt ar klikšķi setting_display_media_default: Paslēpt attēlus un video, kas atzīmēti kā jūtīgi @@ -212,7 +212,7 @@ lv: password: Parole phrase: Atslēgvārds vai frāze setting_advanced_layout: Iespējot paplašināto tīmekļa saskarni - setting_aggregate_reblogs: Grupēt izcēlumus ierakstu lentās + setting_aggregate_reblogs: Grupēt pastiprinājumus ierakstu lentās setting_always_send_emails: Vienmēr sūtīt e-pasta paziņojumus setting_auto_play_gif: Automātiski atskaņot animētos GIF setting_default_language: Publicēšanas valoda @@ -303,6 +303,7 @@ lv: follow_request: Kāds vēlas Tev sekot mention: Kāds pieminēja tevi pending_account: Jāpārskata jaunu kontu + quote: Kāds tevi citēja reblog: Kāds izcēla tavu ierakstu report: Tika iesniegts jauns ziņojums software_updates: @@ -334,9 +335,7 @@ lv: domain: Domēna vārds min_age: Mazākais pieļaujamais vecums user: - date_of_birth_1i: Diena date_of_birth_2i: Mēnesis - date_of_birth_3i: Gads role: Loma time_zone: Laika josla user_role: diff --git a/config/locales/simple_form.nan.yml b/config/locales/simple_form.nan.yml index d9049a784b4..08ae2a55df5 100644 --- a/config/locales/simple_form.nan.yml +++ b/config/locales/simple_form.nan.yml @@ -9,3 +9,18 @@ nan: password: 用 8 ê字元以上 setting_display_media_hide_all: 一直khàm掉媒體 setting_display_media_show_all: 一直展示媒體 + labels: + terms_of_service: + changelog: Siánn物有改? + effective_date: 有效ê日期 + text: 服務規定 + terms_of_service_generator: + admin_email: 法律通知用ê電子phue地址 + arbitration_address: 仲裁通知ê實濟地址 + arbitration_website: 送仲裁通知ê網站 + choice_of_law: 法律ê選擇 + dmca_address: DMCA/版權通知ê實際地址 + dmca_email: DMCA/版權通知ê電子phue地址 + domain: 域名 + jurisdiction: 司法管轄區 + min_age: 最少年紀 diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 5f1cc115e95..c0aa3f692e5 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -88,7 +88,7 @@ nl: activity_api_enabled: Aantallen lokaal gepubliceerde berichten, actieve gebruikers en nieuwe registraties per week app_icon: WEBP, PNG, GIF of JPG. Vervangt op mobiele apparaten het standaard app-pictogram met een aangepast pictogram. backups_retention_period: Gebruikers hebben de mogelijkheid om archieven van hun berichten te genereren om later te downloaden. Indien ingesteld op een positieve waarde, worden deze archieven automatisch verwijderd uit jouw opslag na het opgegeven aantal dagen. - bootstrap_timeline_accounts: Deze accounts worden bovenaan de lijst aanbevolen om te volgen van nieuwe gebruikers vastgezet. Geef een komma gescheiden lijst van accounts. + bootstrap_timeline_accounts: Deze accounts worden bovenaan de aanbevelingen aan nieuwe gebruikers getoond. Meerdere gebruikersnamen met komma's scheiden. closed_registrations_message: Weergegeven wanneer registratie van nieuwe accounts is uitgeschakeld 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. @@ -372,9 +372,7 @@ nl: jurisdiction: Jurisdictie min_age: Minimumleeftijd user: - date_of_birth_1i: Dag date_of_birth_2i: Maand - date_of_birth_3i: Jaar role: Rol time_zone: Tijdzone user_role: diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index 6af7f43de3a..b7b1021cd1d 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -88,6 +88,7 @@ nn: activity_api_enabled: Tal på lokale innlegg, aktive brukarar og nyregistreringar kvar veke app_icon: WEBP, PNG, GIF eller JPG. Overstyrer standard-app-ikonet på mobile einingar med eit eigendefinert ikon. backups_retention_period: Brukarar har moglegheit til å generere arkiv av sine innlegg for å laste ned seinare. Når sett til ein positiv verdi, blir desse arkiva automatisk sletta frå lagringa etter eit gitt antal dagar. + bootstrap_timeline_accounts: Desse brukarkontoane vil bli festa på toppen av tilrådingane for nye brukarar. Skriv inn ei kommadelt liste med brukarkontoar. closed_registrations_message: Vist når det er stengt for registrering content_cache_retention_period: Alle innlegg frå andre tenarar (inkludert framhevingar og svar) vil bli sletta etter talet på dagar du skriv inn, uansett om nokon har samhandla med desse innlegga. Dette inkluderer innlegg der ein lokal brukar har merka det som bokmerke eller som favoritt. Private omtalar mellom brukarar frå ulike nettstader vil gå tapt og vera umogleg å gjenskapa. Bruk av denne innstillinga er meint for spesielle nettstader og bryt med det mange forventar av ein vanleg nettstad. custom_css: Du kan bruka eigendefinerte stilar på nettversjonen av Mastodon. @@ -371,9 +372,7 @@ nn: jurisdiction: Rettskrins min_age: Minstealder user: - date_of_birth_1i: Dag date_of_birth_2i: Månad - date_of_birth_3i: År role: Rolle time_zone: Tidssone user_role: diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index a8518eecc20..192a30a53ae 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -370,9 +370,7 @@ pl: jurisdiction: Jurysdykcja min_age: Wiek minimalny user: - date_of_birth_1i: Dzień date_of_birth_2i: Miesiąc - date_of_birth_3i: Rok role: Rola time_zone: Strefa czasowa user_role: diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 1d3bd5eb1d9..8de2c8b04aa 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -79,6 +79,7 @@ pt-BR: featured_tag: name: 'Aqui estão algumas hashtags usadas recentemente:' filters: + action: Escolha qual ação performar quando uma postagem coincidir com o filtro actions: blur: Oculte a mídia com um aviso, porém mantenha o texto visível hide: Esconder completamente o conteúdo filtrado, comportando-se como se ele não existisse @@ -87,6 +88,7 @@ pt-BR: activity_api_enabled: Contagem de publicações locais, usuários ativos e novos usuários semanais app_icon: WEBP, PNG, GIF ou JPG. Sobrescrever o ícone padrão do aplicativo em dispositivos móveis com um ícone personalizado. backups_retention_period: Os usuários podem gerar arquivos de suas postagens para baixar mais tarde. Quando definido como um valor positivo, esses arquivos serão automaticamente excluídos do seu armazenamento após o número especificado de dias. + bootstrap_timeline_accounts: Estas contas serão fixadas ao topo das recomendações de novos usuários. Forneça uma lista de contas separada por vírgulas. closed_registrations_message: Exibido quando as inscrições estiverem fechadas content_cache_retention_period: Todas as postagens de outros servidores (incluindo boosts e respostas) serão excluídas após o número especificado de dias, sem levar a qualquer interação do usuário local com esses posts. Isto inclui postagens onde um usuário local o marcou como favorito ou favoritos. Menções privadas entre usuários de diferentes instâncias também serão perdidas e impossíveis de restaurar. O uso desta configuração destina-se a instâncias especiais de propósitos e quebra muitas expectativas dos usuários quando implementadas para uso de propósito geral. custom_css: Você pode aplicar estilos personalizados na versão da web do Mastodon. @@ -251,7 +253,7 @@ pt-BR: 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_quick_boosting: Ativar aceleração rápida + setting_quick_boosting: Ativar impulsionamento rápido 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 @@ -370,9 +372,9 @@ pt-BR: jurisdiction: Jurisdição legal min_age: Idade mínima user: - date_of_birth_1i: Dia + date_of_birth_1i: Ano date_of_birth_2i: Mês - date_of_birth_3i: Ano + date_of_birth_3i: Dia role: Cargo time_zone: Fuso horário user_role: diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index a11d63bfa15..608e27a90fb 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -372,9 +372,9 @@ pt-PT: jurisdiction: Jurisdição legal min_age: Idade mínima user: - date_of_birth_1i: Dia + date_of_birth_1i: Ano date_of_birth_2i: Mês - date_of_birth_3i: Ano + date_of_birth_3i: Dia role: Função time_zone: Fuso horário user_role: diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 2f1f8e03e4b..568a45f16c9 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -19,23 +19,23 @@ ru: text: Вы можете использовать всё то же самое, что и в обычных постах, — ссылки, хештеги и упоминания title: Необязательно. Не видно получателю admin_account_action: - include_statuses: Пользователь будет видеть к каким постами применялись модераторские действия и выносились предупреждения + include_statuses: Пользователь увидит, какие посты стали причиной действий модераторов либо вынесения предупреждения send_email_notification: Пользователь получит сообщение о том, что случилось с его/её учётной записью text_html: Необязательно. Можно использовать всё то же самое, что и в обычных постах. Для экономии времени вы можете добавить шаблоны предупреждений - type_html: Выберите, какую санкцию вынести в отношении %{acct} + type_html: Выберите, какое действие применить к %{acct} types: - disable: Запретить пользователю использование своей учётной записи, без удаления или скрытия контента. - none: Отправить пользователю предупреждение, не принимая иных действий. - sensitive: Принудительно отметить опубликованное пользователем содержимое как «деликатного характера». - silence: Запретить пользователю публиковать посты с открытой видимостью, а также скрыть все прошлые посты и уведомления от людей, не читающих этого пользователя. Закрыть все отчеты по этому счету. - suspend: Предотвратить любое взаимодействие с этой учётной записью, удалив всё содержимое опубликованное с неё. Это действие можно отменить в течение 30 дней. Закрывает все отчеты против этого аккаунта. + disable: Запретить пользователю использовать свою учётную запись, при этом не удаляя и не скрывая саму учётную запись. + none: Отправить пользователю предупреждение, не принимая никаких других действий. + sensitive: Принудительно отмечать все медиавложения пользователя как содержимое деликатного характера. + silence: Пользователю будет запрещено создавать посты с публичной видимостью, а увидеть уведомления от этого пользователя и его посты смогут только подписчики. Все жалобы на эту учётную запись будут отмечены как решённые. + suspend: Запретить любое взаимодействие с этой учётной записью и удалить все связанные с ней данные. Это действие можно отменить в течение 30 дней. Все жалобы на эту учётную запись будут отмечены как решённые. warning_preset_id: Необязательно. Вы по-прежнему сможете добавить собственный текст в конец шаблона announcement: all_day: Если выбрано, часы начала и завершения будут скрыты ends_at: Необязательно. Объявление будет автоматически отменено в это время scheduled_at: Оставьте поля незаполненными, чтобы опубликовать объявление сразу starts_at: Необязательно. На случай, если ваше объявление привязано к какому-то временному интервалу - text: Вы можете использовать тот же синтаксис, что и в постах. Будьте предусмотрительны насчёт места, которое займёт объявление на экране пользователей + text: Вы можете использовать всё то же самое, что и в обычных постах. Пожалуйста, не забывайте о пространстве, которое объявление будет занимать на экране пользователя appeal: text: Вы можете обжаловать замечание только один раз defaults: @@ -54,12 +54,18 @@ ru: password: Пароль должен состоять минимум из 8 символов phrase: Поиск соответствия будет выполнен без учёта регистра в тексте поста и предупреждения о содержании scopes: Выберите, какие API приложение сможет использовать. Разрешения верхнего уровня имплицитно включают в себя все разрешения более низких уровней. + setting_advanced_layout: Использовать многоколоночный интерфейс Mastodon, который позволяет одновременно открыть ленту, уведомления и третью колонку по вашему выбору. Не рекомендуется для маленьких экранов. setting_aggregate_reblogs: Не показывать новые продвижения постов, которые уже были недавно продвинуты (применяется только к будущим продвижениям) setting_always_send_emails: По умолчанию уведомления не доставляются по электронной почте, пока вы активно используете Mastodon + setting_boost_modal: Отметьте флажок, чтобы при продвижении поста открывать окно подтверждения, в котором вы сможете изменить видимость вашего продвижения. + setting_default_quote_policy_private: Посты, созданные в Mastodon с видимостью только для подписчиков, не могут быть процитированы другими пользователями. + 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: Как отображать эмодзи. Если выбран вариант «Автоматически», то будут использованы системные эмодзи, а для устаревших браузеров — Twemoji. + setting_quick_boosting_html: Отметьте флажок, чтобы при нажатии на кнопку %{boost_icon} Продвинуть не выбирать между продвижением и цитированием, а сразу продвигать пост. Цитирование будет доступно из меню поста (%{options_icon}). setting_system_scrollbars_ui: Работает только в браузерах для ПК на основе Safari или Chrome setting_use_blurhash: Градиенты основаны на цветах скрытых медиа, но размывают любые детали setting_use_pending_items: Отметьте флажок, чтобы выключить автоматическую прокрутку, и тогда обновления в лентах будут вам показаны только по нажатию @@ -73,18 +79,21 @@ ru: featured_tag: name: 'Вот некоторые хештеги, которые вы использовали чаще других в последнее время:' filters: + action: Выберите действие, которое нужно применить к постам, соответствующим фильтру actions: blur: Скрыть медиа за предупреждением, не скрывая сам текст поста hide: Полностью скрыть отфильтрованный пост, будто бы его не существует warn: Скрыть отфильтрованный пост за предупреждением с указанием названия фильтра form_admin_settings: - activity_api_enabled: Подсчёт количества локальных постов, активных пользователей и новых регистраций на еженедельной основе - app_icon: WEBP, PNG, GIF или JPG. Замените значок приложения по умолчанию на мобильных устройствах пользовательским значком. - backups_retention_period: Пользователи могут создавать архивы своих постов, чтобы потом их забрать. Если задать положительное значение, эти архивы автоматически удалятся с вашего хранилища через указанное число дней. + activity_api_enabled: Еженедельная выгрузка количества локальных постов, активных пользователей и новых регистраций + app_icon: WEBP, PNG, GIF или JPG. Заменяет значок приложения на мобильных устройствах по умолчанию вашим значком. + backups_retention_period: Пользователи могут запустить создание архива своих постов, чтобы скачать его позже. Если задать положительное значение, эти архивы будут автоматически удалены с вашего хранилища через указанное число дней. + bootstrap_timeline_accounts: Эти учётные записи будут закреплены в начале списка рекомендуемых профилей для новых пользователей. Список учётных записей нужно вводить через запятую. closed_registrations_message: Отображается, когда регистрация закрыта content_cache_retention_period: Все сообщения с других серверов (включая бусты и ответы) будут удалены через указанное количество дней, независимо от того, как локальный пользователь взаимодействовал с этими сообщениями. Это касается и тех сообщений, которые локальный пользователь пометил в закладки или избранное. Приватные упоминания между пользователями из разных инстансов также будут потеряны и не смогут быть восстановлены. Использование этой настройки предназначено для экземпляров специального назначения и нарушает многие ожидания пользователей при использовании в общих целях. custom_css: Вы можете применять пользовательские стили в веб-версии Mastodon. favicon: WEBP, PNG, GIF или JPG. Заменяет стандартный фавикон Mastodon на собственный значок. + landing_page: Определите, какую страницу новые посетители увидят, когда зайдут на ваш сервер впервые. Если вы выберете «Актуальное», то необходимо включить тренды в настройках обзора. Если вы выберете «Локальная лента», то необходимо в настройках обзора установить настройку «Доступ к живым лентам, содержащим локальные посты» в значение «Кто угодно». mascot: Заменяет иллюстрацию в расширенном веб-интерфейсе. media_cache_retention_period: Медиафайлы из сообщений, сделанных удаленными пользователями, кэшируются на вашем сервере. При положительном значении медиафайлы будут удалены через указанное количество дней. Если медиаданные будут запрошены после удаления, они будут загружены повторно, если исходный контент все еще доступен. В связи с ограничениями на частоту опроса карточек предварительного просмотра ссылок на сторонних сайтах рекомендуется устанавливать значение не менее 14 дней, иначе карточки предварительного просмотра ссылок не будут обновляться по запросу до этого времени. min_age: Пользователям при регистрации будет предложено ввести свою дату рождения @@ -118,8 +127,8 @@ ru: sign_up_requires_approval: Новые регистрации потребуют вашего одобрения severity: Выберите, что будет происходить с запросами с этого IP rule: - hint: Необязательно. Предоставьте дополнительные сведения о правиле - text: Опишите правило или требование для пользователей на этом сервере. Постарайтесь сделать его коротким и простым + hint: Необязательно. Расскажите о правиле подробнее + text: Опишите правило или требование для пользователей на этом сервере. Постарайтесь сформулировать его просто и лаконично sessions: otp: Создайте код двухфакторной аутентификации в приложении на вашем смартфоне и введите его здесь, или же вы можете использовать один из ваших резервных кодов. webauthn: Если вы используете USB-ключ, не забудьте вставить и активировать его. @@ -136,12 +145,12 @@ ru: admin_email: Юридические уведомления включают в себя встречные уведомления, постановления суда, запросы на удаление и запросы правоохранительных органов. arbitration_address: Может совпадать с почтовым адресом, указанным выше, либо «N/A» в случае электронной почты. arbitration_website: Веб-форма или «N/A» в случае электронной почты. - choice_of_law: Город, регион, территория или государство, внутреннее материальное право которого регулирует любые претензии. + choice_of_law: Город, регион, территория или государство, внутреннее материальное право которого будет регулировать любые претензии. dmca_address: Находящиеся в США операторы должны использовать адрес, зарегистрированный в DMCA Designated Agent Directory. Использовать абонентский ящик возможно при обращении в соответствующей просьбой, для чего нужно с помощью DMCA Designated Agent Post Office Box Waiver Request написать сообщение в Copyright Office и объяснить, что вы занимаетесь модерацией контента из дома и опасаетесь мести за свои действия, поэтому должны использовать абонентский ящик, чтобы убрать ваш домашний адрес из общего доступа. dmca_email: Может совпадать с адресом электронной почты для юридических уведомлений, указанным выше. domain: Имя, позволяющее уникально идентифицировать ваш онлайн-ресурс. jurisdiction: Впишите страну, где находится лицо, оплачивающее счета. Если это компания либо организация, впишите страну инкорпорации, включая город, регион, территорию или штат, если это необходимо. - min_age: Не меньше минимального возраста, требуемого по закону в вашей юрисдикции. + min_age: Не меньше минимального возраста, требуемого по закону в вашей стране. user: chosen_languages: Отметьте языки, на которых вы желаете видеть посты в публичных лентах. Оставьте выбор пустым, чтобы не фильтровать посты по языку date_of_birth: @@ -185,7 +194,7 @@ ru: types: disable: Отключить none: Вынести предупреждение - sensitive: Отметить как «деликатного характера» + sensitive: Скрыть медиа silence: Ограничить suspend: Заблокировать warning_preset_id: Использовать шаблон предупреждения @@ -226,9 +235,12 @@ ru: 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: Отображение медиа @@ -238,6 +250,8 @@ ru: 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: Использовать системные полосы прокрутки @@ -264,32 +278,37 @@ ru: warn: Скрыть с предупреждением form_admin_settings: activity_api_enabled: Публикация агрегированной статистики активности пользователей в API - app_icon: Иконка приложения - backups_retention_period: Период хранения архива пользователя - bootstrap_timeline_accounts: Всегда рекомендовать эти учетные записи новым пользователям - closed_registrations_message: Сообщение, когда регистрация недоступна - content_cache_retention_period: Период хранения удаленного содержимого + app_icon: Значок приложения + backups_retention_period: Срок хранения архива пользователя + bootstrap_timeline_accounts: Всегда рекомендовать эти профили новым пользователям + closed_registrations_message: Текст сообщения о том, что регистрация недоступна + content_cache_retention_period: Срок хранения содержимого с других серверов custom_css: Пользовательский CSS favicon: Favicon + landing_page: Целевая страница для новых посетителей + local_live_feed_access: Доступ к живым лентам, содержащим локальные посты + local_topic_feed_access: Доступ к лентам хештегов и ссылок, содержащим локальные посты mascot: Пользовательский маскот (устаревшее) - media_cache_retention_period: Период хранения кэша медиафайлов - min_age: Требование минимального возраста - peers_api_enabled: Публикация списка обнаруженных узлов в API + media_cache_retention_period: Срок хранения кэша медиа + min_age: Минимальный возраст для регистрации + peers_api_enabled: Публикация списка обнаруженных серверов в API profile_directory: Включить каталог профилей registrations_mode: Кто может зарегистрироваться - require_invite_text: Требуется причина для присоединения - show_domain_blocks: Показать блокировки домена - show_domain_blocks_rationale: Показать причину блокировки доменов - site_contact_email: Контактный e-mail + remote_live_feed_access: Доступ к живым лентам, содержащим посты с других серверов + remote_topic_feed_access: Доступ к лентам хештегов и ссылок, содержащим посты с других серверов + require_invite_text: Требовать указывать причину регистрации + show_domain_blocks: Показывать список заблокированных доменов + show_domain_blocks_rationale: Показывать причину блокировки домена + site_contact_email: Контактный адрес электронной почты site_contact_username: Контактное имя пользователя site_extended_description: Подробное описание site_short_description: Описание сервера site_terms: Политика конфиденциальности site_title: Имя сервера - status_page_url: Страница уведомлений + status_page_url: Страница состояния сервера theme: Тема по умолчанию - thumbnail: Изображение сервера - trendable_by_default: Разрешить треды без предварительной проверки + thumbnail: Обложка сервера + trendable_by_default: Отключить обязательную премодерацию трендов trends: Включить тренды interactions: must_be_follower: Блокировать уведомления от людей, которые не подписаны на вас @@ -315,7 +334,7 @@ ru: follow_request: Мне пришёл запрос на подписку mention: Меня упомянули в посте pending_account: Новая заявка на создание аккаунта - quote: Кто-то процитировал вас + quote: Мой пост процитировали reblog: Мой пост продвинули report: Новое обращение отправлено software_updates: @@ -326,7 +345,7 @@ ru: patch: Уведомлять об обновлении исправлений trending_tag: Новый тренд требует рассмотрения rule: - hint: Больше информации + hint: Дополнительная информация text: Правило settings: indexable: Разрешить индексацию моего профиля поисковыми системами @@ -343,17 +362,17 @@ ru: terms_of_service_generator: admin_email: Адрес электронной почты для юридических уведомлений arbitration_address: Почтовый адрес для уведомлений об арбитраже - arbitration_website: Вебсайт для подачи уведомления об арбитраже - choice_of_law: Юрисдикция + arbitration_website: Веб-сайт для подачи уведомления об арбитраже + choice_of_law: Выбор права dmca_address: Почтовый адрес для обращений правообладателей dmca_email: Адрес электронной почты для обращений правообладателей domain: Доменное имя jurisdiction: Юрисдикция min_age: Минимальный возраст user: - date_of_birth_1i: День + date_of_birth_1i: Год date_of_birth_2i: Месяц - date_of_birth_3i: Год + date_of_birth_3i: День role: Роль time_zone: Часовой пояс user_role: @@ -366,9 +385,9 @@ ru: allow_with_approval: Разрешить регистрацию с одобрением comparison: Метод сравнения webhook: - events: Включенные события - template: Шаблон полезной нагрузки - url: Endpoint URL + events: Типы событий + template: Шаблон данных + url: Адрес 'no': Нет not_recommended: Не рекомендуется overridden: Переопределено diff --git a/config/locales/simple_form.si.yml b/config/locales/simple_form.si.yml index c012ded9900..a81524f182a 100644 --- a/config/locales/simple_form.si.yml +++ b/config/locales/simple_form.si.yml @@ -344,9 +344,7 @@ si: jurisdiction: නීතිමය අධිකරණ බලය min_age: අවම වයස user: - date_of_birth_1i: දහවල date_of_birth_2i: මාසය - date_of_birth_3i: වර්ෂය role: භූමිකාව time_zone: වේලා කලාපය user_role: diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 55cd67b6f04..e87dd799d2a 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -219,8 +219,12 @@ sl: setting_aggregate_reblogs: Skupinske izpostavitve na časovnicah setting_always_send_emails: Vedno pošlji e-obvestila setting_auto_play_gif: Samodejno predvajanje animiranih GIF-ov + setting_boost_modal: Nadziraj vidnost objav setting_default_language: Jezik objavljanja + setting_default_privacy: Vidnost objav + setting_default_quote_policy: Kdo lahko citira setting_default_sensitive: Vedno označi medije kot občutljive + setting_delete_modal: Pred brisanjem objave me opozori setting_disable_hover_cards: Onemogoči predogled profila pod kazalcem setting_disable_swiping: Onemogoči poteze drsanja setting_display_media: Prikaz medijev @@ -305,6 +309,7 @@ sl: follow_request: Pošlji e-pošto, ko vam nekdo želi slediti mention: Pošlji e-pošto, ko vas nekdo omeni pending_account: Pošlji e-pošto, ko je potreben pregled novega računa + quote: Nekdo vas je citiral reblog: Pošlji e-sporočilo, ko nekdo izpostavi vašo objavo report: Novo poročilo je oddano software_updates: @@ -340,9 +345,9 @@ sl: jurisdiction: Pravna pristojnost min_age: Najmanjša starost user: - date_of_birth_1i: Dan + date_of_birth_1i: Leto date_of_birth_2i: Mesec - date_of_birth_3i: Leto + date_of_birth_3i: Dan role: Vloga time_zone: Časovni pas user_role: @@ -351,6 +356,9 @@ sl: name: Ime permissions_as_keys: Pravice position: Prioriteta + username_block: + allow_with_approval: Dovoli registracije z odobritvijo + comparison: Metoda primerjave webhook: events: Omogočeni dogodki template: Predloga obremenitev diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index 2d155cf2a23..346044f7313 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -371,9 +371,9 @@ sq: jurisdiction: Juridiksion ligjor min_age: Mosha minimale user: - date_of_birth_1i: Ditë + date_of_birth_1i: Vit date_of_birth_2i: Muaj - date_of_birth_3i: Vit + date_of_birth_3i: Ditë role: Rol time_zone: Zonë kohore user_role: diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 2af0a51be4c..7368426b0c7 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -54,6 +54,7 @@ sv: password: Använd minst 8 tecken phrase: Matchas oavsett användande i text eller innehållsvarning för ett inlägg scopes: 'Vilka API: er applikationen kommer tillåtas åtkomst till. Om du väljer en omfattning på högstanivån behöver du inte välja individuella sådana.' + setting_advanced_layout: Visa Mastodon med en layout med flera kolumner, så att du kan se tidslinjen, aviseringar, och en tredje kolumn som du väljer själv. Rekommenderas inte för mindre skärmar. setting_aggregate_reblogs: Visa inte nya boostar för inlägg som nyligen blivit boostade (påverkar endast nymottagna boostar) setting_always_send_emails: E-postnotiser kommer vanligtvis inte skickas när du aktivt använder Mastodon setting_default_sensitive: Känslig media döljs som standard och kan visas med ett klick @@ -354,9 +355,9 @@ sv: jurisdiction: Rättslig jurisdiktion min_age: Minimiålder user: - date_of_birth_1i: Dag + date_of_birth_1i: År date_of_birth_2i: Månad - date_of_birth_3i: År + date_of_birth_3i: Dag role: Roll time_zone: Tidszon user_role: diff --git a/config/locales/simple_form.th.yml b/config/locales/simple_form.th.yml index 71eea7a46b1..1561213e303 100644 --- a/config/locales/simple_form.th.yml +++ b/config/locales/simple_form.th.yml @@ -314,9 +314,7 @@ th: terms_of_service_generator: domain: โดเมน user: - date_of_birth_1i: วัน date_of_birth_2i: เดือน - date_of_birth_3i: ปี role: บทบาท time_zone: โซนเวลา user_role: diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 4a5cad50870..0e79c057666 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -372,9 +372,9 @@ tr: jurisdiction: Yasal yetki alanı min_age: Minimum yaş user: - date_of_birth_1i: Gün + date_of_birth_1i: Yıl date_of_birth_2i: Ay - date_of_birth_3i: Yıl + date_of_birth_3i: Gün role: Rol time_zone: Zaman dilimi user_role: diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index a22ebf28834..94a8a4010b6 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -3,7 +3,7 @@ uk: simple_form: hints: account: - attribution_domains: Один на рядок. Захищає від фальшивих атрибутів. + attribution_domains: Один на рядок. Захищає від фальшивих приписувань авторства. discoverable: Ваші дописи та профіль можуть бути рекомендовані в різних частинах Mastodon і ваш профіль може бути запропонований іншим користувачам. display_name: Ваше повне ім'я або ваш псевдонім. fields: Ваша домашня сторінка, займенники, вік, все, що вам заманеться. @@ -54,12 +54,18 @@ uk: password: Не менше 8 символів phrase: Шукає без врахування регістру у тексті допису або у його попередженні про вміст scopes: Які API додатку буде дозволено використовувати. Якщо ви виберете самий верхній, нижчестоящі будуть обрані автоматично. + setting_advanced_layout: 'Показувати Mastodon як декілька колонок: стрічку, сповіщення й третю колонку на ваш вибір. Не рекомендовано для малих екранів.' setting_aggregate_reblogs: Не показувати поширення для дописів, які нещодавно вже були поширені (не вплине на вже отримані поширення) setting_always_send_emails: Зазвичай, під час активного користування Mastodon, сповіщення не будуть відправлятися електронною поштою + setting_boost_modal: Якщо ввімкнено, при поширенні буде показано діалог підтвердження, в якому можна змінити видимість поширення. + setting_default_quote_policy_private: Mastodon не дозволяє цитувати дописи, адресовані лише підписникам. + 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: Як показувати емоджі. «Авто» — використовувати емоджі браузера, а за їхньої відсутності — Twemoji. + setting_quick_boosting_html: Якщо увімкнено, натиск на піктограму %{boost_icon} Поширити призводитиме до негайного поширення, а не відкриватиме меню поширення й цитування. Кнопку цитування буде переміщено до меню %{options_icon} Більше. setting_system_scrollbars_ui: Застосовується лише для настільних браузерів на основі Safari та Chrome setting_use_blurhash: Градієнти, що базуються на кольорах прихованих медіа, але роблять нерозрізненними будь-які деталі setting_use_pending_items: Не додавати нові повідомлення до стрічок миттєво, показувати лише після додаткового клацання @@ -73,6 +79,7 @@ uk: featured_tag: name: 'Ось деякі використані останнім часом хештеґи:' filters: + action: Виберіть, що робити, коли допис відповідає фільтру actions: blur: Приховати медіа за попередженням, не приховуючи сам текст hide: Повністю сховати фільтрований вміст, ніби його не існує @@ -156,6 +163,7 @@ uk: url: Куди надсилатимуться події labels: account: + attribution_domains: Сайти, яким можна вказувати вас як автора discoverable: Функції профілю та дописів у алгоритмах виявлення fields: name: Мітка @@ -219,10 +227,14 @@ uk: 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_disable_swiping: Вимкнути жести гортання setting_display_media: Показ медіа setting_display_media_default: За промовчанням setting_display_media_hide_all: Сховати всі @@ -230,6 +242,8 @@ uk: 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: Використовувати системну панель гортання @@ -342,9 +356,7 @@ uk: jurisdiction: Правова юрисдикція min_age: Мінімальний вік user: - date_of_birth_1i: День date_of_birth_2i: Місяць - date_of_birth_3i: Рік role: Роль time_zone: Часовий пояс user_role: diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index b4506722624..2c84279da2e 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -58,7 +58,7 @@ vi: setting_aggregate_reblogs: Nếu một tút đã được đăng lại thì sẽ không hiện những lượt đăng lại khác trên bảng tin setting_always_send_emails: Bình thường thì sẽ không gửi khi bạn đang dùng Mastodon setting_boost_modal: Nếu được bật, trước khi đăng lại sẽ mở hộp thoại xác nhận - trong đó bạn có thể thay đổi mức độ hiển thị tút của mình. - setting_default_quote_policy_private: Tút chỉ dành cho người theo dõi trên Mastodon không thể được người khác trích dẫn. + setting_default_quote_policy_private: Không thể trích dẫn tút chỉ dành cho người theo dõi trên Mastodon. setting_default_quote_policy_unlisted: Khi ai đó trích dẫn bạn, tút của họ cũng sẽ bị ẩn khỏi bảng tin công khai. setting_default_sensitive: Bắt buộc nhấn vào mới có thể xem setting_display_media_default: Click để xem @@ -371,9 +371,9 @@ vi: jurisdiction: Quyền tài phán pháp lý min_age: Độ tuổi tối thiểu user: - date_of_birth_1i: Ngày + date_of_birth_1i: Năm date_of_birth_2i: Tháng - date_of_birth_3i: Năm + date_of_birth_3i: Ngày role: Vai trò time_zone: Múi giờ user_role: diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index d6069a43d3b..d5c4525233a 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -371,9 +371,9 @@ zh-CN: jurisdiction: 法律管辖区 min_age: 最低年龄 user: - date_of_birth_1i: 日 + date_of_birth_1i: 年 date_of_birth_2i: 月 - date_of_birth_3i: 年 + date_of_birth_3i: 日 role: 角色 time_zone: 时区 user_role: diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml index a92d7bcd959..7c57864b2e8 100644 --- a/config/locales/simple_form.zh-HK.yml +++ b/config/locales/simple_form.zh-HK.yml @@ -305,9 +305,7 @@ zh-HK: terms_of_service_generator: domain: 域名 user: - date_of_birth_1i: 日 date_of_birth_2i: 月 - date_of_birth_3i: 年 role: 角色 time_zone: 時區 user_role: diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index 274ed20284a..e371d0ac077 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -371,9 +371,9 @@ zh-TW: jurisdiction: 司法管轄區 min_age: 最低年齡 user: - date_of_birth_1i: 日 + date_of_birth_1i: 年 date_of_birth_2i: 月 - date_of_birth_3i: 年 + date_of_birth_3i: 日 role: 角色 time_zone: 時區 user_role: diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 6bb4a193bc5..4cbd1eb43ba 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -495,6 +495,24 @@ sl: new: title: Uvozi blokade domen no_file: Nobena datoteka ni izbrana + fasp: + debug: + callbacks: + delete: Izbriši + ip: Naslov IP + providers: + base_url: Osnovna povezava + callback: Povratni klic + delete: Izbriši + edit: Uredi ponudnika + finish_registration: Dokončaj registracijo + name: Ime + registrations: + confirm: Potrdi + reject: Zavrni + save: Shrani + sign_in: Prijava + status: Stanje follow_recommendations: description_html: "Sledi priporočilom pomaga novim uporabnikom, da hitro najdejo zanimivo vsebino. Če uporabnik ni dovolj komuniciral z drugimi, da bi oblikoval prilagojena priporočila za sledenje, se namesto tega priporočajo ti računi. Dnevno se ponovno izračunajo iz kombinacije računov z najvišjimi nedavnimi angažiranostmi in najvišjim številom krajevnih sledilcev za določen jezik." language: Za jezik @@ -569,6 +587,9 @@ sl: all: Vse limited: Omejeno title: Moderiranje + moderation_notes: + description_html: Pokaži in pusti opombe drugim moderatorjem in sebi v prihodnosti + title: Opombe moderiranja private_comment: Zasebni komentar public_comment: Javni komentar purge: Očisti @@ -783,11 +804,16 @@ sl: title: Vloge rules: add_new: Dodaj pravilo + add_translation: Dodaj prevod delete: Izbriši description_html: Večina trdi, da so prebrali in da se strinjajo s pogoji rabe storitve, vendar le-teh ponavadi ne preberejo, dokler ne pride do težav. Poenostavite in naredite pravila svojega strežnika vidna na prvi pogled tako, da jih izpišete v označenem seznamu.Posamezna pravila skušajte ohraniti kratka in enostavna, ne razbijajte pa jih v preveč različnih točk. edit: Uredi pravilo empty: Zaenkrat še ni opredeljenih pravil. + move_down: Premakni navzdol + move_up: Premakni navzgor title: Pravila strežnika + translation: Prevod + translations: Prevodi settings: about: manage_rules: Upravljaj pravila strežnika @@ -812,6 +838,7 @@ sl: title: Privzeto izvzemi uporabnike iz indeksiranja iskalnika discovery: follow_recommendations: Sledi priporočilom + privacy: Zasebnost profile_directory: Imenik profilov public_timelines: Javne časovnice publish_statistics: Objavi statistiko @@ -821,6 +848,9 @@ sl: all: Vsem disabled: Nikomur users: Prijavljenim krajevnim uporabnikom + landing_page: + values: + trends: Trendi registrations: moderation_recommandation: Preden prijave odprete za vse poskrbite, da imate v ekipi moderatorjev zadosti aktivnih članov. preamble: Nadzirajte, kdo lahko ustvari račun na vašem strežniku. @@ -874,6 +904,7 @@ sl: no_status_selected: Nobena objava ni bila spremenjena, ker ni bila nobena izbrana open: Odpri objavo original_status: Izvorna objava + quotes: Citati reblogs: Ponovljeni blogi replied_to_html: V odgovor %{acct_link} status_changed: Objava spremenjena @@ -1070,6 +1101,15 @@ sl: two: Uporabili %{count} osebi v zadnjem tednu title: Priporočila in trendi trending: V porastu + username_blocks: + add_new: Dodaj novo + comparison: + contains: Vsebuje + contains_html: Vsebuje %{string} + delete: Izbriši + new: + create: Ustvari pravilo + not_permitted: Ni dovoljeno warning_presets: add_new: Dodaj novo delete: Izbriši @@ -1144,6 +1184,7 @@ sl: hint_html: Če se želite preseliti iz drugega računa v tega, lahko tukaj ustvarite vzdevek, ki je potreben, preden lahko nadaljujete s selitvijo sledilcev iz starega računa v tega. To dejanje je samo po sebi neškodljivo in povratno. Selitev računa sprožite iz starega računa. remove: Razveži vzdevek appearance: + advanced_settings: Napredne nastavitve animations_and_accessibility: Animacije in dostopnost discovery: Odkrito localization: @@ -1333,6 +1374,8 @@ sl: basic_information: Osnovni podatki hint_html: "Prilagodite, kaj ljudje vidijo na vašem javnem profilu in poleg vaših objav. Drugi vam bodo raje sledili nazaj in z vami klepetali, če boste imeli izpolnjen profil in nastavljeno profilno sliko." other: Drugo + emoji_styles: + auto: Samodejno errors: '400': Zahteva, ki ste jo oddali, je neveljavna ali nepravilno oblikovana. '403': Nimate dovoljenja za ogled te strani. @@ -1582,6 +1625,11 @@ sl: expires_at: Poteče uses: Uporabe title: Povabite ljudi + link_preview: + author_html: Avtor/ica %{name} + potentially_sensitive_content: + action: Kliknite za prikaz + hide_button: Skrij lists: errors: limit: Dosegli ste največje število seznamov @@ -1681,6 +1729,9 @@ sl: title: Nova omemba poll: subject: Anketa, ki jo je pripravil/a %{name}, se je iztekla + quote: + body: 'Vašo objavo je citiral/a %{name}:' + title: Nov citat reblog: body: 'Vašo objavo je izpostavil/a %{name}:' subject: "%{name} je izpostavil/a vašo objavo" @@ -1729,6 +1780,7 @@ sl: self_vote: Ne morete glasovati v lastnih anketah too_few_options: mora imeti več kot en element too_many_options: ne more vsebovati več kot %{max} elementov + vote: Glasuj preferences: other: Ostalo posting_defaults: Privzete nastavitev objavljanja @@ -1890,6 +1942,9 @@ sl: two: "%{count} video posnetka" boosted_from_html: Izpostavljeno z računa %{acct_link} content_warning: 'Opozorilo o vsebini: %{warning}' + content_warnings: + hide: Skrij objavo + show: Pokaži več default_language: Enak kot jezik vmesnika disallowed_hashtags: few: 'vsebuje nedovoljene ključnike: %{tags}' @@ -1905,9 +1960,19 @@ sl: limit: Pripeli ste največje število objav ownership: Objava nekoga drugega ne more biti pripeta reblog: Izpostavitev ne more biti pripeta + quote_error: + not_available: Objava ni na voljo + quote_policies: + followers: Samo sledilci + nobody: Samo jaz + public: Vsi title: "%{name}: »%{quote}«" visibilities: + direct: Zasebna omemba + private: Samo sledilci public: Javno + public_long: Vsem, ki so ali niso na Mastodonu + unlisted: Tiho javno statuses_cleanup: enabled: Samodejno izbriši stare objave enabled_hint: Samodejno izbriše vaše objave, ko dosežejo določen starostni prag, razen če ne ustrezajo eni od spodnjih izjem @@ -1952,6 +2017,8 @@ sl: does_not_match_previous_name: se ne ujema s prejšnjim imenom terms_of_service: title: Pogoji uporabe + terms_of_service_interstitial: + title: Spreminjajo se pogoji uporabe domene %{domain} themes: contrast: Mastodon (Visok kontrast) default: Mastodon (Temna) diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 6af5cada4b4..589badd7440 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -888,6 +888,7 @@ uk: no_status_selected: Жодного допису не було змінено, оскільки жодного з них не було вибрано open: Відкрити допис original_status: Оригінальний допис + quotes: Цитати reblogs: Поширення replied_to_html: Відповів %{acct_link} status_changed: Допис змінено @@ -1159,7 +1160,10 @@ uk: hint_html: Якщо ви збираєтеся мігрувати з іншого облікового запису на цей, ви можете налаштувати псевдонім, який потрібен для перенесення підписок зі старою облікового запису. Ця дія сама по собі нешкідлива і її можна скасувати. Міграція облікового запису починається зі старого облікового запису. remove: Від'єднати псевдонім appearance: + advanced_settings: Додаткові налаштування animations_and_accessibility: Анімація та доступність + boosting_preferences: Параметри поширення + boosting_preferences_info_html: "Підказка: незалежно від налаштувань, Shift + натиск на піктограму %{icon} Поширити призводитиме до негайного поширення." discovery: Виявлення localization: body: Mastodon перекладено волонтерами. @@ -1270,7 +1274,7 @@ uk: more_from_html: Більше від %{name} s_blog: Блог %{name} then_instructions: Потім додайте доменне ім'я публікації у поле нижче. - title: Атрибути авторства + title: Зазначення авторства challenge: confirm: Далі hint_html: "Підказка: ми не будемо запитувати ваш пароль впродовж наступної години." @@ -1538,6 +1542,9 @@ uk: expires_at: Час роботи uses: Використання title: Запросити людей + link_preview: + potentially_sensitive_content: + action: Натисніть, щоб показати lists: errors: limit: Ви досягли максимальної кількості списків @@ -1867,18 +1874,25 @@ uk: limit: Ви вже закріпили максимальну кількість дописів ownership: Не можна закріпити чужий допис reblog: Не можна закріпити просунутий допис + quote_policies: + followers: Лише підписникам + public: Будь-кому title: '%{name}: "%{quote}"' visibilities: + private: Лише підписникам public: Для всіх + public_long: Будь-кому на Mastodon і за його межами + unlisted: Тихо публічно + unlisted_long: Сховано з результатів пошуку Mastodon, трендових і публічних стрічок statuses_cleanup: enabled: Автовидалення давніх дописів enabled_hint: Автоматично видаляє ваші дописи після досягнення вказаного вікового порогу, якщо вони не є одним з наведених винятків exceptions: Винятки explanation: Оскільки видалення дописів є складною операцією, то це відбувається повільно, коли сервер не дуже завантажено. З цієї причини ваші дописи можуть бути видалені через деякий час після досягнення вікового порогу. - ignore_favs: Ігнорувати обране + ignore_favs: Ігнорувати вподобані ignore_reblogs: Ігнорувати поширення interaction_exceptions: Винятки базуються на взаємодії - interaction_exceptions_explanation: Зверніть увагу, що немає гарантії, щоб дописи були видалені, якщо вони йдуть нижче улюблених або підсилювачів після того, як вони перейшли за них. + interaction_exceptions_explanation: 'Зауважте: якщо допис вподобають або поширять достатню кількість разів, а потім це скасують, то його може все одно бути залишено.' keep_direct: Зберігати приватні повідомлення keep_direct_hint: Не видаляти ваших особистих повідомлень keep_media: Зберігати повідомлення з вкладеними мультимедійними файлами @@ -1901,8 +1915,8 @@ uk: '63113904': 2 роки '7889238': 3 місяці min_age_label: Поріг давності - min_favs: Залишати дописи в обраному більше ніж - min_favs_hint: Не видаляти ваших дописів, що були поширені більш ніж вказану кількість разів. Залиште порожнім, щоб видаляти дописи, попри кількість їхніх поширень + min_favs: Залишати дописи, вподобані стільки разів + min_favs_hint: Не видаляти ваших дописів, які були вподобані принаймні стільки разів. Залиште порожнім, щоб видаляти дописи незалежно від кількості вподобань min_reblogs: Залишати дописи, поширені більше ніж min_reblogs_hint: Не видаляти ваших дописів, що були поширені більш ніж вказану кількість разів. Залиште порожнім, щоб видаляти дописи, попри кількість їхніх поширень stream_entries: diff --git a/config/locales/vi.yml b/config/locales/vi.yml index a46e08a6cf7..30476230fdb 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -213,7 +213,7 @@ vi: enable_user: Cho phép đăng nhập memorialize_account: Gán tưởng niệm promote_user: Nâng vai trò - publish_terms_of_service: Đăng Điều khoản Dịch vụ + publish_terms_of_service: Tạo Điều khoản Dịch vụ reject_appeal: Từ chối khiếu nại reject_user: Từ chối đăng ký remove_avatar_user: Xóa ảnh đại diện @@ -1723,7 +1723,7 @@ vi: too_many_options: tối đa %{max} lựa chọn vote: Vốt posting_defaults: - explanation: Các thiết lập này sẽ được sử dụng làm mặc định khi bạn tạo tút mới, nhưng bạn có thể chỉnh sửa cho từng tút khi soạn thảo. + explanation: Các thiết lập này sẽ được sử dụng làm mặc định khi bạn tạo tút mới. Bạn vẫn có thể chỉnh sửa từng tút khi soạn thảo. preferences: other: Khác posting_defaults: Mặc định cho tút @@ -1731,7 +1731,7 @@ vi: privacy: hint_html: Tùy chỉnh cách mọi người tìm thấy hồ sơ và tút của bạn. privacy: Riêng tư - privacy_hint_html: Kiểm soát mức độ chi tiết bạn muốn tiết lộ. Mọi người khám phá các hồ sơ thú vị và các ứng dụng thú vị bằng cách theo dõi những người khác và xem họ đăng từ ứng dụng nào, nhưng có thể bạn muốn ẩn nó đi. + privacy_hint_html: Kiểm soát mức độ tiết lộ chi tiết. Mọi người khám phá các hồ sơ thú vị và các ứng dụng thú vị bằng cách xem bạn theo dõi ai và bạn đăng bằng ứng dụng nào, nhưng có thể bạn muốn ẩn nó đi. reach: Tiếp cận reach_hint_html: Kiểm soát cách bạn được đề xuất và theo dõi. Bạn có muốn tút của mình xuất hiện trên trang Khám phá không? Bạn có muốn bạn xuất hiện trong các đề xuất theo dõi của người khác không? Bạn muốn tự động chấp nhận tất cả những người theo dõi mới hay tự duyệt từng người theo dõi? search: Tìm kiếm diff --git a/config/vite/plugin-mastodon-themes.ts b/config/vite/plugin-mastodon-themes.ts index 251d2d8e724..98bda4cab0e 100644 --- a/config/vite/plugin-mastodon-themes.ts +++ b/config/vite/plugin-mastodon-themes.ts @@ -40,8 +40,14 @@ export function MastodonThemes(): Plugin { // Get all files mentioned in the themes.yml file. const themes = await loadThemesFromConfig(projectRoot); + const allThemes = { + ...themes, + default_theme_tokens: 'styles_new/application.scss', + 'mastodon-light_theme_tokens': 'styles_new/mastodon-light.scss', + contrast_theme_tokens: 'styles_new/contrast.scss', + }; - for (const [themeName, themePath] of Object.entries(themes)) { + for (const [themeName, themePath] of Object.entries(allThemes)) { entrypoints[`themes/${themeName}`] = path.resolve(jsRoot, themePath); } @@ -64,7 +70,11 @@ export function MastodonThemes(): Plugin { // Rewrite the URL to the entrypoint if it matches a theme. if (isThemeFile(req.url ?? '', themes)) { const themeName = pathToThemeName(req.url ?? ''); - req.url = `/packs-dev/${themes[themeName]}`; + const themePath = `/packs-dev/${themes[themeName]}`; + const isThemeTokenRequest = req.url.includes('_theme_tokens'); + req.url = isThemeTokenRequest + ? themePath.replace('styles/', 'styles_new/') + : themePath; } next(); }); @@ -77,7 +87,7 @@ export function MastodonThemes(): Plugin { const themePathToName = new Map( Object.entries(themes).map(([themeName, themePath]) => [ path.resolve(jsRoot, themePath), - `/themes/${themeName}`, + `/themes/${areThemeTokensEnabled() ? `${themeName}_theme_tokens` : themeName}`, ]), ); const themeNames = new Set(); @@ -140,6 +150,7 @@ async function loadThemesFromConfig(root: string) { console.warn(`Invalid theme path "${themePath}" in themes.yml, skipping`); continue; } + themes[themeName] = themePath; } @@ -151,7 +162,7 @@ async function loadThemesFromConfig(root: string) { } function pathToThemeName(file: string) { - const basename = path.basename(file); + const basename = path.basename(file.replace('_theme_tokens', '')); return basename.split(/[.?]/)[0] ?? ''; } @@ -163,3 +174,12 @@ function isThemeFile(file: string, themes: Themes) { const basename = pathToThemeName(file); return basename in themes; } + +function areThemeTokensEnabled() { + const raw = process.env.EXPERIMENTAL_FEATURES ?? ''; + const features = raw + .split(',') + .map((s) => s.trim()) + .filter(Boolean); + return features.includes('theme_tokens'); +} diff --git a/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb b/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb new file mode 100644 index 00000000000..0a8119642d4 --- /dev/null +++ b/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddThumbnailStorageSchemaVersion < ActiveRecord::Migration[8.0] + def change + add_column :media_attachments, :thumbnail_storage_schema_version, :integer + end +end diff --git a/db/migrate/20251118115657_create_collections.rb b/db/migrate/20251118115657_create_collections.rb new file mode 100644 index 00000000000..299cc7aade6 --- /dev/null +++ b/db/migrate/20251118115657_create_collections.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateCollections < ActiveRecord::Migration[8.0] + def change + create_table :collections do |t| + t.references :account, null: false, foreign_key: true + t.string :name, null: false + t.text :description, null: false + t.string :uri + t.boolean :local, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.boolean :sensitive, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.boolean :discoverable, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.references :tag, foreign_key: true + t.integer :original_number_of_items + + t.timestamps + end + end +end diff --git a/db/migrate/20251119093332_create_collection_items.rb b/db/migrate/20251119093332_create_collection_items.rb new file mode 100644 index 00000000000..9fc5d99df53 --- /dev/null +++ b/db/migrate/20251119093332_create_collection_items.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateCollectionItems < ActiveRecord::Migration[8.0] + def change + create_table :collection_items do |t| + t.references :collection, null: false, foreign_key: { on_delete: :cascade } + t.references :account, foreign_key: true + t.integer :position, null: false, default: 1 + t.string :object_uri, index: { unique: true, where: 'activity_uri IS NOT NULL' } + t.string :approval_uri, index: { unique: true, where: 'approval_uri IS NOT NULL' } + t.string :activity_uri + t.datetime :approval_last_verified_at + t.integer :state, null: false, default: 0 + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7bdd6c0ce40..e4e7db3868c 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_23_210145) do +ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -351,6 +351,39 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_23_210145) do t.index ["reference_account_id"], name: "index_canonical_email_blocks_on_reference_account_id" end + create_table "collection_items", force: :cascade do |t| + t.bigint "collection_id", null: false + t.bigint "account_id" + t.integer "position", default: 1, null: false + t.string "object_uri" + t.string "approval_uri" + t.string "activity_uri" + t.datetime "approval_last_verified_at" + t.integer "state", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_collection_items_on_account_id" + t.index ["approval_uri"], name: "index_collection_items_on_approval_uri", unique: true, where: "(approval_uri IS NOT NULL)" + t.index ["collection_id"], name: "index_collection_items_on_collection_id" + t.index ["object_uri"], name: "index_collection_items_on_object_uri", unique: true, where: "(activity_uri IS NOT NULL)" + end + + create_table "collections", force: :cascade do |t| + t.bigint "account_id", null: false + t.string "name", null: false + t.text "description", null: false + t.string "uri" + t.boolean "local", null: false + t.boolean "sensitive", null: false + t.boolean "discoverable", null: false + t.bigint "tag_id" + t.integer "original_number_of_items" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_collections_on_account_id" + t.index ["tag_id"], name: "index_collections_on_tag_id" + end + create_table "conversation_mutes", force: :cascade do |t| t.bigint "conversation_id", null: false t.bigint "account_id", null: false @@ -694,6 +727,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_23_210145) do t.integer "thumbnail_file_size" t.datetime "thumbnail_updated_at", precision: nil t.string "thumbnail_remote_url" + t.integer "thumbnail_storage_schema_version" t.index ["account_id", "status_id"], name: "index_media_attachments_on_account_id_and_status_id", order: { status_id: :desc } t.index ["scheduled_status_id"], name: "index_media_attachments_on_scheduled_status_id", where: "(scheduled_status_id IS NOT NULL)" t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true, opclass: :text_pattern_ops, where: "(shortcode IS NOT NULL)" @@ -1386,6 +1420,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_10_23_210145) do add_foreign_key "bulk_import_rows", "bulk_imports", on_delete: :cascade add_foreign_key "bulk_imports", "accounts", on_delete: :cascade add_foreign_key "canonical_email_blocks", "accounts", column: "reference_account_id", on_delete: :cascade + add_foreign_key "collection_items", "accounts" + add_foreign_key "collection_items", "collections", on_delete: :cascade + add_foreign_key "collections", "accounts" + add_foreign_key "collections", "tags" add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade diff --git a/docker-compose.yml b/docker-compose.yml index 3725010c13d..9140aeb14ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: # es: # restart: always - # image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + # image: docker.elastic.co/elasticsearch/elasticsearch:7.17.29 # environment: # - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true" # - "xpack.license.self_generated.type=basic" @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.5.0 + image: ghcr.io/mastodon/mastodon:v4.5.2 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: # build: # dockerfile: ./streaming/Dockerfile # context: . - image: ghcr.io/mastodon/mastodon-streaming:v4.5.0 + image: ghcr.io/mastodon/mastodon-streaming:v4.5.2 restart: always env_file: .env.production command: node ./streaming/index.js @@ -102,7 +102,7 @@ services: sidekiq: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/mastodon/mastodon:v4.5.0 + image: ghcr.io/mastodon/mastodon:v4.5.2 restart: always env_file: .env.production command: bundle exec sidekiq @@ -115,7 +115,7 @@ services: volumes: - ./public/system:/mastodon/public/system healthcheck: - test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ [78]' || false"] + test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 8' || false"] ## Uncomment to enable federation with tor instances along with adding the following ENV variables ## http_hidden_proxy=http://privoxy:8118 diff --git a/ide-helper.js b/ide-helper.js deleted file mode 100644 index 9e645cb0ebe..00000000000 --- a/ide-helper.js +++ /dev/null @@ -1,12 +0,0 @@ -/* global path */ -/* -Preferences | Languages & Frameworks | JavaScript | Webpack | webpack configuration file -jetbrains://WebStorm/settings?name=Languages+%26+Frameworks--JavaScript--Webpack -*/ -module.exports = { - resolve: { - alias: { - 'mastodon': path.resolve(__dirname, 'app/javascript/mastodon'), - }, - }, -}; diff --git a/lib/mastodon/cli/ip_blocks.rb b/lib/mastodon/cli/ip_blocks.rb index ea6b508c6fe..ba31e6b5ce4 100644 --- a/lib/mastodon/cli/ip_blocks.rb +++ b/lib/mastodon/cli/ip_blocks.rb @@ -107,9 +107,9 @@ module Mastodon::CLI IpBlock.severity_no_access.find_each do |ip_block| case options[:format] when 'nginx' - say "deny #{ip_block.ip}/#{ip_block.ip.prefix};" + say "deny #{ip_block.to_cidr};" else - say "#{ip_block.ip}/#{ip_block.ip.prefix}" + say ip_block.to_cidr end end end diff --git a/lib/mastodon/cli/upgrade.rb b/lib/mastodon/cli/upgrade.rb index 2cb51057948..d5822cacc0c 100644 --- a/lib/mastodon/cli/upgrade.rb +++ b/lib/mastodon/cli/upgrade.rb @@ -123,12 +123,12 @@ module Mastodon::CLI progress.log("Moving #{previous_path} to #{upgraded_path}") if options[:verbose] begin - move_previous_to_upgraded + move_previous_to_upgraded(previous_path, upgraded_path) rescue => e progress.log(pastel.red("Error processing #{previous_path}: #{e}")) success = false - remove_directory + remove_directory(upgraded_path) end end diff --git a/lib/vite_ruby/sri_extensions.rb b/lib/vite_ruby/sri_extensions.rb index d97ab352da0..31363272bfa 100644 --- a/lib/vite_ruby/sri_extensions.rb +++ b/lib/vite_ruby/sri_extensions.rb @@ -107,6 +107,7 @@ module ViteRails::TagHelpers::IntegrityExtension stylesheet, integrity: vite_manifest.integrity_hash_for_file(stylesheet), media: media, + crossorigin: crossorigin, **options ) end diff --git a/package.json b/package.json index 3c50f111cdb..c5fa667d527 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "core-js": "^3.30.2", "cross-env": "^10.0.0", "debug": "^4.4.1", + "delegated-events": "^1.1.2", "detect-passive-events": "^2.0.3", "emoji-mart": "npm:emoji-mart-lazyload@latest", "emojibase": "^16.0.0", @@ -120,7 +121,6 @@ "vite": "^7.1.1", "vite-plugin-manifest-sri": "^0.2.0", "vite-plugin-pwa": "^1.0.2", - "vite-plugin-static-copy": "^3.1.1", "vite-plugin-svgr": "^4.3.0", "vite-tsconfig-paths": "^5.1.4", "wicg-inert": "^3.1.2", @@ -132,10 +132,10 @@ "devDependencies": { "@eslint/js": "^9.23.0", "@formatjs/cli": "^6.1.1", - "@storybook/addon-a11y": "^10.0.2", - "@storybook/addon-docs": "^10.0.2", - "@storybook/addon-vitest": "^10.0.2", - "@storybook/react-vite": "^10.0.2", + "@storybook/addon-a11y": "^10.0.6", + "@storybook/addon-docs": "^10.0.6", + "@storybook/addon-vitest": "^10.0.6", + "@storybook/react-vite": "^10.0.6", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.0", "@types/debug": "^4", @@ -165,7 +165,7 @@ "@vitest/browser-playwright": "^4.0.5", "@vitest/coverage-v8": "^4.0.5", "@vitest/ui": "^4.0.5", - "chromatic": "^13.1.3", + "chromatic": "^13.3.3", "eslint": "^9.23.0", "eslint-import-resolver-typescript": "^4.2.5", "eslint-plugin-formatjs": "^5.3.1", @@ -180,12 +180,12 @@ "globals": "^16.0.0", "husky": "^9.0.11", "lint-staged": "^16.2.6", - "msw": "^2.10.2", - "msw-storybook-addon": "^2.0.5", + "msw": "^2.12.1", + "msw-storybook-addon": "^2.0.6", "playwright": "^1.56.1", "prettier": "^3.3.3", "react-test-renderer": "^18.2.0", - "storybook": "^10.0.2", + "storybook": "^10.0.5", "stylelint": "^16.19.1", "stylelint-config-prettier-scss": "^1.0.0", "stylelint-config-standard-scss": "^16.0.0", diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index 6ec89a1cb65..bce8803be75 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -17,3 +17,7 @@ Fabricator(:account) do discoverable true indexable true end + +Fabricator(:remote_account, from: :account) do + domain 'example.com' +end diff --git a/spec/fabricators/collection_fabricator.rb b/spec/fabricators/collection_fabricator.rb new file mode 100644 index 00000000000..a6a8411ba00 --- /dev/null +++ b/spec/fabricators/collection_fabricator.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +Fabricator(:collection) do + account { Fabricate.build(:account) } + name { sequence(:name) { |i| "Collection ##{i}" } } + description 'People to follow' + local true + sensitive false + discoverable true +end diff --git a/spec/fabricators/collection_item_fabricator.rb b/spec/fabricators/collection_item_fabricator.rb new file mode 100644 index 00000000000..011f9ba5b5e --- /dev/null +++ b/spec/fabricators/collection_item_fabricator.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Fabricator(:collection_item) do + collection { Fabricate.build(:collection) } + account { Fabricate.build(:account) } + position 1 + state :accepted +end + +Fabricator(:unverified_remote_collection_item, from: :collection_item) do + account nil + state :pending + object_uri { Fabricate.build(:remote_account).uri } + approval_uri { sequence(:uri) { |i| "https://example.com/authorizations/#{i}" } } +end diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb index 104d7f99314..300a6d9ba67 100644 --- a/spec/fabricators/user_fabricator.rb +++ b/spec/fabricators/user_fabricator.rb @@ -25,3 +25,9 @@ end Fabricator(:owner_user, from: :user) do role UserRole.find_by(name: 'Owner') end + +Fabricator(:private_user, from: :user) do + account_attributes do + { discoverable: false, locked: true, indexable: false } + end +end diff --git a/spec/lib/activitypub/activity_spec.rb b/spec/lib/activitypub/activity_spec.rb index 218da04d9b5..d7d0700dc65 100644 --- a/spec/lib/activitypub/activity_spec.rb +++ b/spec/lib/activitypub/activity_spec.rb @@ -34,6 +34,8 @@ RSpec.describe ActivityPub::Activity do } end + let(:publication_date) { 1.hour.ago.utc } + let(:create_json) do { '@context': [ @@ -52,7 +54,7 @@ RSpec.describe ActivityPub::Activity do 'https://www.w3.org/ns/activitystreams#Public', ], content: 'foo', - published: '2025-05-24T11:03:10Z', + published: publication_date.iso8601, quote: ActivityPub::TagManager.instance.uri_for(quoted_status), }, }.deep_stringify_keys @@ -77,7 +79,7 @@ RSpec.describe ActivityPub::Activity do 'https://www.w3.org/ns/activitystreams#Public', ], content: 'foo', - published: '2025-05-24T11:03:10Z', + published: publication_date.iso8601, quote: ActivityPub::TagManager.instance.uri_for(quoted_status), quoteAuthorization: approval_uri, }, diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index d531b8b7a86..1f903ac065c 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -251,12 +251,12 @@ RSpec.describe Mastodon::CLI::IpBlocks do it 'exports blocked IPs with "no_access" severity in plain format' do expect { subject } - .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") + .to output_results("#{first_ip_range_block.to_cidr}\n#{second_ip_range_block.to_cidr}") end it 'does not export blocked IPs with different severities' do expect { subject } - .to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") + .to_not output_results(third_ip_range_block.to_cidr) end end @@ -265,19 +265,19 @@ RSpec.describe Mastodon::CLI::IpBlocks do it 'exports blocked IPs with "no_access" severity in plain format' do expect { subject } - .to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};") + .to output_results("deny #{first_ip_range_block.to_cidr};\ndeny #{second_ip_range_block.to_cidr};") end it 'does not export blocked IPs with different severities' do expect { subject } - .to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") + .to_not output_results("deny #{third_ip_range_block.to_cidr};") end end context 'when --format option is not provided' do it 'exports blocked IPs in plain format by default' do expect { subject } - .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") + .to output_results("#{first_ip_range_block.to_cidr}\n#{second_ip_range_block.to_cidr}") end end end diff --git a/spec/lib/mastodon/redis_configuration_spec.rb b/spec/lib/mastodon/redis_configuration_spec.rb index d8a8dd86d29..560b16ed309 100644 --- a/spec/lib/mastodon/redis_configuration_spec.rb +++ b/spec/lib/mastodon/redis_configuration_spec.rb @@ -73,8 +73,6 @@ RSpec.describe Mastodon::RedisConfiguration do end shared_examples 'sentinel support' do |prefix = nil| - prefix = prefix ? "#{prefix}_" : '' - context 'when configuring sentinel support' do around do |example| ClimateControl.modify "#{prefix}REDIS_PASSWORD": 'testpass1', "#{prefix}REDIS_HOST": 'redis2.example.com', "#{prefix}REDIS_SENTINELS": '192.168.0.1:3000,192.168.0.2:4000', "#{prefix}REDIS_SENTINEL_MASTER": 'mainsentinel' do @@ -199,7 +197,7 @@ RSpec.describe Mastodon::RedisConfiguration do it_behaves_like 'secondary configuration', 'SIDEKIQ' it_behaves_like 'setting a different driver' - it_behaves_like 'sentinel support', 'SIDEKIQ' + it_behaves_like 'sentinel support', 'SIDEKIQ_' end describe '#cache' do @@ -225,6 +223,6 @@ RSpec.describe Mastodon::RedisConfiguration do it_behaves_like 'secondary configuration', 'CACHE' it_behaves_like 'setting a different driver' - it_behaves_like 'sentinel support', 'CACHE' + it_behaves_like 'sentinel support', 'CACHE_' end end diff --git a/spec/models/collection_item_spec.rb b/spec/models/collection_item_spec.rb new file mode 100644 index 00000000000..39464b7a340 --- /dev/null +++ b/spec/models/collection_item_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CollectionItem do + describe 'Validations' do + subject { Fabricate.build(:collection_item) } + + it { is_expected.to define_enum_for(:state) } + + it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than(0) } + + context 'when account inclusion is accepted' do + subject { Fabricate.build(:collection_item, state: :accepted) } + + it { is_expected.to validate_presence_of(:account) } + end + + context 'when item is local and account is remote' do + subject { Fabricate.build(:collection_item, account: remote_account) } + + let(:remote_account) { Fabricate.build(:remote_account) } + + it { is_expected.to validate_presence_of(:activity_uri) } + end + + context 'when item is not local' do + subject { Fabricate.build(:collection_item, collection: remote_collection) } + + let(:remote_collection) { Fabricate.build(:collection, local: false) } + + it { is_expected.to validate_absence_of(:approval_uri) } + end + + context 'when account is not present' do + subject { Fabricate.build(:unverified_remote_collection_item) } + + it { is_expected.to validate_presence_of(:object_uri) } + end + end +end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb new file mode 100644 index 00000000000..c6a500210f6 --- /dev/null +++ b/spec/models/collection_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Collection do + describe 'Validations' do + subject { Fabricate.build :collection } + + it { is_expected.to validate_presence_of(:name) } + + it { is_expected.to validate_presence_of(:description) } + + context 'when collection is remote' do + subject { Fabricate.build :collection, local: false } + + it { is_expected.to validate_presence_of(:uri) } + + it { is_expected.to validate_presence_of(:original_number_of_items) } + end + + context 'when using a hashtag as category' do + subject { Fabricate.build(:collection, tag:) } + + context 'when hashtag is usable' do + let(:tag) { Fabricate.build(:tag) } + + it { is_expected.to be_valid } + end + + context 'when hashtag is not usable' do + let(:tag) { Fabricate.build(:tag, usable: false) } + + it { is_expected.to_not be_valid } + end + end + + context 'when there are more items than allowed' do + subject { Fabricate.build(:collection, collection_items:) } + + let(:collection_items) { Fabricate.build_times(described_class::MAX_ITEMS + 1, :collection_item, collection: nil) } + + it { is_expected.to_not be_valid } + end + end +end diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb index 18fb7ea136b..a8b0809511a 100644 --- a/spec/models/ip_block_spec.rb +++ b/spec/models/ip_block_spec.rb @@ -26,6 +26,22 @@ RSpec.describe IpBlock do end end + describe '#to_cidr' do + subject { Fabricate.build(:ip_block, ip:).to_cidr } + + context 'with an IP and a specified prefix' do + let(:ip) { '192.168.1.0/24' } + + it { is_expected.to eq('192.168.1.0/24') } + end + + context 'with an IP and a default prefix' do + let(:ip) { '192.168.1.0' } + + it { is_expected.to eq('192.168.1.0/32') } + end + end + describe '.blocked?' do context 'when the IP is blocked' do it 'returns true' do diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb index 4316c1409d0..f68ebbdb228 100644 --- a/spec/requests/api/v1/accounts/credentials_spec.rb +++ b/spec/requests/api/v1/accounts/credentials_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'credentials API' do - let(:user) { Fabricate(:user, account_attributes: { discoverable: false, locked: true, indexable: false }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :private_user, oauth_scopes: 'read:accounts write:accounts' describe 'GET /api/v1/accounts/verify_credentials' do subject do diff --git a/spec/requests/api/v1/accounts/endorsements_spec.rb b/spec/requests/api/v1/accounts/endorsements_spec.rb index 6e0996a1f1b..c8ad5297726 100644 --- a/spec/requests/api/v1/accounts/endorsements_spec.rb +++ b/spec/requests/api/v1/accounts/endorsements_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Pins API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:kevin) { Fabricate(:user) } before do diff --git a/spec/requests/api/v1/accounts/familiar_followers_spec.rb b/spec/requests/api/v1/accounts/familiar_followers_spec.rb index c698c2d6892..7c71f36a245 100644 --- a/spec/requests/api/v1/accounts/familiar_followers_spec.rb +++ b/spec/requests/api/v1/accounts/familiar_followers_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Familiar Followers API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/familiar_followers' do diff --git a/spec/requests/api/v1/accounts/featured_tags_spec.rb b/spec/requests/api/v1/accounts/featured_tags_spec.rb index 54d92eb1cf0..f2aaf8dfd68 100644 --- a/spec/requests/api/v1/accounts/featured_tags_spec.rb +++ b/spec/requests/api/v1/accounts/featured_tags_spec.rb @@ -3,11 +3,9 @@ require 'rails_helper' RSpec.describe 'account featured tags API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:account) { Fabricate(:account) } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/:id/featured_tags' do subject do diff --git a/spec/requests/api/v1/accounts/follower_accounts_spec.rb b/spec/requests/api/v1/accounts/follower_accounts_spec.rb index 61987fac1cc..1f0779701b2 100644 --- a/spec/requests/api/v1/accounts/follower_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/follower_accounts_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts FollowerAccounts' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/following_accounts_spec.rb b/spec/requests/api/v1/accounts/following_accounts_spec.rb index aae811467d2..193cf2196f0 100644 --- a/spec/requests/api/v1/accounts/following_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/following_accounts_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts FollowingAccounts' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/identity_proofs_spec.rb b/spec/requests/api/v1/accounts/identity_proofs_spec.rb index ba04ed45b9f..21b32fd3b40 100644 --- a/spec/requests/api/v1/accounts/identity_proofs_spec.rb +++ b/spec/requests/api/v1/accounts/identity_proofs_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Identity Proofs API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/identity_proofs' do diff --git a/spec/requests/api/v1/accounts/lists_spec.rb b/spec/requests/api/v1/accounts/lists_spec.rb index cb1ff6b9f28..63b1dc7816f 100644 --- a/spec/requests/api/v1/accounts/lists_spec.rb +++ b/spec/requests/api/v1/accounts/lists_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Lists API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:lists' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:lists' + let(:account) { Fabricate(:account) } let(:list) { Fabricate(:list, account: user.account) } diff --git a/spec/requests/api/v1/accounts/lookup_spec.rb b/spec/requests/api/v1/accounts/lookup_spec.rb index 77c09c0902b..a4d125a87cd 100644 --- a/spec/requests/api/v1/accounts/lookup_spec.rb +++ b/spec/requests/api/v1/accounts/lookup_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Lookup API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/lookup' do diff --git a/spec/requests/api/v1/accounts/notes_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb index e616df1e6f4..e70a4651533 100644 --- a/spec/requests/api/v1/accounts/notes_spec.rb +++ b/spec/requests/api/v1/accounts/notes_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Notes API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:account) { Fabricate(:account) } let(:comment) { 'foo' } diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index 52aeb013280..d735e4d2410 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -7,10 +7,7 @@ RSpec.describe 'GET /api/v1/accounts/relationships' do get '/api/v1/accounts/relationships', headers: headers, params: params end - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' let(:simon) { Fabricate(:account) } let(:lewis) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/search_spec.rb b/spec/requests/api/v1/accounts/search_spec.rb index dc24813e739..5a01628c9ac 100644 --- a/spec/requests/api/v1/accounts/search_spec.rb +++ b/spec/requests/api/v1/accounts/search_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Accounts Search API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' describe 'GET /api/v1/accounts/search' do it 'returns http success' do diff --git a/spec/requests/api/v1/accounts/statuses_spec.rb b/spec/requests/api/v1/accounts/statuses_spec.rb index 1e219502874..0a1daa0488b 100644 --- a/spec/requests/api/v1/accounts/statuses_spec.rb +++ b/spec/requests/api/v1/accounts/statuses_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts Statuses' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses' describe 'GET /api/v1/accounts/:account_id/statuses' do it 'returns expected headers', :aggregate_failures do diff --git a/spec/requests/api/v1/accounts_spec.rb b/spec/requests/api/v1/accounts_spec.rb index 9bbce4877ef..c6a131062b3 100644 --- a/spec/requests/api/v1/accounts_spec.rb +++ b/spec/requests/api/v1/accounts_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe '/api/v1/accounts' do - let(:user) { Fabricate(:user) } - let(:scopes) { '' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/accounts?id[]=:id' do let(:account) { Fabricate(:account) } diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index 4884dba9c79..c1273f468dd 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Account actions' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:write admin:write:accounts' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:write admin:write:accounts' shared_examples 'a successful notification delivery' do it 'notifies the user about the action taken', :inline_jobs do diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index 6a681f9c5e5..d94fcd31e40 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Accounts' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:accounts admin:write:accounts' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:accounts admin:write:accounts' describe 'GET /api/v1/admin/accounts' do subject do diff --git a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb index 25af0a26afe..b3af0a923d9 100644 --- a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb +++ b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Canonical Email Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' describe 'GET /api/v1/admin/canonical_email_blocks' do subject do diff --git a/spec/requests/api/v1/admin/dimensions_spec.rb b/spec/requests/api/v1/admin/dimensions_spec.rb index 3a4cd91716a..488a13ef0f2 100644 --- a/spec/requests/api/v1/admin/dimensions_spec.rb +++ b/spec/requests/api/v1/admin/dimensions_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Dimensions' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user + let(:account) { Fabricate(:account) } describe 'GET /api/v1/admin/dimensions' do diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb index fba1eb15d37..f3ae4076dbc 100644 --- a/spec/requests/api/v1/admin/domain_allows_spec.rb +++ b/spec/requests/api/v1/admin/domain_allows_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain Allows' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' describe 'GET /api/v1/admin/domain_allows' do subject do diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb index 0b01d04f9a0..d532b0d25f9 100644 --- a/spec/requests/api/v1/admin/domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:domain_blocks admin:write:domain_blocks' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:domain_blocks admin:write:domain_blocks' describe 'GET /api/v1/admin/domain_blocks' do subject do diff --git a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb index 2788a45a4a2..a75b5abde4f 100644 --- a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb @@ -3,12 +3,7 @@ require 'rails_helper' RSpec.describe 'Email Domain Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:account) { Fabricate(:account) } - let(:scopes) { 'admin:read:email_domain_blocks admin:write:email_domain_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:email_domain_blocks admin:write:email_domain_blocks' describe 'GET /api/v1/admin/email_domain_blocks' do subject do diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb index 59ef8d29665..a9d62752941 100644 --- a/spec/requests/api/v1/admin/ip_blocks_spec.rb +++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'IP Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:ip_blocks admin:write:ip_blocks' describe 'GET /api/v1/admin/ip_blocks' do subject do @@ -97,7 +93,7 @@ RSpec.describe 'IP Blocks' do expect(response.parsed_body) .to include( - ip: eq("#{ip_block.ip}/#{ip_block.ip.prefix}"), + ip: eq(ip_block.to_cidr), severity: eq(ip_block.severity.to_s) ) end @@ -216,7 +212,7 @@ RSpec.describe 'IP Blocks' do expect(response.content_type) .to start_with('application/json') expect(response.parsed_body).to match(hash_including({ - ip: "#{ip_block.ip}/#{ip_block.ip.prefix}", + ip: ip_block.to_cidr, severity: 'sign_up_requires_approval', comment: 'Decreasing severity', })) diff --git a/spec/requests/api/v1/admin/measures_spec.rb b/spec/requests/api/v1/admin/measures_spec.rb index b55cd0f1b20..6c35da5656d 100644 --- a/spec/requests/api/v1/admin/measures_spec.rb +++ b/spec/requests/api/v1/admin/measures_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Measures' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:account) { Fabricate(:account) } + include_context 'with API authentication', user_fabricator: :admin_user + let(:params) do { keys: %w(instance_accounts instance_follows instance_followers), diff --git a/spec/requests/api/v1/admin/reports_spec.rb b/spec/requests/api/v1/admin/reports_spec.rb index 987f0eda7fb..54dd4c9c8c4 100644 --- a/spec/requests/api/v1/admin/reports_spec.rb +++ b/spec/requests/api/v1/admin/reports_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Reports' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:reports admin:write:reports' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:reports admin:write:reports' describe 'GET /api/v1/admin/reports' do subject do diff --git a/spec/requests/api/v1/admin/retention_spec.rb b/spec/requests/api/v1/admin/retention_spec.rb index 25e626e2593..9c7be0981d3 100644 --- a/spec/requests/api/v1/admin/retention_spec.rb +++ b/spec/requests/api/v1/admin/retention_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Retention' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user + let(:account) { Fabricate(:account) } describe 'GET /api/v1/admin/retention' do diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index 3a57432af78..c84536d1b57 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Tags' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:tag) { Fabricate(:tag) } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } diff --git a/spec/requests/api/v1/admin/trends/links/links_spec.rb b/spec/requests/api/v1/admin/trends/links/links_spec.rb index 51e800734aa..e49c9a0709a 100644 --- a/spec/requests/api/v1/admin/trends/links/links_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/links_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Links' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' describe 'GET /api/v1/admin/trends/links' do subject do diff --git a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb index d46d0ff5555..5fe6cac47f5 100644 --- a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb @@ -3,11 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Links Preview Card Providers' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:preview_card_provider) { Fabricate(:preview_card_provider) } diff --git a/spec/requests/api/v1/admin/trends/statuses_spec.rb b/spec/requests/api/v1/admin/trends/statuses_spec.rb index c63d8d925c7..90a3d80a2a4 100644 --- a/spec/requests/api/v1/admin/trends/statuses_spec.rb +++ b/spec/requests/api/v1/admin/trends/statuses_spec.rb @@ -3,13 +3,10 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Statuses' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:status) { Fabricate(:status) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/admin/trends/statuses' do it 'returns http success' do diff --git a/spec/requests/api/v1/admin/trends/tags_spec.rb b/spec/requests/api/v1/admin/trends/tags_spec.rb index 433cc6c5a6e..750ee8975d3 100644 --- a/spec/requests/api/v1/admin/trends/tags_spec.rb +++ b/spec/requests/api/v1/admin/trends/tags_spec.rb @@ -3,13 +3,10 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Tags' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:tag) { Fabricate(:tag) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/admin/trends/tags' do it 'returns http success' do diff --git a/spec/requests/api/v1/announcements_spec.rb b/spec/requests/api/v1/announcements_spec.rb index 97a4442aa98..b7ba47e1d2d 100644 --- a/spec/requests/api/v1/announcements_spec.rb +++ b/spec/requests/api/v1/announcements_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Announcements' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' let!(:announcement) { Fabricate(:announcement) } diff --git a/spec/requests/api/v1/annual_reports_spec.rb b/spec/requests/api/v1/annual_reports_spec.rb index b9831d17e2c..88a7dbdd825 100644 --- a/spec/requests/api/v1/annual_reports_spec.rb +++ b/spec/requests/api/v1/annual_reports_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Annual Reports' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/annual_reports' do context 'when not authorized' do diff --git a/spec/requests/api/v1/blocks_spec.rb b/spec/requests/api/v1/blocks_spec.rb index 498cf932756..f02739d9113 100644 --- a/spec/requests/api/v1/blocks_spec.rb +++ b/spec/requests/api/v1/blocks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Blocks' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:blocks' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:blocks' describe 'GET /api/v1/blocks' do subject do diff --git a/spec/requests/api/v1/bookmarks_spec.rb b/spec/requests/api/v1/bookmarks_spec.rb index c78e6912365..bd61644f22d 100644 --- a/spec/requests/api/v1/bookmarks_spec.rb +++ b/spec/requests/api/v1/bookmarks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Bookmarks' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:bookmarks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:bookmarks' describe 'GET /api/v1/bookmarks' do subject do diff --git a/spec/requests/api/v1/conversations_spec.rb b/spec/requests/api/v1/conversations_spec.rb index 6e2ac1df53e..6c928bc4329 100644 --- a/spec/requests/api/v1/conversations_spec.rb +++ b/spec/requests/api/v1/conversations_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'API V1 Conversations' do + include_context 'with API authentication', oauth_scopes: 'read:statuses' + let!(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:other) { Fabricate(:user) } diff --git a/spec/requests/api/v1/custom_emojis_spec.rb b/spec/requests/api/v1/custom_emojis_spec.rb index e860fbeb17c..8b832540eff 100644 --- a/spec/requests/api/v1/custom_emojis_spec.rb +++ b/spec/requests/api/v1/custom_emojis_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Custom Emojis' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/custom_emojis' do before do diff --git a/spec/requests/api/v1/directories_spec.rb b/spec/requests/api/v1/directories_spec.rb index 07e65f49b75..69bfdc83435 100644 --- a/spec/requests/api/v1/directories_spec.rb +++ b/spec/requests/api/v1/directories_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Directories API' do + include_context 'with API authentication', oauth_scopes: 'read:follows' + let(:user) { Fabricate(:user, confirmed_at: nil) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/directories' do context 'with no params' do diff --git a/spec/requests/api/v1/domain_blocks_spec.rb b/spec/requests/api/v1/domain_blocks_spec.rb index 339f49fe761..0843c479837 100644 --- a/spec/requests/api/v1/domain_blocks_spec.rb +++ b/spec/requests/api/v1/domain_blocks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain blocks' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:blocks write:blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:blocks write:blocks' describe 'GET /api/v1/domain_blocks' do subject do diff --git a/spec/requests/api/v1/endorsements_spec.rb b/spec/requests/api/v1/endorsements_spec.rb index 730ba6350cf..07ca476f3a8 100644 --- a/spec/requests/api/v1/endorsements_spec.rb +++ b/spec/requests/api/v1/endorsements_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Endorsements' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/endorsements' do context 'when not authorized' do diff --git a/spec/requests/api/v1/favourites_spec.rb b/spec/requests/api/v1/favourites_spec.rb index 44d0239556b..dcc0286e0b0 100644 --- a/spec/requests/api/v1/favourites_spec.rb +++ b/spec/requests/api/v1/favourites_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Favourites' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:favourites' } - let(:headers) { { Authorization: "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:favourites' describe 'GET /api/v1/favourites' do subject do diff --git a/spec/requests/api/v1/featured_tags_spec.rb b/spec/requests/api/v1/featured_tags_spec.rb index 7a5f92cdfd4..eafbe2185eb 100644 --- a/spec/requests/api/v1/featured_tags_spec.rb +++ b/spec/requests/api/v1/featured_tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'FeaturedTags' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts write:accounts' describe 'GET /api/v1/featured_tags' do context 'with wrong scope' do diff --git a/spec/requests/api/v1/filters_spec.rb b/spec/requests/api/v1/filters_spec.rb index 51f03cc04d4..32103a93429 100644 --- a/spec/requests/api/v1/filters_spec.rb +++ b/spec/requests/api/v1/filters_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Filters' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/filters' do let(:scopes) { 'read:filters' } diff --git a/spec/requests/api/v1/follow_requests_spec.rb b/spec/requests/api/v1/follow_requests_spec.rb index f0f73d38ad0..7ebfce3d918 100644 --- a/spec/requests/api/v1/follow_requests_spec.rb +++ b/spec/requests/api/v1/follow_requests_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Follow requests' do - let(:user) { Fabricate(:user, account_attributes: { locked: true }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows write:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows write:follows' + + let(:user) { Fabricate(:user, account_attributes: { locked: true }) } describe 'GET /api/v1/follow_requests' do subject do diff --git a/spec/requests/api/v1/followed_tags_spec.rb b/spec/requests/api/v1/followed_tags_spec.rb index b0191b523fc..a19162c012f 100644 --- a/spec/requests/api/v1/followed_tags_spec.rb +++ b/spec/requests/api/v1/followed_tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Followed tags' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' describe 'GET /api/v1/followed_tags' do subject do diff --git a/spec/requests/api/v1/instance_spec.rb b/spec/requests/api/v1/instance_spec.rb index 821cbfec614..a0cd32e5ab6 100644 --- a/spec/requests/api/v1/instance_spec.rb +++ b/spec/requests/api/v1/instance_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Instances' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/instance' do context 'when not logged in' do diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb index 226632c5ac2..04a03998ad5 100644 --- a/spec/requests/api/v1/lists_spec.rb +++ b/spec/requests/api/v1/lists_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Lists' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:lists write:lists' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:lists write:lists' describe 'GET /api/v1/lists' do subject do diff --git a/spec/requests/api/v1/markers_spec.rb b/spec/requests/api/v1/markers_spec.rb index d7cd78924bc..0e6ecc56855 100644 --- a/spec/requests/api/v1/markers_spec.rb +++ b/spec/requests/api/v1/markers_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API Markers' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses write:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses write:statuses' describe 'GET /api/v1/markers' do before do diff --git a/spec/requests/api/v1/media_spec.rb b/spec/requests/api/v1/media_spec.rb index 4d6e250207d..347ff4b2797 100644 --- a/spec/requests/api/v1/media_spec.rb +++ b/spec/requests/api/v1/media_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Media' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:media' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:media' describe 'GET /api/v1/media/:id' do subject do diff --git a/spec/requests/api/v1/mutes_spec.rb b/spec/requests/api/v1/mutes_spec.rb index 61e32cb9ae0..4b94a5cb5a8 100644 --- a/spec/requests/api/v1/mutes_spec.rb +++ b/spec/requests/api/v1/mutes_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Mutes' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:mutes' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:mutes' describe 'GET /api/v1/mutes' do subject do diff --git a/spec/requests/api/v1/notifications_spec.rb b/spec/requests/api/v1/notifications_spec.rb index 0e8eb6ad3ba..f2ff396795c 100644 --- a/spec/requests/api/v1/notifications_spec.rb +++ b/spec/requests/api/v1/notifications_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Notifications' do + include_context 'with API authentication', oauth_scopes: 'read:notifications write:notifications' + let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:notifications write:notifications' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/notifications/unread_count', :inline_jobs do subject do diff --git a/spec/requests/api/v1/polls_spec.rb b/spec/requests/api/v1/polls_spec.rb index c93231e1ee6..f01f112da9d 100644 --- a/spec/requests/api/v1/polls_spec.rb +++ b/spec/requests/api/v1/polls_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Polls' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses' describe 'GET /api/v1/polls/:id' do subject do diff --git a/spec/requests/api/v1/preferences_spec.rb b/spec/requests/api/v1/preferences_spec.rb index e03b9cf1087..02d63c9d28c 100644 --- a/spec/requests/api/v1/preferences_spec.rb +++ b/spec/requests/api/v1/preferences_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Preferences' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/preferences' do context 'when not authorized' do diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index de7a20b133c..131df7a278e 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' RSpec.describe 'Deleting profile images' do + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:account) do Fabricate( :account, @@ -10,9 +12,7 @@ RSpec.describe 'Deleting profile images' do header: fixture_file_upload('attachment.jpg', 'image/jpeg') ) end - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: account.user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:user) { account.user } describe 'DELETE /api/v1/profile' do context 'when deleting an avatar' do diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index 1f113c649ee..38d9f542c29 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Reports' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:reports' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:reports' describe 'POST /api/v1/reports' do subject do diff --git a/spec/requests/api/v1/scheduled_status_spec.rb b/spec/requests/api/v1/scheduled_status_spec.rb index 3a1b81ce65c..ad446257b40 100644 --- a/spec/requests/api/v1/scheduled_status_spec.rb +++ b/spec/requests/api/v1/scheduled_status_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Scheduled Statuses' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/scheduled_statuses' do context 'when not authorized' do diff --git a/spec/requests/api/v1/statuses/translations_spec.rb b/spec/requests/api/v1/statuses/translations_spec.rb index e95ec1b101a..eb17d9f6a76 100644 --- a/spec/requests/api/v1/statuses/translations_spec.rb +++ b/spec/requests/api/v1/statuses/translations_spec.rb @@ -50,6 +50,21 @@ RSpec.describe 'API V1 Statuses Translations' do end end + context 'with a public status marked with the same language as the current locale when translation backend cannot do same-language translation' do + let(:status) { Fabricate(:status, account: user.account, text: 'Esto está en español pero está marcado como inglés.', language: 'en') } + + it 'returns http forbidden with error message' do + subject + + expect(response) + .to have_http_status(403) + expect(response.media_type) + .to eq('application/json') + expect(response.parsed_body) + .to include(error: /not allowed/) + end + end + context 'with a private status' do let(:status) { Fabricate(:status, visibility: :private, account: user.account, text: 'Hola', language: 'es') } diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 1f431f79d85..e63437cc66f 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -4,10 +4,10 @@ require 'rails_helper' RSpec.describe '/api/v1/statuses' do context 'with an oauth token' do - let(:user) { Fabricate(:user) } + include_context 'with API authentication' + let(:client_app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: client_app, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/statuses?id[]=:id' do let(:status) { Fabricate(:status) } diff --git a/spec/requests/api/v1/suggestions_spec.rb b/spec/requests/api/v1/suggestions_spec.rb index 0a32d8899bc..13ac6e78558 100644 --- a/spec/requests/api/v1/suggestions_spec.rb +++ b/spec/requests/api/v1/suggestions_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Suggestions' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' describe 'GET /api/v1/suggestions' do subject do diff --git a/spec/requests/api/v1/tags_spec.rb b/spec/requests/api/v1/tags_spec.rb index 5beda68db0a..42b9180880d 100644 --- a/spec/requests/api/v1/tags_spec.rb +++ b/spec/requests/api/v1/tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Tags' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'write:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:follows' describe 'GET /api/v1/tags/:id' do subject do diff --git a/spec/requests/api/v1_alpha/async_refreshes_spec.rb b/spec/requests/api/v1_alpha/async_refreshes_spec.rb index 0cd85cf99bf..8b29b7599c6 100644 --- a/spec/requests/api/v1_alpha/async_refreshes_spec.rb +++ b/spec/requests/api/v1_alpha/async_refreshes_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'AsyncRefreshes' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' + let(:job) { AsyncRefresh.new('test_job') } describe 'GET /api/v1_alpha/async_refreshes/:id' do diff --git a/spec/requests/api/v2/filters_spec.rb b/spec/requests/api/v2/filters_spec.rb index 304afc7bd8d..cfa607cff06 100644 --- a/spec/requests/api/v2/filters_spec.rb +++ b/spec/requests/api/v2/filters_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Filters' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:filters write:filters' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:filters write:filters' shared_examples 'unauthorized for invalid token' do let(:headers) { { 'Authorization' => '' } } diff --git a/spec/requests/api/v2/instance_spec.rb b/spec/requests/api/v2/instance_spec.rb index 788d30fa699..92a9744e416 100644 --- a/spec/requests/api/v2/instance_spec.rb +++ b/spec/requests/api/v2/instance_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Instances' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v2/instance' do context 'when logged out' do diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb index 18ebb9cddae..04e48bc02c3 100644 --- a/spec/requests/api/v2/media_spec.rb +++ b/spec/requests/api/v2/media_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Media API', :attachment_processing do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write' describe 'POST /api/v2/media' do context 'when small media format attachment is processed immediately' do diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index 69feb6cb6e0..4b4aa1b4751 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Notifications' do + include_context 'with API authentication', oauth_scopes: 'read:notifications write:notifications' + let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:notifications write:notifications' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v2/notifications/unread_count', :inline_jobs do subject do diff --git a/spec/requests/api/v2/search_spec.rb b/spec/requests/api/v2/search_spec.rb index 6beab4c8c7d..c60861b48f0 100644 --- a/spec/requests/api/v2/search_spec.rb +++ b/spec/requests/api/v2/search_spec.rb @@ -4,10 +4,7 @@ require 'rails_helper' RSpec.describe 'Search API' do context 'with token' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:search' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:search' describe 'GET /api/v2/search' do let!(:bob) { Fabricate(:account, username: 'bob_test') } diff --git a/spec/requests/api/v2/suggestions_spec.rb b/spec/requests/api/v2/suggestions_spec.rb index 578bf1b61b9..dd876046bcc 100644 --- a/spec/requests/api/v2/suggestions_spec.rb +++ b/spec/requests/api/v2/suggestions_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Suggestions API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' describe 'GET /api/v2/suggestions' do let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/web/embeds_spec.rb b/spec/requests/api/web/embeds_spec.rb index 3cc2f977f87..ad71172e87d 100644 --- a/spec/requests/api/web/embeds_spec.rb +++ b/spec/requests/api/web/embeds_spec.rb @@ -65,9 +65,7 @@ RSpec.describe '/api/web/embed' do end context 'with an API token' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' context 'when the requested status is local' do let(:id) { status.id } diff --git a/spec/requests/media_spec.rb b/spec/requests/media_spec.rb index a448a87492e..523c4689d6a 100644 --- a/spec/requests/media_spec.rb +++ b/spec/requests/media_spec.rb @@ -87,4 +87,17 @@ RSpec.describe 'Media' do end end end + + describe 'GET /media/:medium_id/player' do + context 'when media type is not large format type' do + let(:media) { Fabricate :media_attachment } + + it 'responds with not found' do + get medium_player_path(media) + + expect(response) + .to have_http_status(404) + end + end + end end diff --git a/spec/support/api_authentication.rb b/spec/support/api_authentication.rb new file mode 100644 index 00000000000..83f76ab0773 --- /dev/null +++ b/spec/support/api_authentication.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.shared_context 'with API authentication' do |oauth_scopes: '', user_fabricator: :user| + let(:user) { Fabricate(user_fabricator) } + let(:scopes) { oauth_scopes } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } +end diff --git a/spec/support/examples/api.rb b/spec/support/examples/api.rb index ddc61fcbe08..350166b10d5 100644 --- a/spec/support/examples/api.rb +++ b/spec/support/examples/api.rb @@ -13,6 +13,7 @@ end RSpec.shared_examples 'forbidden for wrong role' do |wrong_role| let(:role) { UserRole.find_by(name: wrong_role) } + let(:user) { Fabricate(:user, role:) } it 'returns http forbidden' do # Some examples have a subject which needs to be called to make a request diff --git a/spec/system/media_spec.rb b/spec/system/media_spec.rb index d014c7e88ef..ec069cdcaa9 100644 --- a/spec/system/media_spec.rb +++ b/spec/system/media_spec.rb @@ -4,19 +4,47 @@ require 'rails_helper' RSpec.describe 'Media' do describe 'Player page' do + let(:status) { Fabricate :status } + + before { status.media_attachments << media } + context 'when signed in' do before { sign_in Fabricate(:user) } - it 'visits the media player page and renders the media' do - status = Fabricate :status - media = Fabricate :media_attachment, type: :video - status.media_attachments << media + context 'when media type is video' do + let(:media) { Fabricate :media_attachment, type: :video } - visit medium_player_path(media) + it 'visits the player page and renders media' do + visit medium_player_path(media) - expect(page) - .to have_css('body', class: 'player') - .and have_css('div[data-component="Video"]') + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="Video"] video[controls="controls"] source') + end + end + + context 'when media type is gifv' do + let(:media) { Fabricate :media_attachment, type: :gifv } + + it 'visits the player page and renders media' do + visit medium_player_path(media) + + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="MediaGallery"] video[loop="loop"] source') + end + end + + context 'when media type is audio' do + let(:media) { Fabricate :media_attachment, type: :audio } + + it 'visits the player page and renders media' do + visit medium_player_path(media) + + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="Audio"] audio source') + end end end end diff --git a/stylelint.config.js b/stylelint.config.js index b1f34b7f190..29fe8262aa1 100644 --- a/stylelint.config.js +++ b/stylelint.config.js @@ -2,6 +2,7 @@ module.exports = { extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier-scss'], ignoreFiles: [ 'app/javascript/styles/mastodon/reset.scss', + 'app/javascript/styles_new/mastodon/reset.scss', 'coverage/**/*', 'node_modules/**/*', 'public/assets/**/*', @@ -31,7 +32,7 @@ module.exports = { }, overrides: [ { - 'files': ['app/javascript/styles/entrypoints/mailer.scss'], + 'files': ['app/javascript/styles/entrypoints/mailer.scss', 'app/javascript/styles_new/entrypoints/mailer.scss'], rules: { 'property-no-unknown': [ true, diff --git a/vite.config.mts b/vite.config.mts index f2a0ec05976..3250e8f7861 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -15,7 +15,6 @@ import { } from 'vite'; import manifestSRI from 'vite-plugin-manifest-sri'; import { VitePWA } from 'vite-plugin-pwa'; -import { viteStaticCopy } from 'vite-plugin-static-copy'; import svgr from 'vite-plugin-svgr'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -167,21 +166,6 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { }), MastodonThemes(), MastodonAssetsManifest(), - viteStaticCopy({ - targets: [ - { - src: path.resolve( - __dirname, - 'node_modules/emojibase-data/**/compact.json', - ), - dest: 'emoji', - rename(_name, ext, dir) { - const locale = path.basename(path.dirname(dir)); - return `${locale}.${ext}`; - }, - }, - ], - }), MastodonServiceWorkerLocales(), MastodonEmojiCompressed(), legacy({ diff --git a/yarn.lock b/yarn.lock index c27a442ec3a..a7a34cd7cfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -97,39 +97,39 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.18.9, @babel/core@npm:^7.21.3, @babel/core@npm:^7.24.4, @babel/core@npm:^7.26.10, @babel/core@npm:^7.28.0, @babel/core@npm:^7.28.4": - version: 7.28.4 - resolution: "@babel/core@npm:7.28.4" +"@babel/core@npm:^7.18.9, @babel/core@npm:^7.21.3, @babel/core@npm:^7.24.4, @babel/core@npm:^7.26.10, @babel/core@npm:^7.28.0, @babel/core@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/core@npm:7.28.5" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.28.3" + "@babel/generator": "npm:^7.28.5" "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-module-transforms": "npm:^7.28.3" "@babel/helpers": "npm:^7.28.4" - "@babel/parser": "npm:^7.28.4" + "@babel/parser": "npm:^7.28.5" "@babel/template": "npm:^7.27.2" - "@babel/traverse": "npm:^7.28.4" - "@babel/types": "npm:^7.28.4" + "@babel/traverse": "npm:^7.28.5" + "@babel/types": "npm:^7.28.5" "@jridgewell/remapping": "npm:^2.3.5" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10c0/ef5a6c3c6bf40d3589b5593f8118cfe2602ce737412629fb6e26d595be2fcbaae0807b43027a5c42ec4fba5b895ff65891f2503b5918c8a3ea3542ab44d4c278 + checksum: 10c0/535f82238027621da6bdffbdbe896ebad3558b311d6f8abc680637a9859b96edbf929ab010757055381570b29cf66c4a295b5618318d27a4273c0e2033925e72 languageName: node linkType: hard -"@babel/generator@npm:^7.28.3": - version: 7.28.3 - resolution: "@babel/generator@npm:7.28.3" +"@babel/generator@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/generator@npm:7.28.5" dependencies: - "@babel/parser": "npm:^7.28.3" - "@babel/types": "npm:^7.28.2" + "@babel/parser": "npm:^7.28.5" + "@babel/types": "npm:^7.28.5" "@jridgewell/gen-mapping": "npm:^0.3.12" "@jridgewell/trace-mapping": "npm:^0.3.28" jsesc: "npm:^3.0.2" - checksum: 10c0/0ff58bcf04f8803dcc29479b547b43b9b0b828ec1ee0668e92d79f9e90f388c28589056637c5ff2fd7bcf8d153c990d29c448d449d852bf9d1bc64753ca462bc + checksum: 10c0/9f219fe1d5431b6919f1a5c60db8d5d34fe546c0d8f5a8511b32f847569234ffc8032beb9e7404649a143f54e15224ecb53a3d11b6bb85c3203e573d91fca752 languageName: node linkType: hard @@ -334,7 +334,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.3, @babel/parser@npm:^7.28.4": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" dependencies: @@ -1194,22 +1194,22 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4": - version: 7.28.4 - resolution: "@babel/traverse@npm:7.28.4" +"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/traverse@npm:7.28.5" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.28.3" + "@babel/generator": "npm:^7.28.5" "@babel/helper-globals": "npm:^7.28.0" - "@babel/parser": "npm:^7.28.4" + "@babel/parser": "npm:^7.28.5" "@babel/template": "npm:^7.27.2" - "@babel/types": "npm:^7.28.4" + "@babel/types": "npm:^7.28.5" debug: "npm:^4.3.1" - checksum: 10c0/ee678fdd49c9f54a32e07e8455242390d43ce44887cea6567b233fe13907b89240c377e7633478a32c6cf1be0e17c2f7f3b0c59f0666e39c5074cc47b968489c + checksum: 10c0/f6c4a595993ae2b73f2d4cd9c062f2e232174d293edd4abe1d715bd6281da8d99e47c65857e8d0917d9384c65972f4acdebc6749a7c40a8fcc38b3c7fb3e706f languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.25.4, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.25.4, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" dependencies: @@ -1226,24 +1226,6 @@ __metadata: languageName: node linkType: hard -"@bundled-es-modules/cookie@npm:^2.0.1": - version: 2.0.1 - resolution: "@bundled-es-modules/cookie@npm:2.0.1" - dependencies: - cookie: "npm:^0.7.2" - checksum: 10c0/dfac5e36127e827c5557b8577f17a8aa94c057baff6d38555917927b99da0ecf0b1357e7fedadc8853ecdbd4a8a7fa1f5e64111b2a656612f4a36376f5bdbe8d - languageName: node - linkType: hard - -"@bundled-es-modules/statuses@npm:^1.0.1": - version: 1.0.1 - resolution: "@bundled-es-modules/statuses@npm:1.0.1" - dependencies: - statuses: "npm:^2.0.1" - checksum: 10c0/c1a8ede3efa8da61ccda4b98e773582a9733edfbeeee569d4630785f8e018766202edb190a754a3ec7a7f6bd738e857829affc2fdb676b6dab4db1bb44e62785 - languageName: node - linkType: hard - "@cacheable/memoize@npm:^2.0.1": version: 2.0.1 resolution: "@cacheable/memoize@npm:2.0.1" @@ -2733,10 +2715,10 @@ __metadata: "@rails/ujs": "npm:7.1.600" "@react-spring/web": "npm:^9.7.5" "@reduxjs/toolkit": "npm:^2.0.1" - "@storybook/addon-a11y": "npm:^10.0.2" - "@storybook/addon-docs": "npm:^10.0.2" - "@storybook/addon-vitest": "npm:^10.0.2" - "@storybook/react-vite": "npm:^10.0.2" + "@storybook/addon-a11y": "npm:^10.0.6" + "@storybook/addon-docs": "npm:^10.0.6" + "@storybook/addon-vitest": "npm:^10.0.6" + "@storybook/react-vite": "npm:^10.0.6" "@testing-library/dom": "npm:^10.4.1" "@testing-library/react": "npm:^16.3.0" "@types/debug": "npm:^4" @@ -2775,13 +2757,14 @@ __metadata: babel-plugin-formatjs: "npm:^10.5.37" babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24" blurhash: "npm:^2.0.5" - chromatic: "npm:^13.1.3" + chromatic: "npm:^13.3.3" classnames: "npm:^2.3.2" cocoon-js-vanilla: "npm:^1.5.1" color-blend: "npm:^4.0.0" core-js: "npm:^3.30.2" cross-env: "npm:^10.0.0" debug: "npm:^4.4.1" + delegated-events: "npm:^1.1.2" detect-passive-events: "npm:^2.0.3" emoji-mart: "npm:emoji-mart-lazyload@latest" emojibase: "npm:^16.0.0" @@ -2814,8 +2797,8 @@ __metadata: lint-staged: "npm:^16.2.6" lodash: "npm:^4.17.21" marky: "npm:^1.2.5" - msw: "npm:^2.10.2" - msw-storybook-addon: "npm:^2.0.5" + msw: "npm:^2.12.1" + msw-storybook-addon: "npm:^2.0.6" path-complete-extname: "npm:^1.0.0" playwright: "npm:^1.56.1" postcss-preset-env: "npm:^10.1.5" @@ -2846,7 +2829,7 @@ __metadata: sass: "npm:^1.62.1" scroll-behavior: "npm:^0.11.0" stacktrace-js: "npm:^2.0.2" - storybook: "npm:^10.0.2" + storybook: "npm:^10.0.5" stringz: "npm:^2.1.0" stylelint: "npm:^16.19.1" stylelint-config-prettier-scss: "npm:^1.0.0" @@ -2862,7 +2845,6 @@ __metadata: vite: "npm:^7.1.1" vite-plugin-manifest-sri: "npm:^0.2.0" vite-plugin-pwa: "npm:^1.0.2" - vite-plugin-static-copy: "npm:^3.1.1" vite-plugin-svgr: "npm:^4.3.0" vite-tsconfig-paths: "npm:^5.1.4" vitest: "npm:^4.0.5" @@ -2928,9 +2910,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.39.1": - version: 0.39.2 - resolution: "@mswjs/interceptors@npm:0.39.2" +"@mswjs/interceptors@npm:^0.40.0": + version: 0.40.0 + resolution: "@mswjs/interceptors@npm:0.40.0" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -2938,7 +2920,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10c0/5698e33930a6b6e7cc78cf762291be60c91c6348faa22750acc41ef41528e7891e74541ccfb668ba470d964233fd2121c44d0224a2917eedeba2459cf0b78ca2 + checksum: 10c0/4500f17b65910b2633182fdb15a81ccb6ccd4488a8c45bc2f7acdaaff4621c3cce5362e6b59ddc4fa28d315d0efb0608fd1f0d536bc5345141f8ac03fd7fab22 languageName: node linkType: hard @@ -3333,10 +3315,10 @@ __metadata: languageName: node linkType: hard -"@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 +"@rolldown/pluginutils@npm:1.0.0-beta.47": + version: 1.0.0-beta.47 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.47" + checksum: 10c0/eb0cfa7334d66f090c47eaac612174936b05f26e789352428cb6e03575b590f355de30d26b42576ea4e613d8887b587119d19b2e4b3a8909ceb232ca1cf746c8 languageName: node linkType: hard @@ -3600,38 +3582,38 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-a11y@npm:^10.0.2": - version: 10.0.2 - resolution: "@storybook/addon-a11y@npm:10.0.2" +"@storybook/addon-a11y@npm:^10.0.6": + version: 10.0.6 + resolution: "@storybook/addon-a11y@npm:10.0.6" dependencies: "@storybook/global": "npm:^5.0.0" axe-core: "npm:^4.2.0" peerDependencies: - storybook: ^10.0.2 - checksum: 10c0/a47fed1aa098328bea173f257a0140dff4a5e2d2d6c90b0fa88a33a82583658afa09fa04d012930900fe87453a48d781847b7f5193da0f09b0b8598032596ca2 + storybook: ^10.0.6 + checksum: 10c0/44ab785b48476a9de44686be2d60c3720ed59c0d95d4ca7e5693ec2acb0cc2b062e4abde0a04f6bc0aeb54156e00a65803bb85c2a21f69c8329139096316b299 languageName: node linkType: hard -"@storybook/addon-docs@npm:^10.0.2": - version: 10.0.2 - resolution: "@storybook/addon-docs@npm:10.0.2" +"@storybook/addon-docs@npm:^10.0.6": + version: 10.0.6 + resolution: "@storybook/addon-docs@npm:10.0.6" dependencies: "@mdx-js/react": "npm:^3.0.0" - "@storybook/csf-plugin": "npm:10.0.2" + "@storybook/csf-plugin": "npm:10.0.6" "@storybook/icons": "npm:^1.6.0" - "@storybook/react-dom-shim": "npm:10.0.2" + "@storybook/react-dom-shim": "npm:10.0.6" react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^10.0.2 - checksum: 10c0/167e8b4b230431ac8afcb6dfceedc8450d8d2d5a84c61e7a5f8e28a1a47d1031194a780f16ed697bce03cce10c71a6aa9bb1f102bf68693c96cfc710f4b5dadb + storybook: ^10.0.6 + checksum: 10c0/4b1a59416cf54853a09a156e1e1016c13f4d477f73562be960b12d86eeb86d1f1d2da5111255cc9da1cd232955d0ea68b1c42e4e87671042682dcb7eb2a059a0 languageName: node linkType: hard -"@storybook/addon-vitest@npm:^10.0.2": - version: 10.0.2 - resolution: "@storybook/addon-vitest@npm:10.0.2" +"@storybook/addon-vitest@npm:^10.0.6": + version: 10.0.6 + resolution: "@storybook/addon-vitest@npm:10.0.6" dependencies: "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.6.0" @@ -3641,7 +3623,7 @@ __metadata: "@vitest/browser": ^3.0.0 || ^4.0.0 "@vitest/browser-playwright": ^4.0.0 "@vitest/runner": ^3.0.0 || ^4.0.0 - storybook: ^10.0.2 + storybook: ^10.0.6 vitest: ^3.0.0 || ^4.0.0 peerDependenciesMeta: "@vitest/browser": @@ -3652,32 +3634,32 @@ __metadata: optional: true vitest: optional: true - checksum: 10c0/b97b6b01b242f1c71a44eadbe4d993a355954dab2515b6d21771bc1ff55636ffd32474ba4138d2f6d45a752c2b11cf6ac7aef68b9213f5188bc2ba90ee5d12c0 + checksum: 10c0/6377cfbac4c2f9f9b43006131e0273c2d2f8b76b0ce01fdcb42f2935ac245a2236fab60b2543507a69852dba1ac4e3b770b0a96f5595123e0f9599609d0a57e2 languageName: node linkType: hard -"@storybook/builder-vite@npm:10.0.2": - version: 10.0.2 - resolution: "@storybook/builder-vite@npm:10.0.2" +"@storybook/builder-vite@npm:10.0.6": + version: 10.0.6 + resolution: "@storybook/builder-vite@npm:10.0.6" dependencies: - "@storybook/csf-plugin": "npm:10.0.2" + "@storybook/csf-plugin": "npm:10.0.6" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^10.0.2 + storybook: ^10.0.6 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - checksum: 10c0/a93bbb763c1f5d47b7b6c36f9225ca9a1f9f70c9f07c7b4ee61dd97f77a676b7d31c5c27b6ca48bb237fbaed626f592653380c2999bf9ae48be1071c0c007acf + checksum: 10c0/1e5f163f2abd62f99292ee48cde10f68d8db1ba6f4613c20cb2af679d44c3b548c7a2209338d24b4ffda2a245ae68bfcfc57af9f76de7f4a251253635c4179d8 languageName: node linkType: hard -"@storybook/csf-plugin@npm:10.0.2": - version: 10.0.2 - resolution: "@storybook/csf-plugin@npm:10.0.2" +"@storybook/csf-plugin@npm:10.0.6": + version: 10.0.6 + resolution: "@storybook/csf-plugin@npm:10.0.6" dependencies: unplugin: "npm:^2.3.5" peerDependencies: esbuild: "*" rollup: "*" - storybook: ^10.0.2 + storybook: ^10.0.6 vite: "*" webpack: "*" peerDependenciesMeta: @@ -3689,7 +3671,7 @@ __metadata: optional: true webpack: optional: true - checksum: 10c0/422823c3635c5bab48a258d8f6e7dadf02484afdfb48b08005be962d8bb5b4c8f507f3d68b79523ff11901178a7e913f98c047ec7aced5bcdf47a846c2bcd66f + checksum: 10c0/422286e7d2ef3f64ea2a71bdd1cad0bc3e850b31574f048529616eeb3cd0b1216b5d680c8b36bd300e31d141ad7781586cc7d57763babf993c31430b854491c4 languageName: node linkType: hard @@ -3710,25 +3692,25 @@ __metadata: languageName: node linkType: hard -"@storybook/react-dom-shim@npm:10.0.2": - version: 10.0.2 - resolution: "@storybook/react-dom-shim@npm:10.0.2" +"@storybook/react-dom-shim@npm:10.0.6": + version: 10.0.6 + resolution: "@storybook/react-dom-shim@npm:10.0.6" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.0.2 - checksum: 10c0/8d915c4c0f4e88cc582fe653aadc9b7797105081e84bb22d4911dbb72c6dd4df0054bb074ae92ad1634a92332f4331d250431ca654f621de9f7f0a53e0eefff6 + storybook: ^10.0.6 + checksum: 10c0/067b86aeadc96d0fedccd3e047c4b506484e7b2af21796b995e97cd3f4f31d77f8a2674fb29f77b45245353cc714a4511f49fa618a7386ba12706c663701da08 languageName: node linkType: hard -"@storybook/react-vite@npm:^10.0.2": - version: 10.0.2 - resolution: "@storybook/react-vite@npm:10.0.2" +"@storybook/react-vite@npm:^10.0.6": + version: 10.0.6 + resolution: "@storybook/react-vite@npm:10.0.6" dependencies: "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.6.1" "@rollup/pluginutils": "npm:^5.0.2" - "@storybook/builder-vite": "npm:10.0.2" - "@storybook/react": "npm:10.0.2" + "@storybook/builder-vite": "npm:10.0.6" + "@storybook/react": "npm:10.0.6" empathic: "npm:^2.0.0" magic-string: "npm:^0.30.0" react-docgen: "npm:^8.0.0" @@ -3737,27 +3719,27 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.0.2 + storybook: ^10.0.6 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - checksum: 10c0/982e903ce89d5a87676bd345abee1cf26d4c78d477c5659b7c279ab5af80e3b1246610fd5f434e33895ca2dcd2efd06f7238c82b1b84f6fdec9e3ed0907f4cb3 + checksum: 10c0/1689b7d866650912a8fe5ff2ec1c35292d408085d2d819f3a13055805f72d67b5c0fbeabe359de9a65f6f129500e20b935e832b280559afe5050b10432ccf6f2 languageName: node linkType: hard -"@storybook/react@npm:10.0.2": - version: 10.0.2 - resolution: "@storybook/react@npm:10.0.2" +"@storybook/react@npm:10.0.6": + version: 10.0.6 + resolution: "@storybook/react@npm:10.0.6" dependencies: "@storybook/global": "npm:^5.0.0" - "@storybook/react-dom-shim": "npm:10.0.2" + "@storybook/react-dom-shim": "npm:10.0.6" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.0.2 + storybook: ^10.0.6 typescript: ">= 4.9.x" peerDependenciesMeta: typescript: optional: true - checksum: 10c0/298054e8c5e61f6d7392154e704f5db26ce22c9cdc1cd178c020eb6abd30dc380e10be28269b8d9c7706b8edefaa737360af906d7e0d6ffc4cbac76f1d835805 + checksum: 10c0/7545a3f84a64ccfacc84f41d4f4b744fb71861f1a413ba48e4440694fb89f591273182771324097c420fca65253d738bde852e14a776e5b1df30a0d4beb100b0 languageName: node linkType: hard @@ -4054,13 +4036,6 @@ __metadata: languageName: node linkType: hard -"@types/cookie@npm:^0.6.0": - version: 0.6.0 - resolution: "@types/cookie@npm:0.6.0" - checksum: 10c0/5b326bd0188120fb32c0be086b141b1481fec9941b76ad537f9110e10d61ee2636beac145463319c71e4be67a17e85b81ca9e13ceb6e3bb63b93d16824d6c149 - languageName: node - linkType: hard - "@types/cors@npm:^2.8.16": version: 2.8.19 resolution: "@types/cors@npm:2.8.19" @@ -4838,18 +4813,18 @@ __metadata: linkType: hard "@vitejs/plugin-react@npm:^5.0.0": - version: 5.1.0 - resolution: "@vitejs/plugin-react@npm:5.1.0" + version: 5.1.1 + resolution: "@vitejs/plugin-react@npm:5.1.1" dependencies: - "@babel/core": "npm:^7.28.4" + "@babel/core": "npm:^7.28.5" "@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.43" + "@rolldown/pluginutils": "npm:1.0.0-beta.47" "@types/babel__core": "npm:^7.20.5" react-refresh: "npm:^0.18.0" peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - checksum: 10c0/e192a12e2b854df109eafb1d06c0bc848e8e2b162c686aa6b999b1048658983e72674b2068ccc37562fcce44d32ad92b65f3a4e1897a0cb7859c2ee69cc63eac + checksum: 10c0/e590efaea1eabfbb1beb6e8c9fac0742fd299808e3368e63b2825ce24740adb8a28fcb2668b14b7ca1bdb42890cfefe94d02dd358dcbbf8a27ddf377b9a82abf languageName: node linkType: hard @@ -5192,16 +5167,6 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:~3.1.2": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac - languageName: node - linkType: hard - "are-docs-informative@npm:^0.0.2": version: 0.0.2 resolution: "are-docs-informative@npm:0.0.2" @@ -5588,13 +5553,6 @@ __metadata: languageName: node linkType: hard -"binary-extensions@npm:^2.0.0": - version: 2.3.0 - resolution: "binary-extensions@npm:2.3.0" - checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 - languageName: node - linkType: hard - "bintrees@npm:1.0.2": version: 1.0.2 resolution: "bintrees@npm:1.0.2" @@ -5652,7 +5610,7 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.3, braces@npm:~3.0.2": +"braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" dependencies: @@ -5853,25 +5811,6 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.6.0": - version: 3.6.0 - resolution: "chokidar@npm:3.6.0" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 - languageName: node - linkType: hard - "chokidar@npm:^4.0.0": version: 4.0.0 resolution: "chokidar@npm:4.0.0" @@ -5888,9 +5827,9 @@ __metadata: languageName: node linkType: hard -"chromatic@npm:^13.1.3": - version: 13.2.0 - resolution: "chromatic@npm:13.2.0" +"chromatic@npm:^13.3.3": + version: 13.3.3 + resolution: "chromatic@npm:13.3.3" peerDependencies: "@chromatic-com/cypress": ^0.*.* || ^1.0.0 "@chromatic-com/playwright": ^0.*.* || ^1.0.0 @@ -5903,7 +5842,7 @@ __metadata: chroma: dist/bin.js chromatic: dist/bin.js chromatic-cli: dist/bin.js - checksum: 10c0/0f3419b45c648746ce4bb332a8c00548a41d0981a83c44f259fccced83245109448f1f713dd0379af4f386f0e614b11a52be5c0693ec71a99edc8aaed477c5bc + checksum: 10c0/6fc54df030113d91ef00a2050f5cb13ca182b355dae2c29cdd326fac6cf21d8ddc2cd93dc3f5db04379b7769d4df8e3ea5f18c3642e9e3a48545565f992a838c languageName: node linkType: hard @@ -6083,13 +6022,20 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.7.1, cookie@npm:^0.7.2": +"cookie@npm:^0.7.1": version: 0.7.2 resolution: "cookie@npm:0.7.2" checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2 languageName: node linkType: hard +"cookie@npm:^1.0.2": + version: 1.0.2 + resolution: "cookie@npm:1.0.2" + checksum: 10c0/fd25fe79e8fbcfcaf6aa61cd081c55d144eeeba755206c058682257cb38c4bd6795c6620de3f064c740695bb65b7949ebb1db7a95e4636efb8357a335ad3f54b + languageName: node + linkType: hard + "copy-anything@npm:^2.0.1": version: 2.0.6 resolution: "copy-anything@npm:2.0.6" @@ -6116,9 +6062,9 @@ __metadata: linkType: hard "core-js@npm:^3.30.2, core-js@npm:^3.45.0": - version: 3.46.0 - resolution: "core-js@npm:3.46.0" - checksum: 10c0/12d559d39a58227881bc6c86c36d24dcfbe2d56e52dac42e35e8643278172596ab67f57ede98baf40b153ca1b830f37420ea32c3f7417c0c5a1fed46438ae187 + version: 3.47.0 + resolution: "core-js@npm:3.47.0" + checksum: 10c0/9b1a7088b7c660c7b8f1d4c90bb1816a8d5352ebdcb7bc742e3a0e4eb803316b5aa17bacb8769522342196351a5430178f46914644f2bfdb94ce0ced3c7fd523 languageName: node linkType: hard @@ -6443,6 +6389,15 @@ __metadata: languageName: node linkType: hard +"delegated-events@npm:^1.1.2": + version: 1.1.2 + resolution: "delegated-events@npm:1.1.2" + dependencies: + selector-set: "npm:^1.1.5" + checksum: 10c0/b295a6d6c6cef4b9312bfd4132ac3a1255f3c2fadf3692a04cf7ddf8f0d472bfce9de06323faa75e922d20e5674d45022e1a5378b8500cd7d573ffa0cf7ca602 + languageName: node + linkType: hard + "denque@npm:^2.1.0": version: 2.1.0 resolution: "denque@npm:2.1.0" @@ -7877,7 +7832,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": +"glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" dependencies: @@ -7896,8 +7851,8 @@ __metadata: linkType: hard "glob@npm:^10.0.0, glob@npm:^10.2.2": - version: 10.4.5 - resolution: "glob@npm:10.4.5" + version: 10.5.0 + resolution: "glob@npm:10.5.0" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -7907,7 +7862,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 languageName: node linkType: hard @@ -8481,15 +8436,6 @@ __metadata: languageName: node linkType: hard -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 - languageName: node - linkType: hard - "is-boolean-object@npm:^1.2.1": version: 1.2.2 resolution: "is-boolean-object@npm:1.2.2" @@ -8596,7 +8542,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -8929,13 +8875,13 @@ __metadata: linkType: hard "js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" dependencies: argparse: "npm:^2.0.1" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f + checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 languageName: node linkType: hard @@ -9742,28 +9688,26 @@ __metadata: languageName: node linkType: hard -"msw-storybook-addon@npm:^2.0.5": - version: 2.0.5 - resolution: "msw-storybook-addon@npm:2.0.5" +"msw-storybook-addon@npm:^2.0.6": + version: 2.0.6 + resolution: "msw-storybook-addon@npm:2.0.6" dependencies: is-node-process: "npm:^1.0.1" peerDependencies: msw: ^2.0.0 - checksum: 10c0/3f26fd4a8a6b1b4da165a8940eca4da2e175a69036a1c85c07ec1952fbb595252db689c4380d8f88ec1cfaa66a6696e90ef0c26b2d1bf17c30092b81247d1d40 + checksum: 10c0/e108e8bf247326f5475c1914fdcfaaa6603f1e46f0920ed40d1f1725e2c2f62698658ff8b7d75aee0a0ea1fc0a9714fd6caaf5278426b6dcefd5da381f462c8e languageName: node linkType: hard -"msw@npm:^2.10.2": - version: 2.11.3 - resolution: "msw@npm:2.11.3" +"msw@npm:^2.12.1": + version: 2.12.1 + resolution: "msw@npm:2.12.1" dependencies: - "@bundled-es-modules/cookie": "npm:^2.0.1" - "@bundled-es-modules/statuses": "npm:^1.0.1" "@inquirer/confirm": "npm:^5.0.0" - "@mswjs/interceptors": "npm:^0.39.1" + "@mswjs/interceptors": "npm:^0.40.0" "@open-draft/deferred-promise": "npm:^2.2.0" - "@types/cookie": "npm:^0.6.0" "@types/statuses": "npm:^2.0.4" + cookie: "npm:^1.0.2" graphql: "npm:^16.8.1" headers-polyfill: "npm:^4.0.2" is-node-process: "npm:^1.2.0" @@ -9771,6 +9715,7 @@ __metadata: path-to-regexp: "npm:^6.3.0" picocolors: "npm:^1.1.1" rettime: "npm:^0.7.0" + statuses: "npm:^2.0.2" strict-event-emitter: "npm:^0.5.1" tough-cookie: "npm:^6.0.0" type-fest: "npm:^4.26.1" @@ -9783,7 +9728,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10c0/847cb0e66152328d898474cd12e2ea0b9c11ed997b3a1129018a99afd7ff1134e45aed7158d0a57a1052b7a80b241e91952285f915142bd70c905316dbe9a49d + checksum: 10c0/822f4fc0cb2bdade39a67045d56b32fc7b15f30814a64c637a3c55d99358a4c1d61ed00d21fafafbbee320ad600e5a048d938b195e0cef5c59e016a040595176 languageName: node linkType: hard @@ -9927,7 +9872,7 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": +"normalize-path@npm:^3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 @@ -10134,7 +10079,7 @@ __metadata: languageName: node linkType: hard -"p-map@npm:^7.0.2, p-map@npm:^7.0.3": +"p-map@npm:^7.0.2": version: 7.0.3 resolution: "p-map@npm:7.0.3" checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c @@ -10399,7 +10344,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.2, picomatch@npm:^2.3.1": +"picomatch@npm:^2.2.2, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be @@ -11615,15 +11560,6 @@ __metadata: languageName: node linkType: hard -"readdirp@npm:~3.6.0": - version: 3.6.0 - resolution: "readdirp@npm:3.6.0" - dependencies: - picomatch: "npm:^2.2.1" - checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b - languageName: node - linkType: hard - "real-require@npm:^0.2.0": version: 0.2.0 resolution: "real-require@npm:0.2.0" @@ -12227,6 +12163,13 @@ __metadata: languageName: node linkType: hard +"selector-set@npm:^1.1.5": + version: 1.1.5 + resolution: "selector-set@npm:1.1.5" + checksum: 10c0/4835907846eb8496c2cc4e5ce48355cce1ef3b55e82789542739dcdc71ebfb756e133d1d7f7ec9f0cf4b1c11fc0375a1d3b99a482d9c973493ca85a6d4b012ab + languageName: node + linkType: hard + "semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -12682,7 +12625,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:^2.0.1": +"statuses@npm:^2.0.1, statuses@npm:^2.0.2": version: 2.0.2 resolution: "statuses@npm:2.0.2" checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f @@ -12706,9 +12649,9 @@ __metadata: languageName: node linkType: hard -"storybook@npm:^10.0.2": - version: 10.0.2 - resolution: "storybook@npm:10.0.2" +"storybook@npm:^10.0.5": + version: 10.0.5 + resolution: "storybook@npm:10.0.5" dependencies: "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.6.0" @@ -12728,7 +12671,7 @@ __metadata: optional: true bin: storybook: ./dist/bin/dispatcher.js - checksum: 10c0/abf796718bff0a848959f9dcfdb2699ce8aa824c3d190c401fcff325db0a0fcd5d0436be5628a93284e03a6f8bc5d2be3147eeef352cf9b879e3b7644875dcf5 + checksum: 10c0/ea4bcdbc8d793f53970fe2e72de805bfd5b0872d3640f7526bdf42fbe0114f225c09f3683ab011ac08b5240450fd7726f17c5210d929c6f261dadc851ee09eec languageName: node linkType: hard @@ -14015,20 +13958,6 @@ __metadata: languageName: node linkType: hard -"vite-plugin-static-copy@npm:^3.1.1": - version: 3.1.4 - resolution: "vite-plugin-static-copy@npm:3.1.4" - dependencies: - chokidar: "npm:^3.6.0" - p-map: "npm:^7.0.3" - picocolors: "npm:^1.1.1" - tinyglobby: "npm:^0.2.15" - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 - checksum: 10c0/e733eb123db9ebefbd9c6e5a589f2bfdf71c047ce87190f45575806d893cf19547043ed4a95f30df49d2ac57cb1ba59b3692c559e91181c2321ba363da6c27d3 - languageName: node - linkType: hard - "vite-plugin-svgr@npm:^4.3.0": version: 4.5.0 resolution: "vite-plugin-svgr@npm:4.5.0" @@ -14059,8 +13988,8 @@ __metadata: linkType: hard "vite@npm:^6.0.0 || ^7.0.0, vite@npm:^7.1.1": - version: 7.2.2 - resolution: "vite@npm:7.2.2" + version: 7.2.4 + resolution: "vite@npm:7.2.4" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.5.0" @@ -14109,7 +14038,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/9c76ee441f8dbec645ddaecc28d1f9cf35670ffa91cff69af7b1d5081545331603f0b1289d437b2fa8dc43cdc77b4d96b5bd9c9aed66310f490cb1a06f9c814c + checksum: 10c0/26aa0cad01d6e00f17c837b2a0587ab52f6bd0d0e64606b4220cfc58fa5fa76a4095ef3ea27c886bea542a346363912c4fad9f9462ef1e6757262fedfd5196b2 languageName: node linkType: hard