Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone Build is passing
All checks were successful
continuous-integration/drone Build is passing
This commit is contained in:
commit
2c4f8b71cb
305 changed files with 5080 additions and 2949 deletions
|
@ -70,7 +70,7 @@ services:
|
|||
hard: -1
|
||||
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:v1.4.1
|
||||
image: libretranslate/libretranslate:v1.5.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- lt-data:/home/libretranslate/.local
|
||||
|
|
13
.github/codecov.yml
vendored
Normal file
13
.github/codecov.yml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Github status check is not blocking
|
||||
informational: true
|
||||
patch:
|
||||
default:
|
||||
# Github status check is not blocking
|
||||
informational: true
|
||||
comment:
|
||||
# Only write a comment in PR if there are changes
|
||||
require_changes: true
|
12
.github/workflows/test-ruby.yml
vendored
12
.github/workflows/test-ruby.yml
vendored
|
@ -94,7 +94,7 @@ jobs:
|
|||
DB_HOST: localhost
|
||||
DB_USER: postgres
|
||||
DB_PASS: postgres
|
||||
DISABLE_SIMPLECOV: true
|
||||
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||
RAILS_ENV: test
|
||||
ALLOW_NOPAM: true
|
||||
PAM_ENABLED: true
|
||||
|
@ -137,6 +137,12 @@ jobs:
|
|||
|
||||
- run: bin/rspec
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.ruby-version == '.ruby-version'
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: coverage/lcov/mastodon.lcov
|
||||
|
||||
test-e2e:
|
||||
name: End to End testing
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -221,7 +227,7 @@ jobs:
|
|||
path: tmp/screenshots/
|
||||
|
||||
test-search:
|
||||
name: Testing search
|
||||
name: Elastic Search integration testing
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
|
@ -308,7 +314,7 @@ jobs:
|
|||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
||||
- run: bundle exec rake spec:search
|
||||
- run: bin/rspec --tag search
|
||||
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
@ -41,23 +41,6 @@ Metrics/CyclomaticComplexity:
|
|||
Metrics/PerceivedComplexity:
|
||||
Max: 27
|
||||
|
||||
RSpec/AnyInstance:
|
||||
Exclude:
|
||||
- 'spec/controllers/activitypub/inboxes_controller_spec.rb'
|
||||
- 'spec/controllers/admin/accounts_controller_spec.rb'
|
||||
- 'spec/controllers/admin/resets_controller_spec.rb'
|
||||
- 'spec/controllers/auth/sessions_controller_spec.rb'
|
||||
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
|
||||
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
|
||||
- 'spec/lib/request_spec.rb'
|
||||
- 'spec/lib/status_filter_spec.rb'
|
||||
- 'spec/models/account_spec.rb'
|
||||
- 'spec/models/setting_spec.rb'
|
||||
- 'spec/services/activitypub/process_collection_service_spec.rb'
|
||||
- 'spec/validators/follow_limit_validator_spec.rb'
|
||||
- 'spec/workers/activitypub/delivery_worker_spec.rb'
|
||||
- 'spec/workers/web/push_notification_worker_spec.rb'
|
||||
|
||||
# Configuration parameters: CountAsOne.
|
||||
RSpec/ExampleLength:
|
||||
Max: 22
|
||||
|
|
22
.simplecov
Normal file
22
.simplecov
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
if ENV['CI']
|
||||
require 'simplecov-lcov'
|
||||
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
|
||||
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
|
||||
else
|
||||
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
||||
end
|
||||
|
||||
SimpleCov.start 'rails' do
|
||||
enable_coverage :branch
|
||||
|
||||
add_filter 'lib/linter'
|
||||
|
||||
add_group 'Libraries', 'lib'
|
||||
add_group 'Policies', 'app/policies'
|
||||
add_group 'Presenters', 'app/presenters'
|
||||
add_group 'Serializers', 'app/serializers'
|
||||
add_group 'Services', 'app/services'
|
||||
add_group 'Validators', 'app/validators'
|
||||
end
|
3
.watchmanconfig
Normal file
3
.watchmanconfig
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"ignore_dirs": ["node_modules/", "public/"]
|
||||
}
|
|
@ -38,6 +38,7 @@ RUN apt-get update && \
|
|||
corepack enable
|
||||
|
||||
COPY Gemfile* package.json yarn.lock .yarnrc.yml /opt/mastodon/
|
||||
COPY streaming/package.json /opt/mastodon/streaming/
|
||||
COPY .yarn /opt/mastodon/.yarn
|
||||
|
||||
RUN bundle install -j"$(nproc)"
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -109,6 +109,9 @@ group :test do
|
|||
# RSpec progress bar formatter
|
||||
gem 'fuubar', '~> 2.5'
|
||||
|
||||
# RSpec helpers for email specs
|
||||
gem 'email_spec'
|
||||
|
||||
# Extra RSpec extenion methods and helpers for sidekiq
|
||||
gem 'rspec-sidekiq', '~> 4.0'
|
||||
|
||||
|
@ -139,6 +142,7 @@ group :test do
|
|||
|
||||
# Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false
|
||||
gem 'simplecov', '~> 0.22', require: false
|
||||
gem 'simplecov-lcov', '~> 0.8', require: false
|
||||
|
||||
# Stub web requests for specs
|
||||
gem 'webmock', '~> 3.18'
|
||||
|
|
128
Gemfile.lock
128
Gemfile.lock
|
@ -39,50 +39,51 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actioncable (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actionmailbox (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actionmailer (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actionpack (7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actiontext (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
actionview (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
|
@ -92,22 +93,22 @@ GEM
|
|||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
activejob (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
activerecord (7.1.1)
|
||||
activemodel (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
activemodel (7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
activerecord (7.1.2)
|
||||
activemodel (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
activestorage (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
marcel (~> 1.0)
|
||||
activesupport (7.1.1)
|
||||
activesupport (7.1.2)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
|
@ -218,7 +219,7 @@ GEM
|
|||
activerecord (>= 5.a)
|
||||
database_cleaner-core (~> 2.0.0)
|
||||
database_cleaner-core (2.0.1)
|
||||
date (3.3.3)
|
||||
date (3.3.4)
|
||||
debug_inspector (1.1.0)
|
||||
devise (4.9.3)
|
||||
bcrypt (~> 3.0)
|
||||
|
@ -259,6 +260,10 @@ GEM
|
|||
elasticsearch-transport (7.13.3)
|
||||
faraday (~> 1)
|
||||
multi_json
|
||||
email_spec (2.2.2)
|
||||
htmlentities (~> 4.3.3)
|
||||
launchy (~> 2.1)
|
||||
mail (~> 2.7)
|
||||
encryptor (3.0.0)
|
||||
erubi (1.12.0)
|
||||
et-orbi (1.2.7)
|
||||
|
@ -369,7 +374,7 @@ GEM
|
|||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.6.0)
|
||||
irb (1.8.1)
|
||||
irb (1.8.3)
|
||||
rdoc
|
||||
reline (>= 0.3.8)
|
||||
jmespath (1.6.2)
|
||||
|
@ -462,13 +467,13 @@ GEM
|
|||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.4.1)
|
||||
net-imap (0.4.4)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.18.0)
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.2.1)
|
||||
net-protocol (0.2.2)
|
||||
timeout
|
||||
net-smtp (0.4.0)
|
||||
net-protocol
|
||||
|
@ -526,7 +531,7 @@ GEM
|
|||
net-smtp
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
private_address_check (0.5.0)
|
||||
psych (5.1.1)
|
||||
psych (5.1.1.1)
|
||||
stringio
|
||||
public_suffix (5.0.3)
|
||||
puma (6.4.0)
|
||||
|
@ -557,20 +562,20 @@ GEM
|
|||
rackup (1.0.0)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.1)
|
||||
actioncable (= 7.1.1)
|
||||
actionmailbox (= 7.1.1)
|
||||
actionmailer (= 7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
actiontext (= 7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activemodel (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
rails (7.1.2)
|
||||
actioncable (= 7.1.2)
|
||||
actionmailbox (= 7.1.2)
|
||||
actionmailer (= 7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
actiontext (= 7.1.2)
|
||||
actionview (= 7.1.2)
|
||||
activejob (= 7.1.2)
|
||||
activemodel (= 7.1.2)
|
||||
activerecord (= 7.1.2)
|
||||
activestorage (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.1.1)
|
||||
railties (= 7.1.2)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -585,9 +590,9 @@ GEM
|
|||
rails-i18n (7.0.8)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
railties (7.1.2)
|
||||
actionpack (= 7.1.2)
|
||||
activesupport (= 7.1.2)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
|
@ -724,6 +729,7 @@ GEM
|
|||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
smart_properties (1.17.0)
|
||||
sprockets (3.7.2)
|
||||
|
@ -737,7 +743,7 @@ GEM
|
|||
statsd-ruby (1.5.0)
|
||||
stoplight (3.0.2)
|
||||
redlock (~> 1.0)
|
||||
stringio (3.0.8)
|
||||
stringio (3.0.9)
|
||||
strong_migrations (1.6.4)
|
||||
activerecord (>= 5.2)
|
||||
swd (1.3.0)
|
||||
|
@ -753,7 +759,7 @@ GEM
|
|||
test-prof (1.2.3)
|
||||
thor (1.3.0)
|
||||
tilt (2.3.0)
|
||||
timeout (0.4.0)
|
||||
timeout (0.4.1)
|
||||
tpm-key_attestation (0.12.0)
|
||||
bindata (~> 2.4)
|
||||
openssl (> 2.0)
|
||||
|
@ -851,6 +857,7 @@ DEPENDENCIES
|
|||
doorkeeper (~> 5.6)
|
||||
dotenv-rails (~> 2.8)
|
||||
ed25519 (~> 1.3)
|
||||
email_spec
|
||||
fabrication (~> 2.30)
|
||||
faker (~> 3.2)
|
||||
fast_blank (~> 1.0)
|
||||
|
@ -935,6 +942,7 @@ DEPENDENCIES
|
|||
simple-navigation (~> 4.4)
|
||||
simple_form (~> 5.2)
|
||||
simplecov (~> 0.22)
|
||||
simplecov-lcov (~> 0.8)
|
||||
sprockets (~> 3.7.2)
|
||||
sprockets-rails (~> 3.4)
|
||||
stackprof
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
|
||||
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
|
||||
stream: env PORT=4000 yarn run start
|
||||
stream: env PORT=4000 yarn workspace @mastodon/streaming start
|
||||
webpack: bin/webpack-dev-server
|
||||
|
|
|
@ -53,7 +53,7 @@ class PublicStatusesIndex < Chewy::Index
|
|||
index_scope ::Status.unscoped
|
||||
.kept
|
||||
.indexable
|
||||
.includes(:media_attachments, :preloadable_poll, :preview_cards, :tags)
|
||||
.includes(:media_attachments, :preloadable_poll, :tags, preview_cards_status: :preview_card)
|
||||
|
||||
root date_detection: false do
|
||||
field(:id, type: 'long')
|
||||
|
|
|
@ -50,7 +50,7 @@ class StatusesIndex < Chewy::Index
|
|||
},
|
||||
}
|
||||
|
||||
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preview_cards, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? }
|
||||
index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :local_mentioned, :local_favorited, :local_reblogged, :local_bookmarked, :tags, preview_cards_status: :preview_card, preloadable_poll: :local_voters), delete_if: ->(status) { status.searchable_by.empty? }
|
||||
|
||||
root date_detection: false do
|
||||
field(:id, type: 'long')
|
||||
|
|
|
@ -7,6 +7,7 @@ class Api::BaseController < ApplicationController
|
|||
include RateLimitHeaders
|
||||
include AccessTokenTrackingConcern
|
||||
include ApiCachingConcern
|
||||
include Api::ContentSecurityPolicy
|
||||
|
||||
skip_before_action :require_functional!, unless: :limited_federation_mode?
|
||||
|
||||
|
@ -17,26 +18,6 @@ class Api::BaseController < ApplicationController
|
|||
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
content_security_policy do |p|
|
||||
# Set every directive that does not have a fallback
|
||||
p.default_src :none
|
||||
p.frame_ancestors :none
|
||||
p.form_action :none
|
||||
|
||||
# Disable every directive with a fallback to cut on response size
|
||||
p.base_uri false
|
||||
p.font_src false
|
||||
p.img_src false
|
||||
p.style_src false
|
||||
p.media_src false
|
||||
p.frame_src false
|
||||
p.manifest_src false
|
||||
p.connect_src false
|
||||
p.script_src false
|
||||
p.child_src false
|
||||
p.worker_src false
|
||||
end
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
end
|
||||
|
|
|
@ -16,6 +16,8 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
|||
current_user.update(user_params) if user_params
|
||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::AccountsController < Api::BaseController
|
||||
include RegistrationHelper
|
||||
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers]
|
||||
before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute]
|
||||
|
@ -90,18 +92,14 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone)
|
||||
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code)
|
||||
end
|
||||
|
||||
def invite
|
||||
Invite.find_by(code: params[:invite_code]) if params[:invite_code].present?
|
||||
end
|
||||
|
||||
def check_enabled_registrations
|
||||
forbidden if single_user_mode? || omniauth_only? || !allowed_registrations?
|
||||
end
|
||||
|
||||
def allowed_registrations?
|
||||
Setting.registrations_mode != 'none'
|
||||
end
|
||||
|
||||
def omniauth_only?
|
||||
ENV['OMNIAUTH_ONLY'] == 'true'
|
||||
forbidden unless allowed_registration?(request.remote_ip, invite)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,10 +41,10 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||
account: :account_stat,
|
||||
last_status: [
|
||||
:media_attachments,
|
||||
:preview_cards,
|
||||
:status_stat,
|
||||
:tags,
|
||||
{
|
||||
preview_cards_status: :preview_card,
|
||||
active_mentions: [account: :account_stat],
|
||||
account: :account_stat,
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
|
|||
cache_if_unauthenticated!
|
||||
end
|
||||
|
||||
render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?))
|
||||
render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: show_rationale_in_response?
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -25,4 +25,16 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
|
|||
def set_domain_blocks
|
||||
@domain_blocks = DomainBlock.with_user_facing_limitations.by_severity
|
||||
end
|
||||
|
||||
def show_rationale_in_response?
|
||||
always_show_rationale? || show_rationale_for_user?
|
||||
end
|
||||
|
||||
def always_show_rationale?
|
||||
Setting.show_domain_blocks_rationale == 'all'
|
||||
end
|
||||
|
||||
def show_rationale_for_user?
|
||||
Setting.show_domain_blocks_rationale == 'users' && user_signed_in?
|
||||
end
|
||||
end
|
||||
|
|
30
app/controllers/api/v1/invites_controller.rb
Normal file
30
app/controllers/api/v1/invites_controller.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::InvitesController < Api::BaseController
|
||||
include RegistrationHelper
|
||||
|
||||
skip_before_action :require_authenticated_user!
|
||||
skip_around_action :set_locale
|
||||
|
||||
before_action :set_invite
|
||||
before_action :check_enabled_registrations!
|
||||
|
||||
# Override `current_user` to avoid reading session cookies
|
||||
def current_user; end
|
||||
|
||||
def show
|
||||
render json: { invite_code: params[:invite_code], instance_api_url: api_v2_instance_url }, status: 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_invite
|
||||
@invite = Invite.find_by!(code: params[:invite_code])
|
||||
end
|
||||
|
||||
def check_enabled_registrations!
|
||||
return render json: { error: I18n.t('invites.invalid') }, status: 401 unless @invite.valid_for_use?
|
||||
|
||||
raise Mastodon::NotPermittedError unless allowed_registration?(request.remote_ip, @invite)
|
||||
end
|
||||
end
|
16
app/controllers/api/v1/statuses/base_controller.rb
Normal file
16
app/controllers/api/v1/statuses/base_controller.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::BaseController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action :set_status
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
|
@ -1,11 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::BookmarksController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::BookmarksController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' }
|
||||
before_action :require_user!
|
||||
before_action :set_status, only: [:create]
|
||||
skip_before_action :set_status, only: [:destroy]
|
||||
|
||||
def create
|
||||
current_account.bookmarks.find_or_create_by!(account: current_account, status: @status)
|
||||
|
@ -28,13 +26,4 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
|
|||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
def index
|
||||
|
@ -61,13 +58,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
|||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::FavouritesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::FavouritesController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
|
||||
before_action :require_user!
|
||||
before_action :set_status, only: [:create]
|
||||
skip_before_action :set_status, only: [:destroy]
|
||||
|
||||
def create
|
||||
FavouriteService.new.call(current_account, @status)
|
||||
|
@ -30,13 +28,4 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
|
|||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::HistoriesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::HistoriesController < Api::V1::Statuses::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
|
||||
before_action :set_status
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
|
@ -16,11 +13,4 @@ class Api::V1::Statuses::HistoriesController < Api::BaseController
|
|||
def status_edits
|
||||
@status.edits.includes(:account, status: [:account]).to_a.presence || [@status.build_snapshot(at_time: @status.edited_at || @status.created_at)]
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::MutesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::MutesController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_conversation
|
||||
|
||||
def create
|
||||
|
@ -24,13 +21,6 @@ class Api::V1::Statuses::MutesController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = @status.conversation
|
||||
raise Mastodon::ValidationError if @conversation.nil?
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::PinsController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
|
||||
def create
|
||||
StatusPin.create!(account: current_account, status: @status)
|
||||
|
@ -26,10 +23,6 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
end
|
||||
|
||||
def distribute_add_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::BaseController
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_status
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
def index
|
||||
|
@ -57,13 +54,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
|||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
end
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::ReblogsController < Api::BaseController
|
||||
include Authorization
|
||||
class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
|
||||
include Redisable
|
||||
include Lockable
|
||||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
|
||||
before_action :require_user!
|
||||
before_action :set_reblog, only: [:create]
|
||||
skip_before_action :set_status
|
||||
|
||||
override_rate_limit_headers :create, family: :statuses
|
||||
|
||||
|
|
|
@ -1,21 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::SourcesController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::SourcesController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||
before_action :set_status
|
||||
|
||||
def show
|
||||
render json: @status, serializer: REST::StatusSourceSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::TranslationsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||
before_action :set_status
|
||||
before_action :set_translation
|
||||
|
||||
rescue_from TranslationService::NotConfiguredError, with: :not_found
|
||||
|
@ -24,13 +21,6 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController
|
|||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def set_translation
|
||||
@translation = TranslateStatusService.new.call(@status, content_locale)
|
||||
end
|
||||
|
|
33
app/controllers/api/v1/timelines/base_controller.rb
Normal file
33
app/controllers/api/v1/timelines/base_controller.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::BaseController < Api::BaseController
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
private
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
|
||||
def next_path_params
|
||||
permitted_params.merge(max_id: pagination_max_id)
|
||||
end
|
||||
|
||||
def prev_path_params
|
||||
permitted_params.merge(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params
|
||||
.slice(*self.class::PERMITTED_PARAMS)
|
||||
.permit(*self.class::PERMITTED_PARAMS)
|
||||
end
|
||||
end
|
|
@ -1,9 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::HomeController < Api::BaseController
|
||||
class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
|
||||
before_action :require_user!, only: [:show]
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
PERMITTED_PARAMS = %i(local limit).freeze
|
||||
|
||||
def show
|
||||
with_read_replica do
|
||||
|
@ -40,27 +41,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
|||
HomeFeed.new(current_account)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :limit).permit(:local, :limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_home_url pagination_params(max_id: pagination_max_id)
|
||||
api_v1_timelines_home_url next_path_params
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_home_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
api_v1_timelines_home_url prev_path_params
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::ListController < Api::BaseController
|
||||
class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }
|
||||
before_action :require_user!
|
||||
before_action :set_list
|
||||
before_action :set_statuses
|
||||
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
PERMITTED_PARAMS = %i(limit).freeze
|
||||
|
||||
def show
|
||||
render json: @statuses,
|
||||
|
@ -41,27 +41,11 @@ class Api::V1::Timelines::ListController < Api::BaseController
|
|||
ListFeed.new(@list)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:limit).permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
api_v1_timelines_list_url params[:id], next_path_params
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
api_v1_timelines_list_url params[:id], prev_path_params
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::PublicController < Api::BaseController
|
||||
class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
|
||||
before_action :require_user!, only: [:show], if: :require_auth?
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
PERMITTED_PARAMS = %i(local remote limit only_media).freeze
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
|
@ -42,27 +43,11 @@ class Api::V1::Timelines::PublicController < Api::BaseController
|
|||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_public_url pagination_params(max_id: pagination_max_id)
|
||||
api_v1_timelines_public_url next_path_params
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_public_url pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
api_v1_timelines_public_url prev_path_params
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Timelines::TagController < Api::BaseController
|
||||
class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
|
||||
before_action :load_tag
|
||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||
|
||||
PERMITTED_PARAMS = %i(local limit only_media).freeze
|
||||
|
||||
def show
|
||||
cache_if_unauthenticated!
|
||||
|
@ -51,27 +52,11 @@ class Api::V1::Timelines::TagController < Api::BaseController
|
|||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_timelines_tag_url params[:id], pagination_params(max_id: pagination_max_id)
|
||||
api_v1_timelines_tag_url params[:id], next_path_params
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id)
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@statuses.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
api_v1_timelines_tag_url params[:id], prev_path_params
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,37 +3,13 @@
|
|||
class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
||||
before_action :require_user!
|
||||
before_action :set_push_subscription, only: :update
|
||||
before_action :destroy_previous_subscriptions, only: :create, if: :prior_subscriptions?
|
||||
after_action :update_session_with_subscription, only: :create
|
||||
|
||||
def create
|
||||
active_session = current_session
|
||||
@push_subscription = ::Web::PushSubscription.create!(web_push_subscription_params)
|
||||
|
||||
unless active_session.web_push_subscription.nil?
|
||||
active_session.web_push_subscription.destroy!
|
||||
active_session.update!(web_push_subscription: nil)
|
||||
end
|
||||
|
||||
# Mobile devices do not support regular notifications, so we enable push notifications by default
|
||||
alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet?
|
||||
|
||||
data = {
|
||||
policy: 'all',
|
||||
alerts: Notification::TYPES.index_with { alerts_enabled },
|
||||
}
|
||||
|
||||
data.deep_merge!(data_params) if params[:data]
|
||||
|
||||
push_subscription = ::Web::PushSubscription.create!(
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
data: data,
|
||||
user_id: active_session.user_id,
|
||||
access_token_id: active_session.access_token_id
|
||||
)
|
||||
|
||||
active_session.update!(web_push_subscription: push_subscription)
|
||||
|
||||
render json: push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
render json: @push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -43,6 +19,41 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
|||
|
||||
private
|
||||
|
||||
def active_session
|
||||
@active_session ||= current_session
|
||||
end
|
||||
|
||||
def destroy_previous_subscriptions
|
||||
active_session.web_push_subscription.destroy!
|
||||
active_session.update!(web_push_subscription: nil)
|
||||
end
|
||||
|
||||
def prior_subscriptions?
|
||||
active_session.web_push_subscription.present?
|
||||
end
|
||||
|
||||
def subscription_data
|
||||
default_subscription_data.tap do |data|
|
||||
data.deep_merge!(data_params) if params[:data]
|
||||
end
|
||||
end
|
||||
|
||||
def default_subscription_data
|
||||
{
|
||||
policy: 'all',
|
||||
alerts: Notification::TYPES.index_with { alerts_enabled },
|
||||
}
|
||||
end
|
||||
|
||||
def alerts_enabled
|
||||
# Mobile devices do not support regular notifications, so we enable push notifications by default
|
||||
active_session.detection.device.mobile? || active_session.detection.device.tablet?
|
||||
end
|
||||
|
||||
def update_session_with_subscription
|
||||
active_session.update!(web_push_subscription: @push_subscription)
|
||||
end
|
||||
|
||||
def set_push_subscription
|
||||
@push_subscription = ::Web::PushSubscription.find(params[:id])
|
||||
end
|
||||
|
@ -51,6 +62,17 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
|||
@subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
|
||||
end
|
||||
|
||||
def web_push_subscription_params
|
||||
{
|
||||
access_token_id: active_session.access_token_id,
|
||||
data: subscription_data,
|
||||
endpoint: subscription_params[:endpoint],
|
||||
key_auth: subscription_params[:keys][:auth],
|
||||
key_p256dh: subscription_params[:keys][:p256dh],
|
||||
user_id: active_session.user_id,
|
||||
}
|
||||
end
|
||||
|
||||
def data_params
|
||||
@data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
include RegistrationHelper
|
||||
include RegistrationSpamConcern
|
||||
|
||||
layout :determine_layout
|
||||
|
@ -82,19 +83,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def check_enabled_registrations
|
||||
redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? || ip_blocked?
|
||||
end
|
||||
|
||||
def allowed_registrations?
|
||||
Setting.registrations_mode != 'none' || @invite&.valid_for_use?
|
||||
end
|
||||
|
||||
def omniauth_only?
|
||||
ENV['OMNIAUTH_ONLY'] == 'true'
|
||||
end
|
||||
|
||||
def ip_blocked?
|
||||
IpBlock.where(severity: :sign_up_block).where('ip >>= ?', request.remote_ip.to_s).exists?
|
||||
redirect_to root_path unless allowed_registration?(request.remote_ip, @invite)
|
||||
end
|
||||
|
||||
def invite_code
|
||||
|
|
27
app/controllers/concerns/api/content_security_policy.rb
Normal file
27
app/controllers/concerns/api/content_security_policy.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api::ContentSecurityPolicy
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
content_security_policy do |policy|
|
||||
# Set every directive that does not have a fallback
|
||||
policy.default_src :none
|
||||
policy.frame_ancestors :none
|
||||
policy.form_action :none
|
||||
|
||||
# Disable every directive with a fallback to cut on response size
|
||||
policy.base_uri false
|
||||
policy.font_src false
|
||||
policy.img_src false
|
||||
policy.style_src false
|
||||
policy.media_src false
|
||||
policy.frame_src false
|
||||
policy.manifest_src false
|
||||
policy.connect_src false
|
||||
policy.script_src false
|
||||
policy.child_src false
|
||||
policy.worker_src false
|
||||
end
|
||||
end
|
||||
end
|
21
app/helpers/registration_helper.rb
Normal file
21
app/helpers/registration_helper.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RegistrationHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def allowed_registration?(remote_ip, invite)
|
||||
!Rails.configuration.x.single_user_mode && !omniauth_only? && (registrations_open? || invite&.valid_for_use?) && !ip_blocked?(remote_ip)
|
||||
end
|
||||
|
||||
def registrations_open?
|
||||
Setting.registrations_mode != 'none'
|
||||
end
|
||||
|
||||
def omniauth_only?
|
||||
ENV['OMNIAUTH_ONLY'] == 'true'
|
||||
end
|
||||
|
||||
def ip_blocked?(remote_ip)
|
||||
IpBlock.where(severity: :sign_up_block).exists?(['ip >>= ?', remote_ip.to_s])
|
||||
end
|
||||
end
|
|
@ -661,3 +661,18 @@ export function unpinAccountFail(error) {
|
|||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => {
|
||||
const data = new FormData();
|
||||
|
||||
data.append('display_name', displayName);
|
||||
data.append('note', note);
|
||||
if (avatar) data.append('avatar', avatar);
|
||||
if (header) data.append('header', header);
|
||||
data.append('discoverable', discoverable);
|
||||
data.append('indexable', indexable);
|
||||
|
||||
return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => {
|
||||
dispatch(importFetchedAccount(response.data));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@ export interface ApiAccountJSON {
|
|||
bot: boolean;
|
||||
created_at: string;
|
||||
discoverable: boolean;
|
||||
indexable: boolean;
|
||||
display_name: string;
|
||||
emojis: ApiCustomEmojiJSON[];
|
||||
fields: ApiAccountFieldJSON[];
|
||||
|
|
|
@ -51,7 +51,7 @@ export default class Retention extends PureComponent {
|
|||
let content;
|
||||
|
||||
if (loading) {
|
||||
content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading...' />;
|
||||
content = <FormattedMessage id='loading_indicator.label' defaultMessage='Loading…' />;
|
||||
} else {
|
||||
content = (
|
||||
<table className='retention__table'>
|
||||
|
|
44
app/javascript/mastodon/components/copy_icon_button.jsx
Normal file
44
app/javascript/mastodon/components/copy_icon_button.jsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
|
||||
|
||||
import { showAlert } from 'mastodon/actions/alerts';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
copied: { id: 'copy_icon_button.copied', defaultMessage: 'Copied to clipboard' },
|
||||
});
|
||||
|
||||
export const CopyIconButton = ({ title, value, className }) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
navigator.clipboard.writeText(value);
|
||||
setCopied(true);
|
||||
dispatch(showAlert({ message: messages.copied }));
|
||||
setTimeout(() => setCopied(false), 700);
|
||||
}, [setCopied, value, dispatch]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={classNames(className, copied ? 'copied' : 'copyable')}
|
||||
title={title}
|
||||
onClick={handleClick}
|
||||
iconComponent={ContentCopyIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
CopyIconButton.propTypes = {
|
||||
title: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
};
|
|
@ -1,7 +1,23 @@
|
|||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { CircularProgress } from './circular_progress';
|
||||
|
||||
export const LoadingIndicator: React.FC = () => (
|
||||
<div className='loading-indicator'>
|
||||
const messages = defineMessages({
|
||||
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading…' },
|
||||
});
|
||||
|
||||
export const LoadingIndicator: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<div
|
||||
className='loading-indicator'
|
||||
role='progressbar'
|
||||
aria-busy
|
||||
aria-live='polite'
|
||||
aria-label={intl.formatMessage(messages.loading)}
|
||||
>
|
||||
<CircularProgress size={50} strokeWidth={6} />
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -19,6 +19,8 @@ import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/s
|
|||
import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg';
|
||||
import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg';
|
||||
|
||||
import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg';
|
||||
import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -348,6 +350,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
let replyIcon;
|
||||
let replyIconComponent;
|
||||
let replyTitle;
|
||||
|
||||
if (status.get('in_reply_to_id', null) === null) {
|
||||
replyIcon = 'reply';
|
||||
replyIconComponent = ReplyIcon;
|
||||
|
@ -360,15 +363,20 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
|
||||
|
||||
let reblogTitle = '';
|
||||
let reblogTitle, reblogIconComponent;
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
} else if (reblogPrivate) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||
reblogIconComponent = RepeatPrivateIcon;
|
||||
} else {
|
||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||
reblogIconComponent = RepeatDisabledIcon;
|
||||
}
|
||||
|
||||
const filterButton = this.props.onFilter && (
|
||||
|
@ -380,7 +388,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='status__action-bar'>
|
||||
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
||||
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} />
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@ import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/l
|
|||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications.svg';
|
||||
import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active-fill.svg';
|
||||
import { ReactComponent as ShareIcon } from '@material-symbols/svg-600/outlined/share.svg';
|
||||
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
|
||||
import { Button } from 'mastodon/components/button';
|
||||
import { CopyIconButton } from 'mastodon/components/copy_icon_button';
|
||||
import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
@ -46,6 +48,7 @@ const messages = defineMessages({
|
|||
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
|
||||
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
|
||||
copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' },
|
||||
media: { id: 'account.media', defaultMessage: 'Media' },
|
||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||
|
@ -245,10 +248,9 @@ class Header extends ImmutablePureComponent {
|
|||
const isRemote = account.get('acct') !== account.get('username');
|
||||
const remoteDomain = isRemote ? account.get('acct').split('@')[1] : null;
|
||||
|
||||
let actionBtn, bellBtn, lockedIcon, shareBtn;
|
||||
|
||||
let info = [];
|
||||
let actionBtn = '';
|
||||
let bellBtn = '';
|
||||
let lockedIcon = '';
|
||||
let menu = [];
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||
|
@ -267,6 +269,12 @@ class Header extends ImmutablePureComponent {
|
|||
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||
}
|
||||
|
||||
if ('share' in navigator) {
|
||||
shareBtn = <IconButton className='optional' iconComponent={ShareIcon} title={intl.formatMessage(messages.share, { name: account.get('username') })} onClick={this.handleShare} />;
|
||||
} else {
|
||||
shareBtn = <CopyIconButton className='optional' title={intl.formatMessage(messages.copy)} value={account.get('url')} />;
|
||||
}
|
||||
|
||||
if (me !== account.get('id')) {
|
||||
if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
|
||||
actionBtn = '';
|
||||
|
@ -297,10 +305,6 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
if (isRemote) {
|
||||
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: account.get('url') });
|
||||
}
|
||||
|
||||
if ('share' in navigator && !account.get('suspended')) {
|
||||
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
|
||||
menu.push(null);
|
||||
}
|
||||
|
||||
|
@ -414,6 +418,7 @@ class Header extends ImmutablePureComponent {
|
|||
<>
|
||||
{actionBtn}
|
||||
{bellBtn}
|
||||
{shareBtn}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ class ListTimeline extends PureComponent {
|
|||
</div>
|
||||
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={`list-${id}-exclusive`} defaultChecked={isExclusive} onChange={this.onExclusiveToggle} />
|
||||
<Toggle id={`list-${id}-exclusive`} checked={isExclusive} onChange={this.onExclusiveToggle} />
|
||||
<label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'>
|
||||
<FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home' />
|
||||
</label>
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const ProgressIndicator = ({ steps, completed }) => (
|
||||
<div className='onboarding__progress-indicator'>
|
||||
{(new Array(steps)).fill().map((_, i) => (
|
||||
<Fragment key={i}>
|
||||
{i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />}
|
||||
|
||||
<div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}>
|
||||
{completed > i && <Icon icon={CheckIcon} />}
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
ProgressIndicator.propTypes = {
|
||||
steps: PropTypes.number.isRequired,
|
||||
completed: PropTypes.number,
|
||||
};
|
||||
|
||||
export default ProgressIndicator;
|
|
@ -1,11 +1,13 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => {
|
||||
export const Step = ({ label, description, icon, iconComponent, completed, onClick, href, to }) => {
|
||||
const content = (
|
||||
<>
|
||||
<div className='onboarding__steps__item__icon'>
|
||||
|
@ -29,6 +31,12 @@ const Step = ({ label, description, icon, iconComponent, completed, onClick, hre
|
|||
{content}
|
||||
</a>
|
||||
);
|
||||
} else if (to) {
|
||||
return (
|
||||
<Link to={to} className='onboarding__steps__item'>
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -45,7 +53,6 @@ Step.propTypes = {
|
|||
iconComponent: PropTypes.func,
|
||||
completed: PropTypes.bool,
|
||||
href: PropTypes.string,
|
||||
to: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Step;
|
||||
|
|
|
@ -1,44 +1,31 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
|
||||
import { fetchSuggestions } from 'mastodon/actions/suggestions';
|
||||
import { markAsPartial } from 'mastodon/actions/timelines';
|
||||
import Column from 'mastodon/components/column';
|
||||
import { ColumnBackButton } from 'mastodon/components/column_back_button';
|
||||
import { EmptyAccount } from 'mastodon/components/empty_account';
|
||||
import Account from 'mastodon/containers/account_container';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['suggestions', 'items']),
|
||||
isLoading: state.getIn(['suggestions', 'isLoading']),
|
||||
});
|
||||
export const Follows = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isLoading = useAppSelector(state => state.getIn(['suggestions', 'isLoading']));
|
||||
const suggestions = useAppSelector(state => state.getIn(['suggestions', 'items']));
|
||||
|
||||
class Follows extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
useEffect(() => {
|
||||
dispatch(fetchSuggestions(true));
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
const { dispatch } = this.props;
|
||||
return () => {
|
||||
dispatch(markAsPartial('home'));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { onBack, isLoading, suggestions } = this.props;
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
let loadedContent;
|
||||
|
||||
|
@ -51,8 +38,8 @@ class Follows extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<ColumnBackButton onClick={onBack} />
|
||||
<>
|
||||
<ColumnBackButton />
|
||||
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
|
@ -67,13 +54,9 @@ class Follows extends PureComponent {
|
|||
<p className='onboarding__lead'><FormattedMessage id='onboarding.tips.accounts_from_other_servers' defaultMessage='<strong>Did you know?</strong> Since Mastodon is decentralized, some profiles you come across will be hosted on servers other than yours. And yet you can interact with them seamlessly! Their server is in the second half of their username!' values={{ strong: chunks => <strong>{chunks}</strong> }} /></p>
|
||||
|
||||
<div className='onboarding__footer'>
|
||||
<button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></button>
|
||||
<Link className='link-button' to='/start'><FormattedMessage id='onboarding.actions.back' defaultMessage='Take me back' /></Link>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Follows);
|
||||
};
|
||||
|
|
|
@ -1,116 +1,50 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { Link, Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
|
||||
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
|
||||
import { ReactComponent as EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg';
|
||||
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import illustration from 'mastodon/../images/elephant_ui_conversation.svg';
|
||||
import { fetchAccount } from 'mastodon/actions/accounts';
|
||||
import { focusCompose } from 'mastodon/actions/compose';
|
||||
import { closeOnboarding } from 'mastodon/actions/onboarding';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import Column from 'mastodon/features/ui/components/column';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import { makeGetAccount } from 'mastodon/selectors';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
import { assetHost } from 'mastodon/utils/config';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
import Step from './components/step';
|
||||
import Follows from './follows';
|
||||
import Share from './share';
|
||||
import { Step } from './components/step';
|
||||
import { Follows } from './follows';
|
||||
import { Profile } from './profile';
|
||||
import { Share } from './share';
|
||||
|
||||
const messages = defineMessages({
|
||||
template: { id: 'onboarding.compose.template', defaultMessage: 'Hello #Mastodon!' },
|
||||
});
|
||||
|
||||
const mapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
return state => ({
|
||||
account: getAccount(state, me),
|
||||
});
|
||||
};
|
||||
|
||||
class Onboarding extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
account: ImmutablePropTypes.record,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
step: null,
|
||||
profileClicked: false,
|
||||
shareClicked: false,
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
const { dispatch, history } = this.props;
|
||||
|
||||
dispatch(closeOnboarding());
|
||||
history.push('/home');
|
||||
};
|
||||
|
||||
handleProfileClick = () => {
|
||||
this.setState({ profileClicked: true });
|
||||
};
|
||||
|
||||
handleFollowClick = () => {
|
||||
this.setState({ step: 'follows' });
|
||||
};
|
||||
|
||||
handleComposeClick = () => {
|
||||
const { dispatch, intl, history } = this.props;
|
||||
const Onboarding = () => {
|
||||
const account = useAppSelector(state => state.getIn(['accounts', me]));
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const handleComposeClick = useCallback(() => {
|
||||
dispatch(focusCompose(history, intl.formatMessage(messages.template)));
|
||||
};
|
||||
|
||||
handleShareClick = () => {
|
||||
this.setState({ step: 'share', shareClicked: true });
|
||||
};
|
||||
|
||||
handleBackClick = () => {
|
||||
this.setState({ step: null });
|
||||
};
|
||||
|
||||
handleWindowFocus = debounce(() => {
|
||||
const { dispatch, account } = this.props;
|
||||
dispatch(fetchAccount(account.get('id')));
|
||||
}, 1000, { trailing: true });
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('focus', this.handleWindowFocus, false);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('focus', this.handleWindowFocus);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account } = this.props;
|
||||
const { step, shareClicked } = this.state;
|
||||
|
||||
switch(step) {
|
||||
case 'follows':
|
||||
return <Follows onBack={this.handleBackClick} />;
|
||||
case 'share':
|
||||
return <Share onBack={this.handleBackClick} />;
|
||||
}
|
||||
}, [dispatch, intl, history]);
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Switch>
|
||||
<Route path='/start' exact>
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<img src={illustration} alt='' className='onboarding__illustration' />
|
||||
|
@ -119,10 +53,10 @@ class Onboarding extends ImmutablePureComponent {
|
|||
</div>
|
||||
|
||||
<div className='onboarding__steps'>
|
||||
<Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} />
|
||||
<Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} />
|
||||
<Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} />
|
||||
<Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} />
|
||||
<Step to='/start/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} />
|
||||
<Step to='/start/follows' completed={(account.get('following_count') * 1) >= 1} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} />
|
||||
<Step onClick={handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} />
|
||||
<Step to='/start/share' icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} />
|
||||
</div>
|
||||
|
||||
<p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p>
|
||||
|
@ -139,14 +73,18 @@ class Onboarding extends ImmutablePureComponent {
|
|||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Route>
|
||||
|
||||
<Route path='/start/profile' component={Profile} />
|
||||
<Route path='/start/follows' component={Follows} />
|
||||
<Route path='/start/share' component={Share} />
|
||||
</Switch>
|
||||
|
||||
<Helmet>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(Onboarding)));
|
||||
export default Onboarding;
|
||||
|
|
160
app/javascript/mastodon/features/onboarding/profile.jsx
Normal file
160
app/javascript/mastodon/features/onboarding/profile.jsx
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { useState, useMemo, useCallback, createRef } from 'react';
|
||||
|
||||
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AddPhotoAlternateIcon } from '@material-symbols/svg-600/outlined/add_photo_alternate.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import { updateAccount } from 'mastodon/actions/accounts';
|
||||
import { Button } from 'mastodon/components/button';
|
||||
import { ColumnBackButton } from 'mastodon/components/column_back_button';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
import { unescapeHTML } from 'mastodon/utils/html';
|
||||
|
||||
const messages = defineMessages({
|
||||
uploadHeader: { id: 'onboarding.profile.upload_header', defaultMessage: 'Upload profile header' },
|
||||
uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' },
|
||||
});
|
||||
|
||||
export const Profile = () => {
|
||||
const account = useAppSelector(state => state.getIn(['accounts', me]));
|
||||
const [displayName, setDisplayName] = useState(account.get('display_name'));
|
||||
const [note, setNote] = useState(unescapeHTML(account.get('note')));
|
||||
const [avatar, setAvatar] = useState(null);
|
||||
const [header, setHeader] = useState(null);
|
||||
const [discoverable, setDiscoverable] = useState(account.get('discoverable'));
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [errors, setErrors] = useState();
|
||||
const avatarFileRef = createRef();
|
||||
const headerFileRef = createRef();
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const handleDisplayNameChange = useCallback(e => {
|
||||
setDisplayName(e.target.value);
|
||||
}, [setDisplayName]);
|
||||
|
||||
const handleNoteChange = useCallback(e => {
|
||||
setNote(e.target.value);
|
||||
}, [setNote]);
|
||||
|
||||
const handleDiscoverableChange = useCallback(e => {
|
||||
setDiscoverable(e.target.checked);
|
||||
}, [setDiscoverable]);
|
||||
|
||||
const handleAvatarChange = useCallback(e => {
|
||||
setAvatar(e.target?.files?.[0]);
|
||||
}, [setAvatar]);
|
||||
|
||||
const handleHeaderChange = useCallback(e => {
|
||||
setHeader(e.target?.files?.[0]);
|
||||
}, [setHeader]);
|
||||
|
||||
const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : account.get('avatar'), [avatar, account]);
|
||||
const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : account.get('header'), [header, account]);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
setIsSaving(true);
|
||||
|
||||
dispatch(updateAccount({
|
||||
displayName,
|
||||
note,
|
||||
avatar,
|
||||
header,
|
||||
discoverable,
|
||||
indexable: discoverable,
|
||||
})).then(() => history.push('/start/follows')).catch(err => {
|
||||
setIsSaving(false);
|
||||
setErrors(err.response.data.details);
|
||||
});
|
||||
}, [dispatch, displayName, note, avatar, header, discoverable, history]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColumnBackButton />
|
||||
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
<h3><FormattedMessage id='onboarding.profile.title' defaultMessage='Profile setup' /></h3>
|
||||
<p><FormattedMessage id='onboarding.profile.lead' defaultMessage='You can always complete this later in the settings, where even more customization options are available.' /></p>
|
||||
</div>
|
||||
|
||||
<div className='simple_form'>
|
||||
<div className='onboarding__profile'>
|
||||
<label className={classNames('app-form__header-input', { selected: !!headerPreview, invalid: !!errors?.header })} title={intl.formatMessage(messages.uploadHeader)}>
|
||||
<input
|
||||
type='file'
|
||||
hidden
|
||||
ref={headerFileRef}
|
||||
accept='image/*'
|
||||
onChange={handleHeaderChange}
|
||||
/>
|
||||
|
||||
{headerPreview && <img src={headerPreview} alt='' />}
|
||||
|
||||
<Icon icon={headerPreview ? EditIcon : AddPhotoAlternateIcon} />
|
||||
</label>
|
||||
|
||||
<label className={classNames('app-form__avatar-input', { selected: !!avatarPreview, invalid: !!errors?.avatar })} title={intl.formatMessage(messages.uploadAvatar)}>
|
||||
<input
|
||||
type='file'
|
||||
hidden
|
||||
ref={avatarFileRef}
|
||||
accept='image/*'
|
||||
onChange={handleAvatarChange}
|
||||
/>
|
||||
|
||||
{avatarPreview && <img src={avatarPreview} alt='' />}
|
||||
|
||||
<Icon icon={avatarPreview ? EditIcon : AddPhotoAlternateIcon} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={classNames('input with_block_label', { field_with_errors: !!errors?.display_name })}>
|
||||
<label htmlFor='display_name'><FormattedMessage id='onboarding.profile.display_name' defaultMessage='Display name' /></label>
|
||||
<span className='hint'><FormattedMessage id='onboarding.profile.display_name_hint' defaultMessage='Your full name or your fun name…' /></span>
|
||||
<div className='label_input'>
|
||||
<input id='display_name' type='text' value={displayName} onChange={handleDisplayNameChange} maxLength={30} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classNames('input with_block_label', { field_with_errors: !!errors?.note })}>
|
||||
<label htmlFor='note'><FormattedMessage id='onboarding.profile.note' defaultMessage='Bio' /></label>
|
||||
<span className='hint'><FormattedMessage id='onboarding.profile.note_hint' defaultMessage='You can @mention other people or #hashtags…' /></span>
|
||||
<div className='label_input'>
|
||||
<textarea id='note' value={note} onChange={handleNoteChange} maxLength={500} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label className='app-form__toggle'>
|
||||
<div className='app-form__toggle__label'>
|
||||
<strong><FormattedMessage id='onboarding.profile.discoverable' defaultMessage='Make my profile discoverable' /></strong> <span className='recommended'><FormattedMessage id='recommended' defaultMessage='Recommended' /></span>
|
||||
<span className='hint'><FormattedMessage id='onboarding.profile.discoverable_hint' defaultMessage='When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.' /></span>
|
||||
</div>
|
||||
|
||||
<div className='app-form__toggle__toggle'>
|
||||
<div>
|
||||
<Toggle checked={discoverable} onChange={handleDiscoverableChange} />
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className='onboarding__footer'>
|
||||
<Button block onClick={handleSubmit} disabled={isSaving}>{isSaving ? <LoadingIndicator /> : <FormattedMessage id='onboarding.profile.save_and_continue' defaultMessage='Save and continue' />}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,31 +1,25 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
|
||||
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
|
||||
import SwipeableViews from 'react-swipeable-views';
|
||||
|
||||
import Column from 'mastodon/components/column';
|
||||
import { ColumnBackButton } from 'mastodon/components/column_back_button';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { me, domain } from 'mastodon/initial_state';
|
||||
import { useAppSelector } from 'mastodon/store';
|
||||
|
||||
const messages = defineMessages({
|
||||
shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
account: state.getIn(['accounts', me]),
|
||||
});
|
||||
|
||||
class CopyPasteText extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -141,22 +135,14 @@ class TipCarousel extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
class Share extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onBack: PropTypes.func,
|
||||
account: ImmutablePropTypes.record,
|
||||
intl: PropTypes.object,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { onBack, account, intl } = this.props;
|
||||
|
||||
export const Share = () => {
|
||||
const account = useAppSelector(state => state.getIn(['accounts', me]));
|
||||
const intl = useIntl();
|
||||
const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href;
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<ColumnBackButton onClick={onBack} />
|
||||
<>
|
||||
<ColumnBackButton />
|
||||
|
||||
<div className='scrollable privacy-policy'>
|
||||
<div className='column-title'>
|
||||
|
@ -187,13 +173,9 @@ class Share extends PureComponent {
|
|||
</div>
|
||||
|
||||
<div className='onboarding__footer'>
|
||||
<button className='link-button' onClick={onBack}><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></button>
|
||||
<Link className='link-button' to='/start'><FormattedMessage id='onboarding.action.back' defaultMessage='Take me back' /></Link>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Share));
|
||||
};
|
||||
|
|
|
@ -166,7 +166,7 @@ class Footer extends ImmutablePureComponent {
|
|||
onClose();
|
||||
}
|
||||
|
||||
history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
this.props.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
};
|
||||
|
||||
render () {
|
||||
|
|
|
@ -18,6 +18,8 @@ import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlin
|
|||
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||
import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg';
|
||||
|
||||
import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg';
|
||||
import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -280,6 +282,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
let replyIcon;
|
||||
let replyIconComponent;
|
||||
|
||||
if (status.get('in_reply_to_id', null) === null) {
|
||||
replyIcon = 'reply';
|
||||
replyIconComponent = ReplyIcon;
|
||||
|
@ -290,21 +293,26 @@ class ActionBar extends PureComponent {
|
|||
|
||||
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
|
||||
|
||||
let reblogTitle;
|
||||
let reblogTitle, reblogIconComponent;
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
reblogIconComponent = RepeatIcon;
|
||||
} else if (reblogPrivate) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog_private);
|
||||
reblogIconComponent = RepeatPrivateIcon;
|
||||
} else {
|
||||
reblogTitle = intl.formatMessage(messages.cannot_reblog);
|
||||
reblogIconComponent = RepeatDisabledIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='detailed-status__action-bar'>
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div>
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/start' exact component={Onboarding} content={children} />
|
||||
<WrappedRoute path='/start' component={Onboarding} content={children} />
|
||||
<WrappedRoute path='/directory' component={Directory} content={children} />
|
||||
<WrappedRoute path={['/explore', '/search']} component={Explore} content={children} />
|
||||
<WrappedRoute path={['/publish', '/statuses/new']} component={Compose} content={children} />
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"account.badges.group": "Groep",
|
||||
"account.block": "Blokkeer @{name}",
|
||||
"account.block_domain": "Blokkeer domein {domain}",
|
||||
"account.block_short": "Blokkeer",
|
||||
"account.blocked": "Geblokkeer",
|
||||
"account.browse_more_on_origin_server": "Verken die oorspronklike profiel",
|
||||
"account.cancel_follow_request": "Herroep volgversoek",
|
||||
|
@ -45,6 +46,7 @@
|
|||
"account.posts_with_replies": "Plasings en antwoorde",
|
||||
"account.report": "Rapporteer @{name}",
|
||||
"account.requested": "Wag op goedkeuring. Klik om volgversoek te kanselleer",
|
||||
"account.requested_follow": "{name} het versoek om jou te volg",
|
||||
"account.share": "Deel @{name} se profiel",
|
||||
"account.show_reblogs": "Wys aangestuurde plasings van @{name}",
|
||||
"account.statuses_counter": "{count, plural, one {{counter} Plaas} other {{counter} Plasings}}",
|
||||
|
@ -82,6 +84,7 @@
|
|||
"column.community": "Plaaslike tydlyn",
|
||||
"column.directory": "Blaai deur profiele",
|
||||
"column.domain_blocks": "Geblokkeerde domeine",
|
||||
"column.favourites": "Gunstelinge",
|
||||
"column.follow_requests": "Volgversoeke",
|
||||
"column.home": "Tuis",
|
||||
"column.lists": "Lyste",
|
||||
|
@ -271,6 +274,7 @@
|
|||
"privacy.unlisted.short": "Ongelys",
|
||||
"privacy_policy.last_updated": "Laaste bywerking op {date}",
|
||||
"privacy_policy.title": "Privaatheidsbeleid",
|
||||
"regeneration_indicator.sublabel": "Jou tuis-voer word voorberei!",
|
||||
"reply_indicator.cancel": "Kanselleer",
|
||||
"report.placeholder": "Type or paste additional comments",
|
||||
"report.submit": "Submit report",
|
||||
|
|
|
@ -334,7 +334,6 @@
|
|||
"lists.search": "Buscar entre la chent a la quala sigues",
|
||||
"lists.subheading": "Las tuyas listas",
|
||||
"load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}",
|
||||
"loading_indicator.label": "Cargando...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Amaga la imachen} other {Amaga las imáchens}}",
|
||||
"moved_to_account_banner.text": "La tuya cuenta {disabledAccount} ye actualment deshabilitada perque t'has mudau a {movedToAccount}.",
|
||||
"mute_modal.duration": "Duración",
|
||||
|
|
|
@ -389,7 +389,6 @@
|
|||
"lists.search": "إبحث في قائمة الحسابات التي تُتابِعها",
|
||||
"lists.subheading": "قوائمك",
|
||||
"load_pending": "{count, plural, one {# عنصر جديد} other {# عناصر جديدة}}",
|
||||
"loading_indicator.label": "جارٍ التحميل…",
|
||||
"media_gallery.toggle_visible": "{number, plural, zero {} one {اخف الصورة} two {اخف الصورتين} few {اخف الصور} many {اخف الصور} other {اخف الصور}}",
|
||||
"moved_to_account_banner.text": "حسابك {disabledAccount} معطل حاليًا لأنك انتقلت إلى {movedToAccount}.",
|
||||
"mute_modal.duration": "المدة",
|
||||
|
|
|
@ -272,7 +272,6 @@
|
|||
"lists.search": "Buscar ente los perfiles que sigues",
|
||||
"lists.subheading": "Les tos llistes",
|
||||
"load_pending": "{count, plural, one {# elementu nuevu} other {# elementos nuevos}}",
|
||||
"loading_indicator.label": "Cargando…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Anubrir la imaxe} other {Anubrir les imáxenes}}",
|
||||
"mute_modal.duration": "Duración",
|
||||
"mute_modal.hide_notifications": "¿Quies anubrir los avisos d'esti perfil?",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Заблакіраваны",
|
||||
"account.browse_more_on_origin_server": "Глядзіце больш у арыгінальным профілі",
|
||||
"account.cancel_follow_request": "Скасаваць запыт на падпіску",
|
||||
"account.copy": "Скапіраваць спасылку на профіль",
|
||||
"account.direct": "Згадаць асабіста @{name}",
|
||||
"account.disable_notifications": "Не паведамляць мне пра публікацыі @{name}",
|
||||
"account.domain_blocked": "Дамен заблакаваны",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Адзначыць прачытаным",
|
||||
"conversation.open": "Прагледзець размову",
|
||||
"conversation.with": "З {names}",
|
||||
"copy_icon_button.copied": "Скапіявана ў буфер абмену",
|
||||
"copypaste.copied": "Скапіравана",
|
||||
"copypaste.copy_to_clipboard": "Капіраваць у буфер абмену",
|
||||
"directory.federated": "З вядомага федэсвету",
|
||||
|
@ -201,7 +203,7 @@
|
|||
"disabled_account_banner.text": "Ваш уліковы запіс {disabledAccount} часова адключаны.",
|
||||
"dismissable_banner.community_timeline": "Гэта самыя апошнія допісы ад людзей, уліковыя запісы якіх размяшчаюцца на {domain}.",
|
||||
"dismissable_banner.dismiss": "Адхіліць",
|
||||
"dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца прама зараз на гэтым і іншых серверах дэцэнтралізаванай сеткі.",
|
||||
"dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца цяпер на гэтым і іншых серверах дэцэнтралізаванай сеткі.",
|
||||
"dismissable_banner.explore_statuses": "Допісы з гэтага і іншых сервераў дэцэнтралізаванай сеткі, якія набіраюць папулярнасць прама зараз.",
|
||||
"dismissable_banner.explore_tags": "Гэтыя хэштэгі зараз набіраюць папулярнасць сярод людзей на гэтым і іншых серверах дэцэнтралізаванай сеткі",
|
||||
"dismissable_banner.public_timeline": "Гэта апошнія публічныя допісы людзей з усей сеткі, за якімі сочаць карыстальнікі {domain}.",
|
||||
|
@ -222,6 +224,7 @@
|
|||
"emoji_button.search_results": "Вынікі пошуку",
|
||||
"emoji_button.symbols": "Сімвалы",
|
||||
"emoji_button.travel": "Падарожжы і месцы",
|
||||
"empty_column.account_hides_collections": "Гэты карыстальнік вырашыў схаваць гэтую інфармацыю",
|
||||
"empty_column.account_suspended": "Уліковы запіс прыпынены",
|
||||
"empty_column.account_timeline": "Тут няма допісаў!",
|
||||
"empty_column.account_unavailable": "Профіль недаступны",
|
||||
|
@ -389,7 +392,7 @@
|
|||
"lists.search": "Шукайце сярод людзей, на якіх Вы падпісаны",
|
||||
"lists.subheading": "Вашыя спісы",
|
||||
"load_pending": "{count, plural, one {# новы элемент} few {# новыя элементы} many {# новых элементаў} other {# новых элементаў}}",
|
||||
"loading_indicator.label": "Загрузка...",
|
||||
"loading_indicator.label": "Загрузка…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Схаваць відарыс} other {Схаваць відарысы}}",
|
||||
"moved_to_account_banner.text": "Ваш уліковы запіс {disabledAccount} зараз адключаны таму што вы перанесены на {movedToAccount}.",
|
||||
"mute_modal.duration": "Працягласць",
|
||||
|
@ -478,10 +481,21 @@
|
|||
"onboarding.follows.empty": "На жаль, зараз немагчыма паказаць вынікі. Вы можаце паспрабаваць выкарыстоўваць пошук і праглядзець старонку агляду, каб знайсці людзей, на якіх можна падпісацца, або паўтарыце спробу пазней.",
|
||||
"onboarding.follows.lead": "Вы самі ствараеце свой хатні канал. Чым больш людзей вы падпішаце, тым больш актыўна і цікавей гэта будзе. Гэтыя профілі могуць стаць добрай адпраўной кропкай — вы заўсёды можаце адмяніць падпіску на іх пазней!",
|
||||
"onboarding.follows.title": "Папулярна на Mastodon",
|
||||
"onboarding.profile.discoverable": "Уключыць профіль і допісы ў алгарытмы рэкамендацый",
|
||||
"onboarding.profile.display_name": "Бачнае імя",
|
||||
"onboarding.profile.display_name_hint": "Ваша поўнае імя або ваш псеўданім…",
|
||||
"onboarding.profile.indexable": "Індэксаваць публічныя допісы ў пошукавых сістэмах",
|
||||
"onboarding.profile.lead": "Вы заўсёды можаце выканаць гэта пазней у Наладах, дзе даступна яшчэ больш параметраў.",
|
||||
"onboarding.profile.note": "Біяграфія",
|
||||
"onboarding.profile.note_hint": "Вы можаце @згадаць іншых людзей або выкарыстоўваць #хэштэгі…",
|
||||
"onboarding.profile.save_and_continue": "Захаваць і працягнуць",
|
||||
"onboarding.profile.title": "Налады профілю",
|
||||
"onboarding.profile.upload_avatar": "Загрузіць фота профілю",
|
||||
"onboarding.profile.upload_header": "Загрузіць шапку профілю",
|
||||
"onboarding.share.lead": "Дайце людзям ведаць, як яны могуць знайсці вас на Mastodon!",
|
||||
"onboarding.share.message": "Я {username} на #Mastodon! Сачыце за мной на {url}",
|
||||
"onboarding.share.next_steps": "Магчымыя наступныя крокі:",
|
||||
"onboarding.share.title": "Падзяліцеся сваім профілем",
|
||||
"onboarding.share.title": "Абагульце свой профіль",
|
||||
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
|
||||
"onboarding.start.skip": "Want to skip right ahead?",
|
||||
"onboarding.start.title": "Вы зрабілі гэта!",
|
||||
|
@ -492,7 +506,7 @@
|
|||
"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!",
|
||||
"onboarding.steps.share_profile.title": "Share your profile",
|
||||
"onboarding.steps.share_profile.title": "Абагульць ваш профіль у Mastodon",
|
||||
"onboarding.tips.2fa": "<strong>Ці вы ведаеце?</strong> Вы можаце абараніць свой уліковы запіс, усталяваўшы двухфактарную аўтэнтыфікацыю ў наладах уліковага запісу. Яна працуе з любой праграмай TOTP на ваш выбар, нумар тэлефона не патрэбны!",
|
||||
"onboarding.tips.accounts_from_other_servers": "<strong>Ці вы ведаеце?</strong> Паколькі Mastodon дэцэнтралізаваны, некаторыя профілі, якія вам трапляюцца, будуць размяшчацца на іншых серверах, адрозных ад вашага. І ўсё ж вы можаце бесперашкодна ўзаемадзейнічаць з імі! Іх сервер пазначаны ў другой палове імя карыстальніка!",
|
||||
"onboarding.tips.migration": "<strong>Ці вы ведаеце?</strong> Калі вы адчуваеце, што {domain} не з'яўляецца для вас лепшым выбарам у будучыні, вы можаце перайсці на іншы сервер Mastodon, не губляючы сваіх падпісчыкаў. Вы нават можаце стварыць свой уласны сервер!",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Резултати от търсене",
|
||||
"emoji_button.symbols": "Символи",
|
||||
"emoji_button.travel": "Пътуване и места",
|
||||
"empty_column.account_hides_collections": "Този потребител е избрал да не прави това сведение достъпно",
|
||||
"empty_column.account_suspended": "Спрян акаунт",
|
||||
"empty_column.account_timeline": "Тук няма публикации!",
|
||||
"empty_column.account_unavailable": "Профилът не е наличен",
|
||||
|
@ -389,7 +390,7 @@
|
|||
"lists.search": "Търсене измежду последваните",
|
||||
"lists.subheading": "Вашите списъци",
|
||||
"load_pending": "{count, plural, one {# нов елемент} other {# нови елемента}}",
|
||||
"loading_indicator.label": "Зареждане...",
|
||||
"loading_indicator.label": "Зареждане…",
|
||||
"media_gallery.toggle_visible": "Скриване на {number, plural, one {изображение} other {изображения}}",
|
||||
"moved_to_account_banner.text": "Вашият акаунт {disabledAccount} сега е изключен, защото се преместихте в {movedToAccount}.",
|
||||
"mute_modal.duration": "Времетраене",
|
||||
|
@ -478,6 +479,17 @@
|
|||
"onboarding.follows.empty": "За съжаление, в момента не могат да се показват резултати. Може да опитате да употребявате търсене или да прегледате страницата за изследване, за да намерите страница за последване, или да опитате пак по-късно.",
|
||||
"onboarding.follows.lead": "Може да бъдете куратор на началния си инфоканал. Последвайки повече хора, по-деен и по-интересен ще става. Тези профили може да са добра начална точка, от която винаги по-късно да спрете да следвате!",
|
||||
"onboarding.follows.title": "Популярно в Mastodon",
|
||||
"onboarding.profile.discoverable": "Включване на профила и публикации в алгоритмите за откриване",
|
||||
"onboarding.profile.display_name": "Името на показ",
|
||||
"onboarding.profile.display_name_hint": "Вашето пълно име или псевдоним…",
|
||||
"onboarding.profile.indexable": "Включване на обществени публикации в резултатите от търсене",
|
||||
"onboarding.profile.lead": "Винаги може да завършите това по-късно в настройките, където дори има повече възможности за настройване.",
|
||||
"onboarding.profile.note": "Биогр.",
|
||||
"onboarding.profile.note_hint": "Може да @споменавате други хора или #хаштагове…",
|
||||
"onboarding.profile.save_and_continue": "Запазване и продължаване",
|
||||
"onboarding.profile.title": "Настройване на профила",
|
||||
"onboarding.profile.upload_avatar": "Качване на снимка на профила",
|
||||
"onboarding.profile.upload_header": "Качване на заглавка на профила",
|
||||
"onboarding.share.lead": "Позволете на хората да знаят, че могат да ви намерят в Mastodon!",
|
||||
"onboarding.share.message": "Аз съм {username} в #Mastodon! Елате да ме последвате при {url}",
|
||||
"onboarding.share.next_steps": "Възможни следващи стъпки:",
|
||||
|
|
|
@ -314,7 +314,6 @@
|
|||
"lists.search": "যাদের অনুসরণ করেন তাদের ভেতরে খুঁজুন",
|
||||
"lists.subheading": "আপনার তালিকা",
|
||||
"load_pending": "{count, plural, one {# নতুন জিনিস} other {# নতুন জিনিস}}",
|
||||
"loading_indicator.label": "আসছে...",
|
||||
"media_gallery.toggle_visible": "দৃশ্যতার অবস্থা বদলান",
|
||||
"mute_modal.duration": "সময়কাল",
|
||||
"mute_modal.hide_notifications": "এই ব্যবহারকারীর প্রজ্ঞাপন বন্ধ করবেন ?",
|
||||
|
|
|
@ -329,7 +329,6 @@
|
|||
"lists.search": "Klask e-touez tud heuliet ganeoc'h",
|
||||
"lists.subheading": "Ho listennoù",
|
||||
"load_pending": "{count, plural, one {# dra nevez} other {# dra nevez}}",
|
||||
"loading_indicator.label": "O kargañ...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Kuzhat ar skeudenn} other {Kuzhat ar skeudenn}}",
|
||||
"mute_modal.duration": "Padelezh",
|
||||
"mute_modal.hide_notifications": "Kuzhat kemenadennoù eus an implijer-se ?",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Resultats de la cerca",
|
||||
"emoji_button.symbols": "Símbols",
|
||||
"emoji_button.travel": "Viatges i llocs",
|
||||
"empty_column.account_hides_collections": "Aquest usuari ha elegit no mostrar aquesta informació",
|
||||
"empty_column.account_suspended": "Compte suspès",
|
||||
"empty_column.account_timeline": "No hi ha tuts aquí!",
|
||||
"empty_column.account_unavailable": "Perfil no disponible",
|
||||
|
@ -389,7 +390,6 @@
|
|||
"lists.search": "Cerca entre les persones que segueixes",
|
||||
"lists.subheading": "Les teves llistes",
|
||||
"load_pending": "{count, plural, one {# element nou} other {# elements nous}}",
|
||||
"loading_indicator.label": "Es carrega...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Amaga la imatge} other {Amaga les imatges}}",
|
||||
"moved_to_account_banner.text": "El teu compte {disabledAccount} està desactivat perquè l'has mogut a {movedToAccount}.",
|
||||
"mute_modal.duration": "Durada",
|
||||
|
|
|
@ -342,7 +342,6 @@
|
|||
"lists.search": "بگەڕێ لەناو ئەو کەسانەی کە شوێنیان کەوتویت",
|
||||
"lists.subheading": "لیستەکانت",
|
||||
"load_pending": "{count, plural, one {# بەڕگەی نوێ} other {# بەڕگەی نوێ}}",
|
||||
"loading_indicator.label": "بارکردن...",
|
||||
"media_gallery.toggle_visible": "شاردنەوەی {number, plural, one {image} other {images}}",
|
||||
"moved_to_account_banner.text": "ئەکاونتەکەت {disabledAccount} لە ئێستادا لەکارخراوە چونکە تۆ چوویتە {movedToAccount}.",
|
||||
"mute_modal.duration": "ماوە",
|
||||
|
|
|
@ -236,7 +236,6 @@
|
|||
"lists.search": "Circà indè i vostr'abbunamenti",
|
||||
"lists.subheading": "E vo liste",
|
||||
"load_pending": "{count, plural, one {# entrata nova} other {# entrate nove}}",
|
||||
"loading_indicator.label": "Caricamentu...",
|
||||
"media_gallery.toggle_visible": "Piattà {number, plural, one {ritrattu} other {ritratti}}",
|
||||
"mute_modal.duration": "Durata",
|
||||
"mute_modal.hide_notifications": "Piattà nutificazione da st'utilizatore?",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Blokovaný",
|
||||
"account.browse_more_on_origin_server": "Více na původním profilu",
|
||||
"account.cancel_follow_request": "Zrušit žádost o sledování",
|
||||
"account.copy": "Kopírovat odkaz na profil",
|
||||
"account.direct": "Soukromě zmínit @{name}",
|
||||
"account.disable_notifications": "Přestat mě upozorňovat, když @{name} zveřejní příspěvek",
|
||||
"account.domain_blocked": "Doména blokována",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Označit jako přečtené",
|
||||
"conversation.open": "Zobrazit konverzaci",
|
||||
"conversation.with": "S {names}",
|
||||
"copy_icon_button.copied": "Zkopírováno do schránky",
|
||||
"copypaste.copied": "Zkopírováno",
|
||||
"copypaste.copy_to_clipboard": "Zkopírovat do schránky",
|
||||
"directory.federated": "Ze známého fedivesmíru",
|
||||
|
@ -222,6 +224,7 @@
|
|||
"emoji_button.search_results": "Výsledky hledání",
|
||||
"emoji_button.symbols": "Symboly",
|
||||
"emoji_button.travel": "Cestování a místa",
|
||||
"empty_column.account_hides_collections": "Tento uživatel se rozhodl nezveřejňovat tuto informaci",
|
||||
"empty_column.account_suspended": "Účet je pozastaven",
|
||||
"empty_column.account_timeline": "Nejsou tu žádné příspěvky!",
|
||||
"empty_column.account_unavailable": "Profil není dostupný",
|
||||
|
@ -385,7 +388,6 @@
|
|||
"lists.search": "Hledejte mezi lidmi, které sledujete",
|
||||
"lists.subheading": "Vaše seznamy",
|
||||
"load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}",
|
||||
"loading_indicator.label": "Načítání...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Skrýt obrázek} few {Skrýt obrázky} many {Skrýt obrázky} other {Skrýt obrázky}}",
|
||||
"moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálně deaktivován, protože jste se přesunul/a na {movedToAccount}.",
|
||||
"mute_modal.duration": "Trvání",
|
||||
|
|
|
@ -191,6 +191,7 @@
|
|||
"conversation.mark_as_read": "Nodi fel wedi'i ddarllen",
|
||||
"conversation.open": "Gweld sgwrs",
|
||||
"conversation.with": "Gyda {names}",
|
||||
"copy_icon_button.copied": "Copïwyd i'r clipfwrdd",
|
||||
"copypaste.copied": "Wedi ei gopïo",
|
||||
"copypaste.copy_to_clipboard": "Copïo i'r clipfwrdd",
|
||||
"directory.federated": "O'r ffedysawd cyfan",
|
||||
|
@ -390,7 +391,7 @@
|
|||
"lists.search": "Chwilio ymysg pobl rydych yn eu dilyn",
|
||||
"lists.subheading": "Eich rhestrau",
|
||||
"load_pending": "{count, plural, one {# eitem newydd} other {# eitem newydd}}",
|
||||
"loading_indicator.label": "Llwytho...",
|
||||
"loading_indicator.label": "Yn llwytho…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Cuddio delwedd} other {Cuddio delwedd}}",
|
||||
"moved_to_account_banner.text": "Ar hyn y bryd, mae eich cyfrif {disabledAccount} wedi ei analluogi am i chi symud i {movedToAccount}.",
|
||||
"mute_modal.duration": "Hyd",
|
||||
|
@ -479,6 +480,11 @@
|
|||
"onboarding.follows.empty": "Yn anffodus, nid oes modd dangos unrhyw ganlyniadau ar hyn o bryd. Gallwch geisio defnyddio chwilio neu bori'r dudalen archwilio i ddod o hyd i bobl i'w dilyn, neu ceisio eto yn nes ymlaen.",
|
||||
"onboarding.follows.lead": "Rydych chi'n curadu eich ffrwd gartref eich hun. Po fwyaf o bobl y byddwch chi'n eu dilyn, y mwyaf egnïol a diddorol fydd hi. Gall y proffiliau hyn fod yn fan cychwyn da - gallwch chi bob amser eu dad-ddilyn yn nes ymlaen:",
|
||||
"onboarding.follows.title": "Yn boblogaidd ar Mastodon",
|
||||
"onboarding.profile.display_name_hint": "Eich enw llawn neu'ch enw hwyl…",
|
||||
"onboarding.profile.note": "Bywgraffiad",
|
||||
"onboarding.profile.note_hint": "Gallwch @grybwyll pobl eraill neu #hashnodau…",
|
||||
"onboarding.profile.save_and_continue": "Cadw a pharhau",
|
||||
"onboarding.profile.title": "Gosodiad proffil",
|
||||
"onboarding.share.lead": "Cofiwch ddweud wrth bobl sut y gallan nhw ddod o hyd i chi ar Mastodon!",
|
||||
"onboarding.share.message": "Fi yw {username} ar #Mastodon! Dewch i'm dilyn i yn {url}",
|
||||
"onboarding.share.next_steps": "Camau nesaf posib:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Blokeret",
|
||||
"account.browse_more_on_origin_server": "Se mere på den oprindelige profil",
|
||||
"account.cancel_follow_request": "Annullér anmodning om at følge",
|
||||
"account.copy": "Kopiér link til profil",
|
||||
"account.direct": "Privat omtale @{name}",
|
||||
"account.disable_notifications": "Advisér mig ikke længere, når @{name} poster",
|
||||
"account.domain_blocked": "Domæne blokeret",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Markér som læst",
|
||||
"conversation.open": "Vis samtale",
|
||||
"conversation.with": "Med {names}",
|
||||
"copy_icon_button.copied": "Kopieret til udklipsholderen",
|
||||
"copypaste.copied": "Kopieret",
|
||||
"copypaste.copy_to_clipboard": "Kopiér til udklipsholder",
|
||||
"directory.federated": "Fra kendt fedivers",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Søg blandt personer, som følges",
|
||||
"lists.subheading": "Dine lister",
|
||||
"load_pending": "{count, plural, one {# nyt emne} other {# nye emner}}",
|
||||
"loading_indicator.label": "Indlæser...",
|
||||
"loading_indicator.label": "Indlæser…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Skjul billede} other {Skjul billeder}}",
|
||||
"moved_to_account_banner.text": "Din konto {disabledAccount} er pt. deaktiveret, da du flyttede til {movedToAccount}.",
|
||||
"mute_modal.duration": "Varighed",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Ingen resultater tilgængelige pt. Prøv at bruge søgning eller gennemse siden for at finde personer at følge, eller forsøg igen senere.",
|
||||
"onboarding.follows.lead": "Man kurerer sin eget hjemme-feed. Jo flere personer man følger, des mere aktiv og interessant vil det være. Disse profiler kan være et godt udgangspunkt – de kan altid fjernes senere!",
|
||||
"onboarding.follows.title": "Populært på Mastodon",
|
||||
"onboarding.profile.discoverable": "Fremhæv profil og indlæg i detekteringsalgoritmer",
|
||||
"onboarding.profile.display_name": "Visningsnavn",
|
||||
"onboarding.profile.display_name_hint": "Fulde navn eller dit sjove navn…",
|
||||
"onboarding.profile.indexable": "Medtag offentlige indlæg i søgeresultater",
|
||||
"onboarding.profile.lead": "Dette kan altid færdiggøres senere i indstillingerne, hvor endnu flere tilpasningsmuligheder forefindes.",
|
||||
"onboarding.profile.note": "Bio",
|
||||
"onboarding.profile.note_hint": "Man kan @omtale andre personer eller #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Gem og fortsæt",
|
||||
"onboarding.profile.title": "Profilopsætning",
|
||||
"onboarding.profile.upload_avatar": "Upload profilbillede",
|
||||
"onboarding.profile.upload_header": "Upload profiloverskrift",
|
||||
"onboarding.share.lead": "Lad folk vide, hvordan de kan finde dig på Mastodon!",
|
||||
"onboarding.share.message": "Jeg er {username} på #Mastodon! Følg mig på {url}",
|
||||
"onboarding.share.next_steps": "Mulige næste trin:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Blockiert",
|
||||
"account.browse_more_on_origin_server": "Mehr auf dem Originalprofil durchsuchen",
|
||||
"account.cancel_follow_request": "Folgeanfrage zurückziehen",
|
||||
"account.copy": "Link zum Profil kopieren",
|
||||
"account.direct": "@{name} privat erwähnen",
|
||||
"account.disable_notifications": "Höre auf mich zu benachrichtigen wenn @{name} etwas postet",
|
||||
"account.domain_blocked": "Domain versteckt",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Als gelesen markieren",
|
||||
"conversation.open": "Unterhaltung anzeigen",
|
||||
"conversation.with": "Mit {names}",
|
||||
"copy_icon_button.copied": "In die Zwischenablage kopiert",
|
||||
"copypaste.copied": "Kopiert",
|
||||
"copypaste.copy_to_clipboard": "In die Zwischenablage kopieren",
|
||||
"directory.federated": "Aus bekanntem Fediverse",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Suche nach Leuten, denen du folgst",
|
||||
"lists.subheading": "Deine Listen",
|
||||
"load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}",
|
||||
"loading_indicator.label": "Wird geladen …",
|
||||
"loading_indicator.label": "Wird geladen …",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Medium ausblenden} other {Medien ausblenden}}",
|
||||
"moved_to_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert, weil du zu {movedToAccount} umgezogen bist.",
|
||||
"mute_modal.duration": "Dauer",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Bedauerlicherweise können aktuell keine Ergebnisse angezeigt werden. Du kannst die Suche verwenden oder den Reiter „Entdecken“ auswählen, um neue Leute zum Folgen zu finden – oder du versuchst es später erneut.",
|
||||
"onboarding.follows.lead": "Deine Startseite ist der primäre Anlaufpunkt, um Mastodon zu erleben. Je mehr Profilen du folgst, umso aktiver und interessanter wird sie. Damit du direkt loslegen kannst, gibt es hier ein paar Vorschläge:",
|
||||
"onboarding.follows.title": "Personalisiere deine Startseite",
|
||||
"onboarding.profile.discoverable": "Profil und Beiträge in Suchalgorithmen berücksichtigen",
|
||||
"onboarding.profile.display_name": "Anzeigename",
|
||||
"onboarding.profile.display_name_hint": "Dein richtiger Name oder dein Fantasiename …",
|
||||
"onboarding.profile.indexable": "Öffentliche Beiträge in die Suchergebnisse einbeziehen",
|
||||
"onboarding.profile.lead": "Du kannst das später in den Einstellungen vervollständigen, wo noch mehr Anpassungsmöglichkeiten zur Verfügung stehen.",
|
||||
"onboarding.profile.note": "Über mich",
|
||||
"onboarding.profile.note_hint": "Du kannst andere @Profile erwähnen oder #Hashtags verwenden …",
|
||||
"onboarding.profile.save_and_continue": "Speichern und fortsetzen",
|
||||
"onboarding.profile.title": "Profil einrichten",
|
||||
"onboarding.profile.upload_avatar": "Profilbild hochladen",
|
||||
"onboarding.profile.upload_header": "Titelbild hochladen",
|
||||
"onboarding.share.lead": "Lass die Leute wissen, wie sie dich auf Mastodon finden können!",
|
||||
"onboarding.share.message": "Ich bin {username} auf #Mastodon! Folge mir auf {url}",
|
||||
"onboarding.share.next_steps": "Mögliche nächste Schritte:",
|
||||
|
|
|
@ -345,7 +345,6 @@
|
|||
"lists.search": "Αναζήτησε μεταξύ των ανθρώπων που ακουλουθείς",
|
||||
"lists.subheading": "Οι λίστες σου",
|
||||
"load_pending": "{count, plural, one {# νέο στοιχείο} other {# νέα στοιχεία}}",
|
||||
"loading_indicator.label": "Φορτώνει...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Απόκρυψη εικόνας} other {Απόκρυψη εικόνων}}",
|
||||
"moved_to_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προσωρινά απενεργοποιημένος επειδή μεταφέρθηκες στον {movedToAccount}.",
|
||||
"mute_modal.duration": "Διάρκεια",
|
||||
|
|
|
@ -389,7 +389,7 @@
|
|||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"load_pending": "{count, plural, one {# new item} other {# new items}}",
|
||||
"loading_indicator.label": "Loading...",
|
||||
"loading_indicator.label": "Loading…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
|
||||
"moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
|
||||
"mute_modal.duration": "Duration",
|
||||
|
@ -478,6 +478,17 @@
|
|||
"onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.",
|
||||
"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": "Personalize your home feed",
|
||||
"onboarding.profile.discoverable": "Feature profile and posts in discovery algorithms",
|
||||
"onboarding.profile.display_name": "Display name",
|
||||
"onboarding.profile.display_name_hint": "Your full name or your fun name…",
|
||||
"onboarding.profile.indexable": "Include public posts in search results",
|
||||
"onboarding.profile.lead": "You can always complete this later in the settings, where even more customisation options are available.",
|
||||
"onboarding.profile.note": "Bio",
|
||||
"onboarding.profile.note_hint": "You can @mention other people or #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Save and continue",
|
||||
"onboarding.profile.title": "Profile setup",
|
||||
"onboarding.profile.upload_avatar": "Upload profile picture",
|
||||
"onboarding.profile.upload_header": "Upload profile header",
|
||||
"onboarding.share.lead": "Let people know how they can find you on Mastodon!",
|
||||
"onboarding.share.message": "I'm {username} on #Mastodon! Come follow me at {url}",
|
||||
"onboarding.share.next_steps": "Possible next steps:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Blocked",
|
||||
"account.browse_more_on_origin_server": "Browse more on the original profile",
|
||||
"account.cancel_follow_request": "Cancel follow",
|
||||
"account.copy": "Copy link to profile",
|
||||
"account.direct": "Privately mention @{name}",
|
||||
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
||||
"account.domain_blocked": "Domain blocked",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Mark as read",
|
||||
"conversation.open": "View conversation",
|
||||
"conversation.with": "With {names}",
|
||||
"copy_icon_button.copied": "Copied to clipboard",
|
||||
"copypaste.copied": "Copied",
|
||||
"copypaste.copy_to_clipboard": "Copy to clipboard",
|
||||
"directory.federated": "From known fediverse",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"load_pending": "{count, plural, one {# new item} other {# new items}}",
|
||||
"loading_indicator.label": "Loading...",
|
||||
"loading_indicator.label": "Loading…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
|
||||
"moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
|
||||
"mute_modal.duration": "Duration",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.",
|
||||
"onboarding.follows.lead": "Your home feed is the primary way to experience Mastodon. The more people you follow, the more active and interesting it will be. To get you started, here are some suggestions:",
|
||||
"onboarding.follows.title": "Personalize your home feed",
|
||||
"onboarding.profile.discoverable": "Make my profile discoverable",
|
||||
"onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.",
|
||||
"onboarding.profile.display_name": "Display name",
|
||||
"onboarding.profile.display_name_hint": "Your full name or your fun name…",
|
||||
"onboarding.profile.lead": "You can always complete this later in the settings, where even more customization options are available.",
|
||||
"onboarding.profile.note": "Bio",
|
||||
"onboarding.profile.note_hint": "You can @mention other people or #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Save and continue",
|
||||
"onboarding.profile.title": "Profile setup",
|
||||
"onboarding.profile.upload_avatar": "Upload profile picture",
|
||||
"onboarding.profile.upload_header": "Upload profile header",
|
||||
"onboarding.share.lead": "Let people know how they can find you on Mastodon!",
|
||||
"onboarding.share.message": "I'm {username} on #Mastodon! Come follow me at {url}",
|
||||
"onboarding.share.next_steps": "Possible next steps:",
|
||||
|
@ -522,6 +535,7 @@
|
|||
"privacy.unlisted.short": "Unlisted",
|
||||
"privacy_policy.last_updated": "Last updated {date}",
|
||||
"privacy_policy.title": "Privacy Policy",
|
||||
"recommended": "Recommended",
|
||||
"refresh": "Refresh",
|
||||
"regeneration_indicator.label": "Loading…",
|
||||
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
|
||||
|
|
|
@ -365,7 +365,6 @@
|
|||
"lists.search": "Serĉi inter la homoj, kiujn vi sekvas",
|
||||
"lists.subheading": "Viaj listoj",
|
||||
"load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}",
|
||||
"loading_indicator.label": "Ŝargado…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Kaŝi la bildon} other {Kaŝi la bildojn}}",
|
||||
"moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.",
|
||||
"mute_modal.duration": "Daŭro",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Bloqueado",
|
||||
"account.browse_more_on_origin_server": "Explorar más en el perfil original",
|
||||
"account.cancel_follow_request": "Dejar de seguir",
|
||||
"account.copy": "Copiar enlace al perfil",
|
||||
"account.direct": "Mención privada a @{name}",
|
||||
"account.disable_notifications": "Dejar de notificarme cuando @{name} envíe mensajes",
|
||||
"account.domain_blocked": "Dominio bloqueado",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Marcar como leída",
|
||||
"conversation.open": "Ver conversación",
|
||||
"conversation.with": "Con {names}",
|
||||
"copy_icon_button.copied": "Copiado en el portapapeles",
|
||||
"copypaste.copied": "Copiado",
|
||||
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
|
||||
"directory.federated": "Desde fediverso conocido",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Buscar entre la gente que seguís",
|
||||
"lists.subheading": "Tus listas",
|
||||
"load_pending": "{count, plural, one {# elemento nuevo} other {# elementos nuevos}}",
|
||||
"loading_indicator.label": "Cargando...",
|
||||
"loading_indicator.label": "Cargando…",
|
||||
"media_gallery.toggle_visible": "Ocultar {number, plural, one {imagen} other {imágenes}}",
|
||||
"moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te mudaste a {movedToAccount}.",
|
||||
"mute_modal.duration": "Duración",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Podés intentar usar la búsqueda o navegar por la página de exploración para encontrar cuentas a las que seguir, o intentarlo de nuevo más tarde.",
|
||||
"onboarding.follows.lead": "Tu línea temporal de inicio es la forma principal de experimentar Mastodon. Cuanta más cuentas sigás, más activa e interesante será. Para empezar, acá tenés algunas sugerencias:",
|
||||
"onboarding.follows.title": "Personalizá tu línea de tiempo principal",
|
||||
"onboarding.profile.discoverable": "Destacar perfil y mensajes en algoritmos de descubrimiento",
|
||||
"onboarding.profile.display_name": "Nombre para mostrar",
|
||||
"onboarding.profile.display_name_hint": "Tu nombre completo o tu pseudónimo…",
|
||||
"onboarding.profile.indexable": "Incluir mensajes públicos en resultados de búsqueda",
|
||||
"onboarding.profile.lead": "Siempre podés completar esto más tarde en la configuración, donde hay disponibles más opciones de personalización.",
|
||||
"onboarding.profile.note": "Biografía",
|
||||
"onboarding.profile.note_hint": "Podés @mencionar otras cuentas o usar #etiquetas…",
|
||||
"onboarding.profile.save_and_continue": "Guardar y continuar",
|
||||
"onboarding.profile.title": "Configuración del perfil",
|
||||
"onboarding.profile.upload_avatar": "Subir avatar",
|
||||
"onboarding.profile.upload_header": "Subir cabecera",
|
||||
"onboarding.share.lead": "¡Decile a la gente cómo te pueden encontrar en Mastodon!",
|
||||
"onboarding.share.message": "¡En #Mastodon soy «{username}»! Podés seguirme desde {url}",
|
||||
"onboarding.share.next_steps": "Posibles próximos pasos:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Bloqueado",
|
||||
"account.browse_more_on_origin_server": "Ver más en el perfil original",
|
||||
"account.cancel_follow_request": "Retirar solicitud de seguimiento",
|
||||
"account.copy": "Copiar enlace al perfil",
|
||||
"account.direct": "Mención privada @{name}",
|
||||
"account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo",
|
||||
"account.domain_blocked": "Dominio oculto",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Marcar como leído",
|
||||
"conversation.open": "Ver conversación",
|
||||
"conversation.with": "Con {names}",
|
||||
"copy_icon_button.copied": "Copiado al portapapeles",
|
||||
"copypaste.copied": "Copiado",
|
||||
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
|
||||
"directory.federated": "Desde el fediverso conocido",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar gente a la que seguir, o inténtalo de nuevo más tarde.",
|
||||
"onboarding.follows.lead": "Tienes que personalizar tu inicio. Cuantas más personas sigas, más activo e interesante será. Estos perfiles pueden ser un buen punto de partida, ¡pero siempre puedes dejar de seguirlos más adelante!",
|
||||
"onboarding.follows.title": "Popular en Mastodon",
|
||||
"onboarding.profile.discoverable": "Destacar el perfil y las publicaciones en el algoritmo de descubrimiento",
|
||||
"onboarding.profile.display_name": "Nombre a mostrar",
|
||||
"onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…",
|
||||
"onboarding.profile.indexable": "Incluir publicaciones públicas en los resultados de búsqueda",
|
||||
"onboarding.profile.lead": "Siempre puedes completar esto más tarde en los ajustes, donde hay aún más opciones de personalización disponibles.",
|
||||
"onboarding.profile.note": "Biografía",
|
||||
"onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Guardar y continuar",
|
||||
"onboarding.profile.title": "Configuración del perfil",
|
||||
"onboarding.profile.upload_avatar": "Subir foto de perfil",
|
||||
"onboarding.profile.upload_header": "Subir foto de cabecera",
|
||||
"onboarding.share.lead": "¡Dile a la gente cómo te pueden encontrar en Mastodon!",
|
||||
"onboarding.share.message": "¡Soy {username} en #Mastodon! Ven a seguirme en {url}",
|
||||
"onboarding.share.next_steps": "Posibles siguientes pasos:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Bloqueado",
|
||||
"account.browse_more_on_origin_server": "Ver más en el perfil original",
|
||||
"account.cancel_follow_request": "Retirar solicitud de seguimiento",
|
||||
"account.copy": "Copiar enlace al perfil",
|
||||
"account.direct": "Mención privada a @{name}",
|
||||
"account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo",
|
||||
"account.domain_blocked": "Dominio bloqueado",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Marcar como leído",
|
||||
"conversation.open": "Ver conversación",
|
||||
"conversation.with": "Con {names}",
|
||||
"copy_icon_button.copied": "Copiado al portapapeles",
|
||||
"copypaste.copied": "Copiado",
|
||||
"copypaste.copy_to_clipboard": "Copiar al portapapeles",
|
||||
"directory.federated": "Desde el fediverso conocido",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Desafortunadamente, no se pueden mostrar resultados en este momento. Puedes intentar usar la búsqueda o navegar por la página de exploración para encontrar personas a las que seguir, o inténtalo de nuevo más tarde.",
|
||||
"onboarding.follows.lead": "Tu línea de inicio es la forma principal de experimentar Mastodon. Cuanta más personas sigas, más activa e interesante será. Para empezar, aquí hay algunas sugerencias:",
|
||||
"onboarding.follows.title": "Personaliza tu línea de inicio",
|
||||
"onboarding.profile.discoverable": "Destacar perfil y publicaciones en algoritmos de descubrimiento",
|
||||
"onboarding.profile.display_name": "Nombre para mostrar",
|
||||
"onboarding.profile.display_name_hint": "Tu nombre completo o tu apodo…",
|
||||
"onboarding.profile.indexable": "Incluir publicaciones públicas en los resultados de búsqueda",
|
||||
"onboarding.profile.lead": "Siempre puedes completar esto más tarde en los ajustes, donde hay aún más opciones de personalización disponibles.",
|
||||
"onboarding.profile.note": "Biografía",
|
||||
"onboarding.profile.note_hint": "Puedes @mencionar a otras personas o #etiquetas…",
|
||||
"onboarding.profile.save_and_continue": "Guardar y continuar",
|
||||
"onboarding.profile.title": "Configuración del perfil",
|
||||
"onboarding.profile.upload_avatar": "Subir foto de perfil",
|
||||
"onboarding.profile.upload_header": "Subir encabezado de perfil",
|
||||
"onboarding.share.lead": "¡Cuéntale a otras personas cómo te pueden encontrar en Mastodon!",
|
||||
"onboarding.share.message": "¡Soy {username} en #Mastodon! Ven a seguirme en {url}",
|
||||
"onboarding.share.next_steps": "Posibles siguientes pasos:",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Otsitulemused",
|
||||
"emoji_button.symbols": "Sümbolid",
|
||||
"emoji_button.travel": "Reisimine & kohad",
|
||||
"empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks",
|
||||
"empty_column.account_suspended": "Konto kustutatud",
|
||||
"empty_column.account_timeline": "Siin postitusi ei ole!",
|
||||
"empty_column.account_unavailable": "Profiil pole saadaval",
|
||||
|
@ -389,7 +390,6 @@
|
|||
"lists.search": "Otsi enda jälgitavate inimeste hulgast",
|
||||
"lists.subheading": "Sinu nimekirjad",
|
||||
"load_pending": "{count, plural, one {# uus kirje} other {# uut kirjet}}",
|
||||
"loading_indicator.label": "Laeb..",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Varja pilt} other {Varja pildid}}",
|
||||
"moved_to_account_banner.text": "Kontot {disabledAccount} ei ole praegu võimalik kasutada, sest kolisid kontole {movedToAccount}.",
|
||||
"mute_modal.duration": "Kestus",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Blokeatuta",
|
||||
"account.browse_more_on_origin_server": "Arakatu gehiago jatorrizko profilean",
|
||||
"account.cancel_follow_request": "Baztertu jarraitzeko eskaera",
|
||||
"account.copy": "Kopiatu profilerako esteka",
|
||||
"account.direct": "Aipatu pribatuki @{name}",
|
||||
"account.disable_notifications": "Utzi jakinarazteari @{name} erabiltzailearen bidalketetan",
|
||||
"account.domain_blocked": "Ezkutatutako domeinua",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Markatu irakurrita bezala",
|
||||
"conversation.open": "Ikusi elkarrizketa",
|
||||
"conversation.with": "Hauekin: {names}",
|
||||
"copy_icon_button.copied": "Arbelera kopiatu da",
|
||||
"copypaste.copied": "Kopiatuta",
|
||||
"copypaste.copy_to_clipboard": "Kopiatu arbelera",
|
||||
"directory.federated": "Fedibertso ezagunekoak",
|
||||
|
@ -389,8 +391,8 @@
|
|||
"lists.replies_policy.title": "Erakutsi erantzunak:",
|
||||
"lists.search": "Bilatu jarraitzen dituzun pertsonen artean",
|
||||
"lists.subheading": "Zure zerrendak",
|
||||
"load_pending": "{count, plural, one {eleentuberri #} other {# elementu berri}}",
|
||||
"loading_indicator.label": "Kargatzen...",
|
||||
"load_pending": "{count, plural, one {elementu berri #} other {# elementu berri}}",
|
||||
"loading_indicator.label": "Kargatzen…",
|
||||
"media_gallery.toggle_visible": "Txandakatu ikusgaitasuna",
|
||||
"moved_to_account_banner.text": "Zure {disabledAccount} kontua desgaituta dago une honetan, {movedToAccount} kontura aldatu zinelako.",
|
||||
"mute_modal.duration": "Iraupena",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Zoritxarrez, ezin da emaitzik erakutsi orain. Bilaketa erabil dezakezu edo Arakatu orrian jendea bilatu jarraitzeko, edo saiatu geroago.",
|
||||
"onboarding.follows.lead": "Hasierako orria zuk pertsonalizatzen duzu. Gero eta jende gehiagori jarraitu, orduan eta aktibo eta interesgarriago izango da. Profil hauek egokiak izan daitezke hasteko, beti ere, geroago jarraitzeari utz diezazkiekezu!",
|
||||
"onboarding.follows.title": "Mastodonen pil-pilean",
|
||||
"onboarding.profile.discoverable": "Ezagutarazi profila eta bidalketak bilaketa algoritmoetan",
|
||||
"onboarding.profile.display_name": "Bistaratzeko izena",
|
||||
"onboarding.profile.display_name_hint": "Zure izena edo ezizena…",
|
||||
"onboarding.profile.indexable": "Gehitu argitalpen publikoak bilaketa-emaitzetan",
|
||||
"onboarding.profile.lead": "Geroagoago bete daiteke konfigurazioan, non pertsonalizatzeko aukera gehiago dauden.",
|
||||
"onboarding.profile.note": "Biografia",
|
||||
"onboarding.profile.note_hint": "Beste pertsona batzuk @aipa ditzakezu edo #traolak erabili…",
|
||||
"onboarding.profile.save_and_continue": "Gorde eta jarraitu",
|
||||
"onboarding.profile.title": "Profilaren konfigurazioa",
|
||||
"onboarding.profile.upload_avatar": "Igo profilaren irudia",
|
||||
"onboarding.profile.upload_header": "Igo profilaren goiburua",
|
||||
"onboarding.share.lead": "Esan jendeari nola aurki zaitzaketen Mastodonen!",
|
||||
"onboarding.share.message": "{username} naiz #Mastodon-en! Jarrai nazazu hemen: {url}",
|
||||
"onboarding.share.next_steps": "Hurrengo urrats posibleak:",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "نتایج جستوجو",
|
||||
"emoji_button.symbols": "نمادها",
|
||||
"emoji_button.travel": "سفر و مکان",
|
||||
"empty_column.account_hides_collections": "کاربر خواسته که این اطّلاعات در دسترس نباشند",
|
||||
"empty_column.account_suspended": "حساب معلق شد",
|
||||
"empty_column.account_timeline": "هیچ فرستهای اینجا نیست!",
|
||||
"empty_column.account_unavailable": "نمایهٔ موجود نیست",
|
||||
|
@ -358,13 +359,13 @@
|
|||
"keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
|
||||
"keyboard_shortcuts.reply": "پاسخ به فرسته",
|
||||
"keyboard_shortcuts.requests": "گشودن سیاههٔ درخواستهای پیگیری",
|
||||
"keyboard_shortcuts.search": "تمرکز روی جستوجو",
|
||||
"keyboard_shortcuts.search": "تمرکز روی نوار جستوجو",
|
||||
"keyboard_shortcuts.spoilers": "نمایش/نهفتن زمینهٔ هشدار محتوا",
|
||||
"keyboard_shortcuts.start": "گشودن ستون «آغاز کنید»",
|
||||
"keyboard_shortcuts.toggle_hidden": "نمایش/نهفتن نوشتهٔ پشت هشدار محتوا",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "نمایش/نهفتن رسانه",
|
||||
"keyboard_shortcuts.toot": "شروع یک فرستهٔ جدید",
|
||||
"keyboard_shortcuts.unfocus": "برداشتن تمرکز از نوشتن/جستوجو",
|
||||
"keyboard_shortcuts.unfocus": "برداشتن تمرکز از ناحیهٔ نوشتن یا جستوجو",
|
||||
"keyboard_shortcuts.up": "بالا بردن در سیاهه",
|
||||
"lightbox.close": "بستن",
|
||||
"lightbox.compress": "فشردهسازی جعبهٔ نمایش تصویر",
|
||||
|
@ -389,7 +390,7 @@
|
|||
"lists.search": "جستوجو بین کسانی که پیگرفتهاید",
|
||||
"lists.subheading": "سیاهههایتان",
|
||||
"load_pending": "{count, plural, one {# مورد جدید} other {# مورد جدید}}",
|
||||
"loading_indicator.label": "بار کردن…",
|
||||
"loading_indicator.label": "در حال بارگذاری…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {نهفتن تصویر} other {نهفتن تصاویر}}",
|
||||
"moved_to_account_banner.text": "حسابتان {disabledAccount} اکنون از کار افتاده؛ چرا که به {movedToAccount} منتقل شدید.",
|
||||
"mute_modal.duration": "مدت زمان",
|
||||
|
@ -478,6 +479,13 @@
|
|||
"onboarding.follows.empty": "متأسفانه هماکنون نتیجهای قابل نمایش نیست. میتوانید استفاده از جستوجو یا مرور صفحهٔ کاوش را برای یافتن افرادی برای پیگیری آزموده یا دوباره تلاش کنید.",
|
||||
"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": "Popular on Mastodon",
|
||||
"onboarding.profile.discoverable": "معرفی نمایه و فرستهها در الگوریتمهای کشف",
|
||||
"onboarding.profile.display_name": "نام نمایشی",
|
||||
"onboarding.profile.display_name_hint": "نام کامل یا نام باحالتان…",
|
||||
"onboarding.profile.note": "درباره شما",
|
||||
"onboarding.profile.note_hint": "میتوانید افراد دیگر را @نامبردن یا #برچسب بزنید…",
|
||||
"onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده",
|
||||
"onboarding.profile.title": "تنظیم نمایه",
|
||||
"onboarding.share.lead": "بگذارید افراد بدانند چگونه میتوانند در ماستادون بیابندتان!",
|
||||
"onboarding.share.message": "من {username} روی #ماستودون هستم! مرا در {url} پیبگیرید",
|
||||
"onboarding.share.next_steps": "گامهای ممکن بعدی:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Estetty",
|
||||
"account.browse_more_on_origin_server": "Selaile lisää alkuperäisellä palvelimella",
|
||||
"account.cancel_follow_request": "Peruuta seurantapyyntö",
|
||||
"account.copy": "Kopioi profiilin linkki",
|
||||
"account.direct": "Mainitse @{name} yksityisesti",
|
||||
"account.disable_notifications": "Lopeta ilmoittamasta minulle, kun @{name} julkaisee",
|
||||
"account.domain_blocked": "Verkkotunnus estetty",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Merkitse luetuksi",
|
||||
"conversation.open": "Näytä keskustelu",
|
||||
"conversation.with": "{names} kanssa",
|
||||
"copy_icon_button.copied": "Kopioitu leikepöydälle",
|
||||
"copypaste.copied": "Kopioitu",
|
||||
"copypaste.copy_to_clipboard": "Kopioi leikepöydälle",
|
||||
"directory.federated": "Koko tunnettu fediversumi",
|
||||
|
@ -222,6 +224,7 @@
|
|||
"emoji_button.search_results": "Hakutulokset",
|
||||
"emoji_button.symbols": "Symbolit",
|
||||
"emoji_button.travel": "Matkailu ja paikat",
|
||||
"empty_column.account_hides_collections": "Käyttäjä on päättänyt olla julkaisematta näitä tietoja",
|
||||
"empty_column.account_suspended": "Tili jäädytetty",
|
||||
"empty_column.account_timeline": "Ei viestejä täällä.",
|
||||
"empty_column.account_unavailable": "Profiilia ei löydy",
|
||||
|
@ -389,7 +392,7 @@
|
|||
"lists.search": "Etsi seuraamistasi henkilöistä",
|
||||
"lists.subheading": "Omat listasi",
|
||||
"load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}",
|
||||
"loading_indicator.label": "Ladataan...",
|
||||
"loading_indicator.label": "Ladataan…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Piilota kuva} other {Piilota kuvat}}",
|
||||
"moved_to_account_banner.text": "Tilisi {disabledAccount} on tällä hetkellä poissa käytöstä, koska teit siirron tiliin {movedToAccount}.",
|
||||
"mute_modal.duration": "Kesto",
|
||||
|
@ -478,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Valitettavasti tuloksia ei voida näyttää juuri nyt. Voit kokeilla hakua tai selata tutustumissivua löytääksesi seurattavaa tai yrittää myöhemmin uudelleen.",
|
||||
"onboarding.follows.lead": "Kokoat oman kotisyötteesi itse. Mitä enemmän ihmisiä seuraat, sitä aktiivisempi ja kiinnostavampi syöte on. Nämä profiilit voivat olla alkuun hyvä lähtökohta — voit aina lopettaa niiden seuraamisen myöhemmin!",
|
||||
"onboarding.follows.title": "Mukauta kotisyötettäsi",
|
||||
"onboarding.profile.discoverable": "Pidä profiilia ja julkaisuja esillä löytämisalgoritmeissa",
|
||||
"onboarding.profile.display_name": "Näyttönimi",
|
||||
"onboarding.profile.display_name_hint": "Koko nimesi tai lempinimesi…",
|
||||
"onboarding.profile.indexable": "Sisällytä julkiset julkaisut hakutuloksiin",
|
||||
"onboarding.profile.lead": "Voit viimeistellä tämän milloin tahansa asetuksissa, jossa on saatavilla vielä enemmän mukautusvalintoja.",
|
||||
"onboarding.profile.note": "Elämäkerta",
|
||||
"onboarding.profile.note_hint": "Voit @mainita muita käyttäjiä tai #aihetunnisteita…",
|
||||
"onboarding.profile.save_and_continue": "Tallenna ja jatka",
|
||||
"onboarding.profile.title": "Profiilin määritys",
|
||||
"onboarding.profile.upload_avatar": "Lataa profiilikuva",
|
||||
"onboarding.profile.upload_header": "Lataa profiilin otsakekuva",
|
||||
"onboarding.share.lead": "Kerro ihmisille, kuinka he voivat löytää sinut Mastodonista!",
|
||||
"onboarding.share.message": "Olen {username} #Mastodonissa! Seuraa minua osoitteessa {url}",
|
||||
"onboarding.share.next_steps": "Mahdolliset seuraavat vaiheet:",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Leitiúrslit",
|
||||
"emoji_button.symbols": "Ímyndir",
|
||||
"emoji_button.travel": "Ferðing og støð",
|
||||
"empty_column.account_hides_collections": "Hesin brúkarin hevur valt, at hesar upplýsingarnar ikki skulu vera tøkar",
|
||||
"empty_column.account_suspended": "Kontan gjørd óvirkin",
|
||||
"empty_column.account_timeline": "Einki uppslag her!",
|
||||
"empty_column.account_unavailable": "Vangin er ikki tøkur",
|
||||
|
@ -389,7 +390,6 @@
|
|||
"lists.search": "Leita millum fólk, sum tú fylgir",
|
||||
"lists.subheading": "Tínir listar",
|
||||
"load_pending": "{count, plural, one {# nýtt evni} other {# nýggj evni}}",
|
||||
"loading_indicator.label": "Innlesi...",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Fjal mynd} other {Fjal myndir}}",
|
||||
"moved_to_account_banner.text": "Konta tín {disabledAccount} er í løtuni óvirkin, tí tú flutti til {movedToAccount}.",
|
||||
"mute_modal.duration": "Tíðarbil",
|
||||
|
|
|
@ -479,6 +479,9 @@
|
|||
"onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer de rechercher ou de parcourir la page \"Explorer\" pour trouver des personnes à suivre, ou réessayer plus tard.",
|
||||
"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": "Popular on Mastodon",
|
||||
"onboarding.profile.discoverable": "Autoriser des algorithmes de découverte à mettre en avant votre profil et vos messages",
|
||||
"onboarding.profile.save_and_continue": "Enregistrer et continuer",
|
||||
"onboarding.profile.upload_avatar": "Importer une photo de profil",
|
||||
"onboarding.share.lead": "Faites savoir aux gens comment vous trouver sur Mastodon!",
|
||||
"onboarding.share.message": "Je suis {username} sur #Mastodon! Suivez-moi sur {url}",
|
||||
"onboarding.share.next_steps": "Étapes suivantes possibles:",
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
"closed_registrations_modal.preamble": "Mastodon est décentralisé : peu importe où vous créez votre compte, vous serez en mesure de suivre et d'interagir avec quiconque sur ce serveur. Vous pouvez même l'héberger !",
|
||||
"closed_registrations_modal.title": "Inscription sur Mastodon",
|
||||
"column.about": "À propos",
|
||||
"column.blocks": "Comptes bloqués",
|
||||
"column.blocks": "Utilisateurs bloqués",
|
||||
"column.bookmarks": "Marque-pages",
|
||||
"column.community": "Fil public local",
|
||||
"column.direct": "Mentions privées",
|
||||
|
@ -479,6 +479,9 @@
|
|||
"onboarding.follows.empty": "Malheureusement, aucun résultat ne peut être affiché pour le moment. Vous pouvez essayer d'utiliser la recherche ou parcourir la page de découverte pour trouver des personnes à suivre, ou réessayez plus tard.",
|
||||
"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": "Personnaliser votre flux principal",
|
||||
"onboarding.profile.discoverable": "Autoriser des algorithmes de découverte à mettre en avant votre profil et vos messages",
|
||||
"onboarding.profile.save_and_continue": "Enregistrer et continuer",
|
||||
"onboarding.profile.upload_avatar": "Importer une photo de profil",
|
||||
"onboarding.share.lead": "Faites savoir aux gens comment ils peuvent vous trouver sur Mastodon!",
|
||||
"onboarding.share.message": "Je suis {username} sur #Mastodon ! Suivez-moi sur {url}",
|
||||
"onboarding.share.next_steps": "Étapes suivantes possibles :",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Sykresultaten",
|
||||
"emoji_button.symbols": "Symboalen",
|
||||
"emoji_button.travel": "Reizgje en lokaasjes",
|
||||
"empty_column.account_hides_collections": "Dizze brûker hat derfoar keazen dizze ynformaasje net beskikber te meitsjen",
|
||||
"empty_column.account_suspended": "Account beskoattele",
|
||||
"empty_column.account_timeline": "Hjir binne gjin berjochten!",
|
||||
"empty_column.account_unavailable": "Profyl net beskikber",
|
||||
|
@ -389,7 +390,6 @@
|
|||
"lists.search": "Sykje nei minsken dy’t jo folgje",
|
||||
"lists.subheading": "Jo listen",
|
||||
"load_pending": "{count, plural, one {# nij item} other {# nije items}}",
|
||||
"loading_indicator.label": "Lade…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {ôfbylding ferstopje} other {ôfbyldingen ferstopje}}",
|
||||
"moved_to_account_banner.text": "Omdat jo nei {movedToAccount} ferhuze binne is jo account {disabledAccount} op dit stuit útskeakele.",
|
||||
"mute_modal.duration": "Doer",
|
||||
|
|
|
@ -316,7 +316,6 @@
|
|||
"lists.replies_policy.title": "Taispeáin freagraí:",
|
||||
"lists.search": "Cuardaigh i measc daoine atá á leanúint agat",
|
||||
"lists.subheading": "Do liostaí",
|
||||
"loading_indicator.label": "Ag lódáil...",
|
||||
"mute_modal.duration": "Tréimhse",
|
||||
"mute_modal.hide_notifications": "Cuir póstalacha ón t-úsáideoir seo i bhfolach?",
|
||||
"mute_modal.indefinite": "Gan téarma",
|
||||
|
|
|
@ -389,7 +389,6 @@
|
|||
"lists.search": "Lorg am measg nan daoine a leanas tu",
|
||||
"lists.subheading": "Na liostaichean agad",
|
||||
"load_pending": "{count, plural, one {# nì ùr} two {# nì ùr} few {# nithean ùra} other {# nì ùr}}",
|
||||
"loading_indicator.label": "’Ga luchdadh…",
|
||||
"media_gallery.toggle_visible": "{number, plural, 1 {Falaich an dealbh} one {Falaich na dealbhan} two {Falaich na dealbhan} few {Falaich na dealbhan} other {Falaich na dealbhan}}",
|
||||
"moved_to_account_banner.text": "Tha an cunntas {disabledAccount} agad à comas on a rinn thu imrich gu {movedToAccount}.",
|
||||
"mute_modal.duration": "Faide",
|
||||
|
|
|
@ -390,7 +390,7 @@
|
|||
"lists.search": "Procurar entre as persoas que segues",
|
||||
"lists.subheading": "As túas listaxes",
|
||||
"load_pending": "{count, plural, one {# novo elemento} other {# novos elementos}}",
|
||||
"loading_indicator.label": "Estase a cargar...",
|
||||
"loading_indicator.label": "Estase a cargar…",
|
||||
"media_gallery.toggle_visible": "Agochar {number, plural, one {imaxe} other {imaxes}}",
|
||||
"moved_to_account_banner.text": "A túa conta {disabledAccount} está actualmente desactivada porque movéchela a {movedToAccount}.",
|
||||
"mute_modal.duration": "Duración",
|
||||
|
@ -479,6 +479,17 @@
|
|||
"onboarding.follows.empty": "Desgraciadamente agora mesmo non hai nada que mostrar. Podes intentalo coa busca ou na páxina descubrir para atopar persoas ás que seguir, ou intentalo máis tarde.",
|
||||
"onboarding.follows.lead": "Podes facer que a túa cronoloxía de inicio sexa como ti a queres. Canta máis xente sigas máis interesante será. Estes perfís poderían axudarche a comezar —sempre poderás deixar de seguilos despois!",
|
||||
"onboarding.follows.title": "Popular en Mastodon",
|
||||
"onboarding.profile.discoverable": "Perfil destacado e publicacións nos algoritmos de descubrimento",
|
||||
"onboarding.profile.display_name": "Nome público",
|
||||
"onboarding.profile.display_name_hint": "O teu nome completo ou un nome divertido…",
|
||||
"onboarding.profile.indexable": "Incluír publicacións públicas nos resultados das buscas",
|
||||
"onboarding.profile.lead": "Sempre poderás incluír esta información mais tarde nos axustes, onde terás máis opcións dispoñibles.",
|
||||
"onboarding.profile.note": "Acerca de ti",
|
||||
"onboarding.profile.note_hint": "Podes @mencionar a outras persoas ou usar #cancelos…",
|
||||
"onboarding.profile.save_and_continue": "Gardar e continuar",
|
||||
"onboarding.profile.title": "Configuración do perfil",
|
||||
"onboarding.profile.upload_avatar": "Subir imaxe do perfil",
|
||||
"onboarding.profile.upload_header": "Subir cabeceira para o perfil",
|
||||
"onboarding.share.lead": "Fai que as persoas saiban como atoparte en Mastodon!",
|
||||
"onboarding.share.message": "Son {username} en #Mastodon! Ségueme en {url}",
|
||||
"onboarding.share.next_steps": "Seguintes pasos:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "לחסום",
|
||||
"account.browse_more_on_origin_server": "ראה יותר בפרופיל המקורי",
|
||||
"account.cancel_follow_request": "משיכת בקשת מעקב",
|
||||
"account.copy": "להעתיק קישור לפרופיל",
|
||||
"account.direct": "הודעה פרטית אל @{name}",
|
||||
"account.disable_notifications": "הפסק לשלוח לי התראות כש@{name} מפרסמים",
|
||||
"account.domain_blocked": "הדומיין חסום",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "סמן כנקרא",
|
||||
"conversation.open": "צפו בשיחה",
|
||||
"conversation.with": "עם {names}",
|
||||
"copy_icon_button.copied": "הועתק ללוח",
|
||||
"copypaste.copied": "הועתק",
|
||||
"copypaste.copy_to_clipboard": "העתקה ללוח הגזירים",
|
||||
"directory.federated": "מהפדרציה הידועה",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "חיפוש בין אנשים שאני עוקב\\ת אחריהם",
|
||||
"lists.subheading": "הרשימות שלך",
|
||||
"load_pending": "{count, plural, one {# פריט חדש} other {# פריטים חדשים}}",
|
||||
"loading_indicator.label": "טוען...",
|
||||
"loading_indicator.label": "בטעינה…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {להסתיר תמונה} two {להסתיר תמונותיים} many {להסתיר תמונות} other {להסתיר תמונות}}",
|
||||
"moved_to_account_banner.text": "חשבונך {disabledAccount} אינו פעיל כרגע עקב מעבר ל{movedToAccount}.",
|
||||
"mute_modal.duration": "משך הזמן",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "למצער, תוצאות לחיפושך אינן בנמצא. ניתן להשתמש בחיפוש או בדף החקירות לשם מציאת אנשים ולעקבם. אפשר גם לנסות שוב אחר כך.",
|
||||
"onboarding.follows.lead": "אתם אוצרים את הזרם הבייתי שלכם. ככל שתעקבו אחרי יותר אנשים, הוא יהיה עשיר ופעיל יותר. הנה כמה פרופילים להתחיל בהם - תמיד ניתן להפסיק מעקב אחריהם בהמשך!",
|
||||
"onboarding.follows.title": "פופולארי על מסטודון",
|
||||
"onboarding.profile.discoverable": "הצגת פרופיל והודעות במסך התגליות",
|
||||
"onboarding.profile.display_name": "שם להצגה",
|
||||
"onboarding.profile.display_name_hint": "שמך המלא או כינוי הכיף שלך…",
|
||||
"onboarding.profile.indexable": "הכללת הודעות ציבוריות בתוצאות החיפוש",
|
||||
"onboarding.profile.lead": "תמיד ניתן להשלים זאת אחר כך בהגדרות, שם יש אפילו עוד אפשרויות להתאמה אישית.",
|
||||
"onboarding.profile.note": "אודות",
|
||||
"onboarding.profile.note_hint": "ניתן @לאזכר משתמשים אחרים או #תגיות…",
|
||||
"onboarding.profile.save_and_continue": "לשמור ולהמשיך",
|
||||
"onboarding.profile.title": "הגדרת פרופיל",
|
||||
"onboarding.profile.upload_avatar": "העלאת תמונת פרופיל",
|
||||
"onboarding.profile.upload_header": "העלאת כותרת פרופיל",
|
||||
"onboarding.share.lead": "כדאי להודיע לחברים היכן למצוא אותך במסטודון!",
|
||||
"onboarding.share.message": "אני {username} ברשת #מסטודון! בואו לעקוב אחרי בכתובת {url}",
|
||||
"onboarding.share.next_steps": "לאיפה להמשיך מכאן:",
|
||||
|
@ -518,7 +531,7 @@
|
|||
"privacy.private.short": "לעוקבים בלבד",
|
||||
"privacy.public.long": "גלוי לכל",
|
||||
"privacy.public.short": "פומבי",
|
||||
"privacy.unlisted.long": "גלוי לכל, אבל מוסתר מאמצעי גילוי",
|
||||
"privacy.unlisted.long": "גלוי לכל, אבל מוסתר מאמצעי תגלית",
|
||||
"privacy.unlisted.short": "לא רשום (לא לפיד הכללי)",
|
||||
"privacy_policy.last_updated": "עודכן לאחרונה {date}",
|
||||
"privacy_policy.title": "מדיניות פרטיות",
|
||||
|
|
|
@ -360,7 +360,6 @@
|
|||
"lists.replies_policy.none": "कोई नहीं",
|
||||
"lists.replies_policy.title": "इसके जवाब दिखाएं:",
|
||||
"lists.subheading": "आपकी सूचियाँ",
|
||||
"loading_indicator.label": "लोड हो रहा है...",
|
||||
"mute_modal.duration": "अवधि",
|
||||
"mute_modal.hide_notifications": "इस सभ्य की ओरसे आनेवाली सूचनाए शांत करे",
|
||||
"mute_modal.indefinite": "अनिश्चितकालीन",
|
||||
|
|
|
@ -306,7 +306,6 @@
|
|||
"lists.replies_policy.none": "Nitko",
|
||||
"lists.search": "Traži među praćenim ljudima",
|
||||
"lists.subheading": "Vaše liste",
|
||||
"loading_indicator.label": "Učitavanje...",
|
||||
"media_gallery.toggle_visible": "Sakrij {number, plural, one {sliku} other {slike}}",
|
||||
"mute_modal.duration": "Trajanje",
|
||||
"mute_modal.hide_notifications": "Sakrij obavijesti ovog korisnika?",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Letiltva",
|
||||
"account.browse_more_on_origin_server": "További böngészés az eredeti profilon",
|
||||
"account.cancel_follow_request": "Követési kérés visszavonása",
|
||||
"account.copy": "Hivatkozás másolása a profilba",
|
||||
"account.direct": "@{name} személyes említése",
|
||||
"account.disable_notifications": "Ne figyelmeztessen, ha @{name} bejegyzést tesz közzé",
|
||||
"account.domain_blocked": "Letiltott domain",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Megjelölés olvasottként",
|
||||
"conversation.open": "Beszélgetés megtekintése",
|
||||
"conversation.with": "Velük: {names}",
|
||||
"copy_icon_button.copied": "A szöveg a vágólapra másolva",
|
||||
"copypaste.copied": "Másolva",
|
||||
"copypaste.copy_to_clipboard": "Másolás vágólapra",
|
||||
"directory.federated": "Az ismert fediverzumból",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Keresés a követett személyek között",
|
||||
"lists.subheading": "Saját listák",
|
||||
"load_pending": "{count, plural, one {# új elem} other {# új elem}}",
|
||||
"loading_indicator.label": "Betöltés...",
|
||||
"loading_indicator.label": "Betöltés…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Kép elrejtése} other {Képek elrejtése}}",
|
||||
"moved_to_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva, mert átköltöztél ide: {movedToAccount}.",
|
||||
"mute_modal.duration": "Időtartam",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.",
|
||||
"onboarding.follows.lead": "A saját hírfolyamod az elsődleges tapasztalás a Mastodonon. Minél több embert követsz, annál aktívabb és érdekesebb a dolog. Az induláshoz itt van néhány javaslat:",
|
||||
"onboarding.follows.title": "Népszerű a Mastodonon",
|
||||
"onboarding.profile.discoverable": "Profil és bejegyzések szerepeltetése a felfedezési algoritmusokban",
|
||||
"onboarding.profile.display_name": "Megjelenített név",
|
||||
"onboarding.profile.display_name_hint": "Teljes neved vagy vicces neved…",
|
||||
"onboarding.profile.indexable": "Nyilvános bejegyzések szerepeltetése a keresési eredményekben",
|
||||
"onboarding.profile.lead": "Ezt később bármikor befejezheted a beállításokban, ahol még több testreszabási lehetőség áll rendelkezésre.",
|
||||
"onboarding.profile.note": "Bemutatkozás",
|
||||
"onboarding.profile.note_hint": "Megemlíthetsz @másokat vagy #hashtag-eket…",
|
||||
"onboarding.profile.save_and_continue": "Mentés és folytatás",
|
||||
"onboarding.profile.title": "Profilbeállítás",
|
||||
"onboarding.profile.upload_avatar": "Profilkép feltöltése",
|
||||
"onboarding.profile.upload_header": "Profil fejléc feltöltése",
|
||||
"onboarding.share.lead": "Tudassuk az emberekkel, hogyan találhatnak meg a Mastodonon!",
|
||||
"onboarding.share.message": "{username} vagyok a #Mastodon hálózaton! Kövess itt: {url}.",
|
||||
"onboarding.share.next_steps": "Lehetséges következő lépések:",
|
||||
|
|
|
@ -314,7 +314,6 @@
|
|||
"lists.search": "Փնտրել քո հետեւած մարդկանց մէջ",
|
||||
"lists.subheading": "Քո ցանկերը",
|
||||
"load_pending": "{count, plural, one {# նոր նիւթ} other {# նոր նիւթ}}",
|
||||
"loading_indicator.label": "Բեռնւում է…",
|
||||
"media_gallery.toggle_visible": "Ցուցադրել/թաքցնել",
|
||||
"mute_modal.duration": "Տեւողութիւն",
|
||||
"mute_modal.hide_notifications": "Թաքցնե՞լ ծանուցումներն այս օգտատիրոջից։",
|
||||
|
|
|
@ -353,7 +353,6 @@
|
|||
"lists.search": "Cari di antara orang yang Anda ikuti",
|
||||
"lists.subheading": "Daftar Anda",
|
||||
"load_pending": "{count, plural, other {# item baru}}",
|
||||
"loading_indicator.label": "Tunggu sebentar...",
|
||||
"media_gallery.toggle_visible": "Tampil/Sembunyikan",
|
||||
"moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.",
|
||||
"mute_modal.duration": "Durasi",
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
{
|
||||
"account.add_or_remove_from_list": "Tinye ma ọ bụ Wepu na ndepụta",
|
||||
"account.badges.bot": "Bot",
|
||||
"account.badges.group": "Otù",
|
||||
"account.cancel_follow_request": "Withdraw follow request",
|
||||
"account.follow": "Soro",
|
||||
"account.followers": "Ndị na-eso",
|
||||
"account.following": "Na-eso",
|
||||
"account.follows_you": "Na-eso gị",
|
||||
"account.mute": "Mee ogbi @{name}",
|
||||
"account.unfollow": "Kwụsị iso",
|
||||
|
@ -11,16 +14,20 @@
|
|||
"audio.hide": "Zoo ụda",
|
||||
"bundle_column_error.retry": "Nwaa ọzọ",
|
||||
"bundle_column_error.routing.title": "404",
|
||||
"bundle_modal_error.close": "Mechie",
|
||||
"bundle_modal_error.retry": "Nwaa ọzọ",
|
||||
"column.about": "Maka",
|
||||
"column.blocks": "Ojiarụ egbochiri",
|
||||
"column.bookmarks": "Ebenrụtụakā",
|
||||
"column.home": "Be",
|
||||
"column.lists": "Ndepụta",
|
||||
"column.pins": "Pinned post",
|
||||
"column_header.pin": "Gbado na profaịlụ gị",
|
||||
"column_subheading.settings": "Mwube",
|
||||
"community.column_settings.media_only": "Media only",
|
||||
"compose.language.change": "Gbanwee asụsụ",
|
||||
"compose.language.search": "Chọọ asụsụ...",
|
||||
"compose.published.open": "Mepe",
|
||||
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
|
||||
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
|
||||
"compose_form.placeholder": "What is on your mind?",
|
||||
|
@ -32,7 +39,10 @@
|
|||
"confirmations.delete.message": "Are you sure you want to delete this status?",
|
||||
"confirmations.delete_list.confirm": "Hichapụ",
|
||||
"confirmations.domain_block.confirm": "Hide entire domain",
|
||||
"confirmations.edit.confirm": "Dezie",
|
||||
"confirmations.mute.confirm": "Mee ogbi",
|
||||
"confirmations.reply.confirm": "Zaa",
|
||||
"confirmations.unfollow.confirm": "Kwụsị iso",
|
||||
"conversation.delete": "Hichapụ nkata",
|
||||
"dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
|
||||
"dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
|
||||
|
@ -76,8 +86,8 @@
|
|||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||
"keyboard_shortcuts.up": "to move up in the list",
|
||||
"lists.delete": "Hichapụ ndepụta",
|
||||
"lists.edit": "Dezie ndepụta",
|
||||
"lists.subheading": "Ndepụta gị",
|
||||
"loading_indicator.label": "Na-adọnye...",
|
||||
"navigation_bar.about": "Maka",
|
||||
"navigation_bar.bookmarks": "Ebenrụtụakā",
|
||||
"navigation_bar.domain_blocks": "Hidden domains",
|
||||
|
@ -100,20 +110,27 @@
|
|||
"privacy.change": "Adjust status privacy",
|
||||
"privacy.direct.short": "Direct",
|
||||
"privacy.private.short": "Followers-only",
|
||||
"relative_time.full.just_now": "kịta",
|
||||
"relative_time.just_now": "kịta",
|
||||
"relative_time.today": "taa",
|
||||
"reply_indicator.cancel": "Kagbuo",
|
||||
"report.categories.other": "Ọzọ",
|
||||
"report.categories.spam": "Nzipụ Ozièlètrọniìk Nkeāchọghị",
|
||||
"report.mute": "Mee ogbi",
|
||||
"report.placeholder": "Type or paste additional comments",
|
||||
"report.submit": "Submit report",
|
||||
"report.target": "Report {target}",
|
||||
"report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached",
|
||||
"report_notification.categories.other": "Ọzọ",
|
||||
"search.placeholder": "Chọọ",
|
||||
"server_banner.active_users": "ojiarụ dị ìrè",
|
||||
"server_banner.learn_more": "Mụtakwuo",
|
||||
"sign_in_banner.sign_in": "Sign in",
|
||||
"status.admin_status": "Open this status in the moderation interface",
|
||||
"status.bookmark": "Kee ebenrụtụakā",
|
||||
"status.copy": "Copy link to status",
|
||||
"status.delete": "Hichapụ",
|
||||
"status.edit": "Dezie",
|
||||
"status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}",
|
||||
"status.open": "Expand this status",
|
||||
"status.remove_bookmark": "Wepu ebenrụtụakā",
|
||||
|
|
|
@ -383,7 +383,6 @@
|
|||
"lists.search": "Trovez inter personi quon vu sequas",
|
||||
"lists.subheading": "Vua listi",
|
||||
"load_pending": "{count, plural, one {# nova kozo} other {# nova kozi}}",
|
||||
"loading_indicator.label": "Kargante...",
|
||||
"media_gallery.toggle_visible": "Chanjar videbleso",
|
||||
"moved_to_account_banner.text": "Vua konto {disabledAccount} es nune desaktiva pro ke vu movis a {movedToAccount}.",
|
||||
"mute_modal.duration": "Durado",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Útilokaður",
|
||||
"account.browse_more_on_origin_server": "Skoða nánari upplýsingar á notandasniðinu",
|
||||
"account.cancel_follow_request": "Taka fylgjendabeiðni til baka",
|
||||
"account.copy": "Afrita tengil í notandasnið",
|
||||
"account.direct": "Einkaspjall við @{name}",
|
||||
"account.disable_notifications": "Hætta að láta mig vita þegar @{name} sendir inn",
|
||||
"account.domain_blocked": "Lén útilokað",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Merkja sem lesið",
|
||||
"conversation.open": "Skoða samtal",
|
||||
"conversation.with": "Við {names}",
|
||||
"copy_icon_button.copied": "Afritað á klippispjald",
|
||||
"copypaste.copied": "Afritað",
|
||||
"copypaste.copy_to_clipboard": "Afrita á klippispjald",
|
||||
"directory.federated": "Frá samtengdum vefþjónum",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Leita meðal þeirra sem þú fylgist með",
|
||||
"lists.subheading": "Listarnir þínir",
|
||||
"load_pending": "{count, plural, one {# nýtt atriði} other {# ný atriði}}",
|
||||
"loading_indicator.label": "Hleð inn...",
|
||||
"loading_indicator.label": "Hleð inn…",
|
||||
"media_gallery.toggle_visible": "Víxla sýnileika",
|
||||
"moved_to_account_banner.text": "Aðgangurinn þinn {disabledAccount} er óvirkur í augnablikinu vegna þess að þú fluttir þig yfir á {movedToAccount}.",
|
||||
"mute_modal.duration": "Lengd",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Því miður er ekki hægt að birta neinar niðurstöður í augnablikinu. Þú getur reynt að nota leitina eða skoðað könnunarsíðuna til að finna fólk til að fylgjast með, nú eða prófað aftur síðar.",
|
||||
"onboarding.follows.lead": "Þú ræktar heimastreymið þitt. Því fleira fólki sem þú fylgist með, því virkara og áhugaverðara verður það. Að fylgjast með þessum notendum gæti verið ágætt til að byrja með - þú getur alltaf hætt að fylgjast með þeim síðar!",
|
||||
"onboarding.follows.title": "Vinsælt á Mastodon",
|
||||
"onboarding.profile.discoverable": "Hafa notandasnið og færslur með í reikniritum leitar",
|
||||
"onboarding.profile.display_name": "Birtingarnafn",
|
||||
"onboarding.profile.display_name_hint": "Fullt nafn þitt eða eitthvað til gamans…",
|
||||
"onboarding.profile.indexable": "Hafa opinberar færslur með í leitarniðurstöðum",
|
||||
"onboarding.profile.lead": "Þú getur alltaf klárað þetta seinna í stillingunum, þar sem enn fleiri möguleikar bjóðast á sérsníðingum.",
|
||||
"onboarding.profile.note": "Æviágrip",
|
||||
"onboarding.profile.note_hint": "Þú getur @minnst á annað fólk eða #myllumerki…",
|
||||
"onboarding.profile.save_and_continue": "Vista og halda áfram",
|
||||
"onboarding.profile.title": "Uppsetning notandasniðs",
|
||||
"onboarding.profile.upload_avatar": "Sendu inn auðkennismynd",
|
||||
"onboarding.profile.upload_header": "Sendu inn bakgrunnsmynd í haus notandasniðs",
|
||||
"onboarding.share.lead": "Láttu fólk vita hvernig það getur fundið þig á Mastodon!",
|
||||
"onboarding.share.message": "Ég heiti {username} á #Mastodon! Þú getur fylgst með mér á {url}",
|
||||
"onboarding.share.next_steps": "Möguleg næstu skref:",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"account.blocked": "Bloccato",
|
||||
"account.browse_more_on_origin_server": "Sfoglia di più sul profilo originale",
|
||||
"account.cancel_follow_request": "Annulla la richiesta di seguire",
|
||||
"account.copy": "Copia link del profilo",
|
||||
"account.direct": "Menziona privatamente @{name}",
|
||||
"account.disable_notifications": "Smetti di avvisarmi quando @{name} pubblica un post",
|
||||
"account.domain_blocked": "Dominio bloccato",
|
||||
|
@ -191,6 +192,7 @@
|
|||
"conversation.mark_as_read": "Segna come letto",
|
||||
"conversation.open": "Visualizza conversazione",
|
||||
"conversation.with": "Con {names}",
|
||||
"copy_icon_button.copied": "Copiato negli appunti",
|
||||
"copypaste.copied": "Copiato",
|
||||
"copypaste.copy_to_clipboard": "Copia negli Appunti",
|
||||
"directory.federated": "Da un fediverse noto",
|
||||
|
@ -252,7 +254,7 @@
|
|||
"explore.search_results": "Risultati della ricerca",
|
||||
"explore.suggested_follows": "Persone",
|
||||
"explore.title": "Esplora",
|
||||
"explore.trending_links": "Novità",
|
||||
"explore.trending_links": "Notizie",
|
||||
"explore.trending_statuses": "Post",
|
||||
"explore.trending_tags": "Hashtag",
|
||||
"filter_modal.added.context_mismatch_explanation": "La categoria di questo filtro non si applica al contesto in cui hai acceduto a questo post. Se desideri che il post sia filtrato anche in questo contesto, dovrai modificare il filtro.",
|
||||
|
@ -390,7 +392,7 @@
|
|||
"lists.search": "Cerca tra le persone che segui",
|
||||
"lists.subheading": "Le tue liste",
|
||||
"load_pending": "{count, plural, one {# nuovo oggetto} other {# nuovi oggetti}}",
|
||||
"loading_indicator.label": "Caricamento...",
|
||||
"loading_indicator.label": "Caricamento…",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {Nascondi immagine} other {Nascondi immagini}}",
|
||||
"moved_to_account_banner.text": "Il tuo profilo {disabledAccount} è correntemente disabilitato perché ti sei spostato a {movedToAccount}.",
|
||||
"mute_modal.duration": "Durata",
|
||||
|
@ -479,6 +481,17 @@
|
|||
"onboarding.follows.empty": "Sfortunatamente, nessun risultato può essere mostrato in questo momento. Puoi provare a utilizzare la ricerca o sfogliare la pagina di esplorazione per trovare persone da seguire, oppure riprova più tardi.",
|
||||
"onboarding.follows.lead": "La cronologia della tua home è gestita da te. Più persone segui, più attiva e interessante sarà. Questi profili possono essere un buon punto di partenza; puoi sempre smettere di seguirli in seguito!",
|
||||
"onboarding.follows.title": "Popolare su Mastodon",
|
||||
"onboarding.profile.discoverable": "Include il profilo e i post negli algoritmi di scoperta",
|
||||
"onboarding.profile.display_name": "Nome da visualizzare",
|
||||
"onboarding.profile.display_name_hint": "Il tuo nome completo o il tuo nome divertente…",
|
||||
"onboarding.profile.indexable": "Includi i post pubblici nei risultati di ricerca",
|
||||
"onboarding.profile.lead": "Puoi sempre completarlo in un secondo momento nelle impostazioni, dove sono disponibili ancora più opzioni di personalizzazione.",
|
||||
"onboarding.profile.note": "Biografia",
|
||||
"onboarding.profile.note_hint": "Puoi @menzionare altre persone o #hashtags…",
|
||||
"onboarding.profile.save_and_continue": "Salva e continua",
|
||||
"onboarding.profile.title": "Configurazione del profilo",
|
||||
"onboarding.profile.upload_avatar": "Carica l'immagine del profilo",
|
||||
"onboarding.profile.upload_header": "Carica l'intestazione del profilo",
|
||||
"onboarding.share.lead": "Fai sapere alle persone come possono trovarti su Mastodon!",
|
||||
"onboarding.share.message": "Sono {username} su #Mastodon! Vieni a seguirmi su {url}",
|
||||
"onboarding.share.next_steps": "Possibili passaggi successivi:",
|
||||
|
|
|
@ -390,7 +390,7 @@
|
|||
"lists.search": "フォローしている人の中から検索",
|
||||
"lists.subheading": "あなたのリスト",
|
||||
"load_pending": "{count}件の新着",
|
||||
"loading_indicator.label": "読み込み中...",
|
||||
"loading_indicator.label": "",
|
||||
"media_gallery.toggle_visible": "{number, plural, one {画像を閉じる} other {画像を閉じる}}",
|
||||
"moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。",
|
||||
"mute_modal.duration": "ミュートする期間",
|
||||
|
@ -586,8 +586,8 @@
|
|||
"search.no_recent_searches": "検索履歴はありません",
|
||||
"search.placeholder": "検索",
|
||||
"search.quick_action.account_search": "{x}に該当するプロフィール",
|
||||
"search.quick_action.go_to_account": "{x}のプロフィールを見る",
|
||||
"search.quick_action.go_to_hashtag": "{x}に該当するハッシュタグ",
|
||||
"search.quick_action.go_to_account": "プロフィール {x} を見る",
|
||||
"search.quick_action.go_to_hashtag": "ハッシュタグ {x} を見る",
|
||||
"search.quick_action.open_url": "MastodonでURLを開く",
|
||||
"search.quick_action.status_search": "{x}に該当する投稿",
|
||||
"search.search_or_paste": "検索またはURLを入力",
|
||||
|
|
|
@ -165,7 +165,6 @@
|
|||
"lists.new.title_placeholder": "ახალი სიის სათაური",
|
||||
"lists.search": "ძებნა ადამიანებს შორის რომელთაც მიჰყვებით",
|
||||
"lists.subheading": "თქვენი სიები",
|
||||
"loading_indicator.label": "იტვირთება...",
|
||||
"media_gallery.toggle_visible": "ხილვადობის ჩართვა",
|
||||
"mute_modal.hide_notifications": "დავმალოთ შეტყობინებები ამ მომხმარებლისგან?",
|
||||
"navigation_bar.blocks": "დაბლოკილი მომხმარებლები",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue