From b135a831eaa8b17d86345ab21f28b1bfe40dc2b2 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 13 May 2025 08:38:18 +0200 Subject: [PATCH 001/106] feat: Add "Followers you know" widget to user profiles (#34652) --- .../actions/accounts_familiar_followers.ts | 22 +++++ app/javascript/mastodon/api/accounts.ts | 10 ++- app/javascript/mastodon/api_types/accounts.ts | 6 ++ .../components/avatar_group.tsx | 15 ++-- .../components/account_header.tsx | 2 + .../components/familiar_followers.tsx | 84 +++++++++++++++++++ .../notification_group_with_status.tsx | 10 ++- app/javascript/mastodon/locales/en.json | 3 + .../reducers/accounts_familiar_followers.ts | 19 +++++ app/javascript/mastodon/reducers/index.ts | 2 + app/javascript/mastodon/selectors/accounts.ts | 13 +++ .../styles/mastodon/components.scss | 44 ++++++++-- 12 files changed, 213 insertions(+), 17 deletions(-) create mode 100644 app/javascript/mastodon/actions/accounts_familiar_followers.ts rename app/javascript/mastodon/{features/notifications_v2 => }/components/avatar_group.tsx (66%) create mode 100644 app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx create mode 100644 app/javascript/mastodon/reducers/accounts_familiar_followers.ts diff --git a/app/javascript/mastodon/actions/accounts_familiar_followers.ts b/app/javascript/mastodon/actions/accounts_familiar_followers.ts new file mode 100644 index 0000000000..48968793e4 --- /dev/null +++ b/app/javascript/mastodon/actions/accounts_familiar_followers.ts @@ -0,0 +1,22 @@ +import { createDataLoadingThunk } from 'mastodon/store/typed_functions'; + +import { apiGetFamiliarFollowers } from '../api/accounts'; + +import { importFetchedAccounts } from './importer'; + +export const fetchAccountsFamiliarFollowers = createDataLoadingThunk( + 'accounts_familiar_followers/fetch', + ({ id }: { id: string }) => apiGetFamiliarFollowers(id), + ([data], { dispatch }) => { + if (!data) { + return null; + } + + dispatch(importFetchedAccounts(data.accounts)); + + return { + id: data.id, + accountIds: data.accounts.map((account) => account.id), + }; + }, +); diff --git a/app/javascript/mastodon/api/accounts.ts b/app/javascript/mastodon/api/accounts.ts index 074bcffaa1..6ce7d7248c 100644 --- a/app/javascript/mastodon/api/accounts.ts +++ b/app/javascript/mastodon/api/accounts.ts @@ -1,5 +1,8 @@ import { apiRequestPost, apiRequestGet } from 'mastodon/api'; -import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; +import type { + ApiAccountJSON, + ApiFamiliarFollowersJSON, +} from 'mastodon/api_types/accounts'; import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import type { ApiHashtagJSON } from 'mastodon/api_types/tags'; @@ -31,3 +34,8 @@ export const apiGetFeaturedTags = (id: string) => export const apiGetEndorsedAccounts = (id: string) => apiRequestGet(`v1/accounts/${id}/endorsements`); + +export const apiGetFamiliarFollowers = (id: string) => + apiRequestGet('/v1/accounts/familiar_followers', { + id, + }); diff --git a/app/javascript/mastodon/api_types/accounts.ts b/app/javascript/mastodon/api_types/accounts.ts index 3f8b27497f..b93054a1f6 100644 --- a/app/javascript/mastodon/api_types/accounts.ts +++ b/app/javascript/mastodon/api_types/accounts.ts @@ -54,3 +54,9 @@ export interface ApiMutedAccountJSON extends BaseApiAccountJSON { // For now, we have the same type representing both `Account` and `MutedAccount` // objects, but we should refactor this in the future. export type ApiAccountJSON = ApiMutedAccountJSON; + +// See app/serializers/rest/familiar_followers_serializer.rb +export type ApiFamiliarFollowersJSON = { + id: string; + accounts: ApiAccountJSON[]; +}[]; diff --git a/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx b/app/javascript/mastodon/components/avatar_group.tsx similarity index 66% rename from app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx rename to app/javascript/mastodon/components/avatar_group.tsx index b5da8914a1..4f583defcf 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx +++ b/app/javascript/mastodon/components/avatar_group.tsx @@ -1,7 +1,7 @@ +import classNames from 'classnames'; import { Link } from 'react-router-dom'; import { Avatar } from 'mastodon/components/avatar'; -import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group'; import { useAppSelector } from 'mastodon/store'; const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => { @@ -20,11 +20,14 @@ const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => { ); }; -export const AvatarGroup: React.FC<{ accountIds: string[] }> = ({ - accountIds, -}) => ( -
- {accountIds.slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS).map((accountId) => ( +export const AvatarGroup: React.FC<{ + accountIds: string[]; + compact?: boolean; +}> = ({ accountIds, compact = false }) => ( +
+ {accountIds.map((accountId) => ( ))}
diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index b7908cc8d3..35554d9735 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -59,6 +59,7 @@ import { import { getAccountHidden } from 'mastodon/selectors/accounts'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; +import { FamiliarFollowers } from './familiar_followers'; import { MemorialNote } from './memorial_note'; import { MovedNote } from './moved_note'; @@ -1022,6 +1023,7 @@ export const AccountHeader: React.FC<{ />
+ )} diff --git a/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx new file mode 100644 index 0000000000..b3b97c317b --- /dev/null +++ b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx @@ -0,0 +1,84 @@ +import { useEffect } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import { Link } from 'react-router-dom'; + +import { fetchAccountsFamiliarFollowers } from '@/mastodon/actions/accounts_familiar_followers'; +import { AvatarGroup } from '@/mastodon/components/avatar_group'; +import type { Account } from '@/mastodon/models/account'; +import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; + +const AccountLink: React.FC<{ account?: Account }> = ({ account }) => ( + + {account?.display_name} + +); + +const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({ + familiarFollowers, +}) => { + const messageData = { + name1: , + name2: , + othersCount: familiarFollowers.length - 2, + }; + + if (familiarFollowers.length === 1) { + return ( + + ); + } else if (familiarFollowers.length === 2) { + return ( + + ); + } else { + return ( + + ); + } +}; + +export const FamiliarFollowers: React.FC<{ accountId: string }> = ({ + accountId, +}) => { + const dispatch = useAppDispatch(); + const familiarFollowers = useAppSelector((state) => + getAccountFamiliarFollowers(state, accountId), + ); + + const hasNoData = familiarFollowers === null; + + useEffect(() => { + if (hasNoData) { + void dispatch(fetchAccountsFamiliarFollowers({ id: accountId })); + } + }, [dispatch, accountId, hasNoData]); + + if (hasNoData || familiarFollowers.length === 0) { + return null; + } + + return ( +
+ account.id)} + /> + +
+ ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx index 861556620f..cbb0b85f1d 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx @@ -7,12 +7,13 @@ import { HotKeys } from 'react-hotkeys'; import { replyComposeById } from 'mastodon/actions/compose'; import { navigateToStatus } from 'mastodon/actions/statuses'; +import { AvatarGroup } from 'mastodon/components/avatar_group'; import type { IconProp } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; +import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; -import { AvatarGroup } from './avatar_group'; import { DisplayedName } from './displayed_name'; import { EmbeddedStatus } from './embedded_status'; @@ -98,7 +99,12 @@ export const NotificationGroupWithStatus: React.FC<{
- + {actions && (
{actions}
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 3d6e166498..45bc6109b7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -28,6 +28,9 @@ "account.edit_profile": "Edit profile", "account.enable_notifications": "Notify me when @{name} posts", "account.endorse": "Feature on profile", + "account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {# other} other {# others}}", + "account.familiar_followers_one": "Followed by {name1}", + "account.familiar_followers_two": "Followed by {name1} and {name2}", "account.featured": "Featured", "account.featured.accounts": "Profiles", "account.featured.hashtags": "Hashtags", diff --git a/app/javascript/mastodon/reducers/accounts_familiar_followers.ts b/app/javascript/mastodon/reducers/accounts_familiar_followers.ts new file mode 100644 index 0000000000..8d1c994040 --- /dev/null +++ b/app/javascript/mastodon/reducers/accounts_familiar_followers.ts @@ -0,0 +1,19 @@ +import { createReducer } from '@reduxjs/toolkit'; + +import { fetchAccountsFamiliarFollowers } from '../actions/accounts_familiar_followers'; + +const initialState: Record = {}; + +export const accountsFamiliarFollowersReducer = createReducer( + initialState, + (builder) => { + builder.addCase( + fetchAccountsFamiliarFollowers.fulfilled, + (state, { payload }) => { + if (payload) { + state[payload.id] = payload.accountIds; + } + }, + ); + }, +); diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts index 0b6e66a1b2..d35d166115 100644 --- a/app/javascript/mastodon/reducers/index.ts +++ b/app/javascript/mastodon/reducers/index.ts @@ -4,6 +4,7 @@ import { loadingBarReducer } from 'react-redux-loading-bar'; import { combineReducers } from 'redux-immutable'; import { accountsReducer } from './accounts'; +import { accountsFamiliarFollowersReducer } from './accounts_familiar_followers'; import { accountsMapReducer } from './accounts_map'; import { alertsReducer } from './alerts'; import announcements from './announcements'; @@ -50,6 +51,7 @@ const reducers = { status_lists, accounts: accountsReducer, accounts_map: accountsMapReducer, + accounts_familiar_followers: accountsFamiliarFollowersReducer, statuses, relationships: relationshipsReducer, settings, diff --git a/app/javascript/mastodon/selectors/accounts.ts b/app/javascript/mastodon/selectors/accounts.ts index a33daee867..f9ba1a76a6 100644 --- a/app/javascript/mastodon/selectors/accounts.ts +++ b/app/javascript/mastodon/selectors/accounts.ts @@ -59,3 +59,16 @@ export const getAccountHidden = createSelector( return hidden && !(isSelf || followingOrRequested); }, ); + +export const getAccountFamiliarFollowers = createSelector( + [ + (state: RootState) => state.accounts, + (state: RootState, id: string) => state.accounts_familiar_followers[id], + ], + (accounts, accounts_familiar_followers) => { + if (!accounts_familiar_followers) return null; + return accounts_familiar_followers + .map((id) => accounts.get(id)) + .filter((f) => !!f); + }, +); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e22a9ed9c9..ea48d98ec6 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2167,6 +2167,25 @@ a .account__avatar { cursor: pointer; } +.avatar-group { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.avatar-group--compact { + gap: 0; + flex-wrap: nowrap; + + & > :not(:first-child) { + margin-inline-start: -8px; + + .account__avatar { + box-shadow: 0 0 0 2px var(--background-color); + } + } +} + .account__avatar-overlay { position: relative; @@ -8119,6 +8138,23 @@ noscript { } } } + + &__familiar-followers { + display: flex; + align-items: center; + gap: 10px; + margin-block-end: 16px; + color: $darker-text-color; + + a:any-link { + color: inherit; + text-decoration: underline; + } + + a:hover { + text-decoration: none; + } + } } .account__contents { @@ -10439,14 +10475,6 @@ noscript { } } - &__avatar-group { - display: flex; - gap: 8px; - height: 28px; - overflow-y: hidden; - flex-wrap: wrap; - } - .status { padding: 0; border: 0; From 050d76c010183a9056928cf30699d36f77242508 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 09:38:15 +0200 Subject: [PATCH 002/106] fix(deps): update dependency pg to v8.16.0 (#34670) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1d5b0a2a2d..7f2eaf0330 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12473,10 +12473,10 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.8.5": - version: 2.8.5 - resolution: "pg-connection-string@npm:2.8.5" - checksum: 10c0/5f65afc9dfc99ecf1583a1699c97511f3d505659c9c6a91db8cd0ffe862caa29060722712a034abd6da493356567261febf18b3a6ef223d0a219f0d50d959b97 +"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.9.0": + version: 2.9.0 + resolution: "pg-connection-string@npm:2.9.0" + checksum: 10c0/7145d00688200685a9d9931a7fc8d61c75f348608626aef88080ece956ceb4ff1cbdee29c3284e41b7a3345bab0e4f50f9edc256e270bfa3a563af4ea78bb490 languageName: node linkType: hard @@ -12494,23 +12494,23 @@ __metadata: languageName: node linkType: hard -"pg-pool@npm:^3.9.6": - version: 3.9.6 - resolution: "pg-pool@npm:3.9.6" +"pg-pool@npm:^3.10.0": + version: 3.10.0 + resolution: "pg-pool@npm:3.10.0" peerDependencies: pg: ">=8.0" - checksum: 10c0/458d50a4e7260977f076472d40d0796fa8b513af7e3ce1bf65557e10724e9c13653661c883f6650dff92d0a1a5ff4e7a001a8262b786c1ad4cfbd35c3354353e + checksum: 10c0/b36162dc98c0ad88cd26f3d65f3e3932c3f870abe7a88905f16fc98282e8131692903e482720ebc9698cb08851c9b19242ff16a50af7f9434c8bb0b5d33a9a9a languageName: node linkType: hard -"pg-protocol@npm:*, pg-protocol@npm:^1.9.5": - version: 1.9.5 - resolution: "pg-protocol@npm:1.9.5" - checksum: 10c0/5cb3444cf973adadd22ee9ea26bb1674f0d980ef8f9c66d426bbe67fc9cb5f0ca4a204bf7e432b3ab2ab59ac8227f4b18ab3b2e64eaed537e037e916991c7319 +"pg-protocol@npm:*, pg-protocol@npm:^1.10.0": + version: 1.10.0 + resolution: "pg-protocol@npm:1.10.0" + checksum: 10c0/7d0d64fe9df50262d907fd476454e1e36f41f5f66044c3ba6aa773fb8add1d350a9c162306e5c33e99bdfbdcc1140dd4ca74f66eda41d0aaceb5853244dcdb65 languageName: node linkType: hard -"pg-types@npm:^2.1.0": +"pg-types@npm:2.2.0": version: 2.2.0 resolution: "pg-types@npm:2.2.0" dependencies: @@ -12539,15 +12539,15 @@ __metadata: linkType: hard "pg@npm:^8.5.0": - version: 8.15.6 - resolution: "pg@npm:8.15.6" + version: 8.16.0 + resolution: "pg@npm:8.16.0" dependencies: pg-cloudflare: "npm:^1.2.5" - pg-connection-string: "npm:^2.8.5" - pg-pool: "npm:^3.9.6" - pg-protocol: "npm:^1.9.5" - pg-types: "npm:^2.1.0" - pgpass: "npm:1.x" + pg-connection-string: "npm:^2.9.0" + pg-pool: "npm:^3.10.0" + pg-protocol: "npm:^1.10.0" + pg-types: "npm:2.2.0" + pgpass: "npm:1.0.5" peerDependencies: pg-native: ">=3.0.1" dependenciesMeta: @@ -12556,11 +12556,11 @@ __metadata: peerDependenciesMeta: pg-native: optional: true - checksum: 10c0/d4dc81020ebd137b6cf6228e43c643067acb8240079a07f7b9a7e97be0f33ad4d8c6f2a3f5b512ad87180e3d48e651fbd72885aa807ab58a715da8f3efea0fab + checksum: 10c0/24542229c7e5cbf69d654de32e8cdc8302c73f1338e56728543cb16364fb319d5689e03fa704b69a208105c7065c867cfccb9dbccccea2020bb5c64ead785713 languageName: node linkType: hard -"pgpass@npm:1.x": +"pgpass@npm:1.0.5": version: 1.0.5 resolution: "pgpass@npm:1.0.5" dependencies: From 65baf9b04a4f43954aaa1f3c492f049b889362f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 09:47:56 +0200 Subject: [PATCH 003/106] New Crowdin Translations (automated) (#34672) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/lv.json | 2 +- config/locales/doorkeeper.lv.yml | 34 ++++++++++++------------- config/locales/eo.yml | 2 +- config/locales/it.yml | 2 +- config/locales/lv.yml | 14 +++++----- config/locales/simple_form.lv.yml | 6 ++--- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index 0c164fa765..b1fea93a90 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -39,7 +39,7 @@ "account.following": "Seko", "account.following_counter": "{count, plural, one {seko {counter}} other {seko {counter}}}", "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.", - "account.follows_you": "Seko tev", + "account.follows_you": "Seko Tev", "account.go_to_profile": "Doties uz profilu", "account.hide_reblogs": "Paslēpt @{name} pastiprinātos ierakstus", "account.in_memoriam": "Piemiņai.", diff --git a/config/locales/doorkeeper.lv.yml b/config/locales/doorkeeper.lv.yml index 15c1e7692a..4b8116e6af 100644 --- a/config/locales/doorkeeper.lv.yml +++ b/config/locales/doorkeeper.lv.yml @@ -19,7 +19,7 @@ lv: doorkeeper: applications: buttons: - authorize: Autorizēt + authorize: Pilnvarot cancel: Atcelt destroy: Iznīcināt edit: Labot @@ -55,40 +55,40 @@ lv: title: 'Lietotne: %{name}' authorizations: buttons: - authorize: Autorizēt - deny: Aizliegt + authorize: Pilnvarot + deny: Noraidīt error: title: Radās kļūda new: prompt_html: "%{client_name} vēlas atļauju piekļūt Tavam kontam. Apstiprini šo pieprasījumu tikai tad, ja atpazīsti un uzticies šim avotam!" review_permissions: Pārskatīt atļaujas - title: Nepieciešama autorizācija + title: Nepieciešama pilnvarošana show: - title: Nokopē šo autorizācijas kodu un ielīmē to lietotnē. + title: Šis pilnvarošanas kods jāievieto starpliktuvē un jāielīmē lietotnē. authorized_applications: buttons: revoke: Atsaukt confirmations: revoke: Vai tiešām? index: - authorized_at: Autorizētas %{date} + authorized_at: Pilnvarotas %{date} description_html: Šīs ir lietotnes, kas var piekļūt Tavam kontam ar API. Ja šeit ir lietotnes, kuras neatpazīsti, vai lietotne darbojas ne tā, kā paredzēts, vari atsaukt tās piekļuvi. last_used_at: Pēdējo reizi lietotas %{date} never_used: Nekad nav lietotas scopes: Atļaujas superapp: Iekšējs - title: Tevis autorizētās lietotnes + title: Tevis pilnvarotās lietotnes errors: messages: - access_denied: Resursa īpašnieks vai autorizācijas serveris pieprasījumu noraidīja. + access_denied: Resursa īpašnieks vai pilnvarošanas serveris noraidīja pieprasījumu. credential_flow_not_configured: Resursa īpašnieka paroles akreditācijas datu plūsma neizdevās, jo Doorkeeper.configure.resource_owner_from_credentials nebija konfigurēts. invalid_client: Klienta autentifikācija neizdevās nezināma klienta, klienta autentifikācijas vai neatbalstītas autentifikācijas metodes dēļ. invalid_code_challenge_method: Koda izaicinājuma veidam jābūt S256, vienkāršs netiek atbalstīts. - invalid_grant: Sniegtā autorizācijas piekrišana nav derīga, tai ir beidzies derīguma termiņš, tā ir atsaukta, tā neatbilst autorizācijas pieprasījumā izmantotajam novirzīšanas URI vai tika izsniegta citam klientam. + invalid_grant: Sniegtais pilnvarošanas piešķīrums nav derīgs, tam ir beidzies derīgums, tas ir atsaukts, tas neatbilst pilnvarošanas pieprasījumā izmantotajam pārvirzīšanas URI vai tika izsniegts citam klientam. invalid_redirect_uri: Iekļauts novirzīšanas uri nav derīgs. invalid_request: missing_param: 'Trūkst pieprasītā parametra: %{value}.' - request_not_authorized: Pieprasījums ir jāautorizē. Trūkst vai nav derīgs pieprasījuma autorizēšanai nepieciešamais parametrs. + request_not_authorized: Pieprasījums ir jāpilnvaro. Trūkst vai nav derīgas pieprasījuma pilnvarošanai nepieciešamās vērtības. unknown: Pieprasījumā trūkst nepieciešamā parametra, tajā ir neatbalstīta parametra vērtība vai tas ir citādi nepareizi veidots. invalid_resource_owner: Norādītie resursa īpašnieka akreditācijas dati nav derīgi, vai arī resursa īpašnieku nevar atrast invalid_scope: Pieprasītā darbības joma nav derīga, nav zināma vai ir nepareizi veidota. @@ -97,11 +97,11 @@ lv: revoked: Piekļuves pilnvara tika atsaukta unknown: Piekļuves pilnvara nav derīga resource_owner_authenticator_not_configured: Resursa īpašnieka atrašana neizdevās, jo Doorkeeper.configure.resource_owner_authenticator nav savienots. - server_error: Autorizācijas serverim radās neparedzēts nosacījums, kas neļāva izpildīt pieprasījumu. - temporarily_unavailable: Autorizācijas serveris pašlaik nevar apstrādāt pieprasījumu servera īslaicīgas pārslodzes vai apkopes dēļ. + server_error: Pilnvarošanas serverim radās neparedzēti apstākļi, kas tam neļāva izpildīt pieprasījumu. + temporarily_unavailable: Pilnvarošanas serveris šobrīd nevar apstrādāt pieprasījumu servera īslaicīgas pārslodzes vai uzturēšanas darbu dēļ. unauthorized_client: Klients nav pilnvarots izpildīt šo pieprasījumu, izmantojot šo metodi. - unsupported_grant_type: Autorizācijas serveris neatbalsta atļaujas piešķiršanas veidu. - unsupported_response_type: Autorizācijas serveris neatbalsta šo atbildes veidu. + unsupported_grant_type: Pilnvarošanas serveris neatbalsta atļaujas piešķiršanas veidu. + unsupported_response_type: Pilnvarošanas serveris neatbalsta šo atbildes veidu. flash: applications: create: @@ -123,14 +123,14 @@ lv: admin/accounts: Kontu pārvaldīšana admin/all: Visas administrēšanas funkcijas admin/reports: Ziņojumu pārvaldīšana - all: Pilna piekļuve tavam Mastodon kontam + all: Pilna piekļuve Tavam Mastodon kontam blocks: Bloķētie bookmarks: Grāmatzīmes conversations: Sarunas crypto: Pilnīga šifrēšana favourites: Izlase filters: Filtri - follow: Seko, Izslēdz un Bloķē + follow: Seko, apklusina un liedz follows: Seko lists: Saraksti media: Multividesu pielikumi @@ -147,7 +147,7 @@ lv: applications: Lietotnes oauth2_provider: OAuth2 nodrošinātājs application: - title: OAuth nepieciešama autorizācija + title: Nepieciešama OAuth pilnvarošana scopes: admin:read: lasīt visus datus uz servera admin:read:accounts: lasīt jūtīgu informāciju no visiem kontiem diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 850433d44b..40b7bc898e 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -799,7 +799,7 @@ eo: title: Pri appearance: preamble: Personecigi retinterfaco de Mastodon. - title: Apero + title: Aspekto branding: preamble: Via markeco de servilo malsamigas ĝin de aliaj servilojn en la reto. Do, la informo devus esti simpla kaj mallonga. title: Markeco diff --git a/config/locales/it.yml b/config/locales/it.yml index 62b849b066..9894e99199 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1220,7 +1220,7 @@ it: back: Indietro invited_by: 'Puoi unirti a %{domain} grazie all''invito che hai ricevuto da:' preamble: Questi sono impostati e applicati dai moderatori di %{domain}. - preamble_invited: Prima di procedere, si prega di considera le regole di base stabilite dai moderatori di %{domain}. + preamble_invited: Prima di procedere, si prega di considerare le regole di base stabilite dai moderatori di %{domain}. title: Alcune regole di base. title_invited: Sei stato/a invitato/a. security: Credenziali diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 90cc3f9999..1538dfb363 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -17,7 +17,7 @@ lv: link_verified_on: Šīs saites piederība tika pārbaudīta %{date} nothing_here: Šeit nekā nav. pin_errors: - following: Tev ir jāseko personai, kuru vēlies atbalstīt + following: Tev ir jāseko cilvēkam, kuru vēlies atbalstīt posts: one: Ieraksts other: Ieraksti @@ -475,7 +475,7 @@ lv: import: description_html: Tu gatavojies importēt domēna bloku sarakstu. Lūdzu, ļoti rūpīgi pārskati šo sarakstu, it īpaši, ja tu pats neesi to veidojis. existing_relationships_warning: Esošās sekošanas attiecības - private_comment_description_html: 'Lai palīdzētu tev izsekot, no kurienes nāk importētie bloki, tiks izveidoti importētie bloki ar šādu privātu komentāru: %{comment}' + private_comment_description_html: 'Lai palīdzētu Tev izsekot ievietoto bloku izcelsmei, tiks izveidoti ievietotie bloki ar šādu privātu piebildi: %{comment}' private_comment_template: Importēt no %{source} %{date} title: Importēt bloķētos domēnus invalid_domain_block: 'Viens vai vairāki domēna bloķi tika izlaisti šādas kļūdas(-u) dēļ: %{error}' @@ -1659,12 +1659,12 @@ lv: subject: "%{name} pievienoja tavu ziņu izlasei" title: Jauna izlase follow: - body: "%{name} tagad tev seko!" - subject: "%{name} tagad tev seko" + body: "%{name} tagad seko Tev." + subject: "%{name} tagad seko Tev" title: Jauns sekotājs follow_request: action: Pārvaldīt sekošanas pieprasījumus - body: "%{name} vēlas tev sekot" + body: "%{name} vēlas Tev sekot" subject: 'Gaidošs sekotājs: %{name}' title: Jauns sekotāja pieprasījums mention: @@ -1731,7 +1731,7 @@ lv: privacy: Privātums privacy_hint_html: Kontrolē, cik daudz vēlies izpaust citu labā. Cilvēki atklāj interesantus profilus un lieliskas lietotnes, pārlūkojot citu cilvēku sekotājus un redzot, no kurām lietotnēm viņi izliek ziņas, taču tu, iespējams, vēlēsies to slēpt. reach: Sasniedzamība - reach_hint_html: Kontrolē, vai vēlies, lai tevi atklātu un sekotu jauni cilvēki. Vai vēlies, lai tavas ziņas tiktu parādītas ekrānā Izpēte? Vai vēlies, lai citi cilvēki tevi redzētu savos ieteikumos? Vai vēlies automātiski pieņemt visus jaunos sekotājus vai arī tev ir pilnīga kontrole pār katru? + reach_hint_html: Pārvaldi, vai vēlies, lai Tevi atklātu un sekotu jauni cilvēki. Vai vēlies, lai Tavas ziņas tiktu parādītas skatā "Izpēte"? Vai vēlies, lai citi cilvēki Tevi redzētu savos ieteikumos? Vai vēlies automātiski pieņemt visus jaunos sekotājus vai iegūt izvērstu pārvaldību par katru atsevišķi? search: Meklēt search_hint_html: Nosaki, kā vēlies tikt atrasts! Vai vēlies, lai cilvēki Tevi atrod pēc tā, par ko esi veicis visiem redzamus ierakstus? Vai vēlies, lai cilvēki ārpus Mastodon atrastu Tavu profilu, meklējot tīmeklī? Lūdzu, ņem vērā, ka nevar nodrošināt visiem redzamas informācijas pilnīgu izslēgšanu no visām meklētājiem! title: Privātums un sasniedzamība @@ -2030,7 +2030,7 @@ lv: disable: Tu vairs nevari izmantot savu kontu, taču tavs profils un citi dati paliek neskarti. Tu vari pieprasīt savu datu dublējumu, mainīt konta iestatījumus vai dzēst kontu. mark_statuses_as_sensitive: "%{instance} satura pārraudzītāji dažus no Taviem ierakstiem ir atzīmējuši kā jūtīgus. Tas nozīmē, ka cilvēkiem būs jāpiesit ierakstos esošajiem informāijas nesējiem, pirms tiek attēlots to priekšskatījums. Tu pats vari atzīmēt informācijas nesēju kā jūtīgu, kad nākotnē tādu ievietosi." sensitive: Turpmāk visi augšupielādētās informācijas nesēju datnes tiks atzīmētas kā jūtīgas un paslēptas aiz klikšķināma brīdinājuma. - silence: Tu joprojām vari izmantot savu kontu, taču tikai tie cilvēki, kuri jau tev seko, redzēs tavas ziņas šajā serverī, un tev var tikt liegtas dažādas atklāšanas funkcijas. Tomēr citi joprojām var tev manuāli sekot. + silence: Tu joprojām vari izmantot savu kontu, taču tikai tie cilvēki, kuri jau seko Tev, redzēs Tavas ziņas šajā serverī, un Tevi var neiekļaut dažādās atklāšanas iespējās. Tomēr citi joprojām var pašrocīgi sekot Tev. suspend: Tu vairs nevari izmantot savu kontu, un tavs profils un citi dati vairs nav pieejami. Tu joprojām vari pieteikties, lai pieprasītu savu datu dublēšanu, līdz dati tiks pilnībā noņemti aptuveni 30 dienu laikā, taču mēs saglabāsim dažus pamata datus, lai neļautu tev izvairīties no apturēšanas. reason: 'Iemesls:' statuses: 'Citētās ziņas:' diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index 0743ff1127..fad16d86ea 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -39,7 +39,7 @@ lv: appeal: text: Brīdinājumu var pārsūdzēt tikai vienu reizi defaults: - autofollow: Cilvēki, kuri reģistrējas, izmantojot uzaicinājumu, automātiski sekos tev + autofollow: Cilvēki, kuri reģistrēsies ar uzaicinājumu, automātiski sekos Tev avatar: WEBP, PNG, GIF vai JPG. Ne vairāk kā %{size}. Tiks samazināts līdz %{dimensions}px bot: Paziņo citiem, ka kontā galvenokārt tiek veiktas automatizētas darbības un tas var netikt uzraudzīts context: Viens vai vairāki konteksti, kur jāpiemēro filtrs @@ -308,8 +308,8 @@ lv: appeal: Kāds pārsūdz moderatora lēmumu digest: Sūtīt kopsavilkumu e-pastus favourite: Kāds izcēla tavu ziņu - follow: Kāds uzsāka tev sekot - follow_request: Kāds vēlas tev sekot + follow: Kāds uzsāka Tev sekot + follow_request: Kāds vēlas Tev sekot mention: Kāds pieminēja tevi pending_account: Jāpārskata jaunu kontu reblog: Kāds izcēla tavu ierakstu From f993d7578b9e2767fa3e62ee81aace459fccc640 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 May 2025 13:29:22 +0200 Subject: [PATCH 004/106] Use plural forms for `date_of_birth` prompt (#34636) --- app/views/auth/registrations/new.html.haml | 2 +- config/locales/simple_form.en.yml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 25479104ee..22f53a4f50 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -56,7 +56,7 @@ .fields-group = f.input :date_of_birth, as: :date_of_birth, - hint: t('simple_form.hints.user.date_of_birth', age: Setting.min_age.to_i), + hint: t('simple_form.hints.user.date_of_birth', count: Setting.min_age.to_i), required: true, wrapper: :with_block_label diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 5e162a0d64..9992974027 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -148,7 +148,9 @@ en: min_age: Should not be below the minimum age required by the laws of your jurisdiction. user: chosen_languages: When checked, only posts in selected languages will be displayed in public timelines - date_of_birth: We have to make sure you're at least %{age} to use Mastodon. We won't store this. + date_of_birth: + one: We have to make sure you're at least %{count} to use Mastodon. We won't store this. + other: We have to make sure you're at least %{count} to use Mastodon. We won't store this. role: The role controls which permissions the user has. user_role: color: Color to be used for the role throughout the UI, as RGB in hex format From 5305e939c4e4af12dbad8246d9421164debc397c Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 13 May 2025 13:47:38 +0200 Subject: [PATCH 005/106] Add (currently unused) setting for default quote policy (#34623) --- app/models/user_settings.rb | 1 + app/views/settings/preferences/other/show.html.haml | 11 +++++++++++ config/locales/en.yml | 4 ++++ config/locales/simple_form.en.yml | 2 ++ 4 files changed, 18 insertions(+) diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 4151a9afed..590eed713c 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -15,6 +15,7 @@ class UserSettings setting :default_language, default: nil setting :default_sensitive, default: false setting :default_privacy, default: nil, in: %w(public unlisted private) + setting :default_quote_policy, default: 'public', in: %w(public followers nobody) setting_inverse_alias :indexable, :noindex diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index 92df8e5213..dd79695db7 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -38,6 +38,17 @@ required: false, wrapper: :with_label + .fields-row + .fields-group.fields-row__column.fields-row__column-6 + = ff.input :default_quote_policy, + collection: %w(public followers nobody), + include_blank: false, + label_method: ->(policy) { I18n.t("statuses.quote_policies.#{policy}") }, + label: I18n.t('simple_form.labels.defaults.setting_default_quote_policy'), + hint: I18n.t('simple_form.hints.defaults.setting_default_quote_policy'), + required: false, + wrapper: :with_label + .fields-group = ff.input :default_sensitive, hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive'), diff --git a/config/locales/en.yml b/config/locales/en.yml index 63ef106d5c..83695cd6e7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1859,6 +1859,10 @@ en: limit: You have already pinned the maximum number of posts ownership: Someone else's post cannot be pinned reblog: A boost cannot be pinned + quote_policies: + followers: Followers and mentioned users + nobody: Only mentioned users + public: Everyone title: '%{name}: "%{quote}"' visibilities: direct: Direct diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 9992974027..bb48cddff5 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -56,6 +56,7 @@ en: scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones. setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts) setting_always_send_emails: Normally e-mail notifications won't be sent when you are actively using Mastodon + setting_default_quote_policy: Mentioned users are always allowed to quote. This setting will only take effect for posts created with the next Mastodon version, but you can select your preference in preparation setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click setting_display_media_default: Hide media marked as sensitive setting_display_media_hide_all: Always hide media @@ -231,6 +232,7 @@ en: setting_boost_modal: Show confirmation dialog before boosting setting_default_language: Posting language setting_default_privacy: Posting privacy + setting_default_quote_policy: Who can quote setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialog before deleting a post setting_disable_hover_cards: Disable profile preview on hover From cc57fa4a4158ca5a349a887b8344d28446a768ea Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 13 May 2025 14:22:51 +0200 Subject: [PATCH 006/106] docs: Improve local development readme (#34675) --- docs/DEVELOPMENT.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index bbbad6f1e1..6dd439ddc7 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -7,6 +7,19 @@ what changes are desirable and what general processes to use. ## Environments +The following instructions will guide you through the process of setting up a local development instance of Mastodon on your computer. + +There are instructions for these environments: + +- [Vagrant](#vagrant) +- [macOS](#macos) +- [Linux](#linux) +- [Docker](#docker) +- [Dev Containers](#dev-containers) +- [GitHub Codespaces](#github-codespaces) + +Once completed, continue with the [Next steps](#next-steps) section below. + ### Vagrant A **Vagrant** configuration is included for development purposes. To use it, @@ -28,11 +41,17 @@ To set up **macOS** for native development, complete the following steps: to install the required project dependencies - Use a Ruby version manager to activate the ruby in `.ruby-version` and run `nvm use` to activate the node version from `.nvmrc` -- Run the `bin/setup` script, which will install the required ruby gems and node +- Start the database services by running `brew services start postgresql` and + `brew services start redis` +- Run `RAILS_ENV=development bin/setup`, which will install the required ruby gems and node packages and prepare the database for local development - Finally, run the `bin/dev` script which will launch services via `overmind` (if installed) or `foreman` +### Linux + +The Mastodon documentation has a [guide on installing Mastodon from source](https://docs.joinmastodon.org/dev/setup/#manual) on Linux. + ### Docker For production hosting and deployment with **Docker**, use the `Dockerfile` and @@ -66,6 +85,12 @@ development environment configured with the software needed for this project. - Wait for an _Open in Browser_ prompt. This will open Mastodon - On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_ +## Next steps + +- Once you have successfully set up a development environment, it will be available on http://localhost:3000 +- Log in as the default admin user with the username `admin@mastodon.local` and the password `mastodonadmin`. +- Check out the [Mastodon docs] for tips on working with emails in development (you'll need this when creating new user accounts) as well as a list of useful commands for testing and updating your dev instance. + [codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json [CONTRIBUTING]: ../CONTRIBUTING.md [Dev Container extension]: https://containers.dev/supporting#dev-containers @@ -73,3 +98,4 @@ development environment configured with the software needed for this project. [Docker]: https://docs.docker.com [GitHub Codespaces]: https://docs.github.com/en/codespaces [Homebrew]: https://brew.sh +[Mastodon docs]: https://docs.joinmastodon.org/dev/setup/#working-with-emails-in-development From 865a30ab0de53204e36e69f4a6f5f8b891bf7ebc Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 13 May 2025 19:55:16 +0200 Subject: [PATCH 007/106] fix: Hide FamiliarFollowers widget when logged out (#34678) --- .../features/account_timeline/components/account_header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index 35554d9735..d26286e4fe 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -1023,7 +1023,7 @@ export const AccountHeader: React.FC<{ />
- + {signedIn && }
)}
From 545e8fbd0cfe9ab43d20f1c5cc7330e1a5461732 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 14 May 2025 08:39:05 +0200 Subject: [PATCH 008/106] Fix emoji bundle size (#34677) --- .../components/emoji_picker_dropdown.jsx | 32 +++-------- .../features/emoji/emoji_compressed.d.ts | 1 - .../features/emoji/emoji_compressed.js | 3 +- .../mastodon/features/emoji/emoji_picker.js | 7 --- .../mastodon/features/emoji/emoji_picker.tsx | 53 +++++++++++++++++++ app/javascript/types/emoji_picker.d.ts | 8 +++ tsconfig.json | 1 + 7 files changed, 70 insertions(+), 35 deletions(-) delete mode 100644 app/javascript/mastodon/features/emoji/emoji_picker.js create mode 100644 app/javascript/mastodon/features/emoji/emoji_picker.tsx create mode 100644 app/javascript/types/emoji_picker.d.ts diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx index 2bd6af4cf4..1d96427f6b 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx @@ -12,14 +12,10 @@ import Overlay from 'react-overlays/Overlay'; import MoodIcon from '@/material-icons/400-20px/mood.svg?react'; import { IconButton } from 'mastodon/components/icon_button'; -import emojiCompressed from 'mastodon/features/emoji/emoji_compressed'; -import { assetHost } from 'mastodon/utils/config'; import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji'; import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; -const nimblePickerData = emojiCompressed[5]; - const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' }, @@ -40,19 +36,11 @@ let EmojiPicker, Emoji; // load asynchronously const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true; -const backgroundImageFn = () => `${assetHost}/emoji/sheet_15_1.png`; - const notFoundFn = () => (
@@ -110,12 +98,12 @@ class ModifierPickerMenu extends PureComponent { return (
- - - - - - + + + + + +
); } @@ -150,7 +138,7 @@ class ModifierPicker extends PureComponent { return (
- +
); @@ -286,16 +274,11 @@ class EmojiPickerMenuImpl extends PureComponent { return (
`${assetHost}/emoji/sheet_15_1.png`; + +const Emoji = ({ + set = 'twitter', + sheetSize = 32, + sheetColumns = 62, + sheetRows = 62, + backgroundImageFn = backgroundImageFnDefault, + ...props +}: EmojiProps) => { + return ( + + ); +}; + +const Picker = ({ + set = 'twitter', + sheetSize = 32, + sheetColumns = 62, + sheetRows = 62, + backgroundImageFn = backgroundImageFnDefault, + ...props +}: PickerProps) => { + return ( + + ); +}; + +export { Picker, Emoji }; diff --git a/app/javascript/types/emoji_picker.d.ts b/app/javascript/types/emoji_picker.d.ts new file mode 100644 index 0000000000..4649da7ed0 --- /dev/null +++ b/app/javascript/types/emoji_picker.d.ts @@ -0,0 +1,8 @@ +declare module 'emoji-mart' { + interface PickerProps { + sheetColumns?: number; + sheetRows?: number; + } +} + +export {}; diff --git a/tsconfig.json b/tsconfig.json index 4eb94e4318..37c9e87872 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "module": "CommonJS", "moduleResolution": "node", "allowJs": true, + "resolveJsonModule": true, "noEmit": true, "strict": true, "noImplicitReturns": true, From 16e36d84771a0d11f95f7fb92c4a33befe778679 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 08:57:27 +0200 Subject: [PATCH 009/106] chore(deps): update dependency database_cleaner-active_record to v2.2.1 (#34674) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 131f4933c7..2ccbae6b1b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,7 +171,7 @@ GEM css_parser (1.21.1) addressable csv (3.3.4) - database_cleaner-active_record (2.2.0) + database_cleaner-active_record (2.2.1) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) From 8d5b73d70da8787e020cbd7f6a89f44b4d4b688c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 07:00:32 +0000 Subject: [PATCH 010/106] New Crowdin Translations (automated) (#34679) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/br.json | 3 ++ app/javascript/mastodon/locales/cs.json | 3 ++ app/javascript/mastodon/locales/da.json | 3 ++ app/javascript/mastodon/locales/de.json | 3 ++ app/javascript/mastodon/locales/es-AR.json | 3 ++ app/javascript/mastodon/locales/es-MX.json | 3 ++ app/javascript/mastodon/locales/es.json | 3 ++ app/javascript/mastodon/locales/fi.json | 3 ++ app/javascript/mastodon/locales/fo.json | 3 ++ app/javascript/mastodon/locales/gl.json | 3 ++ app/javascript/mastodon/locales/he.json | 3 ++ app/javascript/mastodon/locales/hu.json | 9 +++++ app/javascript/mastodon/locales/is.json | 3 ++ app/javascript/mastodon/locales/lv.json | 4 +- app/javascript/mastodon/locales/nl.json | 3 ++ app/javascript/mastodon/locales/pt-PT.json | 3 ++ app/javascript/mastodon/locales/ru.json | 47 ++++++++++++---------- app/javascript/mastodon/locales/sq.json | 3 ++ app/javascript/mastodon/locales/tr.json | 3 ++ app/javascript/mastodon/locales/vi.json | 3 ++ app/javascript/mastodon/locales/zh-TW.json | 3 ++ config/locales/br.yml | 2 + config/locales/cs.yml | 4 ++ config/locales/da.yml | 4 ++ config/locales/de.yml | 4 ++ config/locales/devise.lv.yml | 2 +- config/locales/es-AR.yml | 4 ++ config/locales/es-MX.yml | 4 ++ config/locales/es.yml | 4 ++ config/locales/fi.yml | 4 ++ config/locales/fo.yml | 4 ++ config/locales/gl.yml | 4 ++ config/locales/he.yml | 4 ++ config/locales/is.yml | 4 ++ config/locales/lv.yml | 10 ++--- config/locales/nl.yml | 4 ++ config/locales/simple_form.bg.yml | 1 - config/locales/simple_form.ca.yml | 1 - config/locales/simple_form.cs.yml | 8 +++- config/locales/simple_form.cy.yml | 1 - config/locales/simple_form.da.yml | 3 +- config/locales/simple_form.de.yml | 6 ++- config/locales/simple_form.el.yml | 1 - config/locales/simple_form.eo.yml | 1 - config/locales/simple_form.es-AR.yml | 6 ++- config/locales/simple_form.es-MX.yml | 6 ++- config/locales/simple_form.es.yml | 6 ++- config/locales/simple_form.fi.yml | 6 ++- config/locales/simple_form.fo.yml | 6 ++- config/locales/simple_form.fr-CA.yml | 1 - config/locales/simple_form.fr.yml | 1 - config/locales/simple_form.fy.yml | 1 - config/locales/simple_form.ga.yml | 1 - config/locales/simple_form.gd.yml | 1 - config/locales/simple_form.gl.yml | 1 - config/locales/simple_form.he.yml | 8 +++- config/locales/simple_form.hu.yml | 4 +- config/locales/simple_form.is.yml | 6 ++- config/locales/simple_form.it.yml | 1 - config/locales/simple_form.ja.yml | 1 - config/locales/simple_form.ko.yml | 1 - config/locales/simple_form.lt.yml | 1 - config/locales/simple_form.lv.yml | 3 +- config/locales/simple_form.nl.yml | 6 ++- config/locales/simple_form.nn.yml | 1 - config/locales/simple_form.pt-BR.yml | 1 - config/locales/simple_form.pt-PT.yml | 1 - config/locales/simple_form.ru.yml | 1 - config/locales/simple_form.sl.yml | 1 - config/locales/simple_form.sq.yml | 3 +- config/locales/simple_form.tr.yml | 1 - config/locales/simple_form.uk.yml | 1 - config/locales/simple_form.vi.yml | 5 ++- config/locales/simple_form.zh-CN.yml | 1 - config/locales/simple_form.zh-TW.yml | 5 ++- config/locales/sq.yml | 4 ++ config/locales/vi.yml | 4 ++ config/locales/zh-TW.yml | 4 ++ 78 files changed, 228 insertions(+), 70 deletions(-) diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index 8e93da5b82..d327f0965b 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -25,6 +25,9 @@ "account.edit_profile": "Kemmañ ar profil", "account.enable_notifications": "Ma c'hemenn pa vez embannet traoù gant @{name}", "account.endorse": "Lakaat war-wel war ar profil", + "account.familiar_followers_many": "Heuilhet gant {name1}, {name2}, {othersCount, plural, one {hag # all} two {ha # all} few {ha # all} many {ha(g) # all} other {hag(g) # all}}", + "account.familiar_followers_one": "Heuilhet gant {name1}", + "account.familiar_followers_two": "Heuilhet gant {name1} ha {name2}", "account.featured_tags.last_status_at": "Toud diwezhañ : {date}", "account.featured_tags.last_status_never": "Embannadur ebet", "account.follow": "Heuliañ", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 1e8037dff3..679de9e724 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -28,6 +28,9 @@ "account.edit_profile": "Upravit profil", "account.enable_notifications": "Oznamovat mi příspěvky @{name}", "account.endorse": "Zvýraznit na profilu", + "account.familiar_followers_many": "Sleduje je {name1}, {name2} a {othersCount, plural, one {# další} few {# další} many {# dalších} other {# dalších}}", + "account.familiar_followers_one": "Sleduje je {name1}", + "account.familiar_followers_two": "Sleduje je {name1} a {name2}", "account.featured": "Zvýrazněné", "account.featured.accounts": "Profily", "account.featured.hashtags": "Hashtagy", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 143734a38c..e50023c346 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -28,6 +28,9 @@ "account.edit_profile": "Redigér profil", "account.enable_notifications": "Advisér mig, når @{name} poster", "account.endorse": "Fremhæv på profil", + "account.familiar_followers_many": "Følges af {name1}, {name2} og {othersCount, plural, one {# mere} other {# mere}}", + "account.familiar_followers_one": "Følges af {name1}", + "account.familiar_followers_two": "Følges af {name1} og {name2}", "account.featured": "Fremhævet", "account.featured.accounts": "Profiler", "account.featured.hashtags": "Hashtags", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index cf6f4786e7..2464182d52 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -28,6 +28,9 @@ "account.edit_profile": "Profil bearbeiten", "account.enable_notifications": "Benachrichtige mich wenn @{name} etwas postet", "account.endorse": "Im Profil vorstellen", + "account.familiar_followers_many": "Gefolgt von {name1}, {name2} und {othersCount, plural, one {# Profil} other {# weiteren Profilen}}", + "account.familiar_followers_one": "Gefolgt von {name1}", + "account.familiar_followers_two": "Gefolgt von {name1} und {name2}", "account.featured": "Vorgestellt", "account.featured.accounts": "Profile", "account.featured.hashtags": "Hashtags", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 83011e0f29..96a3af6d2f 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -28,6 +28,9 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} envíe mensajes", "account.endorse": "Destacar en el perfil", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# cuenta más} other {# cuentas más}}", + "account.familiar_followers_one": "Seguido por {name1}", + "account.familiar_followers_two": "Seguido por {name1} y {name2}", "account.featured": "Destacados", "account.featured.accounts": "Perfiles", "account.featured.hashtags": "Etiquetas", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 43e9706a62..b28ec59b7d 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -28,6 +28,9 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.endorse": "Destacar en mi perfil", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural,one {# otro} other {# otros}}", + "account.familiar_followers_one": "Seguido por {name1}", + "account.familiar_followers_two": "Seguid por {name1} y {name2}", "account.featured": "Destacado", "account.featured.accounts": "Perfiles", "account.featured.hashtags": "Etiquetas", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 9116c04360..54f100db71 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -28,6 +28,9 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.endorse": "Destacar en el perfil", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# otro} other {# otros}}", + "account.familiar_followers_one": "Seguido por {name1}", + "account.familiar_followers_two": "Seguido por {name1} y {name2}", "account.featured": "Destacado", "account.featured.accounts": "Perfiles", "account.featured.hashtags": "Etiquetas", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 4f2f9d2a9f..7380220e8a 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -28,6 +28,9 @@ "account.edit_profile": "Muokkaa profiilia", "account.enable_notifications": "Ilmoita minulle, kun @{name} julkaisee", "account.endorse": "Suosittele profiilissa", + "account.familiar_followers_many": "Seuraajina {name1}, {name2} ja {othersCount, plural, one {# muu} other {# muuta}}", + "account.familiar_followers_one": "Seuraajana {name1}", + "account.familiar_followers_two": "Seuraajina {name1} ja {name2}", "account.featured": "Suositellut", "account.featured.accounts": "Profiilit", "account.featured.hashtags": "Aihetunnisteet", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index efefbc752c..281232c1e3 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -28,6 +28,9 @@ "account.edit_profile": "Broyt vanga", "account.enable_notifications": "Boða mær frá, tá @{name} skrivar", "account.endorse": "Víst á vangamyndini", + "account.familiar_followers_many": "{name1}, {name2} og {othersCount, plural, one {# annar} other {# onnur}} fylgja", + "account.familiar_followers_one": "{name1} fylgir", + "account.familiar_followers_two": "{name1} og {name2} fylgja", "account.featured": "Tikin fram", "account.featured.accounts": "Vangar", "account.featured.hashtags": "Frámerki", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index c527c8a7ca..6618f26cca 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -28,6 +28,9 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Noficarme cando @{name} publique", "account.endorse": "Amosar no perfil", + "account.familiar_followers_many": "Seguida por {name1}, {name2}, e {othersCount, plural,one {# máis} other {# máis}}", + "account.familiar_followers_one": "Seguida por {name1}", + "account.familiar_followers_two": "Seguida por {name1} e {name2}", "account.featured": "Destacado", "account.featured.accounts": "Perfís", "account.featured.hashtags": "Cancelos", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 859dfd1e9d..bbef04b893 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -28,6 +28,9 @@ "account.edit_profile": "עריכת פרופיל", "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": "תגיות", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 3bbddd7560..5bd0c90c10 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -28,7 +28,11 @@ "account.edit_profile": "Profil szerkesztése", "account.enable_notifications": "Figyelmeztessen, ha @{name} bejegyzést tesz közzé", "account.endorse": "Kiemelés a profilodon", + "account.familiar_followers_many": "{name1}, {name2} és {othersCount, plural, one {# másik} other {# másik}} követi", + "account.familiar_followers_one": "{name1} követi", + "account.familiar_followers_two": "{name1} és {name2} követi", "account.featured": "Kiemelt", + "account.featured.accounts": "Profilok", "account.featured.hashtags": "Hashtagek", "account.featured.posts": "Bejegyzések", "account.featured_tags.last_status_at": "Legutolsó bejegyzés ideje: {date}", @@ -405,8 +409,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} résztvevő} other {{counter} résztvevő}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} bejegyzés} other {{counter} bejegyzés}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} bejegyzés} other {{counter} bejegyzés}} ma", + "hashtag.feature": "Kiemelés a profilodon", "hashtag.follow": "Hashtag követése", "hashtag.mute": "#{hashtag} némítása", + "hashtag.unfeature": "Ne legyen kiemelve a profilodon", "hashtag.unfollow": "Hashtag követésének megszüntetése", "hashtags.and_other": "…és {count, plural, other {# további}}", "hints.profiles.followers_may_be_missing": "A profil követői lehet, hogy hiányoznak.", @@ -561,6 +567,7 @@ "navigation_bar.mutes": "Némított felhasználók", "navigation_bar.opened_in_classic_interface": "A bejegyzések, fiókok és más speciális oldalak alapértelmezés szerint a klasszikus webes felületen nyílnak meg.", "navigation_bar.personal": "Személyes", + "navigation_bar.pins": "Kiemelt bejegyzések", "navigation_bar.preferences": "Beállítások", "navigation_bar.public_timeline": "Föderációs idővonal", "navigation_bar.search": "Keresés", @@ -856,6 +863,7 @@ "status.mute": "@{name} némítása", "status.mute_conversation": "Beszélgetés némítása", "status.open": "Bejegyzés kibontása", + "status.pin": "Kiemelés a profilodon", "status.read_more": "Bővebben", "status.reblog": "Megtolás", "status.reblog_private": "Megtolás az eredeti közönségnek", @@ -880,6 +888,7 @@ "status.translated_from_with": "{lang} nyelvről fordítva {provider} szolgáltatással", "status.uncached_media_warning": "Előnézet nem érhető el", "status.unmute_conversation": "Beszélgetés némításának feloldása", + "status.unpin": "Ne legyen kiemelve a profilodon", "subscribed_languages.lead": "A változtatás után csak a kiválasztott nyelvű bejegyzések fognak megjelenni a kezdőlapon és az idővonalakon. Ha egy sincs kiválasztva, akkor minden nyelven megjelennek a bejegyzések.", "subscribed_languages.save": "Változások mentése", "subscribed_languages.target": "Feliratkozott nyelvek módosítása {target} esetében", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 3f3bf4e707..707bdbd2bb 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -28,6 +28,9 @@ "account.edit_profile": "Breyta notandasniði", "account.enable_notifications": "Láta mig vita þegar @{name} sendir inn", "account.endorse": "Birta á notandasniði", + "account.familiar_followers_many": "Fylgt af {name1}, {name2} og {othersCount, plural, one {# í viðbót} other {# í viðbót}}", + "account.familiar_followers_one": "Fylgt af {name1}", + "account.familiar_followers_two": "Fylgt af {name1} og {name2}", "account.featured": "Með aukið vægi", "account.featured.accounts": "Notendasnið", "account.featured.hashtags": "Myllumerki", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index b1fea93a90..9827628d88 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -241,7 +241,7 @@ "conversation.with": "Ar {names}", "copy_icon_button.copied": "Ievietots starpliktuvē", "copypaste.copied": "Nokopēts", - "copypaste.copy_to_clipboard": "Kopēt uz starpliktuvi", + "copypaste.copy_to_clipboard": "Ievietot starpliktuvē", "directory.federated": "No zināma fediversa", "directory.local": "Tikai no {domain}", "directory.new_arrivals": "Jaunpienācēji", @@ -334,7 +334,7 @@ "firehose.all": "Visi", "firehose.local": "Šis serveris", "firehose.remote": "Citi serveri", - "follow_request.authorize": "Autorizēt", + "follow_request.authorize": "Pilnvarot", "follow_request.reject": "Noraidīt", "follow_requests.unlocked_explanation": "Lai gan Tavs konts nav slēgts, {domain} darbinieki iedomājās, ka Tu varētu vēlēties pašrocīgi pārskatīt sekošanas pieprasījumus no šiem kontiem.", "follow_suggestions.curated_suggestion": "Darbinieku izvēle", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 2fe9a8d784..e3c88bf7de 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -28,6 +28,9 @@ "account.edit_profile": "Profiel bewerken", "account.enable_notifications": "Geef een melding wanneer @{name} een bericht plaatst", "account.endorse": "Op profiel weergeven", + "account.familiar_followers_many": "Gevolgd door {name1}, {name2} en {othersCount, plural, one {# ander account} other {# andere accounts}}", + "account.familiar_followers_one": "Gevolgd door {name1}", + "account.familiar_followers_two": "Gevolgd door {name1} en {name2}", "account.featured": "Uitgelicht", "account.featured.accounts": "Profielen", "account.featured.hashtags": "Hashtags", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index a6d9fd9e4c..45c72d2fc1 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -28,6 +28,9 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificar-me das publicações de @{name}", "account.endorse": "Destacar no perfil", + "account.familiar_followers_many": "Seguido por {name1}, {name2} e {othersCount, plural,one {# outro}other {# outros}}", + "account.familiar_followers_one": "Seguido por {name1}", + "account.familiar_followers_two": "Seguido por {name1} e {name2}", "account.featured": "Destaques", "account.featured.accounts": "Perfis", "account.featured.hashtags": "Etiquetas", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index d3a459eb23..78426e596c 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -4,7 +4,7 @@ "about.disclaimer": "Mastodon — свободное программное обеспечение с открытым исходным кодом и торговая марка Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Причина не указана", "about.domain_blocks.preamble": "Mastodon обычно позволяет просматривать содержимое и взаимодействовать с пользователями любых других серверов в федивёрсе. Вот исключения, сделанные конкретно для этого сервера.", - "about.domain_blocks.silenced.explanation": "Как правило, вы не увидите профили и контент с этого сервера, если вы специально не будете их искать или не подпишетесь на них.", + "about.domain_blocks.silenced.explanation": "Как правило, вы не увидите профили и контент с этого сервера, если специально не будете их искать или не подпишетесь на них.", "about.domain_blocks.silenced.title": "Ограничивается", "about.domain_blocks.suspended.explanation": "Никакие данные с этого сервера не будут обрабатываться, храниться или обмениваться, что делает невозможным любое взаимодействие или связь с пользователями с этого сервера.", "about.domain_blocks.suspended.title": "Заблокирован", @@ -29,7 +29,8 @@ "account.enable_notifications": "Уведомлять о постах от @{name}", "account.endorse": "Рекомендовать в профиле", "account.featured": "Закреплённые…", - "account.featured.hashtags": "Хэштеги", + "account.featured.accounts": "Профили", + "account.featured.hashtags": "Хештеги", "account.featured.posts": "Посты", "account.featured_tags.last_status_at": "Последний пост {date}", "account.featured_tags.last_status_never": "Нет постов", @@ -115,7 +116,7 @@ "annual_report.summary.highlighted_post.by_replies": "пост с наибольшим количеством ответов", "annual_report.summary.highlighted_post.possessive": "{name}", "annual_report.summary.most_used_app.most_used_app": "наиболее часто используемое приложение", - "annual_report.summary.most_used_hashtag.most_used_hashtag": "наиболее часто используемый хэштег", + "annual_report.summary.most_used_hashtag.most_used_hashtag": "наиболее часто используемый хештег", "annual_report.summary.most_used_hashtag.none": "Нет", "annual_report.summary.new_posts.new_posts": "новых постов", "annual_report.summary.percentile.text": "Всё это помещает вас в топпользователей {domain}.", @@ -189,7 +190,7 @@ "compose.saved.body": "Пост отредактирован.", "compose_form.direct_message_warning_learn_more": "Узнать больше", "compose_form.encryption_warning": "Посты в Mastodon не защищены сквозным шифрованием. Не делитесь конфиденциальной информацией через Mastodon.", - "compose_form.hashtag_warning": "Этот пост не появится в поиске по хэштегам, так как он не обозначен как публичный. Только публичные посты можно найти по хэштегу.", + "compose_form.hashtag_warning": "Этот пост не появится в поиске по хештегам, так как он не обозначен как публичный. Только публичные посты можно найти по хештегу.", "compose_form.lock_disclaimer": "Ваша учётная запись {locked}. Любой пользователь сможет подписаться на вас и просматривать посты для подписчиков.", "compose_form.lock_disclaimer.lock": "не закрыта", "compose_form.placeholder": "О чём думаете?", @@ -216,7 +217,7 @@ "confirmations.delete_list.message": "Вы уверены, что хотите навсегда удалить этот список?", "confirmations.delete_list.title": "Удалить список?", "confirmations.discard_edit_media.confirm": "Сбросить", - "confirmations.discard_edit_media.message": "У вас есть несохранённые изменения, касающиеся описания медиа или области предпросмотра, сбросить их?", + "confirmations.discard_edit_media.message": "У вас есть несохранённые изменения, касающиеся описания медиа или области предпросмотра. Сбросить их?", "confirmations.edit.confirm": "Редактировать", "confirmations.edit.message": "Если вы начнёте редактировать сейчас, то набираемый в данный момент пост будет стёрт. Вы уверены, что хотите продолжить?", "confirmations.edit.title": "Стереть несохранённый черновик поста?", @@ -263,7 +264,7 @@ "dismissable_banner.dismiss": "Закрыть", "dismissable_banner.explore_links": "Об этих новостях говорят в федивёрсе прямо сейчас. Свежие новости, опубликованные несколькими разными людьми, ранжируются выше.", "dismissable_banner.explore_statuses": "Эти посты со всего федивёрса прямо сейчас набирают популярность. Более новые посты с более высоким количеством взаимодействий ранжируются выше.", - "dismissable_banner.explore_tags": "Эти хэштеги набирают популярность в федивёрсе прямо сейчас. Хэштеги, используемые несколькими разными людьми, ранжируются выше.", + "dismissable_banner.explore_tags": "Эти хештеги набирают популярность в федивёрсе прямо сейчас. Хештеги, используемые несколькими разными людьми, ранжируются выше.", "dismissable_banner.public_timeline": "Это самые новые публичные посты от всех тех людей в федивёрсе, на которых подписаны пользователи {domain}.", "domain_block_modal.block": "Заблокировать сервер", "domain_block_modal.block_account_instead": "Заблокировать @{name}", @@ -283,7 +284,7 @@ "domain_pill.username": "Имя пользователя", "domain_pill.whats_in_a_handle": "Что это значит?", "domain_pill.who_they_are": "Поскольку адрес позволяет однозначно определить, кто и где находится, вы можете взаимодействовать с пользователями социальной сети .", - "domain_pill.who_you_are": "Поскольку ваш адрес позволяет однозначно определить, кто вы и где находитесь, пользователи социальной сети могут взаимодействовать с вами.", + "domain_pill.who_you_are": "Поскольку ваш адрес позволяет однозначно определить, кто вы и где находитесь, пользователи социальной сети , могут взаимодействовать с вами.", "domain_pill.your_handle": "Ваш адрес:", "domain_pill.your_server": "Ваш цифровой дом, где находятся все ваши посты. Если вам не нравится этот сервер, вы можете в любое время перенести свою учётную запись на другой сервер, не теряя подписчиков.", "domain_pill.your_username": "Ваш уникальный идентификатор на этом сервере. На разных серверах могут встречаться люди с тем же именем пользователя.", @@ -320,8 +321,8 @@ "empty_column.favourited_statuses": "Вы ещё не добавили ни один пост в избранное. Все добавленные вами в избранное посты будут показаны здесь.", "empty_column.favourites": "Никто ещё не добавил этот пост в избранное. Все пользователи, добавившие этот пост в избранное, будут показаны здесь.", "empty_column.follow_requests": "Вам ещё не приходили запросы на подписку. Все новые запросы будут показаны здесь.", - "empty_column.followed_tags": "Вы ещё не подписались ни на один хэштег. Все хэштеги, на которые вы подписаны, будут показаны здесь.", - "empty_column.hashtag": "С этим хэштегом пока ещё никто ничего не опубликовал.", + "empty_column.followed_tags": "Вы ещё не подписались ни на один хештег. Все хештеги, на которые вы подписаны, будут показаны здесь.", + "empty_column.hashtag": "С этим хештегом пока ещё никто ничего не опубликовал.", "empty_column.home": "Ваша домашняя лента совсем пуста! Подпишитесь на кого-нибудь, чтобы заполнить её.", "empty_column.list": "В этом списке пока ничего нет. Все новые посты, опубликованные пользователями в списке, будут появляться здесь.", "empty_column.mutes": "Вы пока что никого не игнорируете.", @@ -330,15 +331,15 @@ "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других серверов, чтобы заполнить ленту", "error.unexpected_crash.explanation": "Из-за несовместимого браузера или ошибки в нашем коде эта страница не может быть корректно отображена.", "error.unexpected_crash.explanation_addons": "Эта страница не может быть корректно отображена. Скорее всего, ошибка вызвана расширением браузера или инструментом автоматического перевода.", - "error.unexpected_crash.next_steps": "Попробуйте обновить страницу. Если это не поможет, вы, скорее всего, всё ещё сможете использовать Mastodon в другом браузере или приложении.", - "error.unexpected_crash.next_steps_addons": "Попробуйте их отключить и обновить страницу. Если это не поможет, вы, скорее всего, всё ещё сможете использовать Mastodon в другом браузере или приложении.", + "error.unexpected_crash.next_steps": "Попробуйте обновить страницу. Если это не поможет, вы, возможно, всё ещё сможете использовать Mastodon в другом браузере или приложении.", + "error.unexpected_crash.next_steps_addons": "Попробуйте их отключить и обновить страницу. Если это не поможет, вы, возможно, всё ещё сможете использовать Mastodon в другом браузере или приложении.", "errors.unexpected_crash.copy_stacktrace": "Скопировать диагностическую информацию", "errors.unexpected_crash.report_issue": "Сообщить о проблеме", "explore.suggested_follows": "Люди", "explore.title": "Обзор", "explore.trending_links": "Новости", "explore.trending_statuses": "Посты", - "explore.trending_tags": "Хэштеги", + "explore.trending_tags": "Хештеги", "filter_modal.added.context_mismatch_explanation": "Этот фильтр не применяется в том контексте, в котором вы видели этот пост. Если вы хотите, чтобы пост был отфильтрован в этом контексте, необходимо редактировать фильтр.", "filter_modal.added.context_mismatch_title": "Несоответствие контекста!", "filter_modal.added.expired_explanation": "Этот фильтр истёк. Чтобы он был применён, вам нужно изменить срок действия фильтра.", @@ -379,7 +380,7 @@ "follow_suggestions.similar_to_recently_followed_longer": "Похоже на профили, на которые вы подписывались в последнее время", "follow_suggestions.view_all": "Посмотреть все", "follow_suggestions.who_to_follow": "На кого подписаться", - "followed_tags": "Подписки на хэштеги", + "followed_tags": "Подписки на хештеги", "footer.about": "О проекте", "footer.directory": "Каталог профилей", "footer.get_app": "Скачать приложение", @@ -397,7 +398,7 @@ "hashtag.column_header.tag_mode.any": "или {additional}", "hashtag.column_header.tag_mode.none": "без {additional}", "hashtag.column_settings.select.no_options_message": "Предложений не найдено", - "hashtag.column_settings.select.placeholder": "Введите хэштеги…", + "hashtag.column_settings.select.placeholder": "Введите хештеги…", "hashtag.column_settings.tag_mode.all": "Все из списка", "hashtag.column_settings.tag_mode.any": "Любой из списка", "hashtag.column_settings.tag_mode.none": "Ни один из списка", @@ -405,8 +406,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} пользователь} few {{counter} пользователя} other {{counter} пользователей}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} пост} few {{counter} поста} other {{counter} постов}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} пост} few {{counter} поста} other {{counter} постов}} сегодня", + "hashtag.feature": "Закрепить в профиле", "hashtag.follow": "Подписаться на новые посты", "hashtag.mute": "Игнорировать #{hashtag}", + "hashtag.unfeature": "Открепить от профиля", "hashtag.unfollow": "Отписаться от новых постов", "hashtags.and_other": "…и {count, plural, other {ещё #}}", "hints.profiles.followers_may_be_missing": "Некоторые подписчики этого профиля могут отсутствовать.", @@ -436,7 +439,7 @@ "ignore_notifications_modal.not_following_title": "Игнорировать уведомления от людей, на которых вы не подписаны?", "ignore_notifications_modal.private_mentions_title": "Игнорировать уведомления о нежелательных личных упоминаниях?", "info_button.label": "Помощь", - "info_button.what_is_alt_text": "

Что это такое?

Альтернативный текст содержит описание изображения для людей с ограничениями зрения, медленным интернетом и для тех, кому нужен дополнительный контекст.

Вы можете улучшить доступность и понимание для всех, написав четкий, краткий и объективный альтернативный текст.

  • Уловите важные элементы
  • Перескажите текстовую информацию на изображении
  • Используйте правильную структуру предложений
  • Избегайте избыточной информации
  • Сосредоточьтесь на тенденциях и ключевых выводах при описании сложных визуализаций (таких как диаграммы или карты)
", + "info_button.what_is_alt_text": "

Что это такое?

Альтернативный текст содержит описание изображения для людей с ограничениями зрения, медленным интернетом и для тех, кому нужен дополнительный контекст.

Вы можете улучшить доступность и понимание для всех, написав чёткий, краткий и объективный альтернативный текст.

  • Уловите важные элементы
  • Перескажите текстовую информацию на изображении
  • Используйте правильную структуру предложений
  • Избегайте избыточной информации
  • Сосредоточьтесь на тенденциях и ключевых выводах при описании сложных визуализаций, таких как диаграммы или карты
", "interaction_modal.action.favourite": "Вы можете добавить этот пост в избранное со своей учётной записью.", "interaction_modal.action.follow": "Вы можете подписаться со своей учётной записью.", "interaction_modal.action.reblog": "Вы можете продвинуть этот пост со своей учётной записью.", @@ -809,7 +812,7 @@ "search_popout.user": "пользователь", "search_results.accounts": "Профили", "search_results.all": "Все", - "search_results.hashtags": "Хэштеги", + "search_results.hashtags": "Хештеги", "search_results.no_results": "Ничего не найдено.", "search_results.no_search_yet": "Попробуйте поискать посты, профили или хэштеги.", "search_results.see_all": "Показать все", @@ -897,7 +900,7 @@ "time_remaining.seconds": "{number, plural, one {# секунда} many {# секунд} other {# секунды}}", "trends.counter_by_accounts": "{count, plural, few {{counter} человека} other {{counter} человек}} за {days, plural, one {последний {days} день} few {последние {days} дня} other {последние {days} дней}}", "trends.trending_now": "Самое актуальное", - "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.", + "ui.beforeunload": "Ваш черновик будет утрачен, если вы покинете Mastodon.", "units.short.billion": "{count} млрд", "units.short.million": "{count} млн", "units.short.thousand": "{count} тыс.", @@ -905,11 +908,11 @@ "upload_button.label": "Прикрепить фото, видео или аудио", "upload_error.limit": "Достигнут лимит загруженных файлов.", "upload_error.poll": "К опросам нельзя прикреплять файлы.", - "upload_form.drag_and_drop.instructions": "Чтобы подобрать прикрепленный файл, нажмите \"Пробел\" (Space) или \"Ввод\" (Enter). При перетаскивании используйте клавиши со стрелками, чтобы переместить прикрепленные файлы в любом направлении. Нажмите \"Пробел\" (Space) или \"Ввод\" (Enter) еще раз, чтобы переместить вложение в новое место, или нажмите кнопку \"Выйти\" (Escape), чтобы отменить.", - "upload_form.drag_and_drop.on_drag_cancel": "Перетаскивание было отменено. Вложение медиа {item} было удалено.", - "upload_form.drag_and_drop.on_drag_end": "Медиа вложение {item} было удалено.", - "upload_form.drag_and_drop.on_drag_over": "Медиа вложение {item} было перемещено.", - "upload_form.drag_and_drop.on_drag_start": "Загружается медиафайл {item}.", + "upload_form.drag_and_drop.instructions": "Чтобы выбрать вложение, нажмите \"Пробел\" (Space) или \"Ввод\" (Enter). Используйте клавиши со стрелками, чтобы передвинуть вложение в любом направлении. Нажмите \"Пробел\" (Space) или \"Ввод\" (Enter) ещё раз, чтобы переместить вложение на новое место, либо нажмите кнопку \"Выйти\" (Escape), чтобы отменить перемещение.", + "upload_form.drag_and_drop.on_drag_cancel": "Перемещение отменено. Вложение {item} было оставлено на прежнем месте.", + "upload_form.drag_and_drop.on_drag_end": "Вложение {item} было перемещено.", + "upload_form.drag_and_drop.on_drag_over": "Вложение {item} было передвинуто.", + "upload_form.drag_and_drop.on_drag_start": "Выбрано вложение {item}.", "upload_form.edit": "Редактировать", "upload_progress.label": "Загрузка...", "upload_progress.processing": "Обработка…", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 50c5f15796..a8c4001239 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -28,6 +28,9 @@ "account.edit_profile": "Përpunoni profilin", "account.enable_notifications": "Njoftomë, kur poston @{name}", "account.endorse": "Pasqyrojeni në profil", + "account.familiar_followers_many": "Ndjekur nga {name1}, {name2} dhe {othersCount, plural, one {# tjetër} other {# të tjerë}}", + "account.familiar_followers_one": "Ndjekur nga {name1}", + "account.familiar_followers_two": "Ndjekur nga {name1} dhe {name2}", "account.featured": "Të zgjedhur", "account.featured.accounts": "Profile", "account.featured.hashtags": "Hashtag-ë", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index d2da1f7751..75b05892f0 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -28,6 +28,9 @@ "account.edit_profile": "Profili düzenle", "account.enable_notifications": "@{name} kişisinin gönderi bildirimlerini aç", "account.endorse": "Profilimde öne çıkar", + "account.familiar_followers_many": "{name1}, {name2}, {othersCount, plural, one {# diğer} other {# diğer}} kişi tarafından takip ediliyor", + "account.familiar_followers_one": "{name1} tarafından takip ediliyor", + "account.familiar_followers_two": "{name1} ve {name2} tarafından takip ediliyor", "account.featured": "Öne çıkan", "account.featured.accounts": "Profiller", "account.featured.hashtags": "Etiketler", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index b6b6f7290e..b1a014f83c 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -28,6 +28,9 @@ "account.edit_profile": "Sửa hồ sơ", "account.enable_notifications": "Nhận thông báo khi @{name} đăng tút", "account.endorse": "Tôn vinh người này", + "account.familiar_followers_many": "Theo dõi bởi {name1}, {name2} và {othersCount, plural, other {# người khác}}", + "account.familiar_followers_one": "Theo dõi bởi {name1}", + "account.familiar_followers_two": "Theo dõi bởi {name1} và {name2}", "account.featured": "Nêu bật", "account.featured.accounts": "Mọi người", "account.featured.hashtags": "Hashtag", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 257cf5b599..226c68cd5c 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -28,6 +28,9 @@ "account.edit_profile": "編輯個人檔案", "account.enable_notifications": "當 @{name} 嘟文時通知我", "account.endorse": "於個人檔案推薦對方", + "account.familiar_followers_many": "被 {name1}、{name2}、及 {othersCount, plural, other {其他 # 人}} 跟隨", + "account.familiar_followers_one": "被 {name1} 跟隨", + "account.familiar_followers_two": "被 {name1} 與 {name2} 跟隨", "account.featured": "精選內容", "account.featured.accounts": "個人檔案", "account.featured.hashtags": "主題標籤", diff --git a/config/locales/br.yml b/config/locales/br.yml index e61a43643e..8a18e000f8 100644 --- a/config/locales/br.yml +++ b/config/locales/br.yml @@ -553,6 +553,8 @@ br: two: "%{count} skeudenn" pin_errors: ownership: N'hallit ket spilhennañ embannadurioù ar re all + quote_policies: + public: Pep den visibilities: direct: War-eeun public: Publik diff --git a/config/locales/cs.yml b/config/locales/cs.yml index bb9f187254..422b2ab415 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1944,6 +1944,10 @@ cs: limit: Už jste si připnuli maximální počet příspěvků ownership: Nelze připnout příspěvek někoho jiného reblog: Boosty nelze připnout + quote_policies: + followers: Sledující a zmínění uživatelé + nobody: Pouze zmínění uživatelé + public: Všichni title: "%{name}: „%{quote}“" visibilities: direct: Přímé diff --git a/config/locales/da.yml b/config/locales/da.yml index db83d2a64b..806eeafef8 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1858,6 +1858,10 @@ da: limit: Maksimalt antal indlæg allerede fastgjort ownership: Andres indlæg kan ikke fastgøres reblog: En fremhævelse kan ikke fastgøres + quote_policies: + followers: Følgere og nævnte brugere + nobody: Kun nævnte brugere + public: Alle title: '%{name}: "%{quote}"' visibilities: direct: Direkte diff --git a/config/locales/de.yml b/config/locales/de.yml index 4a9fdd5cfb..696f986b07 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1855,6 +1855,10 @@ de: limit: Du hast bereits die maximale Anzahl an Beiträgen angeheftet ownership: Du kannst nur eigene Beiträge anheften reblog: Du kannst keine geteilten Beiträge anheften + quote_policies: + followers: Follower und erwähnte Profile + nobody: Nur erwähnte Profile + public: Alle title: "%{name}: „%{quote}“" visibilities: direct: Direktnachricht diff --git a/config/locales/devise.lv.yml b/config/locales/devise.lv.yml index e60f8fa62e..53b88ed08d 100644 --- a/config/locales/devise.lv.yml +++ b/config/locales/devise.lv.yml @@ -6,7 +6,7 @@ lv: send_instructions: Pēc dažām minūtēm saņemsi e-pasta ziņojum ar norādēm, kā apstiprināt savu e-pasta adresi. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu. send_paranoid_instructions: Ja Tava e-pasta adrese ir mūsu datubāzē, pēc dažām minūtēm saņemsi e-pasta ziņojumu ar norādēm, kā apstiprināt savu e-pasta adresi. Lūgums pārbaudīt mēstuļu mapi, ja nesaņēmi šo e-pasta ziņojumu. failure: - already_authenticated: Tu jau esi pieteicies. + already_authenticated: Tu jau pieteicies. inactive: Tavs konts vēl nav aktivizēts. invalid: Nederīga %{authentication_keys} vai parole. last_attempt: Tev ir vēl viens mēģinājums, pirms tavs konts tiks bloķēts. diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index 18767dc0db..e4f282a085 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1858,6 +1858,10 @@ es-AR: limit: Ya fijaste el número máximo de mensajes ownership: No se puede fijar el mensaje de otra cuenta reblog: No se puede fijar una adhesión + quote_policies: + followers: Seguidores y usuarios mencionados + nobody: Solo usuarios mencionados + public: Todos title: '%{name}: "%{quote}"' visibilities: direct: Directo diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 160f26e365..1e58b1c9cd 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1858,6 +1858,10 @@ es-MX: limit: Ya has fijado el número máximo de publicaciones ownership: La publicación de alguien más no puede fijarse reblog: No se puede fijar una publicación impulsada + quote_policies: + followers: Seguidores y usuarios mencionados + nobody: Solo usuarios mencionados + public: Cualquiera title: "%{name}: «%{quote}»" visibilities: direct: Directa diff --git a/config/locales/es.yml b/config/locales/es.yml index 85f6088822..de82abb465 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1858,6 +1858,10 @@ es: limit: Ya has fijado el número máximo de publicaciones ownership: La publicación de otra persona no puede fijarse reblog: Una publicación impulsada no puede fijarse + quote_policies: + followers: Seguidores y usuarios mencionados + nobody: Solo usuarios mencionados + public: Cualquiera title: "%{name}: «%{quote}»" visibilities: direct: Directa diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 718fb653e4..69e473a02d 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1852,6 +1852,10 @@ fi: limit: Olet jo kiinnittänyt enimmäismäärän julkaisuja ownership: Muiden julkaisuja ei voi kiinnittää reblog: Tehostusta ei voi kiinnittää + quote_policies: + followers: Seuraajat ja mainitut käyttäjät + nobody: Vain mainitut käyttäjät + public: Kaikki title: "%{name}: ”%{quote}”" visibilities: direct: Suoraan diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 4bb4327949..8606fa7913 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -1858,6 +1858,10 @@ fo: limit: Tú hevur longu fest loyvda talið av postum ownership: Postar hjá øðrum kunnu ikki festast reblog: Ein stimbran kann ikki festast + quote_policies: + followers: Fylgjarar og nevndir brúkarar + nobody: Bara nevndir brúkarar + public: Øll title: '%{name}: "%{quote}"' visibilities: direct: Beinleiðis diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 371a60eb58..6faa967f30 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1858,6 +1858,10 @@ gl: limit: Xa fixaches o número máximo permitido de publicacións ownership: Non podes fixar a publicación doutra usuaria reblog: Non se poden fixar as mensaxes promovidas + quote_policies: + followers: Seguidoras e usuarias mencionadas + nobody: Só usuarias mencionadas + public: Calquera title: '%{name}: "%{quote}"' visibilities: direct: Directa diff --git a/config/locales/he.yml b/config/locales/he.yml index 3690010b95..23f008b94d 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1944,6 +1944,10 @@ he: limit: הגעת למספר המירבי של ההודעות המוצמדות ownership: הודעות של אחרים לא יכולות להיות מוצמדות reblog: אין אפשרות להצמיד הדהודים + quote_policies: + followers: עוקבים ומאוזכרים + nobody: רק מאוזכרים ומאוזכרות + public: כולם title: '%{name}: "%{quote}"' visibilities: direct: ישיר diff --git a/config/locales/is.yml b/config/locales/is.yml index 17dc450776..f4a6b2d68f 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1862,6 +1862,10 @@ is: limit: Þú hefur þegar fest leyfilegan hámarksfjölda færslna ownership: Færslur frá einhverjum öðrum er ekki hægt að festa reblog: Ekki er hægt að festa endurbirtingu + quote_policies: + followers: Fylgjendur og notendur sem minnst er á + nobody: Einungis notendur sem minnst er á + public: Allir title: "%{name}: „%{quote}‟" visibilities: direct: Beint diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 1538dfb363..38a340516d 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -332,7 +332,7 @@ lv: assign_category: Piešķirt kategoriju by_domain: Domēns copied_msg: Emocijzīmes vietējā kopija ir sekmīgi izveidota - copy: Kopēt + copy: Ievietot starpliktuvē copy_failed_msg: Nevarēja izveidot šīs emocijzīmes vietējo kopiju create_new_category: Izveidot jaunu kategoriju created_msg: Emocijzīme sekmīgi izveidota. @@ -1441,7 +1441,7 @@ lv: cancel: Atcelt changes_saved_msg: Izmaiņas sekmīgi saglabātas. confirm: Apstiprināt - copy: Kopēt + copy: Ievietot starpliktuvē delete: Dzēst deselect: Atcelt visu atlasi none: Neviens @@ -1833,7 +1833,7 @@ lv: account_settings: Konta iestatījumi aliases: Konta aizstājvārdi appearance: Izskats - authorized_apps: Autorizētās lietotnes + authorized_apps: Pilnvarotās lietotnes back: Atgriezties Mastodon delete: Konta dzēšana development: Izstrāde @@ -2006,7 +2006,7 @@ lv: title: Neizdevās otrās pakāpes autentificēšanās suspicious_sign_in: change_password: mainīt paroli - details: 'Šeit ir pieteikšanās izvērsums:' + details: 'Šeit ir informācija par pieteikšanos:' explanation: Esam noteikuši pieteikšanos Tavā kontā no jaunas IP adreses. further_actions_html: Ja tas nebiji tu, iesakām nekavējoties %{action} un iespējot divu faktoru autentifikāciju, lai tavs konts būtu drošībā. subject: Tavam kontam ir piekļūts no jaunas IP adreses @@ -2099,7 +2099,7 @@ lv: extra_instructions_html: Padoms: saite Tavā vietnē var būt neredzama. Svarīga daļa ir rel="me", kas novērš uzdošanos vietnēs ar lietotāju izveidotu saturu. Tu pat vari lapas galvenē izmantot tagu link, nevis a, taču HTML ir jābūt pieejamam bez JavaScript izpildīšanas. here_is_how: Lūk, kā hint_html: "Ikviens var apliecināt savu identitāti Mastodon. Balstīts uz atvērtiem tīmekļa standartiem, tagad un uz visiem laikiem bez maksas. Viss, kas Tev nepieciešams, ir personīga vietne, pēc kuras cilvēki Tevi atpazīst. Kad savā profilu sasaistīsi ar šo tīmekļvietni, mēs pārbaudīsim, vai tīmekļvietnei ir saite uz Tavu profilu, un tajā tiks parādīts redzama norāde." - instructions_html: Ievieto starpliktuvē un ielīmē tālāk norādīto kodu savas tīmekļvietnes HTML! Tad pievieno savas tīmekļvietnes adresi vienā no papildu laukiem savā profila cilnē "Labot profilu" un saglabā izmaiņas! + instructions_html: Ievieto starpliktuvē un ielīmē zemāk norādīto kodu savas tīmekļvietnes HTML! Tad pievieno savas tīmekļvietnes adresi vienā no papildu laukiem savā profila cilnē "Labot profilu" un saglabā izmaiņas! verification: Pārbaude verified_links: Tavas verifikācijas saites website_verification: Tīmekļvietnes apliecināšana diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 0a8ea5bb71..666b1b2495 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1858,6 +1858,10 @@ nl: limit: Je hebt het maximaal aantal bericht al vastgemaakt ownership: Een bericht van iemand anders kan niet worden vastgemaakt reblog: Een boost kan niet worden vastgezet + quote_policies: + followers: Volgers en vermelde gebruikers + nobody: Alleen vermelde gebruikers + public: Iedereen title: '%{name}: "%{quote}"' visibilities: direct: Privébericht diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 6180f51dd3..71cb1faafa 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -147,7 +147,6 @@ bg: min_age: Не трябва да е под изискваната минимална възраст от закона на юрисдикцията ви. user: chosen_languages: Само публикации на отметнатите езици ще се показват в публичните часови оси - date_of_birth: Трябва да се уверим, че сте поне на %{age}, за да употребявате Mastodon. Няма да съхраняваме това. role: Ролята управлява какви позволения има потребителят. user_role: color: Цветът, използван за ролите в потребителския интерфейс, като RGB в шестнадесетичен формат diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 2f85738c32..2cff4bffcd 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -145,7 +145,6 @@ ca: min_age: No hauria de ser inferior a l'edat mínima exigida per la llei de la vostra jurisdicció. user: chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques - date_of_birth: Ens hem d'assegurar que teniu %{age} anys com a mínim. No desarem aquesta dada. role: El rol controla quins permisos té l'usuari. user_role: color: Color que s'usarà per al rol a tota la interfície d'usuari, com a RGB en format hexadecimal diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index 00abf91d3e..7af8c1ce7c 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -56,6 +56,7 @@ cs: scopes: Která API bude aplikace moct používat. Pokud vyberete rozsah nejvyššího stupně, nebudete je muset vybírat jednotlivě. setting_aggregate_reblogs: Nezobrazovat nové boosty pro příspěvky, které byly nedávno boostnuty (ovlivňuje pouze nově přijaté boosty) setting_always_send_emails: Jinak nebudou e-mailové notifikace posílány, když Mastodon aktivně používáte + setting_default_quote_policy: Zmínení uživatelé mají vždy povoleno citovat. Toto nastavení se projeví pouze u příspěvků vytvořených s další verzí Mastodonu, ale můžete si vybrat vaše předvolby v předstihu setting_default_sensitive: Citlivá média jsou ve výchozím stavu skryta a mohou být zobrazena kliknutím setting_display_media_default: Skrývat média označená jako citlivá setting_display_media_hide_all: Vždy skrývat média @@ -148,7 +149,11 @@ cs: min_age: Neměla by být pod minimálním věkem požadovaným zákony vaší jurisdikce. user: chosen_languages: Po zaškrtnutí budou ve veřejných časových osách zobrazeny pouze příspěvky ve zvolených jazycích - date_of_birth: Musíme se ujistit, že je Vám alespoň %{age}, abyste mohli používat Mastodon. Nebudeme to ukládat. + date_of_birth: + few: Musíme se ujistit, že je Vám alespoň %{count}, abyste mohli používat Mastodon. Nebudeme to ukládat. + many: Musíme se ujistit, že je Vám alespoň %{count} let, abyste mohli používat Mastodon. Nebudeme to ukládat. + one: Musíme se ujistit, že je Vám alespoň %{count} rok, abyste mohli používat Mastodon. Nebudeme to ukládat. + other: Musíme se ujistit, že je Vám alespoň %{count} let, abyste mohli používat Mastodon. Nebudeme to ukládat. role: Role určuje, která oprávnění uživatel má. user_role: color: Barva, která má být použita pro roli v celém UI, jako RGB v hex formátu @@ -229,6 +234,7 @@ cs: setting_boost_modal: Před boostnutím zobrazovat potvrzovací okno setting_default_language: Jazyk příspěvků setting_default_privacy: Soukromí příspěvků + setting_default_quote_policy: Kdo může citovat setting_default_sensitive: Vždy označovat média jako citlivá setting_delete_modal: Před smazáním příspěvku zobrazovat potvrzovací dialog setting_disable_hover_cards: Zakázat náhled profilu při přejetí myší diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index c9c7862a91..656cca8b37 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -148,7 +148,6 @@ cy: min_age: Ni ddylai fod yn is na'r isafswm oedran sy'n ofynnol gan gyfreithiau eich awdurdodaeth. user: chosen_languages: Wedi eu dewis, dim ond tŵtiau yn yr ieithoedd hyn bydd yn cael eu harddangos mewn ffrydiau cyhoeddus - date_of_birth: Mae'n rhaid i ni sicrhau eich bod o leiaf %{age} i ddefnyddio Mastodon. Fyddwn ni ddim yn storio hwn. role: Mae'r rôl yn rheoli pa ganiatâd sydd gan y defnyddiwr. user_role: color: Lliw i'w ddefnyddio ar gyfer y rôl drwy'r UI, fel RGB mewn fformat hecs diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index 91582e60c5..f9390ab24d 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -56,6 +56,7 @@ da: scopes: De API'er, som applikationen vil kunne tilgå. Vælges en topniveaudstrækning, vil detailvalg være unødvendige. setting_aggregate_reblogs: Vis ikke nye fremhævelser for nyligt fremhævede indlæg (påvirker kun nyligt modtagne fremhævelser) setting_always_send_emails: Normalt sendes ingen e-mailnotifikationer under aktivt brug af Mastodon + setting_default_quote_policy: Nævnte brugere har altid lov til at citere. Denne indstilling vil kun træde i kraft for indlæg oprettet med den næste Mastodon-version, men man kan som forberedelse vælge sin præference setting_default_sensitive: Sensitive medier er som standard skjult og kan vises med et klik setting_display_media_default: Skjul medier med sensitiv-markering setting_display_media_hide_all: Skjul altid medier @@ -148,7 +149,6 @@ da: min_age: Bør ikke være under den iht. lovgivningen i det aktuelle retsområde krævede minimumsalder. user: chosen_languages: Når markeret, vil kun indlæg på de valgte sprog fremgå på offentlige tidslinjer - date_of_birth: Vi er nødt til at sikre, at man er fyldt %{age} for at bruge Mastodon. Vi gemmer ikke denne information. role: Rollen styrer, hvilke tilladelser brugeren er tildelt. user_role: color: Farven, i RGB hex-format, der skal bruges til rollen i hele UI'en @@ -229,6 +229,7 @@ da: setting_boost_modal: Vis bekræftelsesdialog inden fremhævelse setting_default_language: Sprog for indlæg setting_default_privacy: Fortrolighed for indlæg + setting_default_quote_policy: Hvem, som kan citere setting_default_sensitive: Markér altid medier som sensitive setting_delete_modal: Vis bekræftelsesdialog før et indlæg slettes setting_disable_hover_cards: Deaktivér profilforhåndsvisning ved svæv (hover) diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 16a6be5c3a..305aa6f817 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -56,6 +56,7 @@ de: scopes: Welche Schnittstellen der Applikation erlaubt sind. Wenn du einen Top-Level-Scope auswählst, dann musst du nicht jeden einzelnen darunter auswählen. setting_aggregate_reblogs: Beiträge, die erst kürzlich geteilt wurden, werden nicht noch einmal angezeigt (wirkt sich nur auf zukünftige geteilte Beiträge aus) setting_always_send_emails: Normalerweise werden Benachrichtigungen nicht per E-Mail versendet, wenn du gerade auf Mastodon aktiv bist + setting_default_quote_policy: Erwähnte Profile dürfen immer zitieren. Diese Einstellung wirkt sich nur für Beiträge aus, die mit der zukünftigen Mastodon-Version erstellt wurden. Als Vorbereitung darauf kannst du bereits jetzt die Einstellung vornehmen. setting_default_sensitive: Medien, die mit einer Inhaltswarnung versehen worden sind, werden – je nach Einstellung – erst nach einem zusätzlichen Klick angezeigt setting_display_media_default: Medien mit Inhaltswarnung ausblenden setting_display_media_hide_all: Medien immer ausblenden @@ -148,7 +149,9 @@ de: min_age: Sollte nicht unter dem gesetzlich vorgeschriebenen Mindestalter liegen. user: chosen_languages: Wenn du hier eine oder mehrere Sprachen auswählst, werden ausschließlich Beiträge in diesen Sprachen in deinen öffentlichen Timelines angezeigt - date_of_birth: Wir müssen sicherstellen, dass du mindestens %{age} Jahre alt bist, um Mastodon verwenden zu können. Das Alter wird nicht gespeichert. + date_of_birth: + one: Wir müssen sicherstellen, dass du mindestens %{count} Jahre alt bist, um Mastodon nutzen zu können. Wir werden diese Information nicht aufbewahren. + other: Wir müssen sicherstellen, dass du mindestens %{count} Jahre alt bist, um Mastodon nutzen zu können. Wir werden diese Information nicht aufbewahren. role: Die Rolle bestimmt, welche Berechtigungen das Konto hat. user_role: color: Farbe, die für diese Rolle in der gesamten Benutzerschnittstelle verwendet wird, als RGB im Hexadezimalsystem @@ -229,6 +232,7 @@ de: setting_boost_modal: Bestätigungsdialog beim Teilen eines Beitrags anzeigen setting_default_language: Beitragssprache setting_default_privacy: Beitragssichtbarkeit + setting_default_quote_policy: Wer darf zitieren setting_default_sensitive: Medien immer mit einer Inhaltswarnung versehen setting_delete_modal: Bestätigungsdialog beim Löschen eines Beitrags anzeigen setting_disable_hover_cards: Profilvorschau deaktivieren, wenn die Maus über das Profil bewegt wird diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index 3b82cd3c75..f14c1fa453 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -148,7 +148,6 @@ el: min_age: Δεν πρέπει να είναι κάτω από την ελάχιστη ηλικία που απαιτείται από τους νόμους της δικαιοδοσίας σας. user: chosen_languages: Όταν ενεργοποιηθεί, στη δημόσια ροή θα εμφανίζονται τουτ μόνο από τις επιλεγμένες γλώσσες - date_of_birth: Πρέπει να βεβαιωθείς ότι είσαι τουλάχιστον %{age} για να χρησιμοποιήσεις το Mastodon. Δεν θα το αποθηκεύσουμε. role: Ο ρόλος ελέγχει ποια δικαιώματα έχει ο χρήστης. user_role: color: Το χρώμα που θα χρησιμοποιηθεί για το ρόλο σε ολόκληρη τη διεπαφή, ως RGB σε δεκαεξαδική μορφή diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 3e340cccf7..2687f78229 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -148,7 +148,6 @@ eo: min_age: Ne devus esti malsupre la minimuma aĝo postulita de la leĝoj de via jurisdikcio. user: chosen_languages: Kun tio markita nur mesaĝoj en elektitaj lingvoj aperos en publikaj tempolinioj - date_of_birth: Ni devas certigi, ke vi estas almenaŭ %{age} por uzi Mastodon. Ni ne konservos ĉi tion. role: La rolo kontrolas kiujn permesojn la uzanto havas. user_role: color: Koloro uzita por la rolo sur la UI, kun RGB-formato diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml index 4333db9fed..3b68cf009f 100644 --- a/config/locales/simple_form.es-AR.yml +++ b/config/locales/simple_form.es-AR.yml @@ -56,6 +56,7 @@ es-AR: scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionás el alcance de nivel más alto, no necesitás seleccionar las individuales. setting_aggregate_reblogs: No mostrar nuevas adhesiones de los mensajes que fueron recientemente adheridos (sólo afecta a las adhesiones recibidas recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente + setting_default_quote_policy: Los usuarios mencionados siempre pueden citar. Este ajuste solo afecta a las publicaciones creadas con la próxima versión de Mastodon, pero podés seleccionar tus preferencias con antelación. setting_default_sensitive: El contenido de medios sensibles está oculto predeterminadamente y puede ser mostrado con un clic setting_display_media_default: Ocultar medios marcados como sensibles setting_display_media_hide_all: Siempre ocultar todos los medios @@ -148,7 +149,9 @@ es-AR: min_age: No debería estar por debajo de la edad mínima requerida por las leyes de su jurisdicción. user: chosen_languages: Cuando estén marcados, sólo se mostrarán los mensajes en los idiomas seleccionados en las líneas temporales públicas - date_of_birth: Tenemos que asegurarnos de que al menos tenés %{age} años de edad para usar Mastodon. No almacenaremos esta información. + date_of_birth: + one: Tenemos que asegurarnos de que al menos tenés %{count} años de edad para usar Mastodon. No almacenaremos esta información. + other: Tenemos que asegurarnos de que al menos tenés %{count} años de edad para usar Mastodon. No almacenaremos esta información. role: El rol controla qué permisos tiene el usuario. user_role: color: Color que se utilizará para el rol a lo largo de la interface de usuario, como RGB en formato hexadecimal @@ -229,6 +232,7 @@ es-AR: setting_boost_modal: Mostrar diálogo de confirmación antes de adherir setting_default_language: Idioma de tus mensajes setting_default_privacy: Privacidad de mensajes + setting_default_quote_policy: Quién puede citar setting_default_sensitive: Siempre marcar medios como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de eliminar un mensaje setting_disable_hover_cards: Deshabilitar previsualización del perfil al pasar el cursor diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index b2f7daf500..30614992fc 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -56,6 +56,7 @@ es-MX: scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. setting_aggregate_reblogs: No mostrar nuevos impulsos para las publicaciones que han sido recientemente impulsadas (sólo afecta a las publicaciones recibidas recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente + setting_default_quote_policy: Los usuarios mencionados siempre pueden citar. Esta configuración solo se aplicará a las publicaciones creadas con la próxima versión de Mastodon, pero puedes seleccionar tus preferencias anticipadamente setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un clic setting_display_media_default: Ocultar contenido multimedia marcado como sensible setting_display_media_hide_all: Siempre ocultar todo el contenido multimedia @@ -148,7 +149,9 @@ es-MX: min_age: No debe ser menor de la edad mínima exigida por las leyes de su jurisdicción. user: chosen_languages: Cuando se marca, solo se mostrarán las publicaciones en los idiomas seleccionados en las líneas de tiempo públicas - date_of_birth: Tenemos que asegurarnos de que tienes al menos %{age} para usar Mastodon. No almacenaremos esto. + date_of_birth: + one: Necesitamos asegurarnos de que tengas al menos %{count} para usar Mastodon. No guardaremos esta información. + other: Necesitamos asegurarnos de que tengas al menos %{count} para usar Mastodon. No guardaremos esta información. role: El rol controla qué permisos tiene el usuario. user_role: color: Color que se usará para el rol en toda la interfaz de usuario, como RGB en formato hexadecimal @@ -229,6 +232,7 @@ es-MX: setting_boost_modal: Mostrar ventana de confirmación antes de impulsar setting_default_language: Idioma de publicación setting_default_privacy: Privacidad de publicaciones + setting_default_quote_policy: Quién puede citar setting_default_sensitive: Marcar siempre imágenes como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de borrar una publicación setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index d469c6ec3a..56eea49d25 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -56,6 +56,7 @@ es: scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. setting_aggregate_reblogs: No mostrar nuevos impulsos para las publicaciones que han sido recientemente impulsadas (sólo afecta a los impulsos recibidos recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente + setting_default_quote_policy: Los usuarios mencionados siempre pueden citar. Este ajuste solo afecta a las publicaciones creadas con la próxima versión de Mastodon, pero puedes seleccionar tus preferencias anticipadamente setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un click setting_display_media_default: Ocultar contenido multimedia marcado como sensible setting_display_media_hide_all: Siempre ocultar todo el contenido multimedia @@ -148,7 +149,9 @@ es: min_age: No debería estar por debajo de la edad mínima requerida por las leyes de su jurisdicción. user: chosen_languages: Cuando se marca, solo se mostrarán las publicaciones en los idiomas seleccionados en las líneas de tiempo públicas - date_of_birth: Tenemos que asegurarnos de que al menos tienes %{age} años para usar Mastodon. No almacenaremos esta información. + date_of_birth: + one: Tenemos que asegurarnos de que tienes al menos %{count} años para usar Mastodon. No guardaremos este dato. + other: Tenemos que asegurarnos de que tienes al menos %{count} años para usar Mastodon. No guardaremos este dato. role: El rol controla qué permisos tiene el usuario. user_role: color: Color que se utilizará para el rol a lo largo de la interfaz de usuario, como RGB en formato hexadecimal @@ -229,6 +232,7 @@ es: setting_boost_modal: Mostrar diálogo de confirmación antes de impulsar una publicación setting_default_language: Idioma de publicación setting_default_privacy: Privacidad de publicaciones + setting_default_quote_policy: Quién puede citar setting_default_sensitive: Marcar siempre imágenes como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de borrar una publicación setting_disable_hover_cards: Desactivar vista previa del perfil al pasar el cursor diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 5489780ec7..82f47cd6f3 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -56,6 +56,7 @@ fi: scopes: Mihin ohjelmointirajapintoihin sovelluksella on pääsy. Jos valitset ylätason käyttöoikeuden, sinun ei tarvitse valita yksittäisiä. setting_aggregate_reblogs: Älä näytä uusia tehostuksia julkaisuille, joita on äskettäin tehostettu (koskee vain juuri vastaanotettuja tehostuksia) setting_always_send_emails: Yleensä sähköposti-ilmoituksia ei lähetetä, kun käytät Mastodonia aktiivisesti + setting_default_quote_policy: Mainitut käyttäjät saavat aina lainata. Tämä asetus koskee vain julkaisuja, jotka on luotu seuraavalla Mastodon-versiolla, mutta voit valita asetuksesi valmistautuaksesi setting_default_sensitive: Arkaluonteinen media piilotetaan oletusarvoisesti, ja se voidaan näyttää yhdellä napsautuksella setting_display_media_default: Piilota arkaluonteiseksi merkitty mediasisältö setting_display_media_hide_all: Piilota mediasisältö aina @@ -147,7 +148,9 @@ fi: min_age: Ei pidä alittaa lainkäyttöalueesi lakien vaatimaa vähimmäisikää. user: chosen_languages: Jos valitset kieliä oheisesta luettelosta, vain niidenkieliset julkaisut näkyvät sinulle julkisilla aikajanoilla - date_of_birth: Meidän on varmistettava, että olet vähintään %{age} vuotta vanha, jotta voit käyttää Mastodonia. Emme tallenna tätä. + date_of_birth: + one: Meidän on varmistettava, että olet vähintään %{count}, jotta voi käyttää Mastodonia. Emme tallenna tätä. + other: Meidän on varmistettava, että olet vähintään %{count}, jotta voi käyttää Mastodonia. Emme tallenna tätä. role: Rooli määrää, millaiset käyttöoikeudet käyttäjällä on. user_role: color: Väri, jota käytetään roolille kaikkialla käyttöliittymässä, RGB-heksadesimaalimuodossa @@ -228,6 +231,7 @@ fi: setting_boost_modal: Kysy vahvistusta ennen tehostusta setting_default_language: Julkaisun kieli setting_default_privacy: Julkaisun näkyvyys + setting_default_quote_policy: Kuka voi lainata setting_default_sensitive: Merkitse media aina arkaluonteiseksi setting_delete_modal: Kysy vahvistusta ennen julkaisun poistamista setting_disable_hover_cards: Poista käytöstä profiilin esikatselu osoitettaessa diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml index 32d066ed18..c3f1dad678 100644 --- a/config/locales/simple_form.fo.yml +++ b/config/locales/simple_form.fo.yml @@ -56,6 +56,7 @@ fo: scopes: Hvørji API nýtsluskipanin fær atgongd til. Velur tú eitt vav á hægsta stigi, so er ikki neyðugt at velja tey einstøku. setting_aggregate_reblogs: Vís ikki nýggjar stimbranir fyri postar, sum nýliga eru stimbraðir (ávirkar einans stimbranir, ið eru móttiknar fyri kortum) setting_always_send_emails: Vanliga vera teldupostfráboðanir ikki sendar, tá tú virkin brúkar Mastodon + setting_default_quote_policy: Nevndir brúkarar hava altíð loyvi at sitera. Hendan stillingin verður bara virkin fyri postar, sum verða stovnaðir í næstu Mastodon útgávuni, men sum fyrireiking til tað, kanst tú velja tína stilling longu nú setting_default_sensitive: Viðkvæmar miðlafílur eru fjaldar og kunnu avdúkast við einum klikki setting_display_media_default: Fjal miðlafílur, sum eru merktar sum viðkvæmar setting_display_media_hide_all: Fjal altíð miðlafílur @@ -148,7 +149,9 @@ fo: min_age: Eigur ikki at vera undir lægsta aldri, sum lógirnar í tínum rættarøki krevja. user: chosen_languages: Tá hetta er valt, verða einans postar í valdum málum vístir á almennum tíðarlinjum - date_of_birth: Vit mugu tryggja okkum, at tú er í minsta lagi %{age} ár fyri at brúka Mastodon. Vit goyma ikki hesar upplýsingar. + date_of_birth: + one: Vit mugu tryggja okkum, at tú er í minsta lagi %{count} fyri at brúka Mastodon. Vit goyma ikki hesar upplýsingar. + other: Vit mugu tryggja okkum, at tú er í minsta lagi %{count} fyri at brúka Mastodon. Vit goyma ikki hesar upplýsingar. role: Leikluturin stýrir hvørji rættindi, brúkarin hevur. user_role: color: Litur, sum leikluturin hevur í øllum brúkaramarkamótinum, sum RGB og upplýst sum sekstandatal @@ -229,6 +232,7 @@ fo: setting_boost_modal: Vís váttanarmynd, áðrenn tú stimbrar postar setting_default_language: Mál, sum verður brúkt til postar setting_default_privacy: Hvussu privatir eru postar? + setting_default_quote_policy: Hvør kann sitera setting_default_sensitive: Merk altíð miðlafílur sum viðkvæmar setting_delete_modal: Vís váttanarmynd, áðrenn postar verða strikaðir setting_disable_hover_cards: Ger undanvísing, tá músin verður flutt yvir vangan, óvirkna diff --git a/config/locales/simple_form.fr-CA.yml b/config/locales/simple_form.fr-CA.yml index f917183f55..8856c8906c 100644 --- a/config/locales/simple_form.fr-CA.yml +++ b/config/locales/simple_form.fr-CA.yml @@ -148,7 +148,6 @@ 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: Nous devons nous assurer que vous avez au moins %{age} pour utiliser Mastodon. Nous ne conserverons pas ces informations. 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 diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index a8d6bb86e0..53c4fbfba7 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -148,7 +148,6 @@ 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: Nous devons nous assurer que vous avez au moins %{age} pour utiliser Mastodon. Nous ne conserverons pas ces informations. 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 diff --git a/config/locales/simple_form.fy.yml b/config/locales/simple_form.fy.yml index 66d034d99d..047723e487 100644 --- a/config/locales/simple_form.fy.yml +++ b/config/locales/simple_form.fy.yml @@ -148,7 +148,6 @@ fy: min_age: Mei net leger wêze as de minimale fereaske leeftiid neffens de wetten fan jo jurisdiksje. user: chosen_languages: Allinnich berjochten yn de selektearre talen wurde op de iepenbiere tiidline toand - date_of_birth: Wy moatte derfoar soargje dat jo op syn minst %{age} binne om Mastodon te brûken. Dit wurdt net bewarre. role: De rol bepaalt hokker rjochten in brûker hat. user_role: color: Kleur dy’t brûkt wurdt foar de rol yn de UI, as RGB yn heksadesimaal formaat diff --git a/config/locales/simple_form.ga.yml b/config/locales/simple_form.ga.yml index bd8dac2a36..86ca027566 100644 --- a/config/locales/simple_form.ga.yml +++ b/config/locales/simple_form.ga.yml @@ -148,7 +148,6 @@ ga: min_age: Níor chóir go mbeidís faoi bhun na haoise íosta a éilíonn dlíthe do dhlínse. user: chosen_languages: Nuair a dhéantar iad a sheiceáil, ní thaispeánfar ach postálacha i dteangacha roghnaithe in amlínte poiblí - date_of_birth: Ní mór dúinn a chinntiú go bhfuil tú ar a laghad %{age} chun Mastodon a úsáid. Ní stórálfaimid é seo. role: Rialaíonn an ról na ceadanna atá ag an úsáideoir. user_role: color: Dath le húsáid don ról ar fud an Chomhéadain, mar RGB i bhformáid heicsidheachúlach diff --git a/config/locales/simple_form.gd.yml b/config/locales/simple_form.gd.yml index 1995af5ff7..bb87ba712c 100644 --- a/config/locales/simple_form.gd.yml +++ b/config/locales/simple_form.gd.yml @@ -148,7 +148,6 @@ gd: min_age: Cha bu chòir seo a bhith fon aois as lugha a dh’iarras laghain an t-uachdranais laghail agad. user: chosen_languages: Nuair a bhios cromag ris, cha nochd ach postaichean sna cànain a thagh thu air loidhnichean-ama poblach - date_of_birth: Feumaidh sinn dèanamh cinnteach gu bheil thu %{age} bliadhna(ichean) a dh’aois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo. role: Stiùiridh an dreuchd dè na ceadan a bhios aig cleachdaiche. user_role: color: An datha a bhios air an dreuchd air feadh na h-eadar-aghaidh, ’na RGB san fhòrmat sia-dheicheach diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 3773123f17..c8de52b2e4 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -148,7 +148,6 @@ gl: min_age: Non debería ser inferior á idade mínima requerida polas leis da túa xurisdición. user: chosen_languages: Se ten marca, só as publicacións nos idiomas seleccionados serán mostrados en cronoloxías públicas - date_of_birth: Temos que ter certeza de que tes %{age} como mínimo para usar Mastodon. Non gardamos este dato. role: Os roles establecen os permisos que ten a usuaria. user_role: color: Cor que se usará para o rol a través da IU, como RGB en formato hex diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index 9c01b91638..4bceaa7e8f 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -56,6 +56,7 @@ he: scopes: לאיזה ממשק יורשה היישום לגשת. בבחירת תחום כללי, אין צורך לבחור ממשקים ספציפיים. setting_aggregate_reblogs: לא להראות הדהודים של הודעות שהודהדו לאחרונה (משפיע רק על הדהודים שהתקבלו לא מזמן) setting_always_send_emails: בדרך כלל התראות דוא"ל לא יישלחו בזמן שימוש פעיל במסטודון + setting_default_quote_policy: משתמשיםות מאוזכריםות תמיד חופשיים לצטט. הכיוונון הזה משפיע רק על פרסומים שישלחו בגרסאות מסטודון עתידיות, ניתן לבחור את העדפתך כהכנה לגרסא שתבוא setting_default_sensitive: מדיה רגישה מוסתרת כברירת מחדל וניתן להציגה בקליק setting_display_media_default: הסתרת מדיה המסומנת כרגישה setting_display_media_hide_all: הסתר מדיה תמיד @@ -148,7 +149,11 @@ he: min_age: על הערך להיות לפחות בגיל המינימלי הדרוש בחוק באיזור השיפוט שלך. user: chosen_languages: אם פעיל, רק הודעות בשפות הנבחרות יוצגו לפידים הפומביים - date_of_birth: עלינו לוודא שגילך לפחות %{age} כדי להשתמש במסטודון. המידע לא ישמר בשרת שלנו. + date_of_birth: + many: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו. + one: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו. + other: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו. + two: עלינו לוודא שגילך לפחות %{count} כדי להשתמש במסטודון. המידע לא ישמר אצלנו. role: התפקיד שולט על אילו הרשאות יש למשתמש. user_role: color: צבע לתפקיד בממשק המשתמש, כ RGB בפורמט הקסדצימלי @@ -229,6 +234,7 @@ he: setting_boost_modal: הצגת דיאלוג אישור לפני הדהוד setting_default_language: שפת ברירת מחדל להודעה setting_default_privacy: פרטיות ההודעות + setting_default_quote_policy: למי מותר לצטט setting_default_sensitive: תמיד לתת סימון "רגיש" למדיה setting_delete_modal: להראות תיבת אישור לפני מחיקת חיצרוץ setting_disable_hover_cards: כבה הצצה מקדימה לפרופיל בעת מעבר עכבר מעליו diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index adcc3d5789..e517ddd954 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -148,7 +148,9 @@ hu: min_age: Nem lehet a joghatóság által meghatározott minimális kor alatt. user: chosen_languages: Ha aktív, csak a kiválasztott nyelvű bejegyzések jelennek majd meg a nyilvános idővonalon - date_of_birth: Legalább %{age} évesnek kell lenniük, hogy használhassák a Mastodont. Ezt nem tároljuk. + date_of_birth: + one: Ahhoz, hogy a Mastodont használd, meg kell győződnünk arról, hogy legalább %{count} éves vagy. Ezt nem tároljuk. + other: Ahhoz, hogy a Mastodont használd, meg kell győződnünk arról, hogy legalább %{count} éves vagy. Ezt nem tároljuk. role: A szerep szabályozza, hogy a felhasználó milyen jogosultságokkal rendelkezik. user_role: color: A szerephez használandó szín mindenhol a felhasználói felületen, hexa RGB formátumban diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml index bc68d14f2a..778d2d8f64 100644 --- a/config/locales/simple_form.is.yml +++ b/config/locales/simple_form.is.yml @@ -56,6 +56,7 @@ is: scopes: Að hvaða API-kerfisviðmótum forritið fær aðgang. Ef þú velur efsta-stigs svið, þarftu ekki að gefa einstakar heimildir. setting_aggregate_reblogs: Ekki sýna nýjar endurbirtingar á færslum sem hafa nýlega verið endurbirtar (hefur bara áhrif á ný-mótteknar endurbirtingar) setting_always_send_emails: Venjulega eru tilkynningar í tölvupósti ekki sendar þegar þú ert virk/ur í að nota Mastodon + setting_default_quote_policy: Notendur sem minnst er á geta alltaf gert tilvitnanir. Þessi stilling virkar einungis á færslur sem gerðar hafa verið í næstu útgáfu Mastodon, en þú getur samt valið þetta til að undirbúa þig setting_default_sensitive: Viðkvæmt myndefni er sjálfgefið falið og er hægt að birta með smelli setting_display_media_default: Fela myndefni sem merkt er viðkvæmt setting_display_media_hide_all: Alltaf fela allt myndefni @@ -148,7 +149,9 @@ is: min_age: Ætti ekki að vera lægri en sá lágmarksaldur sek kveðið er á um í lögum þíns lögsagnarumdæmis. user: chosen_languages: Þegar merkt er við þetta, birtast einungis færslur á völdum tungumálum á opinberum tímalínum - date_of_birth: Við verðum að ganga úr skugga um að þú hafir náð %{age} aldri til að nota Mastodon. Við munum ekki geyma þessar upplýsingar. + date_of_birth: + one: Við verðum að ganga úr skugga um að þú hafir náð %{count} aldri til að nota Mastodon. Við munum ekki geyma þessar upplýsingar. + other: Við verðum að ganga úr skugga um að þú hafir náð %{count} aldri til að nota Mastodon. Við munum ekki geyma þessar upplýsingar. role: Hlutverk stýrir hvaða heimildir notandinn hefur. user_role: color: Litur sem notaður er fyrir hlutverkið allsstaðar í viðmótinu, sem RGB-gildi á hex-sniði @@ -229,6 +232,7 @@ is: setting_boost_modal: Sýna staðfestingarglugga fyrir endurbirtingu setting_default_language: Tungumál sem skrifað er á setting_default_privacy: Gagnaleynd færslna + setting_default_quote_policy: Hverjir geta gert tilvitnanir setting_default_sensitive: Alltaf merkja myndefni sem viðkvæmt setting_delete_modal: Birta staðfestingarglugga áður en færslu er eytt setting_disable_hover_cards: Gera óvirka forskoðun notandasniðs við yfirsvif diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index da203270fa..a3672696e5 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -148,7 +148,6 @@ it: min_age: Non si dovrebbe avere un'età inferiore a quella minima richiesta, dalle leggi della tua giurisdizione. user: chosen_languages: Quando una o più lingue sono contrassegnate, nelle timeline pubbliche vengono mostrati solo i toot nelle lingue selezionate - date_of_birth: Dobbiamo verificare che tu abbia almeno %{age} anni per usare Mastodon. Non archivieremo questa informazione. role: Il ruolo controlla quali permessi ha l'utente. user_role: color: Colore da usare per il ruolo in tutta l'UI, come RGB in formato esadecimale diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 5540cedd06..5f9c5467c4 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -148,7 +148,6 @@ ja: min_age: お住まいの国や地域の法律によって定められている最低年齢を下回ってはなりません。 user: chosen_languages: 選択すると、選択した言語の投稿のみが公開タイムラインに表示されるようになります - date_of_birth: Mastodonを利用するには少なくとも%{age}歳以上であることを確認する必要があります。これは保存されません。 role: そのロールは、ユーザーが持つ権限を制御します。 user_role: color: UI 全体でロールの表示に使用される色(16進数RGB形式) diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 661028fff0..a823604584 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -146,7 +146,6 @@ ko: min_age: 관할지역의 법률에서 요구하는 최저 연령보다 작으면 안 됩니다. user: chosen_languages: 체크하면, 선택 된 언어로 작성된 게시물들만 공개 타임라인에 보여집니다 - date_of_birth: 마스토돈을 사용하려면 %{age}세 이상임을 확인해야 합니다. 이 정보는 저장되지 않습니다. role: 역할은 사용자가 어떤 권한을 가지게 될 지 결정합니다. user_role: color: 색상은 사용자 인터페이스에서 역할을 나타내기 위해 사용되며, RGB 16진수 형식입니다 diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index d3febceea9..4aa89ed9ec 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -120,7 +120,6 @@ lt: min_age: Neturėtų būti žemiau mažiausio amžiaus, reikalaujamo pagal jūsų jurisdikcijos įstatymus. user: chosen_languages: Kai pažymėta, viešose laiko skalėse bus rodomi tik įrašai pasirinktomis kalbomis. - date_of_birth: Turime įsitikinti, kad esate bent %{age}, kad naudotumėte „Mastodon“. Mes to neišsaugosime. role: Vaidmuo valdo, kokius leidimus naudotojas turi. labels: account: diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml index fad16d86ea..1068fd6e51 100644 --- a/config/locales/simple_form.lv.yml +++ b/config/locales/simple_form.lv.yml @@ -48,7 +48,7 @@ lv: digest: Tiek nosūtīts tikai pēc ilgstošas bezdarbības un tikai tad, ja savas prombūtnes laikā saņēmi jebkādas personīgas ziņas email: Tev tiks nosūtīts apstiprinājuma e-pasta ziņojums header: WEBP, PNG, GIF vai JPG. Ne vairāk kā %{size}. Tiks samazināts līdz %{dimensions}px - inbox_url: Nokopē URL no tā releja sākumlapas, kuru vēlies izmantot + inbox_url: Releja, kuru vēlies izmantot, sākumlapas URL jāievieto starpliktuvē irreversible: Filtrētās ziņas neatgriezeniski pazudīs, pat ja filtrs vēlāk tiks noņemts locale: Lietotāja saskarnes, e-pasta ziņojumu un push paziņojumu valoda password: Izmanto vismaz 8 rakstzīmes @@ -142,7 +142,6 @@ lv: domain: Sniegtā tiešsaistas pakalpojuma neatkārtojama identifikācija. user: chosen_languages: Ja ieķeksēts, publiskos laika grafikos tiks parādītas tikai ziņas noteiktajās valodās - date_of_birth: Mums jāpārliecinās, ka jums ir vismaz %{age} gadi, lai varētu izmantot Mastodonu. Mēs neuzglabāsim šo informāciju. role: Loma nosaka, kādas lietotājam ir atļaujas. user_role: color: Krāsa, kas jāizmanto lomai visā lietotāja saskarnē, kā RGB hex formātā diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 6029698bd7..dd9e2f78ca 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -56,6 +56,7 @@ nl: scopes: Tot welke API's heeft de toepassing toegang. Wanneer je een toestemming van het bovenste niveau kiest, hoef je geen individuele toestemmingen meer te kiezen. setting_aggregate_reblogs: Geen nieuwe boosts tonen voor berichten die recentelijk nog zijn geboost (heeft alleen effect op nieuw ontvangen boosts) setting_always_send_emails: Normaliter worden er geen e-mailmeldingen verstuurd wanneer je actief Mastodon gebruikt + setting_default_quote_policy: Het is voor gebruikers die vermeld worden altijd toegestaan om te citeren. Deze instelling is alleen van kracht voor berichten die gemaakt zijn met de volgende Mastodon-versie, maar je kunt je voorkeur nu alvast instellen setting_default_sensitive: Gevoelige media wordt standaard verborgen en kan met één klik worden getoond setting_display_media_default: Als gevoelig gemarkeerde media verbergen setting_display_media_hide_all: Media altijd verbergen @@ -148,7 +149,9 @@ nl: min_age: Mag niet lager zijn dan de minimale vereiste leeftijd volgens de wetten van jouw jurisdictie. user: chosen_languages: Alleen berichten in de aangevinkte talen worden op de openbare tijdlijnen getoond - date_of_birth: We moeten ervoor zorgen dat je tenminste %{age} bent om Mastodon te gebruiken. Dit wordt niet opgeslagen. + date_of_birth: + one: We moeten er zeker van zijn dat je tenminste %{count} bent om Mastodon te mogen gebruiken. Deze informatie wordt niet door ons opgeslagen. + other: We moeten er zeker van zijn dat je tenminste %{count} bent om Mastodon te mogen gebruiken. Deze informatie wordt niet door ons opgeslagen. role: De rol bepaalt welke rechten de gebruiker heeft. user_role: color: Kleur die gebruikt wordt voor de rol in de UI, als RGB in hexadecimale formaat @@ -229,6 +232,7 @@ nl: setting_boost_modal: Vraag voor het boosten van een bericht een bevestiging setting_default_language: Taal van jouw berichten setting_default_privacy: Zichtbaarheid van nieuwe berichten + setting_default_quote_policy: Wie kan citeren setting_default_sensitive: Media altijd als gevoelig markeren setting_delete_modal: Vraag voor het verwijderen van een bericht een bevestiging setting_disable_hover_cards: Hover-kaarten met profielvoorbeelden uitschakelen diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index b742375f12..bca47319cb 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -148,7 +148,6 @@ nn: min_age: Skal ikkje vere under minstealder som krevst av lover i jurisdiksjonen din. user: chosen_languages: Når merka vil berre tuta på dei valde språka synast på offentlege tidsliner - date_of_birth: Me må syta for at du er minst %{age} for å bruka Masodon. Me lagrar ikkje dette. role: Rolla kontrollerer kva løyve brukaren har. user_role: color: Fargen som skal nyttast for denne rolla i heile brukargrensesnittet, som RGB i hex-format diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 64221777d7..bb84129c0f 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -148,7 +148,6 @@ pt-BR: min_age: Não deve ter menos que a idade mínima exigida pelas suas leis locais. user: chosen_languages: Apenas as publicações dos idiomas selecionados serão exibidas nas linhas públicas - date_of_birth: Precisamos ter certeza de que você tem, no mínimo, %{age} anos para usar o Mastodon. Não armazenaremos essa informação. role: A função controla quais permissões o usuário tem. user_role: color: Cor a ser usada para o cargo em toda a interface do usuário, como RGB no formato hexadecimal diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml index 5549ef40d6..d08024ef68 100644 --- a/config/locales/simple_form.pt-PT.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -148,7 +148,6 @@ pt-PT: min_age: Não deve ter menos do que a idade mínima exigida pela legislação da sua jurisdição. user: chosen_languages: Quando selecionado, só serão mostradas nas cronologias públicas as publicações nos idiomas escolhidos - date_of_birth: Temos de ter a certeza de que tens pelo menos %{age} para usar o Mastodon. Não vamos guardar esta informação. role: A função controla as permissões que o utilizador tem. user_role: color: Cor a ser utilizada para a função em toda a interface de utilizador, como RGB no formato hexadecimal diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 24f24acadb..ff6f272d25 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -148,7 +148,6 @@ ru: min_age: Не меньше минимального возраста, требуемого по закону в вашей юрисдикции. user: chosen_languages: Если выбрано, то в публичных лентах будут показаны только посты на выбранных языках. - date_of_birth: Нужно убедиться, что вам не меньше %{age} лет. Мы не храним введённые здесь данные. role: Роль определяет, какими правами обладает пользователь. user_role: color: Цвет, который будет использоваться для роли в интерфейсе (UI), как RGB в формате HEX diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 5d55844aa9..d59fc2c844 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -146,7 +146,6 @@ sl: min_age: Ne smete biti mlajši od starostne omejitve, ki jo postavljajo zakoni vašega pravosodnega sistema. user: chosen_languages: Ko je označeno, bodo v javnih časovnicah prikazane samo objave v izbranih jezikih - date_of_birth: Prepričati se moramo, da so uporabniki Mastodona stari vsaj %{age} let. Tega podatka ne bomo shranili. role: Vloga določa, katera dovoljenja ima uporabnik. user_role: color: Barva, uporabljena za vlogo po celem up. vmesniku, podana v šestnajstiškem zapisu RGB diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index 9f5ada184c..5059fbd1e9 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -56,6 +56,7 @@ sq: scopes: Cilat API do të lejohen të përdorin aplikacioni. Nëse përzgjidhni një shkallë të epërme, nuk ju duhet të përzgjidhni individualet një nga një. setting_aggregate_reblogs: Mos shfaq përforcime të reja për mesazhe që janë përforcuar tani së fundi (prek vetëm përforcime të marra rishtas) setting_always_send_emails: Normalisht s’do të dërgohen njoftime, kur përdorni aktivisht Mastodon-in + setting_default_quote_policy: Përdoruesit e përmendur lejohen përherë të citojnë. Ky rregullim do të ketë efekt vetëm për postime të krijuar me versionin pasues të Mastodon-it, por mund të përzgjidhni paraprakisht parapëlqimin tuaj setting_default_sensitive: Media rezervat fshihet, si parazgjedhje, dhe mund të shfaqet me një klikim setting_display_media_default: Fshih media me shenjën rezervat setting_display_media_hide_all: Fshih përherë mediat @@ -147,7 +148,6 @@ sq: min_age: S’duhet të jetë nën moshën minimum të domosdoshme nga ligjet në juridiksionin tuaj. user: chosen_languages: Në iu vëntë shenjë, te rrjedha kohore publike do të shfaqen vetëm mesazhe në gjuhët e përzgjedhura - date_of_birth: Na duhet të sigurohemi se jeni të paktën %{age}, që të përdorni Mastodon-in. Këtë s’e depozitojmë. role: Roli kontrollon cilat leje ka përdoruesi. user_role: color: Ngjyrë për t’u përdorur për rolin nëpër UI, si RGB në format gjashtëmbëdhjetësh @@ -228,6 +228,7 @@ sq: setting_boost_modal: Shfaq dialog ripohimi përpara përforcimi setting_default_language: Gjuhë postimi setting_default_privacy: Privatësi postimi + setting_default_quote_policy: Cilët mund të citojnë setting_default_sensitive: Mediave vëru përherë shenjë si rezervat setting_delete_modal: Shfaq dialog ripohimi përpara fshirjes së një mesazhi setting_disable_hover_cards: Çaktivizo paraparje profili, kur i kalohet kursori përsipër diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 22300ccec3..9a85820fb0 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -148,7 +148,6 @@ tr: min_age: Tabi olduğunuz yasaların gerektirdiği yaştan düşük olmamalıdır. user: chosen_languages: İşaretlendiğinde, yalnızca seçilen dillerdeki gönderiler genel zaman çizelgelerinde görüntülenir - date_of_birth: Mastodon kullanmak için en az %{age} yaşında olduğunuzdan emin olmalıyız. Bu bilgiyi saklamıyoruz. role: Rol, kullanıcıların sahip olduğu izinleri denetler. user_role: color: Arayüz boyunca rol için kullanılacak olan renk, hex biçiminde RGB diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index b43aaeb234..2561eeb2ca 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -147,7 +147,6 @@ uk: min_age: Не повинно бути нижче мінімального віку, необхідного законодавством вашої юрисдикції. user: chosen_languages: У глобальних стрічках будуть показані дописи тільки вибраними мовами - date_of_birth: Ми повинні переконатися, що вам принаймні %{age}, щоб використовувати Mastodon. Ми не будемо зберігати це. role: Роль визначає, які права має користувач. user_role: color: Колір, який буде використовуватися для ролі у всьому інтерфейсі, як RGB у форматі hex diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml index 8b78787f86..16caf67a1c 100644 --- a/config/locales/simple_form.vi.yml +++ b/config/locales/simple_form.vi.yml @@ -56,6 +56,7 @@ vi: scopes: Ứng dụng sẽ được phép truy cập những API nào. Nếu bạn chọn quyền cấp cao nhất, không cần chọn quyền nhỏ. setting_aggregate_reblogs: Nếu một tút đã được đăng lại thì những lượt đăng lại sau sẽ không hiện trên bảng tin nữa setting_always_send_emails: Bình thường thì sẽ không gửi khi bạn đang dùng Mastodon + setting_default_quote_policy: Thiết lập này chỉ hiệu lực đối với các tút được tạo bằng phiên bản Mastodon tiếp theo, nhưng bạn có thể chọn trước tùy chọn của mình trong quá trình chuẩn bị setting_default_sensitive: Bắt buộc nhấn vào mới có thể xem setting_display_media_default: Click để xem setting_display_media_hide_all: Luôn ẩn @@ -148,7 +149,8 @@ vi: min_age: Không được dưới độ tuổi tối thiểu theo quy định của luật pháp tại khu vực của bạn. user: chosen_languages: Chỉ hiển thị những tút viết bằng các ngôn ngữ sau - date_of_birth: Chúng tôi phải đảm bảo rằng bạn ít nhất %{age} tuổi để sử dụng Mastodon. Chúng tôi không lưu trữ thông tin này. + date_of_birth: + other: Chúng tôi cần đảm bảo rằng bạn lớn hơn %{count} tuổi để sử dụng Mastodon. Chúng tôi không lưu trữ thông tin này. role: Vai trò kiểm soát những quyền mà người dùng có. user_role: color: Màu được sử dụng cho vai trò trong toàn bộ giao diện người dùng, dưới dạng RGB ở định dạng hex @@ -229,6 +231,7 @@ vi: setting_boost_modal: Hỏi trước khi đăng lại tút setting_default_language: Ngôn ngữ đăng setting_default_privacy: Kiểu đăng + setting_default_quote_policy: Ai có thể trích dẫn setting_default_sensitive: Đánh dấu media nhạy cảm setting_delete_modal: Hỏi trước khi xóa tút setting_disable_hover_cards: Tắt thẻ xem trước hồ sơ diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index 5b18be5376..f82561420b 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -148,7 +148,6 @@ zh-CN: min_age: 不应低于您所在地法律管辖权要求的最低年龄。 user: chosen_languages: 仅选中语言的嘟文会出现在公共时间线上(全不选则显示所有语言的嘟文) - date_of_birth: 我们必须确认%{age}岁以上的用户才能使用Mastodon。我们不会存储该信息。 role: 角色用于控制用户拥有的权限。 user_role: color: 在界面各处用于标记该角色的颜色,以十六进制 RGB 格式表示 diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index fc86c77b74..fb51f2ed3e 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -56,6 +56,7 @@ zh-TW: scopes: 允許使應用程式存取的 API。 若您選擇最高階範圍,則無須選擇個別項目。 setting_aggregate_reblogs: 不顯示最近已被轉嘟之嘟文的最新轉嘟(只影響最新收到的嘟文) setting_always_send_emails: 一般情況下若您活躍使用 Mastodon ,我們不會寄送電子郵件通知 + setting_default_quote_policy: 已提及使用者總是能引用嘟文。此設定將僅生效於下一版本 Mastodon 建立之嘟文,但您可以預先選好您的偏好設定 setting_default_sensitive: 敏感內容媒體預設隱藏,且按一下即可重新顯示 setting_display_media_default: 隱藏標為敏感內容的媒體 setting_display_media_hide_all: 總是隱藏所有媒體 @@ -148,7 +149,8 @@ zh-TW: min_age: 不應低於您所屬法律管轄區要求之最低年齡。 user: chosen_languages: 當選取時,只有選取語言之嘟文會於公開時間軸中顯示 - date_of_birth: 我們必須確認您至少年滿 %{age} 以使用 Mastodon。我們不會儲存此資料。 + date_of_birth: + other: 我們必須確認您至少年滿 %{count} 以使用 Mastodon。我們不會儲存此資料。 role: 角色控制使用者有哪些權限。 user_role: color: 於整個使用者介面中用於角色的顏色,十六進位格式的 RGB @@ -229,6 +231,7 @@ zh-TW: setting_boost_modal: 轉嘟前先詢問我 setting_default_language: 嘟文語言 setting_default_privacy: 嘟文隱私設定 + setting_default_quote_policy: 誰能引用此嘟文 setting_default_sensitive: 總是將媒體標記為敏感內容 setting_delete_modal: 刪除嘟文前先詢問我 setting_disable_hover_cards: 停用於滑鼠懸停時預覽個人檔案 diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 1c414edbed..b0a1fae266 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1848,6 +1848,10 @@ sq: limit: Keni fiksuar tashmë numrin maksimum të mesazheve ownership: S’mund të fiksohen mesazhet e të tjerëve reblog: S’mund të fiksohet një përforcim + quote_policies: + followers: Ndjekës dhe përdorues të përmendur + nobody: Vetëm përdorues të përmendur + public: Këdo title: '%{name}: "%{quote}"' visibilities: direct: I drejtpërdrejtë diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 5e03fa01c9..1d78ceb511 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1815,6 +1815,10 @@ vi: limit: Bạn đã ghim quá số lượng tút cho phép ownership: Không thể ghim tút của người khác reblog: Không thể ghim tút đăng lại + quote_policies: + followers: Người theo dõi và người được nhắc đến + nobody: Những người được nhắc đến + public: Mọi người title: '%{name}: "%{quote}"' visibilities: direct: Nhắn riêng diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index eddb113ed3..11e7cc14ca 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1817,6 +1817,10 @@ zh-TW: limit: 釘選嘟文的數量已達上限 ownership: 不能釘選他人的嘟文 reblog: 不能釘選轉嘟 + quote_policies: + followers: 跟隨者與已提及使用者 + nobody: 僅限已提及使用者 + public: 任何人 title: "%{name}:「%{quote}」" visibilities: direct: 私訊 From c45ce549afe17cca1ba474a67112038e5966f68e Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 14 May 2025 10:59:08 +0200 Subject: [PATCH 011/106] refactor: Prevent leading slashes in API urls (#34680) --- app/javascript/mastodon/api.ts | 9 +++++---- app/javascript/mastodon/api/accounts.ts | 2 +- app/javascript/mastodon/api/polls.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/api.ts b/app/javascript/mastodon/api.ts index a41b058d2c..dc9c20b508 100644 --- a/app/javascript/mastodon/api.ts +++ b/app/javascript/mastodon/api.ts @@ -83,6 +83,7 @@ export default function api(withAuthorization = true) { return instance; } +type ApiUrl = `v${1 | 2}/${string}`; type RequestParamsOrData = Record; export async function apiRequest( @@ -105,28 +106,28 @@ export async function apiRequest( } export async function apiRequestGet( - url: string, + url: ApiUrl, params?: RequestParamsOrData, ) { return apiRequest('GET', url, { params }); } export async function apiRequestPost( - url: string, + url: ApiUrl, data?: RequestParamsOrData, ) { return apiRequest('POST', url, { data }); } export async function apiRequestPut( - url: string, + url: ApiUrl, data?: RequestParamsOrData, ) { return apiRequest('PUT', url, { data }); } export async function apiRequestDelete( - url: string, + url: ApiUrl, params?: RequestParamsOrData, ) { return apiRequest('DELETE', url, { params }); diff --git a/app/javascript/mastodon/api/accounts.ts b/app/javascript/mastodon/api/accounts.ts index 6ce7d7248c..fb99978cad 100644 --- a/app/javascript/mastodon/api/accounts.ts +++ b/app/javascript/mastodon/api/accounts.ts @@ -36,6 +36,6 @@ export const apiGetEndorsedAccounts = (id: string) => apiRequestGet(`v1/accounts/${id}/endorsements`); export const apiGetFamiliarFollowers = (id: string) => - apiRequestGet('/v1/accounts/familiar_followers', { + apiRequestGet('v1/accounts/familiar_followers', { id, }); diff --git a/app/javascript/mastodon/api/polls.ts b/app/javascript/mastodon/api/polls.ts index cb659986f5..d1a97c53c5 100644 --- a/app/javascript/mastodon/api/polls.ts +++ b/app/javascript/mastodon/api/polls.ts @@ -2,9 +2,9 @@ import { apiRequestGet, apiRequestPost } from 'mastodon/api'; import type { ApiPollJSON } from 'mastodon/api_types/polls'; export const apiGetPoll = (pollId: string) => - apiRequestGet(`/v1/polls/${pollId}`); + apiRequestGet(`v1/polls/${pollId}`); export const apiPollVote = (pollId: string, choices: string[]) => - apiRequestPost(`/v1/polls/${pollId}/votes`, { + apiRequestPost(`v1/polls/${pollId}/votes`, { choices, }); From aa04efb92a47cf27c1aafb2aeb21584e91c526ac Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 May 2025 11:43:07 +0200 Subject: [PATCH 012/106] Move server rule creation form to its own page (#34637) --- app/controllers/admin/rules_controller.rb | 10 +++++++--- app/views/admin/rules/index.html.haml | 15 +++------------ app/views/admin/rules/new.html.haml | 14 ++++++++++++++ config/routes/admin.rb | 2 +- spec/system/admin/rules_spec.rb | 2 ++ 5 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 app/views/admin/rules/new.html.haml diff --git a/app/controllers/admin/rules_controller.rb b/app/controllers/admin/rules_controller.rb index 289b6a98c3..837eb91489 100644 --- a/app/controllers/admin/rules_controller.rb +++ b/app/controllers/admin/rules_controller.rb @@ -2,13 +2,17 @@ module Admin class RulesController < BaseController - before_action :set_rule, except: [:index, :create] + before_action :set_rule, except: [:index, :new, :create] def index authorize :rule, :index? @rules = Rule.ordered - @rule = Rule.new + end + + def new + authorize :rule, :create? + @rule = Rule.new end def edit @@ -24,7 +28,7 @@ module Admin redirect_to admin_rules_path else @rules = Rule.ordered - render :index + render :new end end diff --git a/app/views/admin/rules/index.html.haml b/app/views/admin/rules/index.html.haml index 5a2789edcf..41bff18d2f 100644 --- a/app/views/admin/rules/index.html.haml +++ b/app/views/admin/rules/index.html.haml @@ -1,21 +1,12 @@ - content_for :page_title do = t('admin.rules.title') -%p= t('admin.rules.description_html') +- content_for :heading_actions do + - if can? :create, :rule + = link_to t('admin.rules.add_new'), new_admin_rule_path, class: 'button' %hr.spacer/ -- if can? :create, :rule - = simple_form_for @rule, url: admin_rules_path do |form| - = render 'shared/error_messages', object: @rule - - = render form - - .actions - = form.button :button, t('admin.rules.add_new'), type: :submit - - %hr.spacer/ - - if @rules.empty? .muted-hint.center-text = t 'admin.rules.empty' diff --git a/app/views/admin/rules/new.html.haml b/app/views/admin/rules/new.html.haml new file mode 100644 index 0000000000..bc93c7df55 --- /dev/null +++ b/app/views/admin/rules/new.html.haml @@ -0,0 +1,14 @@ +- content_for :page_title do + = t('admin.rules.add_new') + +%p= t('admin.rules.description_html') + +%hr.spacer/ + += simple_form_for @rule, url: admin_rules_path do |form| + = render 'shared/error_messages', object: @rule + + = render form + + .actions + = form.button :button, t('admin.rules.add_new'), type: :submit diff --git a/config/routes/admin.rb b/config/routes/admin.rb index d5459a2735..2f7b86162b 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -93,7 +93,7 @@ namespace :admin do end end - resources :rules, only: [:index, :create, :edit, :update, :destroy] + resources :rules, only: [:index, :new, :create, :edit, :update, :destroy] resources :webhooks do member do diff --git a/spec/system/admin/rules_spec.rb b/spec/system/admin/rules_spec.rb index 95391ba33d..afe9e87a52 100644 --- a/spec/system/admin/rules_spec.rb +++ b/spec/system/admin/rules_spec.rb @@ -26,6 +26,8 @@ RSpec.describe 'Admin Rules' do it 'creates new record with valid attributes' do visit admin_rules_path + click_on I18n.t('admin.rules.add_new') + # Invalid submission fill_in 'rule_text', with: '' expect { submit_form } From e8e93b82f11d93fef7ecc02881c449c2e46a5d10 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 May 2025 15:43:34 +0200 Subject: [PATCH 013/106] Fix `NoMethodError` in `ActivityPub::FetchAllRepliesWorker` (#34682) --- app/workers/activitypub/fetch_all_replies_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/activitypub/fetch_all_replies_worker.rb b/app/workers/activitypub/fetch_all_replies_worker.rb index 63e1f9618f..d4ac3e85b8 100644 --- a/app/workers/activitypub/fetch_all_replies_worker.rb +++ b/app/workers/activitypub/fetch_all_replies_worker.rb @@ -78,7 +78,7 @@ class ActivityPub::FetchAllRepliesWorker # @param root_status_uri [String] def get_root_replies(root_status_uri, options = {}) root_status_body = fetch_resource(root_status_uri, true) - raise RuntimeError("FetchAllRepliesWorker - #{@root_status.uri}: Root status could not be fetched") if root_status_body.nil? + return if root_status_body.nil? FetchReplyWorker.perform_async(root_status_uri, { **options, prefetched_body: root_status_body }) From 1b68020331783c365cd5baa696945d0d8bc68f72 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 14 May 2025 16:50:32 +0200 Subject: [PATCH 014/106] Fix admin dashboard crash on specific Elasticsearch connection errors (#34683) --- app/lib/admin/system_check/elasticsearch_check.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index e427c83f82..11a1abd777 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -17,7 +17,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck return true unless Chewy.enabled? running_version.present? && compatible_version? && cluster_health['status'] == 'green' && indexes_match? && specifications_match? && preset_matches? - rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error + rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error, HTTPClient::KeepAliveDisconnected false end @@ -54,7 +54,7 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck else Admin::SystemCheck::Message.new(:elasticsearch_preset, nil, 'https://docs.joinmastodon.org/admin/elasticsearch/#scaling') end - rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error + rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error, HTTPClient::KeepAliveDisconnected Admin::SystemCheck::Message.new(:elasticsearch_running_check) end From d475bcce65704e828e1a32590f6c98fc42baa882 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 10:00:55 +0200 Subject: [PATCH 015/106] chore(deps): update dependency bootsnap to v1.18.5 (#34684) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2ccbae6b1b..b013f3d062 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -124,7 +124,7 @@ GEM binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) blurhash (0.1.8) - bootsnap (1.18.4) + bootsnap (1.18.5) msgpack (~> 1.2) brakeman (7.0.2) racc From ccffa11f2be5df500e6a69fdc6c0c2288fa85427 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 15 May 2025 10:07:38 +0200 Subject: [PATCH 016/106] refactor: Remove duplicated `AvatarGroup` CSS and familiar followers cleanup (#34681) --- .../components/__tests__/avatar-test.jsx | 6 ++-- app/javascript/mastodon/components/avatar.tsx | 30 ++++++++++++---- .../mastodon/components/avatar_group.tsx | 31 ++++------------ .../components/familiar_followers.tsx | 24 ++++++++----- .../mastodon/features/lists/members.tsx | 13 ++++--- .../mastodon/features/lists/new.tsx | 35 +++++++++++-------- .../notification_group_with_status.tsx | 22 ++++++++---- app/javascript/mastodon/locales/en.json | 1 - .../styles/mastodon/components.scss | 14 ++++++-- app/javascript/styles/mastodon/forms.scss | 34 ++++-------------- 10 files changed, 110 insertions(+), 100 deletions(-) diff --git a/app/javascript/mastodon/components/__tests__/avatar-test.jsx b/app/javascript/mastodon/components/__tests__/avatar-test.jsx index 21c3ae5800..fc87c0bf72 100644 --- a/app/javascript/mastodon/components/__tests__/avatar-test.jsx +++ b/app/javascript/mastodon/components/__tests__/avatar-test.jsx @@ -1,11 +1,13 @@ -import { fromJS } from 'immutable'; import renderer from 'react-test-renderer'; +import { accountDefaultValues, createAccountFromServerJSON } from '@/mastodon/models/account'; + import { Avatar } from '../avatar'; describe('', () => { - const account = fromJS({ + const account = createAccountFromServerJSON({ + ...accountDefaultValues, username: 'alice', acct: 'alice', display_name: 'Alice', diff --git a/app/javascript/mastodon/components/avatar.tsx b/app/javascript/mastodon/components/avatar.tsx index a2dc0b782e..fb331813a9 100644 --- a/app/javascript/mastodon/components/avatar.tsx +++ b/app/javascript/mastodon/components/avatar.tsx @@ -1,17 +1,21 @@ import { useState, useCallback } from 'react'; import classNames from 'classnames'; +import { Link } from 'react-router-dom'; import { useHovering } from 'mastodon/hooks/useHovering'; import { autoPlayGif } from 'mastodon/initial_state'; import type { Account } from 'mastodon/models/account'; interface Props { - account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there - size: number; + account: + | Pick + | undefined; // FIXME: remove `undefined` once we know for sure its always there + size?: number; style?: React.CSSProperties; inline?: boolean; animate?: boolean; + withLink?: boolean; counter?: number | string; counterBorderColor?: string; } @@ -21,6 +25,7 @@ export const Avatar: React.FC = ({ animate = autoPlayGif, size = 20, inline = false, + withLink = false, style: styleFromParent, counter, counterBorderColor, @@ -35,10 +40,7 @@ export const Avatar: React.FC = ({ height: `${size}px`, }; - const src = - hovering || animate - ? account?.get('avatar') - : account?.get('avatar_static'); + const src = hovering || animate ? account?.avatar : account?.avatar_static; const handleLoad = useCallback(() => { setLoading(false); @@ -48,7 +50,7 @@ export const Avatar: React.FC = ({ setError(true); }, [setError]); - return ( + const avatar = (
= ({ )}
); + + if (withLink) { + return ( + + {avatar} + + ); + } + + return avatar; }; diff --git a/app/javascript/mastodon/components/avatar_group.tsx b/app/javascript/mastodon/components/avatar_group.tsx index 4f583defcf..2420728542 100644 --- a/app/javascript/mastodon/components/avatar_group.tsx +++ b/app/javascript/mastodon/components/avatar_group.tsx @@ -1,34 +1,17 @@ import classNames from 'classnames'; -import { Link } from 'react-router-dom'; -import { Avatar } from 'mastodon/components/avatar'; -import { useAppSelector } from 'mastodon/store'; - -const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => { - const account = useAppSelector((state) => state.accounts.get(accountId)); - - if (!account) return null; - - return ( - - - - ); -}; +/** + * Wrapper for displaying a number of Avatar components horizontally, + * either spaced out (default) or overlapping (using the `compact` prop). + */ export const AvatarGroup: React.FC<{ - accountIds: string[]; compact?: boolean; -}> = ({ accountIds, compact = false }) => ( + children: React.ReactNode; +}> = ({ children, compact = false }) => (
- {accountIds.map((accountId) => ( - - ))} + {children}
); diff --git a/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx index b3b97c317b..1beaceabb4 100644 --- a/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx @@ -5,16 +5,21 @@ import { FormattedMessage } from 'react-intl'; import { Link } from 'react-router-dom'; import { fetchAccountsFamiliarFollowers } from '@/mastodon/actions/accounts_familiar_followers'; +import { Avatar } from '@/mastodon/components/avatar'; import { AvatarGroup } from '@/mastodon/components/avatar_group'; import type { Account } from '@/mastodon/models/account'; import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts'; import { useAppDispatch, useAppSelector } from '@/mastodon/store'; -const AccountLink: React.FC<{ account?: Account }> = ({ account }) => ( - - {account?.display_name} - -); +const AccountLink: React.FC<{ account?: Account }> = ({ account }) => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const name = account?.display_name || `@${account?.acct}`; + return ( + + {name} + + ); +}; const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({ familiarFollowers, @@ -74,10 +79,11 @@ export const FamiliarFollowers: React.FC<{ accountId: string }> = ({ return (
- account.id)} - /> + + {familiarFollowers.map((account) => ( + + ))} +
); diff --git a/app/javascript/mastodon/features/lists/members.tsx b/app/javascript/mastodon/features/lists/members.tsx index 41d02ad9fc..e31943b78b 100644 --- a/app/javascript/mastodon/features/lists/members.tsx +++ b/app/javascript/mastodon/features/lists/members.tsx @@ -35,8 +35,11 @@ import { VerifiedBadge } from 'mastodon/components/verified_badge'; import { me } from 'mastodon/initial_state'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; -const messages = defineMessages({ - heading: { id: 'column.list_members', defaultMessage: 'Manage list members' }, +export const messages = defineMessages({ + manageMembers: { + id: 'column.list_members', + defaultMessage: 'Manage list members', + }, placeholder: { id: 'lists.search', defaultMessage: 'Search', @@ -255,10 +258,10 @@ const ListMembers: React.FC<{ return ( - {intl.formatMessage(messages.heading)} + {intl.formatMessage(messages.manageMembers)} diff --git a/app/javascript/mastodon/features/lists/new.tsx b/app/javascript/mastodon/features/lists/new.tsx index 100f126c37..8253ab58b7 100644 --- a/app/javascript/mastodon/features/lists/new.tsx +++ b/app/javascript/mastodon/features/lists/new.tsx @@ -9,16 +9,23 @@ import { isFulfilled } from '@reduxjs/toolkit'; import Toggle from 'react-toggle'; +import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; import { fetchList } from 'mastodon/actions/lists'; import { createList, updateList } from 'mastodon/actions/lists_typed'; import { apiGetAccounts } from 'mastodon/api/lists'; +import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; import type { RepliesPolicyType } from 'mastodon/api_types/lists'; +import { Avatar } from 'mastodon/components/avatar'; +import { AvatarGroup } from 'mastodon/components/avatar_group'; import { Column } from 'mastodon/components/column'; import { ColumnHeader } from 'mastodon/components/column_header'; +import { Icon } from 'mastodon/components/icon'; import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; +import { messages as membersMessages } from './members'; + const messages = defineMessages({ edit: { id: 'column.edit_list', defaultMessage: 'Edit list' }, create: { id: 'column.create_list', defaultMessage: 'Create list' }, @@ -27,42 +34,40 @@ const messages = defineMessages({ const MembersLink: React.FC<{ id: string; }> = ({ id }) => { - const [count, setCount] = useState(0); - const [avatars, setAvatars] = useState([]); + const intl = useIntl(); + const [avatarCount, setAvatarCount] = useState(0); + const [avatarAccounts, setAvatarAccounts] = useState([]); useEffect(() => { void apiGetAccounts(id) .then((data) => { - setCount(data.length); - setAvatars(data.slice(0, 3).map((a) => a.avatar)); - return ''; + setAvatarCount(data.length); + setAvatarAccounts(data.slice(0, 3)); }) .catch(() => { // Nothing }); - }, [id, setCount, setAvatars]); + }, [id]); return (
- + {intl.formatMessage(membersMessages.manageMembers)} +
-
- {avatars.map((url) => ( - + + {avatarAccounts.map((a) => ( + ))} -
+ ); }; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx index cbb0b85f1d..b0b86eead1 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx @@ -7,6 +7,7 @@ import { HotKeys } from 'react-hotkeys'; import { replyComposeById } from 'mastodon/actions/compose'; import { navigateToStatus } from 'mastodon/actions/statuses'; +import { Avatar } from 'mastodon/components/avatar'; import { AvatarGroup } from 'mastodon/components/avatar_group'; import type { IconProp } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon'; @@ -17,6 +18,14 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { DisplayedName } from './displayed_name'; import { EmbeddedStatus } from './embedded_status'; +export const AvatarById: React.FC<{ accountId: string }> = ({ accountId }) => { + const account = useAppSelector((state) => state.accounts.get(accountId)); + + if (!account) return null; + + return ; +}; + export type LabelRenderer = ( displayedName: JSX.Element, total: number, @@ -99,12 +108,13 @@ export const NotificationGroupWithStatus: React.FC<{
- + + {accountIds + .slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS) + .map((id) => ( + + ))} + {actions && (
{actions}
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 45bc6109b7..2d49346ac7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -518,7 +518,6 @@ "lists.exclusive": "Hide members in Home", "lists.exclusive_hint": "If someone is on this list, hide them in your Home feed to avoid seeing their posts twice.", "lists.find_users_to_add": "Find users to add", - "lists.list_members": "List members", "lists.list_members_count": "{count, plural, one {# member} other {# members}}", "lists.list_name": "List name", "lists.new_list_name": "New list name", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ea48d98ec6..f3271b2d3b 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2179,10 +2179,18 @@ a .account__avatar { & > :not(:first-child) { margin-inline-start: -8px; + } - .account__avatar { - box-shadow: 0 0 0 2px var(--background-color); - } + & > :first-child { + transform: rotate(-4deg); + } + + & > :nth-child(2) { + transform: rotate(-2deg); + } + + .account__avatar { + box-shadow: 0 0 0 2px var(--background-color); } } diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 6ec6a4199f..5dbe4f9e08 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1440,34 +1440,12 @@ code { display: block; color: $primary-text-color; } - } - } -} - -.avatar-pile { - display: flex; - align-items: center; - - img { - display: block; - border-radius: 8px; - width: 32px; - height: 32px; - border: 2px solid var(--background-color); - background: var(--surface-background-color); - margin-inline-end: -16px; - transform: rotate(0); - - &:first-child { - transform: rotate(-4deg); - } - - &:nth-child(2) { - transform: rotate(-2deg); - } - - &:last-child { - margin-inline-end: 0; + + .icon { + vertical-align: -5px; + width: 20px; + height: 20px; + } } } } From e17c78b67934308748d5d036bc144fb6b1ff8509 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 10:50:48 +0200 Subject: [PATCH 017/106] New Crowdin Translations (automated) (#34695) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/bg.json | 5 ++- app/javascript/mastodon/locales/ca.json | 4 +- app/javascript/mastodon/locales/cs.json | 1 - app/javascript/mastodon/locales/cy.json | 1 - app/javascript/mastodon/locales/da.json | 1 - app/javascript/mastodon/locales/de.json | 1 - app/javascript/mastodon/locales/el.json | 1 - app/javascript/mastodon/locales/en-GB.json | 1 - app/javascript/mastodon/locales/eo.json | 1 - app/javascript/mastodon/locales/es-AR.json | 1 - app/javascript/mastodon/locales/es-MX.json | 1 - app/javascript/mastodon/locales/es.json | 3 +- app/javascript/mastodon/locales/et.json | 1 - app/javascript/mastodon/locales/fa.json | 1 - app/javascript/mastodon/locales/fi.json | 1 - app/javascript/mastodon/locales/fo.json | 1 - app/javascript/mastodon/locales/fr-CA.json | 1 - app/javascript/mastodon/locales/fr.json | 1 - app/javascript/mastodon/locales/fy.json | 1 - app/javascript/mastodon/locales/ga.json | 1 - app/javascript/mastodon/locales/gd.json | 1 - app/javascript/mastodon/locales/gl.json | 1 - app/javascript/mastodon/locales/he.json | 1 - app/javascript/mastodon/locales/hu.json | 1 - app/javascript/mastodon/locales/ia.json | 1 - app/javascript/mastodon/locales/io.json | 1 - app/javascript/mastodon/locales/is.json | 1 - app/javascript/mastodon/locales/it.json | 1 - app/javascript/mastodon/locales/ja.json | 1 - app/javascript/mastodon/locales/ko.json | 1 - app/javascript/mastodon/locales/lt.json | 1 - app/javascript/mastodon/locales/nan.json | 1 - app/javascript/mastodon/locales/nl.json | 1 - app/javascript/mastodon/locales/nn.json | 1 - app/javascript/mastodon/locales/pa.json | 1 - app/javascript/mastodon/locales/pl.json | 1 - app/javascript/mastodon/locales/pt-BR.json | 1 - app/javascript/mastodon/locales/pt-PT.json | 1 - app/javascript/mastodon/locales/ru.json | 45 +++++++++++----------- app/javascript/mastodon/locales/sk.json | 1 - app/javascript/mastodon/locales/sl.json | 1 - app/javascript/mastodon/locales/sq.json | 1 - app/javascript/mastodon/locales/sv.json | 1 - app/javascript/mastodon/locales/th.json | 2 +- app/javascript/mastodon/locales/tr.json | 1 - app/javascript/mastodon/locales/uk.json | 1 - app/javascript/mastodon/locales/vi.json | 1 - app/javascript/mastodon/locales/zh-CN.json | 1 - app/javascript/mastodon/locales/zh-TW.json | 1 - config/locales/ca.yml | 4 ++ config/locales/lt.yml | 2 +- config/locales/simple_form.bg.yml | 5 +++ config/locales/simple_form.ca.yml | 5 +++ config/locales/simple_form.gl.yml | 5 +++ config/locales/simple_form.tr.yml | 2 + config/locales/tr.yml | 4 ++ 56 files changed, 57 insertions(+), 73 deletions(-) diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 72ff7ea333..f3c6f771f5 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -28,6 +28,9 @@ "account.edit_profile": "Редактиране на профила", "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": "Хаштагове", @@ -304,6 +307,7 @@ "emoji_button.search_results": "Резултати от търсене", "emoji_button.symbols": "Символи", "emoji_button.travel": "Пътуване и места", + "empty_column.account_featured_other.unknown": "Този акаунт още не е отличил нищо.", "empty_column.account_hides_collections": "Този потребител е избрал да не дава тази информация", "empty_column.account_suspended": "Спрян акаунт", "empty_column.account_timeline": "Тук няма публикации!", @@ -508,7 +512,6 @@ "lists.exclusive": "Скриване на членуващи в Начало", "lists.exclusive_hint": "Ако някой е в този списък, то скрийте го в инфоканала си на Начало, за да избегнете виждането на публикациите му два пъти.", "lists.find_users_to_add": "Намерете потребители за добавяне", - "lists.list_members": "Списък членуващи", "lists.list_members_count": "{count, plural, one {# членуващ} other {# членуващи}}", "lists.list_name": "Име на списък", "lists.new_list_name": "Ново име на списък", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 30edd60606..2ea3a36329 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -28,6 +28,9 @@ "account.edit_profile": "Edita el perfil", "account.enable_notifications": "Notifica'm els tuts de @{name}", "account.endorse": "Recomana en el perfil", + "account.familiar_followers_many": "Seguit per {name1}, {name2} i {othersCount, plural, one {# altre compte} other {# altres comptes}}", + "account.familiar_followers_one": "Seguit per {name1}", + "account.familiar_followers_two": "Seguit per {name1} i {name2}", "account.featured": "Destacat", "account.featured.accounts": "Perfils", "account.featured.hashtags": "Etiquetes", @@ -514,7 +517,6 @@ "lists.exclusive": "Amaga membres a Inici", "lists.exclusive_hint": "Si algú és a la llista, amagueu-los de la pantalla d'inici, per a no veure'n les publicacions duplicades.", "lists.find_users_to_add": "Troba usuaris per a afegir", - "lists.list_members": "Membres de la llista", "lists.list_members_count": "{count, plural, one {# membre} other {# membres}}", "lists.list_name": "Nom de la llista", "lists.new_list_name": "Nom de la nova llista", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 679de9e724..a185c59d79 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -518,7 +518,6 @@ "lists.exclusive": "Skrýt členy na domovském kanálu", "lists.exclusive_hint": "Pokud je někdo na tomto seznamu, skryjte jej ve vašem domovském kanálu, abyste se vyhnuli dvojímu vidění jejich příspěvků.", "lists.find_users_to_add": "Najít uživatele, které chcete přidat", - "lists.list_members": "Členové seznamu", "lists.list_members_count": "{count, plural, one {# člen} few {# členové} many {# členů} other {# členů}}", "lists.list_name": "Název seznamu", "lists.new_list_name": "Název nového seznamu", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 6d8b611817..cc717d4e23 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -515,7 +515,6 @@ "lists.exclusive": "Cuddio aelodau yn y Cartref", "lists.exclusive_hint": "Os oes rhywun ar y rhestr hon, cuddiwch nhw yn eich llif Cartref i osgoi gweld eu postiadau ddwywaith.", "lists.find_users_to_add": "Canfod defnyddwyr i'w hychwanegu", - "lists.list_members": "Aelodau rhestr", "lists.list_members_count": "{count, plural, one {# aelod} other {# aelod}}", "lists.list_name": "Enw rhestr", "lists.new_list_name": "Enw rhestr newydd", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index e50023c346..31d15a3eba 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -518,7 +518,6 @@ "lists.exclusive": "Skjul medlemmer i Hjem", "lists.exclusive_hint": "Er nogen er på denne liste, skjul personen i hjemme-feeds for at undgå at se vedkommendes indlæg to gange.", "lists.find_users_to_add": "Find brugere at tilføje", - "lists.list_members": "Liste over medlemmer", "lists.list_members_count": "{count, plural, one {# medlem} other {# medlemmer}}", "lists.list_name": "Listetitel", "lists.new_list_name": "Ny listetitel", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 2464182d52..dfe6c8c389 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -518,7 +518,6 @@ "lists.exclusive": "Mitglieder auf der Startseite ausblenden", "lists.exclusive_hint": "Profile, die sich auf dieser Liste befinden, werden nicht auf deiner Startseite angezeigt, damit deren Beiträge nicht doppelt erscheinen.", "lists.find_users_to_add": "Suche nach Profilen, um sie hinzuzufügen", - "lists.list_members": "Listenmitglieder", "lists.list_members_count": "{count, plural, one {# Mitglied} other {# Mitglieder}}", "lists.list_name": "Titel der Liste", "lists.new_list_name": "Neuer Listentitel", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 441e15622e..fad98100dc 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -497,7 +497,6 @@ "lists.exclusive": "Απόκρυψη μελών από την Αρχική", "lists.exclusive_hint": "Αν κάποιος είναι σε αυτή τη λίστα, απόκρυψέ τον στην Αρχική σου για να αποφύγεις να βλέπεις τις αναρτήσεις του δύο φορές.", "lists.find_users_to_add": "Εύρεση χρηστών για προσθήκη", - "lists.list_members": "Λίστα μελών", "lists.list_members_count": "{count, plural, one {# μέλος} other {# μέλη}}", "lists.list_name": "Όνομα λίστας", "lists.new_list_name": "Νέο όνομα λίστας", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 89bd2d3f1a..cfac9f4e68 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -490,7 +490,6 @@ "lists.exclusive": "Hide members in Home", "lists.exclusive_hint": "If someone is on this list, hide them in your Home feed to avoid seeing their posts twice.", "lists.find_users_to_add": "Find users to add", - "lists.list_members": "List members", "lists.list_members_count": "{count, plural, one {# member} other {# members}}", "lists.list_name": "List name", "lists.new_list_name": "New list name", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 9e90b91965..8538fd0098 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -515,7 +515,6 @@ "lists.exclusive": "Kaŝi membrojn en Hejmpaĝo", "lists.exclusive_hint": "Se iu estas en ĉi tiuj listo, kaŝu ilin en via hejmpaĝo por eviti vidi iliajn afiŝojn dufoje.", "lists.find_users_to_add": "Trovi uzantojn por aldoni", - "lists.list_members": "Listoj de membroj", "lists.list_members_count": "{count, plural,one {# membro} other {# membroj}}", "lists.list_name": "Nomo de la listo", "lists.new_list_name": "Nomo de nova listo", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 96a3af6d2f..f441d8fca8 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -518,7 +518,6 @@ "lists.exclusive": "Ocultar miembros en la línea temporal principal", "lists.exclusive_hint": "Si alguien está en esta lista, ocultalo en tu línea temporal principal para evitar que aparezcan sus mensajes dos veces.", "lists.find_users_to_add": "Buscar usuarios para agregar", - "lists.list_members": "Miembros de lista", "lists.list_members_count": "{count, plural,one {# miembro} other {# miembros}}", "lists.list_name": "Nombre de la lista", "lists.new_list_name": "Nombre de la nueva lista", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index b28ec59b7d..e0ca7223ba 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -518,7 +518,6 @@ "lists.exclusive": "Ocultar miembros en Inicio", "lists.exclusive_hint": "Si alguien aparece en esta lista, ocúltalo en tu página de inicio para evitar ver sus publicaciones dos veces.", "lists.find_users_to_add": "Buscar usuarios para agregar", - "lists.list_members": "Miembros de la lista", "lists.list_members_count": "{count, plural,one {# miembro} other {# miembros}}", "lists.list_name": "Nombre de la lista", "lists.new_list_name": "Nombre de la nueva lista", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 54f100db71..99a4610e20 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.endorse": "Destacar en el perfil", - "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# otro} other {# otros}}", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# más} other {# más}}", "account.familiar_followers_one": "Seguido por {name1}", "account.familiar_followers_two": "Seguido por {name1} y {name2}", "account.featured": "Destacado", @@ -518,7 +518,6 @@ "lists.exclusive": "Ocultar miembros en Inicio", "lists.exclusive_hint": "Si alguien está en esta lista, escóndelo en tu página de inicio para evitar ver sus publicaciones dos veces.", "lists.find_users_to_add": "Buscar usuarios para añadir", - "lists.list_members": "Miembros de la lista", "lists.list_members_count": "{count, plural,one {# miembro} other {# miembros}}", "lists.list_name": "Nombre de la lista", "lists.new_list_name": "Nombre de la nueva lista", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index 41248f5a1e..857eaa9062 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -490,7 +490,6 @@ "lists.exclusive": "Peida avalehelt liikmed", "lists.exclusive_hint": "Kui keegi on selles loetelus, peida ta avalehe lõimest, vältimaks tema postituste kaks korda nägemist.", "lists.find_users_to_add": "Leia kasutajaid, keda lisada", - "lists.list_members": "Loetelu liikmed", "lists.list_members_count": "{count, plural, one {# liige} other {# liiget}}", "lists.list_name": "Loetelu nimi", "lists.new_list_name": "Loetelu uus nimi", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 08f08c030b..2d96a0d19e 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -510,7 +510,6 @@ "lists.exclusive": "نهفتن اعضا در خانه", "lists.exclusive_hint": "اگر کسی در این سیاهه باشد، در خوراک خانگیتان نهفته تا از نمایش دویارهٔ فرسته‌هایش خودداری شود.", "lists.find_users_to_add": "یافتن کاربرانی برای افزودن", - "lists.list_members": "اعضای سیاهه", "lists.list_members_count": "{count, plural,one {# عضو}other {# عضو}}", "lists.list_name": "نام سیاهه", "lists.new_list_name": "نام سیاههٔ جدید", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 7380220e8a..3615185331 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -518,7 +518,6 @@ "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": "Listan jäsenet", "lists.list_members_count": "{count, plural, one {# jäsen} other {# jäsentä}}", "lists.list_name": "Listan nimi", "lists.new_list_name": "Uuden listan nimi", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 281232c1e3..c659f91eb6 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -518,7 +518,6 @@ "lists.exclusive": "Fjal limir á heimarás", "lists.exclusive_hint": "Um onkur er á hesum listanum, so skulu tey fjalast á heimarásini, so tú sleppir undan at síggja postar teirra tvær ferðir.", "lists.find_users_to_add": "Finn brúkarar at leggja afturat", - "lists.list_members": "Lista limir", "lists.list_members_count": "{count, plural, one {# limur} other {# limir}}", "lists.list_name": "Listanavn", "lists.new_list_name": "Nýtt listanavn", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index cc66e58411..fea37d0da2 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -494,7 +494,6 @@ "lists.exclusive": "Cacher les membres de la page d'accueil", "lists.exclusive_hint": "Si quelqu'un est dans cette liste, les cacher dans votre fil pour éviter de voir leurs messages deux fois.", "lists.find_users_to_add": "Trouver des utilisateurs à ajouter", - "lists.list_members": "Lister les membres", "lists.list_members_count": "{count, plural, one {# member} other {# members}}", "lists.list_name": "Nom de la liste", "lists.new_list_name": "Nom de la nouvelle liste", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 8219c4e155..81178dd68c 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -494,7 +494,6 @@ "lists.exclusive": "Cacher les membres de la page d'accueil", "lists.exclusive_hint": "Si quelqu'un est dans cette liste, les cacher dans votre fil pour éviter de voir leurs messages deux fois.", "lists.find_users_to_add": "Trouver des utilisateurs à ajouter", - "lists.list_members": "Lister les membres", "lists.list_members_count": "{count, plural, one {# member} other {# members}}", "lists.list_name": "Nom de la liste", "lists.new_list_name": "Nom de la nouvelle liste", diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json index 238622b501..8d013bf887 100644 --- a/app/javascript/mastodon/locales/fy.json +++ b/app/javascript/mastodon/locales/fy.json @@ -510,7 +510,6 @@ "lists.exclusive": "Leden op jo Startside ferstopje", "lists.exclusive_hint": "As ien op dizze list stiet, ferstopje dizze persoan dan op jo starttiidline om foar te kommen dat harren berjochten twa kear toand wurde.", "lists.find_users_to_add": "Fyn brûkers om ta te foegjen", - "lists.list_members": "Listleden", "lists.list_members_count": "{count, plural, one{# lid} other{# leden}}", "lists.list_name": "Listnamme", "lists.new_list_name": "Nije listnamme", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index f4258bda01..0337c3736c 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -512,7 +512,6 @@ "lists.exclusive": "Folaigh baill sa Bhaile", "lists.exclusive_hint": "Má tá duine ar an liosta seo, cuir i bhfolach iad i do fhotha Baile ionas nach bhfeicfidh tú a bpoist faoi dhó.", "lists.find_users_to_add": "Aimsigh úsáideoirí le cur leis", - "lists.list_members": "Liostaigh baill", "lists.list_members_count": "{count, plural, one {# ball} two {# bhall} few {# baill} many {# baill} other {# baill}}", "lists.list_name": "Ainm an liosta", "lists.new_list_name": "Ainm liosta nua", diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json index b9cbbfe645..b31c51c2b1 100644 --- a/app/javascript/mastodon/locales/gd.json +++ b/app/javascript/mastodon/locales/gd.json @@ -515,7 +515,6 @@ "lists.exclusive": "Falaich na buill san dachaigh", "lists.exclusive_hint": "Falaich an fheadhainn a tha air an liosta seo air loidhne-ama na dachaigh ach nach fhaic thu na postaichean aca dà thuras.", "lists.find_users_to_add": "Lorg cleachdaichean ri chur ris", - "lists.list_members": "Buill na liosta", "lists.list_members_count": "{count, plural, one {# bhall} two {# bhall} few {# buill} other {# ball}}", "lists.list_name": "Ainm na liosta", "lists.new_list_name": "Ainm na liosta ùire", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 6618f26cca..f88ab43be9 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -518,7 +518,6 @@ "lists.exclusive": "Ocultar membros no Inicio", "lists.exclusive_hint": "Se alguén está nesta lista non aparerá na cronoloxía de Inicio para evitar duplicidades das publicacións.", "lists.find_users_to_add": "Buscar persoas que engadir", - "lists.list_members": "Membros da lista", "lists.list_members_count": "{count, plural, one {# membro} other {# membros}}", "lists.list_name": "Nome da lista", "lists.new_list_name": "Novo nome da lista", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index bbef04b893..d70006669a 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -518,7 +518,6 @@ "lists.exclusive": "הסתרת החברים בפיד הבית", "lists.exclusive_hint": "אם שם כלשהו ברשימה זו, נסתיר אותי בפיד הבית כדי למנוע כפילות.", "lists.find_users_to_add": "חיפוש משתמשים להוספה", - "lists.list_members": "פירוט חברי הרשימה", "lists.list_members_count": "{count, plural, one {חבר רשימה אחד} other {# חברי רשימה}}", "lists.list_name": "שם הרשימה", "lists.new_list_name": "שם רשימה חדשה", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 5bd0c90c10..0ff052d750 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -518,7 +518,6 @@ "lists.exclusive": "Tagok elrejtése a kezdőlapon", "lists.exclusive_hint": "Ha valaki szerepel ezen a listán, el lesz rejtve a kezdőlapod hírfolyamán, hogy ne lásd kétszer a bejegyzéseit.", "lists.find_users_to_add": "Hozzáadandó felhasználók keresése", - "lists.list_members": "Tagok listázása", "lists.list_members_count": "{count, plural, one {# tag} other {# tag}}", "lists.list_name": "Lista neve", "lists.new_list_name": "Új lista neve", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index 1a3d36d83f..7692cf92d6 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -509,7 +509,6 @@ "lists.exclusive": "Celar memberos in Initio", "lists.exclusive_hint": "Si alcuno es sur iste lista, celar iste persona in tu fluxo de initio pro evitar de vider su messages duo vices.", "lists.find_users_to_add": "Trovar usatores a adder", - "lists.list_members": "Membros del lista", "lists.list_members_count": "{count, plural, one {# membro} other {# membros}}", "lists.list_name": "Nomine del lista", "lists.new_list_name": "Nove nomine de lista", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 84c5d7a6b0..01846de792 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -490,7 +490,6 @@ "lists.exclusive": "Celar membri sur Hemo", "lists.exclusive_hint": "Se ulu es en ca listo, celez lu sur vua hemfluo por evitar vidar lua afishi denove.", "lists.find_users_to_add": "Serchi uzanti por adjuntar", - "lists.list_members": "Listigar membri", "lists.list_members_count": "{count, plural,one {# membro} other {#membri}}", "lists.list_name": "Listonomo", "lists.new_list_name": "Nova listonomo", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 707bdbd2bb..e9655f6af5 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -518,7 +518,6 @@ "lists.exclusive": "Fela meðlimi í heimastreyminu", "lists.exclusive_hint": "Ef einhver er á þessum lista, geturðu falið viðkomandi í heimastreyminu þínu til að komast hjá því að sjá færslurnar þeirra í tvígang.", "lists.find_users_to_add": "Finndu notendur til að bæta við", - "lists.list_members": "Meðlimir lista", "lists.list_members_count": "{count, plural, one {# meðlimur} other {# meðlimir}}", "lists.list_name": "Heiti lista", "lists.new_list_name": "Heiti á nýjum lista", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index f0cf02f076..838d662f6b 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -515,7 +515,6 @@ "lists.exclusive": "Nascondi i membri in Home", "lists.exclusive_hint": "Se qualcuno è presente in questa lista, nascondilo nel tuo feed Home per evitare di vedere i suoi post due volte.", "lists.find_users_to_add": "Trova utenti da aggiungere", - "lists.list_members": "Membri della lista", "lists.list_members_count": "{count, plural, one {# membro} other {# membri}}", "lists.list_name": "Nome della lista", "lists.new_list_name": "Nuovo nome della lista", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 9e11806e64..b7886bbff8 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -510,7 +510,6 @@ "lists.exclusive": "メンバーをホームに表示しない", "lists.exclusive_hint": "リストにあるユーザーをホームタイムラインに表示しません。二重に表示させたくない場合に使用できます。", "lists.find_users_to_add": "追加するユーザーを探しましょう", - "lists.list_members": "リストのメンバー", "lists.list_members_count": "{count, plural, other{#人のメンバー}}", "lists.list_name": "リスト名", "lists.new_list_name": "新しいリスト名", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 1d43c0e1fd..ece77c01d4 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -513,7 +513,6 @@ "lists.exclusive": "구성원을 홈에서 숨기기", "lists.exclusive_hint": "누군가가 이 리스트에 있으면 홈 피드에서는 숨겨 게시물을 두 번 보는 것을 방지합니다.", "lists.find_users_to_add": "추가할 사용자 검색", - "lists.list_members": "리스트 구성원", "lists.list_members_count": "{count, plural, other {# 명}}", "lists.list_name": "리스트 이름", "lists.new_list_name": "새 리스트 이름", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 387fd99be6..0e9f96df9b 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -504,7 +504,6 @@ "lists.exclusive": "Slėpti narius pagrindiniame", "lists.exclusive_hint": "Jei kas nors yra šiame sąraše, paslėpkite juos pagrindinio srauto laiko skalėje, kad nematytumėte jų įrašus dukart.", "lists.find_users_to_add": "Raskite naudotojų, kurių pridėti", - "lists.list_members": "Sąrašo nariai", "lists.list_members_count": "{count, plural, one {# narys} few {# nariai} many {# nario} other {# narių}}", "lists.list_name": "Sąrašo pavadinimas", "lists.new_list_name": "Naujas sąrašo pavadinimas", diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index fa3563f1ba..46d4328768 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -515,7 +515,6 @@ "lists.exclusive": "佇tshù ê時間線kā成員tshàng起來。", "lists.exclusive_hint": "Nā bóo-mi̍h口座佇tsit ê列單,ē tuì lí tshù ê時間線kā tsit ê口座tshàng起來,避免koh看見in ê PO文。", "lists.find_users_to_add": "Tshuē beh加添ê用者", - "lists.list_members": "列單ê成員", "lists.list_members_count": "{count, plural, other {# 位成員}}", "lists.list_name": "列單ê名", "lists.new_list_name": "新ê列單ê名", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index e3c88bf7de..3dac5fd7dd 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -518,7 +518,6 @@ "lists.exclusive": "Leden op je Startpagina verbergen", "lists.exclusive_hint": "Als iemand op deze lijst staat, verberg deze persoon dan op je starttijdlijn om te voorkomen dat zijn berichten twee keer worden getoond.", "lists.find_users_to_add": "Vind gebruikers om toe te voegen", - "lists.list_members": "Lijstleden", "lists.list_members_count": "{count, plural, one{# lid} other{# leden}}", "lists.list_name": "Lijstnaam", "lists.new_list_name": "Nieuwe lijstnaam", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index ab37b0fe27..cd08fb6f25 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -515,7 +515,6 @@ "lists.exclusive": "Gøym medlemer frå startskjermen", "lists.exclusive_hint": "Viss nokon er på denne lista, blir dei gøymde frå startskjermen slik at du slepp sjå innlegga deira to gonger.", "lists.find_users_to_add": "Finn brukarar å leggja til", - "lists.list_members": "Syn medlemer", "lists.list_members_count": "{count, plural, one {# medlem} other {# medlemer}}", "lists.list_name": "Namn på lista", "lists.new_list_name": "Namn på den nye lista", diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json index 23a38806b2..8617b48f71 100644 --- a/app/javascript/mastodon/locales/pa.json +++ b/app/javascript/mastodon/locales/pa.json @@ -319,7 +319,6 @@ "lists.done": "ਮੁਕੰਮਲ", "lists.edit": "ਸੂਚੀ ਨੂੰ ਸੋਧੋ", "lists.find_users_to_add": "ਜੋੜਨ ਲਈ ਵਰਤੋਂਕਾਰ ਲੱਭੋ", - "lists.list_members": "ਮੈਂਬਰਾਂ ਦੀ ਸੂਚੀ", "lists.list_members_count": "{count, plural, one {# ਮੈਂਬਰ} other {# ਮੈਂਬਰ}}", "lists.list_name": "ਸੂਚੀ ਦਾ ਨਾਂ", "lists.new_list_name": "ਨਵੀਂ ਸੂਚੀਂ ਦਾ ਨਾਂ", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 3422099390..6320ca1b9c 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -490,7 +490,6 @@ "lists.exclusive": "Nie pokazuj w mojej osi czasu", "lists.exclusive_hint": "Wpisy osób znajdujących się na tej liście nie będą wyświetlane w twojej osi czasu, aby uniknąć duplikowania tych samych wpisów.", "lists.find_users_to_add": "Znajdź kogoś, aby dodać", - "lists.list_members": "Lista osób", "lists.list_members_count": "{count, plural, one {# osoba} few {# osoby} many {# osób} other {# osób}}", "lists.list_name": "Nazwa listy", "lists.new_list_name": "Nazwa nowej listy", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index a9622fb76a..d5745c2d56 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -491,7 +491,6 @@ "lists.exclusive": "Ocultar membros no início", "lists.exclusive_hint": "Se existe alguém nesta lista, oculte-os no seu feed inicial para evitar ver suas publicações duas vezes.", "lists.find_users_to_add": "Encontrar usuários para adicionar", - "lists.list_members": "Membros da lista", "lists.list_members_count": "{count, plural, one {# membro} other {# membros}}", "lists.list_name": "Nome da lista", "lists.new_list_name": "Nome novo da lista", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 45c72d2fc1..5facd93d64 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -518,7 +518,6 @@ "lists.exclusive": "Ocultar membros na página inicial", "lists.exclusive_hint": "Se alguém estiver nesta lista, oculta-o na cronologia da tua página inicial para evitar veres as publicações dessa pessoa duas vezes.", "lists.find_users_to_add": "Encontrar utilizadores para adicionar", - "lists.list_members": "Membros da lista", "lists.list_members_count": "{count, plural, one {# membro} other {# membros}}", "lists.list_name": "Nome da lista", "lists.new_list_name": "Nome da nova lista", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 78426e596c..a82eab47d5 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -515,7 +515,6 @@ "lists.exclusive": "Не показывать участников в домашней ленте", "lists.exclusive_hint": "Если кто-то есть в этом списке, скрыть его в домашней ленте, чтобы не видеть его посты дважды.", "lists.find_users_to_add": "Найти пользователей для добавления", - "lists.list_members": "Пользователи в списке", "lists.list_members_count": "{count, plural, one {# пользователь} few {# пользователя} other {# пользователей}}", "lists.list_name": "Название списка", "lists.new_list_name": "Новый список", @@ -797,18 +796,18 @@ "search.no_recent_searches": "Недавние запросы отсутствуют", "search.placeholder": "Поиск", "search.quick_action.account_search": "Профили, соответствующие {x}", - "search.quick_action.go_to_account": "Перейти в профиль {x}", - "search.quick_action.go_to_hashtag": "Перейти к хэштегу {x}", + "search.quick_action.go_to_account": "Перейти к профилю {x}", + "search.quick_action.go_to_hashtag": "Перейти к хештегу {x}", "search.quick_action.open_url": "Открыть URL в Mastodon", "search.quick_action.status_search": "Посты, соответствующие {x}", "search.search_or_paste": "Поиск (или вставьте URL)", - "search_popout.full_text_search_disabled_message": "Недоступно на {domain}.", - "search_popout.full_text_search_logged_out_message": "Доступно только при авторизации.", - "search_popout.language_code": "Код языка по стандарту ISO", + "search_popout.full_text_search_disabled_message": "Недоступно на сервере {domain}.", + "search_popout.full_text_search_logged_out_message": "Доступно только авторизованным пользователям.", + "search_popout.language_code": "ISO-код языка", "search_popout.options": "Параметры поиска", "search_popout.quick_actions": "Быстрые действия", "search_popout.recent": "Недавние запросы", - "search_popout.specific_date": "конкретная дата", + "search_popout.specific_date": "дата", "search_popout.user": "пользователь", "search_results.accounts": "Профили", "search_results.all": "Все", @@ -818,7 +817,7 @@ "search_results.see_all": "Показать все", "search_results.statuses": "Посты", "search_results.title": "Поиск \"{q}\"", - "server_banner.about_active_users": "Люди, заходившие на этот сервер за последние 30 дней (ежемесячные активные пользователи)", + "server_banner.about_active_users": "Число зарегистрированных пользователей, заходивших на этот сервер за последние 30 дней (MAU-метрика)", "server_banner.active_users": "активные пользователи", "server_banner.administered_by": "Управляется:", "server_banner.is_one_of_many": "{domain} — это один из многих независимых серверов Mastodon, которые вы можете использовать, чтобы присоединиться к сети Fediverse.", @@ -827,7 +826,7 @@ "sign_in_banner.follow_anyone": "Подписывайтесь на кого угодно в федивёрсе и читайте ленту в хронологическом порядке. Никаких алгоритмов, рекламы или кликбейта.", "sign_in_banner.mastodon_is": "Mastodon — лучший способ быть в курсе всего происходящего.", "sign_in_banner.sign_in": "Войти", - "sign_in_banner.sso_redirect": "Войдите или Зарегистрируйтесь", + "sign_in_banner.sso_redirect": "Вход/Регистрация", "status.admin_account": "Открыть интерфейс модератора для @{name}", "status.admin_domain": "Открыть интерфейс модератора для {domain}", "status.admin_status": "Открыть этот пост в интерфейсе модератора", @@ -846,45 +845,45 @@ "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}", "status.embed": "Встроить на свой сайт", "status.favourite": "Добавить в избранное", - "status.favourites": "{count, plural, other {в избранном}}", + "status.favourites": "{count, plural, one {звёздочка} few {звёздочки} other {звёздочек}}", "status.filter": "Фильтровать этот пост", "status.history.created": "{name} создал(а) {date}", "status.history.edited": "{name} отредактировал(а) {date}", - "status.load_more": "Загрузить остальное", + "status.load_more": "Загрузить ещё", "status.media.open": "Нажмите, чтобы открыть.", - "status.media.show": "Нажмите для просмотра", - "status.media_hidden": "Файл скрыт", + "status.media.show": "Нажмите, чтобы показать", + "status.media_hidden": "Вложения скрыты", "status.mention": "Упомянуть @{name}", "status.more": "Ещё", "status.mute": "Игнорировать @{name}", "status.mute_conversation": "Игнорировать обсуждение", "status.open": "Открыть пост", - "status.pin": "Рекомендовать в профиле", - "status.read_more": "Ещё", + "status.pin": "Закрепить в профиле", + "status.read_more": "Читать далее", "status.reblog": "Продвинуть", "status.reblog_private": "Продвинуть для своей аудитории", "status.reblogged_by": "{name} продвинул(а)", "status.reblogs": "{count, plural, one {продвижение} few {продвижения} other {продвижений}}", - "status.reblogs.empty": "Никто ещё не продвинул этот пост. Как только кто-то это сделает, они появятся здесь.", - "status.redraft": "Создать заново", + "status.reblogs.empty": "Никто ещё не продвинул этот пост. Все пользователи, продвинувшие этот пост, будут показаны здесь.", + "status.redraft": "Удалить и исправить", "status.remove_bookmark": "Убрать из закладок", "status.remove_favourite": "Убрать из избранного", - "status.replied_in_thread": "Ответил(а) в треде", + "status.replied_in_thread": "Ответил(а) в обсуждении", "status.replied_to": "Ответил(а) {name}", "status.reply": "Ответить", - "status.replyAll": "Ответить всем", + "status.replyAll": "Ответить в обсуждении", "status.report": "Пожаловаться на @{name}", - "status.sensitive_warning": "Содержимое «деликатного характера»", + "status.sensitive_warning": "Медиа деликатного содержания", "status.share": "Поделиться", "status.show_less_all": "Свернуть все спойлеры в ветке", "status.show_more_all": "Развернуть все спойлеры в ветке", "status.show_original": "Показать оригинал", - "status.title.with_attachments": "{user} опубликовал {attachmentCount, plural, one {{attachmentCount} вложение} few {{attachmentCount} вложения} other {{attachmentCount} вложений}}", + "status.title.with_attachments": "{user} опубликовал(а) {attachmentCount, plural, one {{attachmentCount} вложение} few {{attachmentCount} вложения} other {{attachmentCount} вложений}}", "status.translate": "Перевод", "status.translated_from_with": "Переведено с {lang} с помощью {provider}", "status.uncached_media_warning": "Предварительный просмотр недоступен", "status.unmute_conversation": "Не игнорировать обсуждение", - "status.unpin": "Не рекомендовать в профиле", + "status.unpin": "Открепить от профиля", "subscribed_languages.lead": "Посты лишь на выбранных языках будут появляться в вашей домашней ленте и в списках после изменения. Снимите выбор, чтобы получать посты на всех языках.", "subscribed_languages.save": "Сохранить изменения", "subscribed_languages.target": "Изменить языки подписки для {target}", @@ -906,7 +905,7 @@ "units.short.thousand": "{count} тыс.", "upload_area.title": "Перетащите сюда, чтобы загрузить", "upload_button.label": "Прикрепить фото, видео или аудио", - "upload_error.limit": "Достигнут лимит загруженных файлов.", + "upload_error.limit": "Превышено максимальное количество вложений.", "upload_error.poll": "К опросам нельзя прикреплять файлы.", "upload_form.drag_and_drop.instructions": "Чтобы выбрать вложение, нажмите \"Пробел\" (Space) или \"Ввод\" (Enter). Используйте клавиши со стрелками, чтобы передвинуть вложение в любом направлении. Нажмите \"Пробел\" (Space) или \"Ввод\" (Enter) ещё раз, чтобы переместить вложение на новое место, либо нажмите кнопку \"Выйти\" (Escape), чтобы отменить перемещение.", "upload_form.drag_and_drop.on_drag_cancel": "Перемещение отменено. Вложение {item} было оставлено на прежнем месте.", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 846b51667d..83fd2bb995 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -461,7 +461,6 @@ "lists.exclusive": "Skryť členov na Domovskej osi", "lists.exclusive_hint": "Ak je niekto na tomto zozname, skry ich na tvojej Domácej osi, aby si ich príspevky nevidel/a dvakrát.", "lists.find_users_to_add": "Nájdi užívateľov na pridanie", - "lists.list_members": "Členovia zoznamu", "lists.list_name": "Názov zoznamu", "lists.new_list_name": "Názov nového zoznamu", "lists.no_lists_yet": "Ešte žiadne zoznamy.", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 08a6d8c028..eec21e7a8f 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -490,7 +490,6 @@ "lists.exclusive": "Skrij člane v domovanju", "lists.exclusive_hint": "Objave vseh, ki so na tem seznamu, se ne pokažejo v vašem domačem viru. Tako se izognete podvojenim objavam.", "lists.find_users_to_add": "Poišči člane za dodajanje", - "lists.list_members": "Člani seznama", "lists.list_members_count": "{count, plural, one {# član} two {# člana} few {# člani} other {# članov}}", "lists.list_name": "Ime seznama", "lists.new_list_name": "Novo ime seznama", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index a8c4001239..77d86db377 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -513,7 +513,6 @@ "lists.exclusive": "Fshihni anëtarët në Krye", "lists.exclusive_hint": "Nëse dikush gjendje në këtë listë, fshihini ata te prurja juaj e Kreut, që të shmangni parjen dy herë të postimeve të tyre.", "lists.find_users_to_add": "Gjeni përdorues për t’i shtuar", - "lists.list_members": "Shfaq anëtarë", "lists.list_members_count": "{count, plural, one {# anëtar} other {# anëtarë}}", "lists.list_name": "Emër liste", "lists.new_list_name": "Emër liste të re", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 6a64df26a1..a8f57205b4 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -491,7 +491,6 @@ "lists.exclusive": "Dölj medlemmar i Hem flödet", "lists.exclusive_hint": "Om någon är med på den här listan, göm dem i ditt Hemtidlinje för att undvika att se deras inlägg två gånger.", "lists.find_users_to_add": "Hitta användare att lägga till", - "lists.list_members": "Lista medlemmar", "lists.list_members_count": "{count, plural, one {# medlem} other {# medlemmar}}", "lists.list_name": "Listnamn", "lists.new_list_name": "Nytt listnamn", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 26dad1a595..66ebdc764e 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -23,6 +23,7 @@ "account.copy": "คัดลอกลิงก์ไปยังโปรไฟล์", "account.direct": "กล่าวถึง @{name} แบบส่วนตัว", "account.disable_notifications": "หยุดแจ้งเตือนฉันเมื่อ @{name} โพสต์", + "account.domain_blocking": "โดเมน", "account.edit_profile": "แก้ไขโปรไฟล์", "account.enable_notifications": "แจ้งเตือนฉันเมื่อ @{name} โพสต์", "account.endorse": "แสดงในโปรไฟล์", @@ -483,7 +484,6 @@ "lists.exclusive": "ซ่อนสมาชิกในหน้าแรก", "lists.exclusive_hint": "หากใครสักคนอยู่ในรายการนี้ ให้ซ่อนเขาในฟีดหน้าแรกของคุณเพื่อหลีกเลี่ยงการเห็นโพสต์ของเขาสองครั้ง", "lists.find_users_to_add": "ค้นหาผู้ใช้ที่จะเพิ่ม", - "lists.list_members": "สมาชิกของรายการ", "lists.list_members_count": "{count, plural, other {# สมาชิก}}", "lists.list_name": "ชื่อรายการ", "lists.new_list_name": "ชื่อรายการใหม่", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 75b05892f0..f46f070817 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -518,7 +518,6 @@ "lists.exclusive": "Anasayfada üyeleri gizle", "lists.exclusive_hint": "Birisi bu listede yer alıyorsa, gönderilerini iki kez görmekten kaçınmak için onu anasayfa akışınızda gizleyin.", "lists.find_users_to_add": "Eklenecek kullanıcıları bul", - "lists.list_members": "Liste üyeleri", "lists.list_members_count": "{count, plural, one {# üye} other {# üye}}", "lists.list_name": "Liste adı", "lists.new_list_name": "Yeni liste adı", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index ff3ff3dab8..f006b4015a 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -500,7 +500,6 @@ "lists.exclusive": "Сховати учасників на головній сторінці", "lists.exclusive_hint": "Якщо хтось є у цьому списку, сховайте їх на своїй домашній сторінці, щоб не бачити їхні дописи двічі.", "lists.find_users_to_add": "Знайти користувачів, щоб додати їх", - "lists.list_members": "Учасники списку", "lists.list_members_count": "{count, plural, one {# member} other {# members}}", "lists.list_name": "Назва списку", "lists.new_list_name": "Нова назва списку", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index b1a014f83c..7f7dcae7de 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -518,7 +518,6 @@ "lists.exclusive": "Ẩn thành viên trong Trang chủ", "lists.exclusive_hint": "Nếu ai đó có trong danh sách này, ẩn họ trong Trang chủ để tránh thấy tút của họ hiện trùng lặp.", "lists.find_users_to_add": "Tìm người để thêm vào", - "lists.list_members": "Liệt kê các thành viên", "lists.list_members_count": "{count, plural, other {# thành viên}}", "lists.list_name": "Tên danh sách", "lists.new_list_name": "Tên danh sách mới", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 6644c43bcb..70dcfdaa2f 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -507,7 +507,6 @@ "lists.exclusive": "在主页动态中隐藏列表成员", "lists.exclusive_hint": "列表成员的嘟文将不会在你的主页动态中显示,以免重复阅读。", "lists.find_users_to_add": "查找要添加的用户", - "lists.list_members": "列表成员", "lists.list_members_count": "{count, plural, other {# 人}}", "lists.list_name": "列表名称", "lists.new_list_name": "新列表名称", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 226c68cd5c..b47ba36975 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -518,7 +518,6 @@ "lists.exclusive": "於首頁隱藏成員", "lists.exclusive_hint": "如果某個帳號於此列表中,將自您的首頁時間軸中隱藏此帳號,以防重複見到他們的嘟文。", "lists.find_users_to_add": "尋找欲新增之使用者", - "lists.list_members": "列表成員", "lists.list_members_count": "{count, plural, other {# 個成員}}", "lists.list_name": "列表名稱", "lists.new_list_name": "新列表名稱", diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 652505f3ad..f02ac44ea6 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1836,6 +1836,10 @@ ca: limit: Ja has fixat el màxim nombre de tuts ownership: No es pot fixar el tut d'algú altre reblog: No es pot fixar un impuls + quote_policies: + followers: Seguidors i usuaris mencionats + nobody: Només usuaris mencionats + public: Tothom title: '%{name}: "%{quote}"' visibilities: direct: Directe diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 056570f7b5..5d97bf5246 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ų, jokių įmonių sekimo, etiškas dizainas ir decentralizacija! Turėk 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}" diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 71cb1faafa..06f5440ee9 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -56,6 +56,7 @@ bg: scopes: Указва до кои API има достъп приложението. Ако изберете диапазон от най-високо ниво, няма нужда да избирате индивидуални. setting_aggregate_reblogs: Без показване на нови подсилвания за публикации, които са неотдавна подсилени (засяга само новополучени подсилвания) setting_always_send_emails: Обикновено известията по имейл няма да са изпратени при дейна употреба на Mastodon + setting_default_quote_policy: Споменатите потребители винаги им е позволено да цитират. Тази настройка ще се отрази за публикациите, създадени със следващата версия на Mastodon, но може да изберете предпочитанията си в подготовката setting_default_sensitive: Деликатната мултимедия е скрита по подразбиране и може да се разкрие с едно щракване setting_display_media_default: Скриване на мултимедия отбелязана като деликатна setting_display_media_hide_all: Винаги скриване на мултимедията @@ -147,6 +148,9 @@ bg: min_age: Не трябва да е под изискваната минимална възраст от закона на юрисдикцията ви. user: chosen_languages: Само публикации на отметнатите езици ще се показват в публичните часови оси + date_of_birth: + one: Трябва да се уверим, че сте поне на %{count}, за да употребявате Mastodon. Няма да съхраняваме това. + other: Трябва да се уверим, че сте поне на %{count}, за да употребявате Mastodon. Няма да съхраняваме това. role: Ролята управлява какви позволения има потребителят. user_role: color: Цветът, използван за ролите в потребителския интерфейс, като RGB в шестнадесетичен формат @@ -227,6 +231,7 @@ bg: setting_boost_modal: Показване на прозорец за потвърждение преди подсилване setting_default_language: Език на публикуване setting_default_privacy: Поверителност на публикуване + setting_default_quote_policy: Кой може да цитира setting_default_sensitive: Все да се бележи мултимедията като деликатна setting_delete_modal: Показване на прозорче за потвърждение преди изтриване на публикация setting_disable_hover_cards: Изключване на прегледа на профила, премествайки показалеца отгоре diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 2cff4bffcd..19d5c537a3 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -56,6 +56,7 @@ ca: scopes: API permeses per a accedir a l'aplicació. Si selecciones un àmbit de nivell superior, no cal que en seleccionis un d'individual. setting_aggregate_reblogs: No mostra els nous impulsos dels tuts que ja s'han impulsat recentment (només afecta als impulsos nous rebuts) setting_always_send_emails: Normalment, no s'enviarà cap notificació per correu electrònic mentre facis servir Mastodon + setting_default_quote_policy: Els usuaris mencionats sempre poden citar. Aquesta configuració només tindrà efecte en les publicacions creades amb la següent versió de Mastodon, però podeu seleccionar-ho en preparació setting_default_sensitive: El contingut sensible està ocult per defecte i es pot mostrar fent-hi clic setting_display_media_default: Amaga el contingut gràfic marcat com a sensible setting_display_media_hide_all: Oculta sempre tot el contingut multimèdia @@ -145,6 +146,9 @@ ca: min_age: No hauria de ser inferior a l'edat mínima exigida per la llei de la vostra jurisdicció. user: chosen_languages: Quan estigui marcat, només es mostraran els tuts de les llengües seleccionades en les línies de temps públiques + date_of_birth: + one: Ens hem d'assegurar que teniu com a mínim %{count} any per a fer servir Mastodon. No ho desarem. + other: Ens hem d'assegurar que teniu com a mínim %{count} anys per a fer servir Mastodon. No ho desarem. role: El rol controla quins permisos té l'usuari. user_role: color: Color que s'usarà per al rol a tota la interfície d'usuari, com a RGB en format hexadecimal @@ -225,6 +229,7 @@ ca: setting_boost_modal: Mostra la finestra de confirmació abans d'impulsar setting_default_language: Llengua dels tuts setting_default_privacy: Privacitat dels tuts + setting_default_quote_policy: Qui pot citar setting_default_sensitive: Marcar sempre el contingut gràfic com a sensible setting_delete_modal: Mostra la finestra de confirmació abans d'esborrar un tut setting_disable_hover_cards: Deshabilita la vista prèvia del perfil en passar-hi per sobre diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index c8de52b2e4..9643f92b1d 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -56,6 +56,7 @@ gl: scopes: A que APIs terá acceso a aplicación. Se escolles un ámbito de alto nivel, non precisas seleccionar elementos individuais. setting_aggregate_reblogs: Non mostrar novas promocións de publicacións que foron promovidas recentemente (só afecta a promocións recén recibidas) setting_always_send_emails: Como norma xeral non che enviamos correos electrónicos se usas activamente Mastodon + setting_default_quote_policy: As usuarias mencionadas sempre teñen permiso para citar. Este axuste só ten efecto para publicacións creadas coa próxima versión de Mastodon, pero xa podes ir preparando o axuste. setting_default_sensitive: Medios sensibles marcados como ocultos por defecto e móstranse cun click setting_display_media_default: Ocultar medios marcados como sensibles setting_display_media_hide_all: Ocultar sempre os medios @@ -148,6 +149,9 @@ gl: min_age: Non debería ser inferior á idade mínima requerida polas leis da túa xurisdición. user: chosen_languages: Se ten marca, só as publicacións nos idiomas seleccionados serán mostrados en cronoloxías públicas + date_of_birth: + one: Temos que confirmar que tes %{count} anos polo menos para usar Mastodon. Non gardamos este dato. + other: Temos que confirmar que tes %{count} anos polo menos para usar Mastodon. Non gardamos este dato. role: Os roles establecen os permisos que ten a usuaria. user_role: color: Cor que se usará para o rol a través da IU, como RGB en formato hex @@ -228,6 +232,7 @@ gl: setting_boost_modal: Solicitar confirmación antes de promover setting_default_language: Idioma de publicación setting_default_privacy: Privacidade da publicación + setting_default_quote_policy: Quen pode citar setting_default_sensitive: Marcar sempre multimedia como sensible setting_delete_modal: Solicitar confirmación antes de eliminar unha publicación setting_disable_hover_cards: Desactivar vista previa do perfil ao poñerse enriba diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 9a85820fb0..e1dd0a555a 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -56,6 +56,7 @@ tr: scopes: Uygulamanın erişmesine izin verilen API'ler. Üst seviye bir kapsam seçtiyseniz, bireysel kapsam seçmenize gerek yoktur. setting_aggregate_reblogs: Yakın zamanda teşvik edilmiş gönderiler için yeni teşvikleri göstermeyin (yalnızca yeni alınan teşvikleri etkiler) setting_always_send_emails: Normalde, Mastodon'u aktif olarak kullanırken e-posta bildirimleri gönderilmeyecektir + setting_default_quote_policy: Bahsedilen kullanıcıların her zaman alıntı yapmasına izin verilir. Bu ayar yalnızca bir sonraki Mastodon sürümü ile oluşturulan gönderiler için geçerli olacaktır, ancak tercihinizi hazırlık aşamasında seçebilirsiniz setting_default_sensitive: Hassas medya varsayılan olarak gizlidir ve bir tıklama ile gösterilebilir setting_display_media_default: Hassas olarak işaretlenmiş medyayı gizle setting_display_media_hide_all: Medyayı her zaman gizle @@ -228,6 +229,7 @@ tr: setting_boost_modal: Paylaşmadan önce onay iletişim kutusu göster setting_default_language: Gönderi dili setting_default_privacy: Gönderi gizliliği + setting_default_quote_policy: Kimler alıntılayabilir setting_default_sensitive: Medyayı her zaman hassas olarak işaretle setting_delete_modal: Bir gönderiyi silmeden önce onay iletişim kutusu göster setting_disable_hover_cards: Üstüne geldiğinde profil önizlemesini devre dışı bırak diff --git a/config/locales/tr.yml b/config/locales/tr.yml index ed16e490e0..9afd2dcdaf 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1858,6 +1858,10 @@ tr: limit: Halihazırda maksimum sayıda gönderi sabitlediniz ownership: Başkasının gönderisi sabitlenemez reblog: Bir gönderi sabitlenemez + quote_policies: + followers: Takipçiler ve bahsedilen kullanıcılar + nobody: Sadece bahsedilen kullanıcılar + public: Herkes title: '%{name}: "%{quote}"' visibilities: direct: Doğrudan From ae281f31db7b383fa4a7b82a704566e0968f43fd Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 15 May 2025 10:51:07 +0200 Subject: [PATCH 018/106] Add `dev:populate_sample_data` rake task to populate test data (#34676) --- docs/DEVELOPMENT.md | 1 + lib/tasks/dev.rake | 354 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 lib/tasks/dev.rake diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 6dd439ddc7..a20a5db0b0 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -90,6 +90,7 @@ development environment configured with the software needed for this project. - Once you have successfully set up a development environment, it will be available on http://localhost:3000 - Log in as the default admin user with the username `admin@mastodon.local` and the password `mastodonadmin`. - Check out the [Mastodon docs] for tips on working with emails in development (you'll need this when creating new user accounts) as well as a list of useful commands for testing and updating your dev instance. +- You can optionally populate your database with sample data by running `bin/rails dev:populate_sample_data`. This will create a `@showcase_account` account with various types of contents. [codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json [CONTRIBUTING]: ../CONTRIBUTING.md diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake new file mode 100644 index 0000000000..fbb971ba87 --- /dev/null +++ b/lib/tasks/dev.rake @@ -0,0 +1,354 @@ +# frozen_string_literal: true + +namespace :dev do + desc 'Populate database with test data. Can be run multiple times. Should not be run in production environments' + task populate_sample_data: :environment do + # Create a valid account to showcase multiple post types + showcase_account = Account.create_with(username: 'showcase_account').find_or_create_by!(id: 10_000_000) + showcase_user = User.create_with( + account_id: showcase_account.id, + agreement: true, + password: SecureRandom.hex, + email: ENV.fetch('TEST_DATA_SHOWCASE_EMAIL', 'showcase_account@joinmastodon.org'), + confirmed_at: Time.now.utc, + approved: true + ).find_or_create_by!(id: 10_000_000) + showcase_user.mark_email_as_confirmed! + showcase_user.approve! + + french_post = Status.create_with( + text: 'Ceci est un sondage public écrit en Français', + language: 'fr', + account: showcase_account, + visibility: :public, + poll_attributes: { + voters_count: 0, + account: showcase_account, + expires_at: 1.day.from_now, + options: ['ceci est un choix', 'ceci est un autre choix'], + multiple: false, + } + ).find_or_create_by!(id: 10_000_000) + + private_mentionless = Status.create_with( + text: 'This is a private message written in English', + language: 'en', + account: showcase_account, + visibility: :private + ).find_or_create_by!(id: 10_000_001) + + public_self_reply_with_cw = Status.create_with( + text: 'This is a public self-reply written in English; it has a CW and a multi-choice poll', + spoiler_text: 'poll (CW example)', + language: 'en', + account: showcase_account, + visibility: :public, + thread: french_post, + poll_attributes: { + voters_count: 0, + account: showcase_account, + expires_at: 1.day.from_now, + options: ['this is a choice', 'this is another choice', 'you can chose any number of them'], + multiple: true, + } + ).find_or_create_by!(id: 10_000_002) + ProcessHashtagsService.new.call(public_self_reply_with_cw) + + unlisted_self_reply_with_cw_tag_mention = Status.create_with( + text: 'This is an unlisted (Quiet Public) self-reply written in #English; it has a CW, mentions @showcase_account, and uses an emoji 🦣', + spoiler_text: 'CW example', + language: 'en', + account: showcase_account, + visibility: :unlisted, + thread: public_self_reply_with_cw + ).find_or_create_by!(id: 10_000_003) + Mention.find_or_create_by!(status: unlisted_self_reply_with_cw_tag_mention, account: showcase_account) + ProcessHashtagsService.new.call(unlisted_self_reply_with_cw_tag_mention) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/600x400.png'), + description: 'Mastodon logo' + ).find_or_create_by!(id: 10_000_000) + status_with_media = Status.create_with( + text: "This is a public status with a picture and tags. The attached picture has an alt text\n\n#Mastodon #Logo #English #Test", + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_004) + media_attachment.update(status_id: status_with_media.id) + ProcessHashtagsService.new.call(status_with_media) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/600x400.png'), + description: 'Mastodon logo' + ).find_or_create_by!(id: 10_000_001) + status_with_sensitive_media = Status.create_with( + text: "This is the same public status with a picture and tags, but it is marked as sensitive. The attached picture has an alt text\n\n#Mastodon #Logo #English #Test", + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public, + sensitive: true, + thread: status_with_media + ).find_or_create_by!(id: 10_000_005) + media_attachment.update(status_id: status_with_sensitive_media.id) + ProcessHashtagsService.new.call(status_with_sensitive_media) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/600x400.png'), + description: 'Mastodon logo' + ).find_or_create_by!(id: 10_000_002) + status_with_cw_media = Status.create_with( + text: "This is the same public status with a picture and tags, but it is behind a CW. The attached picture has an alt text\n\n#Mastodon #Logo #English #Test", + spoiler_text: 'Mastodon logo', + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public, + sensitive: true, + thread: status_with_sensitive_media + ).find_or_create_by!(id: 10_000_006) + media_attachment.update(status_id: status_with_cw_media.id) + ProcessHashtagsService.new.call(status_with_cw_media) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/boop.ogg'), + description: 'Mastodon boop' + ).find_or_create_by!(id: 10_000_003) + status_with_audio = Status.create_with( + text: "This is the same public status with an audio file and tags. The attached picture has an alt text\n\n#Mastodon #English #Test", + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public, + thread: status_with_cw_media + ).find_or_create_by!(id: 10_000_007) + media_attachment.update(status_id: status_with_audio.id) + ProcessHashtagsService.new.call(status_with_audio) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/boop.ogg'), + description: 'Mastodon boop' + ).find_or_create_by!(id: 10_000_004) + status_with_sensitive_audio = Status.create_with( + text: "This is the same public status with an audio file and tags, but it is marked as sensitive. The attached picture has an alt text\n\n#Mastodon #English #Test", + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public, + sensitive: true, + thread: status_with_audio + ).find_or_create_by!(id: 10_000_008) + media_attachment.update(status_id: status_with_sensitive_audio.id) + ProcessHashtagsService.new.call(status_with_sensitive_audio) + + media_attachment = MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/boop.ogg'), + description: 'Mastodon boop' + ).find_or_create_by!(id: 10_000_005) + status_with_cw_audio = Status.create_with( + text: "This is the same public status with an audio file and tags, but it is behind a CW. The attached picture has an alt text\n\n#Mastodon #English #Test", + spoiler_text: 'Mastodon boop', + ordered_media_attachment_ids: [media_attachment.id], + account: showcase_account, + visibility: :public, + sensitive: true, + thread: status_with_sensitive_audio + ).find_or_create_by!(id: 10_000_009) + media_attachment.update(status_id: status_with_cw_audio.id) + ProcessHashtagsService.new.call(status_with_cw_audio) + + media_attachments = [ + MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/600x400.png'), + description: 'Mastodon logo' + ).find_or_create_by!(id: 10_000_006), + MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/attachment.jpg') + ).find_or_create_by!(id: 10_000_007), + MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/avatar-high.gif'), + description: 'Walking cartoon cat' + ).find_or_create_by!(id: 10_000_008), + MediaAttachment.create_with( + account: showcase_account, + file: File.open('spec/fixtures/files/text.png'), + description: 'Text saying “Hello Mastodon”' + ).find_or_create_by!(id: 10_000_009), + ] + status_with_multiple_attachments = Status.create_with( + text: "This is a post with multiple attachments, not all of which have a description\n\n#Mastodon #English #Test", + spoiler_text: 'multiple attachments', + ordered_media_attachment_ids: media_attachments.pluck(:id), + account: showcase_account, + visibility: :public, + sensitive: true, + thread: status_with_cw_audio + ).find_or_create_by!(id: 10_000_010) + media_attachments.each { |attachment| attachment.update!(status_id: status_with_multiple_attachments.id) } + ProcessHashtagsService.new.call(status_with_multiple_attachments) + + remote_account = Account.create_with( + username: 'fake.example', + domain: 'example.org', + uri: 'https://example.org/foo/bar', + url: 'https://example.org/foo/bar', + locked: true + ).find_or_create_by!(id: 10_000_001) + + remote_formatted_post = Status.create_with( + text: <<~HTML, +

This is a post with a variety of HTML in it

+

For instance, this text is bold and this one as well, while this text is stricken through and this one as well.

+
+

This thing, here, is a block quote
with some bold as well

+
    +
  • a list item
  • +
  • + and another with +
      +
    • nested
    • +
    • items!
    • +
    +
  • +
+
+
// And this is some code
+        // with two lines of comments
+        
+

And this is inline code

+

Finally, please observe this Ruby element: 明日 (Ashita)

+ HTML + account: remote_account, + uri: 'https://example.org/foo/bar/baz', + url: 'https://example.org/foo/bar/baz' + ).find_or_create_by!(id: 10_000_011) + Status.create_with(account: showcase_account, reblog: remote_formatted_post).find_or_create_by!(id: 10_000_012) + + unattached_quote_post = Status.create_with( + text: 'This is a quote of a post that does not exist', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_013) + Quote.create_with( + status: unattached_quote_post, + quoted_status: nil + ).find_or_create_by!(id: 10_000_000) + + self_quote = Status.create_with( + text: 'This is a quote of a public self-post', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_014) + Quote.create_with( + status: self_quote, + quoted_status: status_with_media, + state: :accepted + ).find_or_create_by!(id: 10_000_001) + + nested_self_quote = Status.create_with( + text: 'This is a quote of a public self-post which itself is a self-quote', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_015) + Quote.create_with( + status: nested_self_quote, + quoted_status: self_quote, + state: :accepted + ).find_or_create_by!(id: 10_000_002) + + recursive_self_quote = Status.create_with( + text: 'This is a recursive self-quote; no real reason for it to exist, but just to make sure we handle them gracefuly', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_016) + Quote.create_with( + status: recursive_self_quote, + quoted_status: recursive_self_quote, + state: :accepted + ).find_or_create_by!(id: 10_000_003) + + self_private_quote = Status.create_with( + text: 'This is a public post of a private self-post: the quoted post should not be visible to non-followers', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_017) + Quote.create_with( + status: self_private_quote, + quoted_status: private_mentionless, + state: :accepted + ).find_or_create_by!(id: 10_000_004) + + uncwed_quote_cwed = Status.create_with( + text: 'This is a quote without CW of a quoted post that has a CW', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_018) + Quote.create_with( + status: uncwed_quote_cwed, + quoted_status: public_self_reply_with_cw, + state: :accepted + ).find_or_create_by!(id: 10_000_005) + + cwed_quote_cwed = Status.create_with( + text: 'This is a quote with a CW of a quoted post that itself has a CW', + spoiler_text: 'Quote post with a CW', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_019) + Quote.create_with( + status: cwed_quote_cwed, + quoted_status: public_self_reply_with_cw, + state: :accepted + ).find_or_create_by!(id: 10_000_006) + + pending_quote_post = Status.create_with( + text: 'This quote post is pending', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_020) + Quote.create_with( + status: pending_quote_post, + quoted_status: remote_formatted_post, + activity_uri: 'https://foo/bar', + state: :pending + ).find_or_create_by!(id: 10_000_007) + + rejected_quote_post = Status.create_with( + text: 'This quote post is rejected', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_021) + Quote.create_with( + status: rejected_quote_post, + quoted_status: remote_formatted_post, + activity_uri: 'https://foo/foo', + state: :rejected + ).find_or_create_by!(id: 10_000_008) + + revoked_quote_post = Status.create_with( + text: 'This quote post is revoked', + account: showcase_account, + visibility: :public + ).find_or_create_by!(id: 10_000_022) + Quote.create_with( + status: revoked_quote_post, + quoted_status: remote_formatted_post, + activity_uri: 'https://foo/baz', + state: :revoked + ).find_or_create_by!(id: 10_000_009) + + StatusPin.create_with(account: showcase_account, status: public_self_reply_with_cw).find_or_create_by!(id: 10_000_000) + StatusPin.create_with(account: showcase_account, status: private_mentionless).find_or_create_by!(id: 10_000_001) + + showcase_account.update!( + display_name: 'Mastodon test/showcase account', + note: 'Test account to showcase many Mastodon features. Most of its posts are public, but some are private!' + ) + end +end From 3f965d83b01c29eadf4e7060a84f10851f6ba5a5 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 15 May 2025 13:33:16 +0200 Subject: [PATCH 019/106] fix: More "Followers you know" polish & bug fixes (#34697) --- .../components/account_header.tsx | 3 ++- .../components/familiar_followers.tsx | 18 +++++++++++------- app/javascript/mastodon/locales/en.json | 2 +- app/javascript/styles/mastodon/components.scss | 10 ++++------ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index d26286e4fe..2eba2282e2 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -912,6 +912,8 @@ export const AccountHeader: React.FC<{
{badges}
)} + {signedIn && } + {!(suspended || hidden) && (
- {signedIn && }
)}
diff --git a/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx index 1beaceabb4..fd6e1d5dfd 100644 --- a/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/familiar_followers.tsx @@ -12,12 +12,16 @@ import { getAccountFamiliarFollowers } from '@/mastodon/selectors/accounts'; import { useAppDispatch, useAppSelector } from '@/mastodon/store'; const AccountLink: React.FC<{ account?: Account }> = ({ account }) => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const name = account?.display_name || `@${account?.acct}`; + if (!account) { + return null; + } + return ( - - {name} - + ); }; @@ -50,7 +54,7 @@ const FamiliarFollowersReadout: React.FC<{ familiarFollowers: Account[] }> = ({ return ( ); @@ -80,7 +84,7 @@ export const FamiliarFollowers: React.FC<{ accountId: string }> = ({ return (
- {familiarFollowers.map((account) => ( + {familiarFollowers.slice(0, 3).map((account) => ( ))} diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 2d49346ac7..ee494c7e45 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -28,7 +28,7 @@ "account.edit_profile": "Edit profile", "account.enable_notifications": "Notify me when @{name} posts", "account.endorse": "Feature on profile", - "account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {# other} other {# others}}", + "account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}", "account.familiar_followers_one": "Followed by {name1}", "account.familiar_followers_two": "Followed by {name1} and {name2}", "account.featured": "Featured", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f3271b2d3b..3245105c94 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2091,6 +2091,7 @@ body > [data-popper-placement] { display: block; position: relative; border-radius: var(--avatar-border-radius); + background: var(--surface-background-color); img { width: 100%; @@ -8151,16 +8152,13 @@ noscript { display: flex; align-items: center; gap: 10px; - margin-block-end: 16px; + margin-block: 16px; color: $darker-text-color; a:any-link { - color: inherit; - text-decoration: underline; - } - - a:hover { + font-weight: 500; text-decoration: none; + color: $primary-text-color; } } } From 6c743831aab13a39f2020827f20933a51bc7cbc2 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 15 May 2025 16:40:01 +0200 Subject: [PATCH 020/106] fix: Hide "Followers you know" widget from your own profile, #34698 (#34699) --- .../features/account_timeline/components/account_header.tsx | 4 +++- app/javascript/styles/mastodon/components.scss | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx index 2eba2282e2..576052ae71 100644 --- a/app/javascript/mastodon/features/account_timeline/components/account_header.tsx +++ b/app/javascript/mastodon/features/account_timeline/components/account_header.tsx @@ -912,7 +912,9 @@ export const AccountHeader: React.FC<{
{badges}
)} - {signedIn && } + {account.id !== me && signedIn && ( + + )} {!(suspended || hidden) && (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 3245105c94..e003ad6046 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2179,7 +2179,7 @@ a .account__avatar { flex-wrap: nowrap; & > :not(:first-child) { - margin-inline-start: -8px; + margin-inline-start: -12px; } & > :first-child { From 24d35996907ffcdf393f9938589eedced84353e4 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 15 May 2025 20:29:43 +0200 Subject: [PATCH 021/106] Fix middle button mouse up on status header always opening status in a new tab (#34700) --- app/javascript/mastodon/components/status.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 21d596a58c..820b24cd6f 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -175,9 +175,8 @@ class Status extends ImmutablePureComponent { } }; - handleMouseUp = e => { + handleHeaderClick = e => { // Only handle clicks on the empty space above the content - if (e.target !== e.currentTarget && e.detail >= 1) { return; } @@ -547,7 +546,7 @@ class Status extends ImmutablePureComponent {
{(connectReply || connectUp || connectToRoot) &&
} -
+
{status.get('edited_at') && *} From c058c45a8e04f54b9ba634c577610003c0e902f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 10:07:14 +0200 Subject: [PATCH 022/106] New Crowdin Translations (automated) (#34701) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/bg.json | 1 - app/javascript/mastodon/locales/br.json | 1 - app/javascript/mastodon/locales/ca.json | 2 +- app/javascript/mastodon/locales/cs.json | 2 +- app/javascript/mastodon/locales/cy.json | 3 +++ app/javascript/mastodon/locales/da.json | 2 +- app/javascript/mastodon/locales/de.json | 2 +- app/javascript/mastodon/locales/es-AR.json | 2 +- app/javascript/mastodon/locales/es-MX.json | 2 +- app/javascript/mastodon/locales/es.json | 2 +- app/javascript/mastodon/locales/fi.json | 2 +- app/javascript/mastodon/locales/fo.json | 2 +- app/javascript/mastodon/locales/gl.json | 2 +- app/javascript/mastodon/locales/he.json | 2 +- app/javascript/mastodon/locales/hu.json | 1 - app/javascript/mastodon/locales/is.json | 2 +- app/javascript/mastodon/locales/it.json | 3 +++ app/javascript/mastodon/locales/nl.json | 2 +- app/javascript/mastodon/locales/pt-PT.json | 2 +- app/javascript/mastodon/locales/sq.json | 2 +- app/javascript/mastodon/locales/tr.json | 2 +- app/javascript/mastodon/locales/vi.json | 2 +- app/javascript/mastodon/locales/zh-TW.json | 2 +- config/locales/cy.yml | 4 ++++ config/locales/it.yml | 4 ++++ config/locales/simple_form.cy.yml | 9 +++++++++ config/locales/simple_form.it.yml | 5 +++++ 27 files changed, 46 insertions(+), 21 deletions(-) diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index f3c6f771f5..2a64f3e68d 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -28,7 +28,6 @@ "account.edit_profile": "Редактиране на профила", "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": "Препоръчано", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json index d327f0965b..8f79d7a05d 100644 --- a/app/javascript/mastodon/locales/br.json +++ b/app/javascript/mastodon/locales/br.json @@ -25,7 +25,6 @@ "account.edit_profile": "Kemmañ ar profil", "account.enable_notifications": "Ma c'hemenn pa vez embannet traoù gant @{name}", "account.endorse": "Lakaat war-wel war ar profil", - "account.familiar_followers_many": "Heuilhet gant {name1}, {name2}, {othersCount, plural, one {hag # all} two {ha # all} few {ha # all} many {ha(g) # all} other {hag(g) # all}}", "account.familiar_followers_one": "Heuilhet gant {name1}", "account.familiar_followers_two": "Heuilhet gant {name1} ha {name2}", "account.featured_tags.last_status_at": "Toud diwezhañ : {date}", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 2ea3a36329..787fb4c6fc 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -28,7 +28,7 @@ "account.edit_profile": "Edita el perfil", "account.enable_notifications": "Notifica'm els tuts de @{name}", "account.endorse": "Recomana en el perfil", - "account.familiar_followers_many": "Seguit per {name1}, {name2} i {othersCount, plural, one {# altre compte} other {# altres comptes}}", + "account.familiar_followers_many": "Seguit per {name1}, {name2} i {othersCount, plural, one {# altre compte} other {# altres comptes}} que coneixeu", "account.familiar_followers_one": "Seguit per {name1}", "account.familiar_followers_two": "Seguit per {name1} i {name2}", "account.featured": "Destacat", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index a185c59d79..ce71e1cf5a 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -28,7 +28,7 @@ "account.edit_profile": "Upravit profil", "account.enable_notifications": "Oznamovat mi příspěvky @{name}", "account.endorse": "Zvýraznit na profilu", - "account.familiar_followers_many": "Sleduje je {name1}, {name2} a {othersCount, plural, one {# další} few {# další} many {# dalších} other {# dalších}}", + "account.familiar_followers_many": "Sleduje je {name1}, {name2} a {othersCount, plural, one {jeden další, které znáte} few {# další, které znáte} many {# dalších, které znáte} other {# dalších, které znáte}}", "account.familiar_followers_one": "Sleduje je {name1}", "account.familiar_followers_two": "Sleduje je {name1} a {name2}", "account.featured": "Zvýrazněné", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index cc717d4e23..6a86a4926c 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -28,6 +28,9 @@ "account.edit_profile": "Golygu'r proffil", "account.enable_notifications": "Rhowch wybod i fi pan fydd @{name} yn postio", "account.endorse": "Dangos ar fy mhroffil", + "account.familiar_followers_many": "Yn cael ei ddilyn gan {name1},{name2}, a {othersCount, plural, one {one other you know} other{# others you know}}", + "account.familiar_followers_one": "Wedi'i ddilyn gan {name1}", + "account.familiar_followers_two": "Wedi'i ddilyn gan {name1} a {name2}", "account.featured": "Nodwedd", "account.featured.accounts": "Proffilau", "account.featured.hashtags": "Hashnodau", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 31d15a3eba..db5e66fe94 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -28,7 +28,7 @@ "account.edit_profile": "Redigér profil", "account.enable_notifications": "Advisér mig, når @{name} poster", "account.endorse": "Fremhæv på profil", - "account.familiar_followers_many": "Følges af {name1}, {name2} og {othersCount, plural, one {# mere} other {# mere}}", + "account.familiar_followers_many": "Følges af {name1}, {name2} og {othersCount, plural, one {# mere, man kender} other {# mere, man kender}}", "account.familiar_followers_one": "Følges af {name1}", "account.familiar_followers_two": "Følges af {name1} og {name2}", "account.featured": "Fremhævet", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index dfe6c8c389..01b195e5d4 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -28,7 +28,7 @@ "account.edit_profile": "Profil bearbeiten", "account.enable_notifications": "Benachrichtige mich wenn @{name} etwas postet", "account.endorse": "Im Profil vorstellen", - "account.familiar_followers_many": "Gefolgt von {name1}, {name2} und {othersCount, plural, one {# Profil} other {# weiteren Profilen}}", + "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}", "account.familiar_followers_two": "Gefolgt von {name1} und {name2}", "account.featured": "Vorgestellt", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index f441d8fca8..73260068fe 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} envíe mensajes", "account.endorse": "Destacar en el perfil", - "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# cuenta más} other {# cuentas más}}", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# cuenta más que conocés} other {# cuentas más que conocés}}", "account.familiar_followers_one": "Seguido por {name1}", "account.familiar_followers_two": "Seguido por {name1} y {name2}", "account.featured": "Destacados", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index e0ca7223ba..ef99fea899 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.endorse": "Destacar en mi perfil", - "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural,one {# otro} other {# otros}}", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural,one {otro que conoces}other {# otros que conoces}}", "account.familiar_followers_one": "Seguido por {name1}", "account.familiar_followers_two": "Seguid por {name1} y {name2}", "account.featured": "Destacado", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 99a4610e20..c41b9de93e 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificarme cuando @{name} publique algo", "account.endorse": "Destacar en el perfil", - "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural, one {# más} other {# más}}", + "account.familiar_followers_many": "Seguido por {name1}, {name2} y {othersCount, plural,one {otro que conoces}other {# otros que conoces}}", "account.familiar_followers_one": "Seguido por {name1}", "account.familiar_followers_two": "Seguido por {name1} y {name2}", "account.featured": "Destacado", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 3615185331..c65ed1288a 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -28,7 +28,7 @@ "account.edit_profile": "Muokkaa profiilia", "account.enable_notifications": "Ilmoita minulle, kun @{name} julkaisee", "account.endorse": "Suosittele profiilissa", - "account.familiar_followers_many": "Seuraajina {name1}, {name2} ja {othersCount, plural, one {# muu} other {# muuta}}", + "account.familiar_followers_many": "Seuraajina {name1}, {name2} ja {othersCount, plural, one {1 muu, jonka tunnet} other {# muuta, jotka tunnet}}", "account.familiar_followers_one": "Seuraajana {name1}", "account.familiar_followers_two": "Seuraajina {name1} ja {name2}", "account.featured": "Suositellut", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index c659f91eb6..1bb9bb29b0 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -28,7 +28,7 @@ "account.edit_profile": "Broyt vanga", "account.enable_notifications": "Boða mær frá, tá @{name} skrivar", "account.endorse": "Víst á vangamyndini", - "account.familiar_followers_many": "{name1}, {name2} og {othersCount, plural, one {# annar} other {# onnur}} fylgja", + "account.familiar_followers_many": "{name1}, {name2} og {othersCount, plural, one {ein annar/onnur tú kennir} other {# onnur tú kennir}} fylgja", "account.familiar_followers_one": "{name1} fylgir", "account.familiar_followers_two": "{name1} og {name2} fylgja", "account.featured": "Tikin fram", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index f88ab43be9..51275bfaee 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Noficarme cando @{name} publique", "account.endorse": "Amosar no perfil", - "account.familiar_followers_many": "Seguida por {name1}, {name2}, e {othersCount, plural,one {# máis} other {# máis}}", + "account.familiar_followers_many": "Seguida por {name1}, {name2}, e {othersCount, plural, one {outra conta que coñeces} other {outras # contas que coñeces}}", "account.familiar_followers_one": "Seguida por {name1}", "account.familiar_followers_two": "Seguida por {name1} e {name2}", "account.featured": "Destacado", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index d70006669a..d3811a75c4 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -28,7 +28,7 @@ "account.edit_profile": "עריכת פרופיל", "account.enable_notifications": "שלח לי התראות כש@{name} מפרסם", "account.endorse": "קדם את החשבון בפרופיל", - "account.familiar_followers_many": "החשבון נעקב על ידי {name1}, {name2} ועוד {othersCount, plural,one {אחד נוסף}other {# נוספים}}", + "account.familiar_followers_many": "החשבון נעקב על ידי {name1}, {name2} ועוד {othersCount, plural,one {אחד נוסף שמוכר לך}other {# נוספים שמוכרים לך}}", "account.familiar_followers_one": "החשבון נעקב על ידי {name1}", "account.familiar_followers_two": "החשבון נעקב על ידי {name1} ו־{name2}", "account.featured": "מומלץ", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 0ff052d750..97f9b7d387 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -28,7 +28,6 @@ "account.edit_profile": "Profil szerkesztése", "account.enable_notifications": "Figyelmeztessen, ha @{name} bejegyzést tesz közzé", "account.endorse": "Kiemelés a profilodon", - "account.familiar_followers_many": "{name1}, {name2} és {othersCount, plural, one {# másik} other {# másik}} követi", "account.familiar_followers_one": "{name1} követi", "account.familiar_followers_two": "{name1} és {name2} követi", "account.featured": "Kiemelt", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index e9655f6af5..aff74649fe 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -28,7 +28,7 @@ "account.edit_profile": "Breyta notandasniði", "account.enable_notifications": "Láta mig vita þegar @{name} sendir inn", "account.endorse": "Birta á notandasniði", - "account.familiar_followers_many": "Fylgt af {name1}, {name2} og {othersCount, plural, one {# í viðbót} other {# í viðbót}}", + "account.familiar_followers_many": "Fylgt af {name1}, {name2} og {othersCount, plural, one {einum öðrum sem þú þekkir} other {# öðrum sem þú þekkir}}", "account.familiar_followers_one": "Fylgt af {name1}", "account.familiar_followers_two": "Fylgt af {name1} og {name2}", "account.featured": "Með aukið vægi", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 838d662f6b..606b9ded92 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -28,6 +28,9 @@ "account.edit_profile": "Modifica profilo", "account.enable_notifications": "Avvisami quando @{name} pubblica un post", "account.endorse": "In evidenza sul profilo", + "account.familiar_followers_many": "Seguito da {name1}, {name2}, e {othersCount, plural, one {un altro che conosci} other {# altri che conosci}}", + "account.familiar_followers_one": "Seguito da {name1}", + "account.familiar_followers_two": "Seguito da {name1} e {name2}", "account.featured": "In primo piano", "account.featured.accounts": "Profili", "account.featured.hashtags": "Hashtag", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 3dac5fd7dd..404b0e0395 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -28,7 +28,7 @@ "account.edit_profile": "Profiel bewerken", "account.enable_notifications": "Geef een melding wanneer @{name} een bericht plaatst", "account.endorse": "Op profiel weergeven", - "account.familiar_followers_many": "Gevolgd door {name1}, {name2} en {othersCount, plural, one {# ander account} other {# andere accounts}}", + "account.familiar_followers_many": "Gevolgd door {name1}, {name2} en {othersCount, plural, one {één ander bekend account} other {# andere bekende accounts}}", "account.familiar_followers_one": "Gevolgd door {name1}", "account.familiar_followers_two": "Gevolgd door {name1} en {name2}", "account.featured": "Uitgelicht", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 5facd93d64..88fd9bf427 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -28,7 +28,7 @@ "account.edit_profile": "Editar perfil", "account.enable_notifications": "Notificar-me das publicações de @{name}", "account.endorse": "Destacar no perfil", - "account.familiar_followers_many": "Seguido por {name1}, {name2} e {othersCount, plural,one {# outro}other {# outros}}", + "account.familiar_followers_many": "Seguido por {name1}, {name2} e {othersCount, plural,one {mais uma pessoa que conhece} other {# outras pessoas que conhece}}", "account.familiar_followers_one": "Seguido por {name1}", "account.familiar_followers_two": "Seguido por {name1} e {name2}", "account.featured": "Destaques", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 77d86db377..af3fd9761f 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -28,7 +28,7 @@ "account.edit_profile": "Përpunoni profilin", "account.enable_notifications": "Njoftomë, kur poston @{name}", "account.endorse": "Pasqyrojeni në profil", - "account.familiar_followers_many": "Ndjekur nga {name1}, {name2} dhe {othersCount, plural, one {# tjetër} other {# të tjerë}}", + "account.familiar_followers_many": "Ndjekur nga {name1}, {name2} dhe {othersCount, plural, one {një tjetër që njihni} other {# të tjerë që njihni}}", "account.familiar_followers_one": "Ndjekur nga {name1}", "account.familiar_followers_two": "Ndjekur nga {name1} dhe {name2}", "account.featured": "Të zgjedhur", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index f46f070817..566b7ddff0 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -28,7 +28,7 @@ "account.edit_profile": "Profili düzenle", "account.enable_notifications": "@{name} kişisinin gönderi bildirimlerini aç", "account.endorse": "Profilimde öne çıkar", - "account.familiar_followers_many": "{name1}, {name2}, {othersCount, plural, one {# diğer} other {# diğer}} kişi tarafından takip ediliyor", + "account.familiar_followers_many": "{name1}, {name2}, {othersCount, plural, one {# diğer} other {# diğer}} bildiğiniz kişi tarafından takip ediliyor", "account.familiar_followers_one": "{name1} tarafından takip ediliyor", "account.familiar_followers_two": "{name1} ve {name2} tarafından takip ediliyor", "account.featured": "Öne çıkan", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 7f7dcae7de..d592b0caa8 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -28,7 +28,7 @@ "account.edit_profile": "Sửa hồ sơ", "account.enable_notifications": "Nhận thông báo khi @{name} đăng tút", "account.endorse": "Tôn vinh người này", - "account.familiar_followers_many": "Theo dõi bởi {name1}, {name2} và {othersCount, plural, other {# người khác}}", + "account.familiar_followers_many": "Theo dõi bởi {name1}, {name2} và {othersCount, plural, other {# người khác mà bạn biết}}", "account.familiar_followers_one": "Theo dõi bởi {name1}", "account.familiar_followers_two": "Theo dõi bởi {name1} và {name2}", "account.featured": "Nêu bật", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index b47ba36975..14c64c92b8 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -28,7 +28,7 @@ "account.edit_profile": "編輯個人檔案", "account.enable_notifications": "當 @{name} 嘟文時通知我", "account.endorse": "於個人檔案推薦對方", - "account.familiar_followers_many": "被 {name1}、{name2}、及 {othersCount, plural, other {其他 # 人}} 跟隨", + "account.familiar_followers_many": "被 {name1}、{name2}、及 {othersCount, plural, other {其他您認識的 # 人}} 跟隨", "account.familiar_followers_one": "被 {name1} 跟隨", "account.familiar_followers_two": "被 {name1} 與 {name2} 跟隨", "account.featured": "精選內容", diff --git a/config/locales/cy.yml b/config/locales/cy.yml index c4d32c5fd7..59117fe1d1 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -2030,6 +2030,10 @@ cy: limit: Rydych chi eisoes wedi pinio uchafswm nifer y postiadau ownership: Nid oes modd pinio postiad rhywun arall reblog: Nid oes modd pinio hwb + quote_policies: + followers: Dilynwyr a defnyddwyr wedi'u crybwyll + nobody: Dim ond defnyddwyr wedi'u crybwyll + public: Pawb title: '%{name}: "%{quote}"' visibilities: direct: Uniongyrchol diff --git a/config/locales/it.yml b/config/locales/it.yml index 9894e99199..6ff9b33310 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1860,6 +1860,10 @@ it: limit: Hai già fissato in cima il massimo numero di post ownership: Non puoi fissare in cima un post di qualcun altro reblog: Un toot condiviso non può essere fissato in cima + quote_policies: + followers: Seguaci e utenti menzionati + nobody: Solo gli utenti menzionati + public: Tutti title: '%{name}: "%{quote}"' visibilities: direct: Diretto diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index 656cca8b37..c6a140b27b 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -56,6 +56,7 @@ cy: scopes: Pa APIs y bydd y rhaglen yn cael mynediad iddynt. Os dewiswch gwmpas lefel uchaf, nid oes angen i chi ddewis rhai unigol. setting_aggregate_reblogs: Peidiwch â dangos hybiau newydd ar bostiadau sydd wedi cael eu hybu'n ddiweddar (dim ond yn effeithio ar hybiau newydd ei dderbyn) setting_always_send_emails: Fel arfer ni fydd hysbysiadau e-bost yn cael eu hanfon pan fyddwch chi wrthi'n defnyddio Mastodon + setting_default_quote_policy: Mae defnyddwyr sy'n cael eu crybwyll yn cael dyfynnu bob amser. Dim ond ar gyfer postiadau a grëwyd gyda'r fersiwn nesaf o Mastodon y bydd y gosodiad hwn yn dod i rym, ond gallwch ddewis eich dewis wrth baratoi. setting_default_sensitive: Mae cyfryngau sensitif wedi'u cuddio yn rhagosodedig a gellir eu datgelu trwy glicio setting_display_media_default: Cuddio cyfryngau wedi eu marcio'n sensitif setting_display_media_hide_all: Cuddio cyfryngau bob tro @@ -148,6 +149,13 @@ cy: min_age: Ni ddylai fod yn is na'r isafswm oedran sy'n ofynnol gan gyfreithiau eich awdurdodaeth. user: chosen_languages: Wedi eu dewis, dim ond tŵtiau yn yr ieithoedd hyn bydd yn cael eu harddangos mewn ffrydiau cyhoeddus + date_of_birth: + few: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. + many: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. + one: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. + other: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. + two: Mae'n rhai i ni wneud yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. + zero: Gwnewch yn siŵr eich bod o leiaf yn %{count} i ddefnyddio Mastodon. Fyddwn ni ddim yn cadw hwn. role: Mae'r rôl yn rheoli pa ganiatâd sydd gan y defnyddiwr. user_role: color: Lliw i'w ddefnyddio ar gyfer y rôl drwy'r UI, fel RGB mewn fformat hecs @@ -228,6 +236,7 @@ cy: setting_boost_modal: Dangos deialog cadarnhau cyn rhoi hwb setting_default_language: Iaith postio setting_default_privacy: Preifatrwydd cyhoeddi + setting_default_quote_policy: Pwy sy'n gallu dyfynnu setting_default_sensitive: Marcio cyfryngau fel eu bod yn sensitif bob tro setting_delete_modal: Dangos deialog cadarnhau cyn dileu postiad setting_disable_hover_cards: Analluogi rhagolwg proffil ar lusgo diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index a3672696e5..556688337b 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -56,6 +56,7 @@ it: scopes: A quali API l'applicazione potrà avere accesso. Se selezionate un ambito di alto livello, non c'è bisogno di selezionare quelle singole. setting_aggregate_reblogs: Non mostrare nuove condivisioni per toot che sono stati condivisi di recente (ha effetto solo sulle nuove condivisioni) setting_always_send_emails: Normalmente le notifiche e-mail non vengono inviate quando si utilizza attivamente Mastodon + setting_default_quote_policy: Gli utenti menzionati sono sempre in grado di citare. Questa impostazione avrà effetto solo per i post che verranno creati con la prossima versione di Mastodon, ma puoi selezionare le tue preferenze in preparazione del rilascio della prossima versione setting_default_sensitive: Media con contenuti sensibili sono nascosti in modo predefinito e possono essere rivelati con un click setting_display_media_default: Nascondi media segnati come sensibili setting_display_media_hide_all: Nascondi sempre tutti i media @@ -148,6 +149,9 @@ it: min_age: Non si dovrebbe avere un'età inferiore a quella minima richiesta, dalle leggi della tua giurisdizione. user: chosen_languages: Quando una o più lingue sono contrassegnate, nelle timeline pubbliche vengono mostrati solo i toot nelle lingue selezionate + date_of_birth: + one: Dobbiamo verificare che tu abbia almeno %{count} anno per usare Mastodon. Non archivieremo questa informazione. + other: Dobbiamo verificare che tu abbia almeno %{count} anni per usare Mastodon. Non archivieremo questa informazione. role: Il ruolo controlla quali permessi ha l'utente. user_role: color: Colore da usare per il ruolo in tutta l'UI, come RGB in formato esadecimale @@ -228,6 +232,7 @@ it: setting_boost_modal: Mostra dialogo di conferma prima del boost setting_default_language: Lingua dei post setting_default_privacy: Privacy dei post + setting_default_quote_policy: Chi può citare setting_default_sensitive: Segna sempre i media come sensibili setting_delete_modal: Mostra dialogo di conferma prima di eliminare un post setting_disable_hover_cards: Disabilita l'anteprima del profilo al passaggio del mouse From 3ea1f074abfae97fbd33c8953e4d3f40c17be856 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 16 May 2025 11:07:33 +0200 Subject: [PATCH 023/106] Fix sidekiq JSON serialization warning in `ActivityPub::FetchAllRepliesWorker` (#34702) --- app/workers/activitypub/fetch_all_replies_worker.rb | 2 +- spec/workers/activitypub/fetch_all_replies_worker_spec.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/workers/activitypub/fetch_all_replies_worker.rb b/app/workers/activitypub/fetch_all_replies_worker.rb index d4ac3e85b8..40b251cf14 100644 --- a/app/workers/activitypub/fetch_all_replies_worker.rb +++ b/app/workers/activitypub/fetch_all_replies_worker.rb @@ -80,7 +80,7 @@ class ActivityPub::FetchAllRepliesWorker root_status_body = fetch_resource(root_status_uri, true) return if root_status_body.nil? - FetchReplyWorker.perform_async(root_status_uri, { **options, prefetched_body: root_status_body }) + FetchReplyWorker.perform_async(root_status_uri, { **options.deep_stringify_keys, 'prefetched_body' => root_status_body }) get_replies(root_status_body, MAX_PAGES, options) end diff --git a/spec/workers/activitypub/fetch_all_replies_worker_spec.rb b/spec/workers/activitypub/fetch_all_replies_worker_spec.rb index 682d538653..9a8bdac030 100644 --- a/spec/workers/activitypub/fetch_all_replies_worker_spec.rb +++ b/spec/workers/activitypub/fetch_all_replies_worker_spec.rb @@ -124,8 +124,6 @@ RSpec.describe ActivityPub::FetchAllRepliesWorker do before do stub_const('Status::FetchRepliesConcern::FETCH_REPLIES_ENABLED', true) - allow(FetchReplyWorker).to receive(:push_bulk) - allow(FetchReplyWorker).to receive(:perform_async) all_items.each do |item| next if [top_note_uri, reply_note_uri].include? item @@ -150,7 +148,7 @@ RSpec.describe ActivityPub::FetchAllRepliesWorker do it 'fetches the top status only once' do _ = subject.perform(status.id, { request_id: 0 }) - expect(FetchReplyWorker).to have_received(:perform_async).with(top_note_uri, { prefetched_body: top_object.deep_stringify_keys, request_id: 0 }) + expect(FetchReplyWorker).to have_enqueued_sidekiq_job(top_note_uri, { 'prefetched_body' => top_object.deep_stringify_keys, 'request_id' => 0 }) expect(a_request(:get, top_note_uri)).to have_been_made.once end end From a5a2c6dc7ec0d8af53594cd53a90da7d6fbefd5a Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 16 May 2025 14:24:02 +0200 Subject: [PATCH 024/106] Add support for FASP data sharing (#34415) --- .../v0/backfill_requests_controller.rb | 26 ++++ .../v0/continuations_controller.rb | 10 ++ .../v0/event_subscriptions_controller.rb | 25 ++++ app/lib/fasp/request.rb | 1 + app/models/account.rb | 1 + app/models/concerns/account/fasp_concern.rb | 37 ++++++ app/models/concerns/favourite/fasp_concern.rb | 17 +++ app/models/concerns/status/fasp_concern.rb | 53 ++++++++ app/models/fasp.rb | 2 + app/models/fasp/backfill_request.rb | 67 ++++++++++ app/models/fasp/provider.rb | 2 + app/models/fasp/subscription.rb | 43 ++++++ app/models/favourite.rb | 1 + app/models/status.rb | 3 +- ...announce_account_lifecycle_event_worker.rb | 28 ++++ ...announce_content_lifecycle_event_worker.rb | 28 ++++ app/workers/fasp/announce_trend_worker.rb | 61 +++++++++ app/workers/fasp/backfill_worker.rb | 32 +++++ config/routes/fasp.rb | 10 ++ config/sidekiq.yml | 1 + ...0241213130230_create_fasp_subscriptions.rb | 18 +++ ...103131909_create_fasp_backfill_requests.rb | 15 +++ db/schema.rb | 27 ++++ spec/fabricators/account_fabricator.rb | 1 + .../fasp/backfill_request_fabricator.rb | 9 ++ .../fasp/subscription_fabricator.rb | 8 ++ .../concerns/account/fasp_concern_spec.rb | 83 ++++++++++++ .../concerns/favourite/fasp_concern_spec.rb | 11 ++ .../concerns/status/fasp_concern_spec.rb | 123 ++++++++++++++++++ spec/models/fasp/backfill_request_spec.rb | 93 +++++++++++++ spec/models/fasp/subscription_spec.rb | 33 +++++ .../data_sharing/v0/backfill_requests_spec.rb | 41 ++++++ .../data_sharing/v0/continuations_spec.rb | 22 ++++ .../v0/event_subscriptions_spec.rb | 57 ++++++++ ...nce_account_lifecycle_event_worker_spec.rb | 34 +++++ ...nce_content_lifecycle_event_worker_spec.rb | 34 +++++ .../fasp/announce_trend_worker_spec.rb | 52 ++++++++ spec/workers/fasp/backfill_worker_spec.rb | 32 +++++ 38 files changed, 1140 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/fasp/data_sharing/v0/backfill_requests_controller.rb create mode 100644 app/controllers/api/fasp/data_sharing/v0/continuations_controller.rb create mode 100644 app/controllers/api/fasp/data_sharing/v0/event_subscriptions_controller.rb create mode 100644 app/models/concerns/account/fasp_concern.rb create mode 100644 app/models/concerns/favourite/fasp_concern.rb create mode 100644 app/models/concerns/status/fasp_concern.rb create mode 100644 app/models/fasp/backfill_request.rb create mode 100644 app/models/fasp/subscription.rb create mode 100644 app/workers/fasp/announce_account_lifecycle_event_worker.rb create mode 100644 app/workers/fasp/announce_content_lifecycle_event_worker.rb create mode 100644 app/workers/fasp/announce_trend_worker.rb create mode 100644 app/workers/fasp/backfill_worker.rb create mode 100644 db/migrate/20241213130230_create_fasp_subscriptions.rb create mode 100644 db/migrate/20250103131909_create_fasp_backfill_requests.rb create mode 100644 spec/fabricators/fasp/backfill_request_fabricator.rb create mode 100644 spec/fabricators/fasp/subscription_fabricator.rb create mode 100644 spec/models/concerns/account/fasp_concern_spec.rb create mode 100644 spec/models/concerns/favourite/fasp_concern_spec.rb create mode 100644 spec/models/concerns/status/fasp_concern_spec.rb create mode 100644 spec/models/fasp/backfill_request_spec.rb create mode 100644 spec/models/fasp/subscription_spec.rb create mode 100644 spec/requests/api/fasp/data_sharing/v0/backfill_requests_spec.rb create mode 100644 spec/requests/api/fasp/data_sharing/v0/continuations_spec.rb create mode 100644 spec/requests/api/fasp/data_sharing/v0/event_subscriptions_spec.rb create mode 100644 spec/workers/fasp/announce_account_lifecycle_event_worker_spec.rb create mode 100644 spec/workers/fasp/announce_content_lifecycle_event_worker_spec.rb create mode 100644 spec/workers/fasp/announce_trend_worker_spec.rb create mode 100644 spec/workers/fasp/backfill_worker_spec.rb diff --git a/app/controllers/api/fasp/data_sharing/v0/backfill_requests_controller.rb b/app/controllers/api/fasp/data_sharing/v0/backfill_requests_controller.rb new file mode 100644 index 0000000000..c37a94f251 --- /dev/null +++ b/app/controllers/api/fasp/data_sharing/v0/backfill_requests_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class Api::Fasp::DataSharing::V0::BackfillRequestsController < Api::Fasp::BaseController + def create + backfill_request = current_provider.fasp_backfill_requests.new(backfill_request_params) + + respond_to do |format| + format.json do + if backfill_request.save + render json: { backfillRequest: { id: backfill_request.id } }, status: 201 + else + head 422 + end + end + end + end + + private + + def backfill_request_params + params + .permit(:category, :maxCount) + .to_unsafe_h + .transform_keys { |k| k.to_s.underscore } + end +end diff --git a/app/controllers/api/fasp/data_sharing/v0/continuations_controller.rb b/app/controllers/api/fasp/data_sharing/v0/continuations_controller.rb new file mode 100644 index 0000000000..eff2ac0e21 --- /dev/null +++ b/app/controllers/api/fasp/data_sharing/v0/continuations_controller.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Api::Fasp::DataSharing::V0::ContinuationsController < Api::Fasp::BaseController + def create + backfill_request = current_provider.fasp_backfill_requests.find(params[:backfill_request_id]) + Fasp::BackfillWorker.perform_async(backfill_request.id) + + head 204 + end +end diff --git a/app/controllers/api/fasp/data_sharing/v0/event_subscriptions_controller.rb b/app/controllers/api/fasp/data_sharing/v0/event_subscriptions_controller.rb new file mode 100644 index 0000000000..29e03d5836 --- /dev/null +++ b/app/controllers/api/fasp/data_sharing/v0/event_subscriptions_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Api::Fasp::DataSharing::V0::EventSubscriptionsController < Api::Fasp::BaseController + def create + subscription = current_provider.fasp_subscriptions.create!(subscription_params) + + render json: { subscription: { id: subscription.id } }, status: 201 + end + + def destroy + subscription = current_provider.fasp_subscriptions.find(params[:id]) + subscription.destroy + + head 204 + end + + private + + def subscription_params + params + .permit(:category, :subscriptionType, :maxBatchSize, threshold: {}) + .to_unsafe_h + .transform_keys { |k| k.to_s.underscore } + end +end diff --git a/app/lib/fasp/request.rb b/app/lib/fasp/request.rb index 2addbe8502..7d8c05d406 100644 --- a/app/lib/fasp/request.rb +++ b/app/lib/fasp/request.rb @@ -32,6 +32,7 @@ class Fasp::Request def request_headers(verb, url, body = '') result = { 'accept' => 'application/json', + 'content-type' => 'application/json', 'content-digest' => content_digest(body), } result.merge(signature_headers(verb, url, result)) diff --git a/app/models/account.rb b/app/models/account.rb index 53bf2407e8..22b8bce601 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -85,6 +85,7 @@ class Account < ApplicationRecord include Account::Associations include Account::Avatar include Account::Counters + include Account::FaspConcern include Account::FinderConcern include Account::Header include Account::Interactions diff --git a/app/models/concerns/account/fasp_concern.rb b/app/models/concerns/account/fasp_concern.rb new file mode 100644 index 0000000000..b18529a3e9 --- /dev/null +++ b/app/models/concerns/account/fasp_concern.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Account::FaspConcern + extend ActiveSupport::Concern + + included do + after_commit :announce_new_account_to_subscribed_fasp, on: :create + after_commit :announce_updated_account_to_subscribed_fasp, on: :update + after_commit :announce_deleted_account_to_subscribed_fasp, on: :destroy + end + + private + + def announce_new_account_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless discoverable? + + uri = ActivityPub::TagManager.instance.uri_for(self) + Fasp::AnnounceAccountLifecycleEventWorker.perform_async(uri, 'new') + end + + def announce_updated_account_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless discoverable? || saved_change_to_discoverable? + + uri = ActivityPub::TagManager.instance.uri_for(self) + Fasp::AnnounceAccountLifecycleEventWorker.perform_async(uri, 'update') + end + + def announce_deleted_account_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless discoverable? + + uri = ActivityPub::TagManager.instance.uri_for(self) + Fasp::AnnounceAccountLifecycleEventWorker.perform_async(uri, 'delete') + end +end diff --git a/app/models/concerns/favourite/fasp_concern.rb b/app/models/concerns/favourite/fasp_concern.rb new file mode 100644 index 0000000000..c72e7c3792 --- /dev/null +++ b/app/models/concerns/favourite/fasp_concern.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Favourite::FaspConcern + extend ActiveSupport::Concern + + included do + after_commit :announce_trends_to_subscribed_fasp, on: :create + end + + private + + def announce_trends_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + + Fasp::AnnounceTrendWorker.perform_async(status_id, 'favourite') + end +end diff --git a/app/models/concerns/status/fasp_concern.rb b/app/models/concerns/status/fasp_concern.rb new file mode 100644 index 0000000000..9a838c3a6a --- /dev/null +++ b/app/models/concerns/status/fasp_concern.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Status::FaspConcern + extend ActiveSupport::Concern + + included do + after_commit :announce_new_content_to_subscribed_fasp, on: :create + after_commit :announce_updated_content_to_subscribed_fasp, on: :update + after_commit :announce_deleted_content_to_subscribed_fasp, on: :destroy + after_commit :announce_trends_to_subscribed_fasp, on: :create + end + + private + + def announce_new_content_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless account_indexable? && public_visibility? + + # We need the uri here, but it is set in another `after_commit` + # callback. Hooks included from modules are run before the ones + # in the class itself and can neither be reordered nor is there + # a way to declare dependencies. + store_uri if uri.nil? + Fasp::AnnounceContentLifecycleEventWorker.perform_async(uri, 'new') + end + + def announce_updated_content_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless account_indexable? && public_visibility? + + Fasp::AnnounceContentLifecycleEventWorker.perform_async(uri, 'update') + end + + def announce_deleted_content_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless account_indexable? && public_visibility? + + Fasp::AnnounceContentLifecycleEventWorker.perform_async(uri, 'delete') + end + + def announce_trends_to_subscribed_fasp + return unless Mastodon::Feature.fasp_enabled? + return unless account_indexable? + + candidate_id, trend_source = + if reblog_of_id + [reblog_of_id, 'reblog'] + elsif in_reply_to_id + [in_reply_to_id, 'reply'] + end + Fasp::AnnounceTrendWorker.perform_async(candidate_id, trend_source) if candidate_id + end +end diff --git a/app/models/fasp.rb b/app/models/fasp.rb index cb33937715..e4e73a2312 100644 --- a/app/models/fasp.rb +++ b/app/models/fasp.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module Fasp + DATA_CATEGORIES = %w(account content).freeze + def self.table_name_prefix 'fasp_' end diff --git a/app/models/fasp/backfill_request.rb b/app/models/fasp/backfill_request.rb new file mode 100644 index 0000000000..e1be611097 --- /dev/null +++ b/app/models/fasp/backfill_request.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: fasp_backfill_requests +# +# id :bigint(8) not null, primary key +# category :string not null +# cursor :string +# fulfilled :boolean default(FALSE), not null +# max_count :integer default(100), not null +# created_at :datetime not null +# updated_at :datetime not null +# fasp_provider_id :bigint(8) not null +# +class Fasp::BackfillRequest < ApplicationRecord + belongs_to :fasp_provider, class_name: 'Fasp::Provider' + + validates :category, presence: true, inclusion: Fasp::DATA_CATEGORIES + validates :max_count, presence: true, + numericality: { only_integer: true } + + after_commit :queue_fulfillment_job, on: :create + + def next_objects + @next_objects ||= base_scope.to_a + end + + def next_uris + next_objects.map { |o| ActivityPub::TagManager.instance.uri_for(o) } + end + + def more_objects_available? + return false if next_objects.empty? + + base_scope.where(id: ...(next_objects.last.id)).any? + end + + def advance! + if more_objects_available? + update!(cursor: next_objects.last.id) + else + update!(fulfilled: true) + end + end + + private + + def base_scope + result = category_scope.limit(max_count).order(id: :desc) + result = result.where(id: ...cursor) if cursor.present? + result + end + + def category_scope + case category + when 'account' + Account.discoverable.without_instance_actor + when 'content' + Status.indexable + end + end + + def queue_fulfillment_job + Fasp::BackfillWorker.perform_async(id) + end +end diff --git a/app/models/fasp/provider.rb b/app/models/fasp/provider.rb index cd1b3008c7..7926953e6c 100644 --- a/app/models/fasp/provider.rb +++ b/app/models/fasp/provider.rb @@ -22,7 +22,9 @@ class Fasp::Provider < ApplicationRecord include DebugConcern + has_many :fasp_backfill_requests, inverse_of: :fasp_provider, class_name: 'Fasp::BackfillRequest', dependent: :delete_all has_many :fasp_debug_callbacks, inverse_of: :fasp_provider, class_name: 'Fasp::DebugCallback', dependent: :delete_all + has_many :fasp_subscriptions, inverse_of: :fasp_provider, class_name: 'Fasp::Subscription', dependent: :delete_all validates :name, presence: true validates :base_url, presence: true, url: true diff --git a/app/models/fasp/subscription.rb b/app/models/fasp/subscription.rb new file mode 100644 index 0000000000..e2e554ed74 --- /dev/null +++ b/app/models/fasp/subscription.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: fasp_subscriptions +# +# id :bigint(8) not null, primary key +# category :string not null +# max_batch_size :integer not null +# subscription_type :string not null +# threshold_likes :integer +# threshold_replies :integer +# threshold_shares :integer +# threshold_timeframe :integer +# created_at :datetime not null +# updated_at :datetime not null +# fasp_provider_id :bigint(8) not null +# +class Fasp::Subscription < ApplicationRecord + TYPES = %w(lifecycle trends).freeze + + belongs_to :fasp_provider, class_name: 'Fasp::Provider' + + validates :category, presence: true, inclusion: Fasp::DATA_CATEGORIES + validates :subscription_type, presence: true, + inclusion: TYPES + + scope :category_content, -> { where(category: 'content') } + scope :category_account, -> { where(category: 'account') } + scope :lifecycle, -> { where(subscription_type: 'lifecycle') } + scope :trends, -> { where(subscription_type: 'trends') } + + def threshold=(threshold) + self.threshold_timeframe = threshold['timeframe'] || 15 + self.threshold_shares = threshold['shares'] || 3 + self.threshold_likes = threshold['likes'] || 3 + self.threshold_replies = threshold['replies'] || 3 + end + + def timeframe_start + threshold_timeframe.minutes.ago + end +end diff --git a/app/models/favourite.rb b/app/models/favourite.rb index 042f72beae..7bf793e2a1 100644 --- a/app/models/favourite.rb +++ b/app/models/favourite.rb @@ -13,6 +13,7 @@ class Favourite < ApplicationRecord include Paginable + include Favourite::FaspConcern update_index('statuses', :status) diff --git a/app/models/status.rb b/app/models/status.rb index 5e89fc3531..8287583bc3 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -36,6 +36,7 @@ class Status < ApplicationRecord include Discard::Model include Paginable include RateLimitable + include Status::FaspConcern include Status::FetchRepliesConcern include Status::SafeReblogInsert include Status::SearchConcern @@ -181,7 +182,7 @@ class Status < ApplicationRecord ], thread: :account - delegate :domain, to: :account, prefix: true + delegate :domain, :indexable?, to: :account, prefix: true REAL_TIME_WINDOW = 6.hours diff --git a/app/workers/fasp/announce_account_lifecycle_event_worker.rb b/app/workers/fasp/announce_account_lifecycle_event_worker.rb new file mode 100644 index 0000000000..ea8544c24d --- /dev/null +++ b/app/workers/fasp/announce_account_lifecycle_event_worker.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class Fasp::AnnounceAccountLifecycleEventWorker + include Sidekiq::Worker + + sidekiq_options queue: 'fasp', retry: 5 + + def perform(uri, event_type) + Fasp::Subscription.includes(:fasp_provider).category_account.lifecycle.each do |subscription| + announce(subscription, uri, event_type) + end + end + + private + + def announce(subscription, uri, event_type) + Fasp::Request.new(subscription.fasp_provider).post('/data_sharing/v0/announcements', body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'account', + eventType: event_type, + objectUris: [uri], + }) + end +end diff --git a/app/workers/fasp/announce_content_lifecycle_event_worker.rb b/app/workers/fasp/announce_content_lifecycle_event_worker.rb new file mode 100644 index 0000000000..744528f2d3 --- /dev/null +++ b/app/workers/fasp/announce_content_lifecycle_event_worker.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class Fasp::AnnounceContentLifecycleEventWorker + include Sidekiq::Worker + + sidekiq_options queue: 'fasp', retry: 5 + + def perform(uri, event_type) + Fasp::Subscription.includes(:fasp_provider).category_content.lifecycle.each do |subscription| + announce(subscription, uri, event_type) + end + end + + private + + def announce(subscription, uri, event_type) + Fasp::Request.new(subscription.fasp_provider).post('/data_sharing/v0/announcements', body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'content', + eventType: event_type, + objectUris: [uri], + }) + end +end diff --git a/app/workers/fasp/announce_trend_worker.rb b/app/workers/fasp/announce_trend_worker.rb new file mode 100644 index 0000000000..ae93c3d9f6 --- /dev/null +++ b/app/workers/fasp/announce_trend_worker.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +class Fasp::AnnounceTrendWorker + include Sidekiq::Worker + + sidekiq_options queue: 'fasp', retry: 5 + + def perform(status_id, trend_source) + status = ::Status.includes(:account).find(status_id) + return unless status.account.indexable? + + Fasp::Subscription.includes(:fasp_provider).category_content.trends.each do |subscription| + announce(subscription, status.uri) if trending?(subscription, status, trend_source) + end + rescue ActiveRecord::RecordNotFound + # status might not exist anymore, in which case there is nothing to do + end + + private + + def trending?(subscription, status, trend_source) + scope = scope_for(status, trend_source) + threshold = threshold_for(subscription, trend_source) + scope.where(created_at: subscription.timeframe_start..).count >= threshold + end + + def scope_for(status, trend_source) + case trend_source + when 'favourite' + status.favourites + when 'reblog' + status.reblogs + when 'reply' + status.replies + end + end + + def threshold_for(subscription, trend_source) + case trend_source + when 'favourite' + subscription.threshold_likes + when 'reblog' + subscription.threshold_shares + when 'reply' + subscription.threshold_replies + end + end + + def announce(subscription, uri) + Fasp::Request.new(subscription.fasp_provider).post('/data_sharing/v0/announcements', body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'content', + eventType: 'trending', + objectUris: [uri], + }) + end +end diff --git a/app/workers/fasp/backfill_worker.rb b/app/workers/fasp/backfill_worker.rb new file mode 100644 index 0000000000..4e30b71a7d --- /dev/null +++ b/app/workers/fasp/backfill_worker.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class Fasp::BackfillWorker + include Sidekiq::Worker + + sidekiq_options queue: 'fasp', retry: 5 + + def perform(backfill_request_id) + backfill_request = Fasp::BackfillRequest.find(backfill_request_id) + + announce(backfill_request) + + backfill_request.advance! + rescue ActiveRecord::RecordNotFound + # ignore missing backfill requests + end + + private + + def announce(backfill_request) + Fasp::Request.new(backfill_request.fasp_provider).post('/data_sharing/v0/announcements', body: { + source: { + backfillRequest: { + id: backfill_request.id.to_s, + }, + }, + category: backfill_request.category, + objectUris: backfill_request.next_uris, + moreObjectsAvailable: backfill_request.more_objects_available?, + }) + end +end diff --git a/config/routes/fasp.rb b/config/routes/fasp.rb index 9d052526de..bd2bb4b520 100644 --- a/config/routes/fasp.rb +++ b/config/routes/fasp.rb @@ -10,6 +10,16 @@ namespace :api, format: false do end end + namespace :data_sharing do + namespace :v0 do + resources :backfill_requests, only: [:create] do + resource :continuation, only: [:create] + end + + resources :event_subscriptions, only: [:create, :destroy] + end + end + resource :registration, only: [:create] end end diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 488c2f2ab3..9bfc7e9984 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -7,6 +7,7 @@ - [mailers, 2] - [pull] - [scheduler] + - [fasp] :scheduler: :listened_queues_only: true diff --git a/db/migrate/20241213130230_create_fasp_subscriptions.rb b/db/migrate/20241213130230_create_fasp_subscriptions.rb new file mode 100644 index 0000000000..7037022303 --- /dev/null +++ b/db/migrate/20241213130230_create_fasp_subscriptions.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateFaspSubscriptions < ActiveRecord::Migration[7.2] + def change + create_table :fasp_subscriptions do |t| + t.string :category, null: false + t.string :subscription_type, null: false + t.integer :max_batch_size, null: false + t.integer :threshold_timeframe + t.integer :threshold_shares + t.integer :threshold_likes + t.integer :threshold_replies + t.references :fasp_provider, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20250103131909_create_fasp_backfill_requests.rb b/db/migrate/20250103131909_create_fasp_backfill_requests.rb new file mode 100644 index 0000000000..31dcaaa469 --- /dev/null +++ b/db/migrate/20250103131909_create_fasp_backfill_requests.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateFaspBackfillRequests < ActiveRecord::Migration[7.2] + def change + create_table :fasp_backfill_requests do |t| + t.string :category, null: false + t.integer :max_count, null: false, default: 100 + t.string :cursor + t.boolean :fulfilled, null: false, default: false + t.references :fasp_provider, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index db1687ba99..77b5b732d1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -445,6 +445,17 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_28_095029) do t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end + create_table "fasp_backfill_requests", force: :cascade do |t| + t.string "category", null: false + t.integer "max_count", default: 100, null: false + t.string "cursor" + t.boolean "fulfilled", default: false, null: false + t.bigint "fasp_provider_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["fasp_provider_id"], name: "index_fasp_backfill_requests_on_fasp_provider_id" + end + create_table "fasp_debug_callbacks", force: :cascade do |t| t.bigint "fasp_provider_id", null: false t.string "ip", null: false @@ -471,6 +482,20 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_28_095029) do t.index ["base_url"], name: "index_fasp_providers_on_base_url", unique: true end + create_table "fasp_subscriptions", force: :cascade do |t| + t.string "category", null: false + t.string "subscription_type", null: false + t.integer "max_batch_size", null: false + t.integer "threshold_timeframe" + t.integer "threshold_shares" + t.integer "threshold_likes" + t.integer "threshold_replies" + t.bigint "fasp_provider_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["fasp_provider_id"], name: "index_fasp_subscriptions_on_fasp_provider_id" + end + create_table "favourites", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false @@ -1322,7 +1347,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_28_095029) do add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade add_foreign_key "custom_filters", "accounts", on_delete: :cascade add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade + add_foreign_key "fasp_backfill_requests", "fasp_providers" add_foreign_key "fasp_debug_callbacks", "fasp_providers" + add_foreign_key "fasp_subscriptions", "fasp_providers" add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade add_foreign_key "featured_tags", "accounts", on_delete: :cascade diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index 534b8ae843..6ec89a1cb6 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -15,4 +15,5 @@ Fabricator(:account) do user { |attrs| attrs[:domain].nil? ? Fabricate.build(:user, account: nil) : nil } uri { |attrs| attrs[:domain].nil? ? '' : "https://#{attrs[:domain]}/users/#{attrs[:username]}" } discoverable true + indexable true end diff --git a/spec/fabricators/fasp/backfill_request_fabricator.rb b/spec/fabricators/fasp/backfill_request_fabricator.rb new file mode 100644 index 0000000000..1dd58b0081 --- /dev/null +++ b/spec/fabricators/fasp/backfill_request_fabricator.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +Fabricator(:fasp_backfill_request, from: 'Fasp::BackfillRequest') do + category 'content' + max_count 10 + cursor nil + fulfilled false + fasp_provider +end diff --git a/spec/fabricators/fasp/subscription_fabricator.rb b/spec/fabricators/fasp/subscription_fabricator.rb new file mode 100644 index 0000000000..6b5fdaaefb --- /dev/null +++ b/spec/fabricators/fasp/subscription_fabricator.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +Fabricator(:fasp_subscription, from: 'Fasp::Subscription') do + category 'content' + subscription_type 'lifecycle' + max_batch_size 10 + fasp_provider +end diff --git a/spec/models/concerns/account/fasp_concern_spec.rb b/spec/models/concerns/account/fasp_concern_spec.rb new file mode 100644 index 0000000000..0434689bff --- /dev/null +++ b/spec/models/concerns/account/fasp_concern_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Account::FaspConcern, feature: :fasp do + describe '#create' do + let(:discoverable_attributes) do + Fabricate.attributes_for(:account).except('user_id') + end + let(:undiscoverable_attributes) do + discoverable_attributes.merge('discoverable' => false) + end + + context 'when account is discoverable' do + it 'queues a job to notify provider' do + Account.create(discoverable_attributes) + + expect(Fasp::AnnounceAccountLifecycleEventWorker).to have_enqueued_sidekiq_job + end + end + + context 'when account is not discoverable' do + it 'does not queue a job' do + Account.create(undiscoverable_attributes) + + expect(Fasp::AnnounceAccountLifecycleEventWorker).to_not have_enqueued_sidekiq_job + end + end + end + + describe '#update' do + before do + # Create account and clear sidekiq queue so we only catch + # jobs queued as part of the update + account + Sidekiq::Worker.clear_all + end + + context 'when account is discoverable' do + let(:account) { Fabricate(:account, domain: 'example.com') } + + it 'queues a job to notify provider' do + expect { account.touch }.to enqueue_sidekiq_job(Fasp::AnnounceAccountLifecycleEventWorker) + end + end + + context 'when account was discoverable before' do + let(:account) { Fabricate(:account, domain: 'example.com') } + + it 'queues a job to notify provider' do + expect do + account.update(discoverable: false) + end.to enqueue_sidekiq_job(Fasp::AnnounceAccountLifecycleEventWorker) + end + end + + context 'when account has not been discoverable' do + let(:account) { Fabricate(:account, domain: 'example.com', discoverable: false) } + + it 'does not queue a job' do + expect { account.touch }.to_not enqueue_sidekiq_job(Fasp::AnnounceAccountLifecycleEventWorker) + end + end + end + + describe '#destroy' do + context 'when account is discoverable' do + let(:account) { Fabricate(:account, domain: 'example.com') } + + it 'queues a job to notify provider' do + expect { account.destroy }.to enqueue_sidekiq_job(Fasp::AnnounceAccountLifecycleEventWorker) + end + end + + context 'when account is not discoverable' do + let(:account) { Fabricate(:account, domain: 'example.com', discoverable: false) } + + it 'does not queue a job' do + expect { account.destroy }.to_not enqueue_sidekiq_job(Fasp::AnnounceAccountLifecycleEventWorker) + end + end + end +end diff --git a/spec/models/concerns/favourite/fasp_concern_spec.rb b/spec/models/concerns/favourite/fasp_concern_spec.rb new file mode 100644 index 0000000000..a56618f1f2 --- /dev/null +++ b/spec/models/concerns/favourite/fasp_concern_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Favourite::FaspConcern, feature: :fasp do + describe '#create' do + it 'queues a job to notify provider' do + expect { Fabricate(:favourite) }.to enqueue_sidekiq_job(Fasp::AnnounceTrendWorker) + end + end +end diff --git a/spec/models/concerns/status/fasp_concern_spec.rb b/spec/models/concerns/status/fasp_concern_spec.rb new file mode 100644 index 0000000000..717a2bbe1a --- /dev/null +++ b/spec/models/concerns/status/fasp_concern_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Status::FaspConcern, feature: :fasp do + describe '#create' do + context 'when account is indexable' do + let(:account) { Fabricate(:account, domain: 'example.com') } + + context 'when status is public' do + it 'queues a job to notify provider of new status' do + expect do + Fabricate(:status, account:) + end.to enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + + context 'when status is not public' do + it 'does not queue a job' do + expect do + Fabricate(:status, account:, visibility: :unlisted) + end.to_not enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + + context 'when status is in reply to another' do + it 'queues a job to notify provider of possible trend' do + parent = Fabricate(:status) + expect do + Fabricate(:status, account:, thread: parent) + end.to enqueue_sidekiq_job(Fasp::AnnounceTrendWorker) + end + end + + context 'when status is a reblog of another' do + it 'queues a job to notify provider of possible trend' do + original = Fabricate(:status, account:) + expect do + Fabricate(:status, account:, reblog: original) + end.to enqueue_sidekiq_job(Fasp::AnnounceTrendWorker) + end + end + end + + context 'when account is not indexable' do + let(:account) { Fabricate(:account, indexable: false) } + + it 'does not queue a job' do + expect do + Fabricate(:status, account:) + end.to_not enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + end + + describe '#update' do + before do + # Create status and clear sidekiq queues to only catch + # jobs queued due to the update + status + Sidekiq::Worker.clear_all + end + + context 'when account is indexable' do + let(:account) { Fabricate(:account, domain: 'example.com') } + let(:status) { Fabricate(:status, account:, visibility:) } + + context 'when status is public' do + let(:visibility) { :public } + + it 'queues a job to notify provider' do + expect { status.touch }.to enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + + context 'when status has not been public' do + let(:visibility) { :unlisted } + + it 'does not queue a job' do + expect do + status.touch + end.to_not enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + end + + context 'when account is not indexable' do + let(:account) { Fabricate(:account, domain: 'example.com', indexable: false) } + let(:status) { Fabricate(:status, account:) } + + it 'does not queue a job' do + expect { status.touch }.to_not enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + end + + describe '#destroy' do + let(:status) { Fabricate(:status, account:) } + + before do + # Create status and clear sidekiq queues to only catch + # jobs queued due to the update + status + Sidekiq::Worker.clear_all + end + + context 'when account is indexable' do + let(:account) { Fabricate(:account, domain: 'example.com') } + + it 'queues a job to notify provider' do + expect { status.destroy }.to enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + + context 'when account is not indexable' do + let(:account) { Fabricate(:account, domain: 'example.com', indexable: false) } + + it 'does not queue a job' do + expect { status.destroy }.to_not enqueue_sidekiq_job(Fasp::AnnounceContentLifecycleEventWorker) + end + end + end +end diff --git a/spec/models/fasp/backfill_request_spec.rb b/spec/models/fasp/backfill_request_spec.rb new file mode 100644 index 0000000000..5ea820db1e --- /dev/null +++ b/spec/models/fasp/backfill_request_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::BackfillRequest do + describe '#next_objects' do + let(:account) { Fabricate(:account) } + let!(:statuses) { Fabricate.times(3, :status, account:).sort_by(&:id) } + + context 'with a new backfill request' do + subject { Fabricate(:fasp_backfill_request, max_count: 2) } + + it 'returns the newest two statuses' do + expect(subject.next_objects).to eq [statuses[2], statuses[1]] + end + end + + context 'with cursor set to second newest status' do + subject do + Fabricate(:fasp_backfill_request, max_count: 2, cursor: statuses[1].id) + end + + it 'returns the oldest status' do + expect(subject.next_objects).to eq [statuses[0]] + end + end + + context 'when all statuses are not `indexable`' do + subject { Fabricate(:fasp_backfill_request) } + + let(:account) { Fabricate(:account, indexable: false) } + + it 'returns no statuses' do + expect(subject.next_objects).to be_empty + end + end + end + + describe '#next_uris' do + subject { Fabricate(:fasp_backfill_request) } + + let(:statuses) { Fabricate.times(2, :status) } + + it 'returns uris of the next objects' do + uris = statuses.map(&:uri) + + expect(subject.next_uris).to match_array(uris) + end + end + + describe '#more_objects_available?' do + subject { Fabricate(:fasp_backfill_request, max_count: 2) } + + context 'when more objects are available' do + before { Fabricate.times(3, :status) } + + it 'returns `true`' do + expect(subject.more_objects_available?).to be true + end + end + + context 'when no more objects are available' do + before { Fabricate.times(2, :status) } + + it 'returns `false`' do + expect(subject.more_objects_available?).to be false + end + end + end + + describe '#advance!' do + subject { Fabricate(:fasp_backfill_request, max_count: 2) } + + context 'when more objects are available' do + before { Fabricate.times(3, :status) } + + it 'updates `cursor`' do + expect { subject.advance! }.to change(subject, :cursor) + expect(subject).to be_persisted + end + end + + context 'when no more objects are available' do + before { Fabricate.times(2, :status) } + + it 'sets `fulfilled` to `true`' do + expect { subject.advance! }.to change(subject, :fulfilled) + .from(false).to(true) + expect(subject).to be_persisted + end + end + end +end diff --git a/spec/models/fasp/subscription_spec.rb b/spec/models/fasp/subscription_spec.rb new file mode 100644 index 0000000000..d51759d48f --- /dev/null +++ b/spec/models/fasp/subscription_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::Subscription do + describe '#threshold=' do + subject { described_class.new } + + it 'allows setting all threshold values at once' do + subject.threshold = { + 'timeframe' => 30, + 'shares' => 5, + 'likes' => 8, + 'replies' => 7, + } + + expect(subject.threshold_timeframe).to eq 30 + expect(subject.threshold_shares).to eq 5 + expect(subject.threshold_likes).to eq 8 + expect(subject.threshold_replies).to eq 7 + end + end + + describe '#timeframe_start' do + subject { described_class.new(threshold_timeframe: 45) } + + it 'returns a Time representing the beginning of the timeframe' do + travel_to Time.zone.local(2025, 4, 7, 16, 40) do + expect(subject.timeframe_start).to eq Time.zone.local(2025, 4, 7, 15, 55) + end + end + end +end diff --git a/spec/requests/api/fasp/data_sharing/v0/backfill_requests_spec.rb b/spec/requests/api/fasp/data_sharing/v0/backfill_requests_spec.rb new file mode 100644 index 0000000000..2d1f1d6417 --- /dev/null +++ b/spec/requests/api/fasp/data_sharing/v0/backfill_requests_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::Fasp::DataSharing::V0::BackfillRequests', feature: :fasp do + include ProviderRequestHelper + + describe 'POST /api/fasp/data_sharing/v0/backfill_requests' do + let(:provider) { Fabricate(:fasp_provider) } + + context 'with valid parameters' do + it 'creates a new backfill request' do + params = { category: 'content', maxCount: 10 } + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_backfill_requests_url, + method: :post, + body: params) + + expect do + post api_fasp_data_sharing_v0_backfill_requests_path, headers:, params:, as: :json + end.to change(Fasp::BackfillRequest, :count).by(1) + expect(response).to have_http_status(201) + end + end + + context 'with invalid parameters' do + it 'does not create a backfill request' do + params = { category: 'unknown', maxCount: 10 } + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_backfill_requests_url, + method: :post, + body: params) + + expect do + post api_fasp_data_sharing_v0_backfill_requests_path, headers:, params:, as: :json + end.to_not change(Fasp::BackfillRequest, :count) + expect(response).to have_http_status(422) + end + end + end +end diff --git a/spec/requests/api/fasp/data_sharing/v0/continuations_spec.rb b/spec/requests/api/fasp/data_sharing/v0/continuations_spec.rb new file mode 100644 index 0000000000..59ab44d0c4 --- /dev/null +++ b/spec/requests/api/fasp/data_sharing/v0/continuations_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::Fasp::DataSharing::V0::Continuations', feature: :fasp do + include ProviderRequestHelper + + describe 'POST /api/fasp/data_sharing/v0/backfill_requests/:id/continuations' do + let(:backfill_request) { Fabricate(:fasp_backfill_request) } + let(:provider) { backfill_request.fasp_provider } + + it 'queues a job to continue the given backfill request' do + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_backfill_request_continuation_url(backfill_request), + method: :post) + + post api_fasp_data_sharing_v0_backfill_request_continuation_path(backfill_request), headers:, as: :json + expect(response).to have_http_status(204) + expect(Fasp::BackfillWorker).to have_enqueued_sidekiq_job(backfill_request.id) + end + end +end diff --git a/spec/requests/api/fasp/data_sharing/v0/event_subscriptions_spec.rb b/spec/requests/api/fasp/data_sharing/v0/event_subscriptions_spec.rb new file mode 100644 index 0000000000..beab9e326f --- /dev/null +++ b/spec/requests/api/fasp/data_sharing/v0/event_subscriptions_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::Fasp::DataSharing::V0::EventSubscriptions', feature: :fasp do + include ProviderRequestHelper + + describe 'POST /api/fasp/data_sharing/v0/event_subscriptions' do + let(:provider) { Fabricate(:fasp_provider) } + + context 'with valid parameters' do + it 'creates a new subscription' do + params = { category: 'content', subscriptionType: 'lifecycle', maxBatchSize: 10 } + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_event_subscriptions_url, + method: :post, + body: params) + + expect do + post api_fasp_data_sharing_v0_event_subscriptions_path, headers:, params:, as: :json + end.to change(Fasp::Subscription, :count).by(1) + expect(response).to have_http_status(201) + end + end + + context 'with invalid parameters' do + it 'does not create a subscription' do + params = { category: 'unknown' } + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_event_subscriptions_url, + method: :post, + body: params) + + expect do + post api_fasp_data_sharing_v0_event_subscriptions_path, headers:, params:, as: :json + end.to_not change(Fasp::Subscription, :count) + expect(response).to have_http_status(422) + end + end + end + + describe 'DELETE /api/fasp/data_sharing/v0/event_subscriptions/:id' do + let(:subscription) { Fabricate(:fasp_subscription) } + let(:provider) { subscription.fasp_provider } + + it 'deletes the subscription' do + headers = request_authentication_headers(provider, + url: api_fasp_data_sharing_v0_event_subscription_url(subscription), + method: :delete) + + expect do + delete api_fasp_data_sharing_v0_event_subscription_path(subscription), headers:, as: :json + end.to change(Fasp::Subscription, :count).by(-1) + expect(response).to have_http_status(204) + end + end +end diff --git a/spec/workers/fasp/announce_account_lifecycle_event_worker_spec.rb b/spec/workers/fasp/announce_account_lifecycle_event_worker_spec.rb new file mode 100644 index 0000000000..0d4a870875 --- /dev/null +++ b/spec/workers/fasp/announce_account_lifecycle_event_worker_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::AnnounceAccountLifecycleEventWorker do + include ProviderRequestHelper + + let(:account_uri) { 'https://masto.example.com/accounts/1' } + let(:subscription) do + Fabricate(:fasp_subscription, category: 'account') + end + let(:provider) { subscription.fasp_provider } + let!(:stubbed_request) do + stub_provider_request(provider, + method: :post, + path: '/data_sharing/v0/announcements', + response_body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'account', + eventType: 'new', + objectUris: [account_uri], + }) + end + + it 'sends the account uri to subscribed providers' do + described_class.new.perform(account_uri, 'new') + + expect(stubbed_request).to have_been_made + end +end diff --git a/spec/workers/fasp/announce_content_lifecycle_event_worker_spec.rb b/spec/workers/fasp/announce_content_lifecycle_event_worker_spec.rb new file mode 100644 index 0000000000..60618607c9 --- /dev/null +++ b/spec/workers/fasp/announce_content_lifecycle_event_worker_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::AnnounceContentLifecycleEventWorker do + include ProviderRequestHelper + + let(:status_uri) { 'https://masto.example.com/status/1' } + let(:subscription) do + Fabricate(:fasp_subscription) + end + let(:provider) { subscription.fasp_provider } + let!(:stubbed_request) do + stub_provider_request(provider, + method: :post, + path: '/data_sharing/v0/announcements', + response_body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'content', + eventType: 'new', + objectUris: [status_uri], + }) + end + + it 'sends the status uri to subscribed providers' do + described_class.new.perform(status_uri, 'new') + + expect(stubbed_request).to have_been_made + end +end diff --git a/spec/workers/fasp/announce_trend_worker_spec.rb b/spec/workers/fasp/announce_trend_worker_spec.rb new file mode 100644 index 0000000000..799d8a8f48 --- /dev/null +++ b/spec/workers/fasp/announce_trend_worker_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::AnnounceTrendWorker do + include ProviderRequestHelper + + let(:status) { Fabricate(:status) } + let(:subscription) do + Fabricate(:fasp_subscription, + category: 'content', + subscription_type: 'trends', + threshold_timeframe: 15, + threshold_likes: 2) + end + let(:provider) { subscription.fasp_provider } + let!(:stubbed_request) do + stub_provider_request(provider, + method: :post, + path: '/data_sharing/v0/announcements', + response_body: { + source: { + subscription: { + id: subscription.id.to_s, + }, + }, + category: 'content', + eventType: 'trending', + objectUris: [status.uri], + }) + end + + context 'when the configured threshold is met' do + before do + Fabricate.times(2, :favourite, status:) + end + + it 'sends the account uri to subscribed providers' do + described_class.new.perform(status.id, 'favourite') + + expect(stubbed_request).to have_been_made + end + end + + context 'when the configured threshold is not met' do + it 'does not notify any provider' do + described_class.new.perform(status.id, 'favourite') + + expect(stubbed_request).to_not have_been_made + end + end +end diff --git a/spec/workers/fasp/backfill_worker_spec.rb b/spec/workers/fasp/backfill_worker_spec.rb new file mode 100644 index 0000000000..43734e02ba --- /dev/null +++ b/spec/workers/fasp/backfill_worker_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Fasp::BackfillWorker do + include ProviderRequestHelper + + let(:backfill_request) { Fabricate(:fasp_backfill_request) } + let(:provider) { backfill_request.fasp_provider } + let(:status) { Fabricate(:status) } + let!(:stubbed_request) do + stub_provider_request(provider, + method: :post, + path: '/data_sharing/v0/announcements', + response_body: { + source: { + backfillRequest: { + id: backfill_request.id.to_s, + }, + }, + category: 'content', + objectUris: [status.uri], + moreObjectsAvailable: false, + }) + end + + it 'sends status uri to provider that requested backfill' do + described_class.new.perform(backfill_request.id) + + expect(stubbed_request).to have_been_made + end +end From c4f47adb49663f24ee80d9174ab24431a8c165c0 Mon Sep 17 00:00:00 2001 From: Echo Date: Fri, 16 May 2025 15:26:12 +0200 Subject: [PATCH 025/106] Convert from Webpack to Vite (#34450) Co-authored-by: Renaud Chaput --- .devcontainer/compose.yaml | 4 +- .github/renovate.json5 | 17 - .github/workflows/test-ruby.yml | 4 +- .gitignore | 3 +- .prettierignore | 5 +- Gemfile | 3 +- Gemfile.lock | 18 +- Procfile.dev | 2 +- app/helpers/routing_helper.rb | 4 +- app/helpers/theme_helper.rb | 8 +- app/javascript/entrypoints/admin.tsx | 3 +- app/javascript/entrypoints/application.ts | 9 +- app/javascript/entrypoints/common.ts | 3 + app/javascript/entrypoints/embed.tsx | 4 - app/javascript/entrypoints/error.ts | 1 - app/javascript/entrypoints/inert.ts | 4 - app/javascript/entrypoints/mailer.ts | 3 - app/javascript/entrypoints/public-path.ts | 23 - app/javascript/entrypoints/public.tsx | 9 +- .../entrypoints/remote_interaction_helper.ts | 2 - app/javascript/entrypoints/share.tsx | 4 - app/javascript/entrypoints/sign_up.ts | 1 - .../mastodon/{common.js => common.ts} | 2 - .../mastodon/components/status_action_bar.jsx | 2 +- .../features/alt_text_modal/index.tsx | 15 +- ...compressed.d.ts => emoji_compressed.d.mts} | 0 ...oji_compressed.js => emoji_compressed.mjs} | 75 +- .../features/emoji/emoji_mart_data_light.ts | 8 +- .../emoji/emoji_unicode_mapping_light.ts | 6 +- .../features/emoji/unicode_to_filename.js | 2 +- .../features/emoji/unicode_to_unified_name.js | 2 +- .../features/ui/util/async-components.js | 118 +- .../mastodon/load_keyboard_extensions.js | 2 +- .../mastodon/locales/load_locale.ts | 19 +- .../mastodon/{main.jsx => main.tsx} | 29 +- app/javascript/mastodon/performance.js | 13 - app/javascript/mastodon/polyfills/index.ts | 5 +- app/javascript/mastodon/polyfills/intl.ts | 8 +- .../service_worker/{entry.js => sw.js} | 4 +- .../service_worker/web_push_locales.js | 41 - .../service_worker/web_push_notifications.js | 6 +- app/javascript/mastodon/utils/environment.ts | 8 +- .../styles/{ => entrypoints}/inert.scss | 0 .../styles/{ => entrypoints}/mailer.scss | 10 +- app/javascript/styles/fonts/inter.scss | 2 +- app/javascript/styles/mastodon/forms.scss | 2 +- app/javascript/types/image.d.ts | 19 +- app/javascript/types/virtual.d.ts | 59 + app/views/auth/sessions/two_factor.html.haml | 2 +- app/views/auth/setup/show.html.haml | 2 +- app/views/layouts/admin.html.haml | 4 +- app/views/layouts/application.html.haml | 8 +- app/views/layouts/auth.html.haml | 2 +- app/views/layouts/embedded.html.haml | 7 +- app/views/layouts/error.html.haml | 5 +- app/views/layouts/helper_frame.html.haml | 2 - app/views/layouts/mailer.html.haml | 2 +- app/views/layouts/modal.html.haml | 2 +- app/views/media/player.html.haml | 2 +- app/views/relationships/show.html.haml | 2 +- .../remote_interaction_helper/index.html.haml | 2 +- .../webauthn_credentials/new.html.haml | 2 +- app/views/shared/_web_app.html.haml | 5 +- app/views/shares/show.html.haml | 2 +- babel.config.js | 80 - bin/vite | 27 + bin/webpack | 19 - bin/webpack-dev-server | 19 - config/application.rb | 3 +- .../initializers/content_security_policy.rb | 7 +- config/vite.json | 12 +- config/vite/plugin-emoji-compressed.ts | 27 + config/vite/plugin-sw-locales.ts | 102 + config/webpack/configuration.js | 28 - config/webpack/development.js | 62 - config/webpack/production.js | 74 - config/webpack/rules/babel.js | 28 - config/webpack/rules/css.js | 28 - config/webpack/rules/file.js | 22 - config/webpack/rules/index.js | 16 - config/webpack/rules/mark.js | 8 - config/webpack/rules/material_icons.js | 14 - config/webpack/rules/tesseract.js | 13 - config/webpack/shared.js | 113 - config/webpacker.yml | 94 - eslint.config.mjs | 9 +- lib/premailer_bundled_asset_strategy.rb | 31 +- lib/tasks/assets.rake | 4 +- lib/tasks/webpacker.rake | 34 - lib/vite_ruby/sri_extensions.rb | 124 + lib/webpacker/helper_extensions.rb | 27 - lib/webpacker/manifest_extensions.rb | 17 - package.json | 61 +- postcss.config.js | 15 - spec/helpers/theme_helper_spec.rb | 2 +- stylelint.config.js | 2 +- tsconfig.json | 8 +- vite.config.mts | 198 +- vitest.config.mts | 26 + yarn.lock | 7459 +++-------------- 100 files changed, 2031 insertions(+), 7424 deletions(-) create mode 100644 app/javascript/entrypoints/common.ts delete mode 100644 app/javascript/entrypoints/inert.ts delete mode 100644 app/javascript/entrypoints/mailer.ts delete mode 100644 app/javascript/entrypoints/public-path.ts rename app/javascript/mastodon/{common.js => common.ts} (67%) rename app/javascript/mastodon/features/emoji/{emoji_compressed.d.ts => emoji_compressed.d.mts} (100%) rename app/javascript/mastodon/features/emoji/{emoji_compressed.js => emoji_compressed.mjs} (58%) rename app/javascript/mastodon/{main.jsx => main.tsx} (58%) rename app/javascript/mastodon/service_worker/{entry.js => sw.js} (95%) delete mode 100644 app/javascript/mastodon/service_worker/web_push_locales.js rename app/javascript/styles/{ => entrypoints}/inert.scss (100%) rename app/javascript/styles/{ => entrypoints}/mailer.scss (97%) create mode 100644 app/javascript/types/virtual.d.ts delete mode 100644 babel.config.js create mode 100755 bin/vite delete mode 100755 bin/webpack delete mode 100755 bin/webpack-dev-server create mode 100644 config/vite/plugin-emoji-compressed.ts create mode 100644 config/vite/plugin-sw-locales.ts delete mode 100644 config/webpack/configuration.js delete mode 100644 config/webpack/development.js delete mode 100644 config/webpack/production.js delete mode 100644 config/webpack/rules/babel.js delete mode 100644 config/webpack/rules/css.js delete mode 100644 config/webpack/rules/file.js delete mode 100644 config/webpack/rules/index.js delete mode 100644 config/webpack/rules/mark.js delete mode 100644 config/webpack/rules/material_icons.js delete mode 100644 config/webpack/rules/tesseract.js delete mode 100644 config/webpack/shared.js delete mode 100644 config/webpacker.yml delete mode 100644 lib/tasks/webpacker.rake create mode 100644 lib/vite_ruby/sri_extensions.rb delete mode 100644 lib/webpacker/helper_extensions.rb delete mode 100644 lib/webpacker/manifest_extensions.rb delete mode 100644 postcss.config.js create mode 100644 vitest.config.mts diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml index 5da1ec3a24..ced5ecfe88 100644 --- a/.devcontainer/compose.yaml +++ b/.devcontainer/compose.yaml @@ -9,6 +9,7 @@ services: environment: RAILS_ENV: development NODE_ENV: development + VITE_RUBY_HOST: 0.0.0.0 BIND: 0.0.0.0 BOOTSNAP_CACHE_DIR: /tmp REDIS_HOST: redis @@ -22,11 +23,12 @@ services: ES_PORT: '9200' LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000 LOCAL_DOMAIN: ${LOCAL_DOMAIN:-localhost:3000} + VITE_DEV_SERVER_PUBLIC: ${VITE_DEV_SERVER_PUBLIC:-localhost:3036} # Overrides default command so things don't shut down after the process ends. command: sleep infinity ports: - '3000:3000' - - '3035:3035' + - '3036:3036' - '4000:4000' networks: - external_network diff --git a/.github/renovate.json5 b/.github/renovate.json5 index e638b9c548..0cf15040cd 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -25,23 +25,6 @@ 'tesseract.js', // Requires code changes 'react-hotkeys', // Requires code changes - // Requires Webpacker upgrade or replacement - '@svgr/webpack', - '@types/webpack', - 'babel-loader', - 'compression-webpack-plugin', - 'css-loader', - 'imports-loader', - 'mini-css-extract-plugin', - 'postcss-loader', - 'sass-loader', - 'terser-webpack-plugin', - 'webpack', - 'webpack-assets-manifest', - 'webpack-bundle-analyzer', - 'webpack-dev-server', - 'webpack-cli', - // react-router: Requires manual upgrade 'history', 'react-router-dom', diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 1f7f8f93a8..2fa28a587c 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -49,7 +49,7 @@ jobs: public/assets public/packs public/packs-test - tmp/cache/webpacker + tmp/cache/vite key: ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }} restore-keys: | ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }} @@ -63,7 +63,7 @@ jobs: - name: Archive asset artifacts run: | - tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* + tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tmp/cache/vite/last-build*.json - uses: actions/upload-artifact@v4 if: matrix.mode == 'test' diff --git a/.gitignore b/.gitignore index a74317bd7d..b4fb2c946b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,11 @@ /public/system /public/assets /public/packs +/public/packs-dev /public/packs-test .env .env.production -/node_modules/ +node_modules/ /build/ # Ignore Vagrant files diff --git a/.prettierignore b/.prettierignore index 07f90e4bbd..8f16e731c8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -18,10 +18,6 @@ !/log/.keep /tmp /coverage -/public/system -/public/assets -/public/packs -/public/packs-test .env .env.production .env.development @@ -60,6 +56,7 @@ docker-compose.override.yml /public/packs /public/packs-test /public/system +/public/vite* # Ignore emoji map file /app/javascript/mastodon/features/emoji/emoji_map.json diff --git a/Gemfile b/Gemfile index 819ed6bf73..e2d1a66751 100644 --- a/Gemfile +++ b/Gemfile @@ -95,7 +95,6 @@ gem 'tty-prompt', '~> 0.23', require: false gem 'twitter-text', '~> 3.1.0' gem 'tzinfo-data', '~> 1.2023' gem 'webauthn', '~> 3.0' -gem 'webpacker', '~> 5.4' gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913' gem 'json-ld' @@ -230,3 +229,5 @@ gem 'rubyzip', '~> 2.3' gem 'hcaptcha', '~> 7.1' gem 'mail', '~> 2.8' + +gem 'vite_rails', '~> 3.0.19' diff --git a/Gemfile.lock b/Gemfile.lock index b013f3d062..6f22891346 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,6 +203,7 @@ GEM railties (>= 5) dotenv (3.1.8) drb (2.2.1) + dry-cli (1.2.0) elasticsearch (7.17.11) elasticsearch-api (= 7.17.11) elasticsearch-transport (= 7.17.11) @@ -806,7 +807,6 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - semantic_range (3.1.0) shoulda-matchers (6.5.0) activesupport (>= 5.2.0) sidekiq (6.5.12) @@ -892,6 +892,15 @@ GEM validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix + vite_rails (3.0.19) + railties (>= 5.1, < 9) + vite_ruby (~> 3.0, >= 3.2.2) + vite_ruby (3.9.2) + dry-cli (>= 0.7, < 2) + logger (~> 1.6) + mutex_m + rack-proxy (~> 0.6, >= 0.6.1) + zeitwerk (~> 2.2) warden (1.2.9) rack (>= 2.0.9) webauthn (3.4.0) @@ -910,11 +919,6 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webpacker (5.4.4) - activesupport (>= 5.2) - rack-proxy (>= 0.6.1) - railties (>= 5.2) - semantic_range (>= 2.3.0) webrick (1.9.1) websocket (1.2.11) websocket-driver (0.7.7) @@ -1078,9 +1082,9 @@ DEPENDENCIES tty-prompt (~> 0.23) twitter-text (~> 3.1.0) tzinfo-data (~> 1.2023) + vite_rails (~> 3.0.19) webauthn (~> 3.0) webmock (~> 3.18) - webpacker (~> 5.4) webpush! xorcist (~> 1.1) diff --git a/Procfile.dev b/Procfile.dev index f81333b04c..99de0616c5 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,4 @@ web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq stream: env PORT=4000 yarn workspace @mastodon/streaming start -webpack: bin/webpack-dev-server +vite: yarn dev diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb index 22efc5f092..1ff8b992c3 100644 --- a/app/helpers/routing_helper.rb +++ b/app/helpers/routing_helper.rb @@ -4,7 +4,7 @@ module RoutingHelper extend ActiveSupport::Concern include ActionView::Helpers::AssetTagHelper - include Webpacker::Helper + include ViteRails::TagHelpers included do include Rails.application.routes.url_helpers @@ -25,7 +25,7 @@ module RoutingHelper end def frontend_asset_path(source, **) - asset_pack_path("media/#{source}", **) + vite_asset_path(source, **) end def frontend_asset_url(source, **) diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index cda380b3bc..074741f4a2 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -4,11 +4,13 @@ module ThemeHelper def theme_style_tags(theme) if theme == 'system' ''.html_safe.tap do |tags| - tags << stylesheet_pack_tag('mastodon-light', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') - tags << stylesheet_pack_tag('default', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag('styles/mastodon-light.scss', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag('styles/application.scss', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') end + elsif theme == 'default' + vite_stylesheet_tag 'styles/application.scss', media: 'all', crossorigin: 'anonymous' else - stylesheet_pack_tag theme, media: 'all', crossorigin: 'anonymous' + vite_stylesheet_tag "styles/#{theme}.scss", media: 'all', crossorigin: 'anonymous' end end diff --git a/app/javascript/entrypoints/admin.tsx b/app/javascript/entrypoints/admin.tsx index 225cb16330..a60778f0c0 100644 --- a/app/javascript/entrypoints/admin.tsx +++ b/app/javascript/entrypoints/admin.tsx @@ -1,4 +1,3 @@ -import './public-path'; import { createRoot } from 'react-dom/client'; import Rails from '@rails/ujs'; @@ -273,7 +272,7 @@ async function mountReactComponent(element: Element) { ); const { default: Component } = (await import( - `@/mastodon/components/admin/${componentName}` + `@/mastodon/components/admin/${componentName}.jsx` )) as { default: React.ComponentType }; const root = createRoot(element); diff --git a/app/javascript/entrypoints/application.ts b/app/javascript/entrypoints/application.ts index 1087b1c4cb..6aeb716f76 100644 --- a/app/javascript/entrypoints/application.ts +++ b/app/javascript/entrypoints/application.ts @@ -1,11 +1,6 @@ -import './public-path'; +import { loadLocale } from 'mastodon/locales'; import main from 'mastodon/main'; - -import { start } from '../mastodon/common'; -import { loadLocale } from '../mastodon/locales'; -import { loadPolyfills } from '../mastodon/polyfills'; - -start(); +import { loadPolyfills } from 'mastodon/polyfills'; loadPolyfills() .then(loadLocale) diff --git a/app/javascript/entrypoints/common.ts b/app/javascript/entrypoints/common.ts new file mode 100644 index 0000000000..fd9839e83c --- /dev/null +++ b/app/javascript/entrypoints/common.ts @@ -0,0 +1,3 @@ +import { start } from 'mastodon/common'; + +start(); diff --git a/app/javascript/entrypoints/embed.tsx b/app/javascript/entrypoints/embed.tsx index 6c091e4d07..067396b1f1 100644 --- a/app/javascript/entrypoints/embed.tsx +++ b/app/javascript/entrypoints/embed.tsx @@ -1,15 +1,11 @@ -import './public-path'; import { createRoot } from 'react-dom/client'; import { afterInitialRender } from 'mastodon/hooks/useRenderSignal'; -import { start } from '../mastodon/common'; import { Status } from '../mastodon/features/standalone/status'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; -start(); - function loaded() { const mountNode = document.getElementById('mastodon-status'); diff --git a/app/javascript/entrypoints/error.ts b/app/javascript/entrypoints/error.ts index db68484f3a..af9d484de1 100644 --- a/app/javascript/entrypoints/error.ts +++ b/app/javascript/entrypoints/error.ts @@ -1,4 +1,3 @@ -import './public-path'; import ready from '../mastodon/ready'; ready(() => { diff --git a/app/javascript/entrypoints/inert.ts b/app/javascript/entrypoints/inert.ts deleted file mode 100644 index 7c04a97faf..0000000000 --- a/app/javascript/entrypoints/inert.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* Placeholder file to have `inert.scss` compiled by Webpack - This is used by the `wicg-inert` polyfill */ - -import '../styles/inert.scss'; diff --git a/app/javascript/entrypoints/mailer.ts b/app/javascript/entrypoints/mailer.ts deleted file mode 100644 index a2ad5e73ac..0000000000 --- a/app/javascript/entrypoints/mailer.ts +++ /dev/null @@ -1,3 +0,0 @@ -import '../styles/mailer.scss'; - -require.context('../icons'); diff --git a/app/javascript/entrypoints/public-path.ts b/app/javascript/entrypoints/public-path.ts deleted file mode 100644 index ac4b9355b9..0000000000 --- a/app/javascript/entrypoints/public-path.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Dynamically set webpack's loading path depending on a meta header, in order -// to share the same assets regardless of instance configuration. -// See https://webpack.js.org/guides/public-path/#on-the-fly - -function removeOuterSlashes(string: string) { - return string.replace(/^\/*/, '').replace(/\/*$/, ''); -} - -function formatPublicPath(host = '', path = '') { - let formattedHost = removeOuterSlashes(host); - if (formattedHost && !/^http/i.test(formattedHost)) { - formattedHost = `//${formattedHost}`; - } - const formattedPath = removeOuterSlashes(path); - return `${formattedHost}/${formattedPath}/`; -} - -const cdnHost = document.querySelector('meta[name=cdn-host]'); - -__webpack_public_path__ = formatPublicPath( - cdnHost ? cdnHost.content : '', - process.env.PUBLIC_OUTPUT_PATH, -); diff --git a/app/javascript/entrypoints/public.tsx b/app/javascript/entrypoints/public.tsx index 9374d6b2d1..0970fc585e 100644 --- a/app/javascript/entrypoints/public.tsx +++ b/app/javascript/entrypoints/public.tsx @@ -1,7 +1,5 @@ import { createRoot } from 'react-dom/client'; -import './public-path'; - import { IntlMessageFormat } from 'intl-messageformat'; import type { MessageDescriptor, PrimitiveType } from 'react-intl'; import { defineMessages } from 'react-intl'; @@ -10,7 +8,6 @@ import Rails from '@rails/ujs'; import axios from 'axios'; import { throttle } from 'lodash'; -import { start } from '../mastodon/common'; import { timeAgoString } from '../mastodon/components/relative_timestamp'; import emojify from '../mastodon/features/emoji/emoji'; import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions'; @@ -20,8 +17,6 @@ import ready from '../mastodon/ready'; import 'cocoon-js-vanilla'; -start(); - const messages = defineMessages({ usernameTaken: { id: 'username.taken', @@ -153,9 +148,7 @@ function loaded() { const reactComponents = document.querySelectorAll('[data-component]'); if (reactComponents.length > 0) { - import( - /* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container' - ) + import('../mastodon/containers/media_container') .then(({ default: MediaContainer }) => { reactComponents.forEach((component) => { Array.from(component.children).forEach((child) => { diff --git a/app/javascript/entrypoints/remote_interaction_helper.ts b/app/javascript/entrypoints/remote_interaction_helper.ts index 419571c896..f50203747d 100644 --- a/app/javascript/entrypoints/remote_interaction_helper.ts +++ b/app/javascript/entrypoints/remote_interaction_helper.ts @@ -8,8 +8,6 @@ and performs no other task. */ -import './public-path'; - import axios from 'axios'; interface JRDLink { diff --git a/app/javascript/entrypoints/share.tsx b/app/javascript/entrypoints/share.tsx index 7926250851..eb2d8eb673 100644 --- a/app/javascript/entrypoints/share.tsx +++ b/app/javascript/entrypoints/share.tsx @@ -1,13 +1,9 @@ -import './public-path'; import { createRoot } from 'react-dom/client'; -import { start } from '../mastodon/common'; import ComposeContainer from '../mastodon/containers/compose_container'; import { loadPolyfills } from '../mastodon/polyfills'; import ready from '../mastodon/ready'; -start(); - function loaded() { const mountNode = document.getElementById('mastodon-compose'); diff --git a/app/javascript/entrypoints/sign_up.ts b/app/javascript/entrypoints/sign_up.ts index 87100be56d..09884476ec 100644 --- a/app/javascript/entrypoints/sign_up.ts +++ b/app/javascript/entrypoints/sign_up.ts @@ -1,4 +1,3 @@ -import './public-path'; import axios from 'axios'; import ready from '../mastodon/ready'; diff --git a/app/javascript/mastodon/common.js b/app/javascript/mastodon/common.ts similarity index 67% rename from app/javascript/mastodon/common.js rename to app/javascript/mastodon/common.ts index c61e02250c..e621a24e39 100644 --- a/app/javascript/mastodon/common.js +++ b/app/javascript/mastodon/common.ts @@ -1,8 +1,6 @@ import Rails from '@rails/ujs'; export function start() { - require.context('../images/', true, /\.(jpg|png|svg)$/); - try { Rails.start(); } catch { diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx index 25116d19b0..ec5a9780cb 100644 --- a/app/javascript/mastodon/components/status_action_bar.jsx +++ b/app/javascript/mastodon/components/status_action_bar.jsx @@ -9,7 +9,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg'; +import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react'; import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react'; import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx index 08e4a8917c..f24a3b6f70 100644 --- a/app/javascript/mastodon/features/alt_text_modal/index.tsx +++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx @@ -15,10 +15,6 @@ import type { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { useSpring, animated } from '@react-spring/web'; import Textarea from 'react-textarea-autosize'; import { length } from 'stringz'; -// eslint-disable-next-line import/extensions -import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js'; -// eslint-disable-next-line import/no-extraneous-dependencies -import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js'; import { showAlertForError } from 'mastodon/actions/alerts'; import { uploadThumbnail } from 'mastodon/actions/compose'; @@ -350,9 +346,15 @@ export const AltTextModal = forwardRef>( fetchTesseract() .then(async ({ createWorker }) => { + const [tesseractWorkerPath, tesseractCorePath] = await Promise.all([ + // eslint-disable-next-line import/extensions + import('tesseract.js/dist/worker.min.js?url'), + // eslint-disable-next-line import/no-extraneous-dependencies + import('tesseract.js-core/tesseract-core.wasm.js?url'), + ]); const worker = await createWorker('eng', 1, { - workerPath: tesseractWorkerPath as string, - corePath: tesseractCorePath as string, + workerPath: tesseractWorkerPath.default, + corePath: tesseractCorePath.default, langPath: `${assetHost}/ocr/lang-data`, cacheMethod: 'write', }); @@ -501,5 +503,4 @@ export const AltTextModal = forwardRef>( ); }, ); - AltTextModal.displayName = 'AltTextModal'; diff --git a/app/javascript/mastodon/features/emoji/emoji_compressed.d.ts b/app/javascript/mastodon/features/emoji/emoji_compressed.d.mts similarity index 100% rename from app/javascript/mastodon/features/emoji/emoji_compressed.d.ts rename to app/javascript/mastodon/features/emoji/emoji_compressed.d.mts diff --git a/app/javascript/mastodon/features/emoji/emoji_compressed.js b/app/javascript/mastodon/features/emoji/emoji_compressed.mjs similarity index 58% rename from app/javascript/mastodon/features/emoji/emoji_compressed.js rename to app/javascript/mastodon/features/emoji/emoji_compressed.mjs index db406393eb..db8a4bc122 100644 --- a/app/javascript/mastodon/features/emoji/emoji_compressed.js +++ b/app/javascript/mastodon/features/emoji/emoji_compressed.mjs @@ -9,28 +9,27 @@ // to ensure that the prevaled file is regenerated by Babel // version: 4 -const { NimbleEmojiIndex } = require('emoji-mart'); -const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data'); +import { NimbleEmojiIndex } from 'emoji-mart'; +import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data'; - -let data = require('./emoji_data.json'); -const emojiMap = require('./emoji_map.json'); -const { unicodeToFilename } = require('./unicode_to_filename'); -const { unicodeToUnifiedName } = require('./unicode_to_unified_name'); +import data from './emoji_data.json'; +import emojiMap from './emoji_map.json'; +import { unicodeToFilename } from './unicode_to_filename'; +import { unicodeToUnifiedName } from './unicode_to_unified_name'; emojiMartUncompress(data); const emojiMartData = data; const emojiIndex = new NimbleEmojiIndex(emojiMartData); -const excluded = ['®', '©', '™']; -const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿']; -const shortcodeMap = {}; +const excluded = ['®', '©', '™']; +const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿']; +const shortcodeMap = {}; const shortCodesToEmojiData = {}; const emojisWithoutShortCodes = []; -Object.keys(emojiIndex.emojis).forEach(key => { +Object.keys(emojiIndex.emojis).forEach((key) => { let emoji = emojiIndex.emojis[key]; // Emojis with skin tone modifiers are stored like this @@ -41,22 +40,22 @@ Object.keys(emojiIndex.emojis).forEach(key => { shortcodeMap[emoji.native] = emoji.id; }); -const stripModifiers = unicode => { - skinTones.forEach(tone => { +const stripModifiers = (unicode) => { + skinTones.forEach((tone) => { unicode = unicode.replace(tone, ''); }); return unicode; }; -Object.keys(emojiMap).forEach(key => { +Object.keys(emojiMap).forEach((key) => { if (excluded.includes(key)) { delete emojiMap[key]; return; } const normalizedKey = stripModifiers(key); - let shortcode = shortcodeMap[normalizedKey]; + let shortcode = shortcodeMap[normalizedKey]; if (!shortcode) { shortcode = shortcodeMap[normalizedKey + '\uFE0F']; @@ -82,7 +81,7 @@ Object.keys(emojiMap).forEach(key => { } }); -Object.keys(emojiIndex.emojis).forEach(key => { +Object.keys(emojiIndex.emojis).forEach((key) => { let emoji = emojiIndex.emojis[key]; // Emojis with skin tone modifiers are stored like this @@ -94,9 +93,11 @@ Object.keys(emojiIndex.emojis).forEach(key => { let { short_names, search, unified } = emojiMartData.emojis[key]; if (short_names[0] !== key) { - throw new Error('The compressor expects the first short_code to be the ' + - 'key. It may need to be rewritten if the emoji change such that this ' + - 'is no longer the case.'); + throw new Error( + 'The compressor expects the first short_code to be the ' + + 'key. It may need to be rewritten if the emoji change such that this ' + + 'is no longer the case.', + ); } short_names = short_names.slice(1); // first short name can be inferred from the key @@ -117,20 +118,22 @@ Object.keys(emojiIndex.emojis).forEach(key => { // JSON.parse/stringify is to emulate what @preval is doing and avoid any // inconsistent behavior in dev mode -module.exports = JSON.parse(JSON.stringify([ - shortCodesToEmojiData, - /* - * The property `skins` is not found in the current context. - * This could potentially lead to issues when interacting with modules or data structures - * that expect the presence of `skins` property. - * Currently, no definitions or references to `skins` property can be found in: - * - {@link node_modules/emoji-mart/dist/utils/data.js} - * - {@link node_modules/emoji-mart/data/all.json} - * - {@link app/javascript/mastodon/features/emoji/emoji_compressed.d.ts#Skins} - * Future refactorings or updates should consider adding definitions or handling for `skins` property. - */ - emojiMartData.skins, - emojiMartData.categories, - emojiMartData.aliases, - emojisWithoutShortCodes -])); +export default JSON.parse( + JSON.stringify([ + shortCodesToEmojiData, + /* + * The property `skins` is not found in the current context. + * This could potentially lead to issues when interacting with modules or data structures + * that expect the presence of `skins` property. + * Currently, no definitions or references to `skins` property can be found in: + * - {@link node_modules/emoji-mart/dist/utils/data.js} + * - {@link node_modules/emoji-mart/data/all.json} + * - {@link app/javascript/mastodon/features/emoji/emoji_compressed.d.ts#Skins} + * Future refactorings or updates should consider adding definitions or handling for `skins` property. + */ + emojiMartData.skins, + emojiMartData.categories, + emojiMartData.aliases, + emojisWithoutShortCodes, + ]), +); diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts index 8eeb457055..ecdda58513 100644 --- a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts +++ b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.ts @@ -3,9 +3,13 @@ // emojiIndex.search functionality. import type { BaseEmoji } from 'emoji-mart'; import type { Emoji } from 'emoji-mart/dist-es/utils/data'; +// eslint-disable-next-line import/no-unresolved +import emojiCompressed from 'virtual:mastodon-emoji-compressed'; +import type { + Search, + ShortCodesToEmojiData, +} from 'virtual:mastodon-emoji-compressed'; -import type { Search, ShortCodesToEmojiData } from './emoji_compressed'; -import emojiCompressed from './emoji_compressed'; import { unicodeToUnifiedName } from './unicode_to_unified_name'; type Emojis = Record< diff --git a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts index 0a5a4c1d76..283508db58 100644 --- a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts +++ b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.ts @@ -2,11 +2,13 @@ // (i.e. the svg filename) and a shortCode intended to be shown // as a "title" attribute in an HTML element (aka tooltip). +// eslint-disable-next-line import/no-unresolved +import emojiCompressed from 'virtual:mastodon-emoji-compressed'; import type { FilenameData, ShortCodesToEmojiDataKey, -} from './emoji_compressed'; -import emojiCompressed from './emoji_compressed'; +} from 'virtual:mastodon-emoji-compressed'; + import { unicodeToFilename } from './unicode_to_filename'; type UnicodeMapping = Record< diff --git a/app/javascript/mastodon/features/emoji/unicode_to_filename.js b/app/javascript/mastodon/features/emoji/unicode_to_filename.js index c75c4cd7d0..cfe5539c7b 100644 --- a/app/javascript/mastodon/features/emoji/unicode_to_filename.js +++ b/app/javascript/mastodon/features/emoji/unicode_to_filename.js @@ -1,6 +1,6 @@ // taken from: // https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866 -exports.unicodeToFilename = (str) => { +export const unicodeToFilename = (str) => { let result = ''; let charCode = 0; let p = 0; diff --git a/app/javascript/mastodon/features/emoji/unicode_to_unified_name.js b/app/javascript/mastodon/features/emoji/unicode_to_unified_name.js index d29550f122..15f60aa7c3 100644 --- a/app/javascript/mastodon/features/emoji/unicode_to_unified_name.js +++ b/app/javascript/mastodon/features/emoji/unicode_to_unified_name.js @@ -6,7 +6,7 @@ function padLeft(str, num) { return str; } -exports.unicodeToUnifiedName = (str) => { +export const unicodeToUnifiedName = (str) => { let output = ''; for (let i = 0; i < str.length; i += 2) { diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index ec493ae283..d865f7d7a5 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -1,235 +1,235 @@ export function EmojiPicker () { - return import(/* webpackChunkName: "emoji_picker" */'../../emoji/emoji_picker'); + return import('../../emoji/emoji_picker'); } export function Compose () { - return import(/* webpackChunkName: "features/compose" */'../../compose'); + return import('../../compose'); } export function Notifications () { - return import(/* webpackChunkName: "features/notifications" */'../../notifications_v2'); + return import('../../notifications_v2'); } export function HomeTimeline () { - return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline'); + return import('../../home_timeline'); } export function PublicTimeline () { - return import(/* webpackChunkName: "features/public_timeline" */'../../public_timeline'); + return import('../../public_timeline'); } export function CommunityTimeline () { - return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline'); + return import('../../community_timeline'); } export function Firehose () { - return import(/* webpackChunkName: "features/firehose" */'../../firehose'); + return import('../../firehose'); } export function HashtagTimeline () { - return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline'); + return import('../../hashtag_timeline'); } export function DirectTimeline() { - return import(/* webpackChunkName: "features/direct_timeline" */'../../direct_timeline'); + return import('../../direct_timeline'); } export function ListTimeline () { - return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline'); + return import('../../list_timeline'); } export function Lists () { - return import(/* webpackChunkName: "features/lists" */'../../lists'); + return import('../../lists'); } export function Status () { - return import(/* webpackChunkName: "features/status" */'../../status'); + return import('../../status'); } export function GettingStarted () { - return import(/* webpackChunkName: "features/getting_started" */'../../getting_started'); + return import('../../getting_started'); } export function KeyboardShortcuts () { - return import(/* webpackChunkName: "features/keyboard_shortcuts" */'../../keyboard_shortcuts'); + return import('../../keyboard_shortcuts'); } export function PinnedStatuses () { - return import(/* webpackChunkName: "features/pinned_statuses" */'../../pinned_statuses'); + return import('../../pinned_statuses'); } export function AccountTimeline () { - return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline'); + return import('../../account_timeline'); } export function AccountGallery () { - return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery'); + return import('../../account_gallery'); } export function AccountFeatured() { - return import(/* webpackChunkName: "features/account_featured" */'../../account_featured'); + return import('../../account_featured'); } export function Followers () { - return import(/* webpackChunkName: "features/followers" */'../../followers'); + return import('../../followers'); } export function Following () { - return import(/* webpackChunkName: "features/following" */'../../following'); + return import('../../following'); } export function Reblogs () { - return import(/* webpackChunkName: "features/reblogs" */'../../reblogs'); + return import('../../reblogs'); } export function Favourites () { - return import(/* webpackChunkName: "features/favourites" */'../../favourites'); + return import('../../favourites'); } export function FollowRequests () { - return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests'); + return import('../../follow_requests'); } export function FavouritedStatuses () { - return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses'); + return import('../../favourited_statuses'); } export function FollowedTags () { - return import(/* webpackChunkName: "features/followed_tags" */'../../followed_tags'); + return import('../../followed_tags'); } export function BookmarkedStatuses () { - return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses'); + return import('../../bookmarked_statuses'); } export function Blocks () { - return import(/* webpackChunkName: "features/blocks" */'../../blocks'); + return import('../../blocks'); } export function DomainBlocks () { - return import(/* webpackChunkName: "features/domain_blocks" */'../../domain_blocks'); + return import('../../domain_blocks'); } export function Mutes () { - return import(/* webpackChunkName: "features/mutes" */'../../mutes'); + return import('../../mutes'); } export function MuteModal () { - return import(/* webpackChunkName: "modals/mute_modal" */'../components/mute_modal'); + return import('../components/mute_modal'); } export function BlockModal () { - return import(/* webpackChunkName: "modals/block_modal" */'../components/block_modal'); + return import('../components/block_modal'); } export function DomainBlockModal () { - return import(/* webpackChunkName: "modals/domain_block_modal" */'../components/domain_block_modal'); + return import('../components/domain_block_modal'); } export function ReportModal () { - return import(/* webpackChunkName: "modals/report_modal" */'../components/report_modal'); + return import('../components/report_modal'); } export function IgnoreNotificationsModal () { - return import(/* webpackChunkName: "modals/domain_block_modal" */'../components/ignore_notifications_modal'); + return import('../components/ignore_notifications_modal'); } export function MediaGallery () { - return import(/* webpackChunkName: "status/media_gallery" */'../../../components/media_gallery'); + return import('../../../components/media_gallery'); } export function Video () { - return import(/* webpackChunkName: "features/video" */'../../video'); + return import('../../video'); } export function EmbedModal () { - return import(/* webpackChunkName: "modals/embed_modal" */'../components/embed_modal'); + return import('../components/embed_modal'); } export function ListAdder () { - return import(/*webpackChunkName: "features/list_adder" */'../../list_adder'); + return import('../../list_adder'); } export function Tesseract () { - return import(/*webpackChunkName: "tesseract" */'tesseract.js'); + return import('tesseract.js'); } export function Audio () { - return import(/* webpackChunkName: "features/audio" */'../../audio'); + return import('../../audio'); } export function Directory () { - return import(/* webpackChunkName: "features/directory" */'../../directory'); + return import('../../directory'); } export function OnboardingProfile () { - return import(/* webpackChunkName: "features/onboarding" */'../../onboarding/profile'); + return import('../../onboarding/profile'); } export function OnboardingFollows () { - return import(/* webpackChunkName: "features/onboarding" */'../../onboarding/follows'); + return import('../../onboarding/follows'); } export function CompareHistoryModal () { - return import(/*webpackChunkName: "modals/compare_history_modal" */'../components/compare_history_modal'); + return import('../components/compare_history_modal'); } export function Explore () { - return import(/* webpackChunkName: "features/explore" */'../../explore'); + return import('../../explore'); } export function Search () { - return import(/* webpackChunkName: "features/explore" */'../../search'); + return import('../../search'); } export function FilterModal () { - return import(/*webpackChunkName: "modals/filter_modal" */'../components/filter_modal'); + return import('../components/filter_modal'); } export function InteractionModal () { - return import(/*webpackChunkName: "modals/interaction_modal" */'../../interaction_modal'); + return import('../../interaction_modal'); } export function SubscribedLanguagesModal () { - return import(/*webpackChunkName: "modals/subscribed_languages_modal" */'../../subscribed_languages_modal'); + return import('../../subscribed_languages_modal'); } export function ClosedRegistrationsModal () { - return import(/*webpackChunkName: "modals/closed_registrations_modal" */'../../closed_registrations_modal'); + return import('../../closed_registrations_modal'); } export function About () { - return import(/*webpackChunkName: "features/about" */'../../about'); + return import('../../about'); } export function PrivacyPolicy () { - return import(/*webpackChunkName: "features/privacy_policy" */'../../privacy_policy'); + return import('../../privacy_policy'); } export function TermsOfService () { - return import(/*webpackChunkName: "features/terms_of_service" */'../../terms_of_service'); + return import('../../terms_of_service'); } export function NotificationRequests () { - return import(/*webpackChunkName: "features/notifications/requests" */'../../notifications/requests'); + return import('../../notifications/requests'); } export function NotificationRequest () { - return import(/*webpackChunkName: "features/notifications/request" */'../../notifications/request'); + return import('../../notifications/request'); } export function LinkTimeline () { - return import(/*webpackChunkName: "features/link_timeline" */'../../link_timeline'); + return import('../../link_timeline'); } export function AnnualReportModal () { - return import(/*webpackChunkName: "modals/annual_report_modal" */'../components/annual_report_modal'); + return import('../components/annual_report_modal'); } export function ListEdit () { - return import(/*webpackChunkName: "features/lists" */'../../lists/new'); + return import('../../lists/new'); } export function ListMembers () { - return import(/* webpackChunkName: "features/lists" */'../../lists/members'); + return import('../../lists/members'); } diff --git a/app/javascript/mastodon/load_keyboard_extensions.js b/app/javascript/mastodon/load_keyboard_extensions.js index 2dd0e45fa7..ebdf94561c 100644 --- a/app/javascript/mastodon/load_keyboard_extensions.js +++ b/app/javascript/mastodon/load_keyboard_extensions.js @@ -3,7 +3,7 @@ // can at least log in using KaiOS devices). function importArrowKeyNavigation() { - return import(/* webpackChunkName: "arrow-key-navigation" */ 'arrow-key-navigation'); + return import('arrow-key-navigation'); } export default function loadKeyboardExtensions() { diff --git a/app/javascript/mastodon/locales/load_locale.ts b/app/javascript/mastodon/locales/load_locale.ts index d21675b179..94c7db1141 100644 --- a/app/javascript/mastodon/locales/load_locale.ts +++ b/app/javascript/mastodon/locales/load_locale.ts @@ -5,6 +5,10 @@ import { isLocaleLoaded, setLocale } from './global_locale'; const localeLoadingSemaphore = new Semaphore(1); +const localeFiles = import.meta.glob<{ default: LocaleData['messages'] }>([ + './*.json', +]); + export async function loadLocale() { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings const locale = document.querySelector('html')?.lang || 'en'; @@ -17,13 +21,14 @@ export async function loadLocale() { // if the locale is already set, then do nothing if (isLocaleLoaded()) return; - const localeData = (await import( - /* webpackMode: "lazy" */ - /* webpackChunkName: "locale/[request]" */ - /* webpackInclude: /\.json$/ */ - /* webpackPreload: true */ - `mastodon/locales/${locale}.json` - )) as LocaleData['messages']; + // If there is no locale file, then fallback to english + const localeFile = Object.hasOwn(localeFiles, `./${locale}.json`) + ? localeFiles[`./${locale}.json`] + : localeFiles['./en.json']; + + if (!localeFile) throw new Error('Could not load the locale JSON file'); + + const { default: localeData } = await localeFile(); setLocale({ messages: localeData, locale }); }); diff --git a/app/javascript/mastodon/main.jsx b/app/javascript/mastodon/main.tsx similarity index 58% rename from app/javascript/mastodon/main.jsx rename to app/javascript/mastodon/main.tsx index e7979d56a1..a9696ac50e 100644 --- a/app/javascript/mastodon/main.jsx +++ b/app/javascript/mastodon/main.tsx @@ -7,17 +7,19 @@ import * as perf from 'mastodon/performance'; import ready from 'mastodon/ready'; import { store } from 'mastodon/store'; -import { isProduction } from './utils/environment'; +import { isProduction, isDevelopment } from './utils/environment'; -/** - * @returns {Promise} - */ function main() { perf.start('main()'); return ready(async () => { const mountNode = document.getElementById('mastodon'); - const props = JSON.parse(mountNode.getAttribute('data-props')); + if (!mountNode) { + throw new Error('Mount node not found'); + } + const props = JSON.parse( + mountNode.getAttribute('data-props') ?? '{}', + ) as Record; const root = createRoot(mountNode); root.render(); @@ -25,8 +27,10 @@ function main() { if (isProduction() && me && 'serviceWorker' in navigator) { const { Workbox } = await import('workbox-window'); - const wb = new Workbox('/sw.js'); - /** @type {ServiceWorkerRegistration} */ + const wb = new Workbox( + isDevelopment() ? '/packs-dev/dev-sw.js?dev-sw' : '/sw.js', + { type: 'module', scope: '/' }, + ); let registration; try { @@ -35,8 +39,14 @@ function main() { console.error(err); } - if (registration && 'Notification' in window && Notification.permission === 'granted') { - const registerPushNotifications = await import('mastodon/actions/push_notifications'); + if ( + registration && + 'Notification' in window && + Notification.permission === 'granted' + ) { + const registerPushNotifications = await import( + 'mastodon/actions/push_notifications' + ); store.dispatch(registerPushNotifications.register()); } @@ -46,4 +56,5 @@ function main() { }); } +// eslint-disable-next-line import/no-default-export export default main; diff --git a/app/javascript/mastodon/performance.js b/app/javascript/mastodon/performance.js index 3bca95e85e..1b2092cfc4 100644 --- a/app/javascript/mastodon/performance.js +++ b/app/javascript/mastodon/performance.js @@ -1,24 +1,11 @@ // // Tools for performance debugging, only enabled in development mode. // Open up Chrome Dev Tools, then Timeline, then User Timing to see output. -// Also see config/webpack/loaders/mark.js for the webpack loader marks. import * as marky from 'marky'; import { isDevelopment } from './utils/environment'; -if (isDevelopment()) { - if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) { - // Increase Firefox's performance entry limit; otherwise it's capped to 150. - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135 - performance.setResourceTimingBufferSize(Infinity); - } - - // allows us to easily do e.g. ReactPerf.printWasted() while debugging - //window.ReactPerf = require('react-addons-perf'); - //window.ReactPerf.start(); -} - export function start(name) { if (isDevelopment()) { marky.mark(name); diff --git a/app/javascript/mastodon/polyfills/index.ts b/app/javascript/mastodon/polyfills/index.ts index 431c5b0f30..c001421c36 100644 --- a/app/javascript/mastodon/polyfills/index.ts +++ b/app/javascript/mastodon/polyfills/index.ts @@ -2,10 +2,13 @@ // If there are no polyfills, then this is just Promise.resolve() which means // it will execute in the same tick of the event loop (i.e. near-instant). +// eslint-disable-next-line import/extensions -- This file is virtual so it thinks it has an extension +import 'vite/modulepreload-polyfill'; + import { loadIntlPolyfills } from './intl'; function importExtraPolyfills() { - return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills'); + return import('./extra_polyfills'); } export function loadPolyfills() { diff --git a/app/javascript/mastodon/polyfills/intl.ts b/app/javascript/mastodon/polyfills/intl.ts index b825da6621..b1157557e5 100644 --- a/app/javascript/mastodon/polyfills/intl.ts +++ b/app/javascript/mastodon/polyfills/intl.ts @@ -54,11 +54,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) { return; } // Load the polyfill 1st BEFORE loading data + await import('@formatjs/intl-pluralrules/polyfill-force'); await import( - /* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force' - ); - await import( - /* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}` + `../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js` ); } @@ -70,11 +68,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) { // } // // Load the polyfill 1st BEFORE loading data // await import( -// /* webpackChunkName: "i18n-relativetimeformat-polyfill" */ // '@formatjs/intl-relativetimeformat/polyfill-force' // ); // await import( -// /* webpackChunkName: "i18n-relativetimeformat-polyfill-[request]" */ // `@formatjs/intl-relativetimeformat/locale-data/${unsupportedLocale}` // ); // } diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/sw.js similarity index 95% rename from app/javascript/mastodon/service_worker/entry.js rename to app/javascript/mastodon/service_worker/sw.js index a4aebcd3a7..9c153b123f 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/sw.js @@ -1,5 +1,4 @@ import { ExpirationPlugin } from 'workbox-expiration'; -import { precacheAndRoute } from 'workbox-precaching'; import { registerRoute } from 'workbox-routing'; import { CacheFirst } from 'workbox-strategies'; @@ -15,10 +14,9 @@ function fetchRoot() { return fetch('/', { credentials: 'include', redirect: 'manual' }); } -precacheAndRoute(self.__WB_MANIFEST); registerRoute( - /locale_.*\.js$/, + /intl\/.*\.js$/, new CacheFirst({ cacheName: `${CACHE_NAME_PREFIX}locales`, plugins: [ diff --git a/app/javascript/mastodon/service_worker/web_push_locales.js b/app/javascript/mastodon/service_worker/web_push_locales.js deleted file mode 100644 index f3d61e0195..0000000000 --- a/app/javascript/mastodon/service_worker/web_push_locales.js +++ /dev/null @@ -1,41 +0,0 @@ -/* @preval */ - -const fs = require('fs'); -const path = require('path'); - -const { defineMessages } = require('react-intl'); - -const messages = defineMessages({ - mentioned_you: { id: 'notification.mentioned_you', defaultMessage: '{name} mentioned you' }, -}); - -const filtered = {}; -const filenames = fs.readdirSync(path.resolve(__dirname, '../locales')); - -filenames.forEach(filename => { - if (!filename.match(/\.json$/)) return; - - const content = fs.readFileSync(path.resolve(__dirname, `../locales/${filename}`), 'utf-8'); - const full = JSON.parse(content); - const locale = filename.split('.')[0]; - - filtered[locale] = { - 'notification.favourite': full['notification.favourite'] || '', - 'notification.follow': full['notification.follow'] || '', - 'notification.follow_request': full['notification.follow_request'] || '', - 'notification.mention': full[messages.mentioned_you.id] || '', - 'notification.reblog': full['notification.reblog'] || '', - 'notification.poll': full['notification.poll'] || '', - 'notification.status': full['notification.status'] || '', - 'notification.update': full['notification.update'] || '', - 'notification.admin.sign_up': full['notification.admin.sign_up'] || '', - - 'status.show_more': full['status.show_more'] || '', - 'status.reblog': full['status.reblog'] || '', - 'status.favourite': full['status.favourite'] || '', - - 'notifications.group': full['notifications.group'] || '', - }; -}); - -module.exports = JSON.parse(JSON.stringify(filtered)); diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js index 77187a59ed..5a43449b0d 100644 --- a/app/javascript/mastodon/service_worker/web_push_notifications.js +++ b/app/javascript/mastodon/service_worker/web_push_notifications.js @@ -1,8 +1,10 @@ import { IntlMessageFormat } from 'intl-messageformat'; import { unescape } from 'lodash'; - -import locales from './web_push_locales'; +// see config/vite/plugins/sw-locales +// it needs to be updated when new locale keys are used in this file +// eslint-disable-next-line import/no-unresolved +import locales from "virtual:mastodon-sw-locales"; const MAX_NOTIFICATIONS = 5; const GROUP_TAG = 'tag'; diff --git a/app/javascript/mastodon/utils/environment.ts b/app/javascript/mastodon/utils/environment.ts index b6371499f6..5ccd4d27e3 100644 --- a/app/javascript/mastodon/utils/environment.ts +++ b/app/javascript/mastodon/utils/environment.ts @@ -1,7 +1,11 @@ export function isDevelopment() { - return process.env.NODE_ENV === 'development'; + if (typeof process !== 'undefined') + return process.env.NODE_ENV === 'development'; + else return import.meta.env.DEV; } export function isProduction() { - return process.env.NODE_ENV === 'production'; + if (typeof process !== 'undefined') + return process.env.NODE_ENV === 'production'; + else return import.meta.env.PROD; } diff --git a/app/javascript/styles/inert.scss b/app/javascript/styles/entrypoints/inert.scss similarity index 100% rename from app/javascript/styles/inert.scss rename to app/javascript/styles/entrypoints/inert.scss diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/entrypoints/mailer.scss similarity index 97% rename from app/javascript/styles/mailer.scss rename to app/javascript/styles/entrypoints/mailer.scss index 1e339b4313..876fb21024 100644 --- a/app/javascript/styles/mailer.scss +++ b/app/javascript/styles/entrypoints/mailer.scss @@ -1,4 +1,4 @@ -@use 'fonts/inter'; +@use '../fonts/inter'; body { accent-color: #6364ff; @@ -259,7 +259,7 @@ table + p { .email-header-td { padding: 16px 32px; background-color: #1b001f; - background-image: url('../images/mailer-new/common/header-bg-start.png'); + background-image: url('../../images/mailer-new/common/header-bg-start.png'); background-position: left top; background-repeat: repeat; } @@ -426,7 +426,7 @@ table + p { // Body content .email-body-td { - background-image: url('../images/mailer-new/common/header-bg-end.png'); + background-image: url('../../images/mailer-new/common/header-bg-end.png'); background-position: left top; background-repeat: no-repeat; } @@ -922,7 +922,7 @@ table + p { // Extra content on light purple background .email-extra-wave { height: 42px; - background-image: url('../images/mailer-new/welcome/purple-extra-soft-wave.png'); + background-image: url('../../images/mailer-new/welcome/purple-extra-soft-wave.png'); background-position: bottom center; background-repeat: no-repeat; } @@ -930,7 +930,7 @@ table + p { .email-extra-td { padding: 32px 32px 24px; background-color: #f0f0ff; - background-image: url('../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes + background-image: url('../../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes .email-column-td { padding-top: 8px; diff --git a/app/javascript/styles/fonts/inter.scss b/app/javascript/styles/fonts/inter.scss index bb4899b701..e155f772be 100644 --- a/app/javascript/styles/fonts/inter.scss +++ b/app/javascript/styles/fonts/inter.scss @@ -1,6 +1,6 @@ @font-face { font-family: Inter; - src: url('../fonts/inter/inter-variable-font-slnt-wght.woff2') + src: url('../../fonts/inter/inter-variable-font-slnt-wght.woff2') format('woff2-variations'); font-weight: 100 900; font-style: normal; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 5dbe4f9e08..e4644a24ce 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -393,7 +393,7 @@ code { max-width: 100%; height: auto; border-radius: var(--avatar-border-radius); - background: url('images/void.png'); + background: url('@/images/void.png'); &[src$='missing.png'] { visibility: hidden; diff --git a/app/javascript/types/image.d.ts b/app/javascript/types/image.d.ts index 8a08eca9f6..aa4b6ded71 100644 --- a/app/javascript/types/image.d.ts +++ b/app/javascript/types/image.d.ts @@ -1,3 +1,5 @@ +/// + /* eslint-disable import/no-default-export */ declare module '*.avif' { const path: string; @@ -19,23 +21,6 @@ declare module '*.png' { export default path; } -declare module '*.svg' { - const path: string; - export default path; -} - -declare module '*.svg?react' { - import type React from 'react'; - - interface SVGPropsWithTitle extends React.SVGProps { - title?: string; - } - - const ReactComponent: React.FC; - - export default ReactComponent; -} - declare module '*.webp' { const path: string; export default path; diff --git a/app/javascript/types/virtual.d.ts b/app/javascript/types/virtual.d.ts new file mode 100644 index 0000000000..9f5b890b8a --- /dev/null +++ b/app/javascript/types/virtual.d.ts @@ -0,0 +1,59 @@ +declare module 'virtual:mastodon-emoji-compressed' { + import type { BaseEmoji, EmojiData, NimbleEmojiIndex } from 'emoji-mart'; + import type { Category, Data, Emoji } from 'emoji-mart/dist-es/utils/data'; + /* + * The 'search' property, although not defined in the [`Emoji`]{@link node_modules/@types/emoji-mart/dist-es/utils/data.d.ts#Emoji} type, + * is used in the application. + * This could be due to an oversight by the library maintainer. + * The `search` property is defined and used [here]{@link node_modules/emoji-mart/dist/utils/data.js#uncompress}. + */ + export type Search = string; + /* + * The 'skins' property does not exist in the application data. + * This could be a potential area of refactoring or error handling. + * The non-existence of 'skins' property is evident at [this location]{@link app/javascript/mastodon/features/emoji/emoji_compressed.js:121}. + */ + type Skins = null; + + type Filename = string; + type UnicodeFilename = string; + export type FilenameData = [ + filename: Filename, + unicodeFilename?: UnicodeFilename, + ][]; + export type ShortCodesToEmojiDataKey = + | EmojiData['id'] + | BaseEmoji['native'] + | keyof NimbleEmojiIndex['emojis']; + + type SearchData = [ + BaseEmoji['native'], + Emoji['short_names'], + Search, + Emoji['unified'], + ]; + + export type ShortCodesToEmojiData = Record< + ShortCodesToEmojiDataKey, + [FilenameData, SearchData] + >; + type EmojisWithoutShortCodes = FilenameData; + + type EmojiCompressed = [ + ShortCodesToEmojiData, + Skins, + Category[], + Data['aliases'], + EmojisWithoutShortCodes, + Data, + ]; + + /* + * `emoji_compressed.js` uses `babel-plugin-preval`, which makes it difficult to convert to TypeScript. + * As a temporary solution, we are allowing a default export here to apply the TypeScript type `EmojiCompressed` to the JS file export. + * - {@link app/javascript/mastodon/features/emoji/emoji_compressed.js} + */ + declare const emojiCompressed: EmojiCompressed; + + export default emojiCompressed; // eslint-disable-line import/no-default-export +} diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml index 653f155801..38def20fc5 100644 --- a/app/views/auth/sessions/two_factor.html.haml +++ b/app/views/auth/sessions/two_factor.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('auth.login') -= javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous' += vite_typescript_tag 'two_factor_authentication.ts', crossorigin: 'anonymous' - if webauthn_enabled? = render partial: 'auth/sessions/two_factor/webauthn_form', locals: { hidden: @scheme_type != 'webauthn' } diff --git a/app/views/auth/setup/show.html.haml b/app/views/auth/setup/show.html.haml index 91654ca214..83e0bfd25f 100644 --- a/app/views/auth/setup/show.html.haml +++ b/app/views/auth/setup/show.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('auth.setup.title') -= javascript_pack_tag 'sign_up', crossorigin: 'anonymous' += vite_typescript_tag 'sign_up.ts', crossorigin: 'anonymous' = simple_form_for(@user, url: auth_setup_path) do |f| = render 'auth/shared/progress', stage: 'confirm' diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 3f7727cdfb..08432a177c 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,7 +1,7 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag 'public', crossorigin: 'anonymous' - = javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous' + = vite_typescript_tag 'public.tsx', crossorigin: 'anonymous' + = vite_typescript_tag 'admin.tsx', crossorigin: 'anonymous' - content_for :body_classes, 'admin' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 6f016c6cf5..3dae993a9a 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -26,11 +26,13 @@ %title= html_title = theme_style_tags current_theme + = vite_client_tag + = vite_react_refresh_tag -# Needed for the wicg-inert polyfill. It needs to be on it's own