diff --git a/Gemfile.lock b/Gemfile.lock index daa470e2d4..ead5da76e7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -135,8 +135,8 @@ GEM racc browser (6.2.0) builder (3.3.0) - bundler-audit (0.9.2) - bundler (>= 1.2.0, < 3) + bundler-audit (0.9.3) + bundler (>= 1.2.0) thor (~> 1.0) capybara (3.40.0) addressable diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index fbef61810d..55a03b9804 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -5,6 +5,15 @@ module Admin def index authorize :custom_emoji, :index? + # If filtering by local emojis, remove by_domain filter. + params.delete(:by_domain) if params[:local].present? + + # If filtering by domain, ensure remote filter is set. + if params[:by_domain].present? + params.delete(:local) + params[:remote] = '1' + end + @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]) @form = Form::CustomEmojiBatch.new end diff --git a/app/controllers/api/v1/annual_reports_controller.rb b/app/controllers/api/v1/annual_reports_controller.rb index b1aee288dd..724c7658d7 100644 --- a/app/controllers/api/v1/annual_reports_controller.rb +++ b/app/controllers/api/v1/annual_reports_controller.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true class Api::V1::AnnualReportsController < Api::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index - before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index + include AsyncRefreshesConcern + + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:read, :generate] + before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:read, :generate] before_action :require_user! - before_action :set_annual_report, except: :index + before_action :set_annual_report, only: [:show, :read] def index with_read_replica do @@ -28,14 +30,59 @@ class Api::V1::AnnualReportsController < Api::BaseController relationships: @relationships end + def state + render json: { state: report_state } + end + + def generate + return render_empty unless year == AnnualReport.current_campaign + return render_empty if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year) + + async_refresh = AsyncRefresh.new(refresh_key) + + if async_refresh.running? + add_async_refresh_header(async_refresh, retry_seconds: 2) + return head 202 + end + + add_async_refresh_header(AsyncRefresh.create(refresh_key), retry_seconds: 2) + + GenerateAnnualReportWorker.perform_async(current_account.id, year) + + head 202 + end + def read @annual_report.view! render_empty end + def refresh_key + "wrapstodon:#{current_account.id}:#{year}" + end + private + def report_state + return 'available' if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year) + + async_refresh = AsyncRefresh.new(refresh_key) + + if async_refresh.running? + add_async_refresh_header(async_refresh, retry_seconds: 2) + 'generating' + elsif AnnualReport.current_campaign == year && AnnualReport.new(current_account, year).eligible? + 'eligible' + else + 'ineligible' + end + end + + def year + params[:id]&.to_i + end + def set_annual_report - @annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: params[:id]) + @annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: year) end end diff --git a/app/controllers/api/v1_alpha/collections_controller.rb b/app/controllers/api/v1_alpha/collections_controller.rb index 5583bb395d..7c33d3bfa2 100644 --- a/app/controllers/api/v1_alpha/collections_controller.rb +++ b/app/controllers/api/v1_alpha/collections_controller.rb @@ -9,7 +9,14 @@ class Api::V1Alpha::CollectionsController < Api::BaseController before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create] - before_action :require_user! + before_action :require_user!, only: [:create] + + def show + cache_if_unauthenticated! + @collection = Collection.find(params[:id]) + + render json: @collection, serializer: REST::CollectionSerializer + end def create @collection = CreateCollectionService.new.call(collection_params, current_user.account) diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 780c0be319..b8c21f3ccd 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -135,7 +135,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController @accept_token = session[:accept_token] = SecureRandom.hex @invite_code = invite_code - set_locale { render :rules } + render :rules end def is_flashing_format? # rubocop:disable Naming/PredicatePrefix diff --git a/app/controllers/wrapstodon_controller.rb b/app/controllers/wrapstodon_controller.rb new file mode 100644 index 0000000000..74f0dbb65a --- /dev/null +++ b/app/controllers/wrapstodon_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class WrapstodonController < ApplicationController + include WebAppControllerConcern + include Authorization + include AccountOwnedConcern + + vary_by 'Accept, Accept-Language, Cookie' + + before_action :set_generated_annual_report + + skip_before_action :require_functional!, only: :show, unless: :limited_federation_mode? + + def show + expires_in 10.seconds, public: true if current_account.nil? + end + + private + + def set_generated_annual_report + @generated_annual_report = GeneratedAnnualReport.find_by!(account: @account, year: params[:year], share_key: params[:share_key]) + end +end diff --git a/app/javascript/flavours/glitch/actions/server.js b/app/javascript/flavours/glitch/actions/server.js index 32ee093afa..e35c25f524 100644 --- a/app/javascript/flavours/glitch/actions/server.js +++ b/app/javascript/flavours/glitch/actions/server.js @@ -1,3 +1,5 @@ +import { checkAnnualReport } from '@/flavours/glitch/reducers/slices/annual_report'; + import api from '../api'; import { importFetchedAccount } from './importer'; @@ -29,6 +31,9 @@ export const fetchServer = () => (dispatch, getState) => { .get('/api/v2/instance').then(({ data }) => { if (data.contact.account) dispatch(importFetchedAccount(data.contact.account)); dispatch(fetchServerSuccess(data)); + if (data.wrapstodon) { + void dispatch(checkAnnualReport()); + } }).catch(err => dispatch(fetchServerFail(err))); }; diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js index 2acba43739..6eb5d904bc 100644 --- a/app/javascript/flavours/glitch/actions/statuses.js +++ b/app/javascript/flavours/glitch/actions/statuses.js @@ -85,6 +85,8 @@ export function fetchStatus(id, { dispatch(fetchStatusSuccess(skipLoading)); }).catch(error => { dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId)); + if (error.status === 404) + dispatch(deleteFromTimelines(id)); }); }; } diff --git a/app/javascript/flavours/glitch/api/annual_report.ts b/app/javascript/flavours/glitch/api/annual_report.ts new file mode 100644 index 0000000000..dc080035d4 --- /dev/null +++ b/app/javascript/flavours/glitch/api/annual_report.ts @@ -0,0 +1,38 @@ +import api, { apiRequestGet, getAsyncRefreshHeader } from '../api'; +import type { ApiAccountJSON } from '../api_types/accounts'; +import type { ApiStatusJSON } from '../api_types/statuses'; +import type { AnnualReport } from '../models/annual_report'; + +export type ApiAnnualReportState = + | 'available' + | 'generating' + | 'eligible' + | 'ineligible'; + +export const apiGetAnnualReportState = async (year: number) => { + const response = await api().get<{ state: ApiAnnualReportState }>( + `/api/v1/annual_reports/${year}/state`, + ); + + return { + state: response.data.state, + refresh: getAsyncRefreshHeader(response), + }; +}; + +export const apiRequestGenerateAnnualReport = async (year: number) => { + const response = await api().post(`/api/v1/annual_reports/${year}/generate`); + + return { + refresh: getAsyncRefreshHeader(response), + }; +}; + +export interface ApiAnnualReportResponse { + annual_reports: AnnualReport[]; + accounts: ApiAccountJSON[]; + statuses: ApiStatusJSON[]; +} + +export const apiGetAnnualReport = (year: number) => + apiRequestGet(`v1/annual_reports/${year}`); diff --git a/app/javascript/flavours/glitch/api_types/custom_emoji.ts b/app/javascript/flavours/glitch/api_types/custom_emoji.ts index 05144d6f68..099ef0b88b 100644 --- a/app/javascript/flavours/glitch/api_types/custom_emoji.ts +++ b/app/javascript/flavours/glitch/api_types/custom_emoji.ts @@ -1,8 +1,9 @@ -// See app/serializers/rest/account_serializer.rb +// See app/serializers/rest/custom_emoji_serializer.rb export interface ApiCustomEmojiJSON { shortcode: string; static_url: string; url: string; category?: string; + featured?: boolean; visible_in_picker: boolean; } diff --git a/app/javascript/flavours/glitch/components/hotkeys/index.tsx b/app/javascript/flavours/glitch/components/hotkeys/index.tsx index db24038ff8..29d43266e1 100644 --- a/app/javascript/flavours/glitch/components/hotkeys/index.tsx +++ b/app/javascript/flavours/glitch/components/hotkeys/index.tsx @@ -112,6 +112,7 @@ const hotkeyMatcherMap = { openProfile: just('p'), moveDown: just('j'), moveUp: just('k'), + moveToTop: just('0'), toggleHidden: just('x'), toggleSensitive: just('h'), toggleComposeSpoilers: optionPlus('x'), diff --git a/app/javascript/flavours/glitch/components/status/handled_link.tsx b/app/javascript/flavours/glitch/components/status/handled_link.tsx index ac49115085..2b1a1bb763 100644 --- a/app/javascript/flavours/glitch/components/status/handled_link.tsx +++ b/app/javascript/flavours/glitch/components/status/handled_link.tsx @@ -198,7 +198,7 @@ export const HandledLink: FC> = ({ title={href} className={classNames('unhandled-link', className)} target='_blank' - rel='noreferrer noopener' + rel='noopener' translate='no' ref={linkRef} > diff --git a/app/javascript/flavours/glitch/components/status_list.jsx b/app/javascript/flavours/glitch/components/status_list.jsx index 14124a212d..c884894c02 100644 --- a/app/javascript/flavours/glitch/components/status_list.jsx +++ b/app/javascript/flavours/glitch/components/status_list.jsx @@ -8,6 +8,8 @@ import { debounce } from 'lodash'; import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'flavours/glitch/actions/timelines'; import { RegenerationIndicator } from 'flavours/glitch/components/regeneration_indicator'; import { InlineFollowSuggestions } from 'flavours/glitch/features/home_timeline/components/inline_follow_suggestions'; +import { AnnualReportTimeline } from 'flavours/glitch/features/annual_report/timeline'; +import { TIMELINE_WRAPSTODON } from '@/flavours/glitch/reducers/slices/annual_report'; import { StatusQuoteManager } from '../components/status_quoted'; @@ -64,10 +66,12 @@ export default class StatusList extends ImmutablePureComponent { switch(statusId) { case TIMELINE_SUGGESTIONS: return ( - + ); + case TIMELINE_WRAPSTODON: + return ( + + ) case TIMELINE_GAP: return ( ; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , +}; + +export const Loading: Story = { + args: { + isLoading: true, + }, + render: Default.render, +}; + +export const WithData: Story = { + args: { + hasData: true, + }, + render: Default.render, +}; diff --git a/app/javascript/flavours/glitch/features/annual_report/announcement/index.tsx b/app/javascript/flavours/glitch/features/annual_report/announcement/index.tsx new file mode 100644 index 0000000000..33a2bed6ef --- /dev/null +++ b/app/javascript/flavours/glitch/features/annual_report/announcement/index.tsx @@ -0,0 +1,46 @@ +import { FormattedMessage } from 'react-intl'; + +import { Button } from '@/flavours/glitch/components/button'; + +import styles from './styles.module.scss'; + +export const AnnualReportAnnouncement: React.FC<{ + year: string; + hasData: boolean; + isLoading: boolean; + onRequestBuild: () => void; + onOpen: () => void; +}> = ({ year, hasData, isLoading, onRequestBuild, onOpen }) => { + return ( +
+

+ +

+

+ +

+ {hasData ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/app/javascript/flavours/glitch/features/annual_report/announcement/styles.module.scss b/app/javascript/flavours/glitch/features/annual_report/announcement/styles.module.scss new file mode 100644 index 0000000000..a09c3425b6 --- /dev/null +++ b/app/javascript/flavours/glitch/features/annual_report/announcement/styles.module.scss @@ -0,0 +1,29 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px 60px; + text-align: center; + font-size: 15px; + line-height: 1.5; + color: var(--color-text-on-media); + background: var(--color-bg-media-base); + background: + radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%), + radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%), + radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%), + radial-gradient(at 16% 95%, #1e948299 0, transparent 50%) + var(--color-bg-media-base); + border-bottom: 1px solid var(--color-border-primary); + + h2 { + font-size: 20px; + font-weight: 500; + line-height: 1.2; + margin-bottom: 8px; + } + + p { + margin-bottom: 20px; + } +} diff --git a/app/javascript/flavours/glitch/features/annual_report/index.tsx b/app/javascript/flavours/glitch/features/annual_report/index.tsx index 2ed9e01fea..4c47ee22ce 100644 --- a/app/javascript/flavours/glitch/features/annual_report/index.tsx +++ b/app/javascript/flavours/glitch/features/annual_report/index.tsx @@ -59,7 +59,7 @@ export const AnnualReport: React.FC<{ const report = response?.annual_reports[0]; - if (!report) { + if (!report || report.schema_version !== 1) { return null; } diff --git a/app/javascript/flavours/glitch/features/annual_report/timeline.tsx b/app/javascript/flavours/glitch/features/annual_report/timeline.tsx new file mode 100644 index 0000000000..dee18cd514 --- /dev/null +++ b/app/javascript/flavours/glitch/features/annual_report/timeline.tsx @@ -0,0 +1,44 @@ +import { useCallback } from 'react'; +import type { FC } from 'react'; + +import { showAlert } from '@/flavours/glitch/actions/alerts'; +import { + generateReport, + selectWrapstodonYear, +} from '@/flavours/glitch/reducers/slices/annual_report'; +import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store'; + +import { AnnualReportAnnouncement } from './announcement'; + +export const AnnualReportTimeline: FC = () => { + const { state } = useAppSelector((state) => state.annualReport); + const year = useAppSelector(selectWrapstodonYear); + + const dispatch = useAppDispatch(); + const handleBuildRequest = useCallback(() => { + void dispatch(generateReport()); + }, [dispatch]); + + const handleOpen = useCallback(() => { + dispatch( + // TODO: Implement opening the annual report view when components are ready. + showAlert({ + message: 'Not yet implemented.', + }), + ); + }, [dispatch]); + + if (!year || !state || state === 'ineligible') { + return null; + } + + return ( + + ); +}; diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx index e424568586..5a4589aa17 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx @@ -99,13 +99,17 @@ class KeyboardShortcuts extends ImmutablePureComponent { - l - + 0 + 1-9 + + l + + n diff --git a/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js b/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js index ca51255a66..2ff85c0b25 100644 --- a/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js +++ b/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js @@ -4,9 +4,10 @@ import { connect } from 'react-redux'; import { debounce } from 'lodash'; -import { scrollTopTimeline, loadPending } from '../../../actions/timelines'; -import StatusList from '../../../components/status_list'; -import { me } from '../../../initial_state'; +import { scrollTopTimeline, loadPending, TIMELINE_SUGGESTIONS } from '@/flavours/glitch/actions/timelines'; +import StatusList from '@/flavours/glitch/components/status_list'; +import { me } from '@/flavours/glitch/initial_state'; +import { TIMELINE_WRAPSTODON } from '@/flavours/glitch/reducers/slices/annual_report'; const getRegex = createSelector([ (state, { regex }) => regex, @@ -28,7 +29,7 @@ const makeGetStatusIds = (pending = false) => createSelector([ getRegex, ], (columnSettings, statusIds, statuses, regex) => { return statusIds.filter(id => { - if (id === null || id === 'inline-follow-suggestions') return true; + if (id === null || id === TIMELINE_SUGGESTIONS || id === TIMELINE_WRAPSTODON) return true; const statusForId = statuses.get(id); diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index f18c44c4c8..9cf4b2372e 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -81,7 +81,7 @@ import { Quotes, } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; -import { focusColumn, getFocusedItemIndex, focusItemSibling } from './util/focusUtils'; +import { focusColumn, getFocusedItemIndex, focusItemSibling, focusFirstItem } from './util/focusUtils'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; // Dummy import, to make sure that ends up in the application bundle. @@ -495,20 +495,21 @@ class UI extends PureComponent { }; handleHotkeyFocusColumn = e => { - focusColumn({index: e.key * 1}); + focusColumn(e.key * 1); }; handleHotkeyLoadMore = () => { document.querySelector('.load-more')?.focus(); }; + handleMoveToTop = () => { + focusFirstItem(); + }; + handleMoveUp = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, -1); } @@ -517,10 +518,7 @@ class UI extends PureComponent { handleMoveDown = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, 1); } @@ -615,6 +613,7 @@ class UI extends PureComponent { focusLoadMore: this.handleHotkeyLoadMore, moveDown: this.handleMoveDown, moveUp: this.handleMoveUp, + moveToTop: this.handleMoveToTop, back: this.handleHotkeyBack, goToHome: this.handleHotkeyGoToHome, goToNotifications: this.handleHotkeyGoToNotifications, diff --git a/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts b/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts index a728a3c5eb..f140cb0b35 100644 --- a/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts +++ b/app/javascript/flavours/glitch/features/ui/util/focusUtils.ts @@ -1,16 +1,30 @@ -interface FocusColumnOptions { - index?: number; - focusItem?: 'first' | 'first-visible'; +/** + * Out of a list of elements, return the first one whose top edge + * is inside of the viewport, and return the element and its BoundingClientRect. + */ +function findFirstVisibleWithRect( + items: HTMLElement[], +): { item: HTMLElement; rect: DOMRect } | null { + const viewportHeight = + window.innerHeight || document.documentElement.clientHeight; + + for (const item of items) { + const rect = item.getBoundingClientRect(); + const isVisible = rect.top >= 0 && rect.top < viewportHeight; + + if (isVisible) { + return { item, rect }; + } + } + + return null; } /** * Move focus to the column of the passed index (1-based). - * Can either focus the topmost item or the first one in the viewport + * Focus is placed on the topmost visible item */ -export function focusColumn({ - index = 1, - focusItem = 'first', -}: FocusColumnOptions = {}) { +export function focusColumn(index = 1) { // Skip the leftmost drawer in multi-column mode const isMultiColumnLayout = !!document.querySelector( 'body.layout-multiple-columns', @@ -27,33 +41,28 @@ export function focusColumn({ if (!container) return; - let itemToFocus: HTMLElement | null = null; + const focusableItems = Array.from( + container.querySelectorAll( + '.focusable:not(.status__quote .focusable)', + ), + ); - if (focusItem === 'first-visible') { - const focusableItems = Array.from( - container.querySelectorAll( - '.focusable:not(.status__quote .focusable)', - ), - ); - - const viewportHeight = - window.innerHeight || document.documentElement.clientHeight; - - // Find first item visible in the viewport - itemToFocus = - focusableItems.find((item) => { - const { top } = item.getBoundingClientRect(); - return top >= 0 && top < viewportHeight; - }) ?? null; - } else { - itemToFocus = container.querySelector('.focusable'); - } + // Find first item visible in the viewport + const itemToFocus = findFirstVisibleWithRect(focusableItems); if (itemToFocus) { - if (container.scrollTop > itemToFocus.offsetTop) { - itemToFocus.scrollIntoView(true); + const viewportWidth = + window.innerWidth || document.documentElement.clientWidth; + const { item, rect } = itemToFocus; + + if ( + container.scrollTop > item.offsetTop || + rect.right > viewportWidth || + rect.left < 0 + ) { + itemToFocus.item.scrollIntoView(true); } - itemToFocus.focus(); + itemToFocus.item.focus(); } } @@ -71,6 +80,26 @@ export function getFocusedItemIndex() { return items.indexOf(focusedItem); } +/** + * Focus the topmost item of the column that currently has focus, + * or the first column if none + */ +export function focusFirstItem() { + const focusedElement = document.activeElement; + const container = + focusedElement?.closest('.scrollable') ?? + document.querySelector('.scrollable'); + + if (!container) return; + + const itemToFocus = container.querySelector('.focusable'); + + if (itemToFocus) { + container.scrollTo(0, 0); + itemToFocus.focus(); + } +} + /** * Focus the item next to the one with the provided index */ diff --git a/app/javascript/flavours/glitch/models/annual_report.ts b/app/javascript/flavours/glitch/models/annual_report.ts index c0a101e6c8..a2c0c51786 100644 --- a/app/javascript/flavours/glitch/models/annual_report.ts +++ b/app/javascript/flavours/glitch/models/annual_report.ts @@ -37,8 +37,30 @@ interface AnnualReportV1 { archetype: Archetype; } -export interface AnnualReport { - year: number; - schema_version: number; - data: AnnualReportV1; +interface AnnualReportV2 { + archetype: Archetype; + time_series: TimeSeriesMonth[]; + top_hashtags: NameAndCount[]; + top_statuses: TopStatuses; + most_used_apps: NameAndCount[]; + type_distribution: { + total: number; + reblogs: number; + replies: number; + standalone: number; + }; } + +export type AnnualReport = { + year: number; +} & ( + | { + schema_version: 1; + data: AnnualReportV1; + } + | { + schema_version: 2; + data: AnnualReportV2; + share_url: string | null; + } +); diff --git a/app/javascript/flavours/glitch/models/custom_emoji.ts b/app/javascript/flavours/glitch/models/custom_emoji.ts index 6fd657df1d..fc589bd53f 100644 --- a/app/javascript/flavours/glitch/models/custom_emoji.ts +++ b/app/javascript/flavours/glitch/models/custom_emoji.ts @@ -11,6 +11,7 @@ export const CustomEmojiFactory = ImmutableRecord({ static_url: '', url: '', category: '', + featured: false, visible_in_picker: false, }); diff --git a/app/javascript/flavours/glitch/reducers/index.ts b/app/javascript/flavours/glitch/reducers/index.ts index 8b3d27b059..2b93c2e906 100644 --- a/app/javascript/flavours/glitch/reducers/index.ts +++ b/app/javascript/flavours/glitch/reducers/index.ts @@ -34,6 +34,7 @@ import { relationshipsReducer } from './relationships'; import { searchReducer } from './search'; import server from './server'; import settings from './settings'; +import { sliceReducers } from './slices'; import status_lists from './status_lists'; import statuses from './statuses'; import { suggestionsReducer } from './suggestions'; @@ -82,6 +83,7 @@ const reducers = { notificationPolicy: notificationPolicyReducer, notificationRequests: notificationRequestsReducer, navigation: navigationReducer, + ...sliceReducers, }; // We want the root state to be an ImmutableRecord, which is an object with a defined list of keys, diff --git a/app/javascript/flavours/glitch/reducers/slices/annual_report.ts b/app/javascript/flavours/glitch/reducers/slices/annual_report.ts new file mode 100644 index 0000000000..b58940a2ac --- /dev/null +++ b/app/javascript/flavours/glitch/reducers/slices/annual_report.ts @@ -0,0 +1,118 @@ +import { createSlice } from '@reduxjs/toolkit'; + +import { insertIntoTimeline } from '@/flavours/glitch/actions/timelines'; +import type { ApiAnnualReportState } from '@/flavours/glitch/api/annual_report'; +import { + apiGetAnnualReport, + apiGetAnnualReportState, + apiRequestGenerateAnnualReport, +} from '@/flavours/glitch/api/annual_report'; +import type { AnnualReport } from '@/flavours/glitch/models/annual_report'; + +import { + createAppSelector, + createAppThunk, + createDataLoadingThunk, +} from '../../store/typed_functions'; + +export const TIMELINE_WRAPSTODON = 'inline-wrapstodon'; + +interface AnnualReportState { + state?: ApiAnnualReportState; + report?: AnnualReport; +} + +const annualReportSlice = createSlice({ + name: 'annualReport', + initialState: {} as AnnualReportState, + reducers: {}, + extraReducers(builder) { + builder + .addCase(fetchReportState.fulfilled, (state, action) => { + state.state = action.payload; + }) + .addCase(generateReport.pending, (state) => { + state.state = 'generating'; + }) + .addCase(getReport.fulfilled, (state, action) => { + if (action.payload) { + state.report = action.payload; + } + }); + }, +}); + +export const annualReport = annualReportSlice.reducer; + +export const selectWrapstodonYear = createAppSelector( + [(state) => state.server.getIn(['server', 'wrapstodon'])], + (year: unknown) => (typeof year === 'number' && year > 2000 ? year : null), +); + +// This kicks everything off, and is called after fetching the server info. +export const checkAnnualReport = createAppThunk( + `${annualReportSlice.name}/checkAnnualReport`, + async (_arg: unknown, { dispatch, getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + return; + } + const state = await dispatch(fetchReportState()); + if ( + state.meta.requestStatus === 'fulfilled' && + state.payload !== 'ineligible' + ) { + dispatch(insertIntoTimeline('home', TIMELINE_WRAPSTODON, 1)); + } + }, +); + +const fetchReportState = createDataLoadingThunk( + `${annualReportSlice.name}/fetchReportState`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReportState(year); + }, + ({ state, refresh }, { dispatch }) => { + if (state === 'generating' && refresh) { + window.setTimeout(() => { + void dispatch(fetchReportState()); + }, 1_000 * refresh.retry); + } else if (state === 'available') { + void dispatch(getReport()); + } + + return state; + }, + { useLoadingBar: false }, +); + +// Triggers the generation of the annual report. +export const generateReport = createDataLoadingThunk( + `${annualReportSlice.name}/generateReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiRequestGenerateAnnualReport(year); + }, + (_arg: unknown, { dispatch }) => { + void dispatch(fetchReportState()); + }, +); + +export const getReport = createDataLoadingThunk( + `${annualReportSlice.name}/getReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReport(year); + }, + (data) => data.annual_reports[0], +); diff --git a/app/javascript/flavours/glitch/reducers/slices/index.ts b/app/javascript/flavours/glitch/reducers/slices/index.ts new file mode 100644 index 0000000000..dfea395127 --- /dev/null +++ b/app/javascript/flavours/glitch/reducers/slices/index.ts @@ -0,0 +1,5 @@ +import { annualReport } from './annual_report'; + +export const sliceReducers = { + annualReport, +}; diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js index 84dac6e0f3..a2c82d3e0c 100644 --- a/app/javascript/flavours/glitch/reducers/statuses.js +++ b/app/javascript/flavours/glitch/reducers/statuses.js @@ -65,6 +65,10 @@ const statusTranslateUndo = (state, id) => { }); }; +const removeStatusStub = (state, id) => { + return state.getIn([id, 'id']) ? state.deleteIn([id, 'isLoading']) : state.delete(id); +} + /** @type {ImmutableMap} */ const initialState = ImmutableMap(); @@ -92,11 +96,10 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'isLoading'], true); case STATUS_FETCH_FAIL: { if (action.parentQuotePostId && action.error.status === 404) { - return state - .delete(action.id) + return removeStatusStub(state, action.id) .setIn([action.parentQuotePostId, 'quote', 'state'], 'deleted') } else { - return state.delete(action.id); + return removeStatusStub(state, action.id); } } case STATUS_IMPORT: diff --git a/app/javascript/flavours/glitch/store/typed_functions.ts b/app/javascript/flavours/glitch/store/typed_functions.ts index 5ceb05909f..79bca08a52 100644 --- a/app/javascript/flavours/glitch/store/typed_functions.ts +++ b/app/javascript/flavours/glitch/store/typed_functions.ts @@ -205,7 +205,7 @@ export function createDataLoadingThunk( thunkOptions?: AppThunkOptions, ): ReturnType>; -// Overload when the `onData` method returns nothing, then the mayload is the `onData` result +// Overload when the `onData` method returns nothing, then the payload is the `onData` result export function createDataLoadingThunk( name: string, loadData: LoadData, diff --git a/app/javascript/flavours/glitch/styles/mastodon/basics.scss b/app/javascript/flavours/glitch/styles/mastodon/basics.scss index 6298409d15..db584f67f1 100644 --- a/app/javascript/flavours/glitch/styles/mastodon/basics.scss +++ b/app/javascript/flavours/glitch/styles/mastodon/basics.scss @@ -1,5 +1,10 @@ @use 'variables' as *; +html { + color: var(--color-text-primary); + background: var(--color-bg-ambient); +} + html.has-modal { &, body { diff --git a/app/javascript/flavours/glitch/utils/links.ts b/app/javascript/flavours/glitch/utils/links.ts index 02b74bde4d..00821fa8d1 100644 --- a/app/javascript/flavours/glitch/utils/links.ts +++ b/app/javascript/flavours/glitch/utils/links.ts @@ -5,20 +5,26 @@ export function setupLinkListeners() { // We don't want to target links with data-confirm here, as those are handled already. on('click', 'a[data-method]:not([data-confirm])', handleMethodLink); + + // We also want to target buttons with data-confirm that are not inside forms. + on('click', ':not(form) button[data-confirm]:not([form])', handleConfirmLink); } function handleConfirmLink(event: MouseEvent) { - const anchor = event.currentTarget; - if (!(anchor instanceof HTMLAnchorElement)) { + const target = event.currentTarget; + if ( + !(target instanceof HTMLAnchorElement) && + !(target instanceof HTMLButtonElement) + ) { return; } - const message = anchor.dataset.confirm; + const message = target.dataset.confirm; if (!message || !window.confirm(message)) { event.preventDefault(); return; } - if (anchor.dataset.method) { + if (target.dataset.method) { handleMethodLink(event); } } diff --git a/app/javascript/mastodon/actions/server.js b/app/javascript/mastodon/actions/server.js index 32ee093afa..47b6e7a176 100644 --- a/app/javascript/mastodon/actions/server.js +++ b/app/javascript/mastodon/actions/server.js @@ -1,3 +1,5 @@ +import { checkAnnualReport } from '@/mastodon/reducers/slices/annual_report'; + import api from '../api'; import { importFetchedAccount } from './importer'; @@ -29,6 +31,9 @@ export const fetchServer = () => (dispatch, getState) => { .get('/api/v2/instance').then(({ data }) => { if (data.contact.account) dispatch(importFetchedAccount(data.contact.account)); dispatch(fetchServerSuccess(data)); + if (data.wrapstodon) { + void dispatch(checkAnnualReport()); + } }).catch(err => dispatch(fetchServerFail(err))); }; diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 5602fcadb6..b883e986dd 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -85,6 +85,8 @@ export function fetchStatus(id, { dispatch(fetchStatusSuccess(skipLoading)); }).catch(error => { dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId)); + if (error.status === 404) + dispatch(deleteFromTimelines(id)); }); }; } diff --git a/app/javascript/mastodon/api/annual_report.ts b/app/javascript/mastodon/api/annual_report.ts new file mode 100644 index 0000000000..dc080035d4 --- /dev/null +++ b/app/javascript/mastodon/api/annual_report.ts @@ -0,0 +1,38 @@ +import api, { apiRequestGet, getAsyncRefreshHeader } from '../api'; +import type { ApiAccountJSON } from '../api_types/accounts'; +import type { ApiStatusJSON } from '../api_types/statuses'; +import type { AnnualReport } from '../models/annual_report'; + +export type ApiAnnualReportState = + | 'available' + | 'generating' + | 'eligible' + | 'ineligible'; + +export const apiGetAnnualReportState = async (year: number) => { + const response = await api().get<{ state: ApiAnnualReportState }>( + `/api/v1/annual_reports/${year}/state`, + ); + + return { + state: response.data.state, + refresh: getAsyncRefreshHeader(response), + }; +}; + +export const apiRequestGenerateAnnualReport = async (year: number) => { + const response = await api().post(`/api/v1/annual_reports/${year}/generate`); + + return { + refresh: getAsyncRefreshHeader(response), + }; +}; + +export interface ApiAnnualReportResponse { + annual_reports: AnnualReport[]; + accounts: ApiAccountJSON[]; + statuses: ApiStatusJSON[]; +} + +export const apiGetAnnualReport = (year: number) => + apiRequestGet(`v1/annual_reports/${year}`); diff --git a/app/javascript/mastodon/api_types/custom_emoji.ts b/app/javascript/mastodon/api_types/custom_emoji.ts index 05144d6f68..099ef0b88b 100644 --- a/app/javascript/mastodon/api_types/custom_emoji.ts +++ b/app/javascript/mastodon/api_types/custom_emoji.ts @@ -1,8 +1,9 @@ -// See app/serializers/rest/account_serializer.rb +// See app/serializers/rest/custom_emoji_serializer.rb export interface ApiCustomEmojiJSON { shortcode: string; static_url: string; url: string; category?: string; + featured?: boolean; visible_in_picker: boolean; } diff --git a/app/javascript/mastodon/components/hotkeys/index.tsx b/app/javascript/mastodon/components/hotkeys/index.tsx index 81ca28eb87..c62fc0c20a 100644 --- a/app/javascript/mastodon/components/hotkeys/index.tsx +++ b/app/javascript/mastodon/components/hotkeys/index.tsx @@ -111,6 +111,7 @@ const hotkeyMatcherMap = { openProfile: just('p'), moveDown: just('j'), moveUp: just('k'), + moveToTop: just('0'), toggleHidden: just('x'), toggleSensitive: just('h'), toggleComposeSpoilers: optionPlus('x'), diff --git a/app/javascript/mastodon/components/status/handled_link.tsx b/app/javascript/mastodon/components/status/handled_link.tsx index 8b6db98fda..5fcea5f8b9 100644 --- a/app/javascript/mastodon/components/status/handled_link.tsx +++ b/app/javascript/mastodon/components/status/handled_link.tsx @@ -75,7 +75,7 @@ export const HandledLink: FC> = ({ title={href} className={classNames('unhandled-link', className)} target='_blank' - rel='noreferrer noopener' + rel='noopener' translate='no' > {children} diff --git a/app/javascript/mastodon/components/status_list.jsx b/app/javascript/mastodon/components/status_list.jsx index cb2a7464cb..78e6fbcf5f 100644 --- a/app/javascript/mastodon/components/status_list.jsx +++ b/app/javascript/mastodon/components/status_list.jsx @@ -8,6 +8,8 @@ import { debounce } from 'lodash'; import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'mastodon/actions/timelines'; import { RegenerationIndicator } from 'mastodon/components/regeneration_indicator'; import { InlineFollowSuggestions } from 'mastodon/features/home_timeline/components/inline_follow_suggestions'; +import { AnnualReportTimeline } from 'mastodon/features/annual_report/timeline'; +import { TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report'; import { StatusQuoteManager } from '../components/status_quoted'; @@ -63,10 +65,12 @@ export default class StatusList extends ImmutablePureComponent { switch(statusId) { case TIMELINE_SUGGESTIONS: return ( - + ); + case TIMELINE_WRAPSTODON: + return ( + + ) case TIMELINE_GAP: return ( ; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , +}; + +export const Loading: Story = { + args: { + isLoading: true, + }, + render: Default.render, +}; + +export const WithData: Story = { + args: { + hasData: true, + }, + render: Default.render, +}; diff --git a/app/javascript/mastodon/features/annual_report/announcement/index.tsx b/app/javascript/mastodon/features/annual_report/announcement/index.tsx new file mode 100644 index 0000000000..7cdb36e35f --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/announcement/index.tsx @@ -0,0 +1,46 @@ +import { FormattedMessage } from 'react-intl'; + +import { Button } from '@/mastodon/components/button'; + +import styles from './styles.module.scss'; + +export const AnnualReportAnnouncement: React.FC<{ + year: string; + hasData: boolean; + isLoading: boolean; + onRequestBuild: () => void; + onOpen: () => void; +}> = ({ year, hasData, isLoading, onRequestBuild, onOpen }) => { + return ( +
+

+ +

+

+ +

+ {hasData ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss b/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss new file mode 100644 index 0000000000..a09c3425b6 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss @@ -0,0 +1,29 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px 60px; + text-align: center; + font-size: 15px; + line-height: 1.5; + color: var(--color-text-on-media); + background: var(--color-bg-media-base); + background: + radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%), + radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%), + radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%), + radial-gradient(at 16% 95%, #1e948299 0, transparent 50%) + var(--color-bg-media-base); + border-bottom: 1px solid var(--color-border-primary); + + h2 { + font-size: 20px; + font-weight: 500; + line-height: 1.2; + margin-bottom: 8px; + } + + p { + margin-bottom: 20px; + } +} diff --git a/app/javascript/mastodon/features/annual_report/index.tsx b/app/javascript/mastodon/features/annual_report/index.tsx index 1b41d9d8f2..9c2c9d71c0 100644 --- a/app/javascript/mastodon/features/annual_report/index.tsx +++ b/app/javascript/mastodon/features/annual_report/index.tsx @@ -59,7 +59,7 @@ export const AnnualReport: React.FC<{ const report = response?.annual_reports[0]; - if (!report) { + if (!report || report.schema_version !== 1) { return null; } diff --git a/app/javascript/mastodon/features/annual_report/timeline.tsx b/app/javascript/mastodon/features/annual_report/timeline.tsx new file mode 100644 index 0000000000..512a192f05 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/timeline.tsx @@ -0,0 +1,44 @@ +import { useCallback } from 'react'; +import type { FC } from 'react'; + +import { showAlert } from '@/mastodon/actions/alerts'; +import { + generateReport, + selectWrapstodonYear, +} from '@/mastodon/reducers/slices/annual_report'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; + +import { AnnualReportAnnouncement } from './announcement'; + +export const AnnualReportTimeline: FC = () => { + const { state } = useAppSelector((state) => state.annualReport); + const year = useAppSelector(selectWrapstodonYear); + + const dispatch = useAppDispatch(); + const handleBuildRequest = useCallback(() => { + void dispatch(generateReport()); + }, [dispatch]); + + const handleOpen = useCallback(() => { + dispatch( + // TODO: Implement opening the annual report view when components are ready. + showAlert({ + message: 'Not yet implemented.', + }), + ); + }, [dispatch]); + + if (!year || !state || state === 'ineligible') { + return null; + } + + return ( + + ); +}; diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx index 01a4f0e1fd..8a6ebe6def 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx @@ -95,13 +95,17 @@ class KeyboardShortcuts extends ImmutablePureComponent { - l - + 0 + 1-9 + + l + + n diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js index d581ad5fe4..66e3b91c7b 100644 --- a/app/javascript/mastodon/features/ui/containers/status_list_container.js +++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js @@ -4,9 +4,10 @@ import { connect } from 'react-redux'; import { debounce } from 'lodash'; -import { scrollTopTimeline, loadPending } from '../../../actions/timelines'; -import StatusList from '../../../components/status_list'; -import { me } from '../../../initial_state'; +import { scrollTopTimeline, loadPending, TIMELINE_SUGGESTIONS } from '@/mastodon/actions/timelines'; +import StatusList from '@/mastodon/components/status_list'; +import { me } from '@/mastodon/initial_state'; +import { TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report'; const makeGetStatusIds = (pending = false) => createSelector([ (state, { type }) => state.getIn(['settings', type], ImmutableMap()), @@ -14,7 +15,7 @@ const makeGetStatusIds = (pending = false) => createSelector([ (state) => state.get('statuses'), ], (columnSettings, statusIds, statuses) => { return statusIds.filter(id => { - if (id === null || id === 'inline-follow-suggestions') return true; + if (id === null || id === TIMELINE_SUGGESTIONS || id === TIMELINE_WRAPSTODON) return true; const statusForId = statuses.get(id); diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 476c25e32d..0d73106808 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -78,7 +78,7 @@ import { Quotes, } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; -import { focusColumn, getFocusedItemIndex, focusItemSibling } from './util/focusUtils'; +import { focusColumn, getFocusedItemIndex, focusItemSibling, focusFirstItem } from './util/focusUtils'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; // Dummy import, to make sure that ends up in the application bundle. @@ -449,20 +449,21 @@ class UI extends PureComponent { }; handleHotkeyFocusColumn = e => { - focusColumn({index: e.key * 1}); + focusColumn(e.key * 1); }; handleHotkeyLoadMore = () => { document.querySelector('.load-more')?.focus(); }; + handleMoveToTop = () => { + focusFirstItem(); + }; + handleMoveUp = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, -1); } @@ -471,10 +472,7 @@ class UI extends PureComponent { handleMoveDown = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, 1); } @@ -562,6 +560,7 @@ class UI extends PureComponent { focusLoadMore: this.handleHotkeyLoadMore, moveDown: this.handleMoveDown, moveUp: this.handleMoveUp, + moveToTop: this.handleMoveToTop, back: this.handleHotkeyBack, goToHome: this.handleHotkeyGoToHome, goToNotifications: this.handleHotkeyGoToNotifications, diff --git a/app/javascript/mastodon/features/ui/util/focusUtils.ts b/app/javascript/mastodon/features/ui/util/focusUtils.ts index a728a3c5eb..f140cb0b35 100644 --- a/app/javascript/mastodon/features/ui/util/focusUtils.ts +++ b/app/javascript/mastodon/features/ui/util/focusUtils.ts @@ -1,16 +1,30 @@ -interface FocusColumnOptions { - index?: number; - focusItem?: 'first' | 'first-visible'; +/** + * Out of a list of elements, return the first one whose top edge + * is inside of the viewport, and return the element and its BoundingClientRect. + */ +function findFirstVisibleWithRect( + items: HTMLElement[], +): { item: HTMLElement; rect: DOMRect } | null { + const viewportHeight = + window.innerHeight || document.documentElement.clientHeight; + + for (const item of items) { + const rect = item.getBoundingClientRect(); + const isVisible = rect.top >= 0 && rect.top < viewportHeight; + + if (isVisible) { + return { item, rect }; + } + } + + return null; } /** * Move focus to the column of the passed index (1-based). - * Can either focus the topmost item or the first one in the viewport + * Focus is placed on the topmost visible item */ -export function focusColumn({ - index = 1, - focusItem = 'first', -}: FocusColumnOptions = {}) { +export function focusColumn(index = 1) { // Skip the leftmost drawer in multi-column mode const isMultiColumnLayout = !!document.querySelector( 'body.layout-multiple-columns', @@ -27,33 +41,28 @@ export function focusColumn({ if (!container) return; - let itemToFocus: HTMLElement | null = null; + const focusableItems = Array.from( + container.querySelectorAll( + '.focusable:not(.status__quote .focusable)', + ), + ); - if (focusItem === 'first-visible') { - const focusableItems = Array.from( - container.querySelectorAll( - '.focusable:not(.status__quote .focusable)', - ), - ); - - const viewportHeight = - window.innerHeight || document.documentElement.clientHeight; - - // Find first item visible in the viewport - itemToFocus = - focusableItems.find((item) => { - const { top } = item.getBoundingClientRect(); - return top >= 0 && top < viewportHeight; - }) ?? null; - } else { - itemToFocus = container.querySelector('.focusable'); - } + // Find first item visible in the viewport + const itemToFocus = findFirstVisibleWithRect(focusableItems); if (itemToFocus) { - if (container.scrollTop > itemToFocus.offsetTop) { - itemToFocus.scrollIntoView(true); + const viewportWidth = + window.innerWidth || document.documentElement.clientWidth; + const { item, rect } = itemToFocus; + + if ( + container.scrollTop > item.offsetTop || + rect.right > viewportWidth || + rect.left < 0 + ) { + itemToFocus.item.scrollIntoView(true); } - itemToFocus.focus(); + itemToFocus.item.focus(); } } @@ -71,6 +80,26 @@ export function getFocusedItemIndex() { return items.indexOf(focusedItem); } +/** + * Focus the topmost item of the column that currently has focus, + * or the first column if none + */ +export function focusFirstItem() { + const focusedElement = document.activeElement; + const container = + focusedElement?.closest('.scrollable') ?? + document.querySelector('.scrollable'); + + if (!container) return; + + const itemToFocus = container.querySelector('.focusable'); + + if (itemToFocus) { + container.scrollTo(0, 0); + itemToFocus.focus(); + } +} + /** * Focus the item next to the one with the provided index */ diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 1740987f90..9da64c8968 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Паказаць/схаваць тэкст за папярэджаннем пра кантэнт", "keyboard_shortcuts.toggle_sensitivity": "Паказаць/схаваць медыя", "keyboard_shortcuts.toot": "Стварыць новы допіс", + "keyboard_shortcuts.top": "Перайсці да вяршыні спіса", "keyboard_shortcuts.translate": "каб перакласці допіс", "keyboard_shortcuts.unfocus": "Расфакусіраваць тэкставую вобласць/пошукавы радок", "keyboard_shortcuts.up": "Перамясціцца ўверх па спісе", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 0af26cf6c2..3d36763958 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Beskriv dette for personer med nedsat syn…", "alt_text_modal.done": "Færdig", "announcement.announcement": "Bekendtgørelse", + "annual_report.announcement.action_build": "Byg min Wrapstodon", + "annual_report.announcement.action_view": "Vis min Wrapstodon", + "annual_report.announcement.description": "Få mere at vide om dit engagement på Mastodon i det forgangne år.", + "annual_report.announcement.title": "Wrapstodon {year} er her", "annual_report.summary.archetype.booster": "Fremhæveren", "annual_report.summary.archetype.lurker": "Lureren", "annual_report.summary.archetype.oracle": "Oraklet", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/skjul tekst bag indholdsadvarsel", "keyboard_shortcuts.toggle_sensitivity": "Vis/skjul medier", "keyboard_shortcuts.toot": "Påbegynd nyt indlæg", + "keyboard_shortcuts.top": "Flyt til toppen af listen", "keyboard_shortcuts.translate": "for at oversætte et indlæg", "keyboard_shortcuts.unfocus": "Fjern fokus fra tekstskrivningsområde/søgning", "keyboard_shortcuts.up": "Flyt opad på listen", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 28cfdd26bd..d8491118e7 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -1,13 +1,13 @@ { - "about.blocks": "Moderierte Server", + "about.blocks": "Eingeschränkte Server", "about.contact": "Kontakt:", "about.default_locale": "Standard", "about.disclaimer": "Mastodon ist eine freie, quelloffene Software und eine Marke der Mastodon gGmbH.", - "about.domain_blocks.no_reason_available": "Grund unbekannt", + "about.domain_blocks.no_reason_available": "Keinen Grund angegeben", "about.domain_blocks.preamble": "Mastodon erlaubt es dir grundsätzlich, alle Inhalte von allen Nutzer*innen auf allen Servern im Fediverse zu sehen und mit ihnen zu interagieren. Für diesen Server gibt es aber ein paar Ausnahmen.", "about.domain_blocks.silenced.explanation": "Standardmäßig werden von diesem Server keine Inhalte oder Profile angezeigt. Du kannst die Profile und Inhalte aber dennoch sehen, wenn du explizit nach diesen suchst oder diesen folgst.", - "about.domain_blocks.silenced.title": "Ausgeblendet", - "about.domain_blocks.suspended.explanation": "Es werden keine Daten von diesem Server verarbeitet, gespeichert oder ausgetauscht, sodass eine Interaktion oder Kommunikation mit Nutzer*innen dieses Servers nicht möglich ist.", + "about.domain_blocks.silenced.title": "Stummgeschaltet", + "about.domain_blocks.suspended.explanation": "Es werden keine Daten von diesem Server verarbeitet, gespeichert oder ausgetauscht, sodass eine Interaktion oder Kommunikation mit Profilen dieses Servers nicht möglich ist.", "about.domain_blocks.suspended.title": "Gesperrt", "about.language_label": "Sprache", "about.not_available": "Diese Informationen sind auf diesem Server nicht verfügbar.", @@ -15,21 +15,21 @@ "about.rules": "Serverregeln", "account.account_note_header": "Persönliche Notiz", "account.add_or_remove_from_list": "Hinzufügen oder Entfernen von Listen", - "account.badges.bot": "Automatisiert", + "account.badges.bot": "Bot", "account.badges.group": "Gruppe", "account.block": "@{name} blockieren", - "account.block_domain": "{domain} sperren", + "account.block_domain": "{domain} blockieren", "account.block_short": "Blockieren", "account.blocked": "Blockiert", "account.blocking": "Blockiert", - "account.cancel_follow_request": "Follower-Anfrage zurückziehen", + "account.cancel_follow_request": "Anfrage zurückziehen", "account.copy": "Link zum Profil kopieren", "account.direct": "@{name} privat erwähnen", - "account.disable_notifications": "Höre auf mich zu benachrichtigen wenn @{name} etwas postet", + "account.disable_notifications": "Benachrichtige mich nicht mehr, wenn @{name} etwas veröffentlicht", "account.domain_blocking": "Domain blockiert", "account.edit_profile": "Profil bearbeiten", "account.edit_profile_short": "Bearbeiten", - "account.enable_notifications": "Benachrichtige mich wenn @{name} etwas postet", + "account.enable_notifications": "Benachrichtige mich, wenn @{name} etwas veröffentlicht", "account.endorse": "Im Profil vorstellen", "account.familiar_followers_many": "Gefolgt von {name1}, {name2} und {othersCount, plural, one {einem weiteren Profil, das dir bekannt ist} other {# weiteren Profilen, die dir bekannt sind}}", "account.familiar_followers_one": "Gefolgt von {name1}", @@ -41,9 +41,9 @@ "account.featured_tags.last_status_never": "Keine Beiträge", "account.follow": "Folgen", "account.follow_back": "Ebenfalls folgen", - "account.follow_back_short": "Ebenfalls folgen", + "account.follow_back_short": "Zurückfolgen", "account.follow_request": "Anfrage zum Folgen", - "account.follow_request_cancel": "Anfrage abbrechen", + "account.follow_request_cancel": "Anfrage zurückziehen", "account.follow_request_cancel_short": "Abbrechen", "account.follow_request_short": "Anfragen", "account.followers": "Follower", @@ -58,8 +58,8 @@ "account.hide_reblogs": "Geteilte Beiträge von @{name} ausblenden", "account.in_memoriam": "Zum Andenken.", "account.joined_short": "Mitglied seit", - "account.languages": "Sprache ändern.", - "account.link_verified_on": "Das Profil mit dieser E-Mail-Adresse wurde bereits am {date} verifiziert", + "account.languages": "Ausgewählte Sprachen ändern", + "account.link_verified_on": "Das Profil mit dieser E-Mail-Adresse wurde bereits am {date} bestätigt", "account.locked_info": "Die Privatsphäre dieses Kontos wurde auf „geschützt“ gesetzt. Die Person bestimmt manuell, wer ihrem Profil folgen darf.", "account.media": "Medien", "account.mention": "@{name} erwähnen", @@ -73,7 +73,7 @@ "account.no_bio": "Keine Beschreibung verfügbar.", "account.open_original_page": "Ursprüngliche Seite öffnen", "account.posts": "Beiträge", - "account.posts_with_replies": "Beiträge und Antworten", + "account.posts_with_replies": "Beiträge & Antworten", "account.remove_from_followers": "{name} als Follower entfernen", "account.report": "@{name} melden", "account.requested_follow": "{name} möchte dir folgen", @@ -81,9 +81,9 @@ "account.share": "Profil von @{name} teilen", "account.show_reblogs": "Geteilte Beiträge von @{name} anzeigen", "account.statuses_counter": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}", - "account.unblock": "{name} nicht mehr blockieren", + "account.unblock": "Blockierung von {name} aufheben", "account.unblock_domain": "Blockierung von {domain} aufheben", - "account.unblock_domain_short": "Entsperren", + "account.unblock_domain_short": "Blockierung aufheben", "account.unblock_short": "Blockierung aufheben", "account.unendorse": "Im Profil nicht mehr vorstellen", "account.unfollow": "Entfolgen", @@ -103,21 +103,25 @@ "alert.rate_limited.message": "Bitte versuche es nach {retry_time, time, medium} erneut.", "alert.rate_limited.title": "Anfragelimit überschritten", "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.", - "alert.unexpected.title": "Oha!", + "alert.unexpected.title": "Ups!", "alt_text_badge.title": "Bildbeschreibung", "alt_text_modal.add_alt_text": "Bildbeschreibung hinzufügen", "alt_text_modal.add_text_from_image": "Text aus Bild hinzufügen", "alt_text_modal.cancel": "Abbrechen", "alt_text_modal.change_thumbnail": "Vorschaubild ändern", - "alt_text_modal.describe_for_people_with_hearing_impairments": "Beschreibe den Inhalt für Menschen mit Schwerhörigkeit …", + "alt_text_modal.describe_for_people_with_hearing_impairments": "Beschreibe den Inhalt für Menschen, die taub oder hörbehindert sind …", "alt_text_modal.describe_for_people_with_visual_impairments": "Beschreibe den Inhalt für Menschen, die blind oder sehbehindert sind …", "alt_text_modal.done": "Fertig", "announcement.announcement": "Ankündigung", + "annual_report.announcement.action_build": "Erstelle mein Wrapstodon", + "annual_report.announcement.action_view": "Mein Wrapstodon anschauen", + "annual_report.announcement.description": "Erfahre mehr über deine Aktivitäten auf Mastodon im vergangenen Jahr.", + "annual_report.announcement.title": "Wrapstodon {year} ist fertiggestellt", "annual_report.summary.archetype.booster": "Trendjäger*in", "annual_report.summary.archetype.lurker": "Beobachter*in", "annual_report.summary.archetype.oracle": "Universaltalent", "annual_report.summary.archetype.pollster": "Meinungsforscher*in", - "annual_report.summary.archetype.replier": "Sozialer Schmetterling", + "annual_report.summary.archetype.replier": "Gesellige*r", "annual_report.summary.followers.followers": "Follower", "annual_report.summary.followers.total": "{count} insgesamt", "annual_report.summary.here_it_is": "Dein Jahresrückblick für {year}:", @@ -148,7 +152,7 @@ "bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren", "bundle_column_error.error.body": "Die angeforderte Seite konnte nicht dargestellt werden. Dies könnte auf einen Fehler in unserem Code oder auf ein Browser-Kompatibilitätsproblem zurückzuführen sein.", "bundle_column_error.error.title": "Oh nein!", - "bundle_column_error.network.body": "Beim Versuch, diese Seite zu laden, ist ein Fehler aufgetreten. Dies könnte auf ein vorübergehendes Problem mit Ihrer Internetverbindung oder diesem Server zurückzuführen sein.", + "bundle_column_error.network.body": "Beim Versuch, diese Seite zu laden, ist ein Fehler aufgetreten. Dies könnte auf ein vorübergehendes Problem mit deiner Internetverbindung oder diesem Server zurückzuführen sein.", "bundle_column_error.network.title": "Netzwerkfehler", "bundle_column_error.retry": "Erneut versuchen", "bundle_column_error.return": "Zurück zur Startseite", @@ -160,9 +164,9 @@ "carousel.current": "Seite {current, number}/{max, number}", "carousel.slide": "Seite {current, number} von {max, number}", "closed_registrations.other_server_instructions": "Da Mastodon dezentralisiert ist, kannst du ein Konto auf einem anderen Server erstellen und trotzdem mit diesem Server interagieren.", - "closed_registrations_modal.description": "Das Anlegen eines Kontos auf {domain} ist derzeit nicht möglich, aber bedenke, dass du kein extra Konto auf {domain} benötigst, um Mastodon nutzen zu können.", + "closed_registrations_modal.description": "Das Anlegen eines Kontos auf {domain} ist derzeit nicht möglich, aber bedenke, dass du nicht zwingend auf {domain} ein Konto benötigst, um Mastodon nutzen zu können.", "closed_registrations_modal.find_another_server": "Einen anderen Server auswählen", - "closed_registrations_modal.preamble": "Mastodon ist dezentralisiert, das heißt, unabhängig davon, wo du dein Konto erstellt hast, kannst du jedem Profil auf diesem Server folgen und mit ihm interagieren. Du kannst sogar deinen eigenen Server hosten!", + "closed_registrations_modal.preamble": "Mastodon ist dezentralisiert, das heißt, unabhängig davon, wo du dein Konto erstellst, kannst du jedem Profil auf diesem Server folgen und mit ihm interagieren. Du kannst sogar deinen eigenen Mastodon-Server hosten!", "closed_registrations_modal.title": "Bei Mastodon registrieren", "column.about": "Über", "column.blocks": "Blockierte Profile", @@ -175,7 +179,7 @@ "column.edit_list": "Liste bearbeiten", "column.favourites": "Favoriten", "column.firehose": "Live-Feeds", - "column.firehose_local": "Live-Feed für diesen Server", + "column.firehose_local": "Live-Feed dieses Servers", "column.firehose_singular": "Live-Feed", "column.follow_requests": "Follower-Anfragen", "column.home": "Startseite", @@ -203,7 +207,7 @@ "compose.published.open": "Öffnen", "compose.saved.body": "Beitrag gespeichert.", "compose_form.direct_message_warning_learn_more": "Mehr erfahren", - "compose_form.encryption_warning": "Beiträge auf Mastodon sind nicht Ende-zu-Ende-verschlüsselt. Teile keine sensiblen Informationen über Mastodon.", + "compose_form.encryption_warning": "Beiträge auf Mastodon sind nicht Ende-zu-Ende-verschlüsselt. Teile keine sensiblen Informationen über Mastodon, auch nicht als „private Erwähnung“.", "compose_form.hashtag_warning": "Dieser Beitrag wird unter keinem Hashtag sichtbar sein, weil er nicht öffentlich ist. Nur öffentliche Beiträge können nach Hashtags durchsucht werden.", "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.", "compose_form.lock_disclaimer.lock": "geschützt", @@ -267,8 +271,8 @@ "confirmations.revoke_quote.confirm": "Zitat entfernen", "confirmations.revoke_quote.message": "Diese Aktion kann nicht rückgängig gemacht werden.", "confirmations.revoke_quote.title": "Zitieren meines Beitrags entfernen?", - "confirmations.unblock.confirm": "Nicht mehr blockieren", - "confirmations.unblock.title": "{name} nicht mehr blockieren?", + "confirmations.unblock.confirm": "Blockierung aufheben", + "confirmations.unblock.title": "Blockierung von {name} aufheben?", "confirmations.unfollow.confirm": "Entfolgen", "confirmations.unfollow.title": "{name} entfolgen?", "confirmations.withdraw_request.confirm": "Anfrage zurückziehen", @@ -336,7 +340,7 @@ "empty_column.account_featured.other": "{acct} hat bisher noch nichts vorgestellt. Wusstest du, dass du deine häufig verwendeten Hashtags und sogar Profile von Freund*innen vorstellen kannst?", "empty_column.account_featured_other.unknown": "Dieses Profil hat bisher noch nichts vorgestellt.", "empty_column.account_hides_collections": "Das Konto hat sich dazu entschieden, diese Information nicht zu veröffentlichen", - "empty_column.account_suspended": "Konto gesperrt", + "empty_column.account_suspended": "Konto dauerhaft gesperrt", "empty_column.account_timeline": "Keine Beiträge vorhanden!", "empty_column.account_unavailable": "Profil nicht verfügbar", "empty_column.blocks": "Du hast bisher keine Profile blockiert.", @@ -349,14 +353,14 @@ "empty_column.favourited_statuses": "Du hast noch keine Beiträge favorisiert. Sobald du einen favorisierst, wird er hier erscheinen.", "empty_column.favourites": "Diesen Beitrag hat bisher noch niemand favorisiert. Sobald es jemand tut, wird das Profil hier erscheinen.", "empty_column.follow_requests": "Es liegen derzeit keine Follower-Anfragen vor. Sobald du eine erhältst, wird sie hier erscheinen.", - "empty_column.followed_tags": "Du folgst noch keinen Hashtags. Wenn du dies tust, werden sie hier erscheinen.", + "empty_column.followed_tags": "Du folgst noch keinen Hashtags. Sobald du Hashtags abonniert hast, werden sie hier angezeigt.", "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.", "empty_column.home": "Die Timeline deiner Startseite ist leer! Folge mehr Leuten, um sie zu füllen.", "empty_column.list": "Diese Liste ist derzeit leer. Wenn Konten auf dieser Liste neue Beiträge veröffentlichen, werden sie hier erscheinen.", "empty_column.mutes": "Du hast keine Profile stummgeschaltet.", "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", + "empty_column.public": "Hier ist nichts zu sehen! Schreibe einen öffentlichen Beitrag oder folge Profilen von anderen Servern im Fediverse, um die Timeline zu fü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.", @@ -388,7 +392,7 @@ "filter_modal.select_filter.subtitle": "Einem vorhandenen Filter hinzufügen oder einen neuen erstellen", "filter_modal.select_filter.title": "Diesen Beitrag filtern", "filter_modal.title.status": "Beitrag per Filter ausblenden", - "filter_warning.matches_filter": "Ausgeblendet wegen des Filters „{title}“", + "filter_warning.matches_filter": "Ausgeblendet wegen deines Filters „{title}“", "filtered_notifications_banner.pending_requests": "Von {count, plural, =0 {keinem Profil, das dir möglicherweise bekannt ist} one {einem Profil, das dir möglicherweise bekannt ist} other {# Profilen, die dir möglicherweise bekannt sind}}", "filtered_notifications_banner.title": "Gefilterte Benachrichtigungen", "firehose.all": "Alle Server", @@ -412,7 +416,7 @@ "follow_suggestions.similar_to_recently_followed_longer": "Ähnlich zu Profilen, denen du seit kurzem folgst", "follow_suggestions.view_all": "Alle anzeigen", "follow_suggestions.who_to_follow": "Empfohlene Profile", - "followed_tags": "Gefolgte Hashtags", + "followed_tags": "Abonnierte Hashtags", "footer.about": "Über", "footer.about_this_server": "Über", "footer.directory": "Profilverzeichnis", @@ -440,10 +444,10 @@ "hashtag.counter_by_uses": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}} heute", "hashtag.feature": "Im Profil vorstellen", - "hashtag.follow": "Hashtag folgen", + "hashtag.follow": "Abonnieren", "hashtag.mute": "#{hashtag} stummschalten", "hashtag.unfeature": "Im Profil nicht mehr vorstellen", - "hashtag.unfollow": "Hashtag entfolgen", + "hashtag.unfollow": "Abbestellen", "hashtags.and_other": "… und {count, plural, one{# weiterer} other {# weitere}}", "hints.profiles.followers_may_be_missing": "Möglicherweise werden für dieses Profil nicht alle Follower angezeigt.", "hints.profiles.follows_may_be_missing": "Möglicherweise werden für dieses Profil nicht alle gefolgten Profile angezeigt.", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden", "keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden", "keyboard_shortcuts.toot": "Neuen Beitrag erstellen", + "keyboard_shortcuts.top": "Zum Listenanfang springen", "keyboard_shortcuts.translate": "Beitrag übersetzen", "keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren", "keyboard_shortcuts.up": "Ansicht nach oben bewegen", @@ -581,7 +586,7 @@ "navigation_bar.favourites": "Favoriten", "navigation_bar.filters": "Stummgeschaltete Wörter", "navigation_bar.follow_requests": "Follower-Anfragen", - "navigation_bar.followed_tags": "Gefolgte Hashtags", + "navigation_bar.followed_tags": "Abonnierte Hashtags", "navigation_bar.follows_and_followers": "Follower und Folge ich", "navigation_bar.import_export": "Importieren und exportieren", "navigation_bar.lists": "Listen", @@ -596,9 +601,9 @@ "navigation_bar.privacy_and_reach": "Datenschutz und Reichweite", "navigation_bar.search": "Suche", "navigation_bar.search_trends": "Suche / Angesagt", - "navigation_panel.collapse_followed_tags": "Menü für gefolgte Hashtags schließen", + "navigation_panel.collapse_followed_tags": "Menü für abonnierte Hashtags schließen", "navigation_panel.collapse_lists": "Listen-Menü schließen", - "navigation_panel.expand_followed_tags": "Menü für gefolgte Hashtags öffnen", + "navigation_panel.expand_followed_tags": "Menü für abonnierte Hashtags öffnen", "navigation_panel.expand_lists": "Listen-Menü öffnen", "not_signed_in_indicator.not_signed_in": "Du musst dich anmelden, um auf diesen Inhalt zugreifen zu können.", "notification.admin.report": "{name} meldete {target}", @@ -849,7 +854,7 @@ "search.quick_action.go_to_hashtag": "Hashtag {x} aufrufen", "search.quick_action.open_url": "URL in Mastodon öffnen", "search.quick_action.status_search": "Beiträge passend zu {x}", - "search.search_or_paste": "Suchen oder URL einfügen", + "search.search_or_paste": "Suche eingeben oder URL einfügen", "search_popout.full_text_search_disabled_message": "Auf {domain} nicht verfügbar.", "search_popout.full_text_search_logged_out_message": "Nur verfügbar, wenn angemeldet.", "search_popout.language_code": "ISO-Sprachcode", @@ -1003,7 +1008,7 @@ "upload_form.drag_and_drop.on_drag_start": "Der Medienanhang {item} wurde aufgenommen.", "upload_form.edit": "Bearbeiten", "upload_progress.label": "Wird hochgeladen …", - "upload_progress.processing": "Wird verarbeitet…", + "upload_progress.processing": "Wird verarbeitet …", "username.taken": "Dieser Profilname ist vergeben. Versuche einen anderen", "video.close": "Video schließen", "video.download": "Datei herunterladen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 82be328a22..186bf33264 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Περιέγραψε αυτό για άτομα με προβλήματα όρασης…", "alt_text_modal.done": "Ολοκληρώθηκε", "announcement.announcement": "Ανακοίνωση", + "annual_report.announcement.action_build": "Φτιάξε το Wrapstodon μου", + "annual_report.announcement.action_view": "Προβολή του Wrapstodon μου", + "annual_report.announcement.description": "Ανακαλύψτε περισσότερα για την αλληλεπίδραση σας στο Mastodon κατά τη διάρκεια του περασμένου έτους.", + "annual_report.announcement.title": "Το Wrapstodon του {year} έφτασε", "annual_report.summary.archetype.booster": "Ο κυνηγός των φοβερών", "annual_report.summary.archetype.lurker": "Ο διακριτικός", "annual_report.summary.archetype.oracle": "Η Πυθία", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Εμφάνιση/απόκρυψη κειμένου πίσω από το CW", "keyboard_shortcuts.toggle_sensitivity": "Εμφάνιση/απόκρυψη πολυμέσων", "keyboard_shortcuts.toot": "Δημιουργία νέας ανάρτησης", + "keyboard_shortcuts.top": "Μετακίνηση στην κορυφή της λίστας", "keyboard_shortcuts.translate": "για να μεταφραστεί μια ανάρτηση", "keyboard_shortcuts.unfocus": "Αποεστίαση του πεδίου σύνθεσης/αναζήτησης", "keyboard_shortcuts.up": "Μετακίνηση προς τα πάνω στη λίστα", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 34c0321532..1be1ed615b 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe this for people with visual impairments…", "alt_text_modal.done": "Done", "announcement.announcement": "Announcement", + "annual_report.announcement.action_build": "Build my Wrapstodon", + "annual_report.announcement.action_view": "View my Wrapstodon", + "annual_report.announcement.description": "Discover more about your engagement on Mastodon over the past year.", + "annual_report.announcement.title": "Wrapstodon {year} has arrived", "annual_report.summary.archetype.booster": "The cool-hunter", "annual_report.summary.archetype.lurker": "The lurker", "annual_report.summary.archetype.oracle": "The oracle", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toggle_sensitivity": "Show/hide media", "keyboard_shortcuts.toot": "to start a brand new post", + "keyboard_shortcuts.top": "Move to top of list", "keyboard_shortcuts.translate": "to translate a post", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 1108f8194e..3a7add31c8 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe this for people with visual impairments…", "alt_text_modal.done": "Done", "announcement.announcement": "Announcement", + "annual_report.announcement.action_build": "Build my Wrapstodon", + "annual_report.announcement.action_view": "View my Wrapstodon", + "annual_report.announcement.description": "Discover more about your engagement on Mastodon over the past year.", + "annual_report.announcement.title": "Wrapstodon {year} has arrived", "annual_report.summary.archetype.booster": "The cool-hunter", "annual_report.summary.archetype.lurker": "The lurker", "annual_report.summary.archetype.oracle": "The oracle", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Show/hide text behind CW", "keyboard_shortcuts.toggle_sensitivity": "Show/hide media", "keyboard_shortcuts.toot": "Start a new post", + "keyboard_shortcuts.top": "Move to top of list", "keyboard_shortcuts.translate": "to translate a post", "keyboard_shortcuts.unfocus": "Unfocus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index f9398ca59e..e9fc2f6dd7 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describí esto para personas con dificultades visuales…", "alt_text_modal.done": "Listo", "announcement.announcement": "Anuncio", + "annual_report.announcement.action_build": "Construir mi MastodonAnual", + "annual_report.announcement.action_view": "Ver mi MastodonAnual", + "annual_report.announcement.description": "Descubrí más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Llegó MastodonAnual {year}", "annual_report.summary.archetype.booster": "Corrió la voz", "annual_report.summary.archetype.lurker": "El acechador", "annual_report.summary.archetype.oracle": "El oráculo", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto detrás de la advertencia de contenido (\"CW\")", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar medios", "keyboard_shortcuts.toot": "Comenzar un mensaje nuevo", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir un mensaje", "keyboard_shortcuts.unfocus": "Quitar el foco del área de texto de redacción o de búsqueda", "keyboard_shortcuts.up": "Subir en la lista", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 69dcde9181..a75bdee1ee 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -113,6 +113,9 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe esto para personas con discapacidad visual…", "alt_text_modal.done": "Hecho", "announcement.announcement": "Anuncio", + "annual_report.announcement.action_view": "Ver mi Wrapstodon", + "annual_report.announcement.description": "Descubre más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Wrapstodon {year} ha llegado", "annual_report.summary.archetype.booster": "El cazador de tendencias", "annual_report.summary.archetype.lurker": "El merodeador", "annual_report.summary.archetype.oracle": "El oráculo", @@ -516,6 +519,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto detrás de AC", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar multimedia", "keyboard_shortcuts.toot": "Comenzar una nueva publicación", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "Desenfocar área de redacción/búsqueda", "keyboard_shortcuts.up": "Ascender en la lista", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 1501531fc3..b755a627f3 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -113,6 +113,9 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descríbelo para personas con discapacidad visual…", "alt_text_modal.done": "Hecho", "announcement.announcement": "Comunicación", + "annual_report.announcement.action_view": "Ver mi Wrapstodon", + "annual_report.announcement.description": "Descubre más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Wrapstodon {year} ha llegado", "annual_report.summary.archetype.booster": "El cazador de tendencias", "annual_report.summary.archetype.lurker": "El acechador", "annual_report.summary.archetype.oracle": "El oráculo", @@ -516,6 +519,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto tras aviso de contenido (CW)", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar multimedia", "keyboard_shortcuts.toot": "Comenzar una nueva publicación", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "Quitar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "Moverse hacia arriba en la lista", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index c19aa7d6b9..4f12c302ed 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Näita/peida teksti hoiatuse taga", "keyboard_shortcuts.toggle_sensitivity": "Näita/peida meediat", "keyboard_shortcuts.toot": "Alusta uut postitust", + "keyboard_shortcuts.top": "Tõsta loendi algusesse", "keyboard_shortcuts.translate": "postituse tõlkimiseks", "keyboard_shortcuts.unfocus": "Fookus tekstialalt/otsingult ära", "keyboard_shortcuts.up": "Liigu loetelus üles", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 093984a09e..73afc7a231 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "شرح برای افرادی با مشکلات بینایی…", "alt_text_modal.done": "انجام شد", "announcement.announcement": "اعلامیه", + "annual_report.announcement.action_build": "ساخت خلاصهٔ ماستودونم", + "annual_report.announcement.action_view": "دیدن خلاصهٔ ماستودونم", + "annual_report.announcement.description": "کشف بیش‌تر دربارهٔ درگیریتان روی ماستودون در سال گذشته.", + "annual_report.announcement.title": "خلاصهٔ ماستودون {year} این‌جاست", "annual_report.summary.archetype.booster": "باحال‌یاب", "annual_report.summary.archetype.lurker": "کم‌پیدا", "annual_report.summary.archetype.oracle": "غیب‌گو", @@ -357,6 +361,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": "لطفاً صفحه را دوباره باز کنید. اگر کمکی نکرد، شاید همچنان بتوانید با ماستودون از راه یک مرورگر دیگر یا با یکی از کاره‌های آن کار کنید.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "نمایش/نهفتن نوشتهٔ پشت هشدار محتوا", "keyboard_shortcuts.toggle_sensitivity": "نمایش/نهفتن رسانه", "keyboard_shortcuts.toot": "شروع یک فرستهٔ جدید", + "keyboard_shortcuts.top": "جابه‌جایی به بالای سیاهه", "keyboard_shortcuts.translate": "برای ترجمه یک پست", "keyboard_shortcuts.unfocus": "برداشتن تمرکز از ناحیهٔ نوشتن یا جست‌وجو", "keyboard_shortcuts.up": "بالا بردن در سیاهه", @@ -607,8 +613,8 @@ "notification.admin.report_statuses_other": "{name}، {target} را گزارش داد", "notification.admin.sign_up": "{name} ثبت نام کرد", "notification.admin.sign_up.name_and_others": "{name} و {count, plural, one {# نفر دیگر} other {# نفر دیگر}} ثبت‌نام کردند", - "notification.annual_report.message": "آمار ‪#Wrapstodon‬ ‏{year} تان منتظر است! لحظه‌های به یاد ماندنی و نقاط پررنگ سال را روی ماستودون رونمایی کنید!", - "notification.annual_report.view": "دیدن ‪#Wrapstodon‬", + "notification.annual_report.message": "#خلاصه_ماستودون {year} منتظرتان است! رونمایی از لحظه‌های به یاد ماندنی و نقاط پررنگ سال روی ماستودون!", + "notification.annual_report.view": "دیدن #خلاصه_ماستودون", "notification.favourite": "{name} فرسته‌تان را برگزید", "notification.favourite.name_and_others_with_link": "{name} و {count, plural, one {# نفر دیگر} other {# نفر دیگر}} فرسته‌تان را برگزیدند", "notification.favourite_pm": "{name} اشارهٔ خصوصیتان را برگزید", @@ -903,6 +909,7 @@ "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد", "status.embed": "گرفتن کد تعبیه", "status.favourite": "برگزیده‌", + "status.favourites_count": "{count, plural, one {{counter} برگزیدن} other {{counter} برگزیدن}}", "status.filter": "پالایش این فرسته", "status.history.created": "توسط {name} در {date} ایجاد شد", "status.history.edited": "توسط {name} در {date} ویرایش شد", @@ -937,12 +944,14 @@ "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.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/fi.json b/app/javascript/mastodon/locales/fi.json index 9b3aacb9b4..f6033a3a8f 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -80,7 +80,7 @@ "account.requests_to_follow_you": "Pyynnöt seurata sinua", "account.share": "Jaa käyttäjän @{name} profiili", "account.show_reblogs": "Näytä käyttäjän @{name} tehostukset", - "account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", "account.unblock": "Kumoa käyttäjän @{name} esto", "account.unblock_domain": "Kumoa verkkotunnuksen {domain} esto", "account.unblock_domain_short": "Kumoa esto", @@ -100,7 +100,7 @@ "admin.impact_report.instance_followers": "Seuraajat, jotka käyttäjämme menettäisivät", "admin.impact_report.instance_follows": "Seuraajat, jotka heidän käyttäjänsä menettäisivät", "admin.impact_report.title": "Vaikutusten yhteenveto", - "alert.rate_limited.message": "Yritä uudelleen {retry_time, time, medium} jälkeen.", + "alert.rate_limited.message": "Yritä uudelleen kello {retry_time, time, medium} jälkeen.", "alert.rate_limited.title": "Pyyntömäärää rajoitettu", "alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.title": "Hups!", @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Kuvaile tätä näkövammallisille ihmisille…", "alt_text_modal.done": "Valmis", "announcement.announcement": "Tiedote", + "annual_report.announcement.action_build": "Koosta oma Wrapstodon", + "annual_report.announcement.action_view": "Näytä oma Wrapstodon", + "annual_report.announcement.description": "Tutustu toimintaasi Mastodonissa kuluneen vuoden aikana.", + "annual_report.announcement.title": "Wrapstodon {year} on täällä", "annual_report.summary.archetype.booster": "Tehostaja", "annual_report.summary.archetype.lurker": "Lymyilijä", "annual_report.summary.archetype.oracle": "Oraakkeli", @@ -202,7 +206,7 @@ "compose.published.body": "Julkaisu lähetetty.", "compose.published.open": "Avaa", "compose.saved.body": "Julkaisu tallennettu.", - "compose_form.direct_message_warning_learn_more": "Lisätietoja", + "compose_form.direct_message_warning_learn_more": "Lue lisää", "compose_form.encryption_warning": "Mastodonin julkaisut eivät ole päästä päähän salattuja. Älä jaa arkaluonteisia tietoja Mastodonissa.", "compose_form.hashtag_warning": "Tätä julkaisua ei voi liittää aihetunnisteisiin, koska se ei ole julkinen. Vain näkyvyydeltään julkisiksi määritettyjä julkaisuja voidaan hakea aihetunnisteiden avulla.", "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.", @@ -298,7 +302,7 @@ "domain_block_modal.they_cant_follow": "Kukaan tältä palvelimelta ei voi seurata sinua.", "domain_block_modal.they_wont_know": "Hän ei saa tietää tulleensa estetyksi.", "domain_block_modal.title": "Estetäänkö verkkotunnus?", - "domain_block_modal.you_will_lose_num_followers": "Menetät {followersCount, plural, one {{followersCountDisplay} seuraajasi} other {{followersCountDisplay} seuraajaasi}} ja {followingCount, plural, one {{followingCountDisplay} seurattavasi} other {{followingCountDisplay} seurattavaasi}}.", + "domain_block_modal.you_will_lose_num_followers": "Menetät {followersCount, plural, one {{followersCountDisplay}:n seuraajasi} other {{followersCountDisplay} seuraajaasi}} ja {followingCount, plural, one {{followingCountDisplay}:n seurattavasi} other {{followingCountDisplay} seurattavaasi}}.", "domain_block_modal.you_will_lose_relationships": "Menetät kaikki tämän palvelimen seuraajasi ja seurattavasi.", "domain_block_modal.you_wont_see_posts": "Et enää näe julkaisuja etkä ilmoituksia tämän palvelimen käyttäjiltä.", "domain_pill.activitypub_lets_connect": "Sen avulla voit muodostaa yhteyden ja olla vuorovaikutuksessa ihmisten kanssa, ei vain Mastodonissa vaan myös muissa sosiaalisissa sovelluksissa.", @@ -338,7 +342,7 @@ "empty_column.account_hides_collections": "Käyttäjä on päättänyt pitää nämä tiedot yksityisinä", "empty_column.account_suspended": "Tili jäädytetty", "empty_column.account_timeline": "Ei julkaisuja täällä!", - "empty_column.account_unavailable": "Profiilia ei ole saatavilla", + "empty_column.account_unavailable": "Profiili ei saatavilla", "empty_column.blocks": "Et ole vielä estänyt käyttäjiä.", "empty_column.bookmarked_statuses": "Et ole vielä lisännyt julkaisuja kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.", "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista, niin homma lähtee käyntiin!", @@ -436,15 +440,15 @@ "hashtag.column_settings.tag_mode.any": "Mikä tahansa näistä", "hashtag.column_settings.tag_mode.none": "Ei mitään näistä", "hashtag.column_settings.tag_toggle": "Sisällytä lisätunnisteet tähän sarakkeeseen", - "hashtag.counter_by_accounts": "{count, plural, one {{counter} osallistuja} other {{counter} osallistujaa}}", - "hashtag.counter_by_uses": "{count, plural, one{{counter} julkaisu} other {{counter} julkaisua}}", - "hashtag.counter_by_uses_today": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}} tänään", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} osallistuja} other {{counter} osallistujaa}}", + "hashtag.counter_by_uses": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "hashtag.counter_by_uses_today": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}} tänään", "hashtag.feature": "Suosittele profiilissa", "hashtag.follow": "Seuraa aihetunnistetta", "hashtag.mute": "Mykistä #{hashtag}", "hashtag.unfeature": "Kumoa suosittelu profiilissa", "hashtag.unfollow": "Lopeta aihetunnisteen seuraaminen", - "hashtags.and_other": "…ja {count, plural, other {# lisää}}", + "hashtags.and_other": "…ja {count, plural, other {# lisää}}", "hints.profiles.followers_may_be_missing": "Tämän profiilin seuraajia saattaa puuttua.", "hints.profiles.follows_may_be_missing": "Tämän profiilin seurattavia saattaa puuttua.", "hints.profiles.posts_may_be_missing": "Tämän profiilin julkaisuja saattaa puuttua.", @@ -479,9 +483,9 @@ "interaction_modal.on_this_server": "Tällä palvelimella", "interaction_modal.title": "Jatka kirjautumalla sisään", "interaction_modal.username_prompt": "Esim. {example}", - "intervals.full.days": "{number, plural, one {# päivä} other {# päivää}}", - "intervals.full.hours": "{number, plural, one {# tunti} other {# tuntia}}", - "intervals.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}}", + "intervals.full.days": "{number, plural, one {# päivä} other {# päivää}}", + "intervals.full.hours": "{number, plural, one {# tunti} other {# tuntia}}", + "intervals.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}}", "keyboard_shortcuts.back": "Siirry takaisin", "keyboard_shortcuts.blocked": "Avaa estettyjen käyttäjien luettelo", "keyboard_shortcuts.boost": "Tehosta julkaisua", @@ -498,7 +502,7 @@ "keyboard_shortcuts.home": "Avaa kotiaikajana", "keyboard_shortcuts.hotkey": "Pikanäppäin", "keyboard_shortcuts.legend": "Näytä tämä ohje", - "keyboard_shortcuts.load_more": "Kohdista ”Lataa lisää” -painikkeeseen", + "keyboard_shortcuts.load_more": "Kohdista ”Lataa lisää” -⁠painikkeeseen", "keyboard_shortcuts.local": "Avaa paikallinen aikajana", "keyboard_shortcuts.mention": "Mainitse tekijä", "keyboard_shortcuts.muted": "Avaa mykistettyjen käyttäjien luettelo", @@ -512,10 +516,11 @@ "keyboard_shortcuts.requests": "Avaa seurantapyyntöjen luettelo", "keyboard_shortcuts.search": "Kohdista hakukenttään", "keyboard_shortcuts.spoilers": "Näytä tai piilota sisältövaroituskenttä", - "keyboard_shortcuts.start": "Avaa Näin pääset alkuun -sarake", + "keyboard_shortcuts.start": "Avaa ”Näin pääset alkuun” -⁠sarake", "keyboard_shortcuts.toggle_hidden": "Näytä tai piilota sisältövaroituksella merkitty teksti", "keyboard_shortcuts.toggle_sensitivity": "Näytä tai piilota media", "keyboard_shortcuts.toot": "Luo uusi julkaisu", + "keyboard_shortcuts.top": "Siirry luettelon alkuun", "keyboard_shortcuts.translate": "Käännä julkaisu", "keyboard_shortcuts.unfocus": "Poistu kirjoitus- tai hakukentästä", "keyboard_shortcuts.up": "Siirry luettelossa taaksepäin", @@ -530,7 +535,7 @@ "limited_account_hint.title": "Palvelimen {domain} moderaattorit ovat piilottaneet tämän profiilin.", "link_preview.author": "Tehnyt {name}", "link_preview.more_from_author": "Lisää tekijältä {name}", - "link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", "lists.add_member": "Lisää", "lists.add_to_list": "Lisää listaan", "lists.add_to_lists": "Lisää {name} listaan", @@ -543,7 +548,7 @@ "lists.exclusive": "Piilota jäsenet kotisyötteestä", "lists.exclusive_hint": "Jos joku on tässä listassa, piilota hänet kotisyötteestäsi, jotta et näe hänen julkaisujaan kahteen kertaan.", "lists.find_users_to_add": "Etsi lisättäviä käyttäjiä", - "lists.list_members_count": "{count, plural, one {# jäsen} other {# jäsentä}}", + "lists.list_members_count": "{count, plural, one {# jäsen} other {# jäsentä}}", "lists.list_name": "Listan nimi", "lists.new_list_name": "Uuden listan nimi", "lists.no_lists_yet": "Ei vielä listoja.", @@ -556,7 +561,7 @@ "lists.save": "Tallenna", "lists.search": "Haku", "lists.show_replies_to": "Sisällytä listan jäsenten vastaukset kohteeseen", - "load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}", + "load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}", "loading_indicator.label": "Ladataan…", "media_gallery.hide": "Piilota", "moved_to_account_banner.text": "Tilisi {disabledAccount} on tällä hetkellä poissa käytöstä, koska teit siirron tiliin {movedToAccount}.", @@ -615,7 +620,7 @@ "notification.favourite_pm": "{name} lisäsi yksityismainintasi suosikkeihinsa", "notification.favourite_pm.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} lisäsivät yksityismainintasi suosikkeihinsa", "notification.follow": "{name} seurasi sinua", - "notification.follow.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} seurasivat sinua", + "notification.follow.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} seurasivat sinua", "notification.follow_request": "{name} on pyytänyt lupaa seurata sinua", "notification.follow_request.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} pyysivät saada seurata sinua", "notification.label.mention": "Maininta", @@ -638,7 +643,7 @@ "notification.poll": "Äänestys, johon osallistuit, on päättynyt", "notification.quoted_update": "{name} muokkasi lainaamaasi julkaisua", "notification.reblog": "{name} tehosti julkaisuasi", - "notification.reblog.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} tehostivat julkaisuasi", + "notification.reblog.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} tehostivat julkaisuasi", "notification.relationships_severance_event": "Menetettiin yhteydet palvelimeen {name}", "notification.relationships_severance_event.account_suspension": "Palvelimen {from} ylläpitäjä on jäädyttänyt palvelimen {target} vuorovaikutuksen. Enää et voi siis vastaanottaa päivityksiä heiltä tai olla yhteyksissä heidän kanssaan.", "notification.relationships_severance_event.domain_block": "Palvelimen {from} ylläpitäjä on estänyt palvelimen {target} vuorovaikutuksen – mukaan lukien {followersCount} seuraajistasi ja {followingCount, plural, one {# seurattavistasi} other {# seurattavistasi}}.", @@ -647,7 +652,7 @@ "notification.status": "{name} julkaisi juuri", "notification.update": "{name} muokkasi julkaisua", "notification_requests.accept": "Hyväksy", - "notification_requests.accept_multiple": "{count, plural, one {Hyväksy # pyyntö…} other {Hyväksy # pyyntöä…}}", + "notification_requests.accept_multiple": "{count, plural, one {Hyväksy # pyyntö…} other {Hyväksy # pyyntöä…}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Hyväksy pyyntö} other {Hyväksy pyynnöt}}", "notification_requests.confirm_accept_multiple.message": "Olet aikeissa hyväksyä {count, plural, one {ilmoituspyynnön} other {# ilmoituspyyntöä}}. Haluatko varmasti jatkaa?", "notification_requests.confirm_accept_multiple.title": "Hyväksytäänkö ilmoituspyynnöt?", @@ -711,7 +716,7 @@ "notifications.policy.filter_limited_accounts_title": "Moderoidut tilit", "notifications.policy.filter_new_accounts.hint": "Luotu {days, plural, one {viime päivän} other {viimeisen # päivän}} aikana", "notifications.policy.filter_new_accounts_title": "Uudet tilit", - "notifications.policy.filter_not_followers_hint": "Mukaan lukien alle {days, plural, one {päivän} other {# päivää}} sinua seuranneet", + "notifications.policy.filter_not_followers_hint": "Mukaan lukien alle {days, plural, one {päivän} other {# päivää}} sinua seuranneet", "notifications.policy.filter_not_followers_title": "Käyttäjät, jotka eivät seuraa sinua", "notifications.policy.filter_not_following_hint": "Kunnes hyväksyt heidät manuaalisesti", "notifications.policy.filter_not_following_title": "Käyttäjät, joita et seuraa", @@ -742,11 +747,11 @@ "poll.closed": "Päättynyt", "poll.refresh": "Päivitä", "poll.reveal": "Näytä tulokset", - "poll.total_people": "{count, plural, one {# käyttäjä} other {# käyttäjää}}", - "poll.total_votes": "{count, plural, one {# ääni} other {# ääntä}}", + "poll.total_people": "{count, plural, one {# käyttäjä} other {# käyttäjää}}", + "poll.total_votes": "{count, plural, one {# ääni} other {# ääntä}}", "poll.vote": "Äänestä", "poll.voted": "Äänestit tätä vastausta", - "poll.votes": "{votes, plural, one {# ääni} other {# ääntä}}", + "poll.votes": "{votes, plural, one {# ääni} other {# ääntä}}", "poll_button.add_poll": "Lisää äänestys", "poll_button.remove_poll": "Poista äänestys", "privacy.change": "Muuta julkaisun näkyvyyttä", @@ -756,7 +761,7 @@ "privacy.private.short": "Seuraajat", "privacy.public.long": "Kuka tahansa Mastodonissa ja sen ulkopuolella", "privacy.public.short": "Julkinen", - "privacy.quote.anyone": "{visibility}, kuka vain voi lainata", + "privacy.quote.anyone": "{visibility}, kuka tahansa voi lainata", "privacy.quote.disabled": "{visibility}, lainaukset poissa käytöstä", "privacy.quote.limited": "{visibility}, lainauksia rajoitettu", "privacy.unlisted.additional": "Tämä toimii muuten kuin julkinen, mutta julkaisut eivät näy livesyöte-, aihetunniste- tai selausnäkymissä eivätkä Mastodonin hakutuloksissa, vaikka ne olisivat käyttäjätililläsi yleisesti sallittuina.", @@ -765,7 +770,7 @@ "privacy_policy.last_updated": "Päivitetty viimeksi {date}", "privacy_policy.title": "Tietosuojakäytäntö", "quote_error.edit": "Lainauksia ei voi lisätä julkaisua muokattaessa.", - "quote_error.poll": "Äänestysten lainaaminen ei ole sallittua.", + "quote_error.poll": "Lainaaminen äänestyksen ohessa ei ole sallittua.", "quote_error.private_mentions": "Lainaaminen ei ole sallittua yksityismaininnoissa.", "quote_error.quote": "Vain yksi lainaus kerrallaan on sallittu.", "quote_error.unauthorized": "Sinulla ei ole valtuuksia lainata tätä julkaisua.", @@ -774,21 +779,21 @@ "refresh": "Päivitä", "regeneration_indicator.please_stand_by": "Ole valmiina.", "regeneration_indicator.preparing_your_home_feed": "Kotisyötettäsi valmistellaan…", - "relative_time.days": "{number} pv", - "relative_time.full.days": "{number, plural, one {# päivä} other {# päivää}} sitten", - "relative_time.full.hours": "{number, plural, one {# tunti} other {# tuntia}} sitten", + "relative_time.days": "{number} pv", + "relative_time.full.days": "{number, plural, one {# päivä} other {# päivää}} sitten", + "relative_time.full.hours": "{number, plural, one {# tunti} other {# tuntia}} sitten", "relative_time.full.just_now": "juuri nyt", - "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten", - "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten", - "relative_time.hours": "{number} t", + "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten", + "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten", + "relative_time.hours": "{number} t", "relative_time.just_now": "nyt", - "relative_time.minutes": "{number} min", - "relative_time.seconds": "{number} s", + "relative_time.minutes": "{number} min", + "relative_time.seconds": "{number} s", "relative_time.today": "tänään", "remove_quote_hint.button_label": "Selvä", "remove_quote_hint.message": "Voit tehdä sen {icon}-valikosta.", "remove_quote_hint.title": "Haluatko poistaa lainatun julkaisusi?", - "reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}", + "reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}", "reply_indicator.cancel": "Peruuta", "reply_indicator.poll": "Äänestys", "report.block": "Estä", @@ -831,7 +836,7 @@ "report.thanks.title_actionable": "Kiitos raportista – tutkimme asiaa.", "report.unfollow": "Lopeta käyttäjän @{name} seuraaminen", "report.unfollow_explanation": "Seuraat tätä tiliä. Jotta et enää näkisi sen julkaisuja kotisyötteessäsi, lopeta tilin seuraaminen.", - "report_notification.attached_statuses": "{count, plural, one {{count} julkaisu} other {{count} julkaisua}} liitteenä", + "report_notification.attached_statuses": "{count, plural, one {{count} julkaisu} other {{count} julkaisua}} liitteenä", "report_notification.categories.legal": "Lakiseikat", "report_notification.categories.legal_sentence": "laiton sisältö", "report_notification.categories.other": "Muu", @@ -847,11 +852,11 @@ "search.quick_action.account_search": "Profiilit haulla {x}", "search.quick_action.go_to_account": "Siirry profiiliin {x}", "search.quick_action.go_to_hashtag": "Siirry aihetunnisteeseen {x}", - "search.quick_action.open_url": "Avaa URL-osoite Mastodonissa", + "search.quick_action.open_url": "Avaa URL-⁠osoite Mastodonissa", "search.quick_action.status_search": "Julkaisut haulla {x}", - "search.search_or_paste": "Hae tai liitä URL-osoite", + "search.search_or_paste": "Hae tai liitä URL-⁠osoite", "search_popout.full_text_search_disabled_message": "Ei saatavilla palvelimella {domain}.", - "search_popout.full_text_search_logged_out_message": "Käytettävissä vain sisäänkirjautuneena.", + "search_popout.full_text_search_logged_out_message": "Saatavilla vain sisäänkirjautuneena.", "search_popout.language_code": "ISO-kielikoodi", "search_popout.options": "Hakuvalinnat", "search_popout.quick_actions": "Pikatoiminnot", @@ -862,7 +867,7 @@ "search_results.all": "Kaikki", "search_results.hashtags": "Aihetunnisteet", "search_results.no_results": "Ei tuloksia.", - "search_results.no_search_yet": "Kokeile hakea julkaisuja, profiileja tai aihetunnisteita.", + "search_results.no_search_yet": "Koeta hakea julkaisuja, profiileja tai aihetunnisteita.", "search_results.see_all": "Näytä kaikki", "search_results.statuses": "Julkaisut", "search_results.title": "Haku ”{q}”", @@ -901,10 +906,10 @@ "status.direct_indicator": "Yksityismaininta", "status.edit": "Muokkaa", "status.edited": "Viimeksi muokattu {date}", - "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}", + "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}", "status.embed": "Hanki upotuskoodi", "status.favourite": "Suosikki", - "status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} 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}", @@ -939,14 +944,14 @@ "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.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.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.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", @@ -963,10 +968,10 @@ "status.show_less_all": "Näytä kaikista vähemmän", "status.show_more_all": "Näytä kaikista enemmän", "status.show_original": "Näytä alkuperäinen", - "status.title.with_attachments": "{user} liitti {attachmentCount, plural, one {{attachmentCount} tiedoston} other {{attachmentCount} tiedostoa}}", + "status.title.with_attachments": "{user} julkaisi {attachmentCount, plural, one {liitteen} other {{attachmentCount} liitettä}}", "status.translate": "Käännä", - "status.translated_from_with": "Käännetty kielestä {lang} käyttäen palvelua {provider}", - "status.uncached_media_warning": "Esikatselu ei ole käytettävissä", + "status.translated_from_with": "Käännetty kielestä {lang} palvelulla {provider}", + "status.uncached_media_warning": "Esikatselu ei saatavilla", "status.unmute_conversation": "Kumoa keskustelun mykistys", "status.unpin": "Irrota profiilista", "subscribed_languages.lead": "Vain valituilla kielillä kirjoitetut julkaisut näkyvät koti- ja lista-aikajanoillasi muutoksen jälkeen. Älä valitse mitään, jos haluat nähdä julkaisuja kaikilla kielillä.", @@ -980,17 +985,17 @@ "terms_of_service.effective_as_of": "Tulee voimaan {date}", "terms_of_service.title": "Käyttöehdot", "terms_of_service.upcoming_changes_on": "Tulevia muutoksia {date}", - "time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä", - "time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä", - "time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä", + "time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä", + "time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä", + "time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä", "time_remaining.moments": "Hetkiä jäljellä", - "time_remaining.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} jäljellä", + "time_remaining.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} jäljellä", "trends.counter_by_accounts": "{count, plural, one {{counter} käyttäjä} other {{counter} käyttäjää}} {days, plural, one {viime päivänä} other {viimeisenä {days} päivänä}}", "trends.trending_now": "Suosittua nyt", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", - "units.short.billion": "{count} mrd.", - "units.short.million": "{count} milj.", - "units.short.thousand": "{count} t.", + "units.short.billion": "{count} mrd.", + "units.short.million": "{count} milj.", + "units.short.thousand": "{count} t.", "upload_area.title": "Lähetä raahaamalla ja pudottamalla tähän", "upload_button.label": "Lisää kuvia, video tai äänitiedosto", "upload_error.limit": "Tiedostolähetysten rajoitus ylitetty.", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 3197a8ccae..b23bd0694d 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Vís/fjal tekst handan CW", "keyboard_shortcuts.toggle_sensitivity": "Vís ella fjal innihald", "keyboard_shortcuts.toot": "Byrja nýggjan post", + "keyboard_shortcuts.top": "Flyt til ovast á listanum", "keyboard_shortcuts.translate": "at umseta ein post", "keyboard_shortcuts.unfocus": "Tak skrivi-/leiti-økið úr miðdeplinum", "keyboard_shortcuts.up": "Flyt upp á listanum", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index cc896dfadc..70466504c2 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "C'est fini ! Il n'y a plus rien ici. Lorsque vous recevez de nouvelles notifications, elles apparaitront ici conformément à vos préférences.", "empty_column.notifications": "Vous n'avez pas encore de notifications. Quand d'autres personnes interagissent avec vous, vous en verrez ici.", "empty_column.public": "Il n’y a rien ici! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres serveurs pour remplir le fil public", + "error.no_hashtag_feed_access": "Rejoindre ou se connecter pour voir et suivre cet hashtag.", "error.unexpected_crash.explanation": "En raison d’un bogue dans notre code ou d’un problème de compatibilité avec votre navigateur, cette page n’a pas pu être affichée correctement.", "error.unexpected_crash.explanation_addons": "Cette page n’a pas pu être affichée correctement. Cette erreur est probablement causée par une extension de navigateur ou des outils de traduction automatique.", "error.unexpected_crash.next_steps": "Essayez de rafraîchir la page. Si cela n’aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.", @@ -515,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier texte derrière avertissement", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.top": "Mettre en tête de liste", "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Ne plus se concentrer sur la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 1b9a3580e3..91fdeff324 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "C'est fini ! Il n'y a plus rien ici. Lorsque vous recevez de nouvelles notifications, elles apparaitront ici conformément à vos préférences.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres serveurs pour remplir le fil public", + "error.no_hashtag_feed_access": "Rejoindre ou se connecter pour voir et suivre cet hashtag.", "error.unexpected_crash.explanation": "En raison d’un bug dans notre code ou d’un problème de compatibilité avec votre navigateur, cette page n’a pas pu être affichée correctement.", "error.unexpected_crash.explanation_addons": "Cette page n’a pas pu être affichée correctement. Cette erreur est probablement causée par une extension de navigateur ou des outils de traduction automatique.", "error.unexpected_crash.next_steps": "Essayez de rafraîchir la page. Si cela n’aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.", @@ -365,7 +366,7 @@ "errors.unexpected_crash.report_issue": "Signaler le problème", "explore.suggested_follows": "Personnes", "explore.title": "Tendances", - "explore.trending_links": "Nouvelles", + "explore.trending_links": "Actualités", "explore.trending_statuses": "Messages", "explore.trending_tags": "Hashtags", "featured_carousel.current": "Message {current, number} / {max, number}", @@ -515,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier le texte derrière un CW", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher les médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.top": "Mettre en tête de liste", "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Quitter la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index ed866f32ce..3f92c863e1 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Taispeáin/folaigh an téacs taobh thiar de CW", "keyboard_shortcuts.toggle_sensitivity": "Taispeáin / cuir i bhfolach meáin", "keyboard_shortcuts.toot": "Cuir tús le postáil nua", + "keyboard_shortcuts.top": "Bog go barr an liosta", "keyboard_shortcuts.translate": "post a aistriú", "keyboard_shortcuts.unfocus": "Unfocus cum textarea/search", "keyboard_shortcuts.up": "Bog suas ar an liosta", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index cdd041df41..29fe1176fc 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "תיאור התוכן לפגועי ראיה…", "alt_text_modal.done": "סיום", "announcement.announcement": "הכרזה", + "annual_report.announcement.action_build": "בנה לי את הסיכומודון שלי", + "annual_report.announcement.action_view": "לצפייה בסיכומודון שלי", + "annual_report.announcement.description": "ללמוד עוד על דבפוסי השימוש שלך במסטודון לאורך השנה החולפת.", + "annual_report.announcement.title": "סיכומודון {year} הגיע", "annual_report.summary.archetype.booster": "ההד-וניסט(ית)", "annual_report.summary.archetype.lurker": "השורץ.ת השקט.ה", "annual_report.summary.archetype.oracle": "כבוד הרב.ה", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "הצגת/הסתרת טקסט מוסתר מאחורי אזהרת תוכן", "keyboard_shortcuts.toggle_sensitivity": "הצגת/הסתרת מדיה", "keyboard_shortcuts.toot": "להתחיל חיצרוץ חדש", + "keyboard_shortcuts.top": "העברה לראש הרשימה", "keyboard_shortcuts.translate": "לתרגם הודעה", "keyboard_shortcuts.unfocus": "לצאת מתיבת חיבור/חיפוש", "keyboard_shortcuts.up": "לנוע במעלה הרשימה", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index ba3730d7b1..c8fa5f86f5 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -9,9 +9,11 @@ "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.rules": "सर्वर के नियम", + "account.account_note_header": "व्यक्तिगत नोंध", "account.add_or_remove_from_list": "सूची में जोड़ें या हटाए", "account.badges.bot": "बॉट", "account.badges.group": "समूह", @@ -19,17 +21,31 @@ "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.domain_blocking": "डोमेन ब्लॉक करें", "account.edit_profile": "प्रोफ़ाइल संपादित करें", + "account.edit_profile_short": "संपादित करें", "account.enable_notifications": "जब @{name} पोस्ट मौजूद हो सूचित करें", "account.endorse": "प्रोफ़ाइल पर दिखाए", + "account.familiar_followers_many": "{name1}{name2} और {othersCount, plural, one {एक और जिन्हे आप जानते है} other {# और जिन्हे आप जानते है}}", + "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_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.following": "फॉलोइंग", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 08082b53ba..d1df41ed7b 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -113,6 +113,7 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Írd le a látássérültek számára…", "alt_text_modal.done": "Kész", "announcement.announcement": "Közlemény", + "annual_report.announcement.action_view": "A Wrapstodon-om megtekintése", "annual_report.summary.archetype.booster": "A cool-vadász", "annual_report.summary.archetype.lurker": "A settenkedő", "annual_report.summary.archetype.oracle": "Az orákulum", @@ -516,6 +517,7 @@ "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése", "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése", "keyboard_shortcuts.toot": "Új bejegyzés írása", + "keyboard_shortcuts.top": "Ugorj a lista elejére", "keyboard_shortcuts.translate": "Bejegyzés lefordítása", "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele", "keyboard_shortcuts.up": "Mozgás felfelé a listában", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 5af1f32ed5..304ee7046b 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Lýstu þessu fyrir fólk með skerta sjón…", "alt_text_modal.done": "Lokið", "announcement.announcement": "Auglýsing", + "annual_report.announcement.action_build": "Byggja Wrapstodon fyrir mig", + "annual_report.announcement.action_view": "Skoða minn Wrapstodon", + "annual_report.announcement.description": "Skoðaðu meira um virkni þína á Mastodon á síðastliðnu ári.", + "annual_report.announcement.title": "Wrapstodon {year} er komið", "annual_report.summary.archetype.booster": "Svali gaurinn", "annual_report.summary.archetype.lurker": "Lurkurinn", "annual_report.summary.archetype.oracle": "Völvan", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Birta/fela texta á bak við aðvörun vegna efnis", "keyboard_shortcuts.toggle_sensitivity": "Birta/fela myndir", "keyboard_shortcuts.toot": "Byrja nýja færslu", + "keyboard_shortcuts.top": "Færa efst á listann", "keyboard_shortcuts.translate": "að þýða færslu", "keyboard_shortcuts.unfocus": "Taka virkni úr textainnsetningarreit eða leit", "keyboard_shortcuts.up": "Fara ofar í listanum", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 9fc0be4531..6ed16f8517 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostra/Nasconde il testo dietro CW", "keyboard_shortcuts.toggle_sensitivity": "Mostra/Nasconde media", "keyboard_shortcuts.toot": "Crea un nuovo post", + "keyboard_shortcuts.top": "Sposta all'inizio della lista", "keyboard_shortcuts.translate": "Traduce un post", "keyboard_shortcuts.unfocus": "Rimuove il focus sull'area di composizione testuale/ricerca", "keyboard_shortcuts.up": "Scorre in su nell'elenco", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 501ff11eb1..09867bcf9f 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -1,6 +1,7 @@ { "about.blocks": "Prižiūrimi serveriai", "about.contact": "Kontaktai:", + "about.default_locale": "Numatyta", "about.disclaimer": "„Mastodon“ – tai nemokama atvirojo kodo programinė įranga ir „Mastodon gGmbH“ prekės ženklas.", "about.domain_blocks.no_reason_available": "Priežastis nepateikta", "about.domain_blocks.preamble": "„Mastodon“ paprastai leidžia peržiūrėti turinį ir bendrauti su naudotojais iš bet kurio kito fediverse esančio serverio. Šios yra išimtys, kurios buvo padarytos šiame konkrečiame serveryje.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Apribota", "about.domain_blocks.suspended.explanation": "Jokie duomenys iš šio serverio nebus apdorojami, saugomi ar keičiami, todėl bet kokia sąveika ar bendravimas su šio serverio naudotojais bus neįmanomas.", "about.domain_blocks.suspended.title": "Pristabdyta", + "about.language_label": "Kalba", "about.not_available": "Ši informacija nebuvo pateikta šiame serveryje.", "about.powered_by": "Decentralizuota socialinė medija, veikianti pagal „{mastodon}“", "about.rules": "Serverio taisyklės", @@ -26,8 +28,12 @@ "account.disable_notifications": "Nustoti man pranešti, kai @{name} paskelbia", "account.domain_blocking": "Blokuoti domeną", "account.edit_profile": "Redaguoti profilį", + "account.edit_profile_short": "Redaguoti", "account.enable_notifications": "Pranešti man, kai @{name} paskelbia", "account.endorse": "Rodyti profilyje", + "account.familiar_followers_many": "Sekama {name1}, {name2}, ir {othersCount, plural, one {dar vieno} few {# dar keleto} many {# dar kelių} other {# kitų pažystąmų}}", + "account.familiar_followers_one": "Seka {name1}", + "account.familiar_followers_two": "{name1} ir {name2} seka", "account.featured": "Rodomi", "account.featured.accounts": "Profiliai", "account.featured.hashtags": "Saitažodžiai", @@ -35,9 +41,15 @@ "account.featured_tags.last_status_never": "Nėra įrašų", "account.follow": "Sekti", "account.follow_back": "Sekti atgal", + "account.follow_back_short": "Sekti atgal", + "account.follow_request": "Prašyti sekti", + "account.follow_request_cancel": "Atšaukti prašymą", + "account.follow_request_cancel_short": "Atšaukti", + "account.follow_request_short": "Prašymas", "account.followers": "Sekėjai", "account.followers.empty": "Šio naudotojo dar niekas neseka.", "account.followers_counter": "{count, plural, one {{counter} sekėjas} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}", + "account.followers_you_know_counter": "{counter} žinomas", "account.following": "Sekama", "account.following_counter": "{count, plural, one {{counter} sekimas} few {{counter} sekimai} many {{counter} sekimo} other {{counter} sekimų}}", "account.follows.empty": "Šis naudotojas dar nieko neseka.", @@ -145,6 +157,7 @@ "bundle_modal_error.close": "Uždaryti", "bundle_modal_error.message": "Įkeliant šį ekraną kažkas nutiko ne taip.", "bundle_modal_error.retry": "Bandyti dar kartą", + "carousel.slide": "Rodoma {current, number} iš {max, number}", "closed_registrations.other_server_instructions": "Kadangi „Mastodon“ yra decentralizuotas, gali susikurti paskyrą kitame serveryje ir vis tiek bendrauti su šiuo serveriu.", "closed_registrations_modal.description": "Sukurti paskyrą serveryje {domain} šiuo metu neįmanoma, bet nepamiršk, kad norint naudotis „Mastodon“ nebūtina turėti paskyrą serveryje {domain}.", "closed_registrations_modal.find_another_server": "Rasti kitą serverį", @@ -161,6 +174,8 @@ "column.edit_list": "Redaguoti sąrašą", "column.favourites": "Mėgstami", "column.firehose": "Tiesioginiai srautai", + "column.firehose_local": "Tiesioginis srautas iš šio serverio", + "column.firehose_singular": "Tiesioginis srautas", "column.follow_requests": "Sekimo prašymai", "column.home": "Pagrindinis", "column.list_members": "Tvarkyti sąrašo narius", @@ -180,6 +195,7 @@ "community.column_settings.local_only": "Tik vietinis", "community.column_settings.media_only": "Tik medija", "community.column_settings.remote_only": "Tik nuotolinis", + "compose.error.blank_post": "Įrašas negali būti tuščias.", "compose.language.change": "Keisti kalbą", "compose.language.search": "Ieškoti kalbų...", "compose.published.body": "Įrašas paskelbtas.", @@ -212,6 +228,13 @@ "confirmations.delete_list.confirm": "Ištrinti", "confirmations.delete_list.message": "Ar tikrai nori negrįžtamai ištrinti šį sąrašą?", "confirmations.delete_list.title": "Ištrinti sąrašą?", + "confirmations.discard_draft.confirm": "Išsaugoti ir tęsti", + "confirmations.discard_draft.edit.cancel": "Tęsti redagavimą", + "confirmations.discard_draft.edit.message": "Tęsdami, jūs prarasite visus pakeitimus, kuriuos padarėte šiuo metu redaguojamame įraše.", + "confirmations.discard_draft.edit.title": "Atmesti pakeitimus savo įraše?", + "confirmations.discard_draft.post.cancel": "Tęsti juodraščio redagavimą", + "confirmations.discard_draft.post.message": "Tęsdami ištrinsite šiuo metu kuriamą įrašą.", + "confirmations.discard_draft.post.title": "Atmesti savo įrašo juodraštį?", "confirmations.discard_edit_media.confirm": "Atmesti", "confirmations.discard_edit_media.message": "Turi neišsaugotų medijos aprašymo ar peržiūros pakeitimų. Vis tiek juos atmesti?", "confirmations.follow_to_list.confirm": "Sekti ir pridėti prie sąrašo", @@ -225,13 +248,30 @@ "confirmations.missing_alt_text.secondary": "Siųsti vis tiek", "confirmations.missing_alt_text.title": "Pridėti alternatyvųjį tekstą?", "confirmations.mute.confirm": "Nutildyti", + "confirmations.private_quote_notify.cancel": "Grįžti prie redagavimo", + "confirmations.private_quote_notify.confirm": "Paskelbti įrašą", + "confirmations.private_quote_notify.do_not_show_again": "Neberodyti šio pranešimo dar kartą", + "confirmations.private_quote_notify.message": "Asmuo, kurį paminite, ir kiti paminėti asmenys bus informuoti ir galės peržiūrėti jūsų įrašą, net jei jie neseka jūsų.", + "confirmations.private_quote_notify.title": "Dalytis su sekėjais ir paminėtais vartotojais?", + "confirmations.quiet_post_quote_info.dismiss": "Daugiau man nepriminti", + "confirmations.quiet_post_quote_info.got_it": "Supratau", + "confirmations.quiet_post_quote_info.message": "Kai norite paminėti tylų viešą įrašą, jūsų įrašas bus paslėptas Tendencijų sąrašuose.", + "confirmations.quiet_post_quote_info.title": "Kai paminite tylius viešus įrašus", "confirmations.redraft.confirm": "Ištrinti ir iš naujo parengti", "confirmations.redraft.message": "Ar tikrai nori ištrinti šį įrašą ir parengti jį iš naujo? Bus prarasti mėgstami ir pasidalinimai, o atsakymai į originalų įrašą bus panaikinti.", "confirmations.redraft.title": "Ištrinti ir iš naujo parengti įrašą?", "confirmations.remove_from_followers.confirm": "Šalinti sekėją", "confirmations.remove_from_followers.message": "{name} nustos jus sekti. Ar tikrai norite tęsti?", "confirmations.remove_from_followers.title": "Šalinti sekėją?", + "confirmations.revoke_quote.confirm": "Pašalinti įrašą", + "confirmations.revoke_quote.message": "Šio veiksmo negalima anuliuoti.", + "confirmations.revoke_quote.title": "Pašalinti įrašą?", + "confirmations.unblock.confirm": "Atblokuoti", + "confirmations.unblock.title": "Atblokuoti {name}?", "confirmations.unfollow.confirm": "Nebesekti", + "confirmations.unfollow.title": "Nebesekti {name}?", + "confirmations.withdraw_request.confirm": "Atšaukti prašymą", + "confirmations.withdraw_request.title": "Atšaukti prašymą sekti {name}?", "content_warning.hide": "Slėpti įrašą", "content_warning.show": "Rodyti vis tiek", "content_warning.show_more": "Rodyti daugiau", @@ -250,6 +290,7 @@ "disabled_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta.", "dismissable_banner.community_timeline": "Tai – naujausi vieši įrašai iš žmonių, kurių paskyros talpinamos {domain}.", "dismissable_banner.dismiss": "Atmesti", + "dismissable_banner.public_timeline": "Tai yra naujausi vieši įrašai iš fediverse, kuriuos seka {domain} vartotojai.", "domain_block_modal.block": "Blokuoti serverį", "domain_block_modal.block_account_instead": "Blokuoti @{name} vietoj to", "domain_block_modal.they_can_interact_with_old_posts": "Žmonės iš šio serverio gali bendrauti su tavo senomis įrašomis.", @@ -272,6 +313,7 @@ "domain_pill.your_handle": "Tavo socialinis medijos vardas:", "domain_pill.your_server": "Tavo skaitmeniniai namai, kuriuose saugomi visi tavo įrašai. Nepatinka šis? Bet kada perkelk serverius ir atsivesk ir savo sekėjus.", "domain_pill.your_username": "Tavo unikalus identifikatorius šiame serveryje. Skirtinguose serveriuose galima rasti naudotojų su tuo pačiu naudotojo vardu.", + "dropdown.empty": "Pasirinkite variantą", "embed.instructions": "Įterpk šį įrašą į savo svetainę nukopijuojant toliau pateiktą kodą.", "embed.preview": "Štai kaip tai atrodys:", "emoji_button.activity": "Veikla", @@ -289,6 +331,8 @@ "emoji_button.search_results": "Paieškos rezultatai", "emoji_button.symbols": "Simboliai", "emoji_button.travel": "Kelionės ir vietos", + "empty_column.account_featured.me": "Jūs dar nieko neparyškinote. Ar žinojote, kad savo profilyje galite parodyti dažniausiai naudojamas žymes ir netgi savo draugų paskyras?", + "empty_column.account_featured.other": "{acct} dar nieko neparyškino. Ar žinojote, kad savo profilyje galite pateikti dažniausiai naudojamus žymes ir netgi savo draugų paskyras?", "empty_column.account_featured_other.unknown": "Ši paskyra dar nieko neparodė.", "empty_column.account_hides_collections": "Šis (-i) naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą.", "empty_column.account_suspended": "Paskyra pristabdyta.", @@ -298,6 +342,7 @@ "empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo pridėtos žymės. Kai vieną iš jų pridėsi į žymes, jis bus rodomas čia.", "empty_column.community": "Vietinė laiko skalė yra tuščia. Parašyk ką nors viešai, kad pradėtum sąveikauti.", "empty_column.direct": "Dar neturi jokių privačių paminėjimų. Kai išsiųsi arba gausi vieną iš jų, jis bus rodomas čia.", + "empty_column.disabled_feed": "Šis srautas buvo išjungtas jūsų serverio administratorių.", "empty_column.domain_blocks": "Kol kas nėra užblokuotų serverių.", "empty_column.explore_statuses": "Šiuo metu niekas nėra tendencinga. Patikrinkite vėliau!", "empty_column.favourited_statuses": "Dar neturi mėgstamų įrašų. Kai vieną iš jų pamėgsi, jis bus rodomas čia.", @@ -306,11 +351,12 @@ "empty_column.followed_tags": "Dar neseki jokių saitažodžių. Kai tai padarysi, jie bus rodomi čia.", "empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.", "empty_column.home": "Tavo pagrindinio laiko skalė tuščia. Sek daugiau žmonių, kad ją užpildytum.", - "empty_column.list": "Nėra nieko šiame sąraše kol kas. Kai šio sąrašo nariai paskelbs naujų įrašų, jie bus rodomi čia.", + "empty_column.list": "Šiame sąraše dar nieko nėra. Kai šio sąrašo nariai paskelbs naujus įrašus, jie bus rodomi čia.", "empty_column.mutes": "Dar nesi nutildęs (-usi) nė vieno naudotojo.", "empty_column.notification_requests": "Viskas švaru! Čia nieko nėra. Kai gausi naujų pranešimų, jie bus rodomi čia pagal tavo nustatymus.", "empty_column.notifications": "Dar neturi jokių pranešimų. Kai kiti žmonės su tavimi sąveikaus, matysi tai čia.", "empty_column.public": "Čia nieko nėra. Parašyk ką nors viešai arba rankiniu būdu sek naudotojus iš kitų serverių, kad jį užpildytum.", + "error.no_hashtag_feed_access": "Registruokitės arba prisijunkite, kad galėtumėte peržiūrėti ir sekti šį žymę.", "error.unexpected_crash.explanation": "Dėl mūsų kodo riktos arba naršyklės suderinamumo problemos šis puslapis negalėjo būti rodomas teisingai.", "error.unexpected_crash.explanation_addons": "Šį puslapį nepavyko parodyti teisingai. Šią klaidą greičiausiai sukėlė naršyklės priedas arba automatinio vertimo įrankiai.", "error.unexpected_crash.next_steps": "Pabandyk atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsi naudotis Mastodon per kitą naršyklę arba savąją programėlę.", @@ -318,9 +364,13 @@ "errors.unexpected_crash.copy_stacktrace": "Kopijuoti dėklo eigą į iškarpinę", "errors.unexpected_crash.report_issue": "Pranešti apie problemą", "explore.suggested_follows": "Žmonės", + "explore.title": "Populiaru", "explore.trending_links": "Naujienos", "explore.trending_statuses": "Įrašai", "explore.trending_tags": "Saitažodžiai", + "featured_carousel.current": "Įrašas {current, number} / {max, number}", + "featured_carousel.header": "{count, plural, one {Iškeltas įrašas} few {Iškelti įrašai} many {Iškeltų įrašų} other {Iškelti įrašai}}", + "featured_carousel.slide": "Įrašas {current, number} iš {max, number}", "filter_modal.added.context_mismatch_explanation": "Ši filtro kategorija netaikoma kontekstui, kuriame peržiūrėjai šį įrašą. Jei nori, kad įrašas būtų filtruojamas ir šiame kontekste, turėsi redaguoti filtrą.", "filter_modal.added.context_mismatch_title": "Konteksto neatitikimas.", "filter_modal.added.expired_explanation": "Ši filtro kategorija nustojo galioti. Kad ji būtų taikoma, turėsi pakeisti galiojimo datą.", @@ -363,6 +413,7 @@ "follow_suggestions.who_to_follow": "Ką sekti", "followed_tags": "Sekami saitažodžiai", "footer.about": "Apie", + "footer.about_this_server": "Apie", "footer.directory": "Profilių katalogas", "footer.get_app": "Gauti programėlę", "footer.keyboard_shortcuts": "Spartieji klavišai", @@ -373,6 +424,8 @@ "generic.saved": "Išsaugota", "getting_started.heading": "Kaip pradėti", "hashtag.admin_moderation": "Atverti prižiūrėjimo sąsają saitažodžiui #{name}", + "hashtag.browse": "Naršyti įrašus su #{hashtag}", + "hashtag.browse_from_account": "Naršyti @{name} įrašus su žyma #{hashtag}", "hashtag.column_header.tag_mode.all": "ir {additional}", "hashtag.column_header.tag_mode.any": "ar {additional}", "hashtag.column_header.tag_mode.none": "be {additional}", @@ -385,7 +438,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} dalyvis} few {{counter} dalyviai} many {{counter} dalyvio} other {{counter} dalyvių}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}} šiandien", + "hashtag.feature": "Rodyti profilyje", "hashtag.follow": "Sekti saitažodį", + "hashtag.mute": "Nutildyti žymą #{hashtag}", + "hashtag.unfeature": "Neberodyti profilyje", "hashtag.unfollow": "Nebesekti saitažodį", "hashtags.and_other": "…ir {count, plural, one {# daugiau} few {# daugiau} many {# daugiau}other {# daugiau}}", "hints.profiles.followers_may_be_missing": "Sekėjai šiai profiliui gali būti nepateikti.", @@ -394,6 +450,7 @@ "hints.profiles.see_more_followers": "Žiūrėti daugiau sekėjų serveryje {domain}", "hints.profiles.see_more_follows": "Žiūrėti daugiau sekimų serveryje {domain}", "hints.profiles.see_more_posts": "Žiūrėti daugiau įrašų serveryje {domain}", + "home.column_settings.show_quotes": "Rodyti paminėjimus", "home.column_settings.show_reblogs": "Rodyti pakėlimus", "home.column_settings.show_replies": "Rodyti atsakymus", "home.hide_announcements": "Slėpti skelbimus", @@ -414,10 +471,12 @@ "ignore_notifications_modal.private_mentions_title": "Ignoruoti pranešimus iš neprašytų privačių paminėjimų?", "info_button.label": "Žinynas", "info_button.what_is_alt_text": "

Kas yra alternatyvusis tekstas?

Alternatyvusis tekstas pateikia vaizdų aprašymus asmenims su regos sutrikimais, turintiems mažo pralaidumo ryšį arba ieškantiems papildomo konteksto.

Galite pagerinti prieinamumą ir suprantamumą visiems, jei parašysite aiškų, glaustą ir objektyvų alternatyvųjį tekstą.

  • Užfiksuokite svarbiausius elementus.
  • Apibendrinkite tekstą vaizduose.
  • Naudokite įprasta sakinio struktūrą.
  • Venkite nereikalingos informacijos.
  • Sutelkite dėmesį į tendencijas ir pagrindines išvadas sudėtinguose vaizdiniuose (tokiuose kaip diagramos ar žemėlapiai).
", + "interaction_modal.action": "Norėdami bendrauti su {name} įrašu, turite prisijungti prie savo paskyros bet kuriame Mastodon serveryje, kurį naudojate.", "interaction_modal.go": "Eiti", "interaction_modal.no_account_yet": "Dar neturite paskyros?", "interaction_modal.on_another_server": "Kitame serveryje", "interaction_modal.on_this_server": "Šiame serveryje", + "interaction_modal.title": "Jei norite tęsti, prisijunkite", "interaction_modal.username_prompt": "Pvz., {example}", "intervals.full.days": "{number, plural, one {# diena} few {# dienos} many {# dienos} other {# dienų}}", "intervals.full.hours": "{number, plural, one {# valanda} few {# valandos} many {# valandos} other {# valandų}}", @@ -438,6 +497,7 @@ "keyboard_shortcuts.home": "Atidaryti pagrindinį laiko skalę", "keyboard_shortcuts.hotkey": "Spartusis klavišas", "keyboard_shortcuts.legend": "Rodyti šią legendą", + "keyboard_shortcuts.load_more": "Fokusuoti „Įkelti daugiau“ mygtuką", "keyboard_shortcuts.local": "Atidaryti vietinę laiko skalę", "keyboard_shortcuts.mention": "Paminėti autorių (-ę)", "keyboard_shortcuts.muted": "Atidaryti nutildytų naudotojų sąrašą", @@ -446,6 +506,7 @@ "keyboard_shortcuts.open_media": "Atidaryti mediją", "keyboard_shortcuts.pinned": "Atverti prisegtų įrašų sąrašą", "keyboard_shortcuts.profile": "Atidaryti autoriaus (-ės) profilį", + "keyboard_shortcuts.quote": "Paminėti įrašą", "keyboard_shortcuts.reply": "Atsakyti į įrašą", "keyboard_shortcuts.requests": "Atidaryti sekimo prašymų sąrašą", "keyboard_shortcuts.search": "Fokusuoti paieškos juostą", @@ -454,9 +515,12 @@ "keyboard_shortcuts.toggle_hidden": "Rodyti / slėpti tekstą po TĮ", "keyboard_shortcuts.toggle_sensitivity": "Rodyti / slėpti mediją", "keyboard_shortcuts.toot": "Pradėti naują įrašą", + "keyboard_shortcuts.top": "Perkelti į sąrašo viršų", "keyboard_shortcuts.translate": "išversti įrašą", "keyboard_shortcuts.unfocus": "Nebefokusuoti rengykles teksto sritį / paiešką", "keyboard_shortcuts.up": "Perkelti į viršų sąraše", + "learn_more_link.got_it": "Supratau", + "learn_more_link.learn_more": "Sužinoti daugiau", "lightbox.close": "Uždaryti", "lightbox.next": "Kitas", "lightbox.previous": "Ankstesnis", @@ -506,8 +570,10 @@ "mute_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.", "mute_modal.you_wont_see_posts": "Jie vis tiek gali matyti tavo įrašus, bet tu nematysi jų.", "navigation_bar.about": "Apie", + "navigation_bar.account_settings": "Slaptažodis ir saugumas", "navigation_bar.administration": "Administravimas", "navigation_bar.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają", + "navigation_bar.automated_deletion": "Automatinis įrašų ištrynimas", "navigation_bar.blocks": "Užblokuoti naudotojai", "navigation_bar.bookmarks": "Žymės", "navigation_bar.direct": "Privatūs paminėjimai", @@ -517,13 +583,23 @@ "navigation_bar.follow_requests": "Sekimo prašymai", "navigation_bar.followed_tags": "Sekami saitažodžiai", "navigation_bar.follows_and_followers": "Sekimai ir sekėjai", + "navigation_bar.import_export": "Importas ir eksportas", "navigation_bar.lists": "Sąrašai", + "navigation_bar.live_feed_local": "Tiesioginis srautas (vietinis)", + "navigation_bar.live_feed_public": "Tiesioginis srautas (viešas)", "navigation_bar.logout": "Atsijungti", "navigation_bar.moderation": "Prižiūrėjimas", + "navigation_bar.more": "Daugiau", "navigation_bar.mutes": "Nutildyti naudotojai", "navigation_bar.opened_in_classic_interface": "Įrašai, paskyros ir kiti konkretūs puslapiai pagal numatytuosius nustatymus atidaromi klasikinėje žiniatinklio sąsajoje.", "navigation_bar.preferences": "Nuostatos", + "navigation_bar.privacy_and_reach": "Privatumas ir pasiekiamumas", "navigation_bar.search": "Ieškoti", + "navigation_bar.search_trends": "Paieška / Populiaru", + "navigation_panel.collapse_followed_tags": "Sutraukti sekamų žymių meniu", + "navigation_panel.collapse_lists": "Sutraukti sąrašo meniu", + "navigation_panel.expand_followed_tags": "Išskleisti sekamų žymių meniu", + "navigation_panel.expand_lists": "Išskleisti sąrašo meniu", "not_signed_in_indicator.not_signed_in": "Norint pasiekti šį išteklį, reikia prisijungti.", "notification.admin.report": "{name} pranešė {target}", "notification.admin.report_account": "{name} pranešė {count, plural, one {# įrašą} few {# įrašus} many {# įrašo} other {# įrašų}} iš {target} kategorijai {category}", @@ -535,13 +611,17 @@ "notification.annual_report.message": "Jūsų laukia {year} #Wrapstodon! Atskleiskite savo metų svarbiausius įvykius ir įsimintinas akimirkas platformoje „Mastodon“.", "notification.annual_report.view": "Peržiūrėti #Wrapstodon", "notification.favourite": "{name} pamėgo tavo įrašą", + "notification.favourite.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pamėgo tavo įrašą", "notification.favourite_pm": "{name} pamėgo jūsų privatų paminėjimą", + "notification.favourite_pm.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pamėgo tavo privatų paminėjimą", "notification.follow": "{name} seka tave", "notification.follow.name_and_others": "{name} ir {count, plural, one {# kitas} few {# kiti} many {# kito} other {# kitų}} seka tave", "notification.follow_request": "{name} paprašė tave sekti", + "notification.follow_request.name_and_others": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pradėjo tave sekti", "notification.label.mention": "Paminėjimas", "notification.label.private_mention": "Privatus paminėjimas", "notification.label.private_reply": "Privatus atsakymas", + "notification.label.quote": "{name} paminėjo jūsų įrašą", "notification.label.reply": "Atsakymas", "notification.mention": "Paminėjimas", "notification.mentioned_you": "{name} paminėjo jus", @@ -556,7 +636,9 @@ "notification.moderation_warning.action_suspend": "Tavo paskyra buvo sustabdyta.", "notification.own_poll": "Tavo apklausa baigėsi", "notification.poll": "Baigėsi apklausa, kurioje balsavai", + "notification.quoted_update": "{name} redagavo jūsų cituotą įrašą", "notification.reblog": "{name} pakėlė tavo įrašą", + "notification.reblog.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} paryškino tavo įrašą", "notification.relationships_severance_event": "Prarasti sąryšiai su {name}", "notification.relationships_severance_event.learn_more": "Sužinoti daugiau", "notification.relationships_severance_event.user_domain_block": "Tu užblokavai {target}. Pašalinama {followersCount} savo sekėjų ir {followingCount, plural, one {# paskyrą} few {# paskyrai} many {# paskyros} other {# paskyrų}}, kurios seki.", @@ -596,6 +678,7 @@ "notifications.column_settings.mention": "Paminėjimai:", "notifications.column_settings.poll": "Balsavimo rezultatai:", "notifications.column_settings.push": "Tiesioginiai pranešimai", + "notifications.column_settings.quote": "Paminėjimai:", "notifications.column_settings.reblog": "Pakėlimai:", "notifications.column_settings.show": "Rodyti stulpelyje", "notifications.column_settings.sound": "Paleisti garsą", @@ -623,9 +706,10 @@ "notifications.policy.filter": "Filtruoti", "notifications.policy.filter_hint": "Siųsti į filtruotų pranešimų gautiejus", "notifications.policy.filter_limited_accounts_hint": "Apribota serverio prižiūrėtojų", - "notifications.policy.filter_limited_accounts_title": "Prižiūrėmi paskyrai", + "notifications.policy.filter_limited_accounts_title": "Moderuojamos paskyros", "notifications.policy.filter_new_accounts.hint": "Sukurta per {days, plural, one {vieną dieną} few {# dienas} many {# dienos} other {# dienų}}", "notifications.policy.filter_new_accounts_title": "Naujos paskyros", + "notifications.policy.filter_not_followers_hint": "Įskaitant žmones, kurie seka jus mažiau nei {days, plural, one {vieną dieną} few {# dienas} many {# dienų} other {# dienų}}", "notifications.policy.filter_not_following_hint": "Kol jų nepatvirtinsi rankiniu būdu", "notifications.policy.filter_not_following_title": "Žmonių, kuriuos neseki", "notifications.policy.filter_private_mentions_title": "Nepageidaujami privatūs paminėjimai", @@ -668,10 +752,19 @@ "privacy.private.short": "Sekėjai", "privacy.public.long": "Bet kas iš Mastodon ir ne Mastodon", "privacy.public.short": "Vieša", + "privacy.quote.anyone": "{visibility}, kiekvienas gali cituoti", + "privacy.quote.disabled": "{visibility}, paminėjimai išjungti", + "privacy.quote.limited": "{visibility}, paminėjimai apriboti", "privacy.unlisted.additional": "Tai veikia lygiai taip pat, kaip ir vieša, tik įrašas nebus rodomas tiesioginiuose srautuose, saitažodžiose, naršyme ar Mastodon paieškoje, net jei esi įtraukęs (-usi) visą paskyrą.", "privacy.unlisted.short": "Tyliai vieša", "privacy_policy.last_updated": "Paskutinį kartą atnaujinta {date}", "privacy_policy.title": "Privatumo politika", + "quote_error.edit": "Paminėjimai negali būti pridedami, kai keičiamas įrašas.", + "quote_error.poll": "Cituoti apklausose negalima.", + "quote_error.private_mentions": "Cituoti privačius paminėjus nėra leidžiama.", + "quote_error.quote": "Leidžiama pateikti tik vieną citatą vienu metu.", + "quote_error.unauthorized": "Jums neleidžiama cituoti šio įrašo.", + "quote_error.upload": "Cituoti ir pridėti papildomas bylas negalima.", "recommended": "Rekomenduojama", "refresh": "Atnaujinti", "regeneration_indicator.please_stand_by": "Laukite.", @@ -687,6 +780,9 @@ "relative_time.minutes": "{number} min.", "relative_time.seconds": "{number} sek.", "relative_time.today": "šiandien", + "remove_quote_hint.button_label": "Supratau", + "remove_quote_hint.message": "Tai galite padaryti iš {icon} parinkčių meniu.", + "remove_quote_hint.title": "Norite pašalinti savo citatą?", "reply_indicator.attachments": "{count, plural, one {# priedas} few {# priedai} many {# priedo} other {# priedų}}", "reply_indicator.cancel": "Atšaukti", "reply_indicator.poll": "Apklausa", @@ -748,7 +844,7 @@ "search.quick_action.open_url": "Atidaryti URL adresą Mastodon", "search.quick_action.status_search": "Pranešimai, atitinkantys {x}", "search.search_or_paste": "Ieškoti arba įklijuoti URL", - "search_popout.full_text_search_disabled_message": "Nepasiekima {domain}.", + "search_popout.full_text_search_disabled_message": "Paieška {domain} įrašuose išjungta.", "search_popout.full_text_search_logged_out_message": "Pasiekiama tik prisijungus.", "search_popout.language_code": "ISO kalbos kodas", "search_popout.options": "Paieškos nustatymai", @@ -777,10 +873,13 @@ "status.admin_account": "Atidaryti prižiūrėjimo sąsają @{name}", "status.admin_domain": "Atidaryti prižiūrėjimo sąsają {domain}", "status.admin_status": "Atidaryti šį įrašą prižiūrėjimo sąsajoje", + "status.all_disabled": "Įrašo pakėlimai ir paminėjimai išjungti", "status.block": "Blokuoti @{name}", "status.bookmark": "Pridėti į žymės", "status.cancel_reblog_private": "Nebepasidalinti", + "status.cannot_quote": "Jums neleidžiama paminėti šio įrašo", "status.cannot_reblog": "Šis įrašas negali būti pakeltas.", + "status.contains_quote": "Turi citatą", "status.continued_thread": "Tęsiama gijoje", "status.copy": "Kopijuoti nuorodą į įrašą", "status.delete": "Ištrinti", @@ -790,6 +889,7 @@ "status.edit": "Redaguoti", "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.embed": "Gaukite įterpimo kodą", "status.favourite": "Pamėgti", "status.filter": "Filtruoti šį įrašą", "status.history.created": "{name} sukurta {date}", @@ -804,18 +904,43 @@ "status.mute_conversation": "Nutildyti pokalbį", "status.open": "Išplėsti šį įrašą", "status.pin": "Prisegti prie profilio", + "status.quote": "Paminėjimai", + "status.quote.cancel": "Atšaukti paminėjimą", + "status.quote_error.blocked_account_hint.title": "Šis įrašas yra paslėptas, nes jūs esate užblokavę @{name}.", + "status.quote_error.blocked_domain_hint.title": "Šis įrašas yra paslėptas, nes jūs užblokavote {domain}.", + "status.quote_error.filtered": "Paslėpta dėl vieno iš jūsų filtrų", + "status.quote_error.limited_account_hint.action": "Vis tiek rodyti", + "status.quote_error.limited_account_hint.title": "Šis paskyra buvo paslėpta {domain} moderatorių.", + "status.quote_error.muted_account_hint.title": "Šis įrašas yra paslėptas, nes jūs esate užtildę @{name}.", + "status.quote_error.not_available": "Įrašas nepasiekiamas", + "status.quote_error.pending_approval": "Įrašas peržiūrimas", + "status.quote_error.pending_approval_popout.body": "„Mastodon“ galite kontroliuoti, ar kas nors gali jus cituoti (paminėti). Šis įrašas bus laukimo būsenoje, kol gausite originalaus įrašo autoriaus sutikimą.", + "status.quote_error.revoked": "Autorius pašalino įrašą", + "status.quote_followers_only": "Tik sekėjai gali cituoti šį įrašą", + "status.quote_manual_review": "Autorius atskirai įvertins paskelbimą", + "status.quote_noun": "Paminėjimas", + "status.quote_policy_change": "Keisti, kas gali cituoti", + "status.quote_post_author": "Paminėjo įrašą iš @{name}", + "status.quote_private": "Privačių įrašų negalima cituoti", + "status.quotes.empty": "Šio įrašo dar niekas nepaminėjo. Kai kas nors tai padarys, jie bus rodomi čia.", + "status.quotes.local_other_disclaimer": "Autoriaus atmesti įrašo paminėjimai nebus rodomi.", + "status.quotes.remote_other_disclaimer": "Čia bus rodoma tik paminėjimai iš {domain}. Autoriaus atmesti įrašo paminėjimai nebus rodomi.", "status.read_more": "Skaityti daugiau", "status.reblog": "Pakelti", + "status.reblog_or_quote": "Paryškinti arba cituoti", "status.reblogged_by": "{name} pakėlė", "status.reblogs.empty": "Šio įrašo dar niekas nepakėlė. Kai kas nors tai padarys, jie bus rodomi čia.", "status.redraft": "Ištrinti ir parengti iš naujo", "status.remove_bookmark": "Pašalinti žymę", "status.remove_favourite": "Šalinti iš mėgstamų", + "status.remove_quote": "Pašalinti", "status.replied_in_thread": "Atsakyta gijoje", "status.replied_to": "Atsakyta į {name}", "status.reply": "Atsakyti", "status.replyAll": "Atsakyti į giją", "status.report": "Pranešti apie @{name}", + "status.request_quote": "Citavimo sutikimas", + "status.revoke_quote": "Pašalinti mano įrašo citavimą iš @{name}’s įrašo", "status.sensitive_warning": "Jautrus turinys", "status.share": "Bendrinti", "status.show_less_all": "Rodyti mažiau visiems", @@ -850,6 +975,7 @@ "upload_button.label": "Pridėti vaizdų, vaizdo įrašą arba garso failą", "upload_error.limit": "Viršyta failo įkėlimo riba.", "upload_error.poll": "Failų įkėlimas neleidžiamas su apklausomis.", + "upload_error.quote": "Paminint įrašą bylos įkėlimas negalimas.", "upload_form.drag_and_drop.instructions": "Kad paimtum medijos priedą, paspausk tarpo arba įvedimo klavišą. Tempant naudok rodyklių klavišus, kad perkeltum medijos priedą bet kuria kryptimi. Dar kartą paspausk tarpo arba įvedimo klavišą, kad nuleistum medijos priedą naujoje vietoje, arba paspausk grįžimo klavišą, kad atšauktum.", "upload_form.drag_and_drop.on_drag_cancel": "Nutempimas buvo atšauktas. Medijos priedas {item} buvo nuleistas.", "upload_form.drag_and_drop.on_drag_end": "Medijos priedas {item} buvo nuleistas.", @@ -868,5 +994,15 @@ "video.mute": "Išjungti garsą", "video.pause": "Pristabdyti", "video.play": "Leisti", - "video.skip_backward": "Praleisti atgal" + "video.skip_backward": "Praleisti atgal", + "visibility_modal.direct_quote_warning.text": "Jei išsaugosite dabartinius nustatymus, įterpta citata bus konvertuota į nuorodą.", + "visibility_modal.direct_quote_warning.title": "Cituojami įrašai negali būti įterpiami į privačius paminėjimus", + "visibility_modal.helper.direct_quoting": "Privatūs paminėjimai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.", + "visibility_modal.helper.privacy_private_self_quote": "Privačių įrašų paminėjimai negali būti skelbiami viešai.", + "visibility_modal.helper.private_quoting": "Tik sekėjams skirti įrašai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.", + "visibility_modal.helper.unlisted_quoting": "Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto.", + "visibility_modal.quote_followers": "Tik sekėjai", + "visibility_modal.quote_label": "Kas gali cituoti", + "visibility_modal.quote_nobody": "Tik aš", + "visibility_modal.quote_public": "Visi" } diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index c2b298e032..75b2f7b63a 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -18,7 +18,7 @@ "account.badges.bot": "機器lâng", "account.badges.group": "群組", "account.block": "封鎖 @{name}", - "account.block_domain": "封鎖網域 {domain}", + "account.block_domain": "封鎖域名 {domain}", "account.block_short": "封鎖", "account.blocked": "Hőng封鎖", "account.blocking": "Teh封鎖", @@ -26,7 +26,7 @@ "account.copy": "Khóo-pih個人資料ê連結", "account.direct": "私人提起 @{name}", "account.disable_notifications": "停止佇 {name} PO文ê時通知我", - "account.domain_blocking": "Teh封鎖ê網域", + "account.domain_blocking": "Teh封鎖ê域名", "account.edit_profile": "編輯個人資料", "account.edit_profile_short": "編輯", "account.enable_notifications": "佇 {name} PO文ê時通知我", @@ -344,7 +344,7 @@ "empty_column.community": "本站時間線是空ê。緊來公開PO文oh!", "empty_column.direct": "Lí iáu無任何ê私人訊息。Nā是lí送á是收著私人訊息,ē佇tsia顯示。.", "empty_column.disabled_feed": "Tsit ê feed已經hōo lí ê服侍器ê管理員停用。", - "empty_column.domain_blocks": "Iáu無封鎖任何網域。", + "empty_column.domain_blocks": "Iáu無封鎖任何域名。", "empty_column.explore_statuses": "目前iáu無有流行ê趨勢,請sió等tsi̍t-ē,koh確認。", "empty_column.favourited_statuses": "Lí iáu無加添任何收藏 ê PO文。Nā是lí加收藏,伊ē佇tsia顯示。", "empty_column.favourites": "Iáu無lâng收藏tsit篇PO文。Nā是有lâng收藏,ē佇tsia顯示。", @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "顯示/隱藏內容警告後壁ê PO文", "keyboard_shortcuts.toggle_sensitivity": "顯示/tshàng媒體", "keyboard_shortcuts.toot": "PO新PO文", + "keyboard_shortcuts.top": "Súa kàu列單ê頭", "keyboard_shortcuts.translate": "kā PO文翻譯", "keyboard_shortcuts.unfocus": "離開輸入框仔/tshiau-tshuē格仔", "keyboard_shortcuts.up": "佇列單內kā suá khah面頂", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index e3497da2ff..85a223cbfe 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Beschrijf dit voor blinden en slechtzienden…", "alt_text_modal.done": "Klaar", "announcement.announcement": "Mededeling", + "annual_report.announcement.action_build": "Bouw mijn Wrapstodon", + "annual_report.announcement.action_view": "Bekijk mijn Wrapstodon", + "annual_report.announcement.description": "Ontdek meer over jouw engagement op Mastodon over het afgelopen jaar.", + "annual_report.announcement.title": "Wrapstodon {year} is gearriveerd", "annual_report.summary.archetype.booster": "De cool-hunter", "annual_report.summary.archetype.lurker": "De lurker", "annual_report.summary.archetype.oracle": "Het orakel", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "Helemaal leeg! Er is hier niets. Wanneer je nieuwe meldingen ontvangt, verschijnen deze hier volgens jouw instellingen.", "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.", "empty_column.public": "Er is hier helemaal niks! Plaatst een openbaar bericht of volg mensen van andere servers om het te vullen", + "error.no_hashtag_feed_access": "Registreren of inloggen om de hashtag te bekijken of te volgen.", "error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.", "error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.", "error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen", "keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen", "keyboard_shortcuts.toot": "Nieuw bericht schrijven", + "keyboard_shortcuts.top": "Naar het begin van de lijst verplaatsen", "keyboard_shortcuts.translate": "om een bericht te vertalen", "keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen", "keyboard_shortcuts.up": "Naar boven in de lijst bewegen", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 17c526140a..6180bd4c65 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Skriv ei skildring for menneske med synsnedsetjingar…", "alt_text_modal.done": "Ferdig", "announcement.announcement": "Kunngjering", + "annual_report.announcement.action_build": "Set saman min Årstodon", + "annual_report.announcement.action_view": "Sjå min Årstodon", + "annual_report.announcement.description": "Sjå meir om kva du har gjort på Mastodon siste året.", + "annual_report.announcement.title": "Årstodon {year} er her", "annual_report.summary.archetype.booster": "Den som jaktar på noko kult", "annual_report.summary.archetype.lurker": "Den som heng på hjørnet", "annual_report.summary.archetype.oracle": "Orakelet", @@ -332,8 +336,8 @@ "emoji_button.search_results": "Søkeresultat", "emoji_button.symbols": "Symbol", "emoji_button.travel": "Reise & stader", - "empty_column.account_featured.me": "Du har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?", - "empty_column.account_featured.other": "{acct} har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?", + "empty_column.account_featured.me": "Du har ikkje valt ut noko enno. Visste du at du kan velja ut emneknaggar du bruker mykje, og til og med venekontoar på profilen din?", + "empty_column.account_featured.other": "{acct} har ikkje valt ut noko enno. Visste du at du kan velja ut emneknaggar du bruker mykje, og til og med venekontoar på profilen din?", "empty_column.account_featured_other.unknown": "Denne kontoen har ikkje valt ut noko enno.", "empty_column.account_hides_collections": "Denne brukaren har valt å ikkje gjere denne informasjonen tilgjengeleg", "empty_column.account_suspended": "Kontoen er utestengd", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "Ferdig! Her er det ingenting. Når du får nye varsel, kjem dei opp her slik du har valt.", "empty_column.notifications": "Du har ingen varsel enno. Kommuniser med andre for å starte samtalen.", "empty_column.public": "Det er ingenting her! Skriv noko offentleg, eller fylg brukarar frå andre tenarar manuelt for å få meir her", + "error.no_hashtag_feed_access": "Bli medlem eller logg inn for å sjå og fylgja denne emneknaggen.", "error.unexpected_crash.explanation": "På grunn av eit nettlesarkompatibilitetsproblem eller ein feil i koden vår, kunne ikkje denne sida bli vist slik den skal.", "error.unexpected_crash.explanation_addons": "Denne sida kunne ikkje visast som den skulle. Feilen kjem truleg frå ei nettleserutviding eller frå automatiske omsetjingsverktøy.", "error.unexpected_crash.next_steps": "Prøv å lasta inn sida på nytt. Hjelper ikkje dette kan du framleis nytta Mastodon i ein annan nettlesar eller app.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/gøym tekst bak innhaldsvarsel", "keyboard_shortcuts.toggle_sensitivity": "Vis/gøym media", "keyboard_shortcuts.toot": "Lag nytt tut", + "keyboard_shortcuts.top": "Flytt til toppen av lista", "keyboard_shortcuts.translate": "å omsetje eit innlegg", "keyboard_shortcuts.unfocus": "for å fokusere vekk skrive-/søkefeltet", "keyboard_shortcuts.up": "Flytt opp på lista", @@ -595,9 +601,9 @@ "navigation_bar.privacy_and_reach": "Personvern og rekkjevidd", "navigation_bar.search": "Søk", "navigation_bar.search_trends": "Søk / Populært", - "navigation_panel.collapse_followed_tags": "Gøym menyen over merkelappar du fylgjer", + "navigation_panel.collapse_followed_tags": "Gøym menyen over emneknaggar du fylgjer", "navigation_panel.collapse_lists": "Gøym listemenyen", - "navigation_panel.expand_followed_tags": "Utvid menyen over merkelappar du fylgjer", + "navigation_panel.expand_followed_tags": "Utvid menyen over emneknaggar du fylgjer", "navigation_panel.expand_lists": "Utvid listemenyen", "not_signed_in_indicator.not_signed_in": "Du må logga inn for å få tilgang til denne ressursen.", "notification.admin.report": "{name} rapporterte {target}", @@ -758,7 +764,7 @@ "privacy.quote.anyone": "{visibility}, alle kan sitera", "privacy.quote.disabled": "{visibility}, ingen kan sitera", "privacy.quote.limited": "{visibility}, avgrensa sitat", - "privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller merkelappar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.", + "privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller emneknaggar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.", "privacy.unlisted.long": "Gøymt frå søkjeresultata på Mastodon, og frå populære og offentlege tidsliner", "privacy.unlisted.short": "Stille offentleg", "privacy_policy.last_updated": "Sist oppdatert {date}", @@ -861,7 +867,7 @@ "search_results.all": "Alt", "search_results.hashtags": "Emneknaggar", "search_results.no_results": "Ingen resultat.", - "search_results.no_search_yet": "Prøv å søkja etter innlegg, profilar eller merkelappar.", + "search_results.no_search_yet": "Prøv å søkja etter innlegg, profilar eller emneknaggar.", "search_results.see_all": "Sjå alle", "search_results.statuses": "Tut", "search_results.title": "Søk etter \"{q}\"", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 2bad3ca2b9..158da029d7 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -55,11 +55,11 @@ "account.follows.empty": "Nada aqui.", "account.follows_you": "Segue você", "account.go_to_profile": "Ir ao perfil", - "account.hide_reblogs": "Ocultar boosts de @{name}", + "account.hide_reblogs": "Ocultar impulsionamentos de @{name}", "account.in_memoriam": "Em memória.", "account.joined_short": "Entrou", "account.languages": "Mudar idiomas inscritos", - "account.link_verified_on": "link verificado em {date}", + "account.link_verified_on": "A propriedade deste link foi verificada em {date}", "account.locked_info": "Trancado. Seguir requer aprovação manual do perfil.", "account.media": "Mídia", "account.mention": "Mencionar @{name}", @@ -79,7 +79,7 @@ "account.requested_follow": "{name} quer te seguir", "account.requests_to_follow_you": "Pediu para seguir você", "account.share": "Compartilhar perfil de @{name}", - "account.show_reblogs": "Mostrar boosts de @{name}", + "account.show_reblogs": "Mostrar impulsionamentos de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear domínio {domain}", @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descreva isto para pessoas com deficiências visuais…", "alt_text_modal.done": "Feito", "announcement.announcement": "Comunicados", + "annual_report.announcement.action_build": "Gerar meu Wrapstodon", + "annual_report.announcement.action_view": "Ver meu Wrapstodon", + "annual_report.announcement.description": "Descubra mais sobre seu engajamento no Mastodon ao longo do último ano.", + "annual_report.announcement.title": "Chegou o Wrapstodon de {year}", "annual_report.summary.archetype.booster": "Caçador legal", "annual_report.summary.archetype.lurker": "O espreitador", "annual_report.summary.archetype.oracle": "O oráculo", @@ -142,9 +146,9 @@ "block_modal.they_will_know": "Poderá ver que você bloqueou.", "block_modal.title": "Bloquear usuário?", "block_modal.you_wont_see_mentions": "Você não verá publicações que mencionem o usuário.", - "boost_modal.combo": "Pressione {combo} para pular isso na próxima vez", + "boost_modal.combo": "Pressione {combo} para pular isto na próxima vez", "boost_modal.reblog": "Impulsionar a publicação?", - "boost_modal.undo_reblog": "Retirar o impulsionamento do post?", + "boost_modal.undo_reblog": "Retirar o impulso do post?", "bundle_column_error.copy_stacktrace": "Copiar relatório do erro", "bundle_column_error.error.body": "A página solicitada não pôde ser renderizada. Pode ser devido a um erro no nosso código, ou um problema de compatibilidade do seu navegador.", "bundle_column_error.error.title": "Ah, não!", @@ -259,7 +263,7 @@ "confirmations.quiet_post_quote_info.message": "Ao citar uma publicação pública silenciosa, sua postagem será oculta das linhas de tempo em tendência.", "confirmations.quiet_post_quote_info.title": "Citando publicações públicas silenciadas", "confirmations.redraft.confirm": "Excluir e rascunhar", - "confirmations.redraft.message": "Você tem certeza de que quer apagar essa postagem e rascunhá-la? Favoritos e impulsos serão perdidos, e respostas à postagem original ficarão órfãs.", + "confirmations.redraft.message": "Você tem certeza de que quer apagar essa publicação e rascunhá-la? Favoritos e impulsos serão perdidos, e respostas à postagem original ficarão órfãs.", "confirmations.redraft.title": "Excluir e rascunhar publicação?", "confirmations.remove_from_followers.confirm": "Remover seguidor", "confirmations.remove_from_followers.message": "{name} vai parar de te seguir. Tem certeza de que deseja continuar?", @@ -293,7 +297,7 @@ "dismissable_banner.dismiss": "Dispensar", "dismissable_banner.public_timeline": "Estas são as publicações mais recentes das pessoas no fediverso que as pessoas do {domain} seguem.", "domain_block_modal.block": "Bloquear servidor", - "domain_block_modal.block_account_instead": "Bloquear @{name}", + "domain_block_modal.block_account_instead": "Bloquear @{name} em vez disso", "domain_block_modal.they_can_interact_with_old_posts": "Pessoas deste servidor podem interagir com suas publicações antigas.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode lhe seguir.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", @@ -452,7 +456,7 @@ "hints.profiles.see_more_follows": "Ver mais seguidos no {domain}", "hints.profiles.see_more_posts": "Ver mais publicações em {domain}", "home.column_settings.show_quotes": "Mostrar citações", - "home.column_settings.show_reblogs": "Mostrar boosts", + "home.column_settings.show_reblogs": "Mostrar impulsos", "home.column_settings.show_replies": "Mostrar respostas", "home.hide_announcements": "Ocultar comunicados", "home.pending_critical_update.body": "Por favor, atualize o seu servidor Mastodon o mais rápido possível!", @@ -484,7 +488,7 @@ "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "keyboard_shortcuts.back": "voltar", "keyboard_shortcuts.blocked": "abrir usuários bloqueados", - "keyboard_shortcuts.boost": "dar boost", + "keyboard_shortcuts.boost": "Impulsionar a publicação", "keyboard_shortcuts.column": "focar na coluna", "keyboard_shortcuts.compose": "focar no compositor", "keyboard_shortcuts.description": "Descrição", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "expandir/ocultar aviso de conteúdo", "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar mídia", "keyboard_shortcuts.toot": "compor novo toot", + "keyboard_shortcuts.top": "Mover para o topo da lista", "keyboard_shortcuts.translate": "Para traduzir um post", "keyboard_shortcuts.unfocus": "desfocar de tudo", "keyboard_shortcuts.up": "mover para cima", @@ -608,7 +613,7 @@ "notification.admin.report_statuses_other": "{name} denunciou {target}", "notification.admin.sign_up": "{name} se inscreveu", "notification.admin.sign_up.name_and_others": "{name} e {count, plural, one {# other} other {# outros}}", - "notification.annual_report.message": "O #Wrapstodon do seu {year} está esperando! Desvende seus destaques do ano e momentos memoráveis no Mastodon!", + "notification.annual_report.message": "O seu #Wrapstodon de {year} está esperando! Desvende seus destaques do ano e momentos memoráveis no Mastodon!", "notification.annual_report.view": "Ver #Wrapstodon", "notification.favourite": "{name} favoritou sua publicação", "notification.favourite.name_and_others_with_link": "{name} e {count, plural, one {# outro} other {# others}} favoritaram a publicação", @@ -637,7 +642,7 @@ "notification.own_poll": "Sua enquete terminou", "notification.poll": "Uma enquete que você votou terminou", "notification.quoted_update": "{name} editou uma pulicação que você citou", - "notification.reblog": "{name} deu boost no teu toot", + "notification.reblog": "{name} impulsionou sua publicação", "notification.reblog.name_and_others_with_link": "{name} e {count, plural, one {# outra} other {# outras}} impulsionaram a publicação", "notification.relationships_severance_event": "Conexões perdidas com {name}", "notification.relationships_severance_event.account_suspension": "Um administrador de {from} suspendeu {target}, o que significa que você não pode mais receber atualizações deles ou interagir com eles.", @@ -681,7 +686,7 @@ "notifications.column_settings.poll": "Enquetes:", "notifications.column_settings.push": "Notificações push", "notifications.column_settings.quote": "Citações:", - "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.reblog": "Impulsos:", "notifications.column_settings.show": "Mostrar na coluna", "notifications.column_settings.sound": "Tocar som", "notifications.column_settings.status": "Novos toots:", @@ -689,7 +694,7 @@ "notifications.column_settings.unread_notifications.highlight": "Destacar notificações não lidas", "notifications.column_settings.update": "Editar:", "notifications.filter.all": "Tudo", - "notifications.filter.boosts": "Boosts", + "notifications.filter.boosts": "Impulsionamentos", "notifications.filter.favourites": "Favoritos", "notifications.filter.follows": "Seguidores", "notifications.filter.mentions": "Menções", @@ -879,12 +884,12 @@ "status.admin_account": "Abrir interface de moderação para @{name}", "status.admin_domain": "Abrir interface de moderação para {domain}", "status.admin_status": "Abrir este toot na interface de moderação", - "status.all_disabled": "Acelerações e citações estão desabilitados", + "status.all_disabled": "Impulsos e citações estão desabilitados", "status.block": "Bloquear @{name}", "status.bookmark": "Salvar", - "status.cancel_reblog_private": "Desfazer boost", + "status.cancel_reblog_private": "Desfazer impulso", "status.cannot_quote": "Você não tem permissão para citar esta publicação", - "status.cannot_reblog": "Este toot não pode receber boost", + "status.cannot_reblog": "Esta publicação não pode ser impulsionada", "status.contains_quote": "Contém citação", "status.context.loading": "Carregando mais respostas", "status.context.loading_error": "Não foi possível carregar novas respostas", @@ -903,7 +908,7 @@ "status.edited": "Última edição em {date}", "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.favourite": "Favoritar", "status.favourites_count": "{count, plural, one {{counter} favorito} other {{counter} favoritos}}", "status.filter": "Filtrar esta publicação", "status.history.created": "{name} criou {date}", @@ -941,12 +946,12 @@ "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} citação} other {{counter} citações}}", "status.read_more": "Ler mais", - "status.reblog": "Dar boost", - "status.reblog_or_quote": "Dar boost ou citar", + "status.reblog": "Impulsionar", + "status.reblog_or_quote": "Impulsionar ou citar", "status.reblog_private": "Compartilhar novamente com seus seguidores", - "status.reblogged_by": "{name} deu boost", - "status.reblogs.empty": "Nada aqui. Quando alguém der boost, o usuário aparecerá aqui.", - "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", + "status.reblogged_by": "{name} impulsionou", + "status.reblogs.empty": "Ninguém impulsionou esta publicação ainda. Quando alguém o fizer, o usuário aparecerá aqui.", + "status.reblogs_count": "{count, plural, one {{counter} impulso} other {{counter} impulsos}}", "status.redraft": "Excluir e rascunhar", "status.remove_bookmark": "Remover do Salvos", "status.remove_favourite": "Remover dos favoritos", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 101a4bbd78..671930e673 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descreve isto para pessoas com problemas de visão…", "alt_text_modal.done": "Concluído", "announcement.announcement": "Mensagem de manutenção", + "annual_report.announcement.action_build": "Criar o meu Wrapstodon", + "annual_report.announcement.action_view": "Ver o meu Wrapstodon", + "annual_report.announcement.description": "Descobre mais sobre o teu envolvimento com o Mastodon durante o último ano.", + "annual_report.announcement.title": "Chegou o Wrapstodon {year}", "annual_report.summary.archetype.booster": "O caçador de tendências", "annual_report.summary.archetype.lurker": "O espreitador", "annual_report.summary.archetype.oracle": "O oráculo", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "mostrar / esconder texto atrás do aviso de conteúdo", "keyboard_shortcuts.toggle_sensitivity": "mostrar / ocultar multimédia", "keyboard_shortcuts.toot": "criar uma nova publicação", + "keyboard_shortcuts.top": "Mover para o topo da lista", "keyboard_shortcuts.translate": "traduzir uma publicação", "keyboard_shortcuts.unfocus": "remover o foco da área de texto / pesquisa", "keyboard_shortcuts.up": "mover para cima na lista", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index bb43bbdbbc..af268b1e49 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -512,6 +512,7 @@ "keyboard_shortcuts.toggle_hidden": "Për shfaqje/fshehje teksti pas CW", "keyboard_shortcuts.toggle_sensitivity": "Për shfaqje/fshehje mediash", "keyboard_shortcuts.toot": "Për të filluar një mesazh të ri", + "keyboard_shortcuts.top": "Shpjere në krye të listës", "keyboard_shortcuts.translate": "për të përkthyer një postim", "keyboard_shortcuts.unfocus": "Për heqjen e fokusit nga fusha e hartimit të mesazheve apo kërkimeve", "keyboard_shortcuts.up": "Për ngjitje sipër nëpër listë", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 49ea6ea173..368498b1ce 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Visa/gömma text bakom CW", "keyboard_shortcuts.toggle_sensitivity": "Visa/gömma media", "keyboard_shortcuts.toot": "Starta nytt inlägg", + "keyboard_shortcuts.top": "Flytta till början av listan", "keyboard_shortcuts.translate": "för att översätta ett inlägg", "keyboard_shortcuts.unfocus": "Avfokusera skrivfält/sökfält", "keyboard_shortcuts.up": "Flytta uppåt i listan", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 2f56042462..1c36079c7f 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -4,10 +4,10 @@ "about.default_locale": "ante ala", "about.disclaimer": "ilo Mastodon la jan ale li lawa e ona li pana e pona tawa ona. kulupu esun Mastodon gGmbH li lawa e nimi ona.", "about.domain_blocks.no_reason_available": "mi sona ala e tan", - "about.domain_blocks.preamble": "ilo Masoton li ken e ni: sina lukin e toki jan pi ma ilo mute. sina ken toki tawa ona lon kulupu ma. taso, ma ni li ken ala e ni tawa ma ni:", + "about.domain_blocks.preamble": "ilo Mastodon li ken e ni: sina lukin e toki jan pi ma ilo mute. sina ken toki tawa ona lon kulupu ma. taso, ma ni li ken ala e ni tawa ma ni:", "about.domain_blocks.silenced.explanation": "sina lukin ala e toki e jan tan ma ni. taso, sina wile la, sina ken ni.", "about.domain_blocks.silenced.title": "ken lukin lili ", - "about.domain_blocks.suspended.explanation": "sona ale pi ma ni li kama pali ala, li kama esun ala, li kama awen ala la sina ken ala toki tawa jan pi ma ni.", + "about.domain_blocks.suspended.explanation": "sona ale pi ma ni li kama pali ala, li kama esun ala, li kama awen ala la, sina ken ala toki tawa jan pi ma ni.", "about.domain_blocks.suspended.title": "weka", "about.language_label": "toki", "about.not_available": "lon kulupu ni la sina ken alasa ala e sona ni.", @@ -24,12 +24,12 @@ "account.blocking": "mi len e jan ni", "account.cancel_follow_request": "o kute ala", "account.copy": "o pali same e linja pi lipu jan", - "account.direct": "len la o mu e @{name}", + "account.direct": "o mu len e @{name}", "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.enable_notifications": "@{name} li toki la o mu e 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", "account.familiar_followers_one": "{name1} li kute e jan ni", @@ -41,10 +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.follow_back_short": "o kute", + "account.follow_request": "o wile kute", + "account.follow_request_cancel": "o wile ala kute", + "account.follow_request_cancel_short": "o wile ala kute", "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}}", @@ -120,17 +120,17 @@ "annual_report.summary.followers.followers": "jan kute sina", "annual_report.summary.followers.total": "ale la {count}", "annual_report.summary.here_it_is": "toki lili la tenpo sike nanpa {year} li sama ni tawa sina:", - "annual_report.summary.highlighted_post.by_favourites": "toki pi pona suli", - "annual_report.summary.highlighted_post.by_reblogs": "toki pi pana suli", - "annual_report.summary.highlighted_post.by_replies": "toki li jo e toki kama pi nanpa wan", - "annual_report.summary.highlighted_post.possessive": "tan jan {name}", + "annual_report.summary.highlighted_post.by_favourites": "toki ni li pona suli tawa jan", + "annual_report.summary.highlighted_post.by_reblogs": "jan ante li pana suli e toki ni", + "annual_report.summary.highlighted_post.by_replies": "la jan ante li toki suli tan toki ni", + "annual_report.summary.highlighted_post.possessive": "{name}", "annual_report.summary.most_used_app.most_used_app": "ilo pi kepeken suli", "annual_report.summary.most_used_hashtag.most_used_hashtag": "kulupu toki pi kepeken suli", "annual_report.summary.most_used_hashtag.none": "ala", "annual_report.summary.new_posts.new_posts": "toki suli sin", "annual_report.summary.percentile.text": "ni la sina nanpa sewipi jan ale lon {domain}.", "annual_report.summary.percentile.we_wont_tell_bernie": "sona ni li len tawa ale.", - "annual_report.summary.thanks": "sina jan pi kulupu Mastodon la sina pona a!", + "annual_report.summary.thanks": "sina lon kulupu Mastodon la sina pona a!", "attachments_list.unprocessed": "(nasin open)", "audio.hide": "o len e kalama", "block_modal.remote_users_caveat": "mi pana e wile sina tawa ma {domain}. taso, o sona: ma li ken kepeken nasin len ante la pakala li ken lon. toki pi lukin ale la jan pi ma ala li ken lukin.", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index b7b4a08cdb..c674509368 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Bunu görme bozukluğu yaşayan kişiler için betimleyin…", "alt_text_modal.done": "Tamamlandı", "announcement.announcement": "Duyuru", + "annual_report.announcement.action_build": "Wrapstodon'umu Oluştur", + "annual_report.announcement.action_view": "Wrapstodon'umu Görüntüle", + "annual_report.announcement.description": "Mastodon'daki geçen yıldaki etkileşimleriniz hakkında daha fazlasını öğrenin.", + "annual_report.announcement.title": "{year} Wrapstodon'u yayında", "annual_report.summary.archetype.booster": "Trend takipçisi", "annual_report.summary.archetype.lurker": "Gizli meraklı", "annual_report.summary.archetype.oracle": "Kahin", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "Hepsi tamam! Burada yeni bir şey yok. Yeni bildirim aldığınızda, ayarlarınıza göre burada görüntülenecekler.", "empty_column.notifications": "Henüz bildiriminiz yok. Sohbete başlamak için başkalarıyla etkileşim kurun.", "empty_column.public": "Burada hiçbir şey yok! Herkese açık bir şeyler yazın veya burayı doldurmak için diğer sunuculardaki kullanıcıları takip edin", + "error.no_hashtag_feed_access": "Bu etiketi görüntülemek ve takip etmek için katılın veya giriş yapın.", "error.unexpected_crash.explanation": "Bizim kodumuzdaki bir hatadan ya da tarayıcı uyumluluk sorunundan dolayı, bu sayfa düzgün görüntülenemedi.", "error.unexpected_crash.explanation_addons": "Bu sayfa doğru görüntülenemedi. Bu hata büyük olasılıkla bir tarayıcı eklentisinden veya otomatik çeviri araçlarından kaynaklanır.", "error.unexpected_crash.next_steps": "Sayfayı yenilemeyi deneyin. Eğer bu yardımcı olmazsa, Mastodon'u farklı bir tarayıcı ya da yerel uygulama üzerinden kullanabilirsiniz.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "CW'den önceki yazıyı göstermek/gizlemek için", "keyboard_shortcuts.toggle_sensitivity": "Medyayı göstermek/gizlemek için", "keyboard_shortcuts.toot": "Yeni bir gönderi başlat", + "keyboard_shortcuts.top": "Listenin üstüne taşı", "keyboard_shortcuts.translate": "bir gönderiyi çevirmek için", "keyboard_shortcuts.unfocus": "Aramada bir gönderiye odaklanmamak için", "keyboard_shortcuts.up": "Listede yukarıya çıkmak için", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index d6437549fe..5f4887267b 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Mô tả cho người khiếm thị…", "alt_text_modal.done": "Xong", "announcement.announcement": "Có gì mới?", + "annual_report.announcement.action_build": "Tạo Wrapstodon của tôi", + "annual_report.announcement.action_view": "Xem Wrapstodon của tôi", + "annual_report.announcement.description": "Tìm hiểu thêm về mức độ tương tác của bạn trên Mastodon trong năm qua.", + "annual_report.announcement.title": "Wrapstodon {year} đã đến", "annual_report.summary.archetype.booster": "Hiệp sĩ ngầu", "annual_report.summary.archetype.lurker": "Kẻ rình mò", "annual_report.summary.archetype.oracle": "Nhà tiên tri", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "ẩn/hiện nội dung ẩn", "keyboard_shortcuts.toggle_sensitivity": "ẩn/hiện ảnh hoặc video", "keyboard_shortcuts.toot": "soạn tút mới", + "keyboard_shortcuts.top": "Chuyển đến đầu danh sách", "keyboard_shortcuts.translate": "dịch tút", "keyboard_shortcuts.unfocus": "đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm", "keyboard_shortcuts.up": "di chuyển lên trên danh sách", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 4986e61e31..1f65a846ad 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "请为视力障碍人士描述此内容…", "alt_text_modal.done": "完成", "announcement.announcement": "公告", + "annual_report.announcement.action_build": "构建我的 Wrapstodon 年度回顾", + "annual_report.announcement.action_view": "查看我的 Wrapstodon 年度回顾", + "annual_report.announcement.description": "探索更多关于您过去一年在 Mastodon 上的互动情况。", + "annual_report.announcement.title": "Wrapstodon {year} 年度回顾来啦", "annual_report.summary.archetype.booster": "潮流捕手", "annual_report.summary.archetype.lurker": "吃瓜群众", "annual_report.summary.archetype.oracle": "未卜先知", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "显示或隐藏被折叠的正文", "keyboard_shortcuts.toggle_sensitivity": "显示/隐藏媒体", "keyboard_shortcuts.toot": "发送新嘟文", + "keyboard_shortcuts.top": "移动到列表顶部", "keyboard_shortcuts.translate": "翻译嘟文", "keyboard_shortcuts.unfocus": "取消输入/搜索", "keyboard_shortcuts.up": "在列表中让光标上移", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index c198ff8e35..27a057c28b 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "替視覺障礙人士描述...", "alt_text_modal.done": "完成", "announcement.announcement": "公告", + "annual_report.announcement.action_build": "建立我的 Mastodon 年度回顧 (Wrapstodon)", + "annual_report.announcement.action_view": "檢視我的 Mastodon 年度回顧 (Wrapstodon)", + "annual_report.announcement.description": "探索更多關於您過去一年於 Mastodon 上的互動情況。", + "annual_report.announcement.title": "您的 Mastodon 年度回顧 {year} 已抵達", "annual_report.summary.archetype.booster": "酷炫獵人", "annual_report.summary.archetype.lurker": "潛水高手", "annual_report.summary.archetype.oracle": "先知", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "顯示或隱藏於內容警告之後的嘟文", "keyboard_shortcuts.toggle_sensitivity": "顯示或隱藏媒體", "keyboard_shortcuts.toot": "發個新嘟文", + "keyboard_shortcuts.top": "移至列表最上方", "keyboard_shortcuts.translate": "翻譯嘟文", "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框", "keyboard_shortcuts.up": "向上移動", diff --git a/app/javascript/mastodon/models/annual_report.ts b/app/javascript/mastodon/models/annual_report.ts index c0a101e6c8..a2c0c51786 100644 --- a/app/javascript/mastodon/models/annual_report.ts +++ b/app/javascript/mastodon/models/annual_report.ts @@ -37,8 +37,30 @@ interface AnnualReportV1 { archetype: Archetype; } -export interface AnnualReport { - year: number; - schema_version: number; - data: AnnualReportV1; +interface AnnualReportV2 { + archetype: Archetype; + time_series: TimeSeriesMonth[]; + top_hashtags: NameAndCount[]; + top_statuses: TopStatuses; + most_used_apps: NameAndCount[]; + type_distribution: { + total: number; + reblogs: number; + replies: number; + standalone: number; + }; } + +export type AnnualReport = { + year: number; +} & ( + | { + schema_version: 1; + data: AnnualReportV1; + } + | { + schema_version: 2; + data: AnnualReportV2; + share_url: string | null; + } +); diff --git a/app/javascript/mastodon/models/custom_emoji.ts b/app/javascript/mastodon/models/custom_emoji.ts index 5297dcd470..19ca951a5c 100644 --- a/app/javascript/mastodon/models/custom_emoji.ts +++ b/app/javascript/mastodon/models/custom_emoji.ts @@ -11,6 +11,7 @@ export const CustomEmojiFactory = ImmutableRecord({ static_url: '', url: '', category: '', + featured: false, visible_in_picker: false, }); diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts index 19ecbbfff4..7343f5e164 100644 --- a/app/javascript/mastodon/reducers/index.ts +++ b/app/javascript/mastodon/reducers/index.ts @@ -33,6 +33,7 @@ import { relationshipsReducer } from './relationships'; import { searchReducer } from './search'; import server from './server'; import settings from './settings'; +import { sliceReducers } from './slices'; import status_lists from './status_lists'; import statuses from './statuses'; import { suggestionsReducer } from './suggestions'; @@ -80,6 +81,7 @@ const reducers = { notificationPolicy: notificationPolicyReducer, notificationRequests: notificationRequestsReducer, navigation: navigationReducer, + ...sliceReducers, }; // We want the root state to be an ImmutableRecord, which is an object with a defined list of keys, diff --git a/app/javascript/mastodon/reducers/slices/annual_report.ts b/app/javascript/mastodon/reducers/slices/annual_report.ts new file mode 100644 index 0000000000..db1c064e71 --- /dev/null +++ b/app/javascript/mastodon/reducers/slices/annual_report.ts @@ -0,0 +1,118 @@ +import { createSlice } from '@reduxjs/toolkit'; + +import { insertIntoTimeline } from '@/mastodon/actions/timelines'; +import type { ApiAnnualReportState } from '@/mastodon/api/annual_report'; +import { + apiGetAnnualReport, + apiGetAnnualReportState, + apiRequestGenerateAnnualReport, +} from '@/mastodon/api/annual_report'; +import type { AnnualReport } from '@/mastodon/models/annual_report'; + +import { + createAppSelector, + createAppThunk, + createDataLoadingThunk, +} from '../../store/typed_functions'; + +export const TIMELINE_WRAPSTODON = 'inline-wrapstodon'; + +interface AnnualReportState { + state?: ApiAnnualReportState; + report?: AnnualReport; +} + +const annualReportSlice = createSlice({ + name: 'annualReport', + initialState: {} as AnnualReportState, + reducers: {}, + extraReducers(builder) { + builder + .addCase(fetchReportState.fulfilled, (state, action) => { + state.state = action.payload; + }) + .addCase(generateReport.pending, (state) => { + state.state = 'generating'; + }) + .addCase(getReport.fulfilled, (state, action) => { + if (action.payload) { + state.report = action.payload; + } + }); + }, +}); + +export const annualReport = annualReportSlice.reducer; + +export const selectWrapstodonYear = createAppSelector( + [(state) => state.server.getIn(['server', 'wrapstodon'])], + (year: unknown) => (typeof year === 'number' && year > 2000 ? year : null), +); + +// This kicks everything off, and is called after fetching the server info. +export const checkAnnualReport = createAppThunk( + `${annualReportSlice.name}/checkAnnualReport`, + async (_arg: unknown, { dispatch, getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + return; + } + const state = await dispatch(fetchReportState()); + if ( + state.meta.requestStatus === 'fulfilled' && + state.payload !== 'ineligible' + ) { + dispatch(insertIntoTimeline('home', TIMELINE_WRAPSTODON, 1)); + } + }, +); + +const fetchReportState = createDataLoadingThunk( + `${annualReportSlice.name}/fetchReportState`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReportState(year); + }, + ({ state, refresh }, { dispatch }) => { + if (state === 'generating' && refresh) { + window.setTimeout(() => { + void dispatch(fetchReportState()); + }, 1_000 * refresh.retry); + } else if (state === 'available') { + void dispatch(getReport()); + } + + return state; + }, + { useLoadingBar: false }, +); + +// Triggers the generation of the annual report. +export const generateReport = createDataLoadingThunk( + `${annualReportSlice.name}/generateReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiRequestGenerateAnnualReport(year); + }, + (_arg: unknown, { dispatch }) => { + void dispatch(fetchReportState()); + }, +); + +export const getReport = createDataLoadingThunk( + `${annualReportSlice.name}/getReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReport(year); + }, + (data) => data.annual_reports[0], +); diff --git a/app/javascript/mastodon/reducers/slices/index.ts b/app/javascript/mastodon/reducers/slices/index.ts new file mode 100644 index 0000000000..dfea395127 --- /dev/null +++ b/app/javascript/mastodon/reducers/slices/index.ts @@ -0,0 +1,5 @@ +import { annualReport } from './annual_report'; + +export const sliceReducers = { + annualReport, +}; diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index a334709db0..cf17c066c3 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -65,6 +65,10 @@ const statusTranslateUndo = (state, id) => { }); }; +const removeStatusStub = (state, id) => { + return state.getIn([id, 'id']) ? state.deleteIn([id, 'isLoading']) : state.delete(id); +} + /** @type {ImmutableMap} */ const initialState = ImmutableMap(); @@ -92,11 +96,10 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'isLoading'], true); case STATUS_FETCH_FAIL: { if (action.parentQuotePostId && action.error.status === 404) { - return state - .delete(action.id) + return removeStatusStub(state, action.id) .setIn([action.parentQuotePostId, 'quote', 'state'], 'deleted') } else { - return state.delete(action.id); + return removeStatusStub(state, action.id); } } case STATUS_IMPORT: diff --git a/app/javascript/mastodon/store/typed_functions.ts b/app/javascript/mastodon/store/typed_functions.ts index 5ceb05909f..79bca08a52 100644 --- a/app/javascript/mastodon/store/typed_functions.ts +++ b/app/javascript/mastodon/store/typed_functions.ts @@ -205,7 +205,7 @@ export function createDataLoadingThunk( thunkOptions?: AppThunkOptions, ): ReturnType>; -// Overload when the `onData` method returns nothing, then the mayload is the `onData` result +// Overload when the `onData` method returns nothing, then the payload is the `onData` result export function createDataLoadingThunk( name: string, loadData: LoadData, diff --git a/app/javascript/mastodon/utils/links.ts b/app/javascript/mastodon/utils/links.ts index 02b74bde4d..00821fa8d1 100644 --- a/app/javascript/mastodon/utils/links.ts +++ b/app/javascript/mastodon/utils/links.ts @@ -5,20 +5,26 @@ export function setupLinkListeners() { // We don't want to target links with data-confirm here, as those are handled already. on('click', 'a[data-method]:not([data-confirm])', handleMethodLink); + + // We also want to target buttons with data-confirm that are not inside forms. + on('click', ':not(form) button[data-confirm]:not([form])', handleConfirmLink); } function handleConfirmLink(event: MouseEvent) { - const anchor = event.currentTarget; - if (!(anchor instanceof HTMLAnchorElement)) { + const target = event.currentTarget; + if ( + !(target instanceof HTMLAnchorElement) && + !(target instanceof HTMLButtonElement) + ) { return; } - const message = anchor.dataset.confirm; + const message = target.dataset.confirm; if (!message || !window.confirm(message)) { event.preventDefault(); return; } - if (anchor.dataset.method) { + if (target.dataset.method) { handleMethodLink(event); } } diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 6298409d15..db584f67f1 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -1,5 +1,10 @@ @use 'variables' as *; +html { + color: var(--color-text-primary); + background: var(--color-bg-ambient); +} + html.has-modal { &, body { diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 8fab2111ed..12d69dfba8 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -18,11 +18,24 @@ class AnnualReport 'annual_report_' end + def self.current_campaign + return unless Mastodon::Feature.wrapstodon_enabled? + + datetime = Time.now.utc + datetime.year if datetime.month == 12 && (10..31).cover?(datetime.day) + end + def initialize(account, year) @account = account @year = year end + def eligible? + with_read_replica do + SOURCES.all? { |klass| klass.new(@account, @year).eligible? } + end + end + def generate return if GeneratedAnnualReport.exists?(account: @account, year: @year) @@ -30,7 +43,8 @@ class AnnualReport account: @account, year: @year, schema_version: SCHEMA, - data: data + data: data, + share_key: SecureRandom.hex(8) ) end diff --git a/app/lib/annual_report/source.rb b/app/lib/annual_report/source.rb index 7f48655369..2898c6a5a2 100644 --- a/app/lib/annual_report/source.rb +++ b/app/lib/annual_report/source.rb @@ -12,6 +12,10 @@ class AnnualReport::Source raise NotImplementedError end + def eligible? + true + end + protected def report_statuses diff --git a/app/lib/annual_report/top_hashtags.rb b/app/lib/annual_report/top_hashtags.rb index a775c29bac..f3a2160341 100644 --- a/app/lib/annual_report/top_hashtags.rb +++ b/app/lib/annual_report/top_hashtags.rb @@ -15,6 +15,10 @@ class AnnualReport::TopHashtags < AnnualReport::Source } end + def eligible? + report_statuses.joins(:tags).exists? + end + private def top_hashtags diff --git a/app/lib/annual_report/top_statuses.rb b/app/lib/annual_report/top_statuses.rb index f32bd09a15..4dcc31892b 100644 --- a/app/lib/annual_report/top_statuses.rb +++ b/app/lib/annual_report/top_statuses.rb @@ -11,6 +11,10 @@ class AnnualReport::TopStatuses < AnnualReport::Source } end + def eligible? + report_statuses.public_visibility.exists? + end + private def status_identifier(status) diff --git a/app/models/collection.rb b/app/models/collection.rb index 41f9ed0f02..308e517e26 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -38,10 +38,18 @@ class Collection < ApplicationRecord validate :tag_is_usable validate :items_do_not_exceed_limit + scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) } + def remote? !local? end + def items_for(account = nil) + result = collection_items.with_accounts + result = result.not_blocked_by(account) unless account.nil? + result + end + private def tag_is_usable diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 0ea50e6914..48a18592fd 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -33,6 +33,8 @@ class CollectionItem < ApplicationRecord validates :object_uri, presence: true, if: -> { account.nil? } scope :ordered, -> { order(position: :asc) } + scope :with_accounts, -> { includes(account: [:account_stat, :user]) } + scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) } def local_item_with_remote_account? local? && account&.remote? diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 30d6f13eda..d5d3ddbaac 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -17,7 +17,7 @@ class Conversation < ApplicationRecord has_many :statuses, dependent: nil - belongs_to :parent_status, class_name: 'Status', optional: true, inverse_of: :conversation + belongs_to :parent_status, class_name: 'Status', optional: true, inverse_of: :owned_conversation belongs_to :parent_account, class_name: 'Account', optional: true scope :local, -> { where(uri: nil) } diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 206bb80be4..e55cb194ee 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -69,6 +69,10 @@ class CustomEmoji < ApplicationRecord :emoji end + def featured? + category&.featured_emoji_id == id + end + def copy! copy = self.class.find_or_initialize_by(domain: nil, shortcode: shortcode) copy.image = image diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb index dfcc156080..cc7db33ece 100644 --- a/app/models/custom_emoji_category.rb +++ b/app/models/custom_emoji_category.rb @@ -4,14 +4,16 @@ # # Table name: custom_emoji_categories # -# id :bigint(8) not null, primary key -# name :string -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# name :string +# created_at :datetime not null +# updated_at :datetime not null +# featured_emoji_id :bigint(8) # class CustomEmojiCategory < ApplicationRecord has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category, dependent: nil + belongs_to :featured_emoji, class_name: 'CustomEmoji', optional: true, inverse_of: :category validates :name, presence: true, uniqueness: true diff --git a/app/models/generated_annual_report.rb b/app/models/generated_annual_report.rb index aba0712fe4..563dd9219c 100644 --- a/app/models/generated_annual_report.rb +++ b/app/models/generated_annual_report.rb @@ -5,13 +5,14 @@ # Table name: generated_annual_reports # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# year :integer not null # data :jsonb not null # schema_version :integer not null +# share_key :string # viewed_at :datetime +# year :integer not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class GeneratedAnnualReport < ApplicationRecord @@ -28,7 +29,12 @@ class GeneratedAnnualReport < ApplicationRecord end def account_ids - data['most_reblogged_accounts'].pluck('account_id') + data['commonly_interacted_with_accounts'].pluck('account_id') + case schema_version + when 1 + data['most_reblogged_accounts'].pluck('account_id') + data['commonly_interacted_with_accounts'].pluck('account_id') + when 2 + [] + end end def status_ids diff --git a/app/models/status.rb b/app/models/status.rb index fb0e96b134..570340c2df 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -161,6 +161,7 @@ class Status < ApplicationRecord around_create Mastodon::Snowflake::Callbacks after_create :set_poll_id + after_create :update_conversation # The `prepend: true` option below ensures this runs before # the `dependent: destroy` callbacks remove relevant records @@ -506,11 +507,16 @@ class Status < ApplicationRecord self.in_reply_to_account_id = carried_over_reply_to_account_id self.conversation_id = thread.conversation_id if conversation_id.nil? elsif conversation_id.nil? - conversation = build_owned_conversation - self.conversation = conversation + build_conversation end end + def update_conversation + return if reply? + + conversation.update!(parent_status: self, parent_account: account) if conversation && conversation.parent_status.nil? + end + def carried_over_reply_to_account_id if thread.account_id == account_id && thread.reply? thread.in_reply_to_account_id diff --git a/app/models/tag.rb b/app/models/tag.rb index f9eb6bfd33..c59b0f36a8 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -112,8 +112,14 @@ class Tag < ApplicationRecord names = Array(name_or_names).map { |str| [normalize(str), str] }.uniq(&:first) names.map do |(normalized_name, display_name)| - tag = matching_name(normalized_name).first || create(name: normalized_name, - display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '')) + tag = begin + matching_name(normalized_name).first || create!( + name: normalized_name, + display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '') + ) + rescue ActiveRecord::RecordNotUnique + find_normalized(normalized_name) + end yield tag if block_given? diff --git a/app/serializers/rest/annual_report_serializer.rb b/app/serializers/rest/annual_report_serializer.rb index 1fb5ddb5c1..99c313e6cb 100644 --- a/app/serializers/rest/annual_report_serializer.rb +++ b/app/serializers/rest/annual_report_serializer.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true class REST::AnnualReportSerializer < ActiveModel::Serializer - attributes :year, :data, :schema_version + include RoutingHelper + + attributes :year, :data, :schema_version, :share_url + + def share_url + public_wrapstodon_url(object.account, object.year, object.share_key) if object.share_key.present? + end end diff --git a/app/serializers/rest/collection_item_serializer.rb b/app/serializers/rest/collection_item_serializer.rb new file mode 100644 index 0000000000..c0acc87bfd --- /dev/null +++ b/app/serializers/rest/collection_item_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::CollectionItemSerializer < ActiveModel::Serializer + delegate :accepted?, to: :object + + attributes :position, :state + + belongs_to :account, serializer: REST::AccountSerializer, if: :accepted? +end diff --git a/app/serializers/rest/collection_serializer.rb b/app/serializers/rest/collection_serializer.rb index c03cc53856..3d63e75398 100644 --- a/app/serializers/rest/collection_serializer.rb +++ b/app/serializers/rest/collection_serializer.rb @@ -5,4 +5,11 @@ class REST::CollectionSerializer < ActiveModel::Serializer :created_at, :updated_at belongs_to :account, serializer: REST::AccountSerializer + belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer + + has_many :items, serializer: REST::CollectionItemSerializer + + def items + object.items_for(current_user&.account) + end end diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index 33da69da53..a173aeae19 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -8,6 +8,7 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer attributes :shortcode, :url, :static_url, :visible_in_picker attribute :category, if: :category_loaded? + attribute :featured, if: :category_loaded? def url full_asset_url(object.image.url) @@ -21,6 +22,10 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer object.category.name end + def featured + object.featured? + end + def category_loaded? object.association(:category).loaded? && object.category.present? end diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 796c6c0574..1253f5a6aa 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -12,7 +12,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer attributes :domain, :title, :version, :source_url, :description, :usage, :thumbnail, :icon, :languages, :configuration, - :registrations, :api_versions + :registrations, :api_versions, :wrapstodon has_one :contact, serializer: ContactSerializer has_many :rules, serializer: REST::RuleSerializer @@ -135,6 +135,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer Mastodon::Version.api_versions end + def wrapstodon + AnnualReport.current_campaign + end + private def registrations_enabled? diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index b9e11a17d1..b7f2d4f631 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -9,15 +9,15 @@ .filter-subset %strong= t('admin.accounts.location.title') %ul - %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil + %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil, by_domain: nil %li - if selected? local: '1', remote: nil = filter_link_to t('admin.accounts.location.local'), { local: nil, remote: nil }, { local: '1', remote: nil } - else - = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil + = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil, by_domain: nil %li - if selected? remote: '1', local: nil - = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil }, { remote: '1', local: nil } + = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil, by_domain: nil }, { remote: '1', local: nil } - else = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil @@ -27,6 +27,8 @@ = form.hidden_field key, value: params[key] if params[key].present? - %i(shortcode by_domain).each do |key| + - next if key == :by_domain && params[:local] == '1' + .input.string.optional = form.text_field key, value: params[key], diff --git a/app/views/wrapstodon/show.html.haml b/app/views/wrapstodon/show.html.haml new file mode 100644 index 0000000000..6978f231f9 --- /dev/null +++ b/app/views/wrapstodon/show.html.haml @@ -0,0 +1,9 @@ +- content_for :page_title, t('wrapstodon.title', name: display_name(@account), year: @generated_annual_report.year) + +- content_for :header_tags do + %meta{ name: 'robots', content: 'noindex, noarchive' }/ + + = opengraph 'og:site_name', site_title + = opengraph 'profile:username', acct(@account)[1..] + += render 'shared/web_app' diff --git a/app/workers/generate_annual_report_worker.rb b/app/workers/generate_annual_report_worker.rb index 7094c1ab9c..7a02cb7fbf 100644 --- a/app/workers/generate_annual_report_worker.rb +++ b/app/workers/generate_annual_report_worker.rb @@ -4,7 +4,11 @@ class GenerateAnnualReportWorker include Sidekiq::Worker def perform(account_id, year) + async_refresh = AsyncRefresh.new("wrapstodon:#{account_id}:#{year}") + AnnualReport.new(Account.find(account_id), year).generate + + async_refresh&.finish! rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordNotUnique true end diff --git a/config/locales/activerecord.fa.yml b/config/locales/activerecord.fa.yml index c25ef64959..a02395ba6c 100644 --- a/config/locales/activerecord.fa.yml +++ b/config/locales/activerecord.fa.yml @@ -32,6 +32,12 @@ fa: attributes: url: invalid: نشانی معتبری نیست + collection: + attributes: + collection_items: + too_many: بیش از حد. بیش از %{count} مجاز نیست + tag: + unusable: ممکن است استفاده نشود doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fr-CA.yml b/config/locales/activerecord.fr-CA.yml index a966bb5a8a..b5db3cc78b 100644 --- a/config/locales/activerecord.fr-CA.yml +++ b/config/locales/activerecord.fr-CA.yml @@ -32,6 +32,12 @@ fr-CA: attributes: url: invalid: n’est pas un URL valide + collection: + attributes: + collection_items: + too_many: trop d'éléments, seuls %{count} éléments sont permis + tag: + unusable: ne peut pas être utilisé doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fr.yml b/config/locales/activerecord.fr.yml index 94d2fa3c6f..5d5efae044 100644 --- a/config/locales/activerecord.fr.yml +++ b/config/locales/activerecord.fr.yml @@ -32,6 +32,12 @@ fr: attributes: url: invalid: n’est pas une URL valide + collection: + attributes: + collection_items: + too_many: trop d'éléments, seuls %{count} éléments sont permis + tag: + unusable: ne peut pas être utilisé doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.lt.yml b/config/locales/activerecord.lt.yml index 1eec2782f4..001243ab90 100644 --- a/config/locales/activerecord.lt.yml +++ b/config/locales/activerecord.lt.yml @@ -6,7 +6,7 @@ lt: expires_at: Galutinė data options: Pasirinkimai user: - agreement: Paslaugos sutartis + agreement: Paslaugų sąlygos email: El. laiško adresas locale: Lokali password: Slaptažodis @@ -32,6 +32,12 @@ lt: attributes: url: invalid: nėra tinkamas URL adresas. + collection: + attributes: + collection_items: + too_many: yra per daug, leidžiama ne daugiau kaip %{count} + tag: + unusable: negali būti panaudota doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.nl.yml b/config/locales/activerecord.nl.yml index b05d6680e7..0d6070ce61 100644 --- a/config/locales/activerecord.nl.yml +++ b/config/locales/activerecord.nl.yml @@ -32,6 +32,12 @@ nl: attributes: url: invalid: is een ongeldige URL + collection: + attributes: + collection_items: + too_many: zijn er te veel, niet meer dan %{count} zijn toegestaan + tag: + unusable: mag niet worden gebruikt doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.nn.yml b/config/locales/activerecord.nn.yml index ae73057dcc..c5e3acf2ab 100644 --- a/config/locales/activerecord.nn.yml +++ b/config/locales/activerecord.nn.yml @@ -32,6 +32,12 @@ nn: attributes: url: invalid: er ikkje ein gyldig URL + collection: + attributes: + collection_items: + too_many: er for mange, du kan ikkje ha meir enn %{count} + tag: + unusable: kan ikkje brukast doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml index db9317afa2..2f5fb50f2d 100644 --- a/config/locales/activerecord.tr.yml +++ b/config/locales/activerecord.tr.yml @@ -32,6 +32,12 @@ tr: attributes: url: invalid: geçerli bir URL değil + collection: + attributes: + collection_items: + too_many: çok fazla, en fazla %{count} olabilir + tag: + unusable: kullanılamaz doorkeeper/application: attributes: website: diff --git a/config/locales/da.yml b/config/locales/da.yml index 1a900bb849..caddba755d 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1706,16 +1706,22 @@ da: body: 'Du blev nævnt af %{name} i:' subject: Du blev nævnt af %{name} title: Ny omtale + moderation_warning: + subject: Du har fået en moderationsadvarsel poll: subject: En afstemning fra %{name} er afsluttet quote: body: 'Dit indlæg blev citeret af %{name}:' subject: "%{name} citerede dit indlæg" title: Nyt citat + quoted_update: + subject: "%{name} redigerede et indlæg, du har citeret" reblog: body: 'Dit indlæg blev fremhævet af %{name}:' subject: "%{name} fremhævede dit indlæg" title: Ny fremhævelse + severed_relationships: + subject: Du har mistet forbindelser på grund af en moderationsbeslutning status: subject: "%{name} har netop postet" update: diff --git a/config/locales/de.yml b/config/locales/de.yml index ccd44a0c8d..d9473031c8 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -33,7 +33,7 @@ de: created_msg: Moderationshinweis erfolgreich abgespeichert! destroyed_msg: Moderationsnotiz erfolgreich entfernt! accounts: - add_email_domain_block: E-Mail-Domain sperren + add_email_domain_block: E-Mail-Domain blockieren approve: Genehmigen approved_msg: Antrag zur Registrierung von %{username} erfolgreich genehmigt are_you_sure: Bist du dir sicher? @@ -154,10 +154,10 @@ de: subscribe: Abonnieren suspend: Sperren suspended: Gesperrt - suspension_irreversible: Die Daten dieses Kontos wurden unwiderruflich gelöscht. Du kannst das Konto entsperren, um es wieder zu verwenden, aber es wird keine Daten wiederherstellen, die es davor hatte. + suspension_irreversible: Die Daten dieses Kontos wurden unwiderruflich gelöscht. Du kannst die Kontosperrung aufheben, damit es wieder aktiv ist und funktioniert, aber es werden keine früheren Daten wiederhergestellt. suspension_reversible_hint_html: Das Konto wurde gesperrt und die Daten werden am %{date} vollständig gelöscht. Bis dahin kann das Konto ohne irgendwelche negativen Auswirkungen wiederhergestellt werden. Wenn du alle Daten des Kontos sofort entfernen möchtest, kannst du das nachfolgend tun. title: Konten - unblock_email: E-Mail-Adresse entsperren + unblock_email: Blockierung der E-Mail-Adresse aufheben unblocked_email_msg: E-Mail-Adresse von %{username} erfolgreich entsperrt unconfirmed_email: Unbestätigte E-Mail-Adresse undo_sensitized: Inhaltswarnung aufheben @@ -184,7 +184,7 @@ de: create_canonical_email_block: E-Mail-Sperre erstellen create_custom_emoji: Eigene Emojis erstellen create_domain_allow: Domain erlauben - create_domain_block: Domain sperren + create_domain_block: Domain blockieren create_email_domain_block: E-Mail-Domain-Sperre erstellen create_ip_block: IP-Regel erstellen create_relay: Relais erstellen @@ -218,20 +218,20 @@ de: promote_user: Benutzer*in hochstufen publish_terms_of_service: Nutzungsbedingungen veröffentlichen reject_appeal: Einspruch ablehnen - reject_user: Benutzer*in ablehnen + reject_user: Profil ablehnen remove_avatar_user: Profilbild entfernen reopen_report: Meldung wieder eröffnen - resend_user: Bestätigungs-E-Mail erneut senden + resend_user: Bestätigungs-E-Mail erneut zuschicken reset_password_user: Passwort zurücksetzen resolve_report: Meldung klären sensitive_account: Konto mit erzwungener Inhaltswarnung silence_account: Konto stummschalten suspend_account: Konto sperren unassigned_report: Meldung widerrufen - unblock_email_account: E-Mail-Adresse entsperren + unblock_email_account: Blockierung der E-Mail-Adresse aufheben unsensitive_account: Konto mit erzwungener Inhaltswarnung rückgängig machen unsilence_account: Konto nicht mehr stummschalten - unsuspend_account: Konto entsperren + unsuspend_account: Kontosperre aufheben update_announcement: Ankündigung aktualisieren update_custom_emoji: Eigenes Emoji aktualisieren update_domain_block: Domain-Sperre aktualisieren @@ -241,7 +241,7 @@ de: update_user_role: Rolle bearbeiten update_username_block: Regel für Profilnamen aktualisieren actions: - approve_appeal_html: "%{name} hat den Einspruch gegen eine Moderationsentscheidung von %{target} genehmigt" + approve_appeal_html: "%{name} genehmigte den Einspruch gegen eine Moderationsentscheidung von %{target}" approve_user_html: "%{name} genehmigte die Registrierung von %{target}" assigned_to_self_report_html: "%{name} wies sich die Meldung %{target} selbst zu" change_email_user_html: "%{name} änderte die E-Mail-Adresse von %{target}" @@ -255,7 +255,7 @@ de: create_domain_block_html: "%{name} sperrte die Domain %{target}" create_email_domain_block_html: "%{name} sperrte die E-Mail-Domain %{target}" create_ip_block_html: "%{name} erstellte eine IP-Regel für %{target}" - create_relay_html: "%{name} erstellte ein Relay %{target}" + create_relay_html: "%{name} erstellte ein Relais %{target}" create_unavailable_domain_html: "%{name} beendete die Zustellung an die Domain %{target}" create_user_role_html: "%{name} erstellte die Rolle %{target}" create_username_block_html: "%{name} erstellte eine Regel für Profilnamen mit %{target}" @@ -285,7 +285,7 @@ de: memorialize_account_html: "%{name} wandelte das Konto von %{target} in eine Gedenkseite um" promote_user_html: "%{name} beförderte %{target}" publish_terms_of_service_html: "%{name} aktualisierte die Nutzungsbedingungen" - reject_appeal_html: "%{name} hat den Einspruch gegen eine Moderationsentscheidung von %{target} abgelehnt" + reject_appeal_html: "%{name} lehnte den Einspruch gegen eine Moderationsentscheidung von %{target} ab" reject_user_html: "%{name} hat die Registrierung von %{target} abgelehnt" remove_avatar_user_html: "%{name} entfernte das Profilbild von %{target}" reopen_report_html: "%{name} öffnete die Meldung %{target} wieder" @@ -296,14 +296,14 @@ de: silence_account_html: "%{name} schaltete das Konto von %{target} stumm" suspend_account_html: "%{name} sperrte das Konto von %{target}" unassigned_report_html: "%{name} entfernte die Zuweisung der Meldung %{target}" - unblock_email_account_html: "%{name} hat die E-Mail-Adresse von %{target} entsperrt" - unsensitive_account_html: "%{name} hat die Inhaltswarnung für Medien von %{target} aufgehoben" + unblock_email_account_html: "%{name} entsperrte die E-Mail-Adresse von %{target}" + unsensitive_account_html: "%{name} hob die Inhaltswarnung für Medien von %{target} auf" unsilence_account_html: "%{name} hob die Stummschaltung von %{target} auf" unsuspend_account_html: "%{name} entsperrte das Konto von %{target}" update_announcement_html: "%{name} überarbeitete die Ankündigung %{target}" update_custom_emoji_html: "%{name} bearbeitete das Emoji %{target}" update_domain_block_html: "%{name} aktualisierte die Domain-Sperre für %{target}" - update_ip_block_html: "%{name} änderte die Regel für die IP-Adresse %{target}" + update_ip_block_html: "%{name} änderte eine IP-Regel für %{target}" update_report_html: "%{name} überarbeitete die Meldung %{target}" update_status_html: "%{name} überarbeitete einen Beitrag von %{target}" update_user_role_html: "%{name} änderte die Rolle von %{target}" @@ -366,7 +366,7 @@ de: shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche title: Emojis uncategorized: Unkategorisiert - unlist: Nicht anzeigen + unlist: Ausblenden unlisted: Nicht sichtbar update_failed_msg: Konnte dieses Emoji nicht bearbeiten updated_msg: Emoji erfolgreich bearbeitet! @@ -522,7 +522,7 @@ de: status: Status suppress: Folgeempfehlung unterbinden suppressed: Unterdrückt - title: Folgeempfehlungen + title: Follower-Empfehlungen unsuppress: Folgeempfehlung nicht mehr unterbinden instances: audit_log: @@ -825,7 +825,7 @@ de: preamble: Passe das Webinterface von Mastodon an. title: Design branding: - preamble: Das Branding deines Servers unterscheidet ihn von anderen Servern im Netzwerk. Diese Informationen können in einer Vielzahl von Umgebungen angezeigt werden, z. B. in der Weboberfläche von Mastodon, in nativen Anwendungen, in Linkvorschauen auf anderen Websites und in Messaging-Apps und so weiter. Aus diesem Grund ist es am besten, diese Informationen klar, kurz und prägnant zu halten. + preamble: Das Branding deines Servers unterscheidet ihn von anderen Servern im Netzwerk. Diese Informationen können in einer Vielzahl von Umgebungen angezeigt werden, z. B. im Webinterface von Mastodon, in nativen Anwendungen, in der Linkvorschau auf anderen Websites, in Messaging-Apps und so weiter. Aus diesem Grund ist es am besten, diese Informationen klar, kurz und prägnant zu halten. title: Branding captcha_enabled: desc_html: Dies beruht auf externen Skripten von hCaptcha, die ein Sicherheits- und Datenschutzproblem darstellen könnten. Darüber hinaus kann das den Registrierungsprozess für manche Menschen (insbesondere für Menschen mit Behinderung) erheblich erschweren. Aus diesen Gründen solltest du alternative Maßnahmen in Betracht ziehen, z. B. eine Registrierung basierend auf einer Einladung oder auf Genehmigungen. @@ -1706,12 +1706,16 @@ de: body: 'Du wurdest von %{name} erwähnt:' subject: "%{name} erwähnte dich" title: Neue Erwähnung + moderation_warning: + subject: Die Moderator*innen haben dich verwarnt poll: subject: Eine Umfrage von %{name} ist beendet quote: body: 'Dein Beitrag wurde von %{name} zitiert:' subject: "%{name} zitierte deinen Beitrag" title: Neuer zitierter Beitrag + quoted_update: + subject: "%{name} bearbeitete einen von dir zitierten Beitrag" reblog: body: 'Dein Beitrag wurde von %{name} geteilt:' subject: "%{name} teilte deinen Beitrag" @@ -1889,7 +1893,7 @@ de: profile: Öffentliches Profil relationships: Follower und Folge ich severed_relationships: Getrennte Beziehungen - statuses_cleanup: Automatische Löschung + statuses_cleanup: Automatisiertes Löschen strikes: Maßnahmen two_factor_authentication: Zwei-Faktor-Authentisierung webauthn_authentication: Sicherheitsschlüssel diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml index d2465fe1ac..0b256f85c0 100644 --- a/config/locales/devise.fi.yml +++ b/config/locales/devise.fi.yml @@ -47,7 +47,7 @@ fi: explanation: Pyysit tilillesi uuden salasanan. extra: Jos et tehnyt pyyntöä itse, voit jättää tämän viestin huomiotta. Salasanaasi ei vaihdeta, ennen kuin painat edellä olevaa linkkiä ja luot uuden salasanan. subject: 'Mastodon: ohjeet salasanan vaihtoon' - title: Salasanan vaihto + title: Salasanan palautus two_factor_disabled: explanation: Sisäänkirjautuminen on nyt mahdollista pelkällä sähköpostiosoitteella ja salasanalla. subject: 'Mastodon: kaksivaiheinen todennus poistettu käytöstä' diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml index 7b80882af0..73730a8689 100644 --- a/config/locales/doorkeeper.fi.yml +++ b/config/locales/doorkeeper.fi.yml @@ -127,7 +127,7 @@ fi: blocks: Estot bookmarks: Kirjanmerkit conversations: Keskustelut - crypto: Päästä päähän -salaus + crypto: Päästä päähän -⁠salaus favourites: Suosikit filters: Suodattimet follow: Seurattavat, mykistykset ja estot @@ -165,7 +165,7 @@ fi: admin:write:email_domain_blocks: suorita moderointitoimia estetyille sähköpostiverkkotunnuksille admin:write:ip_blocks: suorita moderointitoimia estetyille IP-osoitteille admin:write:reports: suorita moderointitoimia raporteille - crypto: käytä päästä päähän -salausta + crypto: käytä päästä päähän -⁠salausta follow: muokkaa tilin seurantasuhteita profile: lue vain tilisi profiilitietoja push: vastaanota puskuilmoituksesi diff --git a/config/locales/el.yml b/config/locales/el.yml index 47a558ada7..259a3f7ff1 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1706,16 +1706,22 @@ el: body: 'Επισημάνθηκες από τον/την %{name} στο:' subject: Επισημάνθηκες από τον/την %{name} title: Νέα επισήμανση + moderation_warning: + subject: Έχετε λάβει μία προειδοποίηση συντονισμού poll: subject: Μια δημοσκόπηση του %{name} έληξε quote: body: 'Η ανάρτησή σου παρατέθηκε από %{name}:' subject: Ο/Η %{name} έκανε παράθεση της ανάρτησής σου title: Νέα παράθεση + quoted_update: + subject: Ο χρήστης %{name} επεξεργάστηκε μία ανάρτηση που παρέθεσες reblog: body: 'Η ανάρτησή σου ενισχύθηκε από τον/την %{name}:' subject: Ο/Η %{name} ενίσχυσε την ανάρτηση σου title: Νέα ενίσχυση + severed_relationships: + subject: Έχετε χάσει συνδέσεις λόγω μιας απόφασης συντονισμού status: subject: Ο/Η %{name} μόλις ανέρτησε κάτι update: diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 3d9f3722c5..4a921bedc2 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1706,16 +1706,22 @@ en-GB: body: 'You were mentioned by %{name} in:' subject: You were mentioned by %{name} title: New mention + moderation_warning: + subject: You have received a moderation warning poll: subject: A poll by %{name} has ended quote: body: 'Your post was quoted by %{name}:' subject: "%{name} quoted your post" title: New quote + quoted_update: + subject: "%{name} edited a post you have quoted" reblog: body: 'Your post was boosted by %{name}:' subject: "%{name} boosted your post" title: New boost + severed_relationships: + subject: You have lost connections due to a moderation decision status: subject: "%{name} just posted" update: diff --git a/config/locales/en.yml b/config/locales/en.yml index ffe6028e72..0c44a7c3bf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1706,16 +1706,22 @@ en: body: 'You were mentioned by %{name} in:' subject: You were mentioned by %{name} title: New mention + moderation_warning: + subject: You have received a moderation warning poll: subject: A poll by %{name} has ended quote: body: 'Your post was quoted by %{name}:' subject: "%{name} quoted your post" title: New quote + quoted_update: + subject: "%{name} edited a post you have quoted" reblog: body: 'Your post was boosted by %{name}:' subject: "%{name} boosted your post" title: New boost + severed_relationships: + subject: You have lost connections due to a moderation decision status: subject: "%{name} just posted" update: @@ -2180,3 +2186,5 @@ en: not_supported: This browser doesn't support security keys otp_required: To use security keys please enable two-factor authentication first. registered_on: Registered on %{date} + wrapstodon: + title: Wrapstodon %{year} for %{name} diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index a2f3e7dcfd..da522bee6a 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1706,16 +1706,22 @@ es-AR: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Recibiste una advertencia de moderación poll: subject: Terminó una encuesta de %{name} quote: body: 'Tu mensaje fue citado por %{name}:' subject: "%{name} citó tu mensaje" title: Nueva cita + quoted_update: + subject: "%{name} editó un mensaje que citaste" reblog: body: "%{name} adhirió a tu mensaje:" subject: "%{name} adhirió a tu mensaje" title: Nueva adhesión + severed_relationships: + subject: Perdiste seguidores y/o cuentas seguidas, debido a una decisión de moderación status: subject: "%{name} acaba de enviar un mensaje" update: diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index b39844b194..7b67ef01ff 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1706,12 +1706,16 @@ es-MX: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Has recibido una advertencia de moderación poll: subject: Una encuesta de %{name} ha terminado quote: body: 'Tu publicación fue citada por %{name}:' subject: "%{name} citó tu publicación" title: Nueva cita + quoted_update: + subject: "%{name} editó una publicación que has citado" reblog: body: 'Tu publicación fue impulsada por %{name}:' subject: "%{name} ha impulsado tu publicación" diff --git a/config/locales/es.yml b/config/locales/es.yml index 9dcb73e006..c7268deb92 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1706,12 +1706,16 @@ es: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Has recibido una advertencia de moderación poll: subject: Una encuesta de %{name} ha terminado quote: body: 'Tu publicación ha sido citada por %{name}:' subject: "%{name} citó tu publicación" title: Nueva cita + quoted_update: + subject: "%{name} editó una publicación que has citado" reblog: body: 'Tu publicación fue impulsada por %{name}:' subject: "%{name} impulsó tu publicación" diff --git a/config/locales/fa.yml b/config/locales/fa.yml index eec1dd5048..ab5742a9fc 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -1706,16 +1706,22 @@ fa: body: "%{name} در این‌جا به شما اشاره کرد:" subject: "%{name} به شما اشاره کرد" title: اشارهٔ جدید + moderation_warning: + subject: هشداری مدیریتی گرفته‌اید poll: subject: نظرسنجی‌ای از %{name} پایان یافت quote: body: 'فرسته‌تان توسط %{name} نقل شد:' subject: "%{name} فرسته‌تان را نقل کرد" title: نقل‌قول جدید + quoted_update: + subject: "‏%{name} فرسته‌ای که نقل کردید را ویراست" reblog: body: "%{name} فرستهٔ شما را تقویت کرد:" subject: "%{name} فرستهٔ شما را تقویت کرد" title: تقویت تازه + severed_relationships: + subject: بنا به تصمیمی مدیریتی ارتباطاتی را از دست داده‌اید status: subject: "%{name} چیزی فرستاد" update: diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 93d0b87433..cc6c80fb08 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -48,7 +48,7 @@ fi: title: Vaihda käyttäjän %{username} sähköposti-osoite change_role: changed_msg: Roolin vaihto onnistui! - edit_roles: Hallinnoi käyttäjien rooleja + edit_roles: Hallitse käyttäjärooleja label: Vaihda rooli no_role: Ei roolia title: Vaihda käyttäjän %{username} rooli @@ -76,10 +76,10 @@ fi: followers: Seuraajat follows: Seurattavat header: Otsakekuva - inbox_url: Postilaatikon osoite + inbox_url: Postilaatikon URL-⁠osoite invite_request_text: Syitä liittymiseen invited_by: Kutsuja - ip: IP-osoite + ip: IP-⁠osoite joined: Liittynyt location: all: Kaikki @@ -101,7 +101,7 @@ fi: title: Moderointi moderation_notes: Moderointimuistiinpanot most_recent_activity: Viimeisin toiminta - most_recent_ip: Viimeisin IP-osoite + most_recent_ip: Viimeisin IP-⁠osoite no_account_selected: Tilejä ei muutettu, koska yhtään ei ollut valittuna no_limits_imposed: Ei asetettuja rajoituksia no_role_assigned: Roolia ei asetettu @@ -124,7 +124,7 @@ fi: remote_suspension_reversible_hint_html: Tili on jäädytetty omalla palvelimellaan, ja kaikki tiedot poistetaan %{date}. Sitä ennen etäpalvelin voi palauttaa tilin ongelmitta. Jos haluat poistaa kaikki tilin tiedot heti, onnistuu se alta. remove_avatar: Poista profiilikuva remove_header: Poista otsakekuva - removed_avatar_msg: Käyttäjän %{username} avatar-kuva poistettiin onnistuneesti + removed_avatar_msg: Käyttäjän %{username} profiilikuva poistettiin onnistuneesti removed_header_msg: Käyttäjän %{username} otsakekuva poistettiin onnistuneesti resend_confirmation: already_confirmed: Tämä käyttäjä on jo vahvistettu @@ -136,14 +136,14 @@ fi: role: Rooli search: Hae search_same_email_domain: Muut käyttäjät, joilla on sama sähköpostiverkkotunnus - search_same_ip: Muut käyttäjät, joilla on sama IP-osoite + search_same_ip: Muut käyttäjät, joilla on sama IP-⁠osoite security: Turvallisuus security_measures: only_password: Vain salasana password_and_2fa: Salasana ja kaksivaiheinen todennus sensitive: Pakota arkaluonteiseksi sensitized: Merkitty arkaluonteiseksi - shared_inbox_url: Jaetun postilaatikon osoite + shared_inbox_url: Jaetun postilaatikon URL-⁠osoite show: created_reports: Tämän tilin luomat raportit targeted_reports: Tästä tilistä tehdyt raportit @@ -186,7 +186,7 @@ fi: create_domain_allow: Luo verkkotunnuksen salliminen create_domain_block: Luo verkkotunnuksen esto create_email_domain_block: Luo sähköpostiverkkotunnuksen esto - create_ip_block: Luo IP-sääntö + create_ip_block: Luo IP-⁠sääntö create_relay: Luo välittäjä create_unavailable_domain: Luo ei-saatavilla oleva verkkotunnus create_user_role: Luo rooli @@ -199,7 +199,7 @@ fi: destroy_domain_block: Poista verkkotunnuksen esto destroy_email_domain_block: Poista sähköpostiverkkotunnuksen esto destroy_instance: Tyhjennä verkkotunnus - destroy_ip_block: Poista IP-sääntö + destroy_ip_block: Poista IP-⁠sääntö destroy_relay: Poista välittäjä destroy_status: Poista julkaisu destroy_unavailable_domain: Poista ei-saatavilla oleva verkkotunnus @@ -224,18 +224,18 @@ fi: resend_user: Lähetä vahvistusviesti uudelleen reset_password_user: Palauta salasana resolve_report: Ratkaise raportti - sensitive_account: Pakota arkaluonteiseksi tiliksi + sensitive_account: Pakota tili arkaluonteiseksi silence_account: Rajoita tiliä suspend_account: Jäädytä tili unassigned_report: Poista raportti käsittelystä unblock_email_account: Kumoa sähköpostiosoitteen esto - unsensitive_account: Kumoa pakotus arkaluonteiseksi tiliksi + unsensitive_account: Kumoa tilin pakotus arkaluonteiseksi unsilence_account: Kumoa tilin rajoitus unsuspend_account: Kumoa tilin jäädytys update_announcement: Päivitä tiedote update_custom_emoji: Päivitä mukautettu emoji update_domain_block: Päivitä verkkotunnuksen esto - update_ip_block: Päivitä IP-sääntö + update_ip_block: Päivitä IP-⁠sääntö update_report: Päivitä raportti update_status: Päivitä julkaisu update_user_role: Päivitä rooli @@ -254,7 +254,7 @@ fi: create_domain_allow_html: "%{name} salli federoinnin verkkotunnuksen %{target} kanssa" create_domain_block_html: "%{name} esti verkkotunnuksen %{target}" create_email_domain_block_html: "%{name} esti sähköpostiverkkotunnuksen %{target}" - create_ip_block_html: "%{name} loi säännön IP-osoitteelle %{target}" + create_ip_block_html: "%{name} loi säännön IP-⁠osoitteelle %{target}" create_relay_html: "%{name} loi välittäjän %{target}" create_unavailable_domain_html: "%{name} pysäytti toimituksen verkkotunnukseen %{target}" create_user_role_html: "%{name} loi roolin %{target}" @@ -267,7 +267,7 @@ fi: destroy_domain_block_html: "%{name} kumosi verkkotunnuksen %{target} eston" destroy_email_domain_block_html: "%{name} kumosi sähköpostiverkkotunnuksen %{target} eston" destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}" - destroy_ip_block_html: "%{name} poisti säännön IP-osoitteelta %{target}" + destroy_ip_block_html: "%{name} poisti säännön IP-⁠osoitteelta %{target}" destroy_relay_html: "%{name} poisti välittäjän %{target}" destroy_status_html: "%{name} poisti käyttäjän %{target} julkaisun" destroy_unavailable_domain_html: "%{name} jatkoi toimitusta verkkotunnukseen %{target}" @@ -303,7 +303,7 @@ fi: update_announcement_html: "%{name} päivitti tiedotteen %{target}" update_custom_emoji_html: "%{name} päivitti emojin %{target}" update_domain_block_html: "%{name} päivitti verkkotunnuksen %{target} eston" - update_ip_block_html: "%{name} muutti IP-osoitteen %{target} sääntöä" + update_ip_block_html: "%{name} muutti IP-⁠osoitteen %{target} sääntöä" update_report_html: "%{name} päivitti raportin %{target}" update_status_html: "%{name} päivitti käyttäjän %{target} julkaisun" update_user_role_html: "%{name} muutti roolia %{target}" @@ -330,8 +330,8 @@ fi: title: Esikatsele tiedoteilmoitus publish: Julkaise published_msg: Tiedotteen julkaisu onnistui! - scheduled_for: Ajoitettu %{time} - scheduled_msg: Tiedotteen julkaisu ajoitettu! + scheduled_for: Ajastettu %{time} + scheduled_msg: Tiedotteen julkaisu ajastettu! title: Tiedotteet unpublish: Lopeta julkaisu unpublished_msg: Tiedotteen julkaisun lopetus onnistui! @@ -362,8 +362,8 @@ fi: no_emoji_selected: Emojeita ei muutettu, koska yhtään ei ollut valittuna not_permitted: Sinulla ei ole oikeutta suorittaa tätä toimintoa overwrite: Korvaa - shortcode: Lyhennekoodi - shortcode_hint: Vähintään 2 merkkiä, vain kirjaimia, numeroita ja alaviivoja + shortcode: Lyhytkoodi + shortcode_hint: Vähintään 2 merkkiä; vain kirjaimia, numeroita ja alaviivoja title: Mukautetut emojit uncategorized: Luokittelematon unlist: Poista listasta @@ -637,7 +637,7 @@ fi: enable: Ota käyttöön enable_hint: Kun tämä on otettu käyttöön, palvelimesi tilaa välittäjältä kaikki sen välittämät julkiset julkaisut ja alkaa lähettää omansa sille. enabled: Käytössä - inbox_url: Välittäjän URL + inbox_url: Välittäjän URL-⁠osoite pending: Odotetaan välittäjän hyväksyntää save_and_enable: Tallenna ja ota käyttöön setup: Määritä yhteys välittäjään @@ -748,7 +748,7 @@ fi: moderation: Moderointi special: Erityistä delete: Poista - description_html: "Käyttäjärooleilla voit mukauttaa, mihin Mastodonin toimintoihin ja alueisiin käyttäjäsi on pääsy." + description_html: "Käyttäjärooleilla voit mukauttaa sitä, mihin Mastodonin toimintoihin ja alueisiin käyttäjilläsi on pääsy." edit: Muokkaa roolia ”%{name}” everyone: Oletuskäyttöoikeudet everyone_full_description_html: Tämä on perusrooli, joka vaikuttaa kaikkiin käyttäjiin, jopa ilman asetettua roolia. Kaikki muut roolit perivät sen käyttöoikeudet. @@ -769,7 +769,7 @@ fi: manage_blocks: Hallita estoja manage_blocks_description: Sallii käyttäjien estää sähköpostipalveluntarjoajia ja IP-osoitteita manage_custom_emojis: Hallita mukautettuja emojeita - manage_custom_emojis_description: Sallii käyttäjien hallita mukautettuja emojeita palvelimella + manage_custom_emojis_description: Sallii käyttäjien hallita palvelimen mukautettuja emojeita manage_federation: Hallita federointia manage_federation_description: Sallii käyttäjien estää tai sallia federointi muiden verkkotunnusten kanssa ja hallita toimitusta manage_invites: Hallita kutsuja @@ -784,7 +784,7 @@ fi: manage_settings_description: Sallii käyttäjien muuttaa sivuston asetuksia manage_taxonomies: Hallita luokittelua manage_taxonomies_description: Sallii käyttäjien tarkistaa suositun sisällön ja päivittää aihetunnisteiden asetuksia - manage_user_access: Hallita käyttäjäoikeuksia + manage_user_access: Hallita käyttäjien oikeuksia manage_user_access_description: Sallii käyttäjien poistaa muiden käyttäjien kaksivaiheinen todennus käytöstä, vaihtaa heidän sähköpostiosoitteensa ja palauttaa heidän salasanansa manage_users: Hallita käyttäjiä manage_users_description: Sallii käyttäjien tarkastella muiden käyttäjien tietoja ja suorittaa moderointitoimia heitä kohtaan @@ -894,7 +894,7 @@ fi: statuses: account: Tekijä application: Sovellus - back_to_account: Takaisin tilin sivulle + back_to_account: Takaisin tilisivulle back_to_report: Takaisin raporttisivulle batch: add_to_report: Lisää raporttiin nro %{id} @@ -1412,7 +1412,7 @@ fi: not_found_multiple: käyttäjänimiä %{usernames} ei löytynyt exports: archive_takeout: - date: Päiväys + date: Päivämäärä download: Lataa arkisto hint_html: Voit pyytää arkistoa omista julkaisuista ja mediasta. Viedyt tiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla. Voit pyytää arkistoa 7 päivän välein. in_progress: Arkistoa kootaan… @@ -1468,7 +1468,7 @@ fi: statuses: back_to_filter: Takaisin suodattimeen batch: - remove: Poista suodattimista + remove: Poista suodattimesta index: hint: Tämä suodatin koskee yksittäisten julkaisujen valintaa muista kriteereistä riippumatta. Voit lisätä lisää julkaisuja tähän suodattimeen selainkäyttöliittymästä. title: Suodatetut julkaisut @@ -1554,7 +1554,7 @@ fi: states: finished: Valmis in_progress: Käynnissä - scheduled: Ajoitettu + scheduled: Ajastettu unconfirmed: Varmistamaton status: Tila success: Tietojen lähettäminen onnistui, ja ne käsitellään aivan pian @@ -1706,16 +1706,22 @@ fi: body: "%{name} mainitsi sinut:" subject: "%{name} mainitsi sinut" title: Uusi maininta + moderation_warning: + subject: Olet saanut moderointivaroituksen poll: subject: Äänestys käyttäjältä %{name} on päättynyt quote: body: "%{name} lainasi julkaisuasi:" subject: "%{name} lainasi julkaisuasi" title: Uusi lainaus + quoted_update: + subject: "%{name} muokkasi lainaamaasi julkaisua" reblog: body: "%{name} tehosti julkaisuasi:" subject: "%{name} tehosti julkaisuasi" title: Uusi tehostus + severed_relationships: + subject: Olet menettänyt yhteyksiä moderointipäätöksen vuoksi status: subject: "%{name} julkaisi juuri" update: @@ -1750,7 +1756,7 @@ fi: truncate: "…" polls: errors: - already_voted: Olet jo äänestänyt tässä äänestyksessä + already_voted: Olet jo osallistunut tähän äänestykseen duplicate_options: sisältää kaksoiskappaleita duration_too_long: on liian kaukana tulevaisuudessa duration_too_short: on liian aikainen @@ -1817,8 +1823,8 @@ fi: account: Julkiset julkaisut tililtä @%{acct} tag: 'Julkiset julkaisut aihetunnisteella #%{hashtag}' scheduled_statuses: - over_daily_limit: Olet ylittänyt %{limit} ajoitetun julkaisun rajan tälle päivälle - over_total_limit: Olet ylittänyt %{limit} ajoitetun julkaisun rajan + over_daily_limit: Olet ylittänyt %{limit} ajastetun julkaisun rajan tälle päivälle + over_total_limit: Olet ylittänyt %{limit} ajastetun julkaisun rajan too_soon: päivämäärän on oltava tulevaisuudessa self_destruct: lead_html: Valitettavasti %{domain} sulkeutuu pysyvästi. Jos sinulla on siellä tili, et voi jatkaa sen käyttöä mutta voit yhä pyytää varmuuskopiota tiedoistasi. @@ -1847,7 +1853,7 @@ fi: unknown_browser: tuntematon selain weibo: Weibo current_session: Nykyinen istunto - date: Päiväys + date: Päivämäärä description: "%{browser} %{platform}" explanation: Nämä verkkoselaimet ovat parhaillaan kirjautuneena Mastodon-tilillesi. ip: IP-osoite @@ -2158,7 +2164,7 @@ fi: extra_instructions_html: Vinkki: Verkkosivustollasi oleva linkki voi olla myös näkymätön. Olennainen osuus on rel="me", joka estää toiseksi henkilöksi tekeytymisen verkkosivustoilla, joilla on käyttäjien luomaa sisältöä. Voit käyttää jopa link-elementtiä sivun head-osassa elementin a sijaan, mutta HTML:n pitää olla käytettävissä ilman JavaScript-koodin suorittamista. here_is_how: Näin se onnistuu hint_html: "Henkilöllisyyden vahvistaminen on Mastodonissa jokaisen käyttäjän ulottuvilla. Se perustuu avoimiin standardeihin ja on maksutonta nyt ja aina. Tarvitset vain henkilökohtaisen verkkosivuston, jonka perusteella sinut voidaan tunnistaa. Kun teet linkin tuolle verkkosivulle profiilistasi, tarkistamme, että verkkosivustolla on linkki takaisin profiiliisi, ja näytämme profiilissasi visuaalisen ilmaisimen." - instructions_html: Kopioi ja liitä seuraava koodi verkkosivustosi HTML-lähdekoodiin. Lisää sitten verkkosivustosi osoite johonkin profiilisi lisäkentistä ”Muokkaa profiilia” -välilehdellä ja tallenna muutokset. + instructions_html: Kopioi ja liitä seuraava koodi verkkosivustosi HTML-lähdekoodiin. Lisää sitten verkkosivustosi osoite johonkin profiilisi lisäkentistä ”Muokkaa profiilia” -⁠välilehdellä ja tallenna muutokset. verification: Vahvistus verified_links: Vahvistetut linkkisi website_verification: Verkkosivuston vahvistus diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index a0e3f1d584..3a46a17f55 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -506,6 +506,7 @@ fr-CA: finish_registration: Terminer l'inscription name: Nom providers: Fournisseur + public_key_fingerprint: Empreinte de la clé publique registration_requested: Inscription demandée registrations: confirm: Confirmer @@ -821,6 +822,7 @@ fr-CA: 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: + desc: Quand les utilisateurs cliquent sur un lien externe, leur navigateur peut envoyer l'adresse du serveur Mastodon en tant qu'adresse d'origine. À désactiver si cela permet d'identifier les utilisateurs, par exemple s'il s'agit d'un serveur Mastodon personnel. title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. @@ -840,6 +842,7 @@ fr-CA: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations + preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. privacy: Vie privée profile_directory: Annuaire des profils public_timelines: Fils publics @@ -853,6 +856,7 @@ fr-CA: feed_access: modes: authenticated: Utilisateurs authentifiés uniquement + disabled: Nécessite un rôle spécifique public: Tout le monde landing_page: values: @@ -938,6 +942,8 @@ fr-CA: system_checks: database_schema_check: message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu + elasticsearch_analysis_index_mismatch: + message_html: Les paramètres de l'analyseur d'index d'Elasticsearch ne sont plus à jour. Veuillez lancer tootctl search deploy --only=%{value} elasticsearch_health_red: message_html: Le cluster Elasticsearch n’est pas sain (statut rouge), les fonctionnalités de recherche ne sont pas disponible elasticsearch_health_yellow: @@ -1113,11 +1119,14 @@ fr-CA: delete: Supprimer edit: title: Modifier la règle de nom d'utilisateur + matches_exactly_html: Égal à %{string} new: create: Créer une règle title: Créer une nouvelle règle de nom d'utilisateur no_username_block_selected: Aucune règle de nom d'utilisateur n'a été modifiée car aucune n'a été sélectionnée + not_permitted: Non autorisé title: Règles de nom d'utilisateur + updated_msg: Règle de nom d'utilisateur mise à jour warning_presets: add_new: Ajouter un nouveau delete: Supprimer @@ -1193,6 +1202,7 @@ fr-CA: advanced_settings: Paramètres avancés animations_and_accessibility: Animations et accessibilité boosting_preferences: Préférences de partage + boosting_preferences_info_html: "Astuce : indépendamment des paramètres, Maj + Clic sur l'icône %{icon} Boost va immédiatement partager." discovery: Découverte localization: body: Mastodon est traduit par des volontaires. @@ -1597,6 +1607,7 @@ fr-CA: link_preview: author_html: Par %{name} potentially_sensitive_content: + action: Cliquer pour afficher confirm_visit: Voulez-vous vraiment ouvrir ce lien ? hide_button: Masquer label: Contenu potentiellement sensible @@ -1664,6 +1675,7 @@ fr-CA: disabled_account: Votre compte actuel ne sera pas entièrement utilisable par la suite. Cependant, vous aurez accès à l'exportation de données et à la réactivation. followers: Cette action va déménager tou·te·s les abonné·e·s du compte actuel vers le nouveau compte only_redirect_html: Alternativement, vous pouvez seulement appliquer une redirection sur votre profil. + other_data: Aucune autre donnée ne sera déplacée automatiquement (y compris vos messages et la liste des comptes suivis) redirect: Le profil de votre compte actuel sera mis à jour avec un avis de redirection et sera exclu des recherches moderation: title: Modération @@ -1920,6 +1932,7 @@ fr-CA: errors: in_reply_not_found: Le message auquel vous essayez de répondre ne semble pas exister. quoted_status_not_found: Le message que vous essayez de citer ne semble pas exister. + quoted_user_not_mentioned: Impossible de citer un compte non mentionné dans une mention privée. over_character_limit: limite de %{max} caractères dépassée pin_errors: direct: Les messages qui ne sont visibles que pour les utilisateur·rice·s mentionné·e·s ne peuvent pas être épinglés @@ -1988,6 +2001,8 @@ fr-CA: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + future_preamble_html: Nous avons modifié nos conditions d'utilisation, ces changements entreront en vigueur le %{date}. Nous vous invitons à prendre connaissance des nouvelles conditions. + past_preamble_html: Nous avons modifié nos conditions d'utilisation depuis votre dernière visite. Nous vous invitons à prendre connaissance des nouvelles conditions. review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 9d43b6efef..28be8c9641 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -506,6 +506,7 @@ fr: finish_registration: Terminer l'inscription name: Nom providers: Fournisseur + public_key_fingerprint: Empreinte de la clé publique registration_requested: Inscription demandée registrations: confirm: Confirmer @@ -821,6 +822,7 @@ fr: 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: + desc: Quand les utilisateurs cliquent sur un lien externe, leur navigateur peut envoyer l'adresse du serveur Mastodon en tant qu'adresse d'origine. À désactiver si cela permet d'identifier les utilisateurs, par exemple s'il s'agit d'un serveur Mastodon personnel. title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. @@ -840,6 +842,7 @@ fr: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations + preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. privacy: Vie privée profile_directory: Annuaire des profils public_timelines: Fils publics @@ -853,6 +856,7 @@ fr: feed_access: modes: authenticated: Utilisateurs authentifiés uniquement + disabled: Nécessite un rôle spécifique public: Tout le monde landing_page: values: @@ -938,6 +942,8 @@ fr: system_checks: database_schema_check: message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu + elasticsearch_analysis_index_mismatch: + message_html: Les paramètres de l'analyseur d'index d'Elasticsearch ne sont plus à jour. Veuillez lancer tootctl search deploy --only=%{value} elasticsearch_health_red: message_html: Le cluster Elasticsearch n’est pas sain (statut rouge), les fonctionnalités de recherche ne sont pas disponible elasticsearch_health_yellow: @@ -1113,11 +1119,14 @@ fr: delete: Supprimer edit: title: Modifier la règle de nom d'utilisateur + matches_exactly_html: Égal à %{string} new: create: Créer une règle title: Créer une nouvelle règle de nom d'utilisateur no_username_block_selected: Aucune règle de nom d'utilisateur n'a été modifiée car aucune n'a été sélectionnée + not_permitted: Non autorisé title: Règles de nom d'utilisateur + updated_msg: Règle de nom d'utilisateur mise à jour warning_presets: add_new: Ajouter un nouveau delete: Supprimer @@ -1193,6 +1202,7 @@ fr: advanced_settings: Paramètres avancés animations_and_accessibility: Animations et accessibilité boosting_preferences: Préférences de partage + boosting_preferences_info_html: "Astuce : indépendamment des paramètres, Maj + Clic sur l'icône %{icon} Boost va immédiatement partager." discovery: Découverte localization: body: Mastodon est traduit par des volontaires. @@ -1597,6 +1607,7 @@ fr: link_preview: author_html: Par %{name} potentially_sensitive_content: + action: Cliquer pour afficher confirm_visit: Voulez-vous vraiment ouvrir ce lien ? hide_button: Masquer label: Contenu potentiellement sensible @@ -1664,6 +1675,7 @@ fr: disabled_account: Votre compte actuel ne sera pas entièrement utilisable par la suite. Cependant, vous aurez accès à l'exportation de données et à la réactivation. followers: Cette action va déménager tou·te·s les abonné·e·s du compte actuel vers le nouveau compte only_redirect_html: Alternativement, vous pouvez seulement appliquer une redirection sur votre profil. + other_data: Aucune autre donnée ne sera déplacée automatiquement (y compris vos messages et la liste des comptes suivis) redirect: Le profil de votre compte actuel sera mis à jour avec un avis de redirection et sera exclu des recherches moderation: title: Modération @@ -1920,6 +1932,7 @@ fr: errors: in_reply_not_found: Le message auquel vous essayez de répondre ne semble pas exister. quoted_status_not_found: Le message que vous essayez de citer ne semble pas exister. + quoted_user_not_mentioned: Impossible de citer un compte non mentionné dans une mention privée. over_character_limit: limite de %{max} caractères dépassée pin_errors: direct: Les messages qui ne sont visibles que pour les utilisateur·rice·s mentionné·e·s ne peuvent pas être épinglés @@ -1988,6 +2001,8 @@ fr: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + future_preamble_html: Nous avons modifié nos conditions d'utilisation, ces changements entreront en vigueur le %{date}. Nous vous invitons à prendre connaissance des nouvelles conditions. + past_preamble_html: Nous avons modifié nos conditions d'utilisation depuis votre dernière visite. Nous vous invitons à prendre connaissance des nouvelles conditions. review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: diff --git a/config/locales/he.yml b/config/locales/he.yml index 53940fef51..584193d981 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1784,16 +1784,22 @@ he: body: 'התקבלה פניה עבורך מאת %{name} ב:' subject: התקבלה פניה עבורך מאת %{name} title: אזכור חדש + moderation_warning: + subject: קיבלת אזהרה מצוות ניהול התוכן poll: subject: סקר מאת %{name} הסתיים quote: body: 'הודעתך צוטטה על ידי %{name}:' subject: "%{name} ציטט.ה את הודעתך" title: ציטוט חדש + quoted_update: + subject: "%{name} ערך הודעה שהשתמשת בה בציטוט" reblog: body: 'הודעתך הודהדה על ידי %{name}:' subject: הודעתך הודהדה על ידי%{name} title: הדהוד חדש + severed_relationships: + subject: איבדת קשרים עם משתמשים אחרים עקב החלטת צוות ניהול התוכן status: subject: "%{name} בדיוק פרסם" update: diff --git a/config/locales/hu.yml b/config/locales/hu.yml index a957165f26..ab49a9b231 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1706,6 +1706,8 @@ hu: body: "%{name} megemlített téged:" subject: "%{name} megemlített téged" title: Új említés + moderation_warning: + subject: Kaptál egy moderálási figyelmeztetést poll: subject: "%{name} szavazása véget ért" quote: diff --git a/config/locales/is.yml b/config/locales/is.yml index ba03643e1d..cec2023a87 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1710,16 +1710,22 @@ is: body: "%{name} minntist á þig í:" subject: "%{name} minntist á þig" title: Ný tilvísun + moderation_warning: + subject: Þú hefur fengið aðvörun frá umsjónarmanni poll: subject: Könnun frá %{name} er lokið quote: body: "%{name} vitnaði í færsluna þína:" subject: "%{name} vitnaði í færsluna þína" title: Ný tilvitnun + quoted_update: + subject: "%{name} breytti færslu sem þú hefur vitnað í" reblog: body: "%{name} endurbirti færsluna þína:" subject: "%{name} endurbirti færsluna þína" title: Ný endurbirting + severed_relationships: + subject: Þú hefur tapað tengingum vegna ákvörðunar umsjónarmanna status: subject: "%{name} sendi inn rétt í þessu" update: diff --git a/config/locales/lt.yml b/config/locales/lt.yml index d9eaf61e37..4a6964dfd5 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -1,7 +1,7 @@ --- lt: about: - about_mastodon_html: 'Ateities socialinis tinklas: jokių reklamų ir įmonių sekimo, etiškas dizainas bei decentralizacija! Turėkite savo duomenis su „Mastodon“.' + about_mastodon_html: 'Ateities socialinis tinklas: jokių reklamų ir įmonių sekimo, etiškas dizainas bei decentralizacija! Turėkite savo duomenis su „Mastodon“!' contact_missing: Nenustatyta contact_unavailable: Nėra hosted_on: "„Mastodon“ talpinamas domene %{domain}" @@ -25,6 +25,7 @@ lt: one: Įrašas other: Įrašų posts_tab_heading: Įrašai + self_follow_error: Neleidžiama sekti savo pačių paskyros admin: account_actions: action: Atlikti veiksmą @@ -36,8 +37,9 @@ lt: created_msg: Prižiūrėjimo pastaba sėkmingai sukurta! destroyed_msg: Prižiūrėjimo pastaba sėkmingai sunaikinta! accounts: + add_email_domain_block: Blokuoti el. pašto domeną approve: Patvirtinti - approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška. + approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška are_you_sure: Ar esi įsitikinęs (-usi)? avatar: Avataras by_domain: Domenas @@ -47,13 +49,13 @@ lt: label: Keisti el. paštą new_email: Naujas el. paštas submit: Keisti el. paštą - title: Keisti el. paštą %{username} + title: Keisti el. paštą vartotjui %{username} change_role: changed_msg: Vaidmuo sėkmingai pakeistas. edit_roles: Tvarkyti naudotojų vaidmenis label: Keisti vaidmenį no_role: Jokios vaidmenį - title: Keisti vaidmenį %{username} + title: Keisti teises vartotojui %{username} confirm: Patvirtinti confirmed: Patvirtinta confirming: Patvirtinama @@ -61,7 +63,7 @@ lt: delete: Ištrinti duomenis deleted: Ištrinta demote: Pažeminti - destroyed_msg: "%{username} duomenys dabar laukia eilėje, kad būtų netrukus ištrinti." + destroyed_msg: "%{username} duomenys dabar laukia eilėje, kad būtų netrukus ištrinti" disable: Sustabdyti disable_two_factor_authentication: Išjungti 2FA disabled: Pristabdyta @@ -72,7 +74,7 @@ lt: email_status: El. pašto būsena enable: Panaikinti sustabdymą enabled: Įjungta - enabled_msg: Sėkmingai panaikintas %{username} paskyros sustabdymas. + enabled_msg: Sėkmingai %{username} paskyros sustabdymas panaikintas followers: Sekėjai follows: Seka header: Antraštė @@ -90,7 +92,7 @@ lt: media_attachments: Medijos priedai memorialize: Paversti į memorialinį memorialized: Memorializuota - memorialized_msg: Sėkmingai paversta %{username} į memorialinę paskyrą. + memorialized_msg: Sėkmingai %{username} paskyra paversta į memorialinę moderation: active: Aktyvus all: Visi @@ -119,15 +121,15 @@ lt: public: Viešas push_subscription_expires: PuSH prenumerata baigiasi redownload: Atnaujinti profilį - redownloaded_msg: Sėkmingai atnaujintas %{username} profilis iš kilmės šaltinio. + redownloaded_msg: Sėkmingai atnaujintas %{username} profilis iš pradinio šaltinio reject: Atmesti - rejected_msg: Sėkmingai atmesta %{username} registracijos paraiška. + rejected_msg: Sėkmingai atmesta %{username} registracijos paraiška remote_suspension_irreversible: Šios paskyros duomenys negrįžtamai ištrinti. remote_suspension_reversible_hint_html: Paskyra jų serveryje buvo pristabdyta, o duomenys bus visiškai pašalinti %{date}. Iki to laiko nuotolinis serveris gali atkurti šią paskyrą be jokių neigiamų pasekmių. Jei nori iš karto pašalinti visus paskyros duomenis, gali tai padaryti toliau. remove_avatar: Pašalinti avatarą remove_header: Pašalinti antraštę - removed_avatar_msg: Sėkmingai pašalintas %{username} avataro vaizdas. - removed_header_msg: Sėkmingai pašalintas %{username} antraštės vaizdas. + removed_avatar_msg: Sėkmingai pašalintas %{username} avataro vaizdas + removed_header_msg: Sėkmingai pašalintas %{username} antraštės paveikslėlis resend_confirmation: already_confirmed: Šis naudotojas jau patvirtintas. send: Iš naujo siųsti patvirtinimo nuorodą @@ -617,11 +619,13 @@ lt: no_status_selected: Jokie įrašai nebuvo pakeisti, nes nė vienas buvo pasirinktas open: Atidaryti įrašą original_status: Originalus įrašas + quotes: Paminėjimai replied_to_html: Atsakyta į %{acct_link} status_title: Paskelbė @%{name} title: Paskyros įrašai – @%{name} trending: Tendencinga view_publicly: Peržiūrėti viešai + view_quoted_post: Peržiūrėti paminėtą įrašą with_media: Su medija system_checks: database_schema_check: @@ -876,6 +880,7 @@ lt: security: Apsauga set_new_password: Nustatyti naują slaptažodį setup: + email_below_hint_html: Patikrinkite savo šlamšto aplanką arba paprašykite naujo el. laiško. Jei el. pašto adresas neteisingas, galite jį pataisyti. email_settings_hint_html: Spustelėkite nuorodą, kurią atsiuntėme adresu %{email}, kad pradėtumėte naudoti „Mastodon“. Lauksime čia. link_not_received: Negavai nuorodos? title: Patikrinti pašto dėžutę @@ -930,7 +935,9 @@ lt: your_appeal_pending: Pateikei apeliaciją your_appeal_rejected: Tavo apeliacija buvo atmesta edit_profile: + basic_information: Pagrindinė informacija hint_html: "Tinkink tai, ką žmonės mato tavo viešame profilyje ir šalia įrašų. Kiti žmonės labiau linkę sekti atgal ir bendrauti su tavimi, jei tavo profilis yra užpildytas ir turi profilio nuotrauką." + other: Kita emoji_styles: auto: Automatinis native: Vietiniai @@ -1075,6 +1082,10 @@ lt: body: 'Tave %{name} paminėjo:' subject: Tave paminėjo %{name} title: Naujas paminėjimas + quote: + body: 'Tavo įrašą paminėjo %{name}:' + subject: "%{name} paminėjo jūsų įrašą" + title: Naujas paminėjimas reblog: body: 'Tavo įrašą pakėlė %{name}:' subject: "%{name} pakėlė tavo įrašą" @@ -1208,11 +1219,22 @@ lt: content_warning: 'Turinio įspėjimas: %{warning}' errors: quoted_status_not_found: Įrašas, kurį bandote cituoti, atrodo, neegzistuoja. + quoted_user_not_mentioned: Privačiame paminėjime negalima cituoti citatoje nepaminėto vartotojo. over_character_limit: pasiektas %{max} simbolių limitas pin_errors: limit: Jūs jau prisegėte maksimalų toot'ų skaičų ownership: Kitų vartotojų toot'ai negali būti prisegti reblog: Pakeltos žinutės negali būti prisegtos + quote_error: + not_available: Įrašas nepasiekiamas + pending_approval: Įrašas peržiūrimas + revoked: Autorius pašalino įrašą + quote_policies: + followers: Tik sekėjai + nobody: Tik aš + public: Visi + quote_post_author: Paminėjo %{acct} įrašą + title: '%{name}: "%{quote}"' visibilities: public: Vieša statuses_cleanup: diff --git a/config/locales/nan.yml b/config/locales/nan.yml index b9e5c09159..5c8109b214 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -31,7 +31,7 @@ nan: created_msg: 管理記錄成功建立! destroyed_msg: 管理記錄成功thâi掉! accounts: - add_email_domain_block: 封鎖電子phue ê網域 + add_email_domain_block: 封鎖電子phue ê域名 approve: 允准 approved_msg: 成功審核 %{username} ê註冊申請ah are_you_sure: Lí kám確定? @@ -412,7 +412,7 @@ nan: stop_communication: Lí ê服侍器ē停止kap hia ê服侍器聯絡。 title: 確認封鎖域名 %{domain} undo_relationships: Tse ē取消任何ê佇in ê服侍器ê口座kap lí ê之間ê跟tuè關係。 - created_msg: 當leh封鎖網域 + created_msg: 當leh封鎖域名 destroyed_msg: 已經取消封鎖域名 domain: 域名 edit: 編輯域名封鎖 @@ -457,12 +457,12 @@ nan: new: create: 加添域名 resolve: 解析域名 - title: 封鎖新ê電子phue網域 + title: 封鎖新ê電子phue ê域名 no_email_domain_block_selected: 因為無揀任何電子phue域名封鎖,所以lóng無改變 not_permitted: 無允准 resolved_dns_records_hint_html: 域名解析做下kha ê MX域名,tsiah ê域名上後負責收電子phue。封鎖MX域名ē封任何有siâng款MX域名ê電子郵件ê註冊,就算通看見ê域名無kâng,mā án-ne。Tio̍h細膩,m̄通封鎖主要ê電子phue提供者。 resolved_through_html: 通過 %{domain} 解析 - title: 封鎖ê電子phue網域 + title: 封鎖ê電子phue域名 export_domain_allows: new: title: 輸入允准ê域名 @@ -659,7 +659,7 @@ nan: are_you_sure: Lí kám確定? assign_to_self: 分配hōo家kī assigned: 分配管理者 - by_target_domain: 受檢舉ê口座ê網域 + by_target_domain: 受檢舉ê口座ê域名 cancel: 取消 category: 類別 category_description_html: Tsit ê 受檢舉ê口座kap/á是內容,ē佇kap tsit ê口座ê聯絡內底引用。 @@ -1362,6 +1362,68 @@ nan: sensitive: Kā 口座文標做敏感ê silence: 口座制限 suspend: 停止口座權限 + your_appeal_approved: Lí ê投訴受允准 + your_appeal_pending: Lí有送投訴 + your_appeal_rejected: Lí ê投訴已經受拒絕 + edit_profile: + basic_information: 基本ê資訊 + hint_html: "自訂lâng佇lí ê個人資料kap lí ê Po文邊仔所通看ê。Nā是lí有添好個人資料kap標á,別lâng較有可能kā lí跟tuè轉去,kap lí互動。" + other: 其他 + emoji_styles: + auto: 自動 + native: 原底ê + twemoji: Twemoji + errors: + '400': Lí所送ê請求無效,á是格式毋著。 + '403': Lí bô權限來看tsit頁。 + '404': Lí teh tshuē ê頁無佇leh。 + '406': Tsit頁bē當照lí所請求ê格式提供。 + '410': Lí所tshuē ê頁,無佇tsia ah。 + '422': + content: 安全驗證出tshê。Lí kám有封鎖cookie? + title: 安全驗證失敗 + '429': 請求siunn tsē + '500': + content: 歹勢,guán ê後臺出問題ah。 + title: Tsit頁無正確 + '503': Tsit頁bē當提供,因為服侍器暫時出tshê。 + noscript_html: Beh用Mastodon web app,請拍開JavaScript。另外,請試Mastodon hōo lí ê平臺用ê app。 + existing_username_validator: + not_found: bē當用tsit ê名tshuē著本地ê用者 + not_found_multiple: tshuē無 %{usernames} + exports: + archive_takeout: + date: 日期 + download: Kā lí ê檔案載落 + hint_html: Lí ē當請求lí ê PO文kap所傳ê媒體ê檔案。輸出ê資料ē用ActivityPub格式,通hōo適用ê軟體讀。Lí ē當每7 kang請求tsi̍t kái。 + in_progress: Teh備辦lí ê檔案…… + request: 請求lí ê檔案 + size: Sài-suh + blocks: Lí封鎖ê + bookmarks: 冊籤 + csv: CSV + domain_blocks: 域名封鎖 + lists: 列單 + mutes: Lí消音ê + storage: 媒體儲存 + featured_tags: + add_new: 加新ê + errors: + limit: Lí已經kā hashtag推薦kàu盡磅ah。 + hint_html: "佇個人資料推薦lí上重要ê hashtag。Tse是誠好ê家私,通the̍h來追蹤lí ê創意作品kap長期計畫。推薦ê hashtag ē佇lí ê個人資料顯目展示,koh允准緊緊接近使用lí家tī ê PO文。" + filters: + contexts: + account: 個人資料 + home: Tshù kap列單 + notifications: 通知 + public: 公共ê時間線 + thread: 會話 + edit: + add_keyword: 加關鍵字 + keywords: 關鍵字 + statuses: 個別ê PO文 + statuses_hint_html: Tsit ê過濾器應用佇所揀ê個別ê PO文,毋管in敢有符合下kha ê關鍵字重頭看,á是kā PO文tuì tsit ê過濾器suá掉。 + title: 編輯過濾器 scheduled_statuses: too_soon: Tio̍h用未來ê日期。 statuses: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 19adee033f..b92cff5f90 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1706,16 +1706,22 @@ nl: body: 'Je bent door %{name} vermeld in:' subject: Je bent vermeld door %{name} title: Nieuwe vermelding + moderation_warning: + subject: Je hebt een moderatie-waarschuwing ontvangen poll: subject: Een peiling van %{name} is beëindigd quote: body: 'Jouw bericht werd door %{name} geciteerd:' subject: "%{name} heeft jouw bericht geciteerd" title: Nieuw citaat + quoted_update: + subject: "%{name} bewerkte een door jou geciteerd bericht" reblog: body: 'Jouw bericht werd door %{name} geboost:' subject: "%{name} boostte jouw bericht" title: Nieuwe boost + severed_relationships: + subject: Door een moderatie-beslissing ben je volgers en/of door jou gevolgde accounts verloren status: subject: "%{name} heeft zojuist een bericht geplaatst" update: diff --git a/config/locales/nn.yml b/config/locales/nn.yml index aad56c92e9..efcd226f31 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1090,7 +1090,7 @@ nn: tag_uses_measure: brukarar totalt description_html: Dette er emneknagger som for øyeblikket vises i mange innlegg som serveren din ser. Det kan hjelpe dine brukere med å finne ut hva folk snakker mest om i øyeblikket. Ingen emneknagger vises offentlig før du godkjenner dem. listable: Kan bli foreslått - no_tag_selected: Ingen merkelappar vart endra fordi ingen var valde + no_tag_selected: Ingen emneknaggar vart endra fordi ingen var valde not_listable: Vil ikke bli foreslått not_trendable: Kjem ikkje til å syna under trendar not_usable: Kan ikke brukes @@ -1706,16 +1706,22 @@ nn: body: 'Du vart nemnd av %{name} i:' subject: Du vart nemnd av %{name} title: Ny omtale + moderation_warning: + subject: Du har fått ei moderasjonsåtvaring poll: subject: Meiningsmålinga frå %{name} er avslutta quote: body: 'Innlegget ditt vart sitert av %{name}:' subject: "%{name} siterte innlegget ditt" title: Nytt sitat + quoted_update: + subject: "%{name} redigerte eit innlegg du har sitert" reblog: body: 'Statusen din vart framheva av %{name}:' subject: "%{name} framheva statusen din" title: Ny framheving + severed_relationships: + subject: Du har tapte tilkoplingar på grunn av ei avgjerd frå ein moderator status: subject: "%{name} postet nettopp" update: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 9bda819e7b..4dea9d51d2 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1706,16 +1706,22 @@ pt-BR: body: "%{name} te mencionou em:" subject: "%{name} te mencionou" title: Nova menção + moderation_warning: + subject: Você recebeu um aviso de moderação poll: subject: Uma enquete por %{name} terminou quote: body: 'Sua publicação foi Citada por %{name}:' subject: "%{name} citou sua publicação" title: Nova citação + quoted_update: + subject: "%{name} editou uma publicação que você citou" reblog: body: "%{name} impulsionou a sua publicação:" subject: "%{name} impulsionou a sua publicação" title: Novo impulso + severed_relationships: + subject: Você perdeu conexões devido a uma decisão da moderação status: subject: "%{name} acabou de publicar" update: @@ -1734,6 +1740,7 @@ pt-BR: quadrillion: QUA thousand: MIL trillion: TRI + unit: '' otp_authentication: code_hint: Digite o código gerado pelo seu aplicativo autenticador para confirmar description_html: Se você ativar a autenticação de dois fatores usando um aplicativo autenticador, ao se conectar será exigido que você esteja com o seu telefone, que gerará tokens para você entrar. diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index 32afa809e9..5aca8c4ec4 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1706,16 +1706,22 @@ pt-PT: body: 'Foste mencionado por %{name}:' subject: "%{name} mencionou-te" title: Nova menção + moderation_warning: + subject: Recebeste um aviso da moderação poll: subject: A sondagem de %{name} terminou quote: body: 'A sua publicação foi citada por %{name}:' subject: "%{name} citou a sua publicação" title: Nova citação + quoted_update: + subject: "%{name} editou uma publicação que citaste" reblog: body: 'A tua publicação foi partilhada por %{name}:' subject: "%{name} partilhou a sua publicação" title: Novo impulso + severed_relationships: + subject: Perdeste ligações devido a uma decisão da moderação status: subject: "%{name} acabou de publicar" update: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index fa81b360fa..4e61fd52b3 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -29,9 +29,9 @@ ru: admin: account_actions: action: Выполнить действие - already_silenced: Эта учетная запись уже ограничена. - already_suspended: Эта учетная запись уже заблокирована. - title: Осуществить модерацию в отношении учётной записи %{acct} + already_silenced: Эта учётная запись уже ограничена. + already_suspended: Эта учётная запись уже заблокирована. + title: Произвести модерацию учётной записи %{acct} account_moderation_notes: create: Создать заметку created_msg: Заметка для модераторов добавлена @@ -63,9 +63,9 @@ ru: delete: Удалить данные deleted: Удалена demote: Разжаловать - destroyed_msg: Данные %{username} поставлены в очередь на удаление + destroyed_msg: Данные %{username} будут удалены в ближайшее время disable: Отключить - disable_sign_in_token_auth: Отключить аутентификацию по e-mail кодам + disable_sign_in_token_auth: Отключить аутентификацию с помощью одноразового пароля по эл. почте disable_two_factor_authentication: Отключить 2FA disabled: Отключена display_name: Отображаемое имя @@ -74,9 +74,9 @@ ru: email: Адрес электронной почты email_status: Подтверждение учётной записи enable: Включить - enable_sign_in_token_auth: Включить аутентификацию по e-mail кодам - enabled: Включен - enabled_msg: Учётная запись %{username} успешно разморожена + enable_sign_in_token_auth: Включить аутентификацию с помощью одноразового пароля по эл. почте + enabled: Включена + enabled_msg: Отменено отключение учётной записи %{username} followers: Подписчики follows: Подписки header: Обложка профиля @@ -92,9 +92,9 @@ ru: title: По расположению login_status: Состояние учётной записи media_attachments: Медиавложения - memorialize: Сделать мемориалом - memorialized: In memoriam - memorialized_msg: "%{username} успешно превращён в памятник" + memorialize: Почтить память + memorialized: Мемориальная + memorialized_msg: Учётная запись %{username} превращена в мемориальную moderation: active: Действующие all: Все @@ -109,7 +109,7 @@ ru: no_account_selected: Ничего не изменилось, так как ни одна учётная запись не была выделена no_limits_imposed: Ограничения отсутствуют no_role_assigned: Роль не присвоена - not_subscribed: Не подписаны + not_subscribed: Подписка отсутствует pending: Ожидает одобрения perform_full_suspension: Заблокировать previous_strikes: Зафиксированные нарушения @@ -118,9 +118,9 @@ ru: many: За этой учётной записью числится %{count} нарушений. one: За этой учётной записью числится %{count} нарушение. other: За этой учётной записью числится %{count} нарушений. - promote: Повысить + promote: Сделать администратором protocol: Протокол - public: Публичный + public: Профиль push_subscription_expires: Подписка PuSH истекает redownload: Обновить аватар redownloaded_msg: Профиль %{username} успешно обновлен из оригинала @@ -138,7 +138,7 @@ ru: success: Ссылка для подтверждения успешно отправлена! reset: Сбросить reset_password: Сбросить пароль - resubscribe: Переподписаться + resubscribe: Пересоздать подписку role: Роль search: Поиск search_same_email_domain: Другие пользователи с тем же доменом эл. почты @@ -169,9 +169,9 @@ ru: undo_sensitized: Не скрывать медиа undo_silenced: Не ограничивать undo_suspension: Разблокировать - unsilenced_msg: Ограничения с учётной записи %{username} сняты успешно + unsilenced_msg: Отменено ограничение учётной записи %{username} unsubscribe: Отписаться - unsuspended_msg: Учётная запись %{username} успешно разморожена + unsuspended_msg: Учётная запись %{username} разблокирована username: Имя пользователя view_domain: Просмотр сводки по домену warn: Предупредить @@ -214,11 +214,11 @@ ru: disable_2fa_user: Отключение 2FA disable_custom_emoji: Отключение эмодзи disable_relay: Отключение ретранслятора - disable_sign_in_token_auth_user: Отключение аутентификации по e-mail кодам + disable_sign_in_token_auth_user: Отключение аутентификации с помощью одноразового пароля по эл. почте disable_user: Заморозка пользователей enable_custom_emoji: Включение эмодзи enable_relay: Включение ретранслятора - enable_sign_in_token_auth_user: Включение аутентификации по e-mail кодам + enable_sign_in_token_auth_user: Включение аутентификации с помощью одноразового пароля по эл. почте enable_user: Разморозка пользователей memorialize_account: Присвоение пользователям статуса «мемориала» promote_user: Повышение пользователей @@ -282,11 +282,11 @@ ru: disable_2fa_user_html: "%{name} отключил(а) требование двухэтапной авторизации для пользователя %{target}" disable_custom_emoji_html: "%{name} отключил(а) эмодзи %{target}" disable_relay_html: "%{name} отключил(а) ретранслятор %{target}" - disable_sign_in_token_auth_user_html: "%{name} отключил(а) аутентификацию по e-mail кодам для %{target}" + disable_sign_in_token_auth_user_html: "%{name} отключил(а) аутентификацию с помощью одноразового пароля по эл. почте для %{target}" disable_user_html: "%{name} заморозил(а) пользователя %{target}" enable_custom_emoji_html: "%{name} включил(а) эмодзи %{target}" enable_relay_html: "%{name} включил(а) ретранслятор %{target}" - enable_sign_in_token_auth_user_html: "%{name} включил(а) аутентификацию по e-mail кодам для %{target}" + enable_sign_in_token_auth_user_html: "%{name} включил(а) аутентификацию с помощью одноразового пароля по эл. почте для %{target}" enable_user_html: "%{name} разморозил(а) пользователя %{target}" memorialize_account_html: "%{name} перевел(а) учётную запись пользователя %{target} в статус памятника" promote_user_html: "%{name} повысил(а) пользователя %{target}" @@ -342,7 +342,7 @@ ru: unpublish: Скрыть unpublished_msg: Объявление скрыто updated_msg: Объявление отредактировано - critical_update_pending: Ожидается обновление критического уровня + critical_update_pending: Доступно критическое обновление custom_emojis: assign_category: Задать категорию by_domain: Домен @@ -384,25 +384,25 @@ ru: new_users: новые пользователи opened_reports: новые жалобы pending_appeals_html: - few: "%{count} ожидают аппеляции" - many: "%{count} ожидают апелляции" - one: "%{count} ожидает апелляции" - other: 'Ожидают апелляции: %{count}' + few: "%{count} открытые апелляции" + many: "%{count} открытых апелляций" + one: "%{count} открытая апелляция" + other: "%{count} открытых апелляций" pending_reports_html: - few: "%{count} ожидающих отчета" - many: "%{count} ожидающих отчетов" - one: "%{count} ожидающий отчет" - other: "%{count} ожидающих отчетов" + few: "%{count} открытые жалобы" + many: "%{count} открытых жалоб" + one: "%{count} открытая жалоба" + other: "%{count} открытых жалоб" pending_tags_html: - few: "%{count} ожидающих хэштега" - many: "%{count} ожидающих хэштегов" - one: "%{count} ожидающий хэштег" - other: "%{count} ожидающих хэштегов" + few: "%{count} нерассмотренных хештега" + many: "%{count} нерассмотренных хештегов" + one: "%{count} нерассмотренный хештег" + other: "%{count} нерассмотренных хештегов" pending_users_html: - few: "%{count} ожидающих пользователя" - many: "%{count} ожидающих пользователей" - one: "%{count} ожидающий пользователь" - other: "%{count} ожидающих пользователей" + few: "%{count} заявки на регистрацию" + many: "%{count} заявок на регистрацию" + one: "%{count} заявка на регистрацию" + other: "%{count} заявок на регистрацию" resolved_reports: жалоб решено software: Программное обеспечение sources: Источники регистрации @@ -416,12 +416,12 @@ ru: empty: Апелляций не найдено. title: Апелляции domain_allows: - add_new: Внести в белый список - created_msg: Домен добавлен в белый список - destroyed_msg: Домен убран из белого списка + add_new: Внести домен в белый список + created_msg: Домен внесён в белый список, федерация с ним теперь разрешена + destroyed_msg: Домен исключён из белого списка, федерация с ним больше не разрешена export: Экспорт import: Импорт - undo: Убрать из белого списка + undo: Исключить из белого списка domain_blocks: add_new: Заблокировать домен confirm_suspension: @@ -468,28 +468,28 @@ ru: add_new: Добавить allow_registrations_with_approval: Разрешить регистрацию с одобрением attempts_over_week: - few: "%{count} попытки за последнюю неделю" - many: "%{count} попыток за последнюю неделю" - one: "%{count} попытка за последнюю неделю" + few: "%{count} попытки регистрации за последнюю неделю" + many: "%{count} попыток регистрации за последнюю неделю" + one: "%{count} попытка регистрации за последнюю неделю" other: "%{count} попыток регистрации за последнюю неделю" - created_msg: Домен email забанен, ура - delete: Удалить + created_msg: Домен электронной почты заблокирован + delete: Разблокировать dns: types: mx: Запись MX domain: Домен new: - create: Создать блокировку - resolve: Проверить домен - title: Блокировка нового почтового домена - no_email_domain_block_selected: Блокировки почтовых доменов не были изменены, так как ни один из них не был выбран - not_permitted: Не разрешено - resolved_dns_records_hint_html: Доменное имя указывает на следующие MX-домены, которые в конечном итоге отвечают за прием электронной почты. Блокировка MX-домена будет блокировать регистрации с любого адреса электронной почты, который использует тот же MX-домен, даже если видимое доменное имя отличается от него. Будьте осторожны, чтобы не заблокировать основных провайдеров электронной почты - resolved_through_html: Разрешено через %{domain} + create: Заблокировать домен + resolve: Проверить DNS-записи + title: Заблокировать домен электронной почты + no_email_domain_block_selected: Ничего не изменилось, так как ни один почтовый домен не был выделен + not_permitted: Недостаточно прав + resolved_dns_records_hint_html: Доменное имя указывает на следующие MX-домены, которые в конечном итоге отвечают за приём электронной почты. Блокировка MX-домена приведёт к блокировке регистраций со всех адресов электронной почты, которые используют тот же MX-домен, даже если видимое доменное имя отличается от него. Будьте осторожны, чтобы не заблокировать основных провайдеров электронной почты + resolved_through_html: Из DNS-записей домена %{domain} title: Блокировки по домену эл. почты export_domain_allows: new: - title: Импорт домена разрешён + title: Импорт белого списка доменов no_file: Файл не выбран export_domain_blocks: import: @@ -527,12 +527,12 @@ ru: status: Пост title: FASP follow_recommendations: - description_html: "Следуйте рекомендациям, чтобы помочь новым пользователям быстро находить интересный контент. Если пользователь не взаимодействовал с другими в достаточной степени, чтобы сформировать персонализированные рекомендации, вместо этого рекомендуется использовать эти учетные записи. Они пересчитываются на ежедневной основе на основе комбинации аккаунтов с наибольшим количеством недавних взаимодействий и наибольшим количеством местных подписчиков для данного языка." - language: Для языка - status: Статус + description_html: "Рекомендации профилей помогают новым пользователям быстрее найти что-нибудь интересное. Если пользователь мало взаимодействовал с другими и составить персонализированные рекомендации не получается, будут предложены указанные здесь профили. Эти рекомендации обновляются ежедневно из совокупности учётных записей с наибольшим количеством недавних взаимодействий и наибольшим количеством подписчиков с этого сервера для данного языка." + language: Фильтр по языку + status: Фильтр по состоянию suppress: Скрыть рекомендацию suppressed: Скрыта - title: Рекомендации подписок + title: Рекомендации профилей unsuppress: Восстановить рекомендацию instances: audit_log: @@ -1666,6 +1666,7 @@ ru: authentication_methods: otp: приложения для генерации кодов password: пароля + sign_in_token: одноразового пароля по эл. почте webauthn: электронного ключа description_html: Если вы заметили действия, которых не совершали, вам следует сменить пароль и включить двухфакторную аутентификацию. empty: История входов отсутствует diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 392d7076d1..70d4296f2e 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -91,23 +91,23 @@ de: bootstrap_timeline_accounts: Diese Konten werden an den Anfang der Follow-Empfehlungen für neue Nutzer angeheftet. Gib eine durch Kommata getrennte Liste von Konten an. closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist. - custom_css: Du kannst benutzerdefinierte Stile auf die Web-Version von Mastodon anwenden. + custom_css: Du kannst eigene Stylesheets für das Webinterface von Mastodon verwenden. favicon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-Mastodon-Favicon mit einem eigenen Symbol. landing_page: Legt fest, welche Seite neue Besucher*innen sehen, wenn sie zum ersten Mal auf deinem Server ankommen. Für „Trends“ müssen die Trends in den Entdecken-Einstellungen aktiviert sein. Für „Lokaler Feed“ muss „Zugriff auf Live-Feeds, die lokale Beiträge beinhalten“ in den Entdecken-Einstellungen auf „Alle“ gesetzt werden. - mascot: Überschreibt die Abbildung in der erweiterten Weboberfläche. + mascot: Überschreibt die Abbildung im erweiterten Webinterface. media_cache_retention_period: Mediendateien aus Beiträgen von externen Nutzer*innen werden auf deinem Server zwischengespeichert. Wenn ein positiver Wert gesetzt ist, werden die Medien nach der festgelegten Anzahl von Tagen gelöscht. Sollten die Medien nach dem Löschvorgang wieder angefragt werden, werden sie erneut heruntergeladen, sofern der ursprüngliche Inhalt noch vorhanden ist. Es wird empfohlen, diesen Wert auf mindestens 14 Tage festzulegen, da die Häufigkeit der Abfrage von Linkvorschaukarten für Websites von Dritten begrenzt ist und die Linkvorschaukarten sonst nicht vor Ablauf dieser Zeit aktualisiert werden. min_age: Nutzer*innen werden bei der Registrierung aufgefordert, ihr Geburtsdatum zu bestätigen peers_api_enabled: Eine Liste von Domains, die diesem Server im Fediverse begegnet sind. Hierbei werden keine Angaben darüber gemacht, ob du mit einem bestimmten Server föderierst, sondern nur, dass dein Server davon weiß. Dies wird von Diensten verwendet, die allgemein Statistiken übers Ferdiverse sammeln. profile_directory: Dieses Verzeichnis zeigt alle Profile an, die sich dafür entschieden haben, entdeckt zu werden. - require_invite_text: Wenn Registrierungen eine manuelle Genehmigung erfordern, dann werden Nutzer einen Grund für ihre Registrierung angeben müssen + require_invite_text: Wenn Registrierungen eine manuelle Genehmigung erfordern, müssen Benutzer*innen ihren Beitrittswunsch begründen site_contact_email: Wie man dich bei rechtlichen oder Support-Anfragen erreichen kann. site_contact_username: Wie man dich auf Mastodon erreichen kann. site_extended_description: Alle zusätzlichen Informationen, die für Besucher*innen und deine Benutzer*innen nützlich sein könnten. Kann mit der Markdown-Syntax formatiert werden. site_short_description: Eine kurze Beschreibung zur eindeutigen Identifizierung des Servers. Wer betreibt ihn, für wen ist er bestimmt? - site_terms: Verwende eine eigene Datenschutzerklärung oder lasse das Feld leer, um die allgemeine Vorlage zu verwenden. Kann mit der Markdown-Syntax formatiert werden. + site_terms: Verwende eine eigene Datenschutzerklärung oder lass das Feld leer, um die allgemeine Vorlage zu verwenden. Kann mit der Markdown-Syntax formatiert werden. site_title: Wie Personen neben dem Domainnamen auf deinen Server verweisen können. status_page_url: Link zu einer Internetseite, auf der der Serverstatus während eines Ausfalls angezeigt wird - theme: Das Design, das abgemeldete Besucher und neue Benutzer sehen. + theme: Das Design, das nicht angemeldete Personen sehen. thumbnail: Ein Bild ungefähr im 2:1-Format, das neben den Server-Informationen angezeigt wird. trendable_by_default: Manuelles Überprüfen angesagter Inhalte überspringen. Einzelne Elemente können später noch aus den Trends entfernt werden. trends: Trends zeigen, welche Beiträge, Hashtags und Nachrichten auf deinem Server immer beliebter werden. @@ -161,7 +161,7 @@ de: color: Farbe, die für diese Rolle in der gesamten Benutzerschnittstelle verwendet wird, als RGB im Hexadezimalsystem highlighted: Dies macht die Rolle öffentlich im Profil sichtbar name: Name der Rolle, der auch öffentlich als Badge angezeigt wird, sofern dies unten aktiviert ist - permissions_as_keys: Benutzer*innen mit dieser Rolle haben Zugriff auf... + permissions_as_keys: Nutzer*innen mit dieser Rolle haben Zugriff auf … position: Höhere Rollen entscheiden über Konfliktlösungen zu gewissen Situationen. Bestimmte Aktionen können nur mit geringfügigeren Rollen durchgeführt werden username_block: allow_with_approval: Anstatt Registrierungen komplett zu verhindern, benötigen übereinstimmende Treffer eine Genehmigung @@ -282,7 +282,7 @@ de: activity_api_enabled: Aggregierte Nutzungsdaten über die API veröffentlichen app_icon: App-Symbol backups_retention_period: Aufbewahrungsfrist für Archive - bootstrap_timeline_accounts: Neuen Nutzern immer diese Konten empfehlen + bootstrap_timeline_accounts: Neuen Nutzer*innen immer diese Konten empfehlen closed_registrations_message: Nachricht, falls Registrierungen deaktiviert sind content_cache_retention_period: Aufbewahrungsfrist für externe Inhalte custom_css: Eigenes CSS @@ -290,7 +290,7 @@ de: landing_page: Landingpage für neue Besucher*innen local_live_feed_access: Zugriff auf Live-Feeds, die lokale Beiträge beinhalten local_topic_feed_access: Zugriff auf Hashtags und Links, die lokale Beiträge beinhalten - mascot: Benutzerdefiniertes Maskottchen (Legacy) + mascot: Eigenes Maskottchen (Veraltet) media_cache_retention_period: Aufbewahrungsfrist für Medien im Cache min_age: Erforderliches Mindestalter peers_api_enabled: Die entdeckten Server im Fediverse über die API veröffentlichen diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml index 20afb20fae..b41307f4fa 100644 --- a/config/locales/simple_form.fa.yml +++ b/config/locales/simple_form.fa.yml @@ -372,7 +372,9 @@ 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 63d63b3291..a89f4a86aa 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -19,7 +19,7 @@ fi: text: Voit käyttää julkaisun syntaksia, kuten URL-osoitteita, aihetunnisteita ja mainintoja title: Valinnainen. Ei näy vastaanottajalle admin_account_action: - include_statuses: Käyttäjä näkee, mitkä julkaisut johtivat moderointitoimeen tai -varoitukseen + include_statuses: Käyttäjä näkee, mitkä julkaisut johtivat moderointitoimeen tai -⁠varoitukseen send_email_notification: Käyttäjä saa selvityksen siitä, mitä hänen tililleen tapahtui text_html: Valinnainen. Voit käyttää julkaisun syntaksia. Voit lisätä varoitusasetuksia säästääksesi aikaa type_html: Valitse mitä teet käyttäjälle %{acct} @@ -28,7 +28,7 @@ fi: none: Käytä tätä lähettääksesi varoitus käyttäjälle käynnistämättä mitään muita toimia. sensitive: Pakota kaikki tämän käyttäjän mediatiedostot arkaluonteisiksi. silence: Estä käyttäjää lähettämästä julkaisuja, joiden näkyvyys on julkinen, sekä piilota hänen julkaisunsa ja häneen liittyvät ilmoitukset niiltä, jotka eivät seuraa häntä. Sulkee kaikki tiliin kohdistuvat raportit. - suspend: Estä kaikki vuorovaikutus tältä tililtä ja tälle tilille sekä poista kaikki sen sisältö. Peruttavissa 30 päivän ajan. Sulkee kaikki tiliin kohdistuvat raportit. + suspend: Estä kaikki vuorovaikutus tältä tililtä ja tälle tilille sekä poista kaikki sen sisältö. Peruttavissa 30 päivän ajan. Sulkee kaikki tiliin kohdistuvat raportit. warning_preset_id: Valinnainen. Voit silti lisätä mukautetun tekstin esiasetuksen loppuun announcement: all_day: Kun valittuna, vain aikavälin päivät näytetään @@ -48,7 +48,7 @@ fi: digest: Lähetetään vain pitkän poissaolon jälkeen ja vain, jos olet saanut suoria viestejä poissaolosi aikana email: Sinulle lähetetään vahvistusviesti header: WEBP, PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px - inbox_url: Kopioi URL-osoite haluamasi välittäjän etusivulta + inbox_url: Kopioi URL-⁠osoite haluamasi välittäjän etusivulta irreversible: Suodatetut julkaisut katoavat peruuttamattomasti, vaikka suodatin poistettaisiin myöhemmin locale: Käyttöliittymän, sähköpostiviestien ja puskuilmoitusten kieli password: Käytä vähintään 8:aa merkkiä @@ -65,12 +65,12 @@ fi: setting_display_media_hide_all: Piilota mediasisältö aina setting_display_media_show_all: Näytä mediasisältö aina setting_emoji_style: Miten emojit näkyvät. ”Automaattinen” pyrkii käyttämään natiiveja emojeita, mutta Twemoji-emojeita käytetään varavaihtoehtoina vanhoissa selaimissa. - setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen painaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -valikkoon. + setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen painaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -⁠valikkoon. setting_system_scrollbars_ui: Koskee vain Safari- ja Chrome-pohjaisia työpöytäselaimia setting_use_blurhash: Liukuvärit perustuvat piilotettujen kuvien väreihin mutta sumentavat yksityiskohdat setting_use_pending_items: Piilota aikajanan päivitykset napsautuksen taakse syötteen automaattisen vierityksen sijaan username: Voit käyttää kirjaimia, numeroita ja alaviivoja - whole_word: Kun avainsana tai -fraasi on täysin aakkosnumeerinen, suodatin aktivoituu vain sen täysvastineille + whole_word: Kun avainsana tai -⁠fraasi on kokonaan aakkosnumeerinen, suodatin tulee voimaan vain sen täysvastineille domain_allow: domain: Tämä verkkotunnus voi noutaa tietoja tältä palvelimelta, ja sieltä saapuvat tiedot käsitellään ja tallennetaan email_domain_block: @@ -99,7 +99,7 @@ fi: min_age: Käyttäjiä pyydetään rekisteröitymisen aikana vahvistamaan syntymäpäivänsä peers_api_enabled: Luettelo verkkotunnuksista, jotka tämä palvelin on kohdannut fediversumissa. Se ei kerro, federoitko tietyn palvelimen kanssa, vaan että palvelimesi on ylipäätään tietoinen siitä. Tätä tietoa käytetään palveluissa, jotka keräävät tilastoja federoinnista yleisellä tasolla. profile_directory: Profiilihakemisto luetteloi kaikki käyttäjät, jotka ovat valinneet olla löydettävissä. - require_invite_text: Kun rekisteröityminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” -tekstikentästä pakollinen vapaaehtoisen sijaan + require_invite_text: Kun rekisteröityminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” -⁠tekstikentästä pakollinen vapaaehtoisen sijaan site_contact_email: Miten sinut voi tavoittaa oikeus- tai tukikysymyksissä. site_contact_username: Miten sinut voi tavoittaa Mastodonissa. site_extended_description: Kaikki lisätiedot, jotka voivat olla hyödyllisiä kävijöille ja käyttäjille. Voidaan jäsentää Markdown-syntaksilla. @@ -146,7 +146,7 @@ fi: arbitration_address: Voi olla sama kuin edellä mainittu Fyysinen osoite tai ”N/A”, jos käytät sähköpostia. arbitration_website: Voi olla verkkolomake tai ”N/A”, jos käytät sähköpostia. choice_of_law: Kaupunki, alue, territorio tai valtio, jonka sisäinen aineellinen oikeus säätelee kaikkia vaatimuksia. - dmca_address: Yhdysvaltalaisten operaattoreiden on käytettävä DMCA Designated Agent Directory -luetteloon rekisteröityä osoitetta. Postilokeroluettelo on saatavissa suoralla pyynnöllä, joten käytä DMCA Designated Agent Post Office Box Waiver Request -lomaketta lähettääksesi sähköpostia tekijänoikeusvirastolle ja kuvaile, että olet kotona toimiva sisältömoderaattori, joka pelkää kostoa tai rangaistusta toimistaan ja tarvitsee postilokeroa pitääkseen kotiosoitteensa poissa julkisuudesta. + dmca_address: Yhdysvaltalaisten operaattoreiden on käytettävä DMCA Designated Agent Directory -⁠luetteloon rekisteröityä osoitetta. Postilokeroluettelo on saatavissa suoralla pyynnöllä, joten käytä DMCA Designated Agent Post Office Box Waiver Request -⁠lomaketta lähettääksesi sähköpostia tekijänoikeusvirastolle ja kuvaile, että olet kotona toimiva sisältömoderaattori, joka pelkää kostoa tai rangaistusta toimistaan ja tarvitsee postilokeroa pitääkseen kotiosoitteensa poissa julkisuudesta. dmca_email: Voi olla sama kuin edellä mainittu ”Sähköpostiosoite oikeudellisille ilmoituksille”. domain: Tarjoamasi verkkopalvelun yksilöllinen tunniste. jurisdiction: Mainitse valtio, jossa laskujen maksaja asuu. Jos kyseessä on yritys tai muu yhteisö, mainitse valtio, johon se on rekisteröity, ja tarvittaessa kaupunki, alue, territorio tai osavaltio. @@ -203,7 +203,7 @@ fi: announcement: all_day: Koko päivän tapahtuma ends_at: Tapahtuman loppu - scheduled_at: Ajoita julkaisu + scheduled_at: Ajasta julkaisu starts_at: Tapahtuman alku text: Tiedote appeal: @@ -224,7 +224,7 @@ fi: fields: Lisäkentät header: Otsakekuva honeypot: "%{label} (älä täytä)" - inbox_url: Välittäjän postilaatikon URL-osoite + inbox_url: Välittäjän postilaatikon URL-⁠osoite irreversible: Pudota piilottamisen sijaan locale: Käyttöliittymän kieli max_uses: Käyttökertoja enintään diff --git a/config/locales/simple_form.fr-CA.yml b/config/locales/simple_form.fr-CA.yml index 2fc9fd5314..265782086c 100644 --- a/config/locales/simple_form.fr-CA.yml +++ b/config/locales/simple_form.fr-CA.yml @@ -57,11 +57,15 @@ fr-CA: 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_boost_modal: Lorsque cette option est activée, avant de partager un message un écran de confirmation vous permettra de changer la visibilité du partage. + setting_default_quote_policy_private: Les messages limités aux personnes qui vous suivent publiés depuis Mastodon ne peuvent pas être cités. setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. setting_default_sensitive: Les médias sensibles sont cachés par défaut et peuvent être révélés d’un simple clic setting_display_media_default: Masquer les médias marqués comme sensibles setting_display_media_hide_all: Toujours masquer les médias setting_display_media_show_all: Toujours afficher les médias + setting_emoji_style: Manière d'afficher les émojis. Utiliser « Auto » pour essayer d'utiliser les émojis natifs, mais Twemoji sera utilisé pour les anciens navigateurs. + setting_quick_boosting_html: Lorsque cette option est activée, cliquer sur l'icône de partage %{boost_icon} va immédiatement partager le message au lieu d'ouvrir le menu déroulant Partage/Citation. L'action de citation est déplacée dans le menu %{options_icon} (options). setting_system_scrollbars_ui: S'applique uniquement aux navigateurs basés sur Safari et Chrome setting_use_blurhash: Les dégradés sont basés sur les couleurs des images cachées mais n’en montrent pas les détails setting_use_pending_items: Cacher les mises à jour des fils d’actualités derrière un clic, au lieu de les afficher automatiquement @@ -75,6 +79,7 @@ fr-CA: featured_tag: name: 'Voici quelques hashtags que vous avez utilisés récemment :' filters: + action: Choisir l'action à effectuer quand un message correspond au filtre actions: blur: Cacher les médias derrière un avertissement, sans cacher le texte hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas @@ -83,10 +88,12 @@ fr-CA: activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié. + bootstrap_timeline_accounts: Ces comptes seront épinglés en haut des recommandations pour les nouveaux utilisateurs. Accepte une liste de comptes séparés par des virgules. closed_registrations_message: Affiché lorsque les inscriptions sont fermées content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires. custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon. favicon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. + landing_page: Sélectionner la page à afficher aux nouveaux visiteurs quand ils arrivent sur votre serveur. Pour utiliser « Tendances » les tendances doivent être activées dans les paramètres de découverte. Pour utiliser « Fil local » le paramètre « Accès au flux en direct de ce serveur » doit être défini sur « Tout le monde » dans les paramètres de découverte. mascot: Remplace l'illustration dans l'interface Web avancée. media_cache_retention_period: Les fichiers médias des messages publiés par des utilisateurs distants sont mis en cache sur votre serveur. Lorsque cette valeur est positive, les médias sont supprimés au terme du nombre de jours spécifié. Si les données des médias sont demandées après leur suppression, elles seront téléchargées à nouveau, dans la mesure où le contenu source est toujours disponible. En raison des restrictions concernant la fréquence à laquelle les cartes de prévisualisation des liens interrogent des sites tiers, il est recommandé de fixer cette valeur à au moins 14 jours, faute de quoi les cartes de prévisualisation des liens ne seront pas mises à jour à la demande avant cette échéance. min_age: Les utilisateurs seront invités à confirmer leur date de naissance lors de l'inscription @@ -146,6 +153,9 @@ fr-CA: min_age: Ne doit pas être en dessous de l’âge minimum requis par les lois de votre juridiction. user: chosen_languages: Lorsque coché, seuls les messages dans les langues sélectionnées seront affichés sur les fils publics + date_of_birth: + one: Nous devons vérifier que vous avez au moins %{count} an pour utiliser %{domain}. Cette information ne sera pas conservée. + other: Nous devons vérifier que vous avez au moins %{count} ans pour utiliser %{domain}. Cette information ne sera pas conservée. role: Le rôle définit quelles autorisations a l'utilisateur⋅rice. user_role: color: Couleur à attribuer au rôle dans l'interface, au format hexadécimal RVB @@ -154,7 +164,9 @@ fr-CA: 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: + allow_with_approval: Au lieu de bloquer l'inscription, les inscriptions correspondantes nécessiteront votre approbation comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles + username: Correspondra peu importe la casse utilisée et pour les homoglyphes courants – par exemple « 4 » pour « a » ou « 3 » pour « e » 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. @@ -276,12 +288,16 @@ fr-CA: custom_css: CSS personnalisé favicon: Favicon landing_page: Page d'accueil pour les nouveaux visiteurs + local_live_feed_access: Accès au flux en direct de ce serveur + local_topic_feed_access: Accès aux flux hashtag et lien de ce serveur mascot: Mascotte personnalisée (héritée) media_cache_retention_period: Durée de rétention des médias dans le cache min_age: Âge minimum requis peers_api_enabled: Publie la liste des serveurs découverts dans l'API profile_directory: Activer l’annuaire des profils registrations_mode: Qui peut s’inscrire + remote_live_feed_access: Accès au flux en direct des autres serveurs + remote_topic_feed_access: Accès aux flux hashtag et lien des autres serveurs require_invite_text: Exiger une raison pour s’inscrire show_domain_blocks: Afficher les blocages de domaines show_domain_blocks_rationale: Montrer pourquoi les domaines ont été bloqués @@ -356,6 +372,7 @@ fr-CA: jurisdiction: Juridiction min_age: Âge minimum user: + date_of_birth_1i: Année date_of_birth_2i: Mois date_of_birth_3i: Jour role: Rôle diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 2b996ce94a..a40211f36d 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -57,11 +57,15 @@ fr: 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_boost_modal: Lorsque cette option est activée, avant de partager un message un écran de confirmation vous permettra de changer la visibilité du partage. + setting_default_quote_policy_private: Les messages limités aux personnes qui vous suivent publiés depuis Mastodon ne peuvent pas être cités. setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. setting_default_sensitive: Les médias sensibles sont cachés par défaut et peuvent être révélés d’un simple clic setting_display_media_default: Masquer les médias marqués comme sensibles setting_display_media_hide_all: Toujours masquer les médias setting_display_media_show_all: Toujours afficher les médias + setting_emoji_style: Manière d'afficher les émojis. Utiliser « Auto » pour essayer d'utiliser les émojis natifs, mais Twemoji sera utilisé pour les anciens navigateurs. + setting_quick_boosting_html: Lorsque cette option est activée, cliquer sur l'icône de partage %{boost_icon} va immédiatement partager le message au lieu d'ouvrir le menu déroulant Partage/Citation. L'action de citation est déplacée dans le menu %{options_icon} (options). setting_system_scrollbars_ui: S'applique uniquement aux navigateurs basés sur Safari et Chrome setting_use_blurhash: Les dégradés sont basés sur les couleurs des images cachées mais n’en montrent pas les détails setting_use_pending_items: Cacher les mises à jour des fils d’actualités derrière un clic, au lieu de les afficher automatiquement @@ -75,6 +79,7 @@ fr: featured_tag: name: 'Voici quelques hashtags que vous avez utilisés récemment :' filters: + action: Choisir l'action à effectuer quand un message correspond au filtre actions: blur: Cacher les médias derrière un avertissement, sans cacher le texte hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas @@ -83,10 +88,12 @@ fr: activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié. + bootstrap_timeline_accounts: Ces comptes seront épinglés en haut des recommandations pour les nouveaux utilisateurs. Accepte une liste de comptes séparés par des virgules. closed_registrations_message: Affiché lorsque les inscriptions sont fermées content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires. custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon. favicon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. + landing_page: Sélectionner la page à afficher aux nouveaux visiteurs quand ils arrivent sur votre serveur. Pour utiliser « Tendances » les tendances doivent être activées dans les paramètres de découverte. Pour utiliser « Fil local » le paramètre « Accès au flux en direct de ce serveur » doit être défini sur « Tout le monde » dans les paramètres de découverte. mascot: Remplace l'illustration dans l'interface Web avancée. media_cache_retention_period: Les fichiers médias des messages publiés par des utilisateurs distants sont mis en cache sur votre serveur. Lorsque cette valeur est positive, les médias sont supprimés au terme du nombre de jours spécifié. Si les données des médias sont demandées après leur suppression, elles seront téléchargées à nouveau, dans la mesure où le contenu source est toujours disponible. En raison des restrictions concernant la fréquence à laquelle les cartes de prévisualisation des liens interrogent des sites tiers, il est recommandé de fixer cette valeur à au moins 14 jours, faute de quoi les cartes de prévisualisation des liens ne seront pas mises à jour à la demande avant cette échéance. min_age: Les utilisateurs seront invités à confirmer leur date de naissance lors de l'inscription @@ -146,6 +153,9 @@ fr: min_age: Ne doit pas être en dessous de l’âge minimum requis par les lois de votre juridiction. user: chosen_languages: Lorsque coché, seuls les messages dans les langues sélectionnées seront affichés sur les fils publics + date_of_birth: + one: Nous devons vérifier que vous avez au moins %{count} an pour utiliser %{domain}. Cette information ne sera pas conservée. + other: Nous devons vérifier que vous avez au moins %{count} ans pour utiliser %{domain}. Cette information ne sera pas conservée. role: Le rôle définit quelles autorisations a l'utilisateur⋅rice. user_role: color: Couleur à attribuer au rôle dans l'interface, au format hexadécimal RVB @@ -154,7 +164,9 @@ fr: 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: + allow_with_approval: Au lieu de bloquer l'inscription, les inscriptions correspondantes nécessiteront votre approbation comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles + username: Correspondra peu importe la casse utilisée et pour les homoglyphes courants – par exemple « 4 » pour « a » ou « 3 » pour « e » 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. @@ -276,12 +288,16 @@ fr: custom_css: CSS personnalisé favicon: Favicon landing_page: Page d'accueil pour les nouveaux visiteurs + local_live_feed_access: Accès au flux en direct de ce serveur + local_topic_feed_access: Accès aux flux hashtag et lien de ce serveur mascot: Mascotte personnalisée (héritée) media_cache_retention_period: Durée de rétention des médias dans le cache min_age: Âge minimum requis peers_api_enabled: Publie la liste des serveurs découverts dans l'API profile_directory: Activer l’annuaire des profils registrations_mode: Qui peut s’inscrire + remote_live_feed_access: Accès au flux en direct des autres serveurs + remote_topic_feed_access: Accès aux flux hashtag et lien des autres serveurs require_invite_text: Exiger une raison pour s’inscrire show_domain_blocks: Afficher les blocages de domaines show_domain_blocks_rationale: Montrer pourquoi les domaines ont été bloqués @@ -356,6 +372,7 @@ fr: jurisdiction: Juridiction min_age: Âge minimum user: + date_of_birth_1i: Année date_of_birth_2i: Mois date_of_birth_3i: Jour role: Rôle diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 8c06f28510..dc4bb1354f 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -56,11 +56,13 @@ ja: scopes: アプリの API に許可するアクセス権を選択してください。最上位のスコープを選択する場合、個々のスコープを選択する必要はありません。 setting_aggregate_reblogs: 最近ブーストされた投稿が新たにブーストされても表示しません (設定後受信したものにのみ影響) setting_always_send_emails: 通常、Mastodon からメール通知は行われません。 + setting_boost_modal: 有効にすると、ブーストによって、まず、ブーストの公開範囲を設定できる確認ダイアログが表示されます。 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: 新着があってもタイムラインを自動的にスクロールしないようにします @@ -222,6 +224,7 @@ ja: 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: 引用できるユーザー diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index 4e88520f84..7010597754 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -41,7 +41,7 @@ lt: defaults: autofollow: Žmonės, kurie užsiregistruos per kvietimą, automatiškai seks tave avatar: WEBP, PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. - bot: Signalizuoti kitiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad ji gali būti nestebima. + bot: Pranešti visiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad jos neverta sekti context: Vienas arba keli kontekstai, kuriems turėtų būti taikomas filtras current_password: Saugumo sumetimais įvesk dabartinės paskyros slaptažodį current_username: Kad patvirtintum, įvesk dabartinės paskyros naudotojo vardą @@ -56,11 +56,14 @@ lt: scopes: Prie kurių API programai bus leidžiama pasiekti. Pasirinkus aukščiausio lygio sritį, atskirų sričių pasirinkti nereikia. setting_aggregate_reblogs: Nerodyti naujų pakėlimų įrašams, kurie neseniai buvo pakelti (taikoma tik naujai gautiems pakėlimams). setting_always_send_emails: Paprastai el. laiško pranešimai nebus siunčiami, kai aktyviai naudoji Mastodon. + setting_default_quote_policy_private: Tik sekėjams skirti įrašai, paskelbti platformoje „Mastodon“, negali būti cituojami kitų. + setting_default_quote_policy_unlisted: Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto. setting_default_sensitive: Jautrioji medija pagal numatytuosius nustatymus yra paslėpta ir gali būti atskleista spustelėjus. setting_display_media_default: Slėpti mediją, pažymėtą kaip jautrią setting_display_media_hide_all: Visada slėpti mediją setting_display_media_show_all: Visada rodyti mediją setting_emoji_style: Kaip rodyti emodžius. „Auto“ bandys naudoti vietinius jaustukus, bet senesnėse naršyklėse grįš prie Tvejaustukų. + setting_quick_boosting_html: Kai ši funkcija įjungta, paspaudus ant %{boost_icon} Paryškinimo piktogramos, įrašas bus iškart paryškintas, o ne atidarytas išskleidžiamasis meniu „paryškinti/cituoti“. Citavimo veiksmas perkeliamas į %{options_icon} meniu. setting_system_scrollbars_ui: Taikoma tik darbalaukio naršyklėms, karkasiniais „Safari“ ir „Chrome“. setting_use_blurhash: Gradientai pagrįsti paslėptų vizualizacijų spalvomis, bet užgožia bet kokias detales. setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio srauto slinkimo. @@ -79,6 +82,7 @@ lt: activity_api_enabled: Vietinių paskelbtų įrašų, aktyvių naudotojų ir naujų registracijų skaičiai kas savaitę app_icon: WEBP, PNG, GIF arba JPG. Pakeičia numatytąją programos piktogramą mobiliuosiuose įrenginiuose pasirinktine piktograma. backups_retention_period: Naudotojai gali generuoti savo įrašų archyvus, kuriuos vėliau galės atsisiųsti. Nustačius teigiamą reikšmę, šie archyvai po nurodyto dienų skaičiaus bus automatiškai ištrinti iš saugyklos. + bootstrap_timeline_accounts: Šios paskyros bus rekomenduojamos naujiems naudotojams Rekomenduojami sekimai skyrelyje. Pateikite rekomenduojamas paskyras, atskirtas kableliais. content_cache_retention_period: Visi įrašai iš kitų serverių (įskaitant pakėlimus ir atsakymus) bus ištrinti po nurodyto dienų skaičiaus, neatsižvelgiant į bet kokią vietinio naudotojo sąveiką su tais įrašais. Tai taikoma ir tiems įrašams, kuriuos vietinis naudotojas yra pažymėjęs kaip žymes ar mėgstamus. Privačios paminėjimai tarp naudotojų iš skirtingų instancijų taip pat bus prarastos ir jų bus neįmanoma atkurti. Šis nustatymas skirtas naudoti ypatingos paskirties instancijose, o įgyvendinus jį bendram naudojimui, pažeidžiami daugelio naudotojų lūkesčiai. favicon: WEBP, PNG, GIF arba JPG. Pakeičia numatytąją Mastodon svetaines piktogramą pasirinktine piktograma. mascot: Pakeičia išplėstinės žiniatinklio sąsajos iliustraciją. @@ -144,6 +148,7 @@ lt: email: El. pašto adresas expires_in: Nustoja galioti po fields: Papildomi laukai + header: Antraštės paveikslėlis irreversible: Mesti vietoj slėpti locale: Sąsajos kalba max_uses: Maksimalus naudojimo skaičius @@ -156,6 +161,7 @@ lt: setting_always_send_emails: Visada siųsti el. laiško pranešimus setting_auto_play_gif: Automatiškai leisti animuotų GIF setting_default_language: Skelbimo kalba + setting_default_quote_policy: Kas gali cituoti setting_default_sensitive: Visada žymėti mediją kaip jautrią setting_disable_hover_cards: Išjungti profilio peržiūrą užvedus setting_disable_swiping: Išjungti perbraukimo judėjimus @@ -187,7 +193,7 @@ lt: hide: Slėpti visiškai warn: Slėpti su įspėjimu form_admin_settings: - activity_api_enabled: Skelbti suvestinį statistiką apie naudotojų veiklą per API + activity_api_enabled: Skelbti suvestinę statistiką apie naudotojų veiklą per API app_icon: Programėlės piktograma bootstrap_timeline_accounts: Visada rekomenduoti šias paskyras naujiems naudotojams content_cache_retention_period: Nuotolinio turinio saugojimo laikotarpis @@ -216,6 +222,7 @@ lt: follow_request: Kažkas paprašė sekti tave mention: Kažkas paminėjo tave pending_account: Reikia peržiūros naujam paskyrui + quote: Kažkas jus paminėjo reblog: Kažkas pakėlė tavo įrašą software_updates: label: Yra nauja Mastodon versija diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index c0aa3f692e..902e4d0b23 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -372,7 +372,9 @@ nl: jurisdiction: Jurisdictie min_age: Minimumleeftijd user: + date_of_birth_1i: Jaar date_of_birth_2i: Maand + date_of_birth_3i: Dag role: Rol time_zone: Tijdzone user_role: diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index b7b1021cd1..640e1b7772 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -289,7 +289,7 @@ nn: favicon: Favorittikon landing_page: Startside for nye lesarar local_live_feed_access: Tilgang til direktestraumar med lokale innlegg - local_topic_feed_access: Tilgang til merkelapp- og lenkestraumar med lokale innlegg + local_topic_feed_access: Tilgang til emneknagg- og lenkestraumar med lokale innlegg mascot: Eigendefinert maskot (eldre funksjon) media_cache_retention_period: Oppbevaringsperiode for mediebuffer min_age: Minste aldersgrense @@ -297,7 +297,7 @@ nn: profile_directory: Aktiver profilkatalog registrations_mode: Kven kan registrera seg remote_live_feed_access: Tilgang til direktestraumar med eksterne innlegg - remote_topic_feed_access: Tilgang til merkelapp- og direktestraumar med eksterne innlegg + remote_topic_feed_access: Tilgang til emneknagg- og direktestraumar med eksterne innlegg require_invite_text: Krev ei grunngjeving for å få bli med show_domain_blocks: Vis domeneblokkeringar show_domain_blocks_rationale: Vis grunngjeving for domeneblokkeringar @@ -372,7 +372,9 @@ nn: jurisdiction: Rettskrins min_age: Minstealder user: + date_of_birth_1i: År date_of_birth_2i: Månad + date_of_birth_3i: Dag role: Rolle time_zone: Tidssone user_role: diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 6b251a7779..8171dcea85 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1691,16 +1691,22 @@ sq: body: 'U përmendët nga %{name} në:' subject: U përmendët nga %{name} title: Përmendje e re + moderation_warning: + subject: Ju është dhënë një sinjalizim moderimi poll: subject: Përfundoi një pyetësor nga %{name} quote: body: 'Postimi juaj u citua nga %{name}:' subject: "%{name} citoi postimin tuaj" title: Citim i ri + quoted_update: + subject: "%{name} përpunoi një postim që keni cituar" reblog: body: 'Gjendja juaj u përforcua nga %{name}:' subject: "%{name} përforcoi gjendjen tuaj" title: Përforcim i ri + severed_relationships: + subject: Keni humbur lidhjet, për shkak të një vendimi moderimi status: subject: "%{name} sapo postoi" update: diff --git a/config/locales/tr.yml b/config/locales/tr.yml index b56ba5d9ac..d0228c5cea 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1706,16 +1706,22 @@ tr: body: "%{name} senden bahsetti:" subject: "%{name} senden bahsetti" title: Yeni bahsetme + moderation_warning: + subject: Hesabınız bir denetim uyarısı aldı poll: subject: Anket %{name} tarafından sonlandırıldı quote: body: "%{name} durumunuzu yeniden paylaştı:" subject: "%{name} gönderini yeniden paylaştı" title: Yeni alıntı + quoted_update: + subject: "%{name} alıntıladığınız bir gönderiyi düzenledi" reblog: body: "%{name} durumunuzu yeniden paylaştı:" subject: "%{name} durumunuzu yeniden paylaştı" title: Yeni paylaşım + severed_relationships: + subject: Bir denetim kararı nedeniyle bağlantılarınızı kaybettiniz status: subject: "%{name} az önce gönderdi" update: diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 30476230fd..0e39f6bf0f 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1667,16 +1667,22 @@ vi: body: 'Bạn vừa được nhắc đến bởi %{name} trong:' subject: Bạn vừa được nhắc đến bởi %{name} title: Lượt nhắc mới + moderation_warning: + subject: Bạn vừa nhận một cảnh báo kiểm duyệt poll: subject: Vốt của %{name} đã kết thúc quote: body: 'Tút của bạn được trích dẫn bởi %{name}:' subject: "%{name} vừa trích dẫn tút của bạn" title: Trích dẫn mới + quoted_update: + subject: "%{name} đã chỉnh sửa tút mà bạn trích dẫn" reblog: body: Tút của bạn vừa được %{name} đăng lại subject: "%{name} vừa đăng lại tút của bạn" title: Lượt đăng lại mới + severed_relationships: + subject: Bạn bị mất các mối quan hệ vì một quyết định kiểm duyệt status: subject: Bài đăng mới từ %{name} update: diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index f4b6dcd837..c7a1fc84a6 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1667,16 +1667,22 @@ zh-CN: body: "%{name} 提到了你:" subject: "%{name} 提到了你" title: 新的提及 + moderation_warning: + subject: 你收到了一条管理警告 poll: subject: "%{name} 创建的一个投票已经结束" quote: body: 你的嘟文被 %{name} 引用了: subject: "%{name} 引用了你的嘟文" title: 新引用 + quoted_update: + subject: "%{name} 编辑了一条你之前引用过的嘟文" reblog: body: 你的嘟文被 %{name} 转嘟了: subject: "%{name} 转嘟了你的嘟文" title: 新的转嘟 + severed_relationships: + subject: 由于管理决定,您已失去网络上的关注关系 status: subject: "%{name} 刚刚发布嘟文" update: diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index bc12e97ac5..e7f8172aee 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1669,16 +1669,22 @@ zh-TW: body: "%{name} 於嘟文中提及您:" subject: "%{name} 於嘟文中提及您" title: 新的提及 + moderation_warning: + subject: 您已收到管理員警告 poll: subject: 由 %{name} 發起的投票已結束 quote: body: 您的嘟文被 %{name} 引用: subject: "%{name} 已引用您的嘟文" title: 新引用 + quoted_update: + subject: "%{name} 已編輯一則您曾引用之嘟文" reblog: body: 您的嘟文被 %{name} 轉嘟: subject: "%{name} 已轉嘟您的嘟文" title: 新的轉嘟 + severed_relationships: + subject: 由於管理員之決定,您已失去您的社交網路 status: subject: "%{name} 剛剛嘟文" update: diff --git a/config/routes.rb b/config/routes.rb index 412372600e..3685e695f9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -163,6 +163,7 @@ Rails.application.routes.draw do get '/@:account_username/followers', to: 'follower_accounts#index' get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status + get '/@:account_username/wrapstodon/:year/:share_key', to: 'wrapstodon#show', as: :public_wrapstodon end get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, as: :account_with_domain, format: false diff --git a/config/routes/api.rb b/config/routes/api.rb index 47ae01a534..9bf23c5238 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -8,7 +8,7 @@ namespace :api, format: false do namespace :v1_alpha do resources :async_refreshes, only: :show - resources :collections, only: [:create] + resources :collections, only: [:show, :create] end # JSON / REST API @@ -73,6 +73,8 @@ namespace :api, format: false do resources :annual_reports, only: [:index, :show] do member do post :read + post :generate + get :state end end diff --git a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb index 9a18a6451c..ad0d4ab4c2 100644 --- a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb +++ b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb @@ -4,6 +4,8 @@ class MigrateTimelinePreviewSetting < ActiveRecord::Migration[8.0] class Setting < ApplicationRecord; end def up + Setting.reset_column_information + setting = Setting.find_by(var: 'timeline_preview') return unless setting.present? && setting.attributes['value'].present? @@ -12,7 +14,8 @@ class MigrateTimelinePreviewSetting < ActiveRecord::Migration[8.0] Setting.upsert_all( %w(local_live_feed_access remote_live_feed_access local_topic_feed_access remote_topic_feed_access).map do |var| { var: var, value: value ? "--- public\n" : "--- authenticated\n" } - end + end, + unique_by: index_exists?(:settings, [:thing_type, :thing_id, :var]) ? [:thing_type, :thing_id, :var] : :var ) end diff --git a/db/migrate/20251023210145_migrate_landing_page_setting.rb b/db/migrate/20251023210145_migrate_landing_page_setting.rb index e8448bc75e..db9dc333b9 100644 --- a/db/migrate/20251023210145_migrate_landing_page_setting.rb +++ b/db/migrate/20251023210145_migrate_landing_page_setting.rb @@ -4,6 +4,8 @@ class MigrateLandingPageSetting < ActiveRecord::Migration[8.0] class Setting < ApplicationRecord; end def up + Setting.reset_column_information + setting = Setting.find_by(var: 'trends_as_landing_page') return unless setting.present? && setting.attributes['value'].present? @@ -12,7 +14,8 @@ class MigrateLandingPageSetting < ActiveRecord::Migration[8.0] Setting.upsert({ var: 'landing_page', value: value ? "--- trends\n" : "--- about\n", - }) + }, + unique_by: index_exists?(:settings, [:thing_type, :thing_id, :var]) ? [:thing_type, :thing_id, :var] : :var) end def down; end diff --git a/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 0000000000..149b0b994e --- /dev/null +++ b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + add_column :custom_emoji_categories, :featured_emoji_id, :bigint, null: true + add_foreign_key :custom_emoji_categories, :custom_emojis, column: :featured_emoji_id, on_delete: :nullify, validate: false + end +end diff --git a/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 0000000000..3ff5ce7b5e --- /dev/null +++ b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ValidateAddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + validate_foreign_key :custom_emoji_categories, :custom_emojis + end +end diff --git a/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb new file mode 100644 index 0000000000..aa4d15a15a --- /dev/null +++ b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddShareKeyToGeneratedAnnualReports < ActiveRecord::Migration[8.0] + def change + add_column :generated_annual_reports, :share_key, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d1619dd933..919651661b 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_11_19_093332) do +ActiveRecord::Schema[8.0].define(version: 2025_12_02_140424) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -404,6 +404,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do t.string "name" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.bigint "featured_emoji_id" t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true end @@ -615,6 +616,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do t.datetime "viewed_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "share_key" t.index ["account_id", "year"], name: "index_generated_annual_reports_on_account_id_and_year", unique: true end @@ -1429,6 +1431,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do 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_emoji_categories", "custom_emojis", column: "featured_emoji_id", on_delete: :nullify add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade diff --git a/spec/fabricators/collection_item_fabricator.rb b/spec/fabricators/collection_item_fabricator.rb index 011f9ba5b5..db55f7d237 100644 --- a/spec/fabricators/collection_item_fabricator.rb +++ b/spec/fabricators/collection_item_fabricator.rb @@ -3,7 +3,7 @@ Fabricator(:collection_item) do collection { Fabricate.build(:collection) } account { Fabricate.build(:account) } - position 1 + position { sequence(:position, 1) } state :accepted end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index 6c160ecb70..3d367b9d90 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -48,4 +48,28 @@ RSpec.describe Collection do it { is_expected.to_not be_valid } end end + + describe '#item_for' do + subject { Fabricate(:collection) } + + let!(:items) { Fabricate.times(2, :collection_item, collection: subject) } + + context 'when given no account' do + it 'returns all items' do + expect(subject.items_for).to match_array(items) + end + end + + context 'when given an account' do + let(:account) { Fabricate(:account) } + + before do + account.block!(items.first.account) + end + + it 'does not return items blocked by this account' do + expect(subject.items_for(account)).to contain_exactly(items.last) + end + end + end end diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 4e48772765..69686d5076 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -599,34 +599,6 @@ RSpec.describe Status do end end - describe 'before_validation' do - it 'sets account being replied to correctly over intermediary nodes' do - first_status = Fabricate(:status, account: bob) - intermediary = Fabricate(:status, thread: first_status, account: alice) - final = Fabricate(:status, thread: intermediary, account: alice) - - expect(final.in_reply_to_account_id).to eq bob.id - end - - it 'creates new conversation for stand-alone status' do - expect(described_class.create(account: alice, text: 'First').conversation_id).to_not be_nil - end - - it 'keeps conversation of parent node' do - parent = Fabricate(:status, text: 'First') - expect(described_class.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id - end - - it 'sets `local` to true for status by local account' do - expect(described_class.create(account: alice, text: 'foo').local).to be true - end - - it 'sets `local` to false for status by remote account' do - alice.update(domain: 'example.com') - expect(described_class.create(account: alice, text: 'foo').local).to be false - end - end - describe 'Validations' do context 'with a remote account' do subject { Fabricate.build :status, account: remote_account } @@ -675,13 +647,49 @@ RSpec.describe Status do end end end - end - describe 'after_create' do - it 'saves ActivityPub uri as uri for local status' do - status = described_class.create(account: alice, text: 'foo') - status.reload - expect(status.uri).to start_with('https://') + describe 'Wiring up replies and conversations' do + it 'sets account being replied to correctly over intermediary nodes' do + first_status = Fabricate(:status, account: bob) + intermediary = Fabricate(:status, thread: first_status, account: alice) + final = Fabricate(:status, thread: intermediary, account: alice) + + expect(final.in_reply_to_account_id).to eq bob.id + end + + it 'creates new conversation for stand-alone status' do + new_status = nil + expect do + new_status = described_class.create(account: alice, text: 'First') + end.to change(Conversation, :count).by(1) + + expect(new_status.conversation_id).to_not be_nil + expect(new_status.conversation.parent_status_id).to eq new_status.id + end + + it 'keeps conversation of parent node' do + parent = Fabricate(:status, text: 'First') + expect(described_class.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id + end + end + + describe 'Setting the `local` flag correctly' do + it 'sets `local` to true for status by local account' do + expect(described_class.create(account: alice, text: 'foo').local).to be true + end + + it 'sets `local` to false for status by remote account' do + alice.update(domain: 'example.com') + expect(described_class.create(account: alice, text: 'foo').local).to be false + end + end + + describe 'after_create' do + it 'saves ActivityPub uri as uri for local status' do + status = described_class.create(account: alice, text: 'foo') + status.reload + expect(status.uri).to start_with('https://') + end end end end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index d41d3a9e21..9a68ae36d6 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -265,6 +265,27 @@ RSpec.describe Tag do end end + describe '.find_or_create_by_names_race_condition' do + it 'handles simultaneous inserts of the same tag in different cases without error' do + tag_name_upper = 'Rails' + tag_name_lower = 'rails' + + threads = [] + + 2.times do |i| + threads << Thread.new do + described_class.find_or_create_by_names(i.zero? ? tag_name_upper : tag_name_lower) + end + end + + threads.each(&:join) + + tags = described_class.where('lower(name) = ?', tag_name_lower.downcase) + expect(tags.count).to eq(1) + expect(tags.first.name.downcase).to eq(tag_name_lower.downcase) + end + end + describe '.search_for' do it 'finds tag records with matching names' do tag = Fabricate(:tag, name: 'match') diff --git a/spec/requests/api/v1/annual_reports_spec.rb b/spec/requests/api/v1/annual_reports_spec.rb index 88a7dbdd82..482e91736c 100644 --- a/spec/requests/api/v1/annual_reports_spec.rb +++ b/spec/requests/api/v1/annual_reports_spec.rb @@ -42,6 +42,153 @@ RSpec.describe 'API V1 Annual Reports' do end end + describe 'GET /api/v1/annual_reports/:year/state' do + context 'when not authorized' do + it 'returns http unauthorized' do + get '/api/v1/annual_reports/2025/state' + + expect(response) + .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'with wrong scope' do + before do + get '/api/v1/annual_reports/2025/state', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts' + end + + context 'with correct scope' do + let(:scopes) { 'read:accounts' } + + context 'when a report is already generated' do + before do + Fabricate(:generated_annual_report, account: user.account, year: 2025) + end + + it 'returns http success and available status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'available') + end + end + + context 'when the feature is not enabled' do + it 'returns http success and ineligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'ineligible') + end + end + + context 'when the feature is enabled and time is within window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http success and eligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'eligible') + end + end + + context 'when the feature is enabled but we are out of the time window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 6, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http success and ineligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'ineligible') + end + end + end + end + + describe 'POST /api/v1/annual_reports/:id/generate' do + context 'when not authorized' do + it 'returns http unauthorized' do + post '/api/v1/annual_reports/2025/generate' + + expect(response) + .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'with wrong scope' do + before do + post '/api/v1/annual_reports/2025/generate', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'read read:accounts' + end + + context 'with correct scope' do + let(:scopes) { 'write:accounts' } + + context 'when the feature is enabled and time is within window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http accepted, create an async job and schedules a job' do + expect { post '/api/v1/annual_reports/2025/generate', headers: headers } + .to enqueue_sidekiq_job(GenerateAnnualReportWorker).with(user.account_id, 2025) + + expect(response) + .to have_http_status(202) + + expect(response.headers['Mastodon-Async-Refresh']).to be_present + end + end + end + end + describe 'POST /api/v1/annual_reports/:id/read' do context 'with correct scope' do let(:scopes) { 'write:accounts' } diff --git a/spec/requests/api/v1_alpha/collections_spec.rb b/spec/requests/api/v1_alpha/collections_spec.rb index 5f9c5e5f34..d576fbf98b 100644 --- a/spec/requests/api/v1_alpha/collections_spec.rb +++ b/spec/requests/api/v1_alpha/collections_spec.rb @@ -5,6 +5,50 @@ require 'rails_helper' RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do include_context 'with API authentication', oauth_scopes: 'read:collections write:collections' + describe 'GET /api/v1_alpha/collections/:id' do + subject do + get "/api/v1_alpha/collections/#{collection.id}", headers: headers + end + + let(:collection) { Fabricate(:collection) } + let!(:items) { Fabricate.times(2, :collection_item, collection:) } + + shared_examples 'unfiltered, successful request' do + it 'includes all items in the response' do + subject + + expect(response).to have_http_status(200) + expect(response.parsed_body[:items].size).to eq 2 + end + end + + context 'when user is not signed in' do + let(:headers) { {} } + + it_behaves_like 'unfiltered, successful request' + end + + context 'when user is signed in' do + context 'when the user has not blocked or muted anyone' do + it_behaves_like 'unfiltered, successful request' + end + + context 'when the user has blocked an account' do + before do + user.account.block!(items.first.account) + end + + it 'only includes the non-blocked account in the response' do + subject + + expect(response).to have_http_status(200) + expect(response.parsed_body[:items].size).to eq 1 + expect(response.parsed_body[:items][0]['position']).to eq items.last.position + end + end + end + end + describe 'POST /api/v1_alpha/collections' do subject do post '/api/v1_alpha/collections', headers: headers, params: params diff --git a/spec/requests/api/v2/instance_spec.rb b/spec/requests/api/v2/instance_spec.rb index 2ffd022fd3..7eae8ee895 100644 --- a/spec/requests/api/v2/instance_spec.rb +++ b/spec/requests/api/v2/instance_spec.rb @@ -42,6 +42,26 @@ RSpec.describe 'Instances' do end end + context 'when wrapstodon is enabled', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + end + + it 'returns http success and the wrapstodon year' do + get api_v2_instance_path + + expect(response) + .to have_http_status(200) + + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(wrapstodon: 2025) + end + end + def include_configuration_limits include( configuration: include( diff --git a/spec/serializers/rest/collection_item_serializer_spec.rb b/spec/serializers/rest/collection_item_serializer_spec.rb new file mode 100644 index 0000000000..bcb7458c4d --- /dev/null +++ b/spec/serializers/rest/collection_item_serializer_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe REST::CollectionItemSerializer do + subject { serialized_record_json(collection_item, described_class) } + + let(:collection_item) do + Fabricate(:collection_item, + state:, + position: 4) + end + + context 'when state is `accepted`' do + let(:state) { :accepted } + + it 'includes the relevant attributes including the account' do + expect(subject) + .to include( + 'account' => an_instance_of(Hash), + 'state' => 'accepted', + 'position' => 4 + ) + end + end + + %i(pending rejected revoked).each do |unaccepted_state| + context "when state is `#{unaccepted_state}`" do + let(:state) { unaccepted_state } + + it 'does not include an account' do + expect(subject.keys).to_not include('account') + end + end + end +end diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb index c498937b50..10bf9ee2b5 100644 --- a/spec/serializers/rest/collection_serializer_spec.rb +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -3,15 +3,24 @@ require 'rails_helper' RSpec.describe REST::CollectionSerializer do - subject { serialized_record_json(collection, described_class) } + subject do + serialized_record_json(collection, described_class, options: { + scope: current_user, + scope_name: :current_user, + }) + end + let(:current_user) { nil } + + let(:tag) { Fabricate(:tag, name: 'discovery') } let(:collection) do Fabricate(:collection, name: 'Exquisite follows', description: 'Always worth a follow', local: true, sensitive: true, - discoverable: false) + discoverable: false, + tag:) end it 'includes the relevant attributes' do @@ -23,6 +32,7 @@ RSpec.describe REST::CollectionSerializer do 'local' => true, 'sensitive' => true, 'discoverable' => false, + 'tag' => a_hash_including('name' => 'discovery'), 'created_at' => match_api_datetime_format, 'updated_at' => match_api_datetime_format ) diff --git a/spec/system/auth/registrations_spec.rb b/spec/system/auth/registrations_spec.rb index 4c08bf47ee..3a103667e6 100644 --- a/spec/system/auth/registrations_spec.rb +++ b/spec/system/auth/registrations_spec.rb @@ -3,6 +3,17 @@ require 'rails_helper' RSpec.describe 'Auth Registration' do + context 'when there are server rules' do + let!(:rule) { Fabricate :rule, text: 'You must be seven meters tall' } + + it 'shows rules page before proceeding with sign up' do + visit new_user_registration_path + expect(page) + .to have_title(I18n.t('auth.register')) + .and have_content(rule.text) + end + end + context 'when age verification is enabled' do before { Setting.min_age = 16 } diff --git a/yarn.lock b/yarn.lock index 6d0ae003ed..c0c3ce0c82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2581,6 +2581,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -3012,14 +3028,14 @@ __metadata: linkType: hard "@optimize-lodash/rollup-plugin@npm:^5.0.2": - version: 5.0.2 - resolution: "@optimize-lodash/rollup-plugin@npm:5.0.2" + version: 5.1.0 + resolution: "@optimize-lodash/rollup-plugin@npm:5.1.0" dependencies: "@optimize-lodash/transform": "npm:3.0.6" "@rollup/pluginutils": "npm:^5.1.0" peerDependencies: rollup: ">= 4.x" - checksum: 10c0/ad3a6baafe1422a2bc0c2c2d1e32961214431ff714f4fff793b65174291b931a55aa2797507115e3e0cb8473c311611c817c7d70d2b591473c5930500bc9edd7 + checksum: 10c0/cb15712b82164fb63d569caebc278fea4972577009040370b43534edd75069f57974c29e884b3dc59896eda7ed4f28dfcb422c0481d8ea0fe5dfc4546af70774 languageName: node linkType: hard @@ -4110,13 +4126,13 @@ __metadata: linkType: hard "@types/express@npm:^5.0.5": - version: 5.0.5 - resolution: "@types/express@npm:5.0.5" + version: 5.0.6 + resolution: "@types/express@npm:5.0.6" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^5.0.0" - "@types/serve-static": "npm:^1" - checksum: 10c0/e96da91c121b43e0e84301a4cfe165908382d016234c11213aeb4f7401cf1a8694e16e3947d21b5c20b3389358d48d60a8c5c38657e041726ac9e8c884d2b8f0 + "@types/serve-static": "npm:^2" + checksum: 10c0/f1071e3389a955d4f9a38aae38634121c7cd9b3171ba4201ec9b56bd534aba07866839d278adc0dda05b942b05a901a02fd174201c3b1f70ce22b10b6c68f24b languageName: node linkType: hard @@ -4183,9 +4199,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.195": - version: 4.17.20 - resolution: "@types/lodash@npm:4.17.20" - checksum: 10c0/98cdd0faae22cbb8079a01a3bb65aa8f8c41143367486c1cbf5adc83f16c9272a2a5d2c1f541f61d0d73da543c16ee1d21cf2ef86cb93cd0cc0ac3bced6dd88f + version: 4.17.21 + resolution: "@types/lodash@npm:4.17.21" + checksum: 10c0/73cb006e047d8871e9d63f3a165543bf16c44a5b6fe3f9f6299e37cb8d67a7b1d55ac730959a81f9def510fd07232ff7e30e05413e5d5a12793baad84ebe36c3 languageName: node linkType: hard @@ -4383,12 +4399,12 @@ __metadata: linkType: hard "@types/react@npm:^18.2.7": - version: 18.3.26 - resolution: "@types/react@npm:18.3.26" + version: 18.3.27 + resolution: "@types/react@npm:18.3.27" dependencies: "@types/prop-types": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10c0/7b62d91c33758f14637311921c92db6045b6328e2300666a35ef8130d06385e39acada005eaf317eee93228edc10ea5f0cd34a0385654d2014d24699a65bfeef + csstype: "npm:^3.2.2" + checksum: 10c0/a761d2f58de03d0714806cc65d32bb3d73fb33a08dd030d255b47a295e5fff2a775cf1c20b786824d8deb6454eaccce9bc6998d9899c14fc04bbd1b0b0b72897 languageName: node linkType: hard @@ -4423,7 +4439,7 @@ __metadata: languageName: node linkType: hard -"@types/send@npm:*, @types/send@npm:<1": +"@types/send@npm:*": version: 0.17.6 resolution: "@types/send@npm:0.17.6" dependencies: @@ -4433,14 +4449,13 @@ __metadata: languageName: node linkType: hard -"@types/serve-static@npm:^1": - version: 1.15.10 - resolution: "@types/serve-static@npm:1.15.10" +"@types/serve-static@npm:^2": + version: 2.2.0 + resolution: "@types/serve-static@npm:2.2.0" dependencies: "@types/http-errors": "npm:*" "@types/node": "npm:*" - "@types/send": "npm:<1" - checksum: 10c0/842fca14c9e80468f89b6cea361773f2dcd685d4616a9f59013b55e1e83f536e4c93d6d8e3ba5072d40c4e7e64085210edd6646b15d538ded94512940a23021f + checksum: 10c0/a3c6126bdbf9685e6c7dc03ad34639666eff32754e912adeed9643bf3dd3aa0ff043002a7f69039306e310d233eb8e160c59308f95b0a619f32366bbc48ee094 languageName: node linkType: hard @@ -5563,20 +5578,20 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^2.2.0": - version: 2.2.0 - resolution: "body-parser@npm:2.2.0" +"body-parser@npm:^2.2.1": + version: 2.2.1 + resolution: "body-parser@npm:2.2.1" dependencies: bytes: "npm:^3.1.2" content-type: "npm:^1.0.5" - debug: "npm:^4.4.0" + debug: "npm:^4.4.3" http-errors: "npm:^2.0.0" - iconv-lite: "npm:^0.6.3" + iconv-lite: "npm:^0.7.0" on-finished: "npm:^2.4.1" qs: "npm:^6.14.0" - raw-body: "npm:^3.0.0" - type-is: "npm:^2.0.0" - checksum: 10c0/a9ded39e71ac9668e2211afa72e82ff86cc5ef94de1250b7d1ba9cc299e4150408aaa5f1e8b03dd4578472a3ce6d1caa2a23b27a6c18e526e48b4595174c116c + raw-body: "npm:^3.0.1" + type-is: "npm:^2.0.1" + checksum: 10c0/ce9608cff4114a908c09e8f57c7b358cd6fef66100320d01244d4c141448d7a6710c4051cc9d6191f8c6b3c7fa0f5b040c7aa1b6bbeb5462e27e668e64cb15bd languageName: node linkType: hard @@ -5653,7 +5668,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2, bytes@npm:^3.1.2": +"bytes@npm:^3.1.2, bytes@npm:~3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e @@ -6127,7 +6142,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -6229,10 +6244,10 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2": - version: 3.1.2 - resolution: "csstype@npm:3.1.2" - checksum: 10c0/32c038af259897c807ac738d9eab16b3d86747c72b09d5c740978e06f067f9b7b1737e1b75e407c7ab1fe1543dc95f20e202b4786aeb1b8d3bdf5d5ce655e6c6 +"csstype@npm:^3.0.2, csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce languageName: node linkType: hard @@ -6394,7 +6409,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0, depd@npm:^2.0.0": +"depd@npm:^2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c @@ -7355,16 +7370,17 @@ __metadata: linkType: hard "express@npm:^5.1.0": - version: 5.1.0 - resolution: "express@npm:5.1.0" + version: 5.2.1 + resolution: "express@npm:5.2.1" dependencies: accepts: "npm:^2.0.0" - body-parser: "npm:^2.2.0" + body-parser: "npm:^2.2.1" content-disposition: "npm:^1.0.0" content-type: "npm:^1.0.5" cookie: "npm:^0.7.1" cookie-signature: "npm:^1.2.1" debug: "npm:^4.4.0" + depd: "npm:^2.0.0" encodeurl: "npm:^2.0.0" escape-html: "npm:^1.0.3" etag: "npm:^1.8.1" @@ -7385,7 +7401,7 @@ __metadata: statuses: "npm:^2.0.1" type-is: "npm:^2.0.1" vary: "npm:^1.1.2" - checksum: 10c0/80ce7c53c5f56887d759b94c3f2283e2e51066c98d4b72a4cc1338e832b77f1e54f30d0239cc10815a0f849bdb753e6a284d2fa48d4ab56faf9c501f55d751d6 + checksum: 10c0/45e8c841ad188a41402ddcd1294901e861ee0819f632fb494f2ed344ef9c43315d294d443fb48d594e6586a3b779785120f43321417adaef8567316a55072949 languageName: node linkType: hard @@ -7607,13 +7623,13 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": - version: 3.1.1 - resolution: "foreground-child@npm:3.1.1" +"foreground-child@npm:^3.1.0, foreground-child@npm:^3.3.1": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" dependencies: - cross-spawn: "npm:^7.0.0" + cross-spawn: "npm:^7.0.6" signal-exit: "npm:^4.0.1" - checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0 + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 languageName: node linkType: hard @@ -7862,6 +7878,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^11.0.1": + version: 11.1.0 + resolution: "glob@npm:11.1.0" + dependencies: + foreground-child: "npm:^3.3.1" + jackspeak: "npm:^4.1.1" + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/1ceae07f23e316a6fa74581d9a74be6e8c2e590d2f7205034dd5c0435c53f5f7b712c2be00c3b65bf0a49294a1c6f4b98cd84c7637e29453b5aa13b79f1763a2 + languageName: node + linkType: hard + "glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -8139,16 +8171,16 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:2.0.0, http-errors@npm:^2.0.0": - version: 2.0.0 - resolution: "http-errors@npm:2.0.0" +"http-errors@npm:^2.0.0, http-errors@npm:~2.0.1": + version: 2.0.1 + resolution: "http-errors@npm:2.0.1" dependencies: - depd: "npm:2.0.0" - inherits: "npm:2.0.4" - setprototypeof: "npm:1.2.0" - statuses: "npm:2.0.1" - toidentifier: "npm:1.0.1" - checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19 + depd: "npm:~2.0.0" + inherits: "npm:~2.0.4" + setprototypeof: "npm:~1.2.0" + statuses: "npm:~2.0.2" + toidentifier: "npm:~1.0.1" + checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4 languageName: node linkType: hard @@ -8197,7 +8229,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.7.0": +"iconv-lite@npm:^0.7.0, iconv-lite@npm:~0.7.0": version: 0.7.0 resolution: "iconv-lite@npm:0.7.0" dependencies: @@ -8321,7 +8353,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:2.0.4": +"inherits@npm:2, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -8844,6 +8876,15 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^4.1.1": + version: 4.1.1 + resolution: "jackspeak@npm:4.1.1" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + checksum: 10c0/84ec4f8e21d6514db24737d9caf65361511f75e5e424980eebca4199f400874f45e562ac20fa8aeb1dd20ca2f3f81f0788b6e9c3e64d216a5794fd6f30e0e042 + languageName: node + linkType: hard + "jake@npm:^10.8.5": version: 10.8.7 resolution: "jake@npm:10.8.7" @@ -9333,7 +9374,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.2": +"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.2": version: 11.2.2 resolution: "lru-cache@npm:11.2.2" checksum: 10c0/72d7831bbebc85e2bdefe01047ee5584db69d641c48d7a509e86f66f6ee111b30af7ec3bd68a967d47b69a4b1fa8bbf3872630bd06a63b6735e6f0a5f1c8e83d @@ -9560,6 +9601,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + languageName: node + linkType: hard + "minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -10210,6 +10260,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + languageName: node + linkType: hard + "path-to-regexp@npm:^1.7.0": version: 1.9.0 resolution: "path-to-regexp@npm:1.9.0" @@ -11193,15 +11253,15 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:^3.0.0": - version: 3.0.1 - resolution: "raw-body@npm:3.0.1" +"raw-body@npm:^3.0.1": + version: 3.0.2 + resolution: "raw-body@npm:3.0.2" dependencies: - bytes: "npm:3.1.2" - http-errors: "npm:2.0.0" - iconv-lite: "npm:0.7.0" - unpipe: "npm:1.0.0" - checksum: 10c0/892f4fbd21ecab7e2fed0f045f7af9e16df7e8050879639d4e482784a2f4640aaaa33d916a0e98013f23acb82e09c2e3c57f84ab97104449f728d22f65a7d79a + bytes: "npm:~3.1.2" + http-errors: "npm:~2.0.1" + iconv-lite: "npm:~0.7.0" + unpipe: "npm:~1.0.0" + checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29 languageName: node linkType: hard @@ -11937,7 +11997,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^2.43.1": +"rollup@npm:^2.79.2": version: 2.79.2 resolution: "rollup@npm:2.79.2" dependencies: @@ -12288,7 +12348,7 @@ __metadata: languageName: node linkType: hard -"setprototypeof@npm:1.2.0": +"setprototypeof@npm:~1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc @@ -12632,14 +12692,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": - version: 2.0.1 - resolution: "statuses@npm:2.0.1" - checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 - languageName: node - linkType: hard - -"statuses@npm:^2.0.1, statuses@npm:^2.0.2": +"statuses@npm:^2.0.1, statuses@npm:^2.0.2, statuses@npm:~2.0.2": version: 2.0.2 resolution: "statuses@npm:2.0.2" checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f @@ -13329,7 +13382,7 @@ __metadata: languageName: node linkType: hard -"toidentifier@npm:1.0.1": +"toidentifier@npm:~1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1 @@ -13493,7 +13546,7 @@ __metadata: languageName: node linkType: hard -"type-is@npm:^2.0.0, type-is@npm:^2.0.1": +"type-is@npm:^2.0.1": version: 2.0.1 resolution: "type-is@npm:2.0.1" dependencies: @@ -13728,7 +13781,7 @@ __metadata: languageName: node linkType: hard -"unpipe@npm:1.0.0": +"unpipe@npm:~1.0.0": version: 1.0.0 resolution: "unpipe@npm:1.0.0" checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c @@ -13952,23 +14005,23 @@ __metadata: linkType: hard "vite-plugin-pwa@npm:^1.0.2": - version: 1.1.0 - resolution: "vite-plugin-pwa@npm:1.1.0" + version: 1.2.0 + resolution: "vite-plugin-pwa@npm:1.2.0" dependencies: debug: "npm:^4.3.6" pretty-bytes: "npm:^6.1.1" tinyglobby: "npm:^0.2.10" - workbox-build: "npm:^7.3.0" - workbox-window: "npm:^7.3.0" + workbox-build: "npm:^7.4.0" + workbox-window: "npm:^7.4.0" peerDependencies: "@vite-pwa/assets-generator": ^1.0.0 vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - workbox-build: ^7.3.0 - workbox-window: ^7.3.0 + workbox-build: ^7.4.0 + workbox-window: ^7.4.0 peerDependenciesMeta: "@vite-pwa/assets-generator": optional: true - checksum: 10c0/dc199ccbb3cd0a9f740edcbbc8efa7820f67481ae80a15340ca769c21d4f7452d9a9c1d184eac4f6e3fd1ecd9f7fdfd31cc8f9520e43e6795860fe187c77103a + checksum: 10c0/d037591fc6a44b9a97f45b2452f691fce34c1c8ece4721d4f1a068f6dd7c30991b63c090a2af87d5b78d351bf5f5eee9d9dbbfc4bb39a77d3bc2ab7d2ca5df6b languageName: node linkType: hard @@ -14331,28 +14384,28 @@ __metadata: languageName: node linkType: hard -"workbox-background-sync@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-background-sync@npm:7.3.0" +"workbox-background-sync@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-background-sync@npm:7.4.0" dependencies: idb: "npm:^7.0.1" - workbox-core: "npm:7.3.0" - checksum: 10c0/cc982d62702847fb16c4ef372a8bd243348a80c2d5da1649a860b0187b45060a799a65582c2d36f1a32e31d5d68dedcb037698c41d3b2f171ea5d54d73453cf1 + workbox-core: "npm:7.4.0" + checksum: 10c0/024dfad37c9ca28480857aaf7c0dd2e2f2b2ea416e100b51260eaf28f14ca5a558d0e12b1e95f862fec04df54432c090fa29582ab88b43d8778a4c821f21d13f languageName: node linkType: hard -"workbox-broadcast-update@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-broadcast-update@npm:7.3.0" +"workbox-broadcast-update@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-broadcast-update@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/25007acd3e845b5ca1f4c9ac9888ce661431723f7419cfa56b3029b6c56cbeca24902dae015c42a2d6f554f956274743e331d03ceeb4b0e3879cb7b908d0e82f + workbox-core: "npm:7.4.0" + checksum: 10c0/9e96d38cb1cfaccf72a37beeed7d36a66db8bacaa09e584bfa15f6b129aee5026c521320b4b68fa762f4f6ba0229f11c6ec6f7bc039419c8d1f1a9ed9f2a37b5 languageName: node linkType: hard -"workbox-build@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-build@npm:7.3.0" +"workbox-build@npm:^7.4.0": + version: 7.4.0 + resolution: "workbox-build@npm:7.4.0" dependencies: "@apideck/better-ajv-errors": "npm:^0.3.1" "@babel/core": "npm:^7.24.4" @@ -14367,157 +14420,157 @@ __metadata: common-tags: "npm:^1.8.0" fast-json-stable-stringify: "npm:^2.1.0" fs-extra: "npm:^9.0.1" - glob: "npm:^7.1.6" + glob: "npm:^11.0.1" lodash: "npm:^4.17.20" pretty-bytes: "npm:^5.3.0" - rollup: "npm:^2.43.1" + rollup: "npm:^2.79.2" source-map: "npm:^0.8.0-beta.0" stringify-object: "npm:^3.3.0" strip-comments: "npm:^2.0.1" tempy: "npm:^0.6.0" upath: "npm:^1.2.0" - workbox-background-sync: "npm:7.3.0" - workbox-broadcast-update: "npm:7.3.0" - workbox-cacheable-response: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-expiration: "npm:7.3.0" - workbox-google-analytics: "npm:7.3.0" - workbox-navigation-preload: "npm:7.3.0" - workbox-precaching: "npm:7.3.0" - workbox-range-requests: "npm:7.3.0" - workbox-recipes: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - workbox-streams: "npm:7.3.0" - workbox-sw: "npm:7.3.0" - workbox-window: "npm:7.3.0" - checksum: 10c0/cb396f9c2a53429d1e11b4c1da2e21c9e1c98473ce15f20ae53277e47bd7ccbcb3f1f843694e588bb70b12d9332faafd098ca05b93abb0293d373f38a8de3ca8 + workbox-background-sync: "npm:7.4.0" + workbox-broadcast-update: "npm:7.4.0" + workbox-cacheable-response: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-expiration: "npm:7.4.0" + workbox-google-analytics: "npm:7.4.0" + workbox-navigation-preload: "npm:7.4.0" + workbox-precaching: "npm:7.4.0" + workbox-range-requests: "npm:7.4.0" + workbox-recipes: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + workbox-streams: "npm:7.4.0" + workbox-sw: "npm:7.4.0" + workbox-window: "npm:7.4.0" + checksum: 10c0/673fb05a0b24bb5534667a8d7c42304e3a1394071d73c9ccbfec881e36a66c98ed59eba92e21669a2758289c79e76a72930077a611f20ba803cdc0d0a24da6b2 languageName: node linkType: hard -"workbox-cacheable-response@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-cacheable-response@npm:7.3.0" +"workbox-cacheable-response@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-cacheable-response@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/192c8a8878c53a205c55398bac78f2c32c0f36e55c95cab282d8a716ddf2fa72563afaed690d34d3438cc8df5fb0df4d98dcb2d93cc6d67c69a9ae592f7bf246 + workbox-core: "npm:7.4.0" + checksum: 10c0/4d4fabf3cbd7b2b0505e62ec653f933de40d4fa7600e49ac15cdf223e86b9e70580927f53c6ef27ddd027b655829918b90a77a3675593017c001811a122c6591 languageName: node linkType: hard -"workbox-core@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-core@npm:7.3.0" - checksum: 10c0/b7dce640cd9665ed207f65f5b08a50e2e24e5599790c6ea4fec987539b9d2ef81765d8c5f94acfee3a8a45d5ade8e1a4ebd0b8847a1471302ef75a5b93c7bd04 +"workbox-core@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-core@npm:7.4.0" + checksum: 10c0/ae7c762df084b57d3d10f42b83a508a5c0bec5663de4c9cefc2b5c2893922a816f451cf8ec45ee76d204a3a6e90ee52b6c550a6cba9109d9bb644da9e98fb9e8 languageName: node linkType: hard -"workbox-expiration@npm:7.3.0, workbox-expiration@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-expiration@npm:7.3.0" +"workbox-expiration@npm:7.4.0, workbox-expiration@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-expiration@npm:7.4.0" dependencies: idb: "npm:^7.0.1" - workbox-core: "npm:7.3.0" - checksum: 10c0/6040d72122ece901becfcc59974586e9cc9b6309840b83b652c9f9aafe32ff89783404a431cadf6f888f80e5371252820e425ced499742964d6d68687f6fad1a + workbox-core: "npm:7.4.0" + checksum: 10c0/d4f5e96e6d58d74a1be7d8c842e68e9fc2d4a22664f629ec71570039a1876e033ca1e54d68eccded83d901999e48fb388c1f807b14f706b9b83c7b3ab5815cf8 languageName: node linkType: hard -"workbox-google-analytics@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-google-analytics@npm:7.3.0" +"workbox-google-analytics@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-google-analytics@npm:7.4.0" dependencies: - workbox-background-sync: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/5317a4bcc01f1aa87480f9708d7d382c15fb37d6119e71e0a2909dfd683f6060b5cc4f7b016a81fc67098f51a5d0cfd1cda20e228f2f3778ee3caf649b59996b + workbox-background-sync: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/34a5ad1ea6fd6c65d5eb78b8c4bcf30b2624099b1c7c2e8f6a2af7135dcfcff00531d042f99b45960dcc42185b0f9c4b62c02a38325429fa15f060f30df61e51 languageName: node linkType: hard -"workbox-navigation-preload@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-navigation-preload@npm:7.3.0" +"workbox-navigation-preload@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-navigation-preload@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/69e4d43c68c06889987e9fa437995378b0632c83bad8c7044b4ed812b05b94b3a4aa8700ea4c26b2ecf68ee6858e94ff41dfa3279815c1bc385ac19c0edfb200 + workbox-core: "npm:7.4.0" + checksum: 10c0/9999d78683247dcbf1ca1e18664b3ecb19e8330c0ff9328403a513051d65eb3fc6578504dcc324fe603c7fb6d3d1054de1f55b4d2b9765236298cfebebefabe9 languageName: node linkType: hard -"workbox-precaching@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-precaching@npm:7.3.0" +"workbox-precaching@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-precaching@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/15c4c5cf5dfec684711ce3536bbfa6873f7af16b712d02ded81d3ff490ea4097e46602705548f5872c49f06e3516fd69f17e72a7fc60631ff6d68460e48f7648 + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/df1d3ac590f56418ae80595c6c6a718f15ce3d6b9622ef43058d9d56f7374d95202ef2932e5ef5410d949f4f4366f5eccaf7956c32f579846fcc4bd83578c246 languageName: node linkType: hard -"workbox-range-requests@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-range-requests@npm:7.3.0" +"workbox-range-requests@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-range-requests@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/d48e1484866442864d66b1891c4965b71e997a83a7634f11452ec1a73a30a5e642e6a95d5cff45578bef4dec7a5f57bc598aeedb6189d17ca210e2c5f2898244 + workbox-core: "npm:7.4.0" + checksum: 10c0/7b945fab877bfc226fa8b7aa8e87b1d2179565314eff424672bfa70c43eec5dce99fe79109f548b6c6b054629f4464fb73085c3357f3c52cea6befe8d36f4fb4 languageName: node linkType: hard -"workbox-recipes@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-recipes@npm:7.3.0" +"workbox-recipes@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-recipes@npm:7.4.0" dependencies: - workbox-cacheable-response: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-expiration: "npm:7.3.0" - workbox-precaching: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/c8146ece4247cbcbefba36a14f2cb65b5f74b2412f64cfc7955ff75ff653857161a1f1d94c987fbae4812f5b770eedcf99af965e512cc375fbc7fb5421bdc99c + workbox-cacheable-response: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-expiration: "npm:7.4.0" + workbox-precaching: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/2de53c679af2879921861d817b2f9b6a682aff0d871d38542519c16902ccfdfe7d5bf456f1e3a1f2fdb86824795f1794fec39a5b342a0d9866fccfd0e02a93a7 languageName: node linkType: hard -"workbox-routing@npm:7.3.0, workbox-routing@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-routing@npm:7.3.0" +"workbox-routing@npm:7.4.0, workbox-routing@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-routing@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/8ac1824211d0fbe0e916ecb2c2427bcb0ef8783f9225d8114fe22e6c326f2d8a040a089bead58064e8b096ec95abe070c04cd7353dd8830dba3ab8d608a053aa + workbox-core: "npm:7.4.0" + checksum: 10c0/62a2a3f24a5c54fc716e9275ccd032c5e35cf645d3bcf670c91531a2a8308025767a4ff799d36483eab50c007647748ab309ea1af76847ebcd78203ef4069ae6 languageName: node linkType: hard -"workbox-strategies@npm:7.3.0, workbox-strategies@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-strategies@npm:7.3.0" +"workbox-strategies@npm:7.4.0, workbox-strategies@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-strategies@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/50f3c28b46b54885a9461ad6559010d9abb2a7e35e0128d05c268f3ea0a96b1a747934758121d0e821f7af63946d9db8f4d2d7e0146f12555fb05c768e6b82bb + workbox-core: "npm:7.4.0" + checksum: 10c0/c25771af342ce10aee006d749e7adba4e77e7379b39cd96dbd05557f0f4968ff1ac88001022fb588ce2c105e5dfded321ce9f14344291c19ab753db37e6611f8 languageName: node linkType: hard -"workbox-streams@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-streams@npm:7.3.0" +"workbox-streams@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-streams@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - checksum: 10c0/2ae541343d187eb7a50da2cfd74051f15771d1ddd1cad6856ffd530f7cccdb8eed9a8af94ff7540b710fef73eeec37d652123ae42b0206fbbd0679dc25e66ff4 + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + checksum: 10c0/213ac3cebb6f499fd2bedf28ae75523c8193ca6c7ace797227347775811dba80551dd645fc0bf04fb1b36eea2cf030e4ce3648d886cc0e8017363bd3806c84f3 languageName: node linkType: hard -"workbox-sw@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-sw@npm:7.3.0" - checksum: 10c0/9ae275e31dd5ec51245773b6d90fda16d0b7f70d59f3a71aec732814b5aedf08aedc7fcce57739e7e89d9e1479ef97e3a202a542a511d732cf5e8b5d1c293870 +"workbox-sw@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-sw@npm:7.4.0" + checksum: 10c0/f87198a9f40da34e13f896ea308c5feedf9caf3ba10b2d40c301850396edf17cdba060775b224fc07697356858816af9912997b751041a715b6934ab14b35300 languageName: node linkType: hard -"workbox-window@npm:7.3.0, workbox-window@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-window@npm:7.3.0" +"workbox-window@npm:7.4.0, workbox-window@npm:^7.3.0, workbox-window@npm:^7.4.0": + version: 7.4.0 + resolution: "workbox-window@npm:7.4.0" dependencies: "@types/trusted-types": "npm:^2.0.2" - workbox-core: "npm:7.3.0" - checksum: 10c0/dbda33c4761ec40051cfe6e3f1701b2381b4f3b191f7a249c32f683503ea35cf8b42d1f99df5ba3b693fac78705d8ed0c191488bdd178c525d1291d0161ec8ff + workbox-core: "npm:7.4.0" + checksum: 10c0/6013d1019fe3bbb06d8e572b6a4ccfec6fc0879448337997c193fde08738c2fa420a4dca385b00220ef1feac3539e82b01896c85f77cd8c45cff379e7a6cb1d5 languageName: node linkType: hard