Compare commits

...

73 commits

Author SHA1 Message Date
ed4a0bba04 Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-31 10:02:26 +01:00
Eugen Rochko
fa0ba67753
Change materialized views to be refreshed concurrently to avoid locks (#29015) 2024-01-30 18:21:30 +00:00
Eugen Rochko
c4af668e5c
Fix follow recommendations for less used languages (#29017) 2024-01-30 17:24:40 +00:00
renovate[bot]
0c0d077276
Update dependency chewy to v7.5.1 (#29018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 15:40:43 +00:00
renovate[bot]
0bc526a967
Update eslint (non-major) (#29001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 15:39:59 +00:00
renovate[bot]
8c08e5cdb2
Update devDependencies (non-major) (#29000)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 15:39:34 +00:00
Matt Jankowski
ce0d134147
Add redirect_with_vary to AllowedMethods for Style/FormatStringToken cop (#28973) 2024-01-30 15:39:01 +00:00
Matt Jankowski
86fbde7b46
Fix Style/NumericLiterals cop in ProfileStories support module (#28971) 2024-01-30 15:38:33 +00:00
Yamagishi Kazutoshi
b3075a9993
Remove unused l18n messages (#28964) 2024-01-30 15:34:07 +00:00
Matt Jankowski
f91acba70a
Combine repeated requests in account controller concern spec (#28957) 2024-01-30 15:32:20 +00:00
renovate[bot]
9d3830344f
Update dependency immutable to v4.3.5 (#28933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-30 15:30:39 +00:00
Matt Jankowski
adcd693b71
Use existing MediaAttachment.remote scope in media CLI (#28912) 2024-01-30 15:29:42 +00:00
J H
1467f1e1e1
Fixed the toggle emoji dropdown bug (#29012) 2024-01-30 13:38:49 +00:00
Matt Jankowski
ff8937aa2c
Move api/v1/statuses/* to request spec (#28954) 2024-01-26 17:45:54 +00:00
Matt Jankowski
44f6d285af
Combine repeated subject in ap fetch remote actor service spec (#28953) 2024-01-26 17:44:12 +00:00
Matt Jankowski
239244e2ed
Combine repeated subject in ap fetch remote account service spec (#28952) 2024-01-26 17:43:08 +00:00
Matt Jankowski
5119fbc9b7
Move api/v1/admin/trends/links/preview_card_providers to request spec (#28951) 2024-01-26 17:41:39 +00:00
Matt Jankowski
b6baab447d
Move api/v2/admin/accounts to request spec (#28950) 2024-01-26 17:41:13 +00:00
Matt Jankowski
7adcc0aae3
Move api/v1/trends/* to request specs (#28949) 2024-01-26 17:40:39 +00:00
Matt Jankowski
0b0ca6f3b8
Move api/v1/timelines/list to request spec (#28948) 2024-01-26 17:40:15 +00:00
renovate[bot]
87ad398ddc
Update formatjs monorepo (#28925)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 16:49:16 +00:00
renovate[bot]
f4a12adfb7
Update dependency axios to v1.6.7 (#28917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 16:49:09 +00:00
renovate[bot]
160c8f4923
Update babel monorepo to v7.23.9 (#28916)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 16:49:03 +00:00
Matt Jankowski
e519f113e8
Combine repeated subject in cacheable response shared example (#28945) 2024-01-26 16:37:05 +00:00
Matt Jankowski
d791bca11b
Combine double subject in well_known/webfinger shared example (#28944) 2024-01-26 16:36:21 +00:00
Matt Jankowski
09a3493fca
Combine double subject in api/v1/media shared example (#28943) 2024-01-26 16:35:49 +00:00
Matt Jankowski
5fbdb2055b
Combine repeated subject in cli/accounts spec shared example (#28942) 2024-01-26 16:35:19 +00:00
Matt Jankowski
1a30a517d6
Combine repeated subjects in link details extractor spec (#28941) 2024-01-26 16:31:07 +00:00
Matt Jankowski
685eaa04d4
Combine double subject in admin/statuses controller shared example (#28940) 2024-01-26 16:30:30 +00:00
Matt Jankowski
beb74fd71c
Combine double subjects in instance actors controller shared example (#28939) 2024-01-26 16:28:50 +00:00
Matt Jankowski
beaef4b672
Combine double subjects in application controller shared example (#28938) 2024-01-26 16:23:12 +00:00
Matt Jankowski
6d35a77c92
Combine repeated subjects in models/user spec (#28937) 2024-01-26 16:22:44 +00:00
Matt Jankowski
2f8656334d
Combine double subjects in admin/accounts controller spec (#28936) 2024-01-26 16:21:31 +00:00
Matt Jankowski
9cc1817bb4
Fix intmermittent failure in api/v1/accounts/statuses controller spec (#28931) 2024-01-26 14:10:26 +00:00
Claire
805dba7f8d
Change compose form to use server-provided post character limit (#28928) 2024-01-26 14:09:45 +00:00
github-actions[bot]
45287049ab
New Crowdin Translations (automated) (#28923)
Co-authored-by: GitHub Actions <noreply@github.com>
2024-01-26 09:15:55 +00:00
Matt Jankowski
0e0a94f483
Handle CLI failure exit status at the top-level script (#28322) 2024-01-26 08:53:44 +00:00
Emelia Smith
881e8c113c
Refactor: fix streaming postgresql and redis typing issues (#28747) 2024-01-25 16:46:02 +00:00
Eugen Rochko
6936e5aa69
Change design of compose form in web UI (#28119)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-25 15:41:31 +00:00
Matt Jankowski
42ab855b23
Add specs for Instance model scopes and add with_domain_follows scope (#28767) 2024-01-25 15:28:27 +00:00
Matt Jankowski
4cdf62e576
Extract rebuild_index method in maintenance CLI (#28911) 2024-01-25 15:26:51 +00:00
Matt Jankowski
0b38946c87
Update paperclip and climate_control gems (#28379) 2024-01-25 15:18:15 +00:00
Matt Jankowski
17ea22671d
Fix Style/GuardClause cop in app/controllers (#28420) 2024-01-25 15:13:41 +00:00
Eugen Rochko
3205a654ca
Refactor conversations components in web UI (#28833)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-25 14:34:26 +00:00
Matt Jankowski
274a48a9f4
Extract helper methods for db connection and table existence check in CLI::Maintenance task (#28281) 2024-01-25 13:49:33 +00:00
Matt Jankowski
2866106ec1
Reduce factory creation in spec/models/account_statuses_cleanup_policy (#28361) 2024-01-25 13:37:25 +00:00
Matt Jankowski
c8f59d2ca4
Fix Style/TernaryParentheses cop (#28387) 2024-01-25 13:28:49 +00:00
Matt Jankowski
ca7053f19c
Consolidate db test prep steps to rake task (#28886) 2024-01-25 13:10:39 +00:00
github-actions[bot]
59d2ea0d82
New Crowdin Translations (automated) (#28899)
Co-authored-by: GitHub Actions <noreply@github.com>
2024-01-25 13:00:44 +00:00
Matt Jankowski
6b6586f5d0
Add CustomFilterKeyword#to_regex method (#28893) 2024-01-25 13:00:34 +00:00
renovate[bot]
a69506a434
fix(deps): update dependency dotenv to v16.4.1 (#28889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 12:37:23 +00:00
Matt Jankowski
7c9c6c7f80
Fix remaining Rails/WhereExists cop violations, regenerate todo (#28892) 2024-01-25 12:37:07 +00:00
renovate[bot]
1a565e4bea
fix(deps): update dependency axios to v1.6.6 (#28895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 12:36:20 +00:00
renovate[bot]
d158f7e622
chore(deps): update dependency rspec-rails to v6.1.1 (#28905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 12:33:56 +00:00
Claire
087415d0fe
Add tests for processing statuses using bearcap URIs (#28904) 2024-01-25 11:13:36 +00:00
Claire
0471a78055
Add tests for redirect confirmations (#28903) 2024-01-25 11:13:33 +00:00
Claire
c50274a0ac
Fix redirect confirmation for accounts (#28902) 2024-01-25 10:44:25 +00:00
KMY(雪あすか)
9a8293f58d
Fix process of receiving posts with bearcaps is not working (#26527) 2024-01-25 10:37:09 +00:00
Matt Jankowski
38f7f8b909
Tidy up association declaration in Instance model (#28880) 2024-01-24 17:30:28 +00:00
renovate[bot]
9c5be13980
chore(deps): update dependency chewy to v7.5.0 (#28730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 16:51:47 +00:00
renovate[bot]
7019af431d
fix(deps): update dependency dotenv to v16.4.0 (#28872)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 16:51:36 +00:00
renovate[bot]
559bbf0aa6
chore(deps): update artifact actions (major) to v4 (major) (#28415)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 16:51:18 +00:00
renovate[bot]
ea5397c373
chore(deps): update dependency selenium-webdriver to v4.17.0 (#28858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 14:55:06 +00:00
renovate[bot]
64993d3f77
chore(deps): update dependency haml_lint to v0.55.0 (#28856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 13:37:37 +00:00
Claire
5a838ceaa9
Use active variants for boost icons and increase icon size (#27924) 2024-01-24 12:37:43 +00:00
Matt Jankowski
1290fede65
Fix Rails/WhereExists cop in app/lib (#28862) 2024-01-24 11:51:09 +00:00
renovate[bot]
41c2af2270
chore(deps): update dependency rubocop to v1.60.1 (#28731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 11:50:41 +00:00
Eugen Rochko
b19ae521b7
Add confirmation when redirecting logged-out requests to permalink (#27792)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-24 10:49:19 +00:00
Matt Jankowski
7a1f087659
Add created_before and updated_before scopes to MediaAttachment (#28869) 2024-01-24 10:32:54 +00:00
Matt Jankowski
a11a2fb052
Add error classes to api/base errors coverage (#28864) 2024-01-24 10:31:31 +00:00
github-actions[bot]
a34d27c18f
New Crowdin Translations (automated) (#28875)
Co-authored-by: GitHub Actions <noreply@github.com>
2024-01-24 10:06:56 +00:00
Matt Jankowski
9d413cbaf8
Fix Rails/WhereExists cop in app/models (#28863) 2024-01-24 09:57:49 +00:00
Matt Jankowski
599bc69503
Simplify AccountSummary.filtered query generation (#28868) 2024-01-24 09:57:32 +00:00
340 changed files with 4107 additions and 5385 deletions

View file

@ -165,7 +165,7 @@ module.exports = defineConfig({
// },
// ],
'jsx-a11y/no-noninteractive-tabindex': 'off',
'jsx-a11y/no-onchange': 'warn',
'jsx-a11y/no-onchange': 'off',
// recommended is full 'error'
'jsx-a11y/no-static-element-interactions': [
'warn',

View file

@ -78,23 +78,8 @@ jobs:
- name: Create database
run: './bin/rails db:create'
- name: Run migrations up to v2.0.0
run: './bin/rails db:migrate VERSION=20171010025614'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2'
- name: Run migrations up to v2.4.0
run: './bin/rails db:migrate VERSION=20180514140000'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4'
- name: Run migrations up to v2.4.3
run: './bin/rails db:migrate VERSION=20180707154237'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4_3'
- name: Run historical migrations with data population
run: './bin/rails tests:migrations:prepare_database'
- name: Run all remaining migrations
run: './bin/rails db:migrate'

View file

@ -45,6 +45,7 @@ jobs:
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
@ -77,28 +78,11 @@ jobs:
- name: Create database
run: './bin/rails db:create'
- name: Run migrations up to v2.0.0
run: './bin/rails db:migrate VERSION=20171010025614'
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2'
- name: Run pre-deployment migrations up to v2.4.0
run: './bin/rails db:migrate VERSION=20180514140000'
- name: Run historical migrations with data population
run: './bin/rails tests:migrations:prepare_database'
env:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4'
- name: Run migrations up to v2.4.3
run: './bin/rails db:migrate VERSION=20180707154237'
env:
SKIP_POST_DEPLOYMENT_MIGRATIONS: true
- name: Populate database with test data
run: './bin/rails tests:migrations:populate_v2_4_3'
- name: Run all remaining pre-deployment migrations
run: './bin/rails db:migrate'
env:

View file

@ -52,7 +52,7 @@ jobs:
run: |
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.mode == 'test'
with:
path: |-
@ -117,7 +117,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: './'
name: ${{ github.sha }}
@ -193,7 +193,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: './public'
name: ${{ github.sha }}
@ -213,14 +213,14 @@ jobs:
- run: bundle exec rake spec:system
- name: Archive logs
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-logs-${{ matrix.ruby-version }}
path: log/
- name: Archive test screenshots
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-screenshots
@ -297,7 +297,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: './public'
name: ${{ github.sha }}
@ -317,14 +317,14 @@ jobs:
- run: bin/rspec --tag search
- name: Archive logs
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-search-logs-${{ matrix.ruby-version }}
path: log/
- name: Archive test screenshots
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-search-screenshots

View file

@ -96,12 +96,11 @@ Rails/FilePath:
Rails/HttpStatus:
EnforcedStyle: numeric
# Reason: Allowed in `tootctl` CLI code and in boot ENV checker
# Reason: Allowed in boot ENV checker
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
Rails/Exit:
Exclude:
- 'config/boot.rb'
- 'lib/mastodon/cli/*.rb'
# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
@ -175,6 +174,15 @@ Style/ClassAndModuleChildren:
Style/Documentation:
Enabled: false
# Reason: Route redirects are not token-formatted and must be skipped
# https://docs.rubocop.org/rubocop/cops_style.html#styleformatstringtoken
Style/FormatStringToken:
inherit_mode:
merge:
- AllowedMethods # The rubocop-rails config adds `redirect`
AllowedMethods:
- redirect_with_vary
# Reason: Enforce modern Ruby style
# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
Style/HashSyntax:

View file

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.59.0.
# using RuboCop version 1.60.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@ -70,28 +70,6 @@ Rails/UniqueValidationWithoutIndex:
- 'app/models/identity.rb'
- 'app/models/webauthn_credential.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: exists, where
Rails/WhereExists:
Exclude:
- 'app/controllers/activitypub/inboxes_controller.rb'
- 'app/controllers/admin/email_domain_blocks_controller.rb'
- 'app/lib/activitypub/activity/create.rb'
- 'app/lib/delivery_failure_tracker.rb'
- 'app/lib/feed_manager.rb'
- 'app/lib/suspicious_sign_in_detector.rb'
- 'app/models/poll.rb'
- 'app/models/session_activation.rb'
- 'app/models/status.rb'
- 'app/policies/status_policy.rb'
- 'app/serializers/rest/announcement_serializer.rb'
- 'app/workers/move_worker.rb'
- 'spec/models/account_spec.rb'
- 'spec/services/activitypub/process_collection_service_spec.rb'
- 'spec/services/purge_domain_service_spec.rb'
- 'spec/services/unallow_domain_service_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowedMethods, AllowedPatterns.
# AllowedMethods: ==, equal?, eql?
@ -143,10 +121,6 @@ Style/GlobalStdStream:
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
Exclude:
- 'app/controllers/admin/confirmations_controller.rb'
- 'app/controllers/auth/confirmations_controller.rb'
- 'app/controllers/auth/passwords_controller.rb'
- 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
- 'app/lib/activitypub/activity/block.rb'
- 'app/lib/request.rb'
- 'app/lib/request_pool.rb'
@ -301,13 +275,6 @@ Style/StringLiterals:
- 'config/initializers/webauthn.rb'
- 'config/routes.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
Style/TernaryParentheses:
Exclude:
- 'config/environments/development.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma

View file

@ -123,7 +123,7 @@ group :test do
gem 'database_cleaner-active_record'
# Used to mock environment variables
gem 'climate_control', '~> 0.2'
gem 'climate_control'
# Generating fake data for specs
gem 'faker', '~> 3.2'

View file

@ -180,12 +180,12 @@ GEM
activesupport
cbor (0.5.9.6)
charlock_holmes (0.7.7)
chewy (7.4.0)
chewy (7.5.1)
activesupport (>= 5.2)
elasticsearch (>= 7.12.0, < 7.14.0)
elasticsearch-dsl
chunky_png (1.4.0)
climate_control (0.2.0)
climate_control (1.2.0)
cocoon (1.2.15)
color_diff (0.1)
concurrent-ruby (1.2.3)
@ -319,7 +319,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.53.0)
haml_lint (0.55.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
@ -360,7 +360,7 @@ GEM
rainbow (>= 2.2.2, < 4.0)
terminal-table (>= 1.5.1)
idn-ruby (0.1.5)
io-console (0.7.1)
io-console (0.7.2)
irb (1.11.1)
rdoc
reline (>= 0.4.2)
@ -445,7 +445,7 @@ GEM
mime-types-data (3.2023.1205)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.20.0)
minitest (5.21.2)
msgpack (1.7.2)
multi_json (1.15.0)
multipart-post (2.3.0)
@ -504,7 +504,7 @@ GEM
orm_adapter (0.5.0)
ox (2.14.17)
parallel (1.24.0)
parser (3.2.2.4)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
parslet (2.0.0)
@ -610,7 +610,7 @@ GEM
redis (>= 4)
redlock (1.3.2)
redis (>= 3.0.0, < 6.0)
regexp_parser (2.8.3)
regexp_parser (2.9.0)
reline (0.4.2)
io-console (~> 0.5)
request_store (1.5.1)
@ -636,7 +636,7 @@ GEM
rspec-mocks (3.12.6)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.1.0)
rspec-rails (6.1.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@ -650,11 +650,11 @@ GEM
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8)
rspec-support (3.12.1)
rubocop (1.59.0)
rubocop (1.60.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
@ -696,7 +696,8 @@ GEM
scenic (1.7.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
selenium-webdriver (4.16.0)
selenium-webdriver (4.17.0)
base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@ -745,8 +746,8 @@ GEM
temple (0.10.3)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
terrapin (1.0.1)
climate_control
test-prof (1.3.1)
thor (1.3.0)
tilt (2.3.0)
@ -835,7 +836,7 @@ DEPENDENCIES
capybara (~> 3.39)
charlock_holmes (~> 0.7.7)
chewy (~> 7.3)
climate_control (~> 0.2)
climate_control
cocoon (~> 1.2)
color_diff (~> 0.1)
concurrent-ruby

View file

@ -24,7 +24,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
def unknown_affected_account?
json = Oj.load(body, mode: :strict)
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.exists?(uri: json['actor'])
rescue Oj::ParseError
false
end

View file

@ -3,7 +3,7 @@
module Admin
class ConfirmationsController < BaseController
before_action :set_user
before_action :check_confirmation, only: [:resend]
before_action :redirect_confirmed_user, only: [:resend], if: :user_confirmed?
def create
authorize @user, :confirm?
@ -25,11 +25,13 @@ module Admin
private
def check_confirmation
if @user.confirmed?
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
redirect_to admin_accounts_path
end
def redirect_confirmed_user
flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
redirect_to admin_accounts_path
end
def user_confirmed?
@user.confirmed?
end
end
end

View file

@ -38,7 +38,7 @@ module Admin
log_action :create, @email_domain_block
(@email_domain_block.other_domains || []).uniq.each do |domain|
next if EmailDomainBlock.where(domain: domain).exists?
next if EmailDomainBlock.exists?(domain: domain)
other_email_domain_block = EmailDomainBlock.create!(domain: domain, allow_with_approval: @email_domain_block.allow_with_approval, parent: @email_domain_block)
log_action :create, other_email_domain_block

View file

@ -49,7 +49,7 @@ module Admin
next
end
@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
@warning_domains = instances_from_imported_blocks.pluck(:domain)
rescue ActionController::ParameterMissing
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
set_dummy_import!
@ -58,6 +58,10 @@ module Admin
private
def instances_from_imported_blocks
Instance.with_domain_follows(@domain_blocks.map(&:domain))
end
def export_filename
'domain_blocks.csv'
end

View file

@ -7,7 +7,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
before_action :set_body_classes
before_action :set_confirmation_user!, only: [:show, :confirm_captcha]
before_action :require_unconfirmed!
before_action :redirect_confirmed_user, if: :signed_in_confirmed_user?
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
before_action :require_captcha_if_needed!, only: [:show]
@ -65,10 +65,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
@confirmation_user.nil? || @confirmation_user.confirmed?
end
def require_unconfirmed!
if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
end
def redirect_confirmed_user
redirect_to(current_user.approved? ? root_path : edit_user_registration_path)
end
def signed_in_confirmed_user?
user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank?
end
def set_body_classes

View file

@ -2,7 +2,7 @@
class Auth::PasswordsController < Devise::PasswordsController
skip_before_action :check_self_destruct!
before_action :check_validity_of_reset_password_token, only: :edit
before_action :redirect_invalid_reset_token, only: :edit, unless: :reset_password_token_is_valid?
before_action :set_body_classes
layout 'auth'
@ -19,11 +19,9 @@ class Auth::PasswordsController < Devise::PasswordsController
private
def check_validity_of_reset_password_token
unless reset_password_token_is_valid?
flash[:error] = I18n.t('auth.invalid_reset_password_token')
redirect_to new_password_path(resource_name)
end
def redirect_invalid_reset_token
flash[:error] = I18n.t('auth.invalid_reset_password_token')
redirect_to new_password_path(resource_name)
end
def set_body_classes

View file

@ -21,10 +21,19 @@ module WebAppControllerConcern
def redirect_unauthenticated_to_permalinks!
return if user_signed_in? && current_account.moved_to_account_id.nil?
redirect_path = PermalinkRedirector.new(request.path).redirect_path
return if redirect_path.blank?
permalink_redirector = PermalinkRedirector.new(request.path)
return if permalink_redirector.redirect_path.blank?
expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in?
redirect_to(redirect_path)
respond_to do |format|
format.html do
redirect_to(permalink_redirector.redirect_confirmation_path, allow_other_host: false)
end
format.json do
redirect_to(permalink_redirector.redirect_uri, allow_other_host: true)
end
end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class Redirect::AccountsController < Redirect::BaseController
private
def set_resource
@resource = Account.find(params[:id])
not_found if @resource.local?
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
class Redirect::BaseController < ApplicationController
vary_by 'Accept-Language'
before_action :set_resource
before_action :set_app_body_class
def show
@redirect_path = ActivityPub::TagManager.instance.url_for(@resource)
render 'redirects/show', layout: 'application'
end
private
def set_app_body_class
@body_classes = 'app-body'
end
def set_resource
raise NotImplementedError
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class Redirect::StatusesController < Redirect::BaseController
private
def set_resource
@resource = Status.find(params[:id])
not_found if @resource.local? || !@resource.distributable?
end
end

View file

@ -6,8 +6,8 @@ module Settings
skip_before_action :check_self_destruct!
skip_before_action :require_functional!
before_action :require_otp_enabled
before_action :require_webauthn_enabled, only: [:index, :destroy]
before_action :redirect_invalid_otp, unless: -> { current_user.otp_enabled? }
before_action :redirect_invalid_webauthn, only: [:index, :destroy], unless: -> { current_user.webauthn_enabled? }
def index; end
def new; end
@ -85,18 +85,14 @@ module Settings
private
def require_otp_enabled
unless current_user.otp_enabled?
flash[:error] = t('webauthn_credentials.otp_required')
redirect_to settings_two_factor_authentication_methods_path
end
def redirect_invalid_otp
flash[:error] = t('webauthn_credentials.otp_required')
redirect_to settings_two_factor_authentication_methods_path
end
def require_webauthn_enabled
unless current_user.webauthn_enabled?
flash[:error] = t('webauthn_credentials.not_enabled')
redirect_to settings_two_factor_authentication_methods_path
end
def redirect_invalid_webauthn
flash[:error] = t('webauthn_credentials.not_enabled')
redirect_to settings_two_factor_authentication_methods_path
end
end
end

View file

@ -0,0 +1,25 @@
<svg width="5" height="80" viewBox="0 0 5 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_253_1286)">
<rect width="5" height="80" fill="url(#paint0_linear_253_1286)"/>
<line x1="-0.860365" y1="6.80136" x2="10.6078" y2="-1.22871" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="14.8314" x2="10.6078" y2="6.80132" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="22.8615" x2="10.6078" y2="14.8314" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="30.8916" x2="10.6078" y2="22.8615" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="38.9216" x2="10.6078" y2="30.8915" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="46.9517" x2="10.6078" y2="38.9216" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="54.9818" x2="10.6078" y2="46.9517" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="63.0118" x2="10.6078" y2="54.9817" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="71.0419" x2="10.6078" y2="63.0118" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="79.072" x2="10.6078" y2="71.0419" stroke="black" stroke-width="3"/>
<line x1="-0.860365" y1="87.102" x2="10.6078" y2="79.072" stroke="black" stroke-width="3"/>
</g>
<defs>
<linearGradient id="paint0_linear_253_1286" x1="2.5" y1="0" x2="2.5" y2="80" gradientUnits="userSpaceOnUse">
<stop stop-color="#FEC84B"/>
<stop offset="1" stop-color="#F79009"/>
</linearGradient>
<clipPath id="clip0_253_1286">
<rect width="5" height="80" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -9,7 +9,11 @@ exports[`<AutosuggestEmoji /> renders emoji with custom url 1`] = `
className="emojione"
src="http://example.com/emoji.png"
/>
:foobar:
<div
className="autosuggest-emoji__name"
>
:foobar:
</div>
</div>
`;
@ -22,6 +26,10 @@ exports[`<AutosuggestEmoji /> renders native emoji 1`] = `
className="emojione"
src="/emoji/1f499.svg"
/>
:foobar:
<div
className="autosuggest-emoji__name"
>
:foobar:
</div>
</div>
`;

View file

@ -37,10 +37,10 @@ class Account extends ImmutablePureComponent {
static propTypes = {
size: PropTypes.number,
account: ImmutablePropTypes.record,
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
onMuteNotifications: PropTypes.func.isRequired,
onFollow: PropTypes.func,
onBlock: PropTypes.func,
onMute: PropTypes.func,
onMuteNotifications: PropTypes.func,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool,
minimal: PropTypes.bool,

View file

@ -35,7 +35,7 @@ export default class AutosuggestEmoji extends PureComponent {
alt={emoji.native || emoji.colons}
/>
{emoji.colons}
<div className='autosuggest-emoji__name'>{emoji.colons}</div>
</div>
);
}

View file

@ -1,5 +1,3 @@
import { FormattedMessage } from 'react-intl';
import { ShortNumber } from 'mastodon/components/short_number';
interface Props {
@ -16,27 +14,18 @@ interface Props {
};
}
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => {
const weeklyUses = tag.history && (
<ShortNumber
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)}
/>
);
return (
<div className='autosuggest-hashtag'>
<div className='autosuggest-hashtag__name'>
#<strong>{tag.name}</strong>
</div>
{tag.history !== undefined && (
<div className='autosuggest-hashtag__uses'>
<FormattedMessage
id='autosuggest_hashtag.per_week'
defaultMessage='{count} per week'
values={{ count: weeklyUses }}
/>
</div>
)}
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => (
<div className='autosuggest-hashtag'>
<div className='autosuggest-hashtag__name'>
#<strong>{tag.name}</strong>
</div>
);
};
{tag.history !== undefined && (
<div className='autosuggest-hashtag__uses'>
<ShortNumber
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)}
/>
</div>
)}
</div>
);

View file

@ -5,6 +5,8 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Overlay from 'react-overlays/Overlay';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import AutosuggestEmoji from './autosuggest_emoji';
@ -195,34 +197,37 @@ export default class AutosuggestInput extends ImmutablePureComponent {
return (
<div className='autosuggest-input'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
<input
type='text'
ref={this.setInput}
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
dir='auto'
aria-autocomplete='list'
aria-label={placeholder}
id={id}
className={className}
maxLength={maxLength}
lang={lang}
spellCheck={spellCheck}
/>
<input
type='text'
ref={this.setInput}
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
dir='auto'
aria-autocomplete='list'
id={id}
className={className}
maxLength={maxLength}
lang={lang}
spellCheck={spellCheck}
/>
</label>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
</div>
<Overlay show={!(suggestionsHidden || suggestions.isEmpty())} offset={[0, 0]} placement='bottom' target={this.input} popperConfig={{ strategy: 'fixed' }}>
{({ props }) => (
<div {...props}>
<div className='autosuggest-textarea__suggestions' style={{ width: this.input?.clientWidth }}>
{suggestions.map(this.renderSuggestion)}
</div>
</div>
)}
</Overlay>
</div>
);
}

View file

@ -5,6 +5,7 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Overlay from 'react-overlays/Overlay';
import Textarea from 'react-textarea-autosize';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
@ -52,7 +53,6 @@ const AutosuggestTextarea = forwardRef(({
onFocus,
autoFocus = true,
lang,
children,
}, textareaRef) => {
const [suggestionsHidden, setSuggestionsHidden] = useState(true);
@ -183,40 +183,38 @@ const AutosuggestTextarea = forwardRef(({
);
};
return [
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
return (
<div className='autosuggest-textarea'>
<Textarea
ref={textareaRef}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
onKeyUp={onKeyUp}
onFocus={handleFocus}
onBlur={handleBlur}
onPaste={handlePaste}
dir='auto'
aria-autocomplete='list'
aria-label={placeholder}
lang={lang}
/>
<Textarea
ref={textareaRef}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
onKeyUp={onKeyUp}
onFocus={handleFocus}
onBlur={handleBlur}
onPaste={handlePaste}
dir='auto'
aria-autocomplete='list'
lang={lang}
/>
</label>
</div>
{children}
</div>,
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(renderSuggestion)}
</div>
</div>,
];
<Overlay show={!(suggestionsHidden || suggestions.isEmpty())} offset={[0, 0]} placement='bottom' target={textareaRef} popperConfig={{ strategy: 'fixed' }}>
{({ props }) => (
<div {...props}>
<div className='autosuggest-textarea__suggestions' style={{ width: textareaRef.current?.clientWidth }}>
{suggestions.map(renderSuggestion)}
</div>
</div>
)}
</Overlay>
</div>
);
});
AutosuggestTextarea.propTypes = {
@ -232,7 +230,6 @@ AutosuggestTextarea.propTypes = {
onKeyDown: PropTypes.func,
onPaste: PropTypes.func.isRequired,
onFocus:PropTypes.func,
children: PropTypes.node,
autoFocus: PropTypes.bool,
lang: PropTypes.string,
};

View file

@ -165,7 +165,7 @@ class Dropdown extends PureComponent {
children: PropTypes.node,
icon: PropTypes.string,
iconComponent: PropTypes.func,
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]),
loading: PropTypes.bool,
size: PropTypes.number,
title: PropTypes.string,

View file

@ -70,9 +70,9 @@ export const defaultMediaVisibility = (status) => {
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
});

View file

@ -18,8 +18,10 @@ import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -366,7 +368,7 @@ class StatusActionBar extends ImmutablePureComponent {
if (status.get('reblogged')) {
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
} else if (publicStatus) {
reblogTitle = intl.formatMessage(messages.reblog);
reblogIconComponent = RepeatIcon;

View file

@ -2,8 +2,8 @@ import { defineMessages, useIntl } from 'react-intl';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
import { Icon } from './icon';
@ -11,14 +11,17 @@ type Visibility = 'public' | 'unlisted' | 'private' | 'direct';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
unlisted_short: {
id: 'privacy.unlisted.short',
defaultMessage: 'Quiet public',
},
private_short: {
id: 'privacy.private.short',
defaultMessage: 'Followers only',
defaultMessage: 'Followers',
},
direct_short: {
id: 'privacy.direct.short',
defaultMessage: 'Mentioned people only',
defaultMessage: 'Specific people',
},
});
@ -35,7 +38,7 @@ export const VisibilityIcon: React.FC<{ visibility: Visibility }> = ({
},
unlisted: {
icon: 'unlock',
iconComponent: LockOpenIcon,
iconComponent: QuietTimeIcon,
text: intl.formatMessage(messages.unlisted_short),
},
private: {

View file

@ -1,14 +1,12 @@
import { PureComponent } from 'react';
import { Provider } from 'react-redux';
import { fetchCustomEmojis } from '../actions/custom_emojis';
import { hydrateStore } from '../actions/store';
import Compose from '../features/standalone/compose';
import initialState from '../initial_state';
import { IntlProvider } from '../locales';
import { store } from '../store';
import { fetchCustomEmojis } from 'mastodon/actions/custom_emojis';
import { hydrateStore } from 'mastodon/actions/store';
import { Router } from 'mastodon/components/router';
import Compose from 'mastodon/features/standalone/compose';
import initialState from 'mastodon/initial_state';
import { IntlProvider } from 'mastodon/locales';
import { store } from 'mastodon/store';
if (initialState) {
store.dispatch(hydrateStore(initialState));
@ -16,16 +14,14 @@ if (initialState) {
store.dispatch(fetchCustomEmojis());
export default class ComposeContainer extends PureComponent {
const ComposeContainer = () => (
<IntlProvider>
<Provider store={store}>
<Router>
<Compose />
</Router>
</Provider>
</IntlProvider>
);
render () {
return (
<IntlProvider>
<Provider store={store}>
<Compose />
</Provider>
</IntlProvider>
);
}
}
export default ComposeContainer;

View file

@ -1,13 +1,13 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { useCallback } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, useIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { useDispatch } from 'react-redux';
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { openModal } from 'mastodon/actions/modal';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import { logOut } from 'mastodon/utils/log_out';
const messages = defineMessages({
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
@ -23,51 +23,52 @@ const messages = defineMessages({
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
});
class ActionBar extends PureComponent {
export const ActionBar = () => {
const dispatch = useDispatch();
const intl = useIntl();
static propTypes = {
account: ImmutablePropTypes.record.isRequired,
onLogout: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
const handleLogoutClick = useCallback(() => {
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
closeWhenConfirm: false,
onConfirm: () => logOut(),
},
}));
}, [dispatch, intl]);
handleLogout = () => {
this.props.onLogout();
};
let menu = [];
render () {
const { intl } = this.props;
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.logout), action: handleLogoutClick });
let menu = [];
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
menu.push(null);
menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
return (
<div className='compose__action-bar'>
<div className='compose__action-bar-dropdown'>
<DropdownMenuContainer items={menu} icon='bars' iconComponent={MenuIcon} size={24} direction='right' />
</div>
</div>
);
}
}
export default injectIntl(ActionBar);
return (
<DropdownMenuContainer
items={menu}
icon='bars'
iconComponent={MoreHorizIcon}
size={24}
direction='right'
/>
);
};

View file

@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
return (
<div className='autosuggest-account' title={account.get('acct')}>
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
<Avatar account={account} size={24} />
<DisplayName account={account} />
</div>
);

View file

@ -1,26 +1,18 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { length } from 'stringz';
export default class CharacterCounter extends PureComponent {
export const CharacterCounter = ({ text, max }) => {
const diff = max - length(text);
static propTypes = {
text: PropTypes.string.isRequired,
max: PropTypes.number.isRequired,
};
checkRemainingText (diff) {
if (diff < 0) {
return <span className='character-counter character-counter--over'>{diff}</span>;
}
return <span className='character-counter'>{diff}</span>;
if (diff < 0) {
return <span className='character-counter character-counter--over'>{diff}</span>;
}
render () {
const diff = this.props.max - length(this.props.text);
return this.checkRemainingText(diff);
}
return <span className='character-counter'>{diff}</span>;
};
}
CharacterCounter.propTypes = {
text: PropTypes.string.isRequired,
max: PropTypes.number.isRequired,
};

View file

@ -10,8 +10,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import { Icon } from 'mastodon/components/icon';
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
import AutosuggestInput from '../../../components/autosuggest_input';
@ -20,25 +18,27 @@ import { Button } from '../../../components/button';
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
import LanguageDropdown from '../containers/language_dropdown_container';
import PollButtonContainer from '../containers/poll_button_container';
import PollFormContainer from '../containers/poll_form_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import UploadButtonContainer from '../containers/upload_button_container';
import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container';
import { countableText } from '../util/counter';
import CharacterCounter from './character_counter';
import { CharacterCounter } from './character_counter';
import { EditIndicator } from './edit_indicator';
import { NavigationBar } from './navigation_bar';
import { PollForm } from "./poll_form";
import { ReplyIndicator } from './reply_indicator';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
publish: { id: 'compose_form.publish', defaultMessage: 'Publish' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Content warning (optional)' },
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Update' },
reply: { id: 'compose_form.reply', defaultMessage: 'Reply' },
});
class ComposeForm extends ImmutablePureComponent {
@ -65,10 +65,12 @@ class ComposeForm extends ImmutablePureComponent {
onPaste: PropTypes.func.isRequired,
onPickEmoji: PropTypes.func.isRequired,
autoFocus: PropTypes.bool,
withoutNavigation: PropTypes.bool,
anyMedia: PropTypes.bool,
isInReply: PropTypes.bool,
singleColumn: PropTypes.bool,
lang: PropTypes.string,
maxChars: PropTypes.number,
...WithOptionalRouterPropTypes
};
@ -100,11 +102,11 @@ class ComposeForm extends ImmutablePureComponent {
};
canSubmit = () => {
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
const { isSubmitting, isChangingUpload, isUploading, anyMedia, maxChars } = this.props;
const fulltext = this.getFulltextForCharacterCounting();
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 1000 || (isOnlyWhitespace && !anyMedia));
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (isOnlyWhitespace && !anyMedia));
};
handleSubmit = (e) => {
@ -223,93 +225,90 @@ class ComposeForm extends ImmutablePureComponent {
};
render () {
const { intl, onPaste, autoFocus } = this.props;
const { intl, onPaste, autoFocus, withoutNavigation, maxChars } = this.props;
const { highlighted } = this.state;
const disabled = this.props.isSubmitting;
let publishText = '';
if (this.props.isEditing) {
publishText = intl.formatMessage(messages.saveChanges);
} else if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
publishText = <><Icon id='lock' icon={LockIcon} /> {intl.formatMessage(messages.publish)}</>;
} else {
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
}
return (
<form className='compose-form' onSubmit={this.handleSubmit}>
<ReplyIndicator />
{!withoutNavigation && <NavigationBar />}
<WarningContainer />
<ReplyIndicatorContainer />
<div className={classNames('compose-form__highlightable', { active: highlighted })} ref={this.setRef}>
<div className='compose-form__scrollable'>
<EditIndicator />
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
<AutosuggestInput
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={this.props.spoilerText}
onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown}
disabled={!this.props.spoiler}
ref={this.setSpoilerText}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']}
id='cw-spoiler-input'
className='spoiler-input__input'
lang={this.props.lang}
spellCheck
/>
</div>
{this.props.spoiler && (
<div className='spoiler-input'>
<div className='spoiler-input__border' />
<div className={classNames('compose-form__highlightable', { active: highlighted })}>
<AutosuggestTextarea
ref={this.textareaRef}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={disabled}
value={this.props.text}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={autoFocus}
lang={this.props.lang}
>
<div className='compose-form__modifiers'>
<UploadFormContainer />
<PollFormContainer />
</div>
</AutosuggestTextarea>
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
<AutosuggestInput
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={this.props.spoilerText}
disabled={disabled}
onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown}
ref={this.setSpoilerText}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']}
id='cw-spoiler-input'
className='spoiler-input__input'
lang={this.props.lang}
spellCheck
/>
<div className='compose-form__buttons-wrapper'>
<div className='compose-form__buttons'>
<UploadButtonContainer />
<PollButtonContainer />
<div className='spoiler-input__border' />
</div>
)}
<AutosuggestTextarea
ref={this.textareaRef}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={disabled}
value={this.props.text}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={autoFocus}
lang={this.props.lang}
/>
</div>
<UploadFormContainer />
<PollForm />
<div className='compose-form__footer'>
<div className='compose-form__dropdowns'>
<PrivacyDropdownContainer disabled={this.props.isEditing} />
<SpoilerButtonContainer />
<LanguageDropdown />
</div>
<div className='character-counter__wrapper'>
<CharacterCounter max={1000} text={this.getFulltextForCharacterCounting()} />
</div>
</div>
</div>
<div className='compose-form__actions'>
<div className='compose-form__buttons'>
<UploadButtonContainer />
<PollButtonContainer />
<SpoilerButtonContainer />
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
<CharacterCounter max={maxChars} text={this.getFulltextForCharacterCounting()} />
</div>
<div className='compose-form__publish'>
<div className='compose-form__publish-button-wrapper'>
<Button
type='submit'
text={publishText}
disabled={!this.canSubmit()}
block
/>
<div className='compose-form__submit'>
<Button
type='submit'
text={intl.formatMessage(this.props.isEditing ? messages.saveChanges : (this.props.isInReply ? messages.reply : messages.publish))}
disabled={!this.canSubmit()}
/>
</div>
</div>
</div>
</div>
</form>

View file

@ -0,0 +1,62 @@
import { useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import BarChart4BarsIcon from 'mastodon/../material-icons/400-24px/bar_chart_4_bars.svg?react';
import CloseIcon from 'mastodon/../material-icons/400-24px/close.svg?react';
import PhotoLibraryIcon from 'mastodon/../material-icons/400-24px/photo_library.svg?react';
import { cancelReplyCompose } from 'mastodon/actions/compose';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
export const EditIndicator = () => {
const intl = useIntl();
const dispatch = useDispatch();
const id = useSelector(state => state.getIn(['compose', 'id']));
const status = useSelector(state => state.getIn(['statuses', id]));
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
const handleCancelClick = useCallback(() => {
dispatch(cancelReplyCompose());
}, [dispatch]);
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
return (
<div className='edit-indicator'>
<div className='edit-indicator__header'>
<div className='edit-indicator__display-name'>
<Link to={`/@${account.get('acct')}`}>@{account.get('acct')}</Link>
·
<Link to={`/@${account.get('acct')}/${status.get('id')}`}><RelativeTimestamp timestamp={status.get('created_at')} /></Link>
</div>
<div className='edit-indicator__cancel'>
<IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={handleCancelClick} inverted />
</div>
</div>
<div className='edit-indicator__content translate' dangerouslySetInnerHTML={content} />
{(status.get('poll') || status.get('media_attachments').size > 0) && (
<div className='edit-indicator__attachments'>
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>}
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>}
</div>
)}
</div>
);
};

View file

@ -10,6 +10,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay';
import MoodIcon from 'mastodon/../material-icons/400-24px/mood.svg?react';
import { IconButton } from 'mastodon/components/icon_button';
import { assetHost } from 'mastodon/utils/config';
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
@ -162,6 +164,7 @@ class EmojiPickerMenuImpl extends PureComponent {
intl: PropTypes.object.isRequired,
skinTone: PropTypes.number.isRequired,
onSkinTone: PropTypes.func.isRequired,
pickerButtonRef: PropTypes.func.isRequired
};
static defaultProps = {
@ -176,7 +179,7 @@ class EmojiPickerMenuImpl extends PureComponent {
};
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
if (this.node && !this.node.contains(e.target) && !this.props.pickerButtonRef.contains(e.target)) {
this.props.onClose();
}
};
@ -231,6 +234,7 @@ class EmojiPickerMenuImpl extends PureComponent {
emoji.native = emoji.colons;
}
if (!(event.ctrlKey || event.metaKey)) {
this.props.onClose();
}
this.props.onPick(emoji);
@ -321,7 +325,6 @@ class EmojiPickerDropdown extends PureComponent {
onPickEmoji: PropTypes.func.isRequired,
onSkinTone: PropTypes.func.isRequired,
skinTone: PropTypes.number.isRequired,
button: PropTypes.node,
};
state = {
@ -379,23 +382,24 @@ class EmojiPickerDropdown extends PureComponent {
};
render () {
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
const title = intl.formatMessage(messages.emoji);
const { active, loading } = this.state;
return (
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
<div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
{button || <img
className={classNames('emojione', { 'pulse-loading': active && loading })}
alt='🙂'
src={`${assetHost}/emoji/1f642.svg`}
/>}
</div>
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown} ref={this.setTargetRef}>
<IconButton
title={title}
aria-expanded={active}
active={active}
iconComponent={MoodIcon}
onClick={this.onToggle}
inverted
/>
<Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
{({ props, placement })=> (
<div {...props} style={{ ...props.style, width: 299 }}>
<div {...props} style={{ ...props.style }}>
<div className={`dropdown-animation ${placement}`}>
<EmojiPickerMenu
custom_emojis={this.props.custom_emojis}
@ -405,6 +409,7 @@ class EmojiPickerDropdown extends PureComponent {
onSkinTone={onSkinTone}
skinTone={skinTone}
frequentlyUsedEmojis={frequentlyUsedEmojis}
pickerButtonRef={this.target}
/>
</div>
</div>

View file

@ -9,10 +9,11 @@ import { supportsPassiveEvents } from 'detect-passive-events';
import fuzzysort from 'fuzzysort';
import Overlay from 'react-overlays/Overlay';
import CancelIcon from 'mastodon/../material-icons/400-24px/cancel-fill.svg?react';
import SearchIcon from 'mastodon/../material-icons/400-24px/search.svg?react';
import TranslateIcon from 'mastodon/../material-icons/400-24px/translate.svg?react';
import { Icon } from 'mastodon/components/icon';
import { languages as preloadedLanguages } from 'mastodon/initial_state';
import { loupeIcon, deleteIcon } from 'mastodon/utils/icons';
import TextIconButton from './text_icon_button';
const messages = defineMessages({
changeLanguage: { id: 'compose.language.change', defaultMessage: 'Change language' },
@ -231,7 +232,7 @@ class LanguageDropdownMenu extends PureComponent {
<div ref={this.setRef}>
<div className='emoji-mart-search'>
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}><Icon icon={!isSearching ? SearchIcon : CancelIcon} /></button>
</div>
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
@ -297,20 +298,24 @@ class LanguageDropdown extends PureComponent {
render () {
const { value, intl, frequentlyUsedLanguages } = this.props;
const { open, placement } = this.state;
const current = preloadedLanguages.find(lang => lang[0] === value) ?? [];
return (
<div className={classNames('privacy-dropdown', placement, { active: open })}>
<div className='privacy-dropdown__value' ref={this.setTargetRef} >
<TextIconButton
className='privacy-dropdown__value-icon'
label={value && value.toUpperCase()}
title={intl.formatMessage(messages.changeLanguage)}
active={open}
onClick={this.handleToggle}
/>
</div>
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
<button
type='button'
title={intl.formatMessage(messages.changeLanguage)}
aria-expanded={open}
onClick={this.handleToggle}
onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown}
className={classNames('dropdown-button', { active: open })}
>
<Icon icon={TranslateIcon} />
<span className='dropdown-button__label'>{current[2] ?? value}</span>
</button>
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
<Overlay show={open} offset={[5, 5]} placement={placement} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
{({ props, placement }) => (
<div {...props}>
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >

View file

@ -1,50 +1,36 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { useIntl, defineMessages } from 'react-intl';
import { Link } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import CloseIcon from 'mastodon/../material-icons/400-24px/close.svg?react';
import { cancelReplyCompose } from 'mastodon/actions/compose';
import Account from 'mastodon/components/account';
import { IconButton } from 'mastodon/components/icon_button';
import { me } from 'mastodon/initial_state';
import { Avatar } from '../../../components/avatar';
import { ActionBar } from './action_bar';
import ActionBar from './action_bar';
export default class NavigationBar extends ImmutablePureComponent {
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
static propTypes = {
account: ImmutablePropTypes.record.isRequired,
onLogout: PropTypes.func.isRequired,
onClose: PropTypes.func,
};
export const NavigationBar = () => {
const dispatch = useDispatch();
const intl = useIntl();
const account = useSelector(state => state.getIn(['accounts', me]));
const isReplying = useSelector(state => !!state.getIn(['compose', 'in_reply_to']));
render () {
const username = this.props.account.get('acct');
return (
<div className='navigation-bar'>
<Link to={`/@${username}`}>
<span style={{ display: 'none' }}>{username}</span>
<Avatar account={this.props.account} size={46} />
</Link>
const handleCancelClick = useCallback(() => {
dispatch(cancelReplyCompose());
}, [dispatch]);
<div className='navigation-bar__profile'>
<span>
<Link to={`/@${username}`}>
<strong className='navigation-bar__profile-account'>@{username}</strong>
</Link>
</span>
<span>
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
</span>
</div>
<div className='navigation-bar__actions'>
<ActionBar account={this.props.account} onLogout={this.props.onLogout} />
</div>
</div>
);
}
}
return (
<div className='navigation-bar'>
<Account account={account} minimal />
{isReplying ? <IconButton title={intl.formatMessage(messages.cancel)} iconComponent={CloseIcon} onClick={handleCancelClick} /> : <ActionBar />}
</div>
);
};

View file

@ -3,11 +3,10 @@ import { PureComponent } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' },
remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' },
@ -22,7 +21,6 @@ class PollButton extends PureComponent {
static propTypes = {
disabled: PropTypes.bool,
unavailable: PropTypes.bool,
active: PropTypes.bool,
onClick: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
@ -33,17 +31,13 @@ class PollButton extends PureComponent {
};
render () {
const { intl, active, unavailable, disabled } = this.props;
if (unavailable) {
return null;
}
const { intl, active, disabled } = this.props;
return (
<div className='compose-form__poll-button'>
<IconButton
icon='tasks'
iconComponent={InsertChartIcon}
iconComponent={BarChart4BarsIcon}
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
disabled={disabled}
onClick={this.handleClick}

View file

@ -1,189 +1,160 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { useCallback } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, useIntl } from 'react-intl';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { useDispatch, useSelector } from 'react-redux';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import {
changePollSettings,
changePollOption,
clearComposeSuggestions,
fetchComposeSuggestions,
selectComposeSuggestion,
} from 'mastodon/actions/compose';
import AutosuggestInput from 'mastodon/components/autosuggest_input';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
const messages = defineMessages({
option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' },
add_option: { id: 'compose_form.poll.add_option', defaultMessage: 'Add a choice' },
remove_option: { id: 'compose_form.poll.remove_option', defaultMessage: 'Remove this choice' },
poll_duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll duration' },
option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Option {number}' },
duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll length' },
type: { id: 'compose_form.poll.type', defaultMessage: 'Style' },
switchToMultiple: { id: 'compose_form.poll.switch_to_multiple', defaultMessage: 'Change poll to allow multiple choices' },
switchToSingle: { id: 'compose_form.poll.switch_to_single', defaultMessage: 'Change poll to allow for a single choice' },
minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
singleChoice: { id: 'compose_form.poll.single', defaultMessage: 'Pick one' },
multipleChoice: { id: 'compose_form.poll.multiple', defaultMessage: 'Multiple choice' },
});
class OptionIntl extends PureComponent {
const Select = ({ label, options, value, onChange }) => {
return (
<label className='compose-form__poll__select'>
<span className='compose-form__poll__select__label'>{label}</span>
static propTypes = {
title: PropTypes.string.isRequired,
lang: PropTypes.string,
index: PropTypes.number.isRequired,
isPollMultiple: PropTypes.bool,
autoFocus: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
onToggleMultiple: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list,
onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
<select className='compose-form__poll__select__value' value={value} onChange={onChange}>
{options.map((option, i) => (
<option key={i} value={option.value}>{option.label}</option>
))}
</select>
</label>
);
};
handleOptionTitleChange = e => {
this.props.onChange(this.props.index, e.target.value);
};
Select.propTypes = {
label: PropTypes.node,
value: PropTypes.any,
onChange: PropTypes.func,
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.node,
value: PropTypes.any,
})),
};
handleOptionRemove = () => {
this.props.onRemove(this.props.index);
};
const Option = ({ multipleChoice, index, title, autoFocus }) => {
const intl = useIntl();
const dispatch = useDispatch();
const suggestions = useSelector(state => state.getIn(['compose', 'suggestions']));
const lang = useSelector(state => state.getIn(['compose', 'language']));
const handleChange = useCallback(({ target: { value } }) => {
dispatch(changePollOption(index, value));
}, [dispatch, index]);
handleToggleMultiple = e => {
this.props.onToggleMultiple();
e.preventDefault();
e.stopPropagation();
};
const handleSuggestionsFetchRequested = useCallback(token => {
dispatch(fetchComposeSuggestions(token));
}, [dispatch]);
handleCheckboxKeypress = e => {
if (e.key === 'Enter' || e.key === ' ') {
this.handleToggleMultiple(e);
}
};
const handleSuggestionsClearRequested = useCallback(() => {
dispatch(clearComposeSuggestions());
}, [dispatch]);
onSuggestionsClearRequested = () => {
this.props.onClearSuggestions();
};
const handleSuggestionSelected = useCallback((tokenStart, token, value) => {
dispatch(selectComposeSuggestion(tokenStart, token, value, ['poll', 'options', index]));
}, [dispatch, index]);
onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token);
};
return (
<label className={classNames('poll__option editable', { empty: index > 1 && title.length === 0 })}>
<span className={classNames('poll__input', { checkbox: multipleChoice })} />
onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
};
<AutosuggestInput
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
maxLength={50}
value={title}
lang={lang}
spellCheck
onChange={handleChange}
suggestions={suggestions}
onSuggestionsFetchRequested={handleSuggestionsFetchRequested}
onSuggestionsClearRequested={handleSuggestionsClearRequested}
onSuggestionSelected={handleSuggestionSelected}
searchTokens={[':']}
autoFocus={autoFocus}
/>
</label>
);
};
render () {
const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
Option.propTypes = {
title: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
multipleChoice: PropTypes.bool,
autoFocus: PropTypes.bool,
};
return (
<li>
<label className='poll__option editable'>
<span
className={classNames('poll__input', { checkbox: isPollMultiple })}
onClick={this.handleToggleMultiple}
onKeyPress={this.handleCheckboxKeypress}
role='button'
tabIndex={0}
title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
/>
export const PollForm = () => {
const intl = useIntl();
const dispatch = useDispatch();
const poll = useSelector(state => state.getIn(['compose', 'poll']));
const options = poll?.get('options');
const expiresIn = poll?.get('expires_in');
const isMultiple = poll?.get('multiple');
<AutosuggestInput
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
maxLength={50}
value={title}
lang={lang}
spellCheck
onChange={this.handleOptionTitleChange}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
searchTokens={[':']}
autoFocus={autoFocus}
/>
</label>
const handleDurationChange = useCallback(({ target: { value } }) => {
dispatch(changePollSettings(value, isMultiple));
}, [dispatch, isMultiple]);
<div className='poll__cancel'>
<IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} />
</div>
</li>
);
const handleTypeChange = useCallback(({ target: { value } }) => {
dispatch(changePollSettings(expiresIn, value === 'true'));
}, [dispatch, expiresIn]);
if (poll === null) {
return null;
}
}
return (
<div className='compose-form__poll'>
{options.map((title, i) => (
<Option
title={title}
key={i}
index={i}
multipleChoice={isMultiple}
autoFocus={i === 0}
/>
))}
const Option = injectIntl(OptionIntl);
<div className='compose-form__poll__footer'>
<Select label={intl.formatMessage(messages.duration)} options={[
{ value: 300, label: intl.formatMessage(messages.minutes, { number: 5 })},
{ value: 1800, label: intl.formatMessage(messages.minutes, { number: 30 })},
{ value: 3600, label: intl.formatMessage(messages.hours, { number: 1 })},
{ value: 21600, label: intl.formatMessage(messages.hours, { number: 6 })},
{ value: 43200, label: intl.formatMessage(messages.hours, { number: 12 })},
{ value: 86400, label: intl.formatMessage(messages.days, { number: 1 })},
{ value: 259200, label: intl.formatMessage(messages.days, { number: 3 })},
{ value: 604800, label: intl.formatMessage(messages.days, { number: 7 })},
]} value={expiresIn} onChange={handleDurationChange} />
class PollForm extends ImmutablePureComponent {
<div className='compose-form__poll__footer__sep' />
static propTypes = {
options: ImmutablePropTypes.list,
lang: PropTypes.string,
expiresIn: PropTypes.number,
isMultiple: PropTypes.bool,
onChangeOption: PropTypes.func.isRequired,
onAddOption: PropTypes.func.isRequired,
onRemoveOption: PropTypes.func.isRequired,
onChangeSettings: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list,
onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleAddOption = () => {
this.props.onAddOption('');
};
handleSelectDuration = e => {
this.props.onChangeSettings(e.target.value, this.props.isMultiple);
};
handleToggleMultiple = () => {
this.props.onChangeSettings(this.props.expiresIn, !this.props.isMultiple);
};
render () {
const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
if (!options) {
return null;
}
const autoFocusIndex = options.indexOf('');
return (
<div className='compose-form__poll-wrapper'>
<ul>
{options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
</ul>
<div className='poll__footer'>
<button type='button' disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button>
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
<select value={expiresIn} onChange={this.handleSelectDuration}>
<option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
<option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
<option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
<option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
<option value={43200}>{intl.formatMessage(messages.hours, { number: 12 })}</option>
<option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
<option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
<option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
</select>
</div>
<Select label={intl.formatMessage(messages.type)} options={[
{ value: false, label: intl.formatMessage(messages.singleChoice) },
{ value: true, label: intl.formatMessage(messages.multipleChoice) },
]} value={isMultiple} onChange={handleTypeChange} />
</div>
);
}
}
export default injectIntl(PollForm);
</div>
);
};

View file

@ -5,28 +5,27 @@ import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
public_long: { id: 'privacy.public.long', defaultMessage: 'Visible for all' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Visible for all, but opted-out of discovery features' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
private_long: { id: 'privacy.private.long', defaultMessage: 'Visible for followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Visible for mentioned users only' },
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' },
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Fewer algorithmic fanfares' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' },
private_long: { id: 'privacy.private.long', defaultMessage: 'Only your followers' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Everyone mentioned in the post' },
change_privacy: { id: 'privacy.change', defaultMessage: 'Change post privacy' },
unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' },
});
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
@ -135,6 +134,12 @@ class PrivacyDropdownMenu extends PureComponent {
<strong>{item.text}</strong>
{item.meta}
</div>
{item.extra && (
<div className='privacy-dropdown__option__additional' title={item.extra}>
<Icon id='info-circle' icon={InfoIcon} />
</div>
)}
</div>
))}
</div>
@ -163,30 +168,11 @@ class PrivacyDropdown extends PureComponent {
};
handleToggle = () => {
if (this.props.isUserTouching && this.props.isUserTouching()) {
if (this.state.open) {
this.props.onModalClose();
} else {
this.props.onModalOpen({
actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
onClick: this.handleModalActionClick,
});
}
} else {
if (this.state.open && this.activeElement) {
this.activeElement.focus({ preventScroll: true });
}
this.setState({ open: !this.state.open });
if (this.state.open && this.activeElement) {
this.activeElement.focus({ preventScroll: true });
}
};
handleModalActionClick = (e) => {
e.preventDefault();
const { value } = this.options[e.currentTarget.getAttribute('data-index')];
this.props.onModalClose();
this.props.onChange(value);
this.setState({ open: !this.state.open });
};
handleKeyDown = e => {
@ -228,7 +214,7 @@ class PrivacyDropdown extends PureComponent {
this.options = [
{ icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'unlock', iconComponent: LockOpenIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
{ icon: 'unlock', iconComponent: QuietTimeIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long), extra: formatMessage(messages.unlisted_extra) },
{ icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
];
@ -259,23 +245,21 @@ class PrivacyDropdown extends PureComponent {
return (
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
<IconButton
className='privacy-dropdown__value-icon'
icon={valueOption.icon}
iconComponent={valueOption.iconComponent}
<button
type='button'
title={intl.formatMessage(messages.change_privacy)}
size={18}
expanded={open}
active={open}
inverted
aria-expanded={open}
onClick={this.handleToggle}
onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown}
style={{ height: null, lineHeight: '27px' }}
disabled={disabled}
/>
className={classNames('dropdown-button', { active: open })}
>
<Icon id={valueOption.icon} icon={valueOption.iconComponent} />
<span className='dropdown-button__label'>{valueOption.text}</span>
</button>
<Overlay show={open} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
<Overlay show={open} offset={[5, 5]} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
{({ props, placement }) => (
<div {...props}>
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>

View file

@ -1,74 +1,48 @@
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { useSelector } from 'react-redux';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import AttachmentList from 'mastodon/components/attachment_list';
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
import { Avatar } from 'mastodon/components/avatar';
import { DisplayName } from 'mastodon/components/display_name';
import { Icon } from 'mastodon/components/icon';
import { Avatar } from '../../../components/avatar';
import { DisplayName } from '../../../components/display_name';
import { IconButton } from '../../../components/icon_button';
export const ReplyIndicator = () => {
const inReplyToId = useSelector(state => state.getIn(['compose', 'in_reply_to']));
const status = useSelector(state => state.getIn(['statuses', inReplyToId]));
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
if (!status) {
return null;
}
class ReplyIndicator extends ImmutablePureComponent {
const content = { __html: status.get('contentHtml') };
static propTypes = {
status: ImmutablePropTypes.map,
onCancel: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
...WithOptionalRouterPropTypes,
};
return (
<div className='reply-indicator'>
<div className='reply-indicator__line' />
handleClick = () => {
this.props.onCancel();
};
<Link to={`/@${account.get('acct')}`} className='detailed-status__display-avatar'>
<Avatar account={account} size={46} />
</Link>
handleAccountClick = (e) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
this.props.history?.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
}
};
render () {
const { status, intl } = this.props;
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
return (
<div className='reply-indicator'>
<div className='reply-indicator__header'>
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={this.handleClick} inverted /></div>
<a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
<DisplayName account={status.get('account')} />
</a>
</div>
<div className='reply-indicator__main'>
<Link to={`/@${account.get('acct')}`} className='detailed-status__display-name'>
<DisplayName account={account} />
</Link>
<div className='reply-indicator__content translate' dangerouslySetInnerHTML={content} />
{status.get('media_attachments').size > 0 && (
<AttachmentList
compact
media={status.get('media_attachments')}
/>
{(status.get('poll') || status.get('media_attachments').size > 0) && (
<div className='reply-indicator__attachments'>
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>}
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>}
</div>
)}
</div>
);
}
}
export default withOptionalRouter(injectIntl(ReplyIndicator));
</div>
);
};

View file

@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
@ -9,7 +11,8 @@ import spring from 'react-motion/lib/spring';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon';
import Motion from '../../ui/util/optional_motion';
@ -18,6 +21,7 @@ export default class Upload extends ImmutablePureComponent {
static propTypes = {
media: ImmutablePropTypes.map.isRequired,
sensitive: PropTypes.bool,
onUndo: PropTypes.func.isRequired,
onOpenFocalPoint: PropTypes.func.isRequired,
};
@ -33,7 +37,7 @@ export default class Upload extends ImmutablePureComponent {
};
render () {
const { media } = this.props;
const { media, sensitive } = this.props;
if (!media) {
return null;
@ -43,22 +47,26 @@ export default class Upload extends ImmutablePureComponent {
const focusY = media.getIn(['meta', 'focus', 'y']);
const x = ((focusX / 2) + .5) * 100;
const y = ((focusY / -2) + .5) * 100;
const missingDescription = (media.get('description') || '').length === 0;
return (
<div className='compose-form__upload'>
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
{({ scale }) => (
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
<div className='compose-form__upload__thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: !sensitive ? `url(${media.get('preview_url')})` : null, backgroundPosition: `${x}% ${y}%` }}>
{sensitive && <Blurhash
hash={media.get('blurhash')}
className='compose-form__upload__preview'
/>}
<div className='compose-form__upload__actions'>
<button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
<button type='button' className='icon-button compose-form__upload__delete' onClick={this.handleUndoClick}><Icon icon={CloseIcon} /></button>
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
</div>
{(media.get('description') || '').length === 0 && (
<div className='compose-form__upload__warning'>
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
</div>
)}
<div className='compose-form__upload__warning'>
<button type='button' className={classNames('icon-button', { active: missingDescription })} onClick={this.handleFocalPointClick}>{missingDescription && <Icon icon={WarningIcon} />} ALT</button>
</div>
</div>
)}
</Motion>

View file

@ -6,9 +6,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate.svg?react';
import { IconButton } from '../../../components/icon_button';
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
import { IconButton } from 'mastodon/components/icon_button';
const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add images, a video or an audio file' },
@ -31,7 +30,6 @@ class UploadButton extends ImmutablePureComponent {
static propTypes = {
disabled: PropTypes.bool,
unavailable: PropTypes.bool,
onSelectFile: PropTypes.func.isRequired,
style: PropTypes.object,
resetFileKey: PropTypes.number,
@ -54,17 +52,13 @@ class UploadButton extends ImmutablePureComponent {
};
render () {
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes } = this.props;
if (unavailable) {
return null;
}
const { intl, resetFileKey, disabled, acceptContentTypes } = this.props;
const message = intl.formatMessage(messages.upload);
return (
<div className='compose-form__upload-button'>
<IconButton icon='paperclip' iconComponent={AddPhotoAlternateIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
<IconButton icon='paperclip' iconComponent={PhotoLibraryIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
<label>
<span style={{ display: 'none' }}>{message}</span>
<input

View file

@ -1,7 +1,6 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import SensitiveButtonContainer from '../containers/sensitive_button_container';
import UploadContainer from '../containers/upload_container';
import UploadProgressContainer from '../containers/upload_progress_container';
@ -15,17 +14,17 @@ export default class UploadForm extends ImmutablePureComponent {
const { mediaIds } = this.props;
return (
<div className='compose-form__upload-wrapper'>
<>
<UploadProgressContainer />
<div className='compose-form__uploads-wrapper'>
{mediaIds.map(id => (
<UploadContainer id={id} key={id} />
))}
</div>
{!mediaIds.isEmpty() && <SensitiveButtonContainer />}
</div>
{mediaIds.size > 0 && (
<div className='compose-form__uploads'>
{mediaIds.map(id => (
<UploadContainer id={id} key={id} />
))}
</div>
)}
</>
);
}

View file

@ -35,9 +35,7 @@ export default class UploadProgress extends PureComponent {
return (
<div className='upload-progress'>
<div className='upload-progress__icon'>
<Icon id='upload' icon={UploadFileIcon} />
</div>
<Icon id='upload' icon={UploadFileIcon} />
<div className='upload-progress__message'>
{message}

View file

@ -28,6 +28,7 @@ const mapStateToProps = state => ({
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
lang: state.getIn(['compose', 'language']),
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
});
const mapDispatchToProps = (dispatch) => ({

View file

@ -1,36 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { openModal } from 'mastodon/actions/modal';
import { logOut } from 'mastodon/utils/log_out';
import { me } from '../../../initial_state';
import NavigationBar from '../components/navigation_bar';
const messages = defineMessages({
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
});
const mapStateToProps = state => {
return {
account: state.getIn(['accounts', me]),
};
};
const mapDispatchToProps = (dispatch, { intl }) => ({
onLogout () {
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
closeWhenConfirm: false,
onConfirm: () => logOut(),
},
}));
},
});
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NavigationBar));

View file

@ -4,7 +4,7 @@ import { addPoll, removePoll } from '../../../actions/compose';
import PollButton from '../components/poll_button';
const mapStateToProps = state => ({
unavailable: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
active: state.getIn(['compose', 'poll']) !== null,
});

View file

@ -1,53 +0,0 @@
import { connect } from 'react-redux';
import {
addPollOption,
removePollOption,
changePollOption,
changePollSettings,
clearComposeSuggestions,
fetchComposeSuggestions,
selectComposeSuggestion,
} from '../../../actions/compose';
import PollForm from '../components/poll_form';
const mapStateToProps = state => ({
suggestions: state.getIn(['compose', 'suggestions']),
options: state.getIn(['compose', 'poll', 'options']),
lang: state.getIn(['compose', 'language']),
expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
isMultiple: state.getIn(['compose', 'poll', 'multiple']),
});
const mapDispatchToProps = dispatch => ({
onAddOption(title) {
dispatch(addPollOption(title));
},
onRemoveOption(index) {
dispatch(removePollOption(index));
},
onChangeOption(index, title) {
dispatch(changePollOption(index, title));
},
onChangeSettings(expiresIn, isMultiple) {
dispatch(changePollSettings(expiresIn, isMultiple));
},
onClearSuggestions () {
dispatch(clearComposeSuggestions());
},
onFetchSuggestions (token) {
dispatch(fetchComposeSuggestions(token));
},
onSuggestionSelected (position, token, accountId, path) {
dispatch(selectComposeSuggestion(position, token, accountId, path));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(PollForm);

View file

@ -1,36 +0,0 @@
import { connect } from 'react-redux';
import { cancelReplyCompose } from '../../../actions/compose';
import { makeGetStatus } from '../../../selectors';
import ReplyIndicator from '../components/reply_indicator';
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = state => {
let statusId = state.getIn(['compose', 'id'], null);
let editing = true;
if (statusId === null) {
statusId = state.getIn(['compose', 'in_reply_to']);
editing = false;
}
return {
status: getStatus(state, { id: statusId }),
editing,
};
};
return mapStateToProps;
};
const mapDispatchToProps = dispatch => ({
onCancel () {
dispatch(cancelReplyCompose());
},
});
export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);

View file

@ -1,73 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { connect } from 'react-redux';
import { changeComposeSensitivity } from 'mastodon/actions/compose';
const messages = defineMessages({
marked: {
id: 'compose_form.sensitive.marked',
defaultMessage: '{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}',
},
unmarked: {
id: 'compose_form.sensitive.unmarked',
defaultMessage: '{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}',
},
});
const mapStateToProps = state => ({
active: state.getIn(['compose', 'sensitive']),
disabled: state.getIn(['compose', 'spoiler']),
mediaCount: state.getIn(['compose', 'media_attachments']).size,
});
const mapDispatchToProps = dispatch => ({
onClick () {
dispatch(changeComposeSensitivity());
},
});
class SensitiveButton extends PureComponent {
static propTypes = {
active: PropTypes.bool,
disabled: PropTypes.bool,
mediaCount: PropTypes.number,
onClick: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
render () {
const { active, disabled, mediaCount, onClick, intl } = this.props;
return (
<div className='compose-form__sensitive-button'>
<label className={classNames('icon-button', { active })} title={intl.formatMessage(active ? messages.marked : messages.unmarked, { count: mediaCount })}>
<input
name='mark-sensitive'
type='checkbox'
checked={active}
onChange={onClick}
disabled={disabled}
/>
<FormattedMessage
id='compose_form.sensitive.hide'
defaultMessage='{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}'
values={{ count: mediaCount }}
/>
</label>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));

View file

@ -2,8 +2,10 @@ import { injectIntl, defineMessages } from 'react-intl';
import { connect } from 'react-redux';
import WarningIcon from 'mastodon/../material-icons/400-24px/warning.svg?react';
import { IconButton } from 'mastodon/components/icon_button';
import { changeComposeSpoilerness } from '../../../actions/compose';
import TextIconButton from '../components/text_icon_button';
const messages = defineMessages({
marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' },
@ -11,10 +13,12 @@ const messages = defineMessages({
});
const mapStateToProps = (state, { intl }) => ({
label: 'CW',
iconComponent: WarningIcon,
title: intl.formatMessage(state.getIn(['compose', 'spoiler']) ? messages.marked : messages.unmarked),
active: state.getIn(['compose', 'spoiler']),
ariaControls: 'cw-spoiler-input',
size: 18,
inverted: true,
});
const mapDispatchToProps = dispatch => ({
@ -25,4 +29,4 @@ const mapDispatchToProps = dispatch => ({
});
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextIconButton));
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(IconButton));

View file

@ -4,8 +4,7 @@ import { uploadCompose } from '../../../actions/compose';
import UploadButton from '../components/upload_button';
const mapStateToProps = state => ({
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size + state.getIn(['compose', 'pending_media_attachments']) > 3 || state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')))),
unavailable: state.getIn(['compose', 'poll']) !== null,
disabled: state.getIn(['compose', 'poll']) !== null || state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size + state.getIn(['compose', 'pending_media_attachments']) > 3 || state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')))),
resetFileKey: state.getIn(['compose', 'resetFileKey']),
});

View file

@ -5,6 +5,7 @@ import Upload from '../components/upload';
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
sensitive: state.getIn(['compose', 'spoiler']),
});
const mapDispatchToProps = dispatch => ({

View file

@ -30,7 +30,6 @@ import { isMobile } from '../../is_mobile';
import Motion from '../ui/util/optional_motion';
import ComposeFormContainer from './containers/compose_form_container';
import NavigationContainer from './containers/navigation_container';
import SearchContainer from './containers/search_container';
import SearchResultsContainer from './containers/search_results_container';
@ -129,8 +128,6 @@ class Compose extends PureComponent {
<div className='drawer__pager'>
<div className='drawer__inner' onFocus={this.onFocus}>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer autoFocus={!isMobile(window.innerWidth)} />
<div className='drawer__inner__mastodon'>
@ -152,7 +149,6 @@ class Compose extends PureComponent {
return (
<Column onFocus={this.onFocus}>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer />
<Helmet>

View file

@ -1,17 +1,24 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link, withRouter } from 'react-router-dom';
import { Link, useHistory } from 'react-router-dom';
import { createSelector } from '@reduxjs/toolkit';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { useDispatch, useSelector } from 'react-redux';
import { HotKeys } from 'react-hotkeys';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import { replyCompose } from 'mastodon/actions/compose';
import { markConversationRead, deleteConversation } from 'mastodon/actions/conversations';
import { openModal } from 'mastodon/actions/modal';
import { muteStatus, unmuteStatus, revealStatus, hideStatus } from 'mastodon/actions/statuses';
import AttachmentList from 'mastodon/components/attachment_list';
import AvatarComposite from 'mastodon/components/avatar_composite';
import { IconButton } from 'mastodon/components/icon_button';
@ -19,7 +26,7 @@ import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import StatusContent from 'mastodon/components/status_content';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import { autoPlayGif } from 'mastodon/initial_state';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { makeGetStatus } from 'mastodon/selectors';
const messages = defineMessages({
more: { id: 'status.more', defaultMessage: 'More' },
@ -29,25 +36,31 @@ const messages = defineMessages({
delete: { id: 'conversation.delete', defaultMessage: 'Delete conversation' },
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
});
class Conversation extends ImmutablePureComponent {
const getAccounts = createSelector(
(state) => state.get('accounts'),
(_, accountIds) => accountIds,
(accounts, accountIds) =>
accountIds.map(id => accounts.get(id))
);
static propTypes = {
conversationId: PropTypes.string.isRequired,
accounts: ImmutablePropTypes.list.isRequired,
lastStatus: ImmutablePropTypes.map,
unread:PropTypes.bool.isRequired,
scrollKey: PropTypes.string,
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
markRead: PropTypes.func.isRequired,
delete: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
...WithRouterPropTypes,
};
const getStatus = makeGetStatus();
handleMouseEnter = ({ currentTarget }) => {
export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown }) => {
const id = conversation.get('id');
const unread = conversation.get('unread');
const lastStatusId = conversation.get('last_status');
const accountIds = conversation.get('accounts');
const intl = useIntl();
const dispatch = useDispatch();
const history = useHistory();
const lastStatus = useSelector(state => getStatus(state, { id: lastStatusId }));
const accounts = useSelector(state => getAccounts(state, accountIds));
const handleMouseEnter = useCallback(({ currentTarget }) => {
if (autoPlayGif) {
return;
}
@ -58,9 +71,9 @@ class Conversation extends ImmutablePureComponent {
let emoji = emojis[i];
emoji.src = emoji.getAttribute('data-original');
}
};
}, []);
handleMouseLeave = ({ currentTarget }) => {
const handleMouseLeave = useCallback(({ currentTarget }) => {
if (autoPlayGif) {
return;
}
@ -71,136 +84,161 @@ class Conversation extends ImmutablePureComponent {
let emoji = emojis[i];
emoji.src = emoji.getAttribute('data-static');
}
};
handleClick = () => {
if (!this.props.history) {
return;
}
const { lastStatus, unread, markRead } = this.props;
}, []);
const handleClick = useCallback(() => {
if (unread) {
markRead();
dispatch(markConversationRead(id));
}
this.props.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
};
history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
}, [dispatch, history, unread, id, lastStatus]);
handleMarkAsRead = () => {
this.props.markRead();
};
const handleMarkAsRead = useCallback(() => {
dispatch(markConversationRead(id));
}, [dispatch, id]);
handleReply = () => {
this.props.reply(this.props.lastStatus, this.props.history);
};
const handleReply = useCallback(() => {
dispatch((_, getState) => {
let state = getState();
handleDelete = () => {
this.props.delete();
};
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.replyMessage),
confirm: intl.formatMessage(messages.replyConfirm),
onConfirm: () => dispatch(replyCompose(lastStatus, history)),
},
}));
} else {
dispatch(replyCompose(lastStatus, history));
}
});
}, [dispatch, lastStatus, history, intl]);
handleHotkeyMoveUp = () => {
this.props.onMoveUp(this.props.conversationId);
};
const handleDelete = useCallback(() => {
dispatch(deleteConversation(id));
}, [dispatch, id]);
handleHotkeyMoveDown = () => {
this.props.onMoveDown(this.props.conversationId);
};
const handleHotkeyMoveUp = useCallback(() => {
onMoveUp(id);
}, [id, onMoveUp]);
handleConversationMute = () => {
this.props.onMute(this.props.lastStatus);
};
const handleHotkeyMoveDown = useCallback(() => {
onMoveDown(id);
}, [id, onMoveDown]);
handleShowMore = () => {
this.props.onToggleHidden(this.props.lastStatus);
};
render () {
const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
if (lastStatus === null) {
return null;
const handleConversationMute = useCallback(() => {
if (lastStatus.get('muted')) {
dispatch(unmuteStatus(lastStatus.get('id')));
} else {
dispatch(muteStatus(lastStatus.get('id')));
}
}, [dispatch, lastStatus]);
const menu = [
{ text: intl.formatMessage(messages.open), action: this.handleClick },
null,
];
menu.push({ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMute });
if (unread) {
menu.push({ text: intl.formatMessage(messages.markAsRead), action: this.handleMarkAsRead });
menu.push(null);
const handleShowMore = useCallback(() => {
if (lastStatus.get('hidden')) {
dispatch(revealStatus(lastStatus.get('id')));
} else {
dispatch(hideStatus(lastStatus.get('id')));
}
}, [dispatch, lastStatus]);
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDelete });
if (!lastStatus) {
return null;
}
const names = accounts.map(a => <Link to={`/@${a.get('acct')}`} key={a.get('id')} title={a.get('acct')}><bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi></Link>).reduce((prev, cur) => [prev, ', ', cur]);
const menu = [
{ text: intl.formatMessage(messages.open), action: handleClick },
null,
{ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: handleConversationMute },
];
const handlers = {
reply: this.handleReply,
open: this.handleClick,
moveUp: this.handleHotkeyMoveUp,
moveDown: this.handleHotkeyMoveDown,
toggleHidden: this.handleShowMore,
};
if (unread) {
menu.push({ text: intl.formatMessage(messages.markAsRead), action: handleMarkAsRead });
menu.push(null);
}
return (
<HotKeys handlers={handlers}>
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
<div className='conversation__avatar' onClick={this.handleClick} role='presentation'>
<AvatarComposite accounts={accounts} size={48} />
</div>
menu.push({ text: intl.formatMessage(messages.delete), action: handleDelete });
<div className='conversation__content'>
<div className='conversation__content__info'>
<div className='conversation__content__relative-time'>
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
</div>
const names = accounts.map(a => (
<Link to={`/@${a.get('acct')}`} key={a.get('id')} title={a.get('acct')}>
<bdi>
<strong
className='display-name__html'
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
/>
</bdi>
</Link>
)).reduce((prev, cur) => [prev, ', ', cur]);
<div className='conversation__content__names' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
</div>
const handlers = {
reply: handleReply,
open: handleClick,
moveUp: handleHotkeyMoveUp,
moveDown: handleHotkeyMoveDown,
toggleHidden: handleShowMore,
};
return (
<HotKeys handlers={handlers}>
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
<div className='conversation__avatar' onClick={handleClick} role='presentation'>
<AvatarComposite accounts={accounts} size={48} />
</div>
<div className='conversation__content'>
<div className='conversation__content__info'>
<div className='conversation__content__relative-time'>
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
</div>
<StatusContent
status={lastStatus}
onClick={this.handleClick}
expanded={!lastStatus.get('hidden')}
onExpandedToggle={this.handleShowMore}
collapsible
<div className='conversation__content__names' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
</div>
</div>
<StatusContent
status={lastStatus}
onClick={handleClick}
expanded={!lastStatus.get('hidden')}
onExpandedToggle={handleShowMore}
collapsible
/>
{lastStatus.get('media_attachments').size > 0 && (
<AttachmentList
compact
media={lastStatus.get('media_attachments')}
/>
)}
{lastStatus.get('media_attachments').size > 0 && (
<AttachmentList
compact
media={lastStatus.get('media_attachments')}
<div className='status__action-bar'>
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={handleReply} />
<div className='status__action-bar-dropdown'>
<DropdownMenuContainer
scrollKey={scrollKey}
status={lastStatus}
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
size={18}
direction='right'
title={intl.formatMessage(messages.more)}
/>
)}
<div className='status__action-bar'>
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={this.handleReply} />
<div className='status__action-bar-dropdown'>
<DropdownMenuContainer
scrollKey={scrollKey}
status={lastStatus}
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
size={18}
direction='right'
title={intl.formatMessage(messages.more)}
/>
</div>
</div>
</div>
</div>
</HotKeys>
);
}
</div>
</HotKeys>
);
};
}
export default withRouter(injectIntl(Conversation));
Conversation.propTypes = {
conversation: ImmutablePropTypes.map.isRequired,
scrollKey: PropTypes.string,
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
};

View file

@ -1,77 +1,72 @@
import PropTypes from 'prop-types';
import { useRef, useMemo, useCallback } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { useSelector, useDispatch } from 'react-redux';
import { debounce } from 'lodash';
import ScrollableList from '../../../components/scrollable_list';
import ConversationContainer from '../containers/conversation_container';
import { expandConversations } from 'mastodon/actions/conversations';
import ScrollableList from 'mastodon/components/scrollable_list';
export default class ConversationsList extends ImmutablePureComponent {
import { Conversation } from './conversation';
static propTypes = {
conversations: ImmutablePropTypes.list.isRequired,
scrollKey: PropTypes.string.isRequired,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
onLoadMore: PropTypes.func,
};
const focusChild = (node, index, alignTop) => {
const element = node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id);
handleMoveUp = id => {
const elementIndex = this.getCurrentIndex(id) - 1;
this._selectChild(elementIndex, true);
};
handleMoveDown = id => {
const elementIndex = this.getCurrentIndex(id) + 1;
this._selectChild(elementIndex, false);
};
_selectChild (index, align_top) {
const container = this.node.node;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
if (element) {
if (align_top && container.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
}
element.focus();
if (element) {
if (alignTop && node.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
} else if (!alignTop && node.scrollTop + node.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
}
element.focus();
}
};
setRef = c => {
this.node = c;
};
export const ConversationsList = ({ scrollKey, ...other }) => {
const listRef = useRef();
const conversations = useSelector(state => state.getIn(['conversations', 'items']));
const isLoading = useSelector(state => state.getIn(['conversations', 'isLoading'], true));
const hasMore = useSelector(state => state.getIn(['conversations', 'hasMore'], false));
const dispatch = useDispatch();
const lastStatusId = conversations.last()?.get('last_status');
handleLoadOlder = debounce(() => {
const last = this.props.conversations.last();
const handleMoveUp = useCallback(id => {
const elementIndex = conversations.findIndex(x => x.get('id') === id) - 1;
focusChild(listRef.current.node, elementIndex, true);
}, [listRef, conversations]);
if (last && last.get('last_status')) {
this.props.onLoadMore(last.get('last_status'));
const handleMoveDown = useCallback(id => {
const elementIndex = conversations.findIndex(x => x.get('id') === id) + 1;
focusChild(listRef.current.node, elementIndex, false);
}, [listRef, conversations]);
const debouncedLoadMore = useMemo(() => debounce(id => {
dispatch(expandConversations({ maxId: id }));
}, 300, { leading: true }), [dispatch]);
const handleLoadMore = useCallback(() => {
if (lastStatusId) {
debouncedLoadMore(lastStatusId);
}
}, 300, { leading: true });
}, [debouncedLoadMore, lastStatusId]);
render () {
const { conversations, isLoading, onLoadMore, ...other } = this.props;
return (
<ScrollableList {...other} scrollKey={scrollKey} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} hasMore={hasMore} onLoadMore={handleLoadMore} ref={listRef}>
{conversations.map(item => (
<Conversation
key={item.get('id')}
conversation={item}
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
scrollKey={scrollKey}
/>
))}
</ScrollableList>
);
};
return (
<ScrollableList {...other} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
{conversations.map(item => (
<ConversationContainer
key={item.get('id')}
conversationId={item.get('id')}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
scrollKey={this.props.scrollKey}
/>
))}
</ScrollableList>
);
}
}
ConversationsList.propTypes = {
scrollKey: PropTypes.string.isRequired,
};

View file

@ -1,80 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { replyCompose } from 'mastodon/actions/compose';
import { markConversationRead, deleteConversation } from 'mastodon/actions/conversations';
import { openModal } from 'mastodon/actions/modal';
import { muteStatus, unmuteStatus, hideStatus, revealStatus } from 'mastodon/actions/statuses';
import { makeGetStatus } from 'mastodon/selectors';
import Conversation from '../components/conversation';
const messages = defineMessages({
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
});
const mapStateToProps = () => {
const getStatus = makeGetStatus();
return (state, { conversationId }) => {
const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
const lastStatusId = conversation.get('last_status', null);
return {
accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
unread: conversation.get('unread'),
lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }),
};
};
};
const mapDispatchToProps = (dispatch, { intl, conversationId }) => ({
markRead () {
dispatch(markConversationRead(conversationId));
},
reply (status, router) {
dispatch((_, getState) => {
let state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.replyMessage),
confirm: intl.formatMessage(messages.replyConfirm),
onConfirm: () => dispatch(replyCompose(status, router)),
},
}));
} else {
dispatch(replyCompose(status, router));
}
});
},
delete () {
dispatch(deleteConversation(conversationId));
},
onMute (status) {
if (status.get('muted')) {
dispatch(unmuteStatus(status.get('id')));
} else {
dispatch(muteStatus(status.get('id')));
}
},
onToggleHidden (status) {
if (status.get('hidden')) {
dispatch(revealStatus(status.get('id')));
} else {
dispatch(hideStatus(status.get('id')));
}
},
});
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Conversation));

View file

@ -1,16 +0,0 @@
import { connect } from 'react-redux';
import { expandConversations } from '../../../actions/conversations';
import ConversationsList from '../components/conversations_list';
const mapStateToProps = state => ({
conversations: state.getIn(['conversations', 'items']),
isLoading: state.getIn(['conversations', 'isLoading'], true),
hasMore: state.getIn(['conversations', 'hasMore'], false),
});
const mapDispatchToProps = dispatch => ({
onLoadMore: maxId => dispatch(expandConversations({ maxId })),
});
export default connect(mapStateToProps, mapDispatchToProps)(ConversationsList);

View file

@ -1,11 +1,11 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { useRef, useCallback, useEffect } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet';
import { connect } from 'react-redux';
import { useDispatch } from 'react-redux';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
@ -14,103 +14,79 @@ import { connectDirectStream } from 'mastodon/actions/streaming';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import ConversationsListContainer from './containers/conversations_list_container';
import { ConversationsList } from './components/conversations_list';
const messages = defineMessages({
title: { id: 'column.direct', defaultMessage: 'Private mentions' },
});
class DirectTimeline extends PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
};
handlePin = () => {
const { columnId, dispatch } = this.props;
const DirectTimeline = ({ columnId, multiColumn }) => {
const columnRef = useRef();
const intl = useIntl();
const dispatch = useDispatch();
const pinned = !!columnId;
const handlePin = useCallback(() => {
if (columnId) {
dispatch(removeColumn(columnId));
} else {
dispatch(addColumn('DIRECT', {}));
}
};
}, [dispatch, columnId]);
handleMove = (dir) => {
const { columnId, dispatch } = this.props;
const handleMove = useCallback((dir) => {
dispatch(moveColumn(columnId, dir));
};
}, [dispatch, columnId]);
handleHeaderClick = () => {
this.column.scrollTop();
};
componentDidMount () {
const { dispatch } = this.props;
const handleHeaderClick = useCallback(() => {
columnRef.current.scrollTop();
}, [columnRef]);
useEffect(() => {
dispatch(mountConversations());
dispatch(expandConversations());
this.disconnect = dispatch(connectDirectStream());
}
componentWillUnmount () {
this.props.dispatch(unmountConversations());
const disconnect = dispatch(connectDirectStream());
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}
return () => {
dispatch(unmountConversations());
disconnect();
};
}, [dispatch]);
setRef = c => {
this.column = c;
};
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='at'
iconComponent={AlternateEmailIcon}
title={intl.formatMessage(messages.title)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
/>
handleLoadMore = maxId => {
this.props.dispatch(expandConversations({ maxId }));
};
<ConversationsList
trackScroll={!pinned}
scrollKey={`direct_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
bindToDocument={!multiColumn}
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
alwaysPrepend
/>
render () {
const { intl, hasUnread, columnId, multiColumn } = this.props;
const pinned = !!columnId;
<Helmet>
<title>{intl.formatMessage(messages.title)}</title>
<meta name='robots' content='noindex' />
</Helmet>
</Column>
);
};
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='at'
iconComponent={AlternateEmailIcon}
active={hasUnread}
title={intl.formatMessage(messages.title)}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
/>
DirectTimeline.propTypes = {
columnId: PropTypes.string,
multiColumn: PropTypes.bool,
};
<ConversationsListContainer
trackScroll={!pinned}
scrollKey={`direct_timeline-${columnId}`}
timelineId='direct'
bindToDocument={!multiColumn}
onLoadMore={this.handleLoadMore}
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
alwaysPrepend
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
/>
<Helmet>
<title>{intl.formatMessage(messages.title)}</title>
<meta name='robots' content='noindex' />
</Helmet>
</Column>
);
}
}
export default connect()(injectIntl(DirectTimeline));
export default DirectTimeline;

View file

@ -26,7 +26,7 @@ import ColumnHeader from 'mastodon/components/column_header';
import LinkFooter from 'mastodon/features/ui/components/link_footer';
import { me, showTrends } from '../../initial_state';
import NavigationContainer from '../compose/containers/navigation_container';
import { NavigationBar } from '../compose/components/navigation_bar';
import ColumnLink from '../ui/components/column_link';
import ColumnSubheading from '../ui/components/column_subheading';
@ -143,7 +143,7 @@ class GettingStarted extends ImmutablePureComponent {
return (
<Column>
{(signedIn && !multiColumn) ? <NavigationContainer /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' iconComponent={MenuIcon} multiColumn={multiColumn} />}
{(signedIn && !multiColumn) ? <NavigationBar /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' iconComponent={MenuIcon} multiColumn={multiColumn} />}
<div className='getting-started scrollable scrollable--flex'>
<div className='getting-started__wrapper'>

View file

@ -1,21 +1,15 @@
import { PureComponent } from 'react';
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
import LoadingBarContainer from 'mastodon/features/ui/containers/loading_bar_container';
import ModalContainer from 'mastodon/features/ui/containers/modal_container';
import NotificationsContainer from 'mastodon/features/ui/containers/notifications_container';
import ComposeFormContainer from '../../compose/containers/compose_form_container';
import LoadingBarContainer from '../../ui/containers/loading_bar_container';
import ModalContainer from '../../ui/containers/modal_container';
import NotificationsContainer from '../../ui/containers/notifications_container';
const Compose = () => (
<>
<ComposeFormContainer autoFocus withoutNavigation />
<NotificationsContainer />
<ModalContainer />
<LoadingBarContainer className='loading-bar' />
</>
);
export default class Compose extends PureComponent {
render () {
return (
<div>
<ComposeFormContainer autoFocus />
<NotificationsContainer />
<ModalContainer />
<LoadingBarContainer className='loading-bar' />
</div>
);
}
}
export default Compose;

View file

@ -17,8 +17,10 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -296,7 +298,7 @@ class ActionBar extends PureComponent {
if (status.get('reblogged')) {
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
reblogIconComponent = publicStatus ? RepeatActiveIcon : RepeatPrivateActiveIcon;
} else if (publicStatus) {
reblogTitle = intl.formatMessage(messages.reblog);
reblogIconComponent = RepeatIcon;

View file

@ -6,7 +6,6 @@ import { connect } from 'react-redux';
import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
import ServerBanner from 'mastodon/components/server_banner';
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
import SearchContainer from 'mastodon/features/compose/containers/search_container';
import LinkFooter from './link_footer';
@ -56,10 +55,7 @@ class ComposePanel extends PureComponent {
)}
{signedIn && (
<>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer singleColumn />
</>
<ComposeFormContainer singleColumn />
)}
<LinkFooter />

View file

@ -21,7 +21,7 @@ import { Button } from 'mastodon/components/button';
import { GIFV } from 'mastodon/components/gifv';
import { IconButton } from 'mastodon/components/icon_button';
import Audio from 'mastodon/features/audio';
import CharacterCounter from 'mastodon/features/compose/components/character_counter';
import { CharacterCounter } from 'mastodon/features/compose/components/character_counter';
import UploadProgress from 'mastodon/features/compose/components/upload_progress';
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
import { me } from 'mastodon/initial_state';

View file

@ -108,7 +108,6 @@ class MuteModal extends PureComponent {
<div>
<span><FormattedMessage id='mute_modal.duration' defaultMessage='Duration' />: </span>
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
<select value={muteDuration} onChange={this.changeMuteDuration}>
<option value={0}>{intl.formatMessage(messages.indefinite)}</option>
<option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>

View file

@ -79,7 +79,6 @@ class NavigationPanel extends Component {
<div className='navigation-panel'>
<div className='navigation-panel__logo'>
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
{!banner && <hr />}
</div>
{banner &&

View file

@ -3,6 +3,7 @@
"about.contact": "Kontak:",
"about.disclaimer": "Mastodon is gratis oopbronsagteware en n handelsmerk van Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Rede nie beskikbaar nie",
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
"about.domain_blocks.silenced.title": "Beperk",
"about.domain_blocks.suspended.title": "Opgeskort",
"about.not_available": "Hierdie inligting is nie op hierdie bediener beskikbaar gestel nie.",
@ -65,7 +66,6 @@
"alert.unexpected.message": "Iets het skeefgeloop.",
"alert.unexpected.title": "Oeps!",
"announcement.announcement": "Aankondiging",
"autosuggest_hashtag.per_week": "{count} per week",
"boost_modal.combo": "Druk {combo} om dit volgende keer oor te slaan",
"bundle_column_error.error.title": "Ag nee!",
"bundle_column_error.network.title": "Netwerkfout",
@ -110,19 +110,12 @@
"compose_form.lock_disclaimer": "Jou rekening is nie {locked} nie. Enigiemand kan jou volg en sien wat jy vir jou volgers plaas.",
"compose_form.lock_disclaimer.lock": "gesluit",
"compose_form.placeholder": "Wat wil jy deel?",
"compose_form.poll.add_option": "Voeg n keuse by",
"compose_form.poll.duration": "Duur van peiling",
"compose_form.poll.option_placeholder": "Keuse {number}",
"compose_form.poll.remove_option": "Verwyder hierdie keuse",
"compose_form.poll.switch_to_multiple": "Verander peiling om meer as een keuse toe te laat",
"compose_form.poll.switch_to_single": "Verander die peiling om slegs een keuse toe te laat",
"compose_form.publish": "Publiseer",
"compose_form.publish_form": "Publiseer",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Stoor veranderinge",
"compose_form.spoiler.marked": "Verwyder inhoudswaarskuwing",
"compose_form.spoiler.unmarked": "Voeg inhoudswaarskuwing by",
"compose_form.spoiler_placeholder": "Skryf jou waarskuwing hier",
"confirmation_modal.cancel": "Kanselleer",
"confirmations.block.block_and_report": "Blokkeer en rapporteer",
"confirmations.block.confirm": "Blokkeer",
@ -233,7 +226,6 @@
"navigation_bar.community_timeline": "Plaaslike tydlyn",
"navigation_bar.compose": "Skep nuwe plasing",
"navigation_bar.domain_blocks": "Geblokkeerde domeine",
"navigation_bar.edit_profile": "Redigeer profiel",
"navigation_bar.lists": "Lyste",
"navigation_bar.logout": "Teken uit",
"navigation_bar.personal": "Persoonlik",
@ -266,14 +258,7 @@
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile",
"privacy.change": "Verander privaatheid van plasing",
"privacy.direct.long": "Slegs sigbaar vir genoemde gebruikers",
"privacy.direct.short": "Slegs genoemde persone",
"privacy.private.long": "Slegs sigbaar vir volgelinge",
"privacy.private.short": "Slegs volgelinge",
"privacy.public.long": "Sigbaar vir almal",
"privacy.public.short": "Publiek",
"privacy.unlisted.long": "Sigbaar vir almal, maar onttrek uit verkennings-kenmerke",
"privacy.unlisted.short": "Ongelys",
"privacy_policy.last_updated": "Laaste bywerking op {date}",
"privacy_policy.title": "Privaatheidsbeleid",
"regeneration_indicator.sublabel": "Jou tuis-voer word voorberei!",

View file

@ -75,7 +75,6 @@
"announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Amagar audio",
"autosuggest_hashtag.per_week": "{count} per semana",
"boost_modal.combo": "Puetz fer clic en {combo} pa blincar este aviso la proxima vegada",
"bundle_column_error.copy_stacktrace": "Copiar informe d'error",
"bundle_column_error.error.body": "La pachina solicitada no podió estar renderizada. Podría deber-se a una error en o nuestro codigo u a un problema de compatibilidat con o navegador.",
@ -126,22 +125,12 @@
"compose_form.lock_disclaimer": "La tuya cuenta no ye {locked}. Totz pueden seguir-te pa veyer las tuyas publicacions nomás pa seguidores.",
"compose_form.lock_disclaimer.lock": "blocau",
"compose_form.placeholder": "En qué yes pensando?",
"compose_form.poll.add_option": "Anyadir una opción",
"compose_form.poll.duration": "Duración d'a enqüesta",
"compose_form.poll.option_placeholder": "Elección {number}",
"compose_form.poll.remove_option": "Eliminar esta opción",
"compose_form.poll.switch_to_multiple": "Modificar enqüesta pa permitir multiples opcions",
"compose_form.poll.switch_to_single": "Modificar enqüesta pa permitir una sola opción",
"compose_form.publish": "Publicar",
"compose_form.publish_form": "Publicar",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Alzar cambios",
"compose_form.sensitive.hide": "{count, plural, one {Marcar material como sensible} other {Marcar material como sensible}}",
"compose_form.sensitive.marked": "{count, plural, one {Material marcau como sensible} other {Material marcau como sensible}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Material no marcau como sensible} other {Material no marcau como sensible}}",
"compose_form.spoiler.marked": "Texto amagau dimpués de l'alvertencia",
"compose_form.spoiler.unmarked": "Texto no amagau",
"compose_form.spoiler_placeholder": "Alvertencia de conteniu",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.block_and_report": "Blocar y Denunciar",
"confirmations.block.confirm": "Blocar",
@ -345,7 +334,6 @@
"navigation_bar.compose": "Escribir nueva publicación",
"navigation_bar.discover": "Descubrir",
"navigation_bar.domain_blocks": "Dominios amagaus",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.explore": "Explorar",
"navigation_bar.filters": "Parolas silenciadas",
"navigation_bar.follow_requests": "Solicitutz pa seguir-te",
@ -429,14 +417,7 @@
"poll_button.add_poll": "Anyadir una enqüesta",
"poll_button.remove_poll": "Eliminar enqüesta",
"privacy.change": "Achustar privacidat",
"privacy.direct.long": "Nomás amostrar a los usuarios mencionaus",
"privacy.direct.short": "Nomás contas mencionadas",
"privacy.private.long": "Nomás amostrar a seguidores",
"privacy.private.short": "Solo seguidores",
"privacy.public.long": "Visible pa totz",
"privacy.public.short": "Publico",
"privacy.unlisted.long": "Visible pa totz, pero excluyiu d'as funcions d'escubrimiento",
"privacy.unlisted.short": "No listau",
"privacy_policy.last_updated": "Ultima vegada actualizau {date}",
"privacy_policy.title": "Politica de Privacidat",
"refresh": "Actualizar",
@ -590,10 +571,8 @@
"upload_error.poll": "Puyada de fichers no permitida con enqüestas.",
"upload_form.audio_description": "Describir pa personas con problemas auditivos",
"upload_form.description": "Describir pa los usuarios con dificultat visual",
"upload_form.description_missing": "Garra descripción anyadida",
"upload_form.edit": "Editar",
"upload_form.thumbnail": "Cambiar miniatura",
"upload_form.undo": "Borrar",
"upload_form.video_description": "Describir pa personas con problemas auditivos u visuals",
"upload_modal.analyzing_picture": "Analisando imachen…",
"upload_modal.apply": "Aplicar",

View file

@ -86,7 +86,6 @@
"announcement.announcement": "إعلان",
"attachments_list.unprocessed": "(غير معالَج)",
"audio.hide": "إخفاء المقطع الصوتي",
"autosuggest_hashtag.per_week": "{count} في الأسبوع",
"boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة",
"bundle_column_error.copy_stacktrace": "انسخ تقرير الخطأ",
"bundle_column_error.error.body": "لا يمكن تقديم الصفحة المطلوبة. قد يكون بسبب خطأ في التعليمات البرمجية، أو مشكلة توافق المتصفح.",
@ -143,22 +142,12 @@
"compose_form.lock_disclaimer": "حسابُك غير {locked}. يُمكن لأي شخص مُتابعتك لرؤية (منشورات المتابعين فقط).",
"compose_form.lock_disclaimer.lock": "مُقفَل",
"compose_form.placeholder": "فِيمَ تُفكِّر؟",
"compose_form.poll.add_option": "إضافة خيار",
"compose_form.poll.duration": "مُدَّة اِستطلاع الرأي",
"compose_form.poll.option_placeholder": "الخيار {number}",
"compose_form.poll.remove_option": "إزالة هذا الخيار",
"compose_form.poll.switch_to_multiple": "تغيِير الاستطلاع للسماح باِخيارات مُتعدِّدة",
"compose_form.poll.switch_to_single": "تغيِير الاستطلاع للسماح باِخيار واحد فقط",
"compose_form.publish": "نشر",
"compose_form.publish_form": "منشور جديد",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "احفظ التعديلات",
"compose_form.sensitive.hide": "{count, plural, one {الإشارة إلى الوَسط كمُحتوى حسّاس} two{الإشارة إلى الوسطان كمُحتويان حسّاسان} other {الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
"compose_form.sensitive.marked": "{count, plural, one {تمَّ الإشارة إلى الوسط كمُحتوى حسّاس} two{تمَّ الإشارة إلى الوسطان كمُحتويان حسّاسان} other {تمَّ الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
"compose_form.sensitive.unmarked": "{count, plural, one {لم تَتِمّ الإشارة إلى الوسط كمُحتوى حسّاس} two{لم تَتِمّ الإشارة إلى الوسطان كمُحتويان حسّاسان} other {لم تَتِمّ الإشارة إلى الوسائط كمُحتويات حسّاسة}}",
"compose_form.spoiler.marked": "إزالة تحذير المحتوى",
"compose_form.spoiler.unmarked": "إضافة تحذير للمحتوى",
"compose_form.spoiler_placeholder": "اُكتُب تحذيركَ هُنا",
"confirmation_modal.cancel": "إلغاء",
"confirmations.block.block_and_report": "حظره والإبلاغ عنه",
"confirmations.block.confirm": "حظر",
@ -402,7 +391,6 @@
"navigation_bar.direct": "الإشارات الخاصة",
"navigation_bar.discover": "اكتشف",
"navigation_bar.domain_blocks": "النطاقات المحظورة",
"navigation_bar.edit_profile": "عدّل الملف التعريفي",
"navigation_bar.explore": "استكشف",
"navigation_bar.favourites": "المفضلة",
"navigation_bar.filters": "الكلمات المكتومة",
@ -509,14 +497,7 @@
"poll_button.add_poll": "إضافة استطلاع للرأي",
"poll_button.remove_poll": "إزالة استطلاع الرأي",
"privacy.change": "اضبط خصوصية المنشور",
"privacy.direct.long": "مرئي للمستخدمين المذكورين فقط",
"privacy.direct.short": "الأشخاص المشار إليهم فقط",
"privacy.private.long": "أنشر لمتابعيك فقط",
"privacy.private.short": "للمتابِعين فقط",
"privacy.public.long": "مرئي للكل",
"privacy.public.short": "للعامة",
"privacy.unlisted.long": "مرئي للجميع، ولكن مِن دون ميزات الاكتشاف",
"privacy.unlisted.short": "غير مدرج",
"privacy_policy.last_updated": "آخر تحديث {date}",
"privacy_policy.title": "سياسة الخصوصية",
"refresh": "أنعِش",
@ -696,10 +677,8 @@
"upload_error.poll": "لا يمكن إدراج ملفات في استطلاعات الرأي.",
"upload_form.audio_description": "وصف للأشخاص ذي قِصر السمع",
"upload_form.description": "وصف للمعاقين بصريا",
"upload_form.description_missing": "لم يُضف وصف",
"upload_form.edit": "تعديل",
"upload_form.thumbnail": "غيّر الصورة المصغرة",
"upload_form.undo": "حذف",
"upload_form.video_description": "وصف للمعاقين بصريا أو لِذي قِصر السمع",
"upload_modal.analyzing_picture": "جارٍ فحص الصورة…",
"upload_modal.apply": "طبّق",

View file

@ -66,7 +66,6 @@
"alert.unexpected.title": "¡Meca!",
"announcement.announcement": "Anunciu",
"attachments_list.unprocessed": "(ensin procesar)",
"autosuggest_hashtag.per_week": "{count} per selmana",
"bundle_column_error.error.body": "La páxina solicitada nun se pudo renderizar. Ye posible que seya pola mor d'un fallu nel códigu o por un problema de compatibilidá del restolador.",
"bundle_column_error.error.title": "¡Oh, non!",
"bundle_column_error.network.body": "Hebo un error al tentar de cargar esta páxina. Esto pudo ser pola mor d'un problema temporal cola conexón a internet o con esti sirvidor.",
@ -109,13 +108,9 @@
"compose_form.lock_disclaimer": "La to cuenta nun ye {locked}. Cualesquier perfil pue siguite pa ver los artículos que son namás pa siguidores.",
"compose_form.lock_disclaimer.lock": "privada",
"compose_form.placeholder": "¿En qué pienses?",
"compose_form.poll.add_option": "Amestar una opción",
"compose_form.poll.option_placeholder": "Opción {number}",
"compose_form.poll.remove_option": "Quitar esta opción",
"compose_form.publish": "Espublizar",
"compose_form.poll.type": "Estilu",
"compose_form.publish_form": "Artículu nuevu",
"compose_form.publish_loud": "¡{publish}!",
"compose_form.save_changes": "Guardar los cambeos",
"confirmation_modal.cancel": "Encaboxar",
"confirmations.block.block_and_report": "Bloquiar ya informar",
"confirmations.block.confirm": "Bloquiar",
@ -286,7 +281,6 @@
"navigation_bar.community_timeline": "Llinia de tiempu llocal",
"navigation_bar.direct": "Menciones privaes",
"navigation_bar.domain_blocks": "Dominios bloquiaos",
"navigation_bar.edit_profile": "Editar el perfil",
"navigation_bar.explore": "Esploración",
"navigation_bar.filters": "Pallabres desactivaes",
"navigation_bar.follow_requests": "Solicitúes de siguimientu",
@ -346,12 +340,7 @@
"poll_button.add_poll": "Amestar una encuesta",
"poll_button.remove_poll": "Quitar la encuesta",
"privacy.change": "Configurar la privacidá del artículu",
"privacy.direct.long": "Artículu visible namás pa los perfiles mentaos",
"privacy.private.long": "Artículu visible namás pa los perfiles siguidores",
"privacy.private.short": "Namás pa siguidores",
"privacy.public.long": "Tol mundu pue ver l'artículu",
"privacy.public.short": "Artículu públicu",
"privacy.unlisted.long": "Artículu visible pa tol mundu mas escluyíu de les funciones de descubrimientu",
"privacy_policy.last_updated": "Data del últimu anovamientu: {date}",
"privacy_policy.title": "Política de privacidá",
"refresh": "Anovar",
@ -368,6 +357,7 @@
"relative_time.seconds": "{number} s",
"relative_time.today": "güei",
"reply_indicator.cancel": "Encaboxar",
"reply_indicator.poll": "Encuesta",
"report.block": "Bloquiar",
"report.categories.spam": "Spam",
"report.categories.violation": "El conteníu incumple una o más normes del sirvidor",
@ -492,9 +482,7 @@
"upload_button.label": "Amestar ficheros multimedia",
"upload_error.poll": "La xuba de ficheros nun ta permitida coles encuestes.",
"upload_form.audio_description": "Describi'l conteníu pa persones sordes ya/o ciegues",
"upload_form.description_missing": "Nun s'amestó la descripción",
"upload_form.edit": "Editar",
"upload_form.undo": "Desaniciar",
"upload_modal.analyzing_picture": "Analizando la semeya…",
"upload_modal.apply": "Aplicar",
"upload_modal.applying": "Aplicando…",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Аб'ява",
"attachments_list.unprocessed": "(неапрацаваны)",
"audio.hide": "Схаваць аўдыя",
"autosuggest_hashtag.per_week": "{count} за тыдзень",
"boost_modal.combo": "Націсніце {combo}, каб прапусціць наступным разам",
"bundle_column_error.copy_stacktrace": "Скапіраваць справаздачу пра памылку",
"bundle_column_error.error.body": "Запытаная старонка не можа быць адлюстраваная. Гэта магло стацца праз хібу ў нашым кодзе, або праз памылку сумяшчальнасці з браўзерам.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Ваш уліковы запіс не {locked}. Усе могуць падпісацца на вас, каб бачыць допісы толькі для падпісчыкаў.",
"compose_form.lock_disclaimer.lock": "закрыты",
"compose_form.placeholder": "Што здарылася?",
"compose_form.poll.add_option": "Дадаць варыянт",
"compose_form.poll.duration": "Працягласць апытання",
"compose_form.poll.option_placeholder": "Варыянт {number}",
"compose_form.poll.remove_option": "Выдаліць гэты варыянт",
"compose_form.poll.switch_to_multiple": "Змяніце апытанне, каб дазволіць некалькі варыянтаў адказу",
"compose_form.poll.switch_to_single": "Змяніце апытанне, каб дазволіць адзіны варыянт адказу",
"compose_form.publish": "Апублікаваць",
"compose_form.publish_form": "Апублікаваць",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Захаваць змены",
"compose_form.sensitive.hide": "{count, plural, one {Пазначыць кантэнт як далікатны} other {Пазначыць кантэнт як далікатны}}",
"compose_form.sensitive.marked": "{count, plural, one {Кантэнт пазначаны як далікатны} other {Кантэнт пазначаны як далікатны}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Кантэнт не пазначаны як далікатны} other {Кантэнт не пазначаны як далікатны}}",
"compose_form.spoiler.marked": "Выдаліць папярэджанне аб змесціве",
"compose_form.spoiler.unmarked": "Дадаць папярэджанне аб змесціве",
"compose_form.spoiler_placeholder": "Напішыце сваё папярэджанне тут",
"confirmation_modal.cancel": "Скасаваць",
"confirmations.block.block_and_report": "Заблакіраваць і паскардзіцца",
"confirmations.block.confirm": "Заблакіраваць",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Асабістыя згадванні",
"navigation_bar.discover": "Даведайцесь",
"navigation_bar.domain_blocks": "Заблакіраваныя дамены",
"navigation_bar.edit_profile": "Рэдагаваць профіль",
"navigation_bar.explore": "Агляд",
"navigation_bar.favourites": "Упадабанае",
"navigation_bar.filters": "Ігнараваныя словы",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Дадаць апытанне",
"poll_button.remove_poll": "Выдаліць апытанне",
"privacy.change": "Змяніць прыватнасць допісу",
"privacy.direct.long": "Паказаць толькі згаданым карыстальнікам",
"privacy.direct.short": "Толькі згаданыя людзі",
"privacy.private.long": "Паказаць толькі падпісчыкам",
"privacy.private.short": "Толькі для падпісчыкаў",
"privacy.public.long": "Бачны для ўсіх",
"privacy.public.short": "Публічны",
"privacy.unlisted.long": "Бачна для ўсіх, але не праз магчымасці агляду",
"privacy.unlisted.short": "Не ў стужках",
"privacy_policy.last_updated": "Адноўлена {date}",
"privacy_policy.title": "Палітыка канфідэнцыйнасці",
"recommended": "Рэкамендуем",
@ -715,10 +696,8 @@
"upload_error.poll": "Немагчыма прымацаваць файл да апытання.",
"upload_form.audio_description": "Апісанне для людзей з парушэннямі слыху",
"upload_form.description": "Апісаць для людзей са слабым зрокам",
"upload_form.description_missing": "Апісанне адсутнічае",
"upload_form.edit": "Рэдагаваць",
"upload_form.thumbnail": "Змяніць мініяцюру",
"upload_form.undo": "Выдаліць",
"upload_form.video_description": "Апісанне для людзей з парушэннямі зроку і слыху",
"upload_modal.analyzing_picture": "Аналіз выявы…",
"upload_modal.apply": "Ужыць",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Оповестяване",
"attachments_list.unprocessed": "(необработено)",
"audio.hide": "Скриване на звука",
"autosuggest_hashtag.per_week": "{count} на седмица",
"boost_modal.combo": "Можете да натиснете {combo}, за да пропуснете това следващия път",
"bundle_column_error.copy_stacktrace": "Копиране на доклада за грешката",
"bundle_column_error.error.body": "Заявената страница не може да се изобрази. Това може да е заради грешка в кода ни или проблем със съвместимостта на браузъра.",
@ -148,20 +147,20 @@
"compose_form.placeholder": "Какво мислите?",
"compose_form.poll.add_option": "Добавяне на избор",
"compose_form.poll.duration": "Времетраене на анкетата",
"compose_form.poll.multiple": "Множествен избор",
"compose_form.poll.option_placeholder": "Избор {number}",
"compose_form.poll.remove_option": "Премахване на този избор",
"compose_form.poll.remove_option": "Премахване на тази възможност",
"compose_form.poll.single": "Подберете нещо",
"compose_form.poll.switch_to_multiple": "Промяна на анкетата, за да се позволят множество възможни избора",
"compose_form.poll.switch_to_single": "Промяна на анкетата, за да се позволи един възможен избор",
"compose_form.publish": "Публикуване",
"compose_form.poll.type": "Стил",
"compose_form.publish": "Публикация",
"compose_form.publish_form": "Публикуване",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Запазване на промените",
"compose_form.sensitive.hide": "{count, plural, one {Маркиране на мултимедията като деликатна} other {Маркиране на мултимедиите като деликатни}}",
"compose_form.sensitive.marked": "{count, plural, one {мултимедия е означена като деликатна} other {мултимедии са означени като деликатни}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Мултимедията не е маркирана като деликатна} other {Мултимедиите не са маркирани като деликатни}}",
"compose_form.reply": "Отговор",
"compose_form.save_changes": "Обновяване",
"compose_form.spoiler.marked": "Отстраняване на предупреждение за съдържание",
"compose_form.spoiler.unmarked": "Добавяне на предупреждение за съдържание",
"compose_form.spoiler_placeholder": "Тук напишете предупреждението си",
"compose_form.spoiler_placeholder": "Предупреждение за съдържание (по избор)",
"confirmation_modal.cancel": "Отказ",
"confirmations.block.block_and_report": "Блокиране и докладване",
"confirmations.block.confirm": "Блокиране",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Частни споменавания",
"navigation_bar.discover": "Откриване",
"navigation_bar.domain_blocks": "Блокирани домейни",
"navigation_bar.edit_profile": "Редактиране на профила",
"navigation_bar.explore": "Изследване",
"navigation_bar.favourites": "Любими",
"navigation_bar.filters": "Заглушени думи",
@ -526,14 +524,14 @@
"poll_button.add_poll": "Анкетиране",
"poll_button.remove_poll": "Премахване на анкета",
"privacy.change": "Промяна на поверителността на публикация",
"privacy.direct.long": "Видимо само за споменатите потребители",
"privacy.direct.short": "Само споменатите хора",
"privacy.private.long": "Видимо само за последователите",
"privacy.private.short": "Само последователи",
"privacy.public.long": "Видимо за всички",
"privacy.direct.long": "Всеки споменат в публикацията",
"privacy.direct.short": "Определени хора",
"privacy.private.long": "Само последователите ви",
"privacy.private.short": "Последователи",
"privacy.public.long": "Всеки във и извън Mastodon",
"privacy.public.short": "Публично",
"privacy.unlisted.long": "Видимо за всички, но не чрез възможността за откриване",
"privacy.unlisted.short": "Несписъчно",
"privacy.unlisted.long": "По-малко алгоритмични фанфари",
"privacy.unlisted.short": "Тиха публика",
"privacy_policy.last_updated": "Последно осъвременяване на {date}",
"privacy_policy.title": "Политика за поверителност",
"recommended": "Препоръчано",
@ -551,7 +549,9 @@
"relative_time.minutes": "{number}м.",
"relative_time.seconds": "{number}с.",
"relative_time.today": "днес",
"reply_indicator.attachments": "{count, plural, one {# прикаване} other {# прикачвания}}",
"reply_indicator.cancel": "Отказ",
"reply_indicator.poll": "Анкета",
"report.block": "Блокиране",
"report.block_explanation": "Няма да им виждате публикациите. Те няма да могат да виждат публикациите ви или да ви последват. Те ще могат да казват, че са били блокирани.",
"report.categories.legal": "Правни въпроси",
@ -715,10 +715,8 @@
"upload_error.poll": "Качването на файлове не е позволено с анкети.",
"upload_form.audio_description": "Опишете за хора, които са глухи или трудно чуват",
"upload_form.description": "Опишете за хора, които са слепи или имат слабо зрение",
"upload_form.description_missing": "Няма добавен опис",
"upload_form.edit": "Редактиране",
"upload_form.thumbnail": "Промяна на миниобраза",
"upload_form.undo": "Изтриване",
"upload_form.video_description": "Опишете за хора, които са глухи или трудно чуват, слепи или имат слабо зрение",
"upload_modal.analyzing_picture": "Снимков анализ…",
"upload_modal.apply": "Прилагане",

View file

@ -85,7 +85,6 @@
"announcement.announcement": "ঘোষণা",
"attachments_list.unprocessed": "(প্রক্রিয়া করা যায়নি)",
"audio.hide": "অডিও লুকান",
"autosuggest_hashtag.per_week": "প্রতি সপ্তাহে {count}",
"boost_modal.combo": "পরেরবার আপনি {combo} টিপলে এটি আর আসবে না",
"bundle_column_error.copy_stacktrace": "এরর রিপোর্ট কপি করুন",
"bundle_column_error.error.body": "অনুরোধ করা পৃষ্ঠাটি রেন্ডার করা যায়নি। এটি আমাদের কোডে একটি বাগ বা ব্রাউজার সামঞ্জস্যের সমস্যার কারণে হতে পারে।",
@ -142,22 +141,12 @@
"compose_form.lock_disclaimer": "আপনার নিবন্ধনে তালা দেওয়া নেই, যে কেও আপনাকে অনুসরণ করতে পারবে এবং অনুশারকদের জন্য লেখা দেখতে পারবে।",
"compose_form.lock_disclaimer.lock": "তালা দেওয়া",
"compose_form.placeholder": "আপনি কি ভাবছেন ?",
"compose_form.poll.add_option": "আরেকটি বিকল্প যোগ করুন",
"compose_form.poll.duration": "ভোটগ্রহনের সময়",
"compose_form.poll.option_placeholder": "বিকল্প {number}",
"compose_form.poll.remove_option": "এই বিকল্পটি মুছে ফেলুন",
"compose_form.poll.switch_to_multiple": "একাধিক পছন্দ অনুমতি দেওয়ার জন্য পোল পরিবর্তন করুন",
"compose_form.poll.switch_to_single": "একটি একক পছন্দের অনুমতি দেওয়ার জন্য পোল পরিবর্তন করুন",
"compose_form.publish": "প্রকাশ করুন",
"compose_form.publish_form": "প্রকাশ করুন",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "পরিবর্তনগুলো প্রকাশ করুন",
"compose_form.sensitive.hide": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করতে",
"compose_form.sensitive.marked": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করা হয়েছে",
"compose_form.sensitive.unmarked": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করা হয়নি",
"compose_form.spoiler.marked": "সতর্কতার পিছনে লেখানটি লুকানো আছে",
"compose_form.spoiler.unmarked": "লেখাটি লুকানো নেই",
"compose_form.spoiler_placeholder": "আপনার লেখা দেখার সাবধানবাণী লিখুন",
"confirmation_modal.cancel": "বাতিল করুন",
"confirmations.block.block_and_report": "ব্লক করুন এবং রিপোর্ট করুন",
"confirmations.block.confirm": "ব্লক করুন",
@ -324,7 +313,6 @@
"navigation_bar.compose": "নতুন টুট লিখুন",
"navigation_bar.discover": "ঘুরে দেখুন",
"navigation_bar.domain_blocks": "লুকানো ডোমেনগুলি",
"navigation_bar.edit_profile": "নিজের পাতা সম্পাদনা করতে",
"navigation_bar.explore": "পরিব্রাজন",
"navigation_bar.favourites": "পছন্দসমূহ",
"navigation_bar.filters": "বন্ধ করা শব্দ",
@ -395,12 +383,7 @@
"poll_button.add_poll": "একটা নির্বাচন যোগ করতে",
"poll_button.remove_poll": "নির্বাচন বাদ দিতে",
"privacy.change": "লেখার গোপনীয়তা অবস্থা ঠিক করতে",
"privacy.direct.long": "শুধুমাত্র উল্লেখিত ব্যবহারকারীদের কাছে লিখতে",
"privacy.direct.short": "Direct",
"privacy.private.long": "শুধুমাত্র আপনার অনুসরণকারীদের লিখতে",
"privacy.private.short": "Followers-only",
"privacy.public.short": "সর্বজনীন প্রকাশ্য",
"privacy.unlisted.short": "প্রকাশ্য নয়",
"refresh": "সতেজ করা",
"regeneration_indicator.label": "আসছে…",
"regeneration_indicator.sublabel": "আপনার বাড়ির-সময়রেখা প্রস্তূত করা হচ্ছে!",
@ -506,7 +489,6 @@
"upload_form.description": "যারা দেখতে পায়না তাদের জন্য এটা বর্ণনা করতে",
"upload_form.edit": "সম্পাদন",
"upload_form.thumbnail": "থাম্বনেল পরিবর্তন করুন",
"upload_form.undo": "মুছে ফেলতে",
"upload_form.video_description": "শ্রবণশক্তি হ্রাস বা চাক্ষুষ প্রতিবন্ধী ব্যক্তিদের জন্য বর্ণনা করুন",
"upload_modal.analyzing_picture": "চিত্র বিশ্লেষণ করা হচ্ছে…",
"upload_modal.apply": "প্রয়োগ করুন",

View file

@ -87,7 +87,6 @@
"announcement.announcement": "Kemennad",
"attachments_list.unprocessed": "(ket meret)",
"audio.hide": "Kuzhat ar c'hleved",
"autosuggest_hashtag.per_week": "{count} bep sizhun",
"boost_modal.combo": "Ar wezh kentañ e c'halliot gwaskañ war {combo} evit tremen hebiou",
"bundle_column_error.copy_stacktrace": "Eilañ an danevell fazi",
"bundle_column_error.error.body": "N'haller ket skrammañ ar bajenn goulennet. Gallout a ra bezañ abalamour d'ur beug er c'hod pe d'ur gudenn keverlec'hded gant ar merdeer.",
@ -143,22 +142,12 @@
"compose_form.lock_disclaimer": "N'eo ket {locked} ho kont. An holl a c'hal ho heuliañ evit gwelet ho toudoù prevez.",
"compose_form.lock_disclaimer.lock": "prennet",
"compose_form.placeholder": "Petra emaoc'h o soñjal e-barzh ?",
"compose_form.poll.add_option": "Ouzhpenniñ un dibab",
"compose_form.poll.duration": "Pad ar sontadeg",
"compose_form.poll.option_placeholder": "Dibab {number}",
"compose_form.poll.remove_option": "Lemel an dibab-mañ",
"compose_form.poll.switch_to_multiple": "Kemmañ ar sontadeg evit aotren meur a zibab",
"compose_form.poll.switch_to_single": "Kemmañ ar sontadeg evit aotren un dibab hepken",
"compose_form.publish": "Embann",
"compose_form.publish_form": "Embann",
"compose_form.publish_loud": "{publish} !",
"compose_form.save_changes": "Enrollañ ar cheñchamantoù",
"compose_form.sensitive.hide": "Merkañ ar media evel kizidik",
"compose_form.sensitive.marked": "Merket eo ar media evel kizidik",
"compose_form.sensitive.unmarked": "N'eo ket merket ar media evel kizidik",
"compose_form.spoiler.marked": "Kuzhet eo an destenn a-dreñv ur c'hemenn",
"compose_form.spoiler.unmarked": "N'eo ket kuzhet an destenn",
"compose_form.spoiler_placeholder": "Skrivit ho kemenn diwall amañ",
"confirmation_modal.cancel": "Nullañ",
"confirmations.block.block_and_report": "Berzañ ha Disklêriañ",
"confirmations.block.confirm": "Stankañ",
@ -383,7 +372,6 @@
"navigation_bar.direct": "Menegoù prevez",
"navigation_bar.discover": "Dizoleiñ",
"navigation_bar.domain_blocks": "Domanioù kuzhet",
"navigation_bar.edit_profile": "Kemmañ ar profil",
"navigation_bar.explore": "Furchal",
"navigation_bar.favourites": "Muiañ-karet",
"navigation_bar.filters": "Gerioù kuzhet",
@ -487,14 +475,7 @@
"poll_button.add_poll": "Ouzhpennañ ur sontadeg",
"poll_button.remove_poll": "Dilemel ar sontadeg",
"privacy.change": "Cheñch prevezded an toud",
"privacy.direct.long": "Embann evit an implijer·ezed·ien meneget hepken",
"privacy.direct.short": "Tud meneget hepken",
"privacy.private.long": "Embann evit ar re a heuilh ac'hanon hepken",
"privacy.private.short": "Tud koumanantet hepken",
"privacy.public.long": "Gwelus d'an holl",
"privacy.public.short": "Publik",
"privacy.unlisted.long": "Gwelus gant an holl, met hep arc'hweladur dizoleiñ",
"privacy.unlisted.short": "Anlistennet",
"privacy_policy.last_updated": "Hizivadenn ziwezhañ {date}",
"privacy_policy.title": "Reolennoù Prevezded",
"recommended": "Erbedet",
@ -667,10 +648,8 @@
"upload_error.poll": "Pellgargañ restroù n'eo ket aotreet gant sontadegoù.",
"upload_form.audio_description": "Diskrivañ evit tud a zo kollet o c'hlev",
"upload_form.description": "Diskrivañ evit tud a zo kollet o gweled",
"upload_form.description_missing": "Deskrivadur diank",
"upload_form.edit": "Kemmañ",
"upload_form.thumbnail": "Kemmañ ar velvenn",
"upload_form.undo": "Dilemel",
"upload_form.video_description": "Diskrivañ evit tud a zo kollet o gweled pe o c'hlev",
"upload_modal.analyzing_picture": "O tielfennañ ar skeudenn…",
"upload_modal.apply": "Arloañ",

View file

@ -66,8 +66,6 @@
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile",
"privacy.change": "Adjust status privacy",
"privacy.direct.short": "Direct",
"privacy.private.short": "Followers-only",
"report.placeholder": "Type or paste additional comments",
"report.submit": "Submit report",
"report.target": "Report {target}",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Anunci",
"attachments_list.unprocessed": "(sense processar)",
"audio.hide": "Amaga l'àudio",
"autosuggest_hashtag.per_week": "{count} per setmana",
"boost_modal.combo": "Pots prémer {combo} per a evitar-ho el pròxim cop",
"bundle_column_error.copy_stacktrace": "Copia l'informe d'error",
"bundle_column_error.error.body": "No s'ha pogut renderitzar la pàgina sol·licitada. Podria ser per un error en el nostre codi o per un problema de compatibilitat del navegador.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "El teu compte no està {locked}. Tothom pot seguir-te i veure els tuts de només per a seguidors.",
"compose_form.lock_disclaimer.lock": "blocat",
"compose_form.placeholder": "Què tens al cap?",
"compose_form.poll.add_option": "Afegeix una opció",
"compose_form.poll.add_option": "Afegiu una opció",
"compose_form.poll.duration": "Durada de l'enquesta",
"compose_form.poll.multiple": "Opcions múltiples",
"compose_form.poll.option_placeholder": "Opció {number}",
"compose_form.poll.remove_option": "Elimina aquesta opció",
"compose_form.poll.remove_option": "Treu aquesta opció",
"compose_form.poll.single": "Trieu-ne una",
"compose_form.poll.switch_to_multiple": "Canvia lenquesta per a permetre múltiples opcions",
"compose_form.poll.switch_to_single": "Canvia lenquesta per a permetre una única opció",
"compose_form.publish": "Tut",
"compose_form.poll.type": "Estil",
"compose_form.publish": "Publica",
"compose_form.publish_form": "Nou tut",
"compose_form.publish_loud": "Tut!",
"compose_form.save_changes": "Desa els canvis",
"compose_form.sensitive.hide": "{count, plural, one {Marca mèdia com a sensible} other {Marca mèdia com a sensible}}",
"compose_form.sensitive.marked": "{count, plural, one {Contingut marcat com a sensible} other {Contingut marcat com a sensible}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Contingut no marcat com a sensible} other {Contingut no marcat com a sensible}}",
"compose_form.reply": "Responeu",
"compose_form.save_changes": "Actualitza",
"compose_form.spoiler.marked": "Elimina l'avís de contingut",
"compose_form.spoiler.unmarked": "Afegeix avís de contingut",
"compose_form.spoiler_placeholder": "Escriu l'avís aquí",
"compose_form.spoiler_placeholder": "Avís de contingut (opcional)",
"confirmation_modal.cancel": "Cancel·la",
"confirmations.block.block_and_report": "Bloca i denuncia",
"confirmations.block.confirm": "Bloca",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Mencions privades",
"navigation_bar.discover": "Descobreix",
"navigation_bar.domain_blocks": "Dominis blocats",
"navigation_bar.edit_profile": "Edita el perfil",
"navigation_bar.explore": "Explora",
"navigation_bar.favourites": "Favorits",
"navigation_bar.filters": "Paraules silenciades",
@ -521,19 +519,17 @@
"poll.total_people": "{count, plural, one {# persona} other {# persones}}",
"poll.total_votes": "{count, plural, one {# vot} other {# vots}}",
"poll.vote": "Vota",
"poll.voted": "Vas votar per aquesta resposta",
"poll.voted": "Vau votar aquesta resposta",
"poll.votes": "{votes, plural, one {# vot} other {# vots}}",
"poll_button.add_poll": "Afegeix una enquesta",
"poll_button.remove_poll": "Elimina l'enquesta",
"privacy.change": "Canvia la privacitat del tut",
"privacy.direct.long": "Visible només per als usuaris esmentats",
"privacy.direct.short": "Només gent mencionada",
"privacy.private.long": "Visible només per als seguidors",
"privacy.private.short": "Només seguidors",
"privacy.public.long": "Visible per a tothom",
"privacy.direct.long": "Tothom mencionat en aquesta publicació",
"privacy.direct.short": "Persones concretes",
"privacy.private.long": "Només els vostres seguidors",
"privacy.private.short": "Seguidors",
"privacy.public.long": "Tothom dins o fora Mastodon",
"privacy.public.short": "Públic",
"privacy.unlisted.long": "Visible per a tothom però exclosa de les funcions de descobriment",
"privacy.unlisted.short": "No llistada",
"privacy_policy.last_updated": "Darrera actualització {date}",
"privacy_policy.title": "Política de Privacitat",
"recommended": "Recomanat",
@ -715,10 +711,8 @@
"upload_error.poll": "No es permet carregar fitxers a les enquestes.",
"upload_form.audio_description": "Descriu-ho per a persones amb problemes d'audició",
"upload_form.description": "Descriu-ho per a persones amb problemes de visió",
"upload_form.description_missing": "No s'hi ha afegit cap descripció",
"upload_form.edit": "Edita",
"upload_form.thumbnail": "Canvia la miniatura",
"upload_form.undo": "Elimina",
"upload_form.video_description": "Descriu-ho per a persones amb problemes de visió o audició",
"upload_modal.analyzing_picture": "S'analitza la imatge…",
"upload_modal.apply": "Aplica",

View file

@ -76,7 +76,6 @@
"announcement.announcement": "بانگەواز",
"attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "شاردنەوەی دەنگ",
"autosuggest_hashtag.per_week": "{count} هەرهەفتە",
"boost_modal.combo": "دەتوانیت دەست بنێی بە سەر {combo} بۆ بازدان لە جاری داهاتوو",
"bundle_column_error.copy_stacktrace": "ڕاپۆرتی هەڵەی کۆپی بکە",
"bundle_column_error.error.body": "لاپەڕەی داواکراو نەتوانرا ڕەندەر بکرێت. دەکرێت بەهۆی هەڵەیەکی کۆدەکەمانەوە بێت، یان کێشەی گونجانی وێبگەڕ.",
@ -128,22 +127,12 @@
"compose_form.lock_disclaimer": "هەژمێرەکەی لە حاڵەتی {locked}. هەر کەسێک دەتوانێت شوێنت بکەوێت بۆ پیشاندانی بابەتەکانی تەنها دوایخۆی.",
"compose_form.lock_disclaimer.lock": "قفڵ دراوە",
"compose_form.placeholder": "چی لە مێشکتدایە?",
"compose_form.poll.add_option": "زیادکردنی هەڵبژاردەیەک",
"compose_form.poll.duration": "ماوەی ڕاپرسی",
"compose_form.poll.option_placeholder": "هەڵبژاردن {number}",
"compose_form.poll.remove_option": "لابردنی ئەم هەڵبژاردەیە",
"compose_form.poll.switch_to_multiple": "ڕاپرسی بگۆڕە بۆ ڕێگەدان بە چەند هەڵبژاردنێک",
"compose_form.poll.switch_to_single": "گۆڕینی ڕاپرسی بۆ ڕێگەدان بە تاکە هەڵبژاردنێک",
"compose_form.publish": "بڵاوی بکەوە",
"compose_form.publish_form": "بڵاوی بکەوە",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "پاشکەوتی گۆڕانکاریەکان",
"compose_form.sensitive.hide": "نیشانکردنی میدیا وەک هەستیار",
"compose_form.sensitive.marked": "وادەی کۆتایی",
"compose_form.sensitive.unmarked": "میدیا وەک هەستیار نیشان نەکراوە",
"compose_form.spoiler.marked": "دەق لە پشت ئاگاداریدا شاراوەتەوە",
"compose_form.spoiler.unmarked": "دەق شاراوە نییە",
"compose_form.spoiler_placeholder": "ئاگاداریەکەت لێرە بنووسە",
"confirmation_modal.cancel": "هەڵوەشاندنەوه",
"confirmations.block.block_and_report": "بلۆک & گوزارشت",
"confirmations.block.confirm": "بلۆک",
@ -354,7 +343,6 @@
"navigation_bar.direct": "ئاماژەی تایبەت",
"navigation_bar.discover": "دۆزینەوە",
"navigation_bar.domain_blocks": "دۆمەینە بلۆک کراوەکان",
"navigation_bar.edit_profile": "دەستکاری پرۆفایل بکە",
"navigation_bar.explore": "گەڕان",
"navigation_bar.filters": "وشە کپەکان",
"navigation_bar.follow_requests": "بەدواداچوی داواکاریەکان بکە",
@ -439,14 +427,7 @@
"poll_button.add_poll": "ڕاپرسییەک زیاد بکە",
"poll_button.remove_poll": "ده‌نگدان بسڕه‌وه‌‌",
"privacy.change": "ڕێکخستنی تایبەتمەندی توت",
"privacy.direct.long": "تەنیا بۆ بەکارهێنەرانی ناوبراو",
"privacy.direct.short": "تەنها کەسانی باس کراو",
"privacy.private.long": "بینراو تەنها بۆ شوێنکەوتوان",
"privacy.private.short": "تەنیا شوێنکەوتووان",
"privacy.public.long": "بۆ هەمووان دیارە",
"privacy.public.short": "گشتی",
"privacy.unlisted.long": "بۆ هەمووان دیارە، بەڵام لە تایبەتمەندییەکانی دۆزینەوە دەرچووە",
"privacy.unlisted.short": "لە لیست نەکراو",
"privacy_policy.last_updated": "دوایین نوێکردنەوە {date}",
"privacy_policy.title": "سیاسەتی تایبەتێتی",
"refresh": "نوێکردنەوە",
@ -610,10 +591,8 @@
"upload_error.poll": "فایل و ڕاپرسی پێکەوە ڕێپێنەدراون.",
"upload_form.audio_description": "پەیامەکەت بۆ نابیستەکان",
"upload_form.description": "پەیامەکەت بۆ نابیناکان",
"upload_form.description_missing": "هیچ وەسفێک زیاد نەکراوە",
"upload_form.edit": "دەستکاری",
"upload_form.thumbnail": "گۆڕانی وینۆچکە",
"upload_form.undo": "بیسڕەوە",
"upload_form.video_description": "پەیامەکەت بۆ نابیست و نابیناکان",
"upload_modal.analyzing_picture": "وێنەکە شی دەکرێتەوە…",
"upload_modal.apply": "بیسەپێنە",

View file

@ -45,7 +45,6 @@
"alert.unexpected.title": "Uups!",
"announcement.announcement": "Annunziu",
"attachments_list.unprocessed": "(micca trattata)",
"autosuggest_hashtag.per_week": "{count} per settimana",
"boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta",
"bundle_column_error.retry": "Pruvà torna",
"bundle_modal_error.close": "Chjudà",
@ -80,20 +79,12 @@
"compose_form.lock_disclaimer": "U vostru contu ùn hè micca {locked}. Tuttu u mondu pò seguitavi è vede i vostri statuti privati.",
"compose_form.lock_disclaimer.lock": "privatu",
"compose_form.placeholder": "À chè pensate?",
"compose_form.poll.add_option": "Aghjunghje scelta",
"compose_form.poll.duration": "Durata di u scandagliu",
"compose_form.poll.option_placeholder": "Scelta {number}",
"compose_form.poll.remove_option": "Toglie sta scelta",
"compose_form.poll.switch_to_multiple": "Cambià u scandagliu per accittà parechje scelte",
"compose_form.poll.switch_to_single": "Cambià u scandagliu per ùn accittà ch'una scelta",
"compose_form.publish_form": "Publish",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "{count, plural, one {Indicà u media cum'è sensibile} other {Indicà i media cum'è sensibili}}",
"compose_form.sensitive.marked": "{count, plural, one {Media indicatu cum'è sensibile} other {Media indicati cum'è sensibili}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Media micca indicatu cum'è sensibile} other {Media micca indicati cum'è sensibili}}",
"compose_form.spoiler.marked": "Testu piattatu daret'à un'avertimentu",
"compose_form.spoiler.unmarked": "Testu micca piattatu",
"compose_form.spoiler_placeholder": "Scrive u vostr'avertimentu quì",
"confirmation_modal.cancel": "Annullà",
"confirmations.block.block_and_report": "Bluccà è signalà",
"confirmations.block.confirm": "Bluccà",
@ -245,7 +236,6 @@
"navigation_bar.compose": "Scrive un novu statutu",
"navigation_bar.discover": "Scopre",
"navigation_bar.domain_blocks": "Duminii piattati",
"navigation_bar.edit_profile": "Mudificà u prufile",
"navigation_bar.filters": "Parolle silenzate",
"navigation_bar.follow_requests": "Dumande d'abbunamentu",
"navigation_bar.follows_and_followers": "Abbunati è abbunamenti",
@ -318,12 +308,7 @@
"poll_button.add_poll": "Aghjunghje",
"poll_button.remove_poll": "Toglie u scandagliu",
"privacy.change": "Mudificà a cunfidenzialità di u statutu",
"privacy.direct.long": "Mandà solu à quelli chì so mintuvati",
"privacy.direct.short": "Direct",
"privacy.private.long": "Mustrà solu à l'abbunati",
"privacy.private.short": "Followers-only",
"privacy.public.short": "Pubblicu",
"privacy.unlisted.short": "Micca listatu",
"refresh": "Attualizà",
"regeneration_indicator.label": "Caricamentu…",
"regeneration_indicator.sublabel": "Priparazione di a vostra pagina d'accolta!",
@ -409,7 +394,6 @@
"upload_form.description": "Discrizzione per i malvistosi",
"upload_form.edit": "Mudificà",
"upload_form.thumbnail": "Cambià vignetta",
"upload_form.undo": "Sguassà",
"upload_form.video_description": "Discrizzione per i ciochi o cechi",
"upload_modal.analyzing_picture": "Analisi di u ritrattu…",
"upload_modal.apply": "Affettà",

View file

@ -87,7 +87,6 @@
"announcement.announcement": "Oznámení",
"attachments_list.unprocessed": "(nezpracováno)",
"audio.hide": "Skrýt zvuk",
"autosuggest_hashtag.per_week": "{count} za týden",
"boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
"bundle_column_error.copy_stacktrace": "Zkopírovat zprávu o chybě",
"bundle_column_error.error.body": "Požadovanou stránku nelze vykreslit. Může to být způsobeno chybou v našem kódu nebo problémem s kompatibilitou prohlížeče.",
@ -144,22 +143,12 @@
"compose_form.lock_disclaimer": "Váš účet není {locked}. Kdokoliv vás může sledovat a vidět vaše příspěvky učené pouze pro sledující.",
"compose_form.lock_disclaimer.lock": "zamčený",
"compose_form.placeholder": "Co se vám honí hlavou?",
"compose_form.poll.add_option": "Přidat volbu",
"compose_form.poll.duration": "Doba trvání ankety",
"compose_form.poll.option_placeholder": "Volba {number}",
"compose_form.poll.remove_option": "Odebrat tuto volbu",
"compose_form.poll.switch_to_multiple": "Povolit u ankety výběr více voleb",
"compose_form.poll.switch_to_single": "Povolit u ankety výběr pouze jedné volby",
"compose_form.publish": "Zveřejnit",
"compose_form.publish_form": "Zveřejnit",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Uložit změny",
"compose_form.sensitive.hide": "{count, plural, one {Označit média za citlivá} few {Označit média za citlivá} many {Označit média za citlivá} other {Označit média za citlivá}}",
"compose_form.sensitive.marked": "{count, plural, one {Média jsou označena za citlivá} few {Média jsou označena za citlivá} many {Média jsou označena za citlivá} other {Média jsou označena za citlivá}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Média nejsou označena za citlivá} few {Média nejsou označena za citlivá} many {Média nejsou označena za citlivá} other {Média nejsou označena za citlivá}}",
"compose_form.spoiler.marked": "Odebrat varování o obsahu",
"compose_form.spoiler.unmarked": "Přidat varování o obsahu",
"compose_form.spoiler_placeholder": "Sem napište vaše varování",
"confirmation_modal.cancel": "Zrušit",
"confirmations.block.block_and_report": "Blokovat a nahlásit",
"confirmations.block.confirm": "Blokovat",
@ -401,7 +390,6 @@
"navigation_bar.direct": "Soukromé zmínky",
"navigation_bar.discover": "Objevit",
"navigation_bar.domain_blocks": "Blokované domény",
"navigation_bar.edit_profile": "Upravit profil",
"navigation_bar.explore": "Prozkoumat",
"navigation_bar.favourites": "Oblíbené",
"navigation_bar.filters": "Skrytá slova",
@ -508,14 +496,7 @@
"poll_button.add_poll": "Přidat anketu",
"poll_button.remove_poll": "Odebrat anketu",
"privacy.change": "Změnit soukromí příspěvku",
"privacy.direct.long": "Viditelný pouze pro zmíněné uživatele",
"privacy.direct.short": "Pouze zmínění lidé",
"privacy.private.long": "Viditelný pouze pro sledující",
"privacy.private.short": "Pouze sledující",
"privacy.public.long": "Viditelný pro všechny",
"privacy.public.short": "Veřejné",
"privacy.unlisted.long": "Viditelný pro všechny, ale vyňat z funkcí objevování",
"privacy.unlisted.short": "Neveřejný",
"privacy_policy.last_updated": "Naposledy aktualizováno {date}",
"privacy_policy.title": "Zásady ochrany osobních údajů",
"refresh": "Obnovit",
@ -695,10 +676,8 @@
"upload_error.poll": "Nahrávání souborů není povoleno s anketami.",
"upload_form.audio_description": "Popis pro sluchově postižené",
"upload_form.description": "Popis pro zrakově postižené",
"upload_form.description_missing": "Nebyl přidán popis",
"upload_form.edit": "Upravit",
"upload_form.thumbnail": "Změnit miniaturu",
"upload_form.undo": "Smazat",
"upload_form.video_description": "Popis pro sluchově či zrakově postižené",
"upload_modal.analyzing_picture": "Analyzuji obrázek…",
"upload_modal.apply": "Použít",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Cyhoeddiad",
"attachments_list.unprocessed": "(heb eu prosesu)",
"audio.hide": "Cuddio sain",
"autosuggest_hashtag.per_week": "{count} yr wythnos",
"boost_modal.combo": "Mae modd pwyso {combo} er mwyn hepgor hyn tro nesa",
"bundle_column_error.copy_stacktrace": "Copïo'r adroddiad gwall",
"bundle_column_error.error.body": "Nid oedd modd cynhyrchu'r dudalen honno. Gall fod oherwydd gwall yn ein cod neu fater cydnawsedd porwr.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich postiadau dilynwyr-yn-unig.",
"compose_form.lock_disclaimer.lock": "wedi ei gloi",
"compose_form.placeholder": "Beth sydd ar eich meddwl?",
"compose_form.poll.add_option": "Ychwanegu dewis",
"compose_form.poll.duration": "Cyfnod pleidlais",
"compose_form.poll.option_placeholder": "Dewis {number}",
"compose_form.poll.remove_option": "Tynnu'r dewis",
"compose_form.poll.switch_to_multiple": "Newid pleidlais i adael mwy nag un dewis",
"compose_form.poll.switch_to_single": "Newid pleidlais i gyfyngu i un dewis",
"compose_form.publish": "Cyhoeddi",
"compose_form.publish_form": "Cyhoeddi",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Cadw newidiadau",
"compose_form.sensitive.hide": "Marcio cyfryngau fel eu bod yn sensitif",
"compose_form.sensitive.marked": "Cyfryngau wedi'u marcio'n sensitif",
"compose_form.sensitive.unmarked": "Nid yw'r cyfryngau wedi'u marcio'n sensitif",
"compose_form.spoiler.marked": "Dileu rhybudd cynnwys",
"compose_form.spoiler.unmarked": "Ychwanegu rhybudd cynnwys",
"compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
"confirmation_modal.cancel": "Diddymu",
"confirmations.block.block_and_report": "Rhwystro ac Adrodd",
"confirmations.block.confirm": "Blocio",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Crybwylliadau preifat",
"navigation_bar.discover": "Darganfod",
"navigation_bar.domain_blocks": "Parthau wedi'u blocio",
"navigation_bar.edit_profile": "Golygu proffil",
"navigation_bar.explore": "Darganfod",
"navigation_bar.favourites": "Ffefrynnau",
"navigation_bar.filters": "Geiriau wedi'u tewi",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Ychwanegu pleidlais",
"poll_button.remove_poll": "Tynnu pleidlais",
"privacy.change": "Addasu preifatrwdd y post",
"privacy.direct.long": "Dim ond yn weladwy i ddefnyddwyr a grybwyllwyd",
"privacy.direct.short": "Dim ond pobl sy wedi'u crybwyll",
"privacy.private.long": "Dim ond pobl sy'n ddilynwyr",
"privacy.private.short": "Dilynwyr yn unig",
"privacy.public.long": "Gweladwy i bawb",
"privacy.public.short": "Cyhoeddus",
"privacy.unlisted.long": "Gweladwy i bawb, ond wedi optio allan o nodweddion darganfod",
"privacy.unlisted.short": "Heb ei restru",
"privacy_policy.last_updated": "Diweddarwyd ddiwethaf ar {date}",
"privacy_policy.title": "Polisi Preifatrwydd",
"recommended": "Argymhellwyd",
@ -715,10 +696,8 @@
"upload_error.poll": "Nid oes modd llwytho ffeiliau â phleidleisiau.",
"upload_form.audio_description": "Disgrifio ar gyfer pobl sydd â cholled clyw",
"upload_form.description": "Disgrifio i'r rheini a nam ar ei golwg",
"upload_form.description_missing": "Dim disgrifiad wedi'i ychwanegu",
"upload_form.edit": "Golygu",
"upload_form.thumbnail": "Newid llun bach",
"upload_form.undo": "Dileu",
"upload_form.video_description": "Disgrifio ar gyfer pobl sydd â cholled clyw neu amhariad golwg",
"upload_modal.analyzing_picture": "Yn dadansoddi llun…",
"upload_modal.apply": "Gosod",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Bekendtgørelse",
"attachments_list.unprocessed": "(ubehandlet)",
"audio.hide": "Skjul lyd",
"autosuggest_hashtag.per_week": "{count} pr. uge",
"boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang",
"bundle_column_error.copy_stacktrace": "Kopiér fejlrapport",
"bundle_column_error.error.body": "Den anmodede side kunne ikke gengives. Dette kan skyldes flere typer fejl.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "Din konto er ikke {locked}. Enhver kan følge dig og se indlæg kun beregnet for følgere.",
"compose_form.lock_disclaimer.lock": "låst",
"compose_form.placeholder": "Hvad tænker du på?",
"compose_form.poll.add_option": "Tilføj valgmulighed",
"compose_form.poll.add_option": "Tilføj mulighed",
"compose_form.poll.duration": "Afstemningens varighed",
"compose_form.poll.multiple": "Multivalg",
"compose_form.poll.option_placeholder": "Valgmulighed {number}",
"compose_form.poll.remove_option": "Fjern denne valgmulighed",
"compose_form.poll.single": "Vælg én",
"compose_form.poll.switch_to_multiple": "Ændr afstemning til flervalgstype",
"compose_form.poll.switch_to_single": "Ændr afstemning til enkeltvalgstype",
"compose_form.publish": "Publicér",
"compose_form.poll.type": "Stil",
"compose_form.publish": "Indsend",
"compose_form.publish_form": "Publicér",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Gem ændringer",
"compose_form.sensitive.hide": "{count, plural, one {Markér medie som følsomt} other {Markér medier som følsomme}}",
"compose_form.sensitive.marked": "{count, plural, one {Medie er markeret som sensitivt} other {Medier er markerede som sensitive}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Medie er ikke market som sensitivt} other {Medier er ikke markerede som sensitive}}",
"compose_form.reply": "Svar",
"compose_form.save_changes": "Opdatér",
"compose_form.spoiler.marked": "Fjern indholdsadvarsel",
"compose_form.spoiler.unmarked": "Tilføj indholdsadvarsel",
"compose_form.spoiler_placeholder": "Skriv din advarsel hér",
"compose_form.spoiler_placeholder": "Indholdsadvarsel (valgfri)",
"confirmation_modal.cancel": "Afbryd",
"confirmations.block.block_and_report": "Blokér og Anmeld",
"confirmations.block.confirm": "Blokér",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Private omtaler",
"navigation_bar.discover": "Opdag",
"navigation_bar.domain_blocks": "Blokerede domæner",
"navigation_bar.edit_profile": "Redigér profil",
"navigation_bar.explore": "Udforsk",
"navigation_bar.favourites": "Favoritter",
"navigation_bar.filters": "Skjulte ord (mutede)",
@ -526,14 +524,15 @@
"poll_button.add_poll": "Tilføj en afstemning",
"poll_button.remove_poll": "Fjern afstemning",
"privacy.change": "Tilpas indlægsfortrolighed",
"privacy.direct.long": "Kun synlig for nævnte brugere",
"privacy.direct.short": "Kun omtalte personer",
"privacy.private.long": "Kun synlig for følgere",
"privacy.private.short": "Kun følgere",
"privacy.public.long": "Synlig for alle",
"privacy.direct.long": "Alle nævnt i indlægget",
"privacy.direct.short": "Bestemte personer",
"privacy.private.long": "Kun dine følgere",
"privacy.private.short": "Følgere",
"privacy.public.long": "Alle på og udenfor Mastodon",
"privacy.public.short": "Offentlig",
"privacy.unlisted.long": "Synlig for alle, men med fravalgt visning i opdagelsesfunktioner",
"privacy.unlisted.short": "Diskret",
"privacy.unlisted.additional": "Dette er præcis som offentlig adfærd, dog vises indlægget ikke i live feeds/hashtags, udforsk eller Mastodon-søgning, selv hvis valget gælder hele kontoen.",
"privacy.unlisted.long": "Færre algoritmiske fanfarer",
"privacy.unlisted.short": "Tavsgøre offentligt",
"privacy_policy.last_updated": "Senest opdateret {date}",
"privacy_policy.title": "Privatlivspolitik",
"recommended": "Anbefalet",
@ -551,7 +550,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "i dag",
"reply_indicator.attachments": "{count, plural, one {# vedhæftning} other {# vedhæftninger}}",
"reply_indicator.cancel": "Afbryd",
"reply_indicator.poll": "Afstemning",
"report.block": "Blokér",
"report.block_explanation": "Du vil ikke se vedkommendes indlæg. Vedkommende vil ikke kunne se dine indlæg eller følge dig. Vedkommende vil kunne se, at de er blokeret.",
"report.categories.legal": "Juridisk",
@ -715,10 +716,8 @@
"upload_error.poll": "Filupload ikke tilladt for afstemninger.",
"upload_form.audio_description": "Beskrivelse til hørehæmmede",
"upload_form.description": "Beskrivelse til svagtseende",
"upload_form.description_missing": "Ingen beskrivelse tilføjet",
"upload_form.edit": "Redigér",
"upload_form.thumbnail": "Skift miniature",
"upload_form.undo": "Slet",
"upload_form.video_description": "Beskrivelse for hørehæmmede eller synshandicappede personer",
"upload_modal.analyzing_picture": "Analyserer billede…",
"upload_modal.apply": "Anvend",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Ankündigung",
"attachments_list.unprocessed": "(ausstehend)",
"audio.hide": "Audio ausblenden",
"autosuggest_hashtag.per_week": "{count} pro Woche",
"boost_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
"bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren",
"bundle_column_error.error.body": "Die angeforderte Seite konnte nicht dargestellt werden. Dies könnte auf einen Fehler in unserem Code oder auf ein Browser-Kompatibilitätsproblem zurückzuführen sein.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.",
"compose_form.lock_disclaimer.lock": "geschützt",
"compose_form.placeholder": "Was gibts Neues?",
"compose_form.poll.add_option": "Auswahl",
"compose_form.poll.add_option": "Auswahl hinzufügen",
"compose_form.poll.duration": "Umfragedauer",
"compose_form.poll.option_placeholder": "{number}. Auswahl",
"compose_form.poll.remove_option": "Auswahlfeld entfernen",
"compose_form.poll.multiple": "Mehrfachauswahl",
"compose_form.poll.option_placeholder": "{number}. Auswahlmöglichkeit",
"compose_form.poll.remove_option": "Dieses Auswahlfeld entfernen",
"compose_form.poll.single": "Einfachauswahl",
"compose_form.poll.switch_to_multiple": "Mehrfachauswahl erlauben",
"compose_form.poll.switch_to_single": "Nur Einzelauswahl erlauben",
"compose_form.poll.switch_to_single": "Nur Einfachauswahl erlauben",
"compose_form.poll.type": "Art",
"compose_form.publish": "Veröffentlichen",
"compose_form.publish_form": "Neuer Beitrag",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Änderungen speichern",
"compose_form.sensitive.hide": "{count, plural, one {Mit einer Inhaltswarnung versehen} other {Mit einer Inhaltswarnung versehen}}",
"compose_form.sensitive.marked": "{count, plural, one {Medien-Datei ist mit einer Inhaltswarnung versehen} other {Medien-Dateien sind mit einer Inhaltswarnung versehen}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Medien-Datei ist nicht mit einer Inhaltswarnung versehen} other {Medien-Dateien sind nicht mit einer Inhaltswarnung versehen}}",
"compose_form.reply": "Antworten",
"compose_form.save_changes": "Aktualisieren",
"compose_form.spoiler.marked": "Inhaltswarnung entfernen",
"compose_form.spoiler.unmarked": "Inhaltswarnung hinzufügen",
"compose_form.spoiler_placeholder": "Inhaltswarnung",
"compose_form.spoiler_placeholder": "Inhaltswarnung (optional)",
"confirmation_modal.cancel": "Abbrechen",
"confirmations.block.block_and_report": "Blockieren und melden",
"confirmations.block.confirm": "Blockieren",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Private Erwähnungen",
"navigation_bar.discover": "Entdecken",
"navigation_bar.domain_blocks": "Blockierte Domains",
"navigation_bar.edit_profile": "Profil bearbeiten",
"navigation_bar.explore": "Entdecken",
"navigation_bar.favourites": "Favoriten",
"navigation_bar.filters": "Stummgeschaltete Wörter",
@ -526,14 +524,15 @@
"poll_button.add_poll": "Umfrage erstellen",
"poll_button.remove_poll": "Umfrage entfernen",
"privacy.change": "Sichtbarkeit anpassen",
"privacy.direct.long": "Nur für die erwähnten Profile sichtbar",
"privacy.direct.short": "Nur erwähnte Profile",
"privacy.private.long": "Nur für deine Follower sichtbar",
"privacy.private.short": "Nur Follower",
"privacy.public.long": "Für alle sichtbar",
"privacy.direct.long": "Alle in diesem Beitrag erwähnten Profile",
"privacy.direct.short": "Bestimmte Profile",
"privacy.private.long": "Nur deine Follower",
"privacy.private.short": "Follower",
"privacy.public.long": "Alle auf und außerhalb von Mastodon",
"privacy.public.short": "Öffentlich",
"privacy.unlisted.long": "Für alle sichtbar, aber nicht über die Suche zu finden",
"privacy.unlisted.short": "Nicht gelistet",
"privacy.unlisted.additional": "Das Verhalten ist wie bei „Öffentlich“, jedoch erscheint dieser Beitrag nicht in „Live-Feeds“, „Erkunden“, Hashtags oder über die Mastodon-Suchfunktion selbst wenn du das in den Einstellungen aktiviert hast.",
"privacy.unlisted.long": "Weniger im Algorithmus berücksichtigt",
"privacy.unlisted.short": "Öffentlich (eingeschränkt)",
"privacy_policy.last_updated": "Stand: {date}",
"privacy_policy.title": "Datenschutzerklärung",
"recommended": "Empfohlen",
@ -551,7 +550,9 @@
"relative_time.minutes": "{number} Min.",
"relative_time.seconds": "{number} Sek.",
"relative_time.today": "heute",
"reply_indicator.attachments": "{count, plural, one {# Anhang} other {# Anhänge}}",
"reply_indicator.cancel": "Abbrechen",
"reply_indicator.poll": "Umfrage",
"report.block": "Blockieren",
"report.block_explanation": "Du wirst keine Beiträge mehr von diesem Konto sehen. Das blockierte Konto wird deine Beiträge nicht mehr sehen oder dir folgen können. Die Person könnte mitbekommen, dass du sie blockiert hast.",
"report.categories.legal": "Rechtlich",
@ -715,10 +716,8 @@
"upload_error.poll": "Medien-Anhänge sind zusammen mit Umfragen nicht erlaubt.",
"upload_form.audio_description": "Beschreibe für Menschen mit Hörbehinderung",
"upload_form.description": "Beschreibe für Menschen mit Sehbehinderung",
"upload_form.description_missing": "Keine Beschreibung hinzugefügt",
"upload_form.edit": "Bearbeiten",
"upload_form.thumbnail": "Vorschaubild ändern",
"upload_form.undo": "Löschen",
"upload_form.video_description": "Beschreibe für Menschen mit einer Hör- oder Sehbehinderung",
"upload_modal.analyzing_picture": "Bild wird analysiert …",
"upload_modal.apply": "Übernehmen",

View file

@ -85,7 +85,6 @@
"announcement.announcement": "Ανακοίνωση",
"attachments_list.unprocessed": "(μη επεξεργασμένο)",
"audio.hide": "Απόκρυψη αρχείου ήχου",
"autosuggest_hashtag.per_week": "{count} ανά εβδομάδα",
"boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις την επόμενη φορά",
"bundle_column_error.copy_stacktrace": "Αντιγραφή αναφοράς σφάλματος",
"bundle_column_error.error.body": "Δεν ήταν δυνατή η απόδοση της σελίδας που ζήτησες. Μπορεί να οφείλεται σε σφάλμα στον κώδικά μας ή σε πρόβλημα συμβατότητας του προγράμματος περιήγησης.",
@ -111,6 +110,7 @@
"column.direct": "Ιδιωτικές αναφορές",
"column.directory": "Περιήγηση στα προφίλ",
"column.domain_blocks": "Αποκλεισμένοι τομείς",
"column.favourites": "Αγαπημένα",
"column.follow_requests": "Αιτήματα ακολούθησης",
"column.home": "Αρχική",
"column.lists": "Λίστες",
@ -131,6 +131,9 @@
"community.column_settings.remote_only": "Απομακρυσμένα μόνο",
"compose.language.change": "Αλλαγή γλώσσας",
"compose.language.search": "Αναζήτηση γλωσσών...",
"compose.published.body": "Η ανάρτηση δημοσιεύτηκε.",
"compose.published.open": "Άνοιγμα",
"compose.saved.body": "Η ανάρτηση αποθηκεύτηκε.",
"compose_form.direct_message_warning_learn_more": "Μάθε περισσότερα",
"compose_form.encryption_warning": "Οι δημοσιεύσεις στο Mastodon δεν είναι κρυπτογραφημένες από άκρο σε άκρο. Μη μοιράζεσαι ευαίσθητες πληροφορίες μέσω του Mastodon.",
"compose_form.hashtag_warning": "Αυτή η δημοσίευση δεν θα εμφανίζεται κάτω από οποιαδήποτε ετικέτα καθώς δεν είναι δημόσια. Μόνο οι δημόσιες δημοσιεύσεις μπορούν να αναζητηθούν με ετικέτα.",
@ -139,20 +142,19 @@
"compose_form.placeholder": "Τι σκέφτεσαι;",
"compose_form.poll.add_option": "Προσθήκη επιλογής",
"compose_form.poll.duration": "Διάρκεια δημοσκόπησης",
"compose_form.poll.multiple": "Πολλαπλή επιλογή",
"compose_form.poll.option_placeholder": "Επιλογή {number}",
"compose_form.poll.remove_option": "Αφαίρεση επιλογής",
"compose_form.poll.switch_to_multiple": "Ενημέρωση δημοσκόπησης με πολλαπλές επιλογές",
"compose_form.poll.switch_to_single": "Ενημέρωση δημοσκόπησης με μοναδική επιλογή",
"compose_form.publish": "Δημοσίευση",
"compose_form.poll.type": "Στυλ",
"compose_form.publish": "Ανάρτηση",
"compose_form.publish_form": "Δημοσίευση",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Αποθήκευση αλλαγών",
"compose_form.sensitive.hide": "{count, plural, one {Επισήμανση πολυμέσου ως ευαίσθητο} other {Επισήμανση πολυμέσων ως ευαίσθητα}}",
"compose_form.sensitive.marked": "{count, plural, one {Το πολυμέσο έχει σημειωθεί ως ευαίσθητο} other {Τα πολυμέσα έχουν σημειωθεί ως ευαίσθητα}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Το πολυμέσο δεν έχει σημειωθεί ως ευαίσθητο} other {Τα πολυμέσα δεν έχουν σημειωθεί ως ευαίσθητα}}",
"compose_form.reply": "Απάντηση",
"compose_form.save_changes": "Ενημέρωση",
"compose_form.spoiler.marked": "Αφαίρεση προειδοποίηση περιεχομένου",
"compose_form.spoiler.unmarked": "Προσθήκη προειδοποίησης περιεχομένου",
"compose_form.spoiler_placeholder": "Γράψε την προειδοποίησή σου εδώ",
"compose_form.spoiler_placeholder": "Προειδοποίηση περιεχομένου (προαιρετική)",
"confirmation_modal.cancel": "Άκυρο",
"confirmations.block.block_and_report": "Αποκλεισμός & Αναφορά",
"confirmations.block.confirm": "Αποκλεισμός",
@ -184,6 +186,7 @@
"conversation.mark_as_read": "Σήμανση ως αναγνωσμένο",
"conversation.open": "Προβολή συνομιλίας",
"conversation.with": "Με {names}",
"copy_icon_button.copied": "Αντιγράφηκε στο πρόχειρο",
"copypaste.copied": "Αντιγράφηκε",
"copypaste.copy_to_clipboard": "Αντιγραφή στο πρόχειρο",
"directory.federated": "Από το γνωστό fediverse",
@ -380,7 +383,6 @@
"navigation_bar.direct": "Ιδιωτικές επισημάνσεις",
"navigation_bar.discover": "Ανακάλυψη",
"navigation_bar.domain_blocks": "Αποκλεισμένοι τομείς",
"navigation_bar.edit_profile": "Επεξεργασία προφίλ",
"navigation_bar.explore": "Εξερεύνηση",
"navigation_bar.filters": "Αποσιωπημένες λέξεις",
"navigation_bar.follow_requests": "Αιτήματα ακολούθησης",
@ -450,6 +452,12 @@
"onboarding.actions.go_to_home": "Πηγαίνετε στην αρχική σας ροή",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Δημοφιλή στο Mastodon",
"onboarding.profile.note": "Βιογραφικό",
"onboarding.profile.note_hint": "Μπορείτε να @αναφέρετε άλλα άτομα ή #hashtags…",
"onboarding.profile.save_and_continue": "Αποθήκευση και συνέχεια",
"onboarding.profile.title": "Ρύθμιση προφίλ",
"onboarding.profile.upload_avatar": "Μεταφόρτωση εικόνας προφίλ",
"onboarding.profile.upload_header": "Μεταφόρτωση κεφαλίδας προφίλ",
"onboarding.share.lead": "Let people know how they can find you on Mastodon!\nΕνημερώστε άλλα άτομα πώς μπορούν να σας βρουν στο Mastodon!",
"onboarding.share.next_steps": "Πιθανά επόμενα βήματα:",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
@ -458,6 +466,7 @@
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
"onboarding.steps.publish_status.body": "Say hello to the world.",
"onboarding.steps.publish_status.title": "Κάντε την πρώτη σας δημοσίευση",
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
"onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
@ -467,6 +476,7 @@
"picture_in_picture.restore": "Βάλε το πίσω",
"poll.closed": "Κλειστή",
"poll.refresh": "Ανανέωση",
"poll.reveal": "Δείτε τα αποτελέσματα",
"poll.total_people": "{count, plural, one {# άτομο} other {# άτομα}}",
"poll.total_votes": "{count, plural, one {# ψήφος} other {# ψήφοι}}",
"poll.vote": "Ψήφισε",
@ -475,16 +485,14 @@
"poll_button.add_poll": "Προσθήκη δημοσκόπησης",
"poll_button.remove_poll": "Αφαίρεση δημοσκόπησης",
"privacy.change": "Προσαρμογή ιδιωτικότητας ανάρτησης",
"privacy.direct.long": "Δημοσίευση μόνο σε όσους επισημαίνονται",
"privacy.direct.short": "Αναφερόμενα άτομα μόνο",
"privacy.private.long": "Ορατό μόνο για τους ακολούθους",
"privacy.private.short": "Μόνο ακόλουθοι",
"privacy.public.long": "Ορατό σε όλους",
"privacy.direct.long": "Όλοι όσοι αναφέρθηκαν στη δημοσίευση",
"privacy.direct.short": "Συγκεκριμένα άτομα",
"privacy.private.long": "Μόνο οι ακόλουθοί σας",
"privacy.private.short": "Ακόλουθοι",
"privacy.public.short": "Δημόσιο",
"privacy.unlisted.long": "Ορατό για όλους, εκτός αυτών που δεν συμμετέχουν σε δυνατότητες ανακάλυψης",
"privacy.unlisted.short": "Μη καταχωρημένα",
"privacy_policy.last_updated": "Τελευταία ενημέρωση {date}",
"privacy_policy.title": "Πολιτική Απορρήτου",
"recommended": "Προτεινόμενα",
"refresh": "Ανανέωση",
"regeneration_indicator.label": "Φορτώνει…",
"regeneration_indicator.sublabel": "Η αρχική σου ροή ετοιμάζεται!",
@ -500,8 +508,10 @@
"relative_time.seconds": "{number}δ",
"relative_time.today": "σήμερα",
"reply_indicator.cancel": "Άκυρο",
"reply_indicator.poll": "Δημοσκόπηση",
"report.block": "Αποκλεισμός",
"report.block_explanation": "Δεν θα βλέπεις τις αναρτήσεις του. Δεν θα μπορεί να δει τις αναρτήσεις σου ή να σε ακολουθήσει. Θα μπορεί να δει ότι έχει αποκλειστεί.",
"report.categories.legal": "Νομικό περιεχόμενο",
"report.categories.other": "Άλλες",
"report.categories.spam": "Ανεπιθύμητα",
"report.categories.violation": "Το περιεχόμενο παραβιάζει έναν ή περισσότερους κανόνες διακομιστή",
@ -519,6 +529,8 @@
"report.placeholder": "Επιπλέον σχόλια",
"report.reasons.dislike": "Δεν μου αρέσει",
"report.reasons.dislike_description": "Δεν είναι κάτι που θα ήθελες να δεις",
"report.reasons.legal": "Είναι παράνομο",
"report.reasons.legal_description": "Πιστεύετε ότι παραβιάζει το νόμο της χώρας σας ή της χώρας του διακομιστή",
"report.reasons.other": "Είναι κάτι άλλο",
"report.reasons.other_description": "Το ζήτημα δεν ταιριάζει σε άλλες κατηγορίες",
"report.reasons.spam": "Είναι σπαμ",
@ -552,10 +564,12 @@
"search.search_or_paste": "Αναζήτηση ή εισαγωγή URL",
"search_popout.quick_actions": "Γρήγορες ενέργειες",
"search_popout.recent": "Πρόσφατες αναζητήσεις",
"search_popout.user": "χρήστης",
"search_results.accounts": "Προφίλ",
"search_results.all": "Όλα",
"search_results.hashtags": "Ετικέτες",
"search_results.nothing_found": "Δεν βρέθηκε τίποτα με αυτούς τους όρους αναζήτησης",
"search_results.see_all": "Δες τα όλα",
"search_results.statuses": "Αναρτήσεις",
"search_results.title": "Αναζήτηση για {q}",
"server_banner.about_active_users": "Άτομα που χρησιμοποιούν αυτόν τον διακομιστή κατά τις τελευταίες 30 ημέρες (Μηνιαία Ενεργοί Χρήστες)",
@ -566,6 +580,8 @@
"server_banner.server_stats": "Στατιστικά διακομιστή:",
"sign_in_banner.create_account": "Δημιουργία λογαριασμού",
"sign_in_banner.sign_in": "Σύνδεση",
"sign_in_banner.sso_redirect": "Συνδεθείτε ή Εγγραφείτε",
"sign_in_banner.text": "Συνδεθείτε για να ακολουθήσετε προφίλ ή ετικέτες, αγαπήστε, μοιραστείτε και απαντήστε σε δημοσιεύσεις. Μπορείτε επίσης να αλληλεπιδράσετε από τον λογαριασμό σας σε διαφορετικό διακομιστή.",
"status.admin_account": "Άνοιγμα διεπαφής συντονισμού για τον/την @{name}",
"status.admin_domain": "Άνοιγμα λειτουργίας διαμεσολάβησης για {domain}",
"status.admin_status": "Άνοιγμα αυτής της ανάρτησης σε διεπαφή συντονισμού",
@ -582,6 +598,7 @@
"status.edited": "Επεξεργάστηκε στις {date}",
"status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}",
"status.embed": "Ενσωμάτωσε",
"status.favourite": "Αγαπημένα",
"status.filter": "Φιλτράρισμα αυτής της ανάρτησης",
"status.filtered": "Φιλτραρισμένα",
"status.hide": "Απόκρυψη ανάρτησης",
@ -646,10 +663,8 @@
"upload_error.poll": "Στις δημοσκοπήσεις δεν επιτρέπεται η μεταφόρτωση αρχείου.",
"upload_form.audio_description": "Περιγραφή για άτομα με προβλήματα ακοής",
"upload_form.description": "Περιγραφή για άτομα με προβλήματα όρασης",
"upload_form.description_missing": "Δεν προστέθηκε περιγραφή",
"upload_form.edit": "Επεξεργασία",
"upload_form.thumbnail": "Αλλαγή μικρογραφίας",
"upload_form.undo": "Διαγραφή",
"upload_form.video_description": "Περιγραφή για άτομα με προβλήματα ακοής ή όρασης",
"upload_modal.analyzing_picture": "Ανάλυση εικόνας…",
"upload_modal.apply": "Εφαρμογή",

View file

@ -87,7 +87,6 @@
"announcement.announcement": "Announcement",
"attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "Hide audio",
"autosuggest_hashtag.per_week": "{count} per week",
"boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.copy_stacktrace": "Copy error report",
"bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
@ -144,22 +143,12 @@
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
"compose_form.lock_disclaimer.lock": "locked",
"compose_form.placeholder": "What's on your mind?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
"compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
"compose_form.publish": "Publish",
"compose_form.publish_form": "New post",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Save changes",
"compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
"compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
"compose_form.spoiler.marked": "Remove content warning",
"compose_form.spoiler.unmarked": "Add content warning",
"compose_form.spoiler_placeholder": "Write your warning here",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.confirm": "Block",
@ -406,7 +395,6 @@
"navigation_bar.direct": "Private mentions",
"navigation_bar.discover": "Discover",
"navigation_bar.domain_blocks": "Blocked domains",
"navigation_bar.edit_profile": "Edit profile",
"navigation_bar.explore": "Explore",
"navigation_bar.favourites": "Favourites",
"navigation_bar.filters": "Muted words",
@ -524,14 +512,7 @@
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.change": "Change post privacy",
"privacy.direct.long": "Visible for mentioned users only",
"privacy.direct.short": "Mentioned people only",
"privacy.private.long": "Visible for followers only",
"privacy.private.short": "Followers-only",
"privacy.public.long": "Visible for all",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
"privacy.unlisted.short": "Unlisted",
"privacy_policy.last_updated": "Last updated {date}",
"privacy_policy.title": "Privacy Policy",
"recommended": "Recommended",
@ -713,10 +694,8 @@
"upload_error.poll": "File upload not allowed with polls.",
"upload_form.audio_description": "Describe for people who are deaf or hard of hearing",
"upload_form.description": "Describe for people who are blind or have low vision",
"upload_form.description_missing": "No description added",
"upload_form.edit": "Edit",
"upload_form.thumbnail": "Change thumbnail",
"upload_form.undo": "Delete",
"upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
"upload_modal.analyzing_picture": "Analysing picture…",
"upload_modal.apply": "Apply",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Announcement",
"attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "Hide audio",
"autosuggest_hashtag.per_week": "{count} per week",
"boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.copy_stacktrace": "Copy error report",
"bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
@ -146,22 +145,20 @@
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
"compose_form.lock_disclaimer.lock": "locked",
"compose_form.placeholder": "What's on your mind?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.poll.multiple": "Multiple choice",
"compose_form.poll.option_placeholder": "Option {number}",
"compose_form.poll.single": "Pick one",
"compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
"compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
"compose_form.publish": "Publish",
"compose_form.poll.type": "Style",
"compose_form.publish": "Post",
"compose_form.publish_form": "New post",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Save changes",
"compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
"compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
"compose_form.reply": "Reply",
"compose_form.save_changes": "Update",
"compose_form.spoiler.marked": "Remove content warning",
"compose_form.spoiler.unmarked": "Add content warning",
"compose_form.spoiler_placeholder": "Write your warning here",
"compose_form.spoiler_placeholder": "Content warning (optional)",
"confirmation_modal.cancel": "Cancel",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.confirm": "Block",
@ -408,7 +405,6 @@
"navigation_bar.direct": "Private mentions",
"navigation_bar.discover": "Discover",
"navigation_bar.domain_blocks": "Blocked domains",
"navigation_bar.edit_profile": "Edit profile",
"navigation_bar.explore": "Explore",
"navigation_bar.favourites": "Favorites",
"navigation_bar.filters": "Muted words",
@ -526,14 +522,15 @@
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.change": "Change post privacy",
"privacy.direct.long": "Visible for mentioned users only",
"privacy.direct.short": "Mentioned people only",
"privacy.private.long": "Visible for followers only",
"privacy.private.short": "Followers only",
"privacy.public.long": "Visible for all",
"privacy.direct.long": "Everyone mentioned in the post",
"privacy.direct.short": "Specific people",
"privacy.private.long": "Only your followers",
"privacy.private.short": "Followers",
"privacy.public.long": "Anyone on and off Mastodon",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
"privacy.unlisted.short": "Unlisted",
"privacy.unlisted.additional": "This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.",
"privacy.unlisted.long": "Fewer algorithmic fanfares",
"privacy.unlisted.short": "Quiet public",
"privacy_policy.last_updated": "Last updated {date}",
"privacy_policy.title": "Privacy Policy",
"recommended": "Recommended",
@ -551,7 +548,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "today",
"reply_indicator.attachments": "{count, plural, one {# attachment} other {# attachments}}",
"reply_indicator.cancel": "Cancel",
"reply_indicator.poll": "Poll",
"report.block": "Block",
"report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
"report.categories.legal": "Legal",
@ -715,10 +714,8 @@
"upload_error.poll": "File upload not allowed with polls.",
"upload_form.audio_description": "Describe for people who are deaf or hard of hearing",
"upload_form.description": "Describe for people who are blind or have low vision",
"upload_form.description_missing": "No description added",
"upload_form.edit": "Edit",
"upload_form.thumbnail": "Change thumbnail",
"upload_form.undo": "Delete",
"upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
"upload_modal.analyzing_picture": "Analyzing picture…",
"upload_modal.apply": "Apply",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Anoncoj",
"attachments_list.unprocessed": "(neprilaborita)",
"audio.hide": "Kaŝi aŭdion",
"autosuggest_hashtag.per_week": "po {count} por semajno",
"boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
"bundle_column_error.copy_stacktrace": "Kopii la eraran raporton",
"bundle_column_error.error.body": "La petita paĝo ne povas redonitis. Eble estas eraro.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Via konto ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn afiŝojn nur al la sekvantoj.",
"compose_form.lock_disclaimer.lock": "ŝlosita",
"compose_form.placeholder": "Kion vi pensas?",
"compose_form.poll.add_option": "Aldoni elekteblon",
"compose_form.poll.duration": "Daŭro de la balotenketo",
"compose_form.poll.option_placeholder": "Elekteblo {number}",
"compose_form.poll.remove_option": "Forigi ĉi tiu elekteblon",
"compose_form.poll.switch_to_multiple": "Ŝanĝi la balotenketon por permesi multajn elektojn",
"compose_form.poll.switch_to_single": "Ŝanĝi la balotenketon por permesi unu solan elekton",
"compose_form.publish": "Afiŝi",
"compose_form.publish_form": "Afiŝi",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Konservi ŝanĝojn",
"compose_form.sensitive.hide": "{count, plural, one {Marki la plurmedio kiel tikla} other {Marki la plurmedioj kiel tiklaj}}",
"compose_form.sensitive.marked": "{count, plural, one {La plurmedio estas markita kiel tikla} other {La plurmedioj estas markitaj kiel tiklaj}}",
"compose_form.sensitive.unmarked": "{count, plural, one {La plurmedio ne estas markita kiel tikla} other {La plurmedioj ne estas markitaj kiel tiklaj}}",
"compose_form.spoiler.marked": "Forigi la averton de enhavo",
"compose_form.spoiler.unmarked": "Aldoni averton de enhavo",
"compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie",
"confirmation_modal.cancel": "Nuligi",
"confirmations.block.block_and_report": "Bloki kaj raporti",
"confirmations.block.confirm": "Bloki",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Privataj mencioj",
"navigation_bar.discover": "Esplori",
"navigation_bar.domain_blocks": "Blokitaj domajnoj",
"navigation_bar.edit_profile": "Redakti profilon",
"navigation_bar.explore": "Esplori",
"navigation_bar.favourites": "Stelumoj",
"navigation_bar.filters": "Silentigitaj vortoj",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Aldoni balotenketon",
"poll_button.remove_poll": "Forigi balotenketon",
"privacy.change": "Agordi mesaĝan privatecon",
"privacy.direct.long": "Videbla nur al menciitaj uzantoj",
"privacy.direct.short": "Nur menciitaj personoj",
"privacy.private.long": "Videbla nur al viaj sekvantoj",
"privacy.private.short": "Nur abonantoj",
"privacy.public.long": "Videbla por ĉiuj",
"privacy.public.short": "Publika",
"privacy.unlisted.long": "Videbla por ĉiuj, sed ekskluzive el la funkcio de esploro",
"privacy.unlisted.short": "Nelistigita",
"privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
"privacy_policy.title": "Politiko de privateco",
"recommended": "Rekomendita",
@ -715,10 +696,8 @@
"upload_error.poll": "Alŝuto de dosiero ne permesita kun balotenketo.",
"upload_form.audio_description": "Priskribi por homoj kiuj malfacile aŭdi",
"upload_form.description": "Priskribi por personoj, kiuj estas blindaj aŭ havas vidmalsufiĉon",
"upload_form.description_missing": "Neniu priskribo aldonita",
"upload_form.edit": "Redakti",
"upload_form.thumbnail": "Ŝanĝi etigita bildo",
"upload_form.undo": "Forigi",
"upload_form.video_description": "Priskribi por homoj kiuj malfacile aŭdi aŭ vidi",
"upload_modal.analyzing_picture": "Bilda analizado…",
"upload_modal.apply": "Apliki",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "[sin procesar]",
"audio.hide": "Ocultar audio",
"autosuggest_hashtag.per_week": "{count} por semana",
"boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez",
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
"bundle_column_error.error.body": "La página solicitada no pudo ser cargada. Podría deberse a un error de programación en nuestro código o a un problema de compatibilidad con el navegador web.",
@ -146,22 +145,21 @@
"compose_form.lock_disclaimer": "Tu cuenta no es {locked}. Todos pueden seguirte para ver tus mensajes marcados como \"Sólo para seguidores\".",
"compose_form.lock_disclaimer.lock": "privada",
"compose_form.placeholder": "¿Qué onda?",
"compose_form.poll.add_option": "Agregá una opción",
"compose_form.poll.duration": "Duración de la encuesta",
"compose_form.poll.multiple": "Selección múltiple",
"compose_form.poll.option_placeholder": "Opción {number}",
"compose_form.poll.remove_option": "Quitar esta opción",
"compose_form.poll.single": "Elige uno",
"compose_form.poll.switch_to_multiple": "Cambiar encuesta para permitir opciones múltiples",
"compose_form.poll.switch_to_single": "Cambiar encuesta para permitir una sola opción",
"compose_form.poll.type": "Estilo",
"compose_form.publish": "Publicar",
"compose_form.publish_form": "Nuevo mensaje",
"compose_form.publish_loud": "¡{publish}!",
"compose_form.save_changes": "Guardar cambios",
"compose_form.sensitive.hide": "Marcar medio como sensible",
"compose_form.sensitive.marked": "{count, plural, one {El medio está marcado como sensible} other {Los medios están marcados como sensibles}}",
"compose_form.sensitive.unmarked": "El medio no está marcado como sensible",
"compose_form.reply": "Responder",
"compose_form.save_changes": "Actualizar",
"compose_form.spoiler.marked": "Quitar advertencia de contenido",
"compose_form.spoiler.unmarked": "Agregar advertencia de contenido",
"compose_form.spoiler_placeholder": "Escribí tu advertencia acá",
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.block_and_report": "Bloquear y denunciar",
"confirmations.block.confirm": "Bloquear",
@ -408,7 +406,6 @@
"navigation_bar.direct": "Menciones privadas",
"navigation_bar.discover": "Descubrir",
"navigation_bar.domain_blocks": "Dominios bloqueados",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.explore": "Explorá",
"navigation_bar.favourites": "Favoritos",
"navigation_bar.filters": "Palabras silenciadas",
@ -526,14 +523,10 @@
"poll_button.add_poll": "Agregar encuesta",
"poll_button.remove_poll": "Quitar encuesta",
"privacy.change": "Configurar privacidad del mensaje",
"privacy.direct.long": "Visible sólo para los usuarios mencionados",
"privacy.direct.short": "Sólo cuentas mencionadas",
"privacy.private.long": "Visible sólo para los seguidores",
"privacy.private.short": "Sólo para seguidores",
"privacy.public.long": "Visible para todos",
"privacy.direct.short": "Personas específicas",
"privacy.private.long": "Solo tus seguidores",
"privacy.private.short": "Seguidores",
"privacy.public.short": "Público",
"privacy.unlisted.long": "Visible para todos, pero excluido de las características de descubrimiento",
"privacy.unlisted.short": "No listado",
"privacy_policy.last_updated": "Última actualización: {date}",
"privacy_policy.title": "Política de privacidad",
"recommended": "Opción recomendada",
@ -715,10 +708,8 @@
"upload_error.poll": "No se permite la subida de archivos en encuestas.",
"upload_form.audio_description": "Agregá una descripción para personas con dificultades auditivas",
"upload_form.description": "Agregá una descripción para personas con dificultades visuales",
"upload_form.description_missing": "No se agregó descripción",
"upload_form.edit": "Editar",
"upload_form.thumbnail": "Cambiar miniatura",
"upload_form.undo": "Eliminar",
"upload_form.video_description": "Agregá una descripción para personas con dificultades auditivas o visuales",
"upload_modal.analyzing_picture": "Analizando imagen…",
"upload_modal.apply": "Aplicar",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Ocultar audio",
"autosuggest_hashtag.per_week": "{count} por semana",
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
"bundle_column_error.error.body": "La página solicitada no pudo ser renderizada. Podría deberse a un error en nuestro código o a un problema de compatibilidad con el navegador.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.",
"compose_form.lock_disclaimer.lock": "bloqueado",
"compose_form.placeholder": "¿En qué estás pensando?",
"compose_form.poll.add_option": "Añadir una opción",
"compose_form.poll.add_option": "Agregar opción",
"compose_form.poll.duration": "Duración de la encuesta",
"compose_form.poll.option_placeholder": "Elección {number}",
"compose_form.poll.multiple": "Selección múltiple",
"compose_form.poll.option_placeholder": "Opción {number}",
"compose_form.poll.remove_option": "Eliminar esta opción",
"compose_form.poll.single": "Seleccione uno",
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
"compose_form.publish": "Publicar",
"compose_form.poll.type": "Estilo",
"compose_form.publish": "Publicación",
"compose_form.publish_form": "Publicar",
"compose_form.publish_loud": "¡{publish}!",
"compose_form.save_changes": "Guardar cambios",
"compose_form.sensitive.hide": "Marcar multimedia como sensible",
"compose_form.sensitive.marked": "Material marcado como sensible",
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
"compose_form.reply": "Respuesta",
"compose_form.save_changes": "Actualización",
"compose_form.spoiler.marked": "Texto oculto tras la advertencia",
"compose_form.spoiler.unmarked": "Texto no oculto",
"compose_form.spoiler_placeholder": "Advertencia de contenido",
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.block_and_report": "Bloquear y Denunciar",
"confirmations.block.confirm": "Bloquear",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Menciones privadas",
"navigation_bar.discover": "Descubrir",
"navigation_bar.domain_blocks": "Dominios ocultos",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.explore": "Explorar",
"navigation_bar.favourites": "Favoritos",
"navigation_bar.filters": "Palabras silenciadas",
@ -526,14 +524,15 @@
"poll_button.add_poll": "Añadir una encuesta",
"poll_button.remove_poll": "Eliminar encuesta",
"privacy.change": "Ajustar privacidad",
"privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
"privacy.direct.short": "Solo personas mencionadas",
"privacy.private.long": "Sólo mostrar a seguidores",
"privacy.private.short": "Solo seguidores",
"privacy.public.long": "Visible para todos",
"privacy.direct.long": "Todos los mencionados en la publicación",
"privacy.direct.short": "Personas específicas",
"privacy.private.long": "Sólo tus seguidores",
"privacy.private.short": "Seguidores",
"privacy.public.long": "Cualquiera dentro y fuera de Mastodon",
"privacy.public.short": "Público",
"privacy.unlisted.long": "Visible para todos, pero excluido de las funciones de descubrimiento",
"privacy.unlisted.short": "No listado",
"privacy.unlisted.additional": "Esto se comporta exactamente igual que el público, excepto que el post no aparecerá en las cronologías en directo o en los hashtags, la exploración o busquedas en Mastodon, incluso si está optado por activar la cuenta de usuario.",
"privacy.unlisted.long": "Menos fanfares algorítmicos",
"privacy.unlisted.short": "Público silencioso",
"privacy_policy.last_updated": "Actualizado por última vez {date}",
"privacy_policy.title": "Política de Privacidad",
"recommended": "Recomendado",
@ -551,7 +550,9 @@
"relative_time.minutes": "{number} m",
"relative_time.seconds": "{number} s",
"relative_time.today": "hoy",
"reply_indicator.attachments": "{count, plural, one {# adjunto} other {# adjuntos}}",
"reply_indicator.cancel": "Cancelar",
"reply_indicator.poll": "Encuesta",
"report.block": "Bloquear",
"report.block_explanation": "No veras sus publicaciones. No podrán ver tus publicaciones ni seguirte. Podrán saber que están bloqueados.",
"report.categories.legal": "Legal",
@ -715,10 +716,8 @@
"upload_error.poll": "Subida de archivos no permitida con encuestas.",
"upload_form.audio_description": "Describir para personas con problemas auditivos",
"upload_form.description": "Describir para los usuarios con dificultad visual",
"upload_form.description_missing": "Sin descripción añadida",
"upload_form.edit": "Editar",
"upload_form.thumbnail": "Cambiar miniatura",
"upload_form.undo": "Borrar",
"upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
"upload_modal.analyzing_picture": "Analizando imagen…",
"upload_modal.apply": "Aplicar",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Ocultar audio",
"autosuggest_hashtag.per_week": "{count} por semana",
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
"bundle_column_error.copy_stacktrace": "Copiar informe de error",
"bundle_column_error.error.body": "La página solicitada no pudo ser renderizada. Podría deberse a un error en nuestro código o a un problema de compatibilidad con el navegador.",
@ -146,22 +145,21 @@
"compose_form.lock_disclaimer": "Tu cuenta no está {locked}. Todos pueden seguirte para ver tus publicaciones solo para seguidores.",
"compose_form.lock_disclaimer.lock": "bloqueado",
"compose_form.placeholder": "¿En qué estás pensando?",
"compose_form.poll.add_option": "Añadir una opción",
"compose_form.poll.duration": "Duración de la encuesta",
"compose_form.poll.option_placeholder": "Elección {number}",
"compose_form.poll.remove_option": "Eliminar esta opción",
"compose_form.poll.multiple": "Selección múltiple",
"compose_form.poll.option_placeholder": "Opción {number}",
"compose_form.poll.remove_option": "Quitar esta opción",
"compose_form.poll.single": "Elige uno",
"compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
"compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
"compose_form.poll.type": "Estilo",
"compose_form.publish": "Publicar",
"compose_form.publish_form": "Publicar",
"compose_form.publish_loud": "¡{publish}!",
"compose_form.save_changes": "Guardar cambios",
"compose_form.sensitive.hide": "{count, plural, one {Marcar material como sensible} other {Marcar material como sensible}}",
"compose_form.sensitive.marked": "{count, plural, one {Material marcado como sensible} other {Material marcado como sensible}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Material no marcado como sensible} other {Material no marcado como sensible}}",
"compose_form.reply": "Responder",
"compose_form.save_changes": "Actualizar",
"compose_form.spoiler.marked": "Quitar advertencia de contenido",
"compose_form.spoiler.unmarked": "Añadir advertencia de contenido",
"compose_form.spoiler_placeholder": "Escribe aquí tu advertencia",
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",
"confirmation_modal.cancel": "Cancelar",
"confirmations.block.block_and_report": "Bloquear y Reportar",
"confirmations.block.confirm": "Bloquear",
@ -408,7 +406,6 @@
"navigation_bar.direct": "Menciones privadas",
"navigation_bar.discover": "Descubrir",
"navigation_bar.domain_blocks": "Dominios ocultos",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.explore": "Explorar",
"navigation_bar.favourites": "Favoritos",
"navigation_bar.filters": "Palabras silenciadas",
@ -526,14 +523,10 @@
"poll_button.add_poll": "Añadir una encuesta",
"poll_button.remove_poll": "Eliminar encuesta",
"privacy.change": "Ajustar privacidad",
"privacy.direct.long": "Visible solo para usuarios mencionados",
"privacy.direct.short": "Sólo cuentas mencionadas",
"privacy.private.long": "Sólo mostrar a seguidores",
"privacy.private.short": "Solo seguidores",
"privacy.public.long": "Visible para todos",
"privacy.direct.short": "Personas específicas",
"privacy.private.long": "Solo tus seguidores",
"privacy.private.short": "Seguidores",
"privacy.public.short": "Público",
"privacy.unlisted.long": "Visible para todos, pero excluido de las funciones de descubrimiento",
"privacy.unlisted.short": "No listado",
"privacy_policy.last_updated": "Actualizado por última vez {date}",
"privacy_policy.title": "Política de Privacidad",
"recommended": "Recomendado",
@ -715,10 +708,8 @@
"upload_error.poll": "No se permite la subida de archivos con encuestas.",
"upload_form.audio_description": "Describir para personas con problemas auditivos",
"upload_form.description": "Describir para personas con discapacidad visual",
"upload_form.description_missing": "No se ha añadido ninguna descripción",
"upload_form.edit": "Editar",
"upload_form.thumbnail": "Cambiar miniatura",
"upload_form.undo": "Eliminar",
"upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
"upload_modal.analyzing_picture": "Analizando imagen…",
"upload_modal.apply": "Aplicar",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Teadaanne",
"attachments_list.unprocessed": "(töötlemata)",
"audio.hide": "Peida audio",
"autosuggest_hashtag.per_week": "{count} nädalas",
"boost_modal.combo": "Vajutades {combo}, saab selle edaspidi vahele jätta",
"bundle_column_error.copy_stacktrace": "Kopeeri veateade",
"bundle_column_error.error.body": "Soovitud lehte ei õnnestunud esitada. See võib olla meie koodiviga või probleem brauseri ühilduvusega.",
@ -148,20 +147,20 @@
"compose_form.placeholder": "Millest mõtled?",
"compose_form.poll.add_option": "Lisa valik",
"compose_form.poll.duration": "Küsitluse kestus",
"compose_form.poll.multiple": "Valikvastustega",
"compose_form.poll.option_placeholder": "Valik {number}",
"compose_form.poll.remove_option": "Eemalda see valik",
"compose_form.poll.single": "Vali üks",
"compose_form.poll.switch_to_multiple": "Muuda küsitlust mitmikvaliku lubamiseks",
"compose_form.poll.switch_to_single": "Muuda küsitlust ainult ühe valiku lubamiseks",
"compose_form.poll.type": "Stiil",
"compose_form.publish": "Postita",
"compose_form.publish_form": "Postita",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Salvesta muudatused",
"compose_form.sensitive.hide": "{count, plural, one {Märgi meedia tundlikuks} other {Märgi meediad tundlikuks}}",
"compose_form.sensitive.marked": "{count, plural, one {Meedia on märgitud tundlikuks} other {Meediad on märgitud tundlikuks}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Meedia ei ole tundlikuks märgitud} other {Meediad ei ole märgitud tundlikuks}}",
"compose_form.reply": "Vasta",
"compose_form.save_changes": "Uuenda",
"compose_form.spoiler.marked": "Tekst on hoiatuse taha peidetud",
"compose_form.spoiler.unmarked": "Märgi sisu tundlikuks",
"compose_form.spoiler_placeholder": "Kirjuta hoiatus siia",
"compose_form.spoiler_placeholder": "Sisuhoiatus (valikuline)",
"confirmation_modal.cancel": "Katkesta",
"confirmations.block.block_and_report": "Blokeeri ja teata",
"confirmations.block.confirm": "Blokeeri",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Privaatsed mainimised",
"navigation_bar.discover": "Avasta",
"navigation_bar.domain_blocks": "Peidetud domeenid",
"navigation_bar.edit_profile": "Muuda profiili",
"navigation_bar.explore": "Avasta",
"navigation_bar.favourites": "Lemmikud",
"navigation_bar.filters": "Vaigistatud sõnad",
@ -526,14 +524,14 @@
"poll_button.add_poll": "Lisa küsitlus",
"poll_button.remove_poll": "Eemalda küsitlus",
"privacy.change": "Muuda postituse nähtavust",
"privacy.direct.long": "Postita ainult mainitud kasutajatele",
"privacy.direct.short": "Mainitud inimesed ainult",
"privacy.private.long": "Postita ainult jälgijatele",
"privacy.private.short": "Jälgijad ainult",
"privacy.public.long": "Kõigile nähtav",
"privacy.direct.long": "Kõik postituses mainitud",
"privacy.direct.short": "Määratud kasutajad",
"privacy.private.long": "Ainult jälgijad",
"privacy.private.short": "Jälgijad",
"privacy.public.long": "Nii kasutajad kui mittekasutajad",
"privacy.public.short": "Avalik",
"privacy.unlisted.long": "Kõigile nähtav, aga ei ilmu avastamise vaadetes",
"privacy.unlisted.short": "Määramata",
"privacy.unlisted.long": "Vähem algoritmilisi teavitusi",
"privacy.unlisted.short": "Vaikselt avalik",
"privacy_policy.last_updated": "Viimati uuendatud {date}",
"privacy_policy.title": "Isikuandmete kaitse",
"recommended": "Soovitatud",
@ -551,7 +549,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "täna",
"reply_indicator.attachments": "{count, plural, one {# lisa} other {# lisa}}",
"reply_indicator.cancel": "Tühista",
"reply_indicator.poll": "Küsitlus",
"report.block": "Blokeeri",
"report.block_explanation": "Sa ei näe tema postitusi. Tema ei saa näha sinu postitusi ega sind jälgida. Talle on näha, et ta on blokeeritud.",
"report.categories.legal": "Juriidiline",
@ -715,10 +715,8 @@
"upload_error.poll": "Küsitlustes pole faili üleslaadimine lubatud.",
"upload_form.audio_description": "Kirjelda kuulmispuudega inimeste jaoks",
"upload_form.description": "Kirjelda vaegnägijatele",
"upload_form.description_missing": "Kirjeldus puudub",
"upload_form.edit": "Muuda",
"upload_form.thumbnail": "Muuda pisipilti",
"upload_form.undo": "Kustuta",
"upload_form.video_description": "Kirjelda kuulmis- või nägemispuudega inimeste jaoks",
"upload_modal.analyzing_picture": "Analüüsime pilti…",
"upload_modal.apply": "Rakenda",

View file

@ -25,7 +25,7 @@
"account.direct": "Aipatu pribatuki @{name}",
"account.disable_notifications": "Utzi jakinarazteari @{name} erabiltzaileak argitaratzean",
"account.domain_blocked": "Ezkutatutako domeinua",
"account.edit_profile": "Aldatu profila",
"account.edit_profile": "Editatu profila",
"account.enable_notifications": "Jakinarazi @{name} erabiltzaileak argitaratzean",
"account.endorse": "Nabarmendu profilean",
"account.featured_tags.last_status_at": "Azken bidalketa {date} datan",
@ -89,7 +89,6 @@
"announcement.announcement": "Iragarpena",
"attachments_list.unprocessed": "(prozesatu gabe)",
"audio.hide": "Ezkutatu audioa",
"autosuggest_hashtag.per_week": "{count} asteko",
"boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko",
"bundle_column_error.copy_stacktrace": "Kopiatu errore-txostena",
"bundle_column_error.error.body": "Eskatutako orria ezin izan da bistaratu. Kodeko errore bategatik izan daiteke edo nabigatzailearen bateragarritasun arazo bategatik.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "Zure kontua ez dago {locked}. Edonork jarraitu zaitzake zure jarraitzaileentzako soilik diren bidalketak ikusteko.",
"compose_form.lock_disclaimer.lock": "giltzapetuta",
"compose_form.placeholder": "Zer duzu buruan?",
"compose_form.poll.add_option": "Gehitu aukera bat",
"compose_form.poll.add_option": "Gehitu aukera",
"compose_form.poll.duration": "Inkestaren iraupena",
"compose_form.poll.multiple": "Aukera aniza",
"compose_form.poll.option_placeholder": "{number}. aukera",
"compose_form.poll.remove_option": "Kendu aukera hau",
"compose_form.poll.single": "Hautatu bat",
"compose_form.poll.switch_to_multiple": "Aldatu inkesta hainbat aukera onartzeko",
"compose_form.poll.switch_to_single": "Aldatu inkesta aukera bakarra onartzeko",
"compose_form.poll.type": "Estiloa",
"compose_form.publish": "Argitaratu",
"compose_form.publish_form": "Argitaratu",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Gorde aldaketak",
"compose_form.sensitive.hide": "Markatu multimedia hunkigarri gisa",
"compose_form.sensitive.marked": "Multimedia edukia hunkigarri gisa markatu da",
"compose_form.sensitive.unmarked": "Multimedia edukia ez da hunkigarri gisa markatu",
"compose_form.reply": "Erantzun",
"compose_form.save_changes": "Eguneratu",
"compose_form.spoiler.marked": "Testua abisu batek ezkutatzen du",
"compose_form.spoiler.unmarked": "Testua ez dago ezkutatuta",
"compose_form.spoiler_placeholder": "Idatzi zure abisua hemen",
"compose_form.spoiler_placeholder": "Edukiaren abisua (aukerakoa)",
"confirmation_modal.cancel": "Utzi",
"confirmations.block.block_and_report": "Blokeatu eta salatu",
"confirmations.block.confirm": "Blokeatu",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Aipamen pribatuak",
"navigation_bar.discover": "Aurkitu",
"navigation_bar.domain_blocks": "Ezkutatutako domeinuak",
"navigation_bar.edit_profile": "Aldatu profila",
"navigation_bar.explore": "Arakatu",
"navigation_bar.favourites": "Gogokoak",
"navigation_bar.filters": "Mutututako hitzak",
@ -526,14 +524,15 @@
"poll_button.add_poll": "Gehitu inkesta bat",
"poll_button.remove_poll": "Kendu inkesta",
"privacy.change": "Aldatu bidalketaren pribatutasuna",
"privacy.direct.long": "Bidali aipatutako erabiltzaileei besterik ez",
"privacy.direct.short": "Aipatutako jendea soilik",
"privacy.private.long": "Bidali jarraitzaileei besterik ez",
"privacy.private.short": "Jarraitzaileak soilik",
"privacy.public.long": "Guztientzat ikusgai",
"privacy.direct.long": "Argitalpen honetan aipatutako denak",
"privacy.direct.short": "Jende jakina",
"privacy.private.long": "Soilik jarraitzaileak",
"privacy.private.short": "Jarraitzaileak",
"privacy.public.long": "Mastodonen dagoen edo ez dagoen edonor",
"privacy.public.short": "Publikoa",
"privacy.unlisted.long": "Guztientzat ikusgai, baina ez aurkitzeko ezaugarrietan",
"privacy.unlisted.short": "Zerrendatu gabea",
"privacy.unlisted.additional": "Aukera honek publiko modua bezala funtzionatzen du, baina argitalpena ez da agertuko zuzeneko jarioetan edo traoletan, \"Arakatu\" atalean edo Mastodonen bilaketan, nahiz eta kontua zabaltzeko onartu duzun.",
"privacy.unlisted.long": "Tontakeria algoritmiko gutxiago",
"privacy.unlisted.short": "Deiadar urrikoa",
"privacy_policy.last_updated": "Azkenengo eguneraketa {date}",
"privacy_policy.title": "Pribatutasun politika",
"recommended": "Gomendatua",
@ -551,7 +550,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "gaur",
"reply_indicator.attachments": "{count, plural, one {# eranskin} other {# eranskin}}",
"reply_indicator.cancel": "Utzi",
"reply_indicator.poll": "Inkesta",
"report.block": "Blokeatu",
"report.block_explanation": "Ez dituzu bere bidalketak ikusiko. Ezingo dituzte zure bidalketak ikusi eta ez jarraitu. Blokeatu dituzula jakin dezakete.",
"report.categories.legal": "Juridikoa",
@ -715,10 +716,8 @@
"upload_error.poll": "Ez da inkestetan fitxategiak igotzea onartzen.",
"upload_form.audio_description": "Deskribatu entzumen galera duten pertsonentzat",
"upload_form.description": "Deskribatu ikusmen arazoak dituztenentzat",
"upload_form.description_missing": "Ez da deskribapenik gehitu",
"upload_form.edit": "Editatu",
"upload_form.thumbnail": "Aldatu koadro txikia",
"upload_form.undo": "Ezabatu",
"upload_form.video_description": "Deskribatu entzumen galera edo ikusmen urritasuna duten pertsonentzat",
"upload_modal.analyzing_picture": "Irudia aztertzen…",
"upload_modal.apply": "Aplikatu",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "اعلامیه",
"attachments_list.unprocessed": "(پردازش نشده)",
"audio.hide": "نهفتن صدا",
"autosuggest_hashtag.per_week": "{count} در هفته",
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
"bundle_column_error.copy_stacktrace": "رونوشت از گزارش خطا",
"bundle_column_error.error.body": "صفحهٔ درخواستی نتوانست پرداخت شود. ممکن است به خاطر اشکالی در کدمان یا مشکل سازگاری مرورگر باشد.",
@ -148,20 +147,20 @@
"compose_form.placeholder": "تازه چه خبر؟",
"compose_form.poll.add_option": "افزودن گزینه",
"compose_form.poll.duration": "مدت نظرسنجی",
"compose_form.poll.multiple": "چند گزینه‌ای",
"compose_form.poll.option_placeholder": "گزینهٔ {number}",
"compose_form.poll.remove_option": "برداشتن این گزینه",
"compose_form.poll.single": "گزینش یکی",
"compose_form.poll.switch_to_multiple": "تغییر نظرسنجی برای اجازه به چندین گزینه",
"compose_form.poll.switch_to_single": "تبدیل به نظرسنجی تک‌گزینه‌ای",
"compose_form.publish": "انتشار",
"compose_form.poll.type": "سبک",
"compose_form.publish": "فرستادن",
"compose_form.publish_form": "انتشار",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "ذخیرهٔ تغییرات",
"compose_form.sensitive.hide": "{count, plural, one {علامت‌گذاری رسانه به عنوان حساس} other {علامت‌گذاری رسانه‌ها به عنوان حساس}}",
"compose_form.sensitive.marked": "{count, plural, one {رسانه به عنوان حساس علامت‌گذاری شد} other {رسانه‌ها به عنوان حساس علامت‌گذاری شدند}}",
"compose_form.sensitive.unmarked": "{count, plural, one {رسانه به عنوان حساس علامت‌گذاری نشد} other {رسانه‌ها به عنوان حساس علامت‌گذاری نشدند}}",
"compose_form.reply": "پاسخ",
"compose_form.save_changes": "به‌روز رسانی",
"compose_form.spoiler.marked": "برداشتن هشدار محتوا",
"compose_form.spoiler.unmarked": "افزودن هشدار محتوا",
"compose_form.spoiler_placeholder": "هشدارتان را این‌جا بنویسید",
"compose_form.spoiler_placeholder": "هشدار محتوا (اختیاری)",
"confirmation_modal.cancel": "لغو",
"confirmations.block.block_and_report": "انسداد و گزارش",
"confirmations.block.confirm": "انسداد",
@ -408,7 +407,6 @@
"navigation_bar.direct": "اشاره‌های خصوصی",
"navigation_bar.discover": "گشت و گذار",
"navigation_bar.domain_blocks": "دامنه‌های مسدود شده",
"navigation_bar.edit_profile": "ویرایش نمایه",
"navigation_bar.explore": "کاوش",
"navigation_bar.favourites": "برگزیده‌ها",
"navigation_bar.filters": "واژه‌های خموش",
@ -524,14 +522,14 @@
"poll_button.add_poll": "افزودن نظرسنجی",
"poll_button.remove_poll": "برداشتن نظرسنجی",
"privacy.change": "تغییر محرمانگی فرسته",
"privacy.direct.long": "نمایان فقط برای کاربران اشاره شده",
"privacy.direct.short": "فقط افراد اشاره شده",
"privacy.private.long": "نمایان فقط برای پی‌گیرندگان",
"privacy.private.short": "فقط پی‌گیرندگان",
"privacy.public.long": "نمایان برای همه",
"privacy.direct.long": "هرکسی که در فرسته نام برده شده",
"privacy.direct.short": "افراد مشخّص",
"privacy.private.long": "تنها پی‌گیرندگانتان",
"privacy.private.short": "پی‌گیرندگان",
"privacy.public.long": "هرکسی در و بیرون از ماستودون",
"privacy.public.short": "عمومی",
"privacy.unlisted.long": "نمایان برای همه، ولی خارج از قابلیت‌های کشف",
"privacy.unlisted.short": "فهرست نشده",
"privacy.unlisted.long": "سروصدای الگوریتمی کم‌تر",
"privacy.unlisted.short": "عمومی ساکت",
"privacy_policy.last_updated": "آخرین به‌روز رسانی در {date}",
"privacy_policy.title": "سیاست محرمانگی",
"recommended": "پیشنهادشده",
@ -550,6 +548,7 @@
"relative_time.seconds": "{number} ثانیه",
"relative_time.today": "امروز",
"reply_indicator.cancel": "لغو",
"reply_indicator.poll": "نظرسنجی",
"report.block": "انسداد",
"report.block_explanation": "شما فرسته‌هایشان را نخواهید دید. آن‌ها نمی‌توانند فرسته‌هایتان را ببینند یا شما را پی‌بگیرند. آنها می‌توانند بگویند که مسدود شده‌اند.",
"report.categories.legal": "حقوقی",
@ -713,10 +712,8 @@
"upload_error.poll": "بارگذاری پرونده در نظرسنجی‌ها مجاز نیست.",
"upload_form.audio_description": "برای ناشنوایان توصیفش کنید",
"upload_form.description": "برای کم‌بینایان توصیفش کنید",
"upload_form.description_missing": "شرحی افزوده نشده",
"upload_form.edit": "ویرایش",
"upload_form.thumbnail": "تغییر بندانگشتی",
"upload_form.undo": "حذف",
"upload_form.video_description": "برای کم‌بینایان یا ناشنوایان توصیفش کنید",
"upload_modal.analyzing_picture": "در حال پردازش تصویر…",
"upload_modal.apply": "اعمال",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Ilmoitus",
"attachments_list.unprocessed": "(käsittelemätön)",
"audio.hide": "Piilota ääni",
"autosuggest_hashtag.per_week": "{count} viikossa",
"boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
"bundle_column_error.copy_stacktrace": "Kopioi virheraportti",
"bundle_column_error.error.body": "Pyydettyä sivua ei voitu hahmontaa. Se voi johtua virheestä koodissamme tai selaimen yhteensopivuudessa.",
@ -146,22 +145,22 @@
"compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.",
"compose_form.lock_disclaimer.lock": "lukittu",
"compose_form.placeholder": "Mitä mietit?",
"compose_form.poll.add_option": "Lisää valinta",
"compose_form.poll.add_option": "Lisää vaihtoehto",
"compose_form.poll.duration": "Äänestyksen kesto",
"compose_form.poll.option_placeholder": "Valinta {number}",
"compose_form.poll.remove_option": "Poista tämä valinta",
"compose_form.poll.multiple": "Monivalinta",
"compose_form.poll.option_placeholder": "Vaihtoehto {number}",
"compose_form.poll.remove_option": "Poista tämä vaihtoehto",
"compose_form.poll.single": "Valitse yksi",
"compose_form.poll.switch_to_multiple": "Muuta äänestys monivalinnaksi",
"compose_form.poll.switch_to_single": "Muuta äänestys sallimaan vain yksi valinta",
"compose_form.poll.type": "Tyyli",
"compose_form.publish": "Julkaise",
"compose_form.publish_form": "Uusi julkaisu",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Tallenna muutokset",
"compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluonteiseksi} other {Merkitse mediat arkaluonteisiksi}}",
"compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluonteiseksi} other {Mediat on merkitty arkaluonteisiksi}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluonteiseksi} other {Medioita ei ole merkitty arkaluonteisiksi}}",
"compose_form.reply": "Vastaa",
"compose_form.save_changes": "Päivitä",
"compose_form.spoiler.marked": "Poista sisältövaroitus",
"compose_form.spoiler.unmarked": "Lisää sisältövaroitus",
"compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän",
"compose_form.spoiler_placeholder": "Sisältövaroitus (valinnainen)",
"confirmation_modal.cancel": "Peruuta",
"confirmations.block.block_and_report": "Estä ja raportoi",
"confirmations.block.confirm": "Estä",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Yksityiset maininnat",
"navigation_bar.discover": "Löydä uutta",
"navigation_bar.domain_blocks": "Estetyt verkkotunnukset",
"navigation_bar.edit_profile": "Muokkaa profiilia",
"navigation_bar.explore": "Selaa",
"navigation_bar.favourites": "Suosikit",
"navigation_bar.filters": "Mykistetyt sanat",
@ -526,14 +524,13 @@
"poll_button.add_poll": "Lisää äänestys",
"poll_button.remove_poll": "Poista äänestys",
"privacy.change": "Muuta julkaisun näkyvyyttä",
"privacy.direct.long": "Näkyy vain mainituille käyttäjille",
"privacy.direct.short": "Vain mainitut käyttäjät",
"privacy.private.long": "Näkyy vain seuraajille",
"privacy.private.short": "Vain seuraajat",
"privacy.public.long": "Näkyy kaikille",
"privacy.direct.long": "Kaikki tässä julkaisussa mainitut",
"privacy.direct.short": "Tietyt henkilöt",
"privacy.private.long": "Vain seuraajasi",
"privacy.private.short": "Seuraajat",
"privacy.public.long": "Kuka tahansa Mastodonissa ja sen ulkopuolella",
"privacy.public.short": "Julkinen",
"privacy.unlisted.long": "Näkyy kaikille mutta jää pois löytämisominaisuuksista",
"privacy.unlisted.short": "Listaamaton",
"privacy.unlisted.additional": "Tämä toimii kuten julkinen, paitsi että julkaisu ei näy livesyötteissä, aihetunnisteissa, selaa-näkymässä tai Mastodon-haussa, vaikka olisit sallinut ne käyttäjätilin laajuisesti.",
"privacy_policy.last_updated": "Viimeksi päivitetty {date}",
"privacy_policy.title": "Tietosuojakäytäntö",
"recommended": "Suositeltu",
@ -551,7 +548,9 @@
"relative_time.minutes": "{number} min",
"relative_time.seconds": "{number} s",
"relative_time.today": "tänään",
"reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}",
"reply_indicator.cancel": "Peruuta",
"reply_indicator.poll": "Kysely",
"report.block": "Estä",
"report.block_explanation": "Et näe hänen viestejään, eikä hän voi nähdä viestejäsi tai seurata sinua. Hän näkee, että olet estänyt hänet.",
"report.categories.legal": "Lakiasiat",
@ -715,10 +714,8 @@
"upload_error.poll": "Tiedoston lataaminen ei ole sallittua äänestyksissä.",
"upload_form.audio_description": "Kuvaile sisältöä kuuroille ja kuulorajoitteisille",
"upload_form.description": "Kuvaile sisältöä sokeille ja näkörajoitteisille",
"upload_form.description_missing": "Kuvausta ei ole lisätty",
"upload_form.edit": "Muokkaa",
"upload_form.thumbnail": "Vaihda pikkukuva",
"upload_form.undo": "Poista",
"upload_form.video_description": "Kuvaile sisältöä kuuroille, kuulorajoitteisille, sokeille tai näkörajoitteisille",
"upload_modal.analyzing_picture": "Analysoidaan kuvaa…",
"upload_modal.apply": "Käytä",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Kunngerð",
"attachments_list.unprocessed": "(óviðgjørt)",
"audio.hide": "Fjal ljóð",
"autosuggest_hashtag.per_week": "{count} um vikuna",
"boost_modal.combo": "Tú kanst trýsta á {combo} fyri at loypa uppum hetta næstu ferð",
"bundle_column_error.copy_stacktrace": "Avrita feilfráboðan",
"bundle_column_error.error.body": "Umbidna síðan kann ikki vísast. Tað kann vera orsakað av einum feili í koduni hjá okkum ella tað kann vera orsakað av kaganum, sum tú brúkar.",
@ -148,20 +147,20 @@
"compose_form.placeholder": "Hvat hevur tú í huga?",
"compose_form.poll.add_option": "Legg valmøguleika afturat",
"compose_form.poll.duration": "Atkvøðugreiðslutíð",
"compose_form.poll.multiple": "Fleiri valmøguleikar",
"compose_form.poll.option_placeholder": "Valmøguleiki {number}",
"compose_form.poll.remove_option": "Strika valmøguleikan",
"compose_form.poll.remove_option": "Strika hendan valmøguleikan",
"compose_form.poll.single": "Vel ein",
"compose_form.poll.switch_to_multiple": "Broyt atkvøðugreiðslu til at loyva fleiri svarum",
"compose_form.poll.switch_to_single": "Broyt atkvøðugreiðslu til einstakt svar",
"compose_form.publish": "Legg út",
"compose_form.poll.type": "Stílur",
"compose_form.publish": "Posta",
"compose_form.publish_form": "Legg út",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Goym broytingar",
"compose_form.sensitive.hide": "{count, plural, one {Frámerk tilfar sum viðkvæmt} other {Frámerk tilfar sum viðkvæmt}}",
"compose_form.sensitive.marked": "{count, plural, one {Tilfarið er frámerkt sum viðkvæmt} other {Tilfarið er frámerkt sum viðkvæmt}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Tilfarið er ikki merkt sum viðkvæmt} other {Tilfarið er ikki merkt sum viðkvæmt}}",
"compose_form.reply": "Svara",
"compose_form.save_changes": "Dagfør",
"compose_form.spoiler.marked": "Ávaring um at strika innihald",
"compose_form.spoiler.unmarked": "Skriva ávaring um innihald",
"compose_form.spoiler_placeholder": "Skriva tína ávaring her",
"compose_form.spoiler_placeholder": "Innihaldsávaring (valfrí)",
"confirmation_modal.cancel": "Strika",
"confirmations.block.block_and_report": "Banna og melda",
"confirmations.block.confirm": "Banna",
@ -408,7 +407,6 @@
"navigation_bar.direct": "Privatar umrøður",
"navigation_bar.discover": "Uppdaga",
"navigation_bar.domain_blocks": "Bannað økisnøvn",
"navigation_bar.edit_profile": "Broyt vanga",
"navigation_bar.explore": "Rannsaka",
"navigation_bar.favourites": "Dámdir postar",
"navigation_bar.filters": "Doyvd orð",
@ -526,14 +524,15 @@
"poll_button.add_poll": "Legg atkvøðugreiðslu afturat",
"poll_button.remove_poll": "Strika atkvøðugreiðslu",
"privacy.change": "Broyt privatverju av posti",
"privacy.direct.long": "Bert sjónligt hjá nevndum brúkarum",
"privacy.direct.short": "Bert nevnd fólk",
"privacy.private.long": "Bert sjónligt hjá fylgjarum",
"privacy.private.short": "Einans fylgjarar",
"privacy.public.long": "Sjónligt hjá øllum",
"privacy.direct.long": "Øll, sum eru nevnd í postinum",
"privacy.direct.short": "Ávís fólk",
"privacy.private.long": "Einans tey, ið fylgja tær",
"privacy.private.short": "Fylgjarar",
"privacy.public.long": "Øll í og uttanfyri Mastodon",
"privacy.public.short": "Alment",
"privacy.unlisted.long": "Sjónligur fyri øll, men ikki gjøgnum uppdagingarhentleikarnar",
"privacy.unlisted.short": "Ikki listað",
"privacy.unlisted.additional": "Hetta er júst sum almenn, tó verður posturin ikki vístur í samtíðarrásum ella frámerkjum, rannsakan ella Mastodon leitingum, sjálvt um valið er galdandi fyri alla kontuna.",
"privacy.unlisted.long": "Færri algoritmiskar fanfarur",
"privacy.unlisted.short": "Stillur almenningur",
"privacy_policy.last_updated": "Seinast dagført {date}",
"privacy_policy.title": "Privatlívspolitikkur",
"recommended": "Viðmælt",
@ -551,7 +550,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "í dag",
"reply_indicator.attachments": "{count, plural, one {# viðfesti} other {# viðfesti}}",
"reply_indicator.cancel": "Ógilda",
"reply_indicator.poll": "Atkvøðugreiðsla",
"report.block": "Blokera",
"report.block_explanation": "Tú fer ikki at síggja postarnar hjá teimum. Tey kunnu ikki síggja tínar postar ella fylgja tær. Tey síggja, at tey eru blokeraði.",
"report.categories.legal": "Løgfrøðisligt",
@ -715,10 +716,8 @@
"upload_error.poll": "Ikki loyvt at leggja fílur upp í spurnarkanningum.",
"upload_form.audio_description": "Lýs fyri teimum, sum eru deyv ella hava ringa hoyrn",
"upload_form.description": "Lýs fyri teimum, sum eru blind ella eru sjónveik",
"upload_form.description_missing": "Lýsing vantar",
"upload_form.edit": "Rætta",
"upload_form.thumbnail": "Broyt smámynd",
"upload_form.undo": "Strika",
"upload_form.video_description": "Lýs fyri teimum, sum eru deyv, hava ringa hoyrn, eru blind ella eru sjónveik",
"upload_modal.analyzing_picture": "Greini mynd…",
"upload_modal.apply": "Ger virkið",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Annonce",
"attachments_list.unprocessed": "(non traité)",
"audio.hide": "Masquer l'audio",
"autosuggest_hashtag.per_week": "{count} par semaine",
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour sauter ceci la prochaine fois",
"bundle_column_error.copy_stacktrace": "Copier le rapport d'erreur",
"bundle_column_error.error.body": "La page demandée n'a pas pu être affichée. Cela pourrait être dû à un bogue dans notre code, ou à un problème de compatibilité avec le navigateur.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Votre compte nest pas {locked}. Tout le monde peut vous suivre et voir vos publications privés.",
"compose_form.lock_disclaimer.lock": "verrouillé",
"compose_form.placeholder": "À quoi pensez-vous?",
"compose_form.poll.add_option": "Ajouter un choix",
"compose_form.poll.duration": "Durée du sondage",
"compose_form.poll.option_placeholder": "Choix {number}",
"compose_form.poll.remove_option": "Supprimer ce choix",
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
"compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix",
"compose_form.publish": "Publier",
"compose_form.publish_form": "Publier",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Enregistrer les modifications",
"compose_form.sensitive.hide": "{count, plural, one {Marquer média comme sensible} other {Marquer médias comme sensibles}}",
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Le média nest pas marqué comme sensible} other {Les médias ne sont pas marqués comme sensibles}}",
"compose_form.spoiler.marked": "Enlever l'avertissement de contenu",
"compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu",
"compose_form.spoiler_placeholder": "Écrivez votre avertissement ici",
"confirmation_modal.cancel": "Annuler",
"confirmations.block.block_and_report": "Bloquer et signaler",
"confirmations.block.confirm": "Bloquer",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Mention privée",
"navigation_bar.discover": "Découvrir",
"navigation_bar.domain_blocks": "Domaines bloqués",
"navigation_bar.edit_profile": "Modifier le profil",
"navigation_bar.explore": "Explorer",
"navigation_bar.favourites": "Favoris",
"navigation_bar.filters": "Mots masqués",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Ajouter un sondage",
"poll_button.remove_poll": "Supprimer le sondage",
"privacy.change": "Changer la confidentialité des messages",
"privacy.direct.long": "Visible uniquement par les comptes mentionnés",
"privacy.direct.short": "Personnes mentionnées uniquement",
"privacy.private.long": "Visible uniquement pour vos abonné·e·s",
"privacy.private.short": "Abonné·e·s seulement",
"privacy.public.long": "Visible pour tous",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Visible pour tous, mais sans fonctionnalités de découverte",
"privacy.unlisted.short": "Non listé",
"privacy_policy.last_updated": "Dernière mise à jour {date}",
"privacy_policy.title": "Politique de confidentialité",
"recommended": "Recommandé",
@ -715,10 +696,8 @@
"upload_error.poll": "Lenvoi de fichiers nest pas autorisé avec les sondages.",
"upload_form.audio_description": "Décrire pour les personnes ayant des difficultés daudition",
"upload_form.description": "Décrire pour les malvoyants",
"upload_form.description_missing": "Description manquante",
"upload_form.edit": "Modifier",
"upload_form.thumbnail": "Changer la vignette",
"upload_form.undo": "Supprimer",
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
"upload_modal.analyzing_picture": "Analyse de limage en cours…",
"upload_modal.apply": "Appliquer",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Annonce",
"attachments_list.unprocessed": "(non traité)",
"audio.hide": "Masquer l'audio",
"autosuggest_hashtag.per_week": "{count} par semaine",
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci la prochaine fois",
"bundle_column_error.copy_stacktrace": "Copier le rapport d'erreur",
"bundle_column_error.error.body": "La page demandée n'a pas pu être affichée. Cela peut être dû à un bogue dans notre code, ou à un problème de compatibilité avec le navigateur.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Votre compte nest pas {locked}. Tout le monde peut vous suivre pour voir vos messages réservés à vos abonné⋅e⋅s.",
"compose_form.lock_disclaimer.lock": "verrouillé",
"compose_form.placeholder": "Quavez-vous en tête?",
"compose_form.poll.add_option": "Ajouter un choix",
"compose_form.poll.duration": "Durée du sondage",
"compose_form.poll.option_placeholder": "Choix {number}",
"compose_form.poll.remove_option": "Supprimer ce choix",
"compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
"compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
"compose_form.publish": "Publier",
"compose_form.publish_form": "Nouvelle publication",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Enregistrer les modifications",
"compose_form.sensitive.hide": "{count, plural, one {Marquer le média comme sensible} other {Marquer les médias comme sensibles}}",
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Le média nest pas marqué comme sensible} other {Les médias ne sont pas marqués comme sensibles}}",
"compose_form.spoiler.marked": "Enlever lavertissement de contenu",
"compose_form.spoiler.unmarked": "Ajouter un avertissement de contenu",
"compose_form.spoiler_placeholder": "Écrivez votre avertissement ici",
"confirmation_modal.cancel": "Annuler",
"confirmations.block.block_and_report": "Bloquer et signaler",
"confirmations.block.confirm": "Bloquer",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Mention privée",
"navigation_bar.discover": "Découvrir",
"navigation_bar.domain_blocks": "Domaines bloqués",
"navigation_bar.edit_profile": "Modifier le profil",
"navigation_bar.explore": "Explorer",
"navigation_bar.favourites": "Favoris",
"navigation_bar.filters": "Mots masqués",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Ajouter un sondage",
"poll_button.remove_poll": "Supprimer le sondage",
"privacy.change": "Ajuster la confidentialité du message",
"privacy.direct.long": "Visible uniquement par les comptes mentionnés",
"privacy.direct.short": "Personnes mentionnées uniquement",
"privacy.private.long": "Visible uniquement par vos abonnés",
"privacy.private.short": "Abonnés uniquement",
"privacy.public.long": "Visible pour tous",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Visible pour tous, mais sans fonctionnalités de découverte",
"privacy.unlisted.short": "Non listé",
"privacy_policy.last_updated": "Dernière mise à jour {date}",
"privacy_policy.title": "Politique de confidentialité",
"recommended": "Recommandé",
@ -715,10 +696,8 @@
"upload_error.poll": "Lenvoi de fichiers nest pas autorisé avec les sondages.",
"upload_form.audio_description": "Décrire pour les personnes ayant des difficultés daudition",
"upload_form.description": "Décrire pour les malvoyant·e·s",
"upload_form.description_missing": "Description manquante",
"upload_form.edit": "Modifier",
"upload_form.thumbnail": "Changer la vignette",
"upload_form.undo": "Supprimer",
"upload_form.video_description": "Décrire pour les personnes ayant des problèmes de vue ou d'audition",
"upload_modal.analyzing_picture": "Analyse de limage en cours…",
"upload_modal.apply": "Appliquer",

View file

@ -89,7 +89,6 @@
"announcement.announcement": "Oankundiging",
"attachments_list.unprocessed": "(net ferwurke)",
"audio.hide": "Audio ferstopje",
"autosuggest_hashtag.per_week": "{count} yn e wike",
"boost_modal.combo": "Jo kinne op {combo} drukke om dit de folgjende kear oer te slaan",
"bundle_column_error.copy_stacktrace": "Flaterrapport kopiearje",
"bundle_column_error.error.body": "De opfrege side koe net werjûn wurde. It kin wêze troch in flater yn ús koade, of in probleem mei browserkompatibiliteit.",
@ -146,22 +145,12 @@
"compose_form.lock_disclaimer": "Jo account is net {locked}. Elkenien kin jo folgje en kin de berjochten sjen dyt jo allinnich oan jo folgers rjochte hawwe.",
"compose_form.lock_disclaimer.lock": "beskoattele",
"compose_form.placeholder": "Wat wolle jo kwyt?",
"compose_form.poll.add_option": "Kar tafoegje",
"compose_form.poll.duration": "Doer fan de enkête",
"compose_form.poll.option_placeholder": "Kar {number}",
"compose_form.poll.remove_option": "Dizze kar fuortsmite",
"compose_form.poll.switch_to_multiple": "Enkête wizigje om meardere karren ta te stean",
"compose_form.poll.switch_to_single": "Enkête wizigje om in inkelde kar ta te stean",
"compose_form.publish": "Publisearje",
"compose_form.publish_form": "Publisearje",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Wizigingen bewarje",
"compose_form.sensitive.hide": "{count, plural, one {Media as gefoelich markearje} other {Media as gefoelich markearje}}",
"compose_form.sensitive.marked": "{count, plural, one {Media as gefoelich markearre} other {Media as gefoelich markearre}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Media is net as gefoelich markearre} other {Media is net as gefoelich markearre}}",
"compose_form.spoiler.marked": "Ynhâldswarskôging fuortsmite",
"compose_form.spoiler.unmarked": "Ynhâldswarskôging tafoegje",
"compose_form.spoiler_placeholder": "Warskôgingstekst",
"confirmation_modal.cancel": "Annulearje",
"confirmations.block.block_and_report": "Blokkearje en rapportearje",
"confirmations.block.confirm": "Blokkearje",
@ -408,7 +397,6 @@
"navigation_bar.direct": "Priveefermeldingen",
"navigation_bar.discover": "Untdekke",
"navigation_bar.domain_blocks": "Blokkearre domeinen",
"navigation_bar.edit_profile": "Profyl bewurkje",
"navigation_bar.explore": "Ferkenne",
"navigation_bar.favourites": "Favoriten",
"navigation_bar.filters": "Negearre wurden",
@ -526,14 +514,7 @@
"poll_button.add_poll": "Poll tafoegje",
"poll_button.remove_poll": "Enkête fuortsmite",
"privacy.change": "Sichtberheid fan berjocht oanpasse",
"privacy.direct.long": "Allinnich sichtber foar fermelde brûkers",
"privacy.direct.short": "Allinnich fermelde minsken",
"privacy.private.long": "Allinnich sichtber foar folgers",
"privacy.private.short": "Allinnich folgers",
"privacy.public.long": "Sichtber foar elkenien",
"privacy.public.short": "Iepenbier",
"privacy.unlisted.long": "Foar elkenien sichtber, mar net ûnder trends, hashtags en op iepenbiere tiidlinen",
"privacy.unlisted.short": "Minder iepenbier",
"privacy_policy.last_updated": "Lêst bywurke op {date}",
"privacy_policy.title": "Privacybelied",
"recommended": "Oanrekommandearre",
@ -715,10 +696,8 @@
"upload_error.poll": "It opladen fan bestannen is yn enkêten net tastien.",
"upload_form.audio_description": "Describe for people with hearing loss",
"upload_form.description": "Describe for the visually impaired",
"upload_form.description_missing": "Gjin omskriuwing tafoege",
"upload_form.edit": "Bewurkje",
"upload_form.thumbnail": "Miniatuerôfbylding wizigje",
"upload_form.undo": "Fuortsmite",
"upload_form.video_description": "Describe for people with hearing loss or visual impairment",
"upload_modal.analyzing_picture": "Ofbylding analysearje…",
"upload_modal.apply": "Tapasse",

Some files were not shown because too many files have changed in this diff Show more