Merge pull request #1848 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes
main
Claire 2 years ago committed by GitHub
commit 2aafdd0efb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -20,7 +20,7 @@
"forwardPorts": [3000, 4000], "forwardPorts": [3000, 4000],
// Use 'postCreateCommand' to run commands after the container is created. // Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "bundle install --path vendor/bundle && yarn install && ./bin/rails db:setup", "postCreateCommand": "bundle install --path vendor/bundle && yarn install && git checkout -- Gemfile.lock && ./bin/rails db:setup",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode" "remoteUser": "vscode"

@ -27,6 +27,7 @@ services:
ES_ENABLED: 'true' ES_ENABLED: 'true'
ES_HOST: es ES_HOST: es
ES_PORT: '9200' ES_PORT: '9200'
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
# Overrides default command so things don't shut down after the process ends. # Overrides default command so things don't shut down after the process ends.
command: sleep infinity command: sleep infinity
networks: networks:
@ -72,6 +73,12 @@ services:
soft: -1 soft: -1
hard: -1 hard: -1
libretranslate:
image: libretranslate/libretranslate:v1.2.9
restart: unless-stopped
networks:
- internal_network
volumes: volumes:
postgres-data: postgres-data:
redis-data: redis-data:

@ -10,6 +10,9 @@ on:
paths: paths:
- .github/workflows/build-image.yml - .github/workflows/build-image.yml
- Dockerfile - Dockerfile
permissions:
contents: read
jobs: jobs:
build-image: build-image:
runs-on: ubuntu-latest runs-on: ubuntu-latest

@ -9,6 +9,9 @@ on:
env: env:
RAILS_ENV: test RAILS_ENV: test
permissions:
contents: read
jobs: jobs:
check-i18n: check-i18n:
runs-on: ubuntu-latest runs-on: ubuntu-latest

@ -55,7 +55,7 @@ jobs:
with: with:
node-version: 16.x node-version: 16.x
cache: yarn cache: yarn
- name: Intall dependencies - name: Install dependencies
run: yarn install --frozen-lockfile run: yarn install --frozen-lockfile
- name: Set-up RuboCop Problem Mathcher - name: Set-up RuboCop Problem Mathcher
uses: r7kamura/rubocop-problem-matchers-action@v1 uses: r7kamura/rubocop-problem-matchers-action@v1

@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
# Install Node v16 (LTS) # Install Node v16 (LTS)
ENV NODE_VER="16.16.0" ENV NODE_VER="16.17.1"
RUN ARCH= && \ RUN ARCH= && \
dpkgArch="$(dpkg --print-architecture)" && \ dpkgArch="$(dpkg --print-architecture)" && \
case "${dpkgArch##*-}" in \ case "${dpkgArch##*-}" in \
@ -19,7 +19,7 @@ RUN ARCH= && \
esac && \ esac && \
echo "Etc/UTC" > /etc/localtime && \ echo "Etc/UTC" > /etc/localtime && \
apt-get update && \ apt-get update && \
apt-get install -y --no-install-recommends ca-certificates wget python apt-utils && \ apt-get install -y --no-install-recommends ca-certificates wget python3 apt-utils && \
cd ~ && \ cd ~ && \
wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \ wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \
tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \ tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \

@ -7,7 +7,7 @@ gem 'pkg-config', '~> 1.4'
gem 'rexml', '~> 3.2' gem 'rexml', '~> 3.2'
gem 'puma', '~> 5.6' gem 'puma', '~> 5.6'
gem 'rails', '~> 6.1.6' gem 'rails', '~> 6.1.7'
gem 'sprockets', '~> 3.7.2' gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 1.2' gem 'thor', '~> 1.2'
gem 'rack', '~> 2.2.4' gem 'rack', '~> 2.2.4'
@ -46,7 +46,7 @@ gem 'omniauth-rails_csrf_protection', '~> 0.1'
gem 'color_diff', '~> 0.1' gem 'color_diff', '~> 0.1'
gem 'discard', '~> 1.2' gem 'discard', '~> 1.2'
gem 'doorkeeper', '~> 5.5' gem 'doorkeeper', '~> 5.6'
gem 'ed25519', '~> 1.3' gem 'ed25519', '~> 1.3'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
@ -55,7 +55,7 @@ gem 'redis-namespace', '~> 1.9'
gem 'htmlentities', '~> 4.3' gem 'htmlentities', '~> 4.3'
gem 'http', '~> 5.1' gem 'http', '~> 5.1'
gem 'http_accept_language', '~> 2.1' gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.5.0' gem 'httplog', '~> 1.6.0'
gem 'idn-ruby', require: 'idn' gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2' gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0' gem 'link_header', '~> 0.0'
@ -116,7 +116,7 @@ end
group :test do group :test do
gem 'capybara', '~> 3.37' gem 'capybara', '~> 3.37'
gem 'climate_control', '~> 0.2' gem 'climate_control', '~> 0.2'
gem 'faker', '~> 2.22' gem 'faker', '~> 2.23'
gem 'microformats', '~> 4.4' gem 'microformats', '~> 4.4'
gem 'rails-controller-testing', '~> 1.0' gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.1' gem 'rspec-sidekiq', '~> 3.1'

@ -10,40 +10,40 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (6.1.6) actioncable (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (6.1.6) actionmailbox (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
activejob (= 6.1.6) activejob (= 6.1.7)
activerecord (= 6.1.6) activerecord (= 6.1.7)
activestorage (= 6.1.6) activestorage (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
mail (>= 2.7.1) mail (>= 2.7.1)
actionmailer (6.1.6) actionmailer (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
actionview (= 6.1.6) actionview (= 6.1.7)
activejob (= 6.1.6) activejob (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (6.1.6) actionpack (6.1.7)
actionview (= 6.1.6) actionview (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
rack (~> 2.0, >= 2.0.9) rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.6) actiontext (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
activerecord (= 6.1.6) activerecord (= 6.1.7)
activestorage (= 6.1.6) activestorage (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (6.1.6) actionview (6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -54,22 +54,22 @@ GEM
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
active_record_query_trace (1.8) active_record_query_trace (1.8)
activejob (6.1.6) activejob (6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (6.1.6) activemodel (6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
activerecord (6.1.6) activerecord (6.1.7)
activemodel (= 6.1.6) activemodel (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
activestorage (6.1.6) activestorage (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
activejob (= 6.1.6) activejob (= 6.1.7)
activerecord (= 6.1.6) activerecord (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
marcel (~> 1.0) marcel (~> 1.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activesupport (6.1.6) activesupport (6.1.7)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -110,12 +110,11 @@ GEM
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
better_html (1.0.16) better_html (2.0.1)
actionview (>= 4.0) actionview (>= 6.0)
activesupport (>= 4.0) activesupport (>= 6.0)
ast (~> 2.0) ast (~> 2.0)
erubi (~> 1.4) erubi (~> 1.4)
html_tokenizer (~> 0.0.6)
parser (>= 2.4) parser (>= 2.4)
smart_properties smart_properties
bindata (2.4.10) bindata (2.4.10)
@ -176,7 +175,7 @@ GEM
coderay (1.1.3) coderay (1.1.3)
color_diff (0.1) color_diff (0.1)
concurrent-ruby (1.1.10) concurrent-ruby (1.1.10)
connection_pool (2.2.5) connection_pool (2.3.0)
cose (1.2.1) cose (1.2.1)
cbor (~> 0.5.9) cbor (~> 0.5.9)
openssl-signature_algorithm (~> 1.0) openssl-signature_algorithm (~> 1.0)
@ -207,7 +206,7 @@ GEM
docile (1.3.4) docile (1.3.4)
domain_name (0.5.20190701) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.5.4) doorkeeper (5.6.0)
railties (>= 5) railties (>= 5)
dotenv (2.8.1) dotenv (2.8.1)
dotenv-rails (2.8.1) dotenv-rails (2.8.1)
@ -224,12 +223,12 @@ GEM
faraday (~> 1) faraday (~> 1)
multi_json multi_json
encryptor (3.0.0) encryptor (3.0.0)
erubi (1.10.0) erubi (1.11.0)
et-orbi (1.2.7) et-orbi (1.2.7)
tzinfo tzinfo
excon (0.76.0) excon (0.76.0)
fabrication (2.30.0) fabrication (2.30.0)
faker (2.22.0) faker (2.23.0)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
faraday (1.9.3) faraday (1.9.3)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
@ -301,7 +300,6 @@ GEM
highline (2.0.3) highline (2.0.3)
hiredis (0.6.3) hiredis (0.6.3)
hkdf (0.3.0) hkdf (0.3.0)
html_tokenizer (0.0.7)
htmlentities (4.3.4) htmlentities (4.3.4)
http (5.1.0) http (5.1.0)
addressable (~> 2.8) addressable (~> 2.8)
@ -313,15 +311,15 @@ GEM
http-form_data (2.3.0) http-form_data (2.3.0)
http_accept_language (2.1.1) http_accept_language (2.1.1)
httpclient (2.8.3) httpclient (2.8.3)
httplog (1.5.0) httplog (1.6.0)
rack (>= 1.0) rack (>= 2.0)
rainbow (>= 2.0.0) rainbow (>= 2.0.0)
i18n (1.12.0) i18n (1.12.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (1.0.11) i18n-tasks (1.0.12)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
ast (>= 2.1.0) ast (>= 2.1.0)
better_html (~> 1.0) better_html (>= 1.0, < 3.0)
erubi erubi
highline (>= 2.0.0) highline (>= 2.0.0)
i18n i18n
@ -386,7 +384,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.18.0) loofah (2.19.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.1) mail (2.7.1)
@ -407,7 +405,7 @@ GEM
mime-types-data (3.2022.0105) mime-types-data (3.2022.0105)
mini_mime (1.1.2) mini_mime (1.1.2)
mini_portile2 (2.8.0) mini_portile2 (2.8.0)
minitest (5.16.2) minitest (5.16.3)
msgpack (1.5.4) msgpack (1.5.4)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.1.1) multipart-post (2.1.1)
@ -454,7 +452,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
ox (2.14.11) ox (2.14.11)
parallel (1.22.1) parallel (1.22.1)
parser (3.1.2.0) parser (3.1.2.1)
ast (~> 2.4.1) ast (~> 2.4.1)
parslet (2.0.0) parslet (2.0.0)
pastel (0.8.0) pastel (0.8.0)
@ -502,20 +500,20 @@ GEM
rack rack
rack-test (2.0.2) rack-test (2.0.2)
rack (>= 1.3) rack (>= 1.3)
rails (6.1.6) rails (6.1.7)
actioncable (= 6.1.6) actioncable (= 6.1.7)
actionmailbox (= 6.1.6) actionmailbox (= 6.1.7)
actionmailer (= 6.1.6) actionmailer (= 6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
actiontext (= 6.1.6) actiontext (= 6.1.7)
actionview (= 6.1.6) actionview (= 6.1.7)
activejob (= 6.1.6) activejob (= 6.1.7)
activemodel (= 6.1.6) activemodel (= 6.1.7)
activerecord (= 6.1.6) activerecord (= 6.1.7)
activestorage (= 6.1.6) activestorage (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 6.1.6) railties (= 6.1.7)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
@ -531,9 +529,9 @@ GEM
railties (>= 6.0.0, < 7) railties (>= 6.0.0, < 7)
rails-settings-cached (0.6.6) rails-settings-cached (0.6.6)
rails (>= 4.2.0) rails (>= 4.2.0)
railties (6.1.6) railties (6.1.7)
actionpack (= 6.1.6) actionpack (= 6.1.7)
activesupport (= 6.1.6) activesupport (= 6.1.7)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
@ -613,10 +611,10 @@ GEM
activerecord (>= 4.0.0) activerecord (>= 4.0.0)
railties (>= 4.0.0) railties (>= 4.0.0)
semantic_range (3.0.0) semantic_range (3.0.0)
sidekiq (6.5.5) sidekiq (6.5.7)
connection_pool (>= 2.2.2) connection_pool (>= 2.2.5)
rack (~> 2.0) rack (~> 2.0)
redis (>= 4.5.0) redis (>= 4.5.0, < 5)
sidekiq-bulk (0.2.0) sidekiq-bulk (0.2.0)
sidekiq sidekiq
sidekiq-scheduler (4.0.2) sidekiq-scheduler (4.0.2)
@ -686,12 +684,12 @@ GEM
unf (~> 0.1.0) unf (~> 0.1.0)
tzinfo (2.0.5) tzinfo (2.0.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
tzinfo-data (1.2022.3) tzinfo-data (1.2022.4)
tzinfo (>= 1.0.0) tzinfo (>= 1.0.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8.2) unf_ext (0.0.8.2)
unicode-display_width (2.1.0) unicode-display_width (2.3.0)
uniform_notifier (1.16.0) uniform_notifier (1.16.0)
validate_email (0.1.6) validate_email (0.1.6)
activemodel (>= 3.0) activemodel (>= 3.0)
@ -764,11 +762,11 @@ DEPENDENCIES
devise-two-factor (~> 4.0) devise-two-factor (~> 4.0)
devise_pam_authenticatable2 (~> 9.2) devise_pam_authenticatable2 (~> 9.2)
discard (~> 1.2) discard (~> 1.2)
doorkeeper (~> 5.5) doorkeeper (~> 5.6)
dotenv-rails (~> 2.8) dotenv-rails (~> 2.8)
ed25519 (~> 1.3) ed25519 (~> 1.3)
fabrication (~> 2.30) fabrication (~> 2.30)
faker (~> 2.22) faker (~> 2.23)
fast_blank (~> 1.0) fast_blank (~> 1.0)
fastimage fastimage
fog-core (<= 2.1.0) fog-core (<= 2.1.0)
@ -781,7 +779,7 @@ DEPENDENCIES
htmlentities (~> 4.3) htmlentities (~> 4.3)
http (~> 5.1) http (~> 5.1)
http_accept_language (~> 2.1) http_accept_language (~> 2.1)
httplog (~> 1.5.0) httplog (~> 1.6.0)
i18n-tasks (~> 1.0) i18n-tasks (~> 1.0)
idn-ruby idn-ruby
json-ld json-ld
@ -820,7 +818,7 @@ DEPENDENCIES
rack (~> 2.2.4) rack (~> 2.2.4)
rack-attack (~> 6.6) rack-attack (~> 6.6)
rack-cors (~> 1.1) rack-cors (~> 1.1)
rails (~> 6.1.6) rails (~> 6.1.7)
rails-controller-testing (~> 1.0) rails-controller-testing (~> 1.0)
rails-i18n (~> 6.0) rails-i18n (~> 6.0)
rails-settings-cached (~> 0.6) rails-settings-cached (~> 0.6)

@ -10,10 +10,10 @@ class AboutController < ApplicationController
before_action :require_open_federation!, only: [:show, :more] before_action :require_open_federation!, only: [:show, :more]
before_action :set_body_classes, only: :show before_action :set_body_classes, only: :show
before_action :set_instance_presenter before_action :set_instance_presenter
before_action :set_expires_in, only: [:more, :terms] before_action :set_expires_in, only: [:more]
before_action :set_registration_form_time, only: :show before_action :set_registration_form_time, only: :show
skip_before_action :require_functional!, only: [:more, :terms] skip_before_action :require_functional!, only: [:more]
def show; end def show; end
@ -28,8 +28,6 @@ class AboutController < ApplicationController
@blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? @blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks?
end end
def terms; end
helper_method :display_blocks? helper_method :display_blocks?
helper_method :display_blocks_rationale? helper_method :display_blocks_rationale?
helper_method :public_fetch_mode? helper_method :public_fetch_mode?

@ -7,7 +7,7 @@ class AccountsController < ApplicationController
include AccountControllerConcern include AccountControllerConcern
include SignatureAuthentication include SignatureAuthentication
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers before_action :set_cache_headers
before_action :set_body_classes before_action :set_body_classes

@ -6,7 +6,7 @@ class ActivityPub::ClaimsController < ActivityPub::BaseController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :require_signature! before_action :require_account_signature!
before_action :set_claim_result before_action :set_claim_result
def create def create

@ -4,7 +4,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
include SignatureVerification include SignatureVerification
include AccountOwnedConcern include AccountOwnedConcern
before_action :require_signature!, if: :authorized_fetch_mode? before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_items before_action :set_items
before_action :set_size before_action :set_size
before_action :set_type before_action :set_type

@ -4,7 +4,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
include SignatureVerification include SignatureVerification
include AccountOwnedConcern include AccountOwnedConcern
before_action :require_signature! before_action :require_account_signature!
before_action :set_items before_action :set_items
before_action :set_cache_headers before_action :set_cache_headers

@ -6,7 +6,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
include AccountOwnedConcern include AccountOwnedConcern
before_action :skip_unknown_actor_activity before_action :skip_unknown_actor_activity
before_action :require_signature! before_action :require_actor_signature!
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
def create def create
@ -49,17 +49,17 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
end end
def upgrade_account def upgrade_account
if signed_request_account.ostatus? if signed_request_account&.ostatus?
signed_request_account.update(last_webfingered_at: nil) signed_request_account.update(last_webfingered_at: nil)
ResolveAccountWorker.perform_async(signed_request_account.acct) ResolveAccountWorker.perform_async(signed_request_account.acct)
end end
DeliveryFailureTracker.reset!(signed_request_account.inbox_url) DeliveryFailureTracker.reset!(signed_request_actor.inbox_url)
end end
def process_collection_synchronization def process_collection_synchronization
raw_params = request.headers['Collection-Synchronization'] raw_params = request.headers['Collection-Synchronization']
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil?
# Re-using the syntax for signature parameters # Re-using the syntax for signature parameters
tree = SignatureParamsParser.new.parse(raw_params) tree = SignatureParamsParser.new.parse(raw_params)
@ -71,6 +71,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
end end
def process_payload def process_payload
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id) ActivityPub::ProcessingWorker.perform_async(signed_request_actor.id, body, @account&.id, signed_request_actor.class.name)
end end
end end

@ -6,7 +6,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
include SignatureVerification include SignatureVerification
include AccountOwnedConcern include AccountOwnedConcern
before_action :require_signature!, if: :authorized_fetch_mode? before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_statuses before_action :set_statuses
before_action :set_cache_headers before_action :set_cache_headers

@ -7,7 +7,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
DESCENDANTS_LIMIT = 60 DESCENDANTS_LIMIT = 60
before_action :require_signature!, if: :authorized_fetch_mode? before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :set_status before_action :set_status
before_action :set_cache_headers before_action :set_cache_headers
before_action :set_replies before_action :set_replies

@ -131,4 +131,10 @@ class Api::BaseController < ApplicationController
def disallow_unauthenticated_api_access? def disallow_unauthenticated_api_access?
authorized_fetch_mode? authorized_fetch_mode?
end end
private
def respond_with_error(code)
render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code
end
end end

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Accounts::FollowerAccountsController < Api::BaseController class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' } before_action -> { authorize_if_got_token! :read, :'read:accounts' }
before_action :set_account before_action :set_account
after_action :insert_pagination_headers after_action :insert_pagination_headers

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Accounts::FollowingAccountsController < Api::BaseController class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' } before_action -> { authorize_if_got_token! :read, :'read:accounts' }
before_action :set_account before_action :set_account
after_action :insert_pagination_headers after_action :insert_pagination_headers

@ -30,12 +30,12 @@ class Api::V1::AccountsController < Api::BaseController
self.response_body = Oj.dump(response.body) self.response_body = Oj.dump(response.body)
self.status = response.status self.status = response.status
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
render json: ValidationErrorFormatter.new(e, :'account.username' => :username, :'invite_request.text' => :reason).as_json, status: :unprocessable_entity render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: :unprocessable_entity
end end
def follow def follow
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: true) follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, languages: params.key?(:languages) ? params[:languages] : nil, with_rate_limit: true)
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify? } }, requested_map: { @account.id => false } } options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify?, languages: follow.languages } }, requested_map: { @account.id => false } }
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
end end

@ -0,0 +1,29 @@
# frozen_string_literal: true
class Api::V1::Statuses::TranslationsController < Api::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :set_status
before_action :set_translation
rescue_from TranslationService::NotConfiguredError, with: :not_found
rescue_from TranslationService::UnexpectedResponseError, TranslationService::QuotaExceededError, TranslationService::TooManyRequestsError, with: :service_unavailable
def create
render json: @translation, serializer: REST::TranslationSerializer
end
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
end

@ -45,10 +45,14 @@ module SignatureVerification
end end
end end
def require_signature! def require_account_signature!
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
end end
def require_actor_signature!
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
end
def signed_request? def signed_request?
request.headers['Signature'].present? request.headers['Signature'].present?
end end
@ -68,7 +72,11 @@ module SignatureVerification
end end
def signed_request_account def signed_request_account
return @signed_request_account if defined?(@signed_request_account) signed_request_actor.is_a?(Account) ? signed_request_actor : nil
end
def signed_request_actor
return @signed_request_actor if defined?(@signed_request_actor)
raise SignatureVerificationError, 'Request not signed' unless signed_request? raise SignatureVerificationError, 'Request not signed' unless signed_request?
raise SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters? raise SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
@ -78,26 +86,30 @@ module SignatureVerification
verify_signature_strength! verify_signature_strength!
verify_body_digest! verify_body_digest!
account = account_from_key_id(signature_params['keyId']) actor = actor_from_key_id(signature_params['keyId'])
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil? raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
signature = Base64.decode64(signature_params['signature']) signature = Base64.decode64(signature_params['signature'])
compare_signed_string = build_signed_string compare_signed_string = build_signed_string
return account unless verify_signature(account, signature, compare_signed_string).nil? return actor unless verify_signature(actor, signature, compare_signed_string).nil?
account = stoplight_wrap_request { account.possibly_stale? ? account.refresh! : account_refresh_key(account) } actor = stoplight_wrap_request { actor_refresh_key!(actor) }
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil? raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
return account unless verify_signature(account, signature, compare_signed_string).nil? return actor unless verify_signature(actor, signature, compare_signed_string).nil?
@signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)" fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)"
@signed_request_account = nil
rescue SignatureVerificationError => e rescue SignatureVerificationError => e
@signature_verification_failure_reason = e.message fail_with! e.message
@signed_request_account = nil rescue HTTP::Error, OpenSSL::SSL::SSLError => e
fail_with! "Failed to fetch remote data: #{e.message}"
rescue Mastodon::UnexpectedResponseError
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
rescue Stoplight::Error::RedLight
fail_with! 'Fetching attempt skipped because of recent connection failure'
end end
def request_body def request_body
@ -106,6 +118,11 @@ module SignatureVerification
private private
def fail_with!(message)
@signature_verification_failure_reason = message
@signed_request_actor = nil
end
def signature_params def signature_params
@signature_params ||= begin @signature_params ||= begin
raw_signature = request.headers['Signature'] raw_signature = request.headers['Signature']
@ -138,13 +155,23 @@ module SignatureVerification
digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] } digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
sha256 = digests.assoc('sha-256') sha256 = digests.assoc('sha-256')
raise SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil? raise SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}" if body_digest != sha256[1]
return if body_digest == sha256[1]
digest_size = begin
Base64.strict_decode64(sha256[1].strip).length
rescue ArgumentError
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{sha256[1]}"
end
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
end end
def verify_signature(account, signature, compare_signed_string) def verify_signature(actor, signature, compare_signed_string)
if account.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string) if actor.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
@signed_request_account = account @signed_request_actor = actor
@signed_request_account @signed_request_actor
end end
rescue OpenSSL::PKey::RSAError rescue OpenSSL::PKey::RSAError
nil nil
@ -207,7 +234,7 @@ module SignatureVerification
signature_params['keyId'].blank? || signature_params['signature'].blank? signature_params['keyId'].blank? || signature_params['signature'].blank?
end end
def account_from_key_id(key_id) def actor_from_key_id(key_id)
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
if domain_not_allowed?(domain) if domain_not_allowed?(domain)
@ -216,27 +243,34 @@ module SignatureVerification
end end
if key_id.start_with?('acct:') if key_id.start_with?('acct:')
stoplight_wrap_request { ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, '')) } stoplight_wrap_request { ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, ''), suppress_errors: false) }
elsif !ActivityPub::TagManager.instance.local_uri?(key_id) elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account) account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false) } account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false, suppress_errors: false) }
account account
end end
rescue Mastodon::HostValidationError rescue Mastodon::PrivateNetworkAddressError => e
nil raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, ActivityPub::FetchRemoteKeyService::Error, Webfinger::Error => e
raise SignatureVerificationError, e.message
end end
def stoplight_wrap_request(&block) def stoplight_wrap_request(&block)
Stoplight("source:#{request.remote_ip}", &block) Stoplight("source:#{request.remote_ip}", &block)
.with_fallback { nil }
.with_threshold(1) .with_threshold(1)
.with_cool_off_time(5.minutes.seconds) .with_cool_off_time(5.minutes.seconds)
.with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) } .with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
.run .run
end end
def account_refresh_key(account) def actor_refresh_key!(actor)
return if account.local? || !account.activitypub? return if actor.local? || !actor.activitypub?
ActivityPub::FetchRemoteAccountService.new.call(account.uri, only_key: true) return actor.refresh! if actor.respond_to?(:refresh!) && actor.possibly_stale?
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
rescue Mastodon::PrivateNetworkAddressError => e
raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
raise SignatureVerificationError, e.message
end end
end end

@ -4,7 +4,7 @@ class FollowerAccountsController < ApplicationController
include AccountControllerConcern include AccountControllerConcern
include SignatureVerification include SignatureVerification
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json } skip_around_action :set_locale, if: -> { request.format == :json }

@ -4,7 +4,7 @@ class FollowingAccountsController < ApplicationController
include AccountControllerConcern include AccountControllerConcern
include SignatureVerification include SignatureVerification
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_cache_headers before_action :set_cache_headers
skip_around_action :set_locale, if: -> { request.format == :json } skip_around_action :set_locale, if: -> { request.format == :json }

@ -0,0 +1,22 @@
# frozen_string_literal: true
class PrivacyController < ApplicationController
layout 'public'
before_action :set_instance_presenter
before_action :set_expires_in
skip_before_action :require_functional!
def show; end
private
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def set_expires_in
expires_in 0, public: true
end
end

@ -8,7 +8,7 @@ class StatusesController < ApplicationController
layout 'public' layout 'public'
before_action :require_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? } before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
before_action :set_status before_action :set_status
before_action :set_instance_presenter before_action :set_instance_presenter
before_action :set_link_headers before_action :set_link_headers

@ -8,7 +8,7 @@ class TagsController < ApplicationController
layout 'public' layout 'public'
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :authenticate_user!, if: :whitelist_mode? before_action :authenticate_user!, if: :whitelist_mode?
before_action :set_local before_action :set_local
before_action :set_tag before_action :set_tag

@ -51,6 +51,7 @@ const messages = defineMessages({
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' }, add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' },
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
}); });
const dateFormatOptions = { const dateFormatOptions = {
@ -85,6 +86,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onEditAccountNote: PropTypes.func.isRequired, onEditAccountNote: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
domain: PropTypes.string.isRequired, domain: PropTypes.string.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
@ -215,6 +217,9 @@ class Header extends ImmutablePureComponent {
} else { } else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle }); menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} }
menu.push({ text: intl.formatMessage(messages.languages), action: this.props.onChangeLanguages });
menu.push(null);
} }
menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle }); menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });

@ -23,6 +23,7 @@ export default class Header extends ImmutablePureComponent {
onUnblockDomain: PropTypes.func.isRequired, onUnblockDomain: PropTypes.func.isRequired,
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
hideTabs: PropTypes.bool, hideTabs: PropTypes.bool,
domain: PropTypes.string.isRequired, domain: PropTypes.string.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
@ -92,6 +93,10 @@ export default class Header extends ImmutablePureComponent {
this.props.onEditAccountNote(this.props.account); this.props.onEditAccountNote(this.props.account);
} }
handleChangeLanguages = () => {
this.props.onChangeLanguages(this.props.account);
}
render () { render () {
const { account, hidden, hideTabs } = this.props; const { account, hidden, hideTabs } = this.props;
@ -118,6 +123,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle={this.handleEndorseToggle} onEndorseToggle={this.handleEndorseToggle}
onAddToList={this.handleAddToList} onAddToList={this.handleAddToList}
onEditAccountNote={this.handleEditAccountNote} onEditAccountNote={this.handleEditAccountNote}
onChangeLanguages={this.handleChangeLanguages}
domain={this.props.domain} domain={this.props.domain}
hidden={hidden} hidden={hidden}
/> />

@ -130,12 +130,18 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(unblockDomain(domain)); dispatch(unblockDomain(domain));
}, },
onAddToList(account){ onAddToList (account) {
dispatch(openModal('LIST_ADDER', { dispatch(openModal('LIST_ADDER', {
accountId: account.get('id'), accountId: account.get('id'),
})); }));
}, },
onChangeLanguages (account) {
dispatch(openModal('SUBSCRIBED_LANGUAGES', {
accountId: account.get('id'),
}));
},
}); });
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header)); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));

@ -7,6 +7,7 @@ import DisplayName from 'flavours/glitch/components/display_name';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import Option from './option'; import Option from './option';
import MediaAttachments from 'flavours/glitch/components/media_attachments'; import MediaAttachments from 'flavours/glitch/components/media_attachments';
import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
export default class StatusCheckBox extends React.PureComponent { export default class StatusCheckBox extends React.PureComponent {
@ -36,7 +37,7 @@ export default class StatusCheckBox extends React.PureComponent {
<Avatar account={status.get('account')} size={46} /> <Avatar account={status.get('account')} size={46} />
</div> </div>
<div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div> <div><DisplayName account={status.get('account')} /> · <VisibilityIcon visibility={status.get('visibility')} /><RelativeTimestamp timestamp={status.get('created_at')} /></div>
</div> </div>
<StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} /> <StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} />

@ -0,0 +1,125 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
import { languages as preloadedLanguages } from 'flavours/glitch/util/initial_state';
import Option from 'flavours/glitch/features/report/components/option';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import IconButton from 'flavours/glitch/components/icon_button';
import Button from 'flavours/glitch/components/button';
import { followAccount } from 'flavours/glitch/actions/accounts';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
});
const getAccountLanguages = createSelector([
(state, accountId) => state.getIn(['timelines', `account:${accountId}`, 'items'], ImmutableList()),
state => state.get('statuses'),
], (statusIds, statuses) =>
new ImmutableSet(statusIds.map(statusId => statuses.get(statusId)).filter(status => !status.get('reblog')).map(status => status.get('language'))));
const mapStateToProps = (state, { accountId }) => ({
acct: state.getIn(['accounts', accountId, 'acct']),
availableLanguages: getAccountLanguages(state, accountId),
selectedLanguages: ImmutableSet(state.getIn(['relationships', accountId, 'languages']) || ImmutableList()),
});
const mapDispatchToProps = (dispatch, { accountId }) => ({
onSubmit (languages) {
dispatch(followAccount(accountId, { languages }));
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class SubscribedLanguagesModal extends ImmutablePureComponent {
static propTypes = {
accountId: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
availableLanguages: ImmutablePropTypes.setOf(PropTypes.string),
selectedLanguages: ImmutablePropTypes.setOf(PropTypes.string),
onClose: PropTypes.func.isRequired,
languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
intl: PropTypes.object.isRequired,
submit: PropTypes.func.isRequired,
};
static defaultProps = {
languages: preloadedLanguages,
};
state = {
selectedLanguages: this.props.selectedLanguages,
};
handleLanguageToggle = (value, checked) => {
const { selectedLanguages } = this.state;
if (checked) {
this.setState({ selectedLanguages: selectedLanguages.add(value) });
} else {
this.setState({ selectedLanguages: selectedLanguages.delete(value) });
}
};
handleSubmit = () => {
this.props.onSubmit(this.state.selectedLanguages.toArray());
this.props.onClose();
}
renderItem (value) {
const language = this.props.languages.find(language => language[0] === value);
const checked = this.state.selectedLanguages.includes(value);
if (!language) {
return null;
}
return (
<Option
key={value}
name='languages'
value={value}
label={language[1]}
checked={checked}
onToggle={this.handleLanguageToggle}
multiple
/>
);
}
render () {
const { acct, availableLanguages, selectedLanguages, intl, onClose } = this.props;
return (
<div className='modal-root__modal report-dialog-modal'>
<div className='report-modal__target'>
<IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
<FormattedMessage id='subscribed_languages.target' defaultMessage='Change subscribed languages for {target}' values={{ target: <strong>{acct}</strong> }} />
</div>
<div className='report-dialog-modal__container'>
<p className='report-dialog-modal__lead'><FormattedMessage id='subscribed_languages.lead' defaultMessage='Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.' /></p>
<div>
{availableLanguages.union(selectedLanguages).delete(null).map(value => this.renderItem(value))}
</div>
<div className='flex-spacer' />
<div className='report-dialog-modal__actions'>
<Button disabled={is(this.state.selectedLanguages, this.props.selectedLanguages)} onClick={this.handleSubmit}><FormattedMessage id='subscribed_languages.save' defaultMessage='Save changes' /></Button>
</div>
</div>
</div>
);
}
}

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { limitedFederationMode, version, repository, source_url } from 'flavours/glitch/util/initial_state'; import { limitedFederationMode, version, repository, source_url } from 'flavours/glitch/util/initial_state';
import { signOutLink, securityLink } from 'flavours/glitch/util/backend_links'; import { signOutLink, securityLink, privacyPolicyLink } from 'flavours/glitch/util/backend_links';
import { logOut } from 'flavours/glitch/util/log_out'; import { logOut } from 'flavours/glitch/util/log_out';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions'; import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions';
@ -55,7 +55,7 @@ class LinkFooter extends React.PureComponent {
{!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>} {!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>}
{!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>} {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li> <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li> <li><a href={privacyPolicyLink} target='_blank'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></a> · </li>
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li> <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li> <li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
<li><a href={signOutLink} onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li> <li><a href={signOutLink} onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>

@ -13,6 +13,7 @@ import FavouriteModal from './favourite_modal';
import AudioModal from './audio_modal'; import AudioModal from './audio_modal';
import DoodleModal from './doodle_modal'; import DoodleModal from './doodle_modal';
import ConfirmationModal from './confirmation_modal'; import ConfirmationModal from './confirmation_modal';
import SubscribedLanguagesModal from 'flavours/glitch/features/subscribed_languages_modal';
import FocalPointModal from './focal_point_modal'; import FocalPointModal from './focal_point_modal';
import DeprecatedSettingsModal from './deprecated_settings_modal'; import DeprecatedSettingsModal from './deprecated_settings_modal';
import { import {
@ -51,6 +52,7 @@ const MODAL_COMPONENTS = {
'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor, 'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor,
'COMPARE_HISTORY': CompareHistoryModal, 'COMPARE_HISTORY': CompareHistoryModal,
'FILTER': FilterModal, 'FILTER': FilterModal,
'SUBSCRIBED_LANGUAGES': () => Promise.resolve({ default: SubscribedLanguagesModal }),
}; };
export default class ModalRoot extends React.PureComponent { export default class ModalRoot extends React.PureComponent {

@ -1,7 +1,7 @@
export const preferencesLink = '/settings/preferences'; export const preferencesLink = '/settings/preferences';
export const profileLink = '/settings/profile'; export const profileLink = '/settings/profile';
export const signOutLink = '/auth/sign_out'; export const signOutLink = '/auth/sign_out';
export const termsLink = '/terms'; export const privacyPolicyLink = '/privacy-policy';
export const accountAdminLink = (id) => `/admin/accounts/${id}`; export const accountAdminLink = (id) => `/admin/accounts/${id}`;
export const statusAdminLink = (account_id, status_id) => `/admin/accounts/${account_id}/statuses?id=${status_id}`; export const statusAdminLink = (account_id, status_id) => `/admin/accounts/${account_id}/statuses?id=${status_id}`;
export const filterEditLink = (id) => `/filters/${id}/edit`; export const filterEditLink = (id) => `/filters/${id}/edit`;

@ -536,10 +536,12 @@ export function expandFollowingFail(id, error) {
export function fetchRelationships(accountIds) { export function fetchRelationships(accountIds) {
return (dispatch, getState) => { return (dispatch, getState) => {
const loadedRelationships = getState().get('relationships'); const state = getState();
const loadedRelationships = state.get('relationships');
const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null); const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
const signedIn = !!state.getIn(['meta', 'me']);
if (newAccountIds.length === 0) { if (!signedIn || newAccountIds.length === 0) {
return; return;
} }

@ -1,6 +1,7 @@
import api from '../api'; import api from '../api';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import compareId from '../compare_id'; import compareId from '../compare_id';
import { List as ImmutableList } from 'immutable';
export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST'; export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS'; export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
@ -11,7 +12,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
const accessToken = getState().getIn(['meta', 'access_token'], ''); const accessToken = getState().getIn(['meta', 'access_token'], '');
const params = _buildParams(getState()); const params = _buildParams(getState());
if (Object.keys(params).length === 0) { if (Object.keys(params).length === 0 || accessToken === '') {
return; return;
} }
@ -63,7 +64,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
const _buildParams = (state) => { const _buildParams = (state) => {
const params = {}; const params = {};
const lastHomeId = state.getIn(['timelines', 'home', 'items']).find(item => item !== null); const lastHomeId = state.getIn(['timelines', 'home', 'items'], ImmutableList()).find(item => item !== null);
const lastNotificationId = state.getIn(['notifications', 'lastReadId']); const lastNotificationId = state.getIn(['notifications', 'lastReadId']);
if (lastHomeId && compareId(lastHomeId, state.getIn(['markers', 'home'])) > 0) { if (lastHomeId && compareId(lastHomeId, state.getIn(['markers', 'home'])) > 0) {
@ -82,9 +83,10 @@ const _buildParams = (state) => {
}; };
const debouncedSubmitMarkers = debounce((dispatch, getState) => { const debouncedSubmitMarkers = debounce((dispatch, getState) => {
const params = _buildParams(getState()); const accessToken = getState().getIn(['meta', 'access_token'], '');
const params = _buildParams(getState());
if (Object.keys(params).length === 0) { if (Object.keys(params).length === 0 || accessToken === '') {
return; return;
} }

@ -34,6 +34,11 @@ export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST';
export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS'; export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS';
export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL'; export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL';
export const STATUS_TRANSLATE_REQUEST = 'STATUS_TRANSLATE_REQUEST';
export const STATUS_TRANSLATE_SUCCESS = 'STATUS_TRANSLATE_SUCCESS';
export const STATUS_TRANSLATE_FAIL = 'STATUS_TRANSLATE_FAIL';
export const STATUS_TRANSLATE_UNDO = 'STATUS_TRANSLATE_UNDO';
export function fetchStatusRequest(id, skipLoading) { export function fetchStatusRequest(id, skipLoading) {
return { return {
type: STATUS_FETCH_REQUEST, type: STATUS_FETCH_REQUEST,
@ -309,4 +314,36 @@ export function toggleStatusCollapse(id, isCollapsed) {
id, id,
isCollapsed, isCollapsed,
}; };
} };
export const translateStatus = id => (dispatch, getState) => {
dispatch(translateStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/translate`).then(response => {
dispatch(translateStatusSuccess(id, response.data));
}).catch(error => {
dispatch(translateStatusFail(id, error));
});
};
export const translateStatusRequest = id => ({
type: STATUS_TRANSLATE_REQUEST,
id,
});
export const translateStatusSuccess = (id, translation) => ({
type: STATUS_TRANSLATE_SUCCESS,
id,
translation,
});
export const translateStatusFail = (id, error) => ({
type: STATUS_TRANSLATE_FAIL,
id,
error,
});
export const undoStatusTranslation = id => ({
type: STATUS_TRANSLATE_UNDO,
id,
});

@ -6,7 +6,7 @@ exports[`<Avatar /> Autoplay renders a animated avatar 1`] = `
onMouseEnter={[Function]} onMouseEnter={[Function]}
onMouseLeave={[Function]} onMouseLeave={[Function]}
style={ style={
Object { {
"backgroundImage": "url(/animated/alice.gif)", "backgroundImage": "url(/animated/alice.gif)",
"backgroundSize": "100px 100px", "backgroundSize": "100px 100px",
"height": "100px", "height": "100px",
@ -22,7 +22,7 @@ exports[`<Avatar /> Still renders a still avatar 1`] = `
onMouseEnter={[Function]} onMouseEnter={[Function]}
onMouseLeave={[Function]} onMouseLeave={[Function]}
style={ style={
Object { {
"backgroundImage": "url(/static/alice.jpg)", "backgroundImage": "url(/static/alice.jpg)",
"backgroundSize": "100px 100px", "backgroundSize": "100px 100px",
"height": "100px", "height": "100px",

@ -7,7 +7,7 @@ exports[`<AvatarOverlay renders a overlay avatar 1`] = `
<div <div
className="account__avatar-overlay-base" className="account__avatar-overlay-base"
style={ style={
Object { {
"backgroundImage": "url(/static/alice.jpg)", "backgroundImage": "url(/static/alice.jpg)",
} }
} }
@ -15,7 +15,7 @@ exports[`<AvatarOverlay renders a overlay avatar 1`] = `
<div <div
className="account__avatar-overlay-overlay" className="account__avatar-overlay-overlay"
style={ style={
Object { {
"backgroundImage": "url(/static/eve.jpg)", "backgroundImage": "url(/static/eve.jpg)",
} }
} }

@ -10,7 +10,7 @@ exports[`<DisplayName /> renders display name + account name 1`] = `
<strong <strong
className="display-name__html" className="display-name__html"
dangerouslySetInnerHTML={ dangerouslySetInnerHTML={
Object { {
"__html": "<p>Foo</p>", "__html": "<p>Foo</p>",
} }
} }

@ -17,6 +17,7 @@ class ColumnHeader extends React.PureComponent {
static contextTypes = { static contextTypes = {
router: PropTypes.object, router: PropTypes.object,
identity: PropTypes.object,
}; };
static propTypes = { static propTypes = {
@ -145,7 +146,7 @@ class ColumnHeader extends React.PureComponent {
collapsedContent.push(moveButtons); collapsedContent.push(moveButtons);
} }
if (children || (multiColumn && this.props.onPin)) { if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
collapseButton = ( collapseButton = (
<button <button
className={collapsibleButtonClassName} className={collapsibleButtonClassName}

@ -1,7 +1,7 @@
// @ts-check // @ts-check
import React from 'react'; import React from 'react';
import { Sparklines, SparklinesCurve } from 'react-sparklines'; import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from './permalink'; import Permalink from './permalink';
@ -9,10 +9,6 @@ import ShortNumber from 'mastodon/components/short_number';
import Skeleton from 'mastodon/components/skeleton'; import Skeleton from 'mastodon/components/skeleton';
import classNames from 'classnames'; import classNames from 'classnames';
const messages = defineMessages({
totalVolume: { id: 'hashtag.total_volume', defaultMessage: 'Total volume in the last {days, plural, one {day} other {{days} days}}' },
});
class SilentErrorBoundary extends React.Component { class SilentErrorBoundary extends React.Component {
static propTypes = { static propTypes = {
@ -69,7 +65,7 @@ ImmutableHashtag.propTypes = {
hashtag: ImmutablePropTypes.map.isRequired, hashtag: ImmutablePropTypes.map.isRequired,
}; };
const Hashtag = injectIntl(({ name, href, to, people, uses, history, className, intl }) => ( const Hashtag = ({ name, href, to, people, history, className }) => (
<div className={classNames('trends__item', className)}> <div className={classNames('trends__item', className)}>
<div className='trends__item__name'> <div className='trends__item__name'>
<Permalink href={href} to={to}> <Permalink href={href} to={to}>
@ -79,11 +75,6 @@ const Hashtag = injectIntl(({ name, href, to, people, uses, history, className,
{typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />} {typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}
</div> </div>
<abbr className='trends__item__current' title={intl.formatMessage(messages.totalVolume, { days: 2 })}>
{typeof uses !== 'undefined' ? <ShortNumber value={uses} /> : <Skeleton width={42} height={36} />}
<span className='trends__item__current__asterisk'>*</span>
</abbr>
<div className='trends__item__sparkline'> <div className='trends__item__sparkline'>
<SilentErrorBoundary> <SilentErrorBoundary>
<Sparklines width={50} height={28} data={history ? history : Array.from(Array(7)).map(() => 0)}> <Sparklines width={50} height={28} data={history ? history : Array.from(Array(7)).map(() => 0)}>
@ -92,7 +83,7 @@ const Hashtag = injectIntl(({ name, href, to, people, uses, history, className,
</SilentErrorBoundary> </SilentErrorBoundary>
</div> </div>
</div> </div>
)); );
Hashtag.propTypes = { Hashtag.propTypes = {
name: PropTypes.string, name: PropTypes.string,

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
const Logo = () => ( const Logo = () => (
<svg viewBox='0 0 216.4144 232.00976' className='logo'> <svg viewBox='0 0 261 66' className='logo'>
<use xlinkHref='#mastodon-svg-logo' /> <use xlinkHref='#logo-symbol-wordmark' />
</svg> </svg>
); );

@ -34,6 +34,10 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
export default @injectIntl export default @injectIntl
class Poll extends ImmutablePureComponent { class Poll extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
poll: ImmutablePropTypes.map, poll: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -217,7 +221,7 @@ class Poll extends ImmutablePureComponent {
</ul> </ul>
<div className='poll__footer'> <div className='poll__footer'>
{!showResults && <button className='button button-secondary' disabled={disabled} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>} {!showResults && <button className='button button-secondary' disabled={disabled || !this.context.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
{showResults && !this.props.disabled && <span><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </span>} {showResults && !this.props.disabled && <span><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </span>}
{votesCount} {votesCount}
{poll.get('expires_at') && <span> · {timeRemaining}</span>} {poll.get('expires_at') && <span> · {timeRemaining}</span>}

@ -85,6 +85,7 @@ class Status extends ImmutablePureComponent {
onHeightChange: PropTypes.func, onHeightChange: PropTypes.func,
onToggleHidden: PropTypes.func, onToggleHidden: PropTypes.func,
onToggleCollapsed: PropTypes.func, onToggleCollapsed: PropTypes.func,
onTranslate: PropTypes.func,
muted: PropTypes.bool, muted: PropTypes.bool,
hidden: PropTypes.bool, hidden: PropTypes.bool,
unread: PropTypes.bool, unread: PropTypes.bool,
@ -171,6 +172,10 @@ class Status extends ImmutablePureComponent {
this.props.onToggleCollapsed(this._properStatus(), isCollapsed); this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
} }
handleTranslate = () => {
this.props.onTranslate(this._properStatus());
}
renderLoadingMediaGallery () { renderLoadingMediaGallery () {
return <div className='media-gallery' style={{ height: '110px' }} />; return <div className='media-gallery' style={{ height: '110px' }} />;
} }
@ -512,7 +517,16 @@ class Status extends ImmutablePureComponent {
</a> </a>
</div> </div>
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} showThread={showThread} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} /> <StatusContent
status={status}
onClick={this.handleClick}
expanded={!status.get('hidden')}
showThread={showThread}
onExpandedToggle={this.handleExpandedToggle}
onTranslate={this.handleTranslate}
collapsable
onCollapsedToggle={this.handleCollapsedToggle}
/>
{media} {media}

@ -1,19 +1,21 @@
import React from 'react'; import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage, injectIntl } from 'react-intl';
import Permalink from './permalink'; import Permalink from './permalink';
import classnames from 'classnames'; import classnames from 'classnames';
import PollContainer from 'mastodon/containers/poll_container'; import PollContainer from 'mastodon/containers/poll_container';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { autoPlayGif } from 'mastodon/initial_state'; import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
export default class StatusContent extends React.PureComponent { export default @injectIntl
class StatusContent extends React.PureComponent {
static contextTypes = { static contextTypes = {
router: PropTypes.object, router: PropTypes.object,
identity: PropTypes.object,
}; };
static propTypes = { static propTypes = {
@ -21,9 +23,11 @@ export default class StatusContent extends React.PureComponent {
expanded: PropTypes.bool, expanded: PropTypes.bool,
showThread: PropTypes.bool, showThread: PropTypes.bool,
onExpandedToggle: PropTypes.func, onExpandedToggle: PropTypes.func,
onTranslate: PropTypes.func,
onClick: PropTypes.func, onClick: PropTypes.func,
collapsable: PropTypes.bool, collapsable: PropTypes.bool,
onCollapsedToggle: PropTypes.func, onCollapsedToggle: PropTypes.func,
intl: PropTypes.object,
}; };
state = { state = {
@ -163,20 +167,27 @@ export default class StatusContent extends React.PureComponent {
} }
} }
handleTranslate = () => {
this.props.onTranslate();
}
setRef = (c) => { setRef = (c) => {
this.node = c; this.node = c;
} }
render () { render () {
const { status } = this.props; const { status, intl } = this.props;
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
const renderReadMore = this.props.onClick && status.get('collapsed'); const renderReadMore = this.props.onClick && status.get('collapsed');
const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']); const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
const renderTranslate = this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && intl.locale !== status.get('language');
const language = preloadedLanguages.find(lang => lang[0] === status.get('language'));
const languageName = language ? language[2] : status.get('language');
const content = { __html: status.get('contentHtml') }; const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
const spoilerContent = { __html: status.get('spoilerHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') };
const lang = status.get('language'); const lang = status.get('translation') ? intl.locale : status.get('language');
const classNames = classnames('status__content', { const classNames = classnames('status__content', {
'status__content--with-action': this.props.onClick && this.context.router, 'status__content--with-action': this.props.onClick && this.context.router,
'status__content--with-spoiler': status.get('spoiler_text').length > 0, 'status__content--with-spoiler': status.get('spoiler_text').length > 0,
@ -195,6 +206,12 @@ export default class StatusContent extends React.PureComponent {
</button> </button>
); );
const translateButton = (
<button className='status__content__read-more-button' onClick={this.handleTranslate}>
{status.get('translation') ? <span><FormattedMessage id='status.translated_from' defaultMessage='Translated from {lang}' values={{ lang: languageName }} /> · <FormattedMessage id='status.show_original' defaultMessage='Show original' /></span> : <FormattedMessage id='status.translate' defaultMessage='Translate' />}
</button>
);
if (status.get('spoiler_text').length > 0) { if (status.get('spoiler_text').length > 0) {
let mentionsPlaceholder = ''; let mentionsPlaceholder = '';
@ -223,7 +240,7 @@ export default class StatusContent extends React.PureComponent {
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''} translate`} lang={lang} dangerouslySetInnerHTML={content} /> <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''} translate`} lang={lang} dangerouslySetInnerHTML={content} />
{!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{!hidden && renderTranslate && translateButton}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div> </div>
); );
@ -233,7 +250,7 @@ export default class StatusContent extends React.PureComponent {
<div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} /> <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} />
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{renderTranslate && translateButton}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div>, </div>,
]; ];
@ -249,7 +266,7 @@ export default class StatusContent extends React.PureComponent {
<div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} /> <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} />
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{renderTranslate && translateButton}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div> </div>
); );

@ -26,7 +26,7 @@ const createIdentityContext = state => ({
signedIn: !!state.meta.me, signedIn: !!state.meta.me,
accountId: state.meta.me, accountId: state.meta.me,
accessToken: state.meta.access_token, accessToken: state.meta.access_token,
permissions: state.role.permissions, permissions: state.role ? state.role.permissions : 0,
}); });
export default class Mastodon extends React.PureComponent { export default class Mastodon extends React.PureComponent {

@ -25,6 +25,8 @@ import {
revealStatus, revealStatus,
toggleStatusCollapse, toggleStatusCollapse,
editStatus, editStatus,
translateStatus,
undoStatusTranslation,
} from '../actions/statuses'; } from '../actions/statuses';
import { import {
unmuteAccount, unmuteAccount,
@ -150,6 +152,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
dispatch(editStatus(status.get('id'), history)); dispatch(editStatus(status.get('id'), history));
}, },
onTranslate (status) {
if (status.get('translation')) {
dispatch(undoStatusTranslation(status.get('id')));
} else {
dispatch(translateStatus(status.get('id')));
}
},
onDirect (account, router) { onDirect (account, router) {
dispatch(directCompose(account, router)); dispatch(directCompose(account, router));
}, },

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Button from 'mastodon/components/button'; import Button from 'mastodon/components/button';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { autoPlayGif, me } from 'mastodon/initial_state'; import { autoPlayGif, me, title, domain } from 'mastodon/initial_state';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import IconButton from 'mastodon/components/icon_button'; import IconButton from 'mastodon/components/icon_button';
@ -15,6 +15,7 @@ import { NavLink } from 'react-router-dom';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import AccountNoteContainer from '../containers/account_note_container'; import AccountNoteContainer from '../containers/account_note_container';
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
import { Helmet } from 'react-helmet';
const messages = defineMessages({ const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@ -51,8 +52,17 @@ const messages = defineMessages({
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
}); });
const titleFromAccount = account => {
const displayName = account.get('display_name');
const acct = account.get('acct') === account.get('username') ? `${account.get('username')}@${domain}` : account.get('acct');
const prefix = displayName.trim().length === 0 ? account.get('username') : displayName;
return `${prefix} (@${acct})`;
};
const dateFormatOptions = { const dateFormatOptions = {
month: 'short', month: 'short',
day: 'numeric', day: 'numeric',
@ -85,6 +95,7 @@ class Header extends ImmutablePureComponent {
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onEditAccountNote: PropTypes.func.isRequired, onEditAccountNote: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
domain: PropTypes.string.isRequired, domain: PropTypes.string.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
@ -130,6 +141,7 @@ class Header extends ImmutablePureComponent {
render () { render () {
const { account, hidden, intl, domain } = this.props; const { account, hidden, intl, domain } = this.props;
const { signedIn } = this.context.identity;
if (!account) { if (!account) {
return null; return null;
@ -160,12 +172,12 @@ class Header extends ImmutablePureComponent {
} }
if (me !== account.get('id')) { if (me !== account.get('id')) {
if (!account.get('relationship')) { // Wait until the relationship is loaded if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
actionBtn = ''; actionBtn = '';
} else if (account.getIn(['relationship', 'requested'])) { } else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className={classNames('logo-button', { 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />; actionBtn = <Button className={classNames('logo-button', { 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) { } else if (!account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />; actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : undefined} />;
} else if (account.getIn(['relationship', 'blocking'])) { } else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />; actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
} }
@ -181,7 +193,7 @@ class Header extends ImmutablePureComponent {
lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />; lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />;
} }
if (account.get('id') !== me) { if (signedIn && account.get('id') !== me) {
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention }); menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect }); menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
menu.push(null); menu.push(null);
@ -204,7 +216,7 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
} else { } else if (signedIn) {
if (account.getIn(['relationship', 'following'])) { if (account.getIn(['relationship', 'following'])) {
if (!account.getIn(['relationship', 'muting'])) { if (!account.getIn(['relationship', 'muting'])) {
if (account.getIn(['relationship', 'showing_reblogs'])) { if (account.getIn(['relationship', 'showing_reblogs'])) {
@ -212,6 +224,9 @@ class Header extends ImmutablePureComponent {
} else { } else {
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle }); menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
} }
menu.push({ text: intl.formatMessage(messages.languages), action: this.props.onChangeLanguages });
menu.push(null);
} }
menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle }); menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
@ -234,7 +249,7 @@ class Header extends ImmutablePureComponent {
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport }); menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
} }
if (account.get('acct') !== account.get('username')) { if (signedIn && account.get('acct') !== account.get('username')) {
const domain = account.get('acct').split('@')[1]; const domain = account.get('acct').split('@')[1];
menu.push(null); menu.push(null);
@ -293,7 +308,7 @@ class Header extends ImmutablePureComponent {
</React.Fragment> </React.Fragment>
)} )}
<DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' /> <DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' size={24} direction='right' />
</div> </div>
)} )}
</div> </div>
@ -322,7 +337,7 @@ class Header extends ImmutablePureComponent {
</div> </div>
)} )}
{account.get('id') !== me && <AccountNoteContainer account={account} />} {(account.get('id') !== me && signedIn) && <AccountNoteContainer account={account} />}
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />} {account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
@ -354,6 +369,10 @@ class Header extends ImmutablePureComponent {
</div> </div>
)} )}
</div> </div>
<Helmet>
<title>{titleFromAccount(account)} - {title}</title>
</Helmet>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ export default class Header extends ImmutablePureComponent {
onUnblockDomain: PropTypes.func.isRequired, onUnblockDomain: PropTypes.func.isRequired,
onEndorseToggle: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired,
onAddToList: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired,
onChangeLanguages: PropTypes.func.isRequired,
hideTabs: PropTypes.bool, hideTabs: PropTypes.bool,
domain: PropTypes.string.isRequired, domain: PropTypes.string.isRequired,
hidden: PropTypes.bool, hidden: PropTypes.bool,
@ -91,6 +92,10 @@ export default class Header extends ImmutablePureComponent {
this.props.onEditAccountNote(this.props.account); this.props.onEditAccountNote(this.props.account);
} }
handleChangeLanguages = () => {
this.props.onChangeLanguages(this.props.account);
}
render () { render () {
const { account, hidden, hideTabs } = this.props; const { account, hidden, hideTabs } = this.props;
@ -117,6 +122,7 @@ export default class Header extends ImmutablePureComponent {
onEndorseToggle={this.handleEndorseToggle} onEndorseToggle={this.handleEndorseToggle}
onAddToList={this.handleAddToList} onAddToList={this.handleAddToList}
onEditAccountNote={this.handleEditAccountNote} onEditAccountNote={this.handleEditAccountNote}
onChangeLanguages={this.handleChangeLanguages}
domain={this.props.domain} domain={this.props.domain}
hidden={hidden} hidden={hidden}
/> />

@ -121,12 +121,18 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(unblockDomain(domain)); dispatch(unblockDomain(domain));
}, },
onAddToList(account){ onAddToList (account) {
dispatch(openModal('LIST_ADDER', { dispatch(openModal('LIST_ADDER', {
accountId: account.get('id'), accountId: account.get('id'),
})); }));
}, },
onChangeLanguages (account) {
dispatch(openModal('SUBSCRIBED_LANGUAGES', {
accountId: account.get('id'),
}));
},
}); });
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header)); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));

@ -9,6 +9,8 @@ import { expandCommunityTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { connectCommunityStream } from '../../actions/streaming'; import { connectCommunityStream } from '../../actions/streaming';
import { Helmet } from 'react-helmet';
import { title } from 'mastodon/initial_state';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.community', defaultMessage: 'Local timeline' }, title: { id: 'column.community', defaultMessage: 'Local timeline' },
@ -128,6 +130,10 @@ class CommunityTimeline extends React.PureComponent {
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
/> />
<Helmet>
<title>{intl.formatMessage(messages.title)} - {title}</title>
</Helmet>
</Column> </Column>
); );
} }

@ -13,6 +13,8 @@ import RadioButton from 'mastodon/components/radio_button';
import LoadMore from 'mastodon/components/load_more'; import LoadMore from 'mastodon/components/load_more';
import ScrollContainer from 'mastodon/containers/scroll_container'; import ScrollContainer from 'mastodon/containers/scroll_container';
import LoadingIndicator from 'mastodon/components/loading_indicator'; import LoadingIndicator from 'mastodon/components/loading_indicator';
import { title } from 'mastodon/initial_state';
import { Helmet } from 'react-helmet';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.directory', defaultMessage: 'Browse profiles' }, title: { id: 'column.directory', defaultMessage: 'Browse profiles' },
@ -165,6 +167,10 @@ class Directory extends React.PureComponent {
/> />
{multiColumn && !pinned ? <ScrollContainer scrollKey='directory'>{scrollableArea}</ScrollContainer> : scrollableArea} {multiColumn && !pinned ? <ScrollContainer scrollKey='directory'>{scrollableArea}</ScrollContainer> : scrollableArea}
<Helmet>
<title>{intl.formatMessage(messages.title)} - {title}</title>
</Helmet>
</Column> </Column>
); );
} }

@ -11,6 +11,8 @@ import Statuses from './statuses';
import Suggestions from './suggestions'; import Suggestions from './suggestions';
import Search from 'mastodon/features/compose/containers/search_container'; import Search from 'mastodon/features/compose/containers/search_container';
import SearchResults from './results'; import SearchResults from './results';
import { Helmet } from 'react-helmet';
import { title } from 'mastodon/initial_state';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'explore.title', defaultMessage: 'Explore' }, title: { id: 'explore.title', defaultMessage: 'Explore' },
@ -81,6 +83,10 @@ class Explore extends React.PureComponent {
<Route path='/explore/suggestions' component={Suggestions} /> <Route path='/explore/suggestions' component={Suggestions} />
<Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} /> <Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} />
</Switch> </Switch>
<Helmet>
<title>{intl.formatMessage(messages.title)} - {title}</title>
</Helmet>
</React.Fragment> </React.Fragment>
)} )}
</div> </div>

@ -5,6 +5,7 @@ import Story from './components/story';
import LoadingIndicator from 'mastodon/components/loading_indicator'; import LoadingIndicator from 'mastodon/components/loading_indicator';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchTrendingLinks } from 'mastodon/actions/trends'; import { fetchTrendingLinks } from 'mastodon/actions/trends';
import { FormattedMessage } from 'react-intl';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
links: state.getIn(['trends', 'links', 'items']), links: state.getIn(['trends', 'links', 'items']),
@ -28,6 +29,16 @@ class Links extends React.PureComponent {
render () { render () {
const { isLoading, links } = this.props; const { isLoading, links } = this.props;
if (!isLoading && links.isEmpty()) {
return (
<div className='explore__links scrollable scrollable--flex'>
<div className='empty-column-indicator'>
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
</div>
</div>
);
}
return ( return (
<div className='explore__links'> <div className='explore__links'>
{isLoading ? (<LoadingIndicator />) : links.map(link => ( {isLoading ? (<LoadingIndicator />) : links.map(link => (

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl'; import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { expandSearch } from 'mastodon/actions/search'; import { expandSearch } from 'mastodon/actions/search';
import Account from 'mastodon/containers/account_container'; import Account from 'mastodon/containers/account_container';
@ -10,10 +10,17 @@ import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import LoadMore from 'mastodon/components/load_more'; import LoadMore from 'mastodon/components/load_more';
import LoadingIndicator from 'mastodon/components/loading_indicator'; import LoadingIndicator from 'mastodon/components/loading_indicator';
import { title } from 'mastodon/initial_state';
import { Helmet } from 'react-helmet';
const messages = defineMessages({
title: { id: 'search_results.title', defaultMessage: 'Search for {q}' },
});
const mapStateToProps = state => ({ const mapStateToProps = state => ({
isLoading: state.getIn(['search', 'isLoading']), isLoading: state.getIn(['search', 'isLoading']),
results: state.getIn(['search', 'results']), results: state.getIn(['search', 'results']),
q: state.getIn(['search', 'searchTerm']),
}); });
const appendLoadMore = (id, list, onLoadMore) => { const appendLoadMore = (id, list, onLoadMore) => {
@ -37,6 +44,7 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul
)), onLoadMore); )), onLoadMore);
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@injectIntl
class Results extends React.PureComponent { class Results extends React.PureComponent {
static propTypes = { static propTypes = {
@ -44,6 +52,8 @@ class Results extends React.PureComponent {
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
q: PropTypes.string,
intl: PropTypes.object,
}; };
state = { state = {
@ -64,7 +74,7 @@ class Results extends React.PureComponent {
} }
render () { render () {
const { isLoading, results } = this.props; const { intl, isLoading, q, results } = this.props;
const { type } = this.state; const { type } = this.state;
let filteredResults = ImmutableList(); let filteredResults = ImmutableList();
@ -106,6 +116,10 @@ class Results extends React.PureComponent {
<div className='explore__search-results'> <div className='explore__search-results'>
{isLoading ? <LoadingIndicator /> : filteredResults} {isLoading ? <LoadingIndicator /> : filteredResults}
</div> </div>
<Helmet>
<title>{intl.formatMessage(messages.title, { q })} - {title}</title>
</Helmet>
</React.Fragment> </React.Fragment>
); );
} }

@ -5,6 +5,7 @@ import AccountCard from 'mastodon/features/directory/components/account_card';
import LoadingIndicator from 'mastodon/components/loading_indicator'; import LoadingIndicator from 'mastodon/components/loading_indicator';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchSuggestions } from 'mastodon/actions/suggestions'; import { fetchSuggestions } from 'mastodon/actions/suggestions';
import { FormattedMessage } from 'react-intl';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
suggestions: state.getIn(['suggestions', 'items']), suggestions: state.getIn(['suggestions', 'items']),
@ -28,6 +29,16 @@ class Suggestions extends React.PureComponent {
render () { render () {
const { isLoading, suggestions } = this.props; const { isLoading, suggestions } = this.props;
if (!isLoading && suggestions.isEmpty()) {
return (
<div className='explore__suggestions scrollable scrollable--flex'>
<div className='empty-column-indicator'>
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
</div>
</div>
);
}
return ( return (
<div className='explore__suggestions'> <div className='explore__suggestions'>
{isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => ( {isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => (

@ -5,6 +5,7 @@ import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
import LoadingIndicator from 'mastodon/components/loading_indicator'; import LoadingIndicator from 'mastodon/components/loading_indicator';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchTrendingHashtags } from 'mastodon/actions/trends'; import { fetchTrendingHashtags } from 'mastodon/actions/trends';
import { FormattedMessage } from 'react-intl';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
hashtags: state.getIn(['trends', 'tags', 'items']), hashtags: state.getIn(['trends', 'tags', 'items']),
@ -28,6 +29,16 @@ class Tags extends React.PureComponent {
render () { render () {
const { isLoading, hashtags } = this.props; const { isLoading, hashtags } = this.props;
if (!isLoading && hashtags.isEmpty()) {
return (
<div className='explore__links scrollable scrollable--flex'>
<div className='empty-column-indicator'>
<FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />
</div>
</div>
);
}
return ( return (
<div className='explore__links'> <div className='explore__links'>
{isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => ( {isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => (

@ -10,7 +10,6 @@ import { requestBrowserPermission } from 'mastodon/actions/notifications';
import { markAsPartial } from 'mastodon/actions/timelines'; import { markAsPartial } from 'mastodon/actions/timelines';
import Column from 'mastodon/features/ui/components/column'; import Column from 'mastodon/features/ui/components/column';
import Account from './components/account'; import Account from './components/account';
import Logo from 'mastodon/components/logo';
import imageGreeting from 'mastodon/../images/elephant_ui_greeting.svg'; import imageGreeting from 'mastodon/../images/elephant_ui_greeting.svg';
import Button from 'mastodon/components/button'; import Button from 'mastodon/components/button';
@ -78,7 +77,10 @@ class FollowRecommendations extends ImmutablePureComponent {
<Column> <Column>
<div className='scrollable follow-recommendations-container'> <div className='scrollable follow-recommendations-container'>
<div className='column-title'> <div className='column-title'>
<Logo /> <svg viewBox='0 0 79 79' className='logo'>
<use xlinkHref='#logo-symbol-icon' />
</svg>
<h3><FormattedMessage id='follow_recommendations.heading' defaultMessage="Follow people you'd like to see posts from! Here are some suggestions." /></h3> <h3><FormattedMessage id='follow_recommendations.heading' defaultMessage="Follow people you'd like to see posts from! Here are some suggestions." /></h3>
<p><FormattedMessage id='follow_recommendations.lead' defaultMessage="Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!" /></p> <p><FormattedMessage id='follow_recommendations.lead' defaultMessage="Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!" /></p>
</div> </div>

@ -14,6 +14,8 @@ import { isEqual } from 'lodash';
import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/tags'; import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/tags';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import classNames from 'classnames'; import classNames from 'classnames';
import { title } from 'mastodon/initial_state';
import { Helmet } from 'react-helmet';
const messages = defineMessages({ const messages = defineMessages({
followHashtag: { id: 'hashtag.follow', defaultMessage: 'Follow hashtag' }, followHashtag: { id: 'hashtag.follow', defaultMessage: 'Follow hashtag' },
@ -31,6 +33,10 @@ class HashtagTimeline extends React.PureComponent {
disconnects = []; disconnects = [];
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
@ -158,6 +164,11 @@ class HashtagTimeline extends React.PureComponent {
handleFollow = () => { handleFollow = () => {
const { dispatch, params, tag } = this.props; const { dispatch, params, tag } = this.props;
const { id } = params; const { id } = params;
const { signedIn } = this.context.identity;
if (!signedIn) {
return;
}
if (tag.get('following')) { if (tag.get('following')) {
dispatch(unfollowHashtag(id)); dispatch(unfollowHashtag(id));
@ -170,6 +181,7 @@ class HashtagTimeline extends React.PureComponent {
const { hasUnread, columnId, multiColumn, tag, intl } = this.props; const { hasUnread, columnId, multiColumn, tag, intl } = this.props;
const { id, local } = this.props.params; const { id, local } = this.props.params;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.context.identity;
let followButton; let followButton;
@ -177,7 +189,7 @@ class HashtagTimeline extends React.PureComponent {
const following = tag.get('following'); const following = tag.get('following');
followButton = ( followButton = (
<button className={classNames('column-header__button')} onClick={this.handleFollow} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-pressed={following ? 'true' : 'false'}> <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-pressed={following ? 'true' : 'false'}>
<Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' /> <Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' />
</button> </button>
); );
@ -208,6 +220,10 @@ class HashtagTimeline extends React.PureComponent {
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />} emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
/> />
<Helmet>
<title>{`#${id}`} - {title}</title>
</Helmet>
</Column> </Column>
); );
} }

@ -9,6 +9,8 @@ import { expandPublicTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { connectPublicStream } from '../../actions/streaming'; import { connectPublicStream } from '../../actions/streaming';
import { Helmet } from 'react-helmet';
import { title } from 'mastodon/initial_state';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.public', defaultMessage: 'Federated timeline' }, title: { id: 'column.public', defaultMessage: 'Federated timeline' },
@ -131,6 +133,10 @@ class PublicTimeline extends React.PureComponent {
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />} emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
/> />
<Helmet>
<title>{intl.formatMessage(messages.title)} - {title}</title>
</Helmet>
</Column> </Column>
); );
} }

@ -7,14 +7,25 @@ import DisplayName from 'mastodon/components/display_name';
import RelativeTimestamp from 'mastodon/components/relative_timestamp'; import RelativeTimestamp from 'mastodon/components/relative_timestamp';
import Option from './option'; import Option from './option';
import MediaAttachments from 'mastodon/components/media_attachments'; import MediaAttachments from 'mastodon/components/media_attachments';
import { injectIntl, defineMessages } from 'react-intl';
import Icon from 'mastodon/components/icon';
export default class StatusCheckBox extends React.PureComponent { const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
});
export default @injectIntl
class StatusCheckBox extends React.PureComponent {
static propTypes = { static propTypes = {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
checked: PropTypes.bool, checked: PropTypes.bool,
onToggle: PropTypes.func.isRequired, onToggle: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}; };
handleStatusesToggle = (value, checked) => { handleStatusesToggle = (value, checked) => {
@ -23,12 +34,21 @@ export default class StatusCheckBox extends React.PureComponent {
}; };
render () { render () {
const { status, checked } = this.props; const { status, checked, intl } = this.props;
if (status.get('reblog')) { if (status.get('reblog')) {
return null; return null;
} }
const visibilityIconInfo = {
'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) },
};
const visibilityIcon = visibilityIconInfo[status.get('visibility')];
const labelComponent = ( const labelComponent = (
<div className='status-check-box__status poll__option__text'> <div className='status-check-box__status poll__option__text'>
<div className='detailed-status__display-name'> <div className='detailed-status__display-name'>
@ -37,7 +57,7 @@ export default class StatusCheckBox extends React.PureComponent {
</div> </div>
<div> <div>
<DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /> <DisplayName account={status.get('account')} /> · <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> <RelativeTimestamp timestamp={status.get('created_at')} />
</div> </div>
</div> </div>

@ -37,6 +37,7 @@ class DetailedStatus extends ImmutablePureComponent {
onOpenMedia: PropTypes.func.isRequired, onOpenMedia: PropTypes.func.isRequired,
onOpenVideo: PropTypes.func.isRequired, onOpenVideo: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func.isRequired, onToggleHidden: PropTypes.func.isRequired,
onTranslate: PropTypes.func.isRequired,
measureHeight: PropTypes.bool, measureHeight: PropTypes.bool,
onHeightChange: PropTypes.func, onHeightChange: PropTypes.func,
domain: PropTypes.string.isRequired, domain: PropTypes.string.isRequired,
@ -103,6 +104,11 @@ class DetailedStatus extends ImmutablePureComponent {
window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
} }
handleTranslate = () => {
const { onTranslate, status } = this.props;
onTranslate(status);
}
render () { render () {
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
const outerStyle = { boxSizing: 'border-box' }; const outerStyle = { boxSizing: 'border-box' };
@ -260,7 +266,12 @@ class DetailedStatus extends ImmutablePureComponent {
<DisplayName account={status.get('account')} localDomain={this.props.domain} /> <DisplayName account={status.get('account')} localDomain={this.props.domain} />
</a> </a>
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} /> <StatusContent
status={status}
expanded={!status.get('hidden')}
onExpandedToggle={this.handleExpandedToggle}
onTranslate={this.handleTranslate}
/>
{media} {media}

@ -32,6 +32,8 @@ import {
editStatus, editStatus,
hideStatus, hideStatus,
revealStatus, revealStatus,
translateStatus,
undoStatusTranslation,
} from '../../actions/statuses'; } from '../../actions/statuses';
import { import {
unblockAccount, unblockAccount,
@ -54,10 +56,11 @@ import { openModal } from '../../actions/modal';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { boostModal, deleteModal } from '../../initial_state'; import { boostModal, deleteModal, title } from '../../initial_state';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
import { textForScreenReader, defaultMediaVisibility } from '../../components/status'; import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { Helmet } from 'react-helmet';
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -154,6 +157,23 @@ const makeMapStateToProps = () => {
return mapStateToProps; return mapStateToProps;
}; };
const truncate = (str, num) => {
if (str.length > num) {
return str.slice(0, num) + '…';
} else {
return str;
}
};
const titleFromStatus = status => {
const displayName = status.getIn(['account', 'display_name']);
const username = status.getIn(['account', 'username']);
const prefix = displayName.trim().length === 0 ? username : displayName;
const text = status.get('search_index');
return `${prefix}: "${truncate(text, 30)}"`;
};
export default @injectIntl export default @injectIntl
@connect(makeMapStateToProps) @connect(makeMapStateToProps)
class Status extends ImmutablePureComponent { class Status extends ImmutablePureComponent {
@ -339,6 +359,16 @@ class Status extends ImmutablePureComponent {
} }
} }
handleTranslate = status => {
const { dispatch } = this.props;
if (status.get('translation')) {
dispatch(undoStatusTranslation(status.get('id')));
} else {
dispatch(translateStatus(status.get('id')));
}
}
handleBlockClick = (status) => { handleBlockClick = (status) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const account = status.get('account'); const account = status.get('account');
@ -558,6 +588,7 @@ class Status extends ImmutablePureComponent {
onOpenVideo={this.handleOpenVideo} onOpenVideo={this.handleOpenVideo}
onOpenMedia={this.handleOpenMedia} onOpenMedia={this.handleOpenMedia}
onToggleHidden={this.handleToggleHidden} onToggleHidden={this.handleToggleHidden}
onTranslate={this.handleTranslate}
domain={domain} domain={domain}
showMedia={this.state.showMedia} showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility} onToggleMediaVisibility={this.handleToggleMediaVisibility}
@ -592,6 +623,10 @@ class Status extends ImmutablePureComponent {
{descendants} {descendants}
</div> </div>
</ScrollContainer> </ScrollContainer>
<Helmet>
<title>{titleFromStatus(status)} - {title}</title>
</Helmet>
</Column> </Column>
); );
} }

@ -0,0 +1,121 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
import { languages as preloadedLanguages } from 'mastodon/initial_state';
import Option from 'mastodon/features/report/components/option';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import IconButton from 'mastodon/components/icon_button';
import Button from 'mastodon/components/button';
import { followAccount } from 'mastodon/actions/accounts';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
});
const getAccountLanguages = createSelector([
(state, accountId) => state.getIn(['timelines', `account:${accountId}`, 'items'], ImmutableList()),
state => state.get('statuses'),
], (statusIds, statuses) =>
new ImmutableSet(statusIds.map(statusId => statuses.get(statusId)).filter(status => !status.get('reblog')).map(status => status.get('language'))));
const mapStateToProps = (state, { accountId }) => ({
acct: state.getIn(['accounts', accountId, 'acct']),
availableLanguages: getAccountLanguages(state, accountId),
selectedLanguages: ImmutableSet(state.getIn(['relationships', accountId, 'languages']) || ImmutableList()),
});
const mapDispatchToProps = (dispatch, { accountId }) => ({
onSubmit (languages) {
dispatch(followAccount(accountId, { languages }));
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class SubscribedLanguagesModal extends ImmutablePureComponent {
static propTypes = {
accountId: PropTypes.string.isRequired,
acct: PropTypes.string.isRequired,
availableLanguages: ImmutablePropTypes.setOf(PropTypes.string),
selectedLanguages: ImmutablePropTypes.setOf(PropTypes.string),
onClose: PropTypes.func.isRequired,
languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
intl: PropTypes.object.isRequired,
submit: PropTypes.func.isRequired,
};
static defaultProps = {
languages: preloadedLanguages,
};
state = {
selectedLanguages: this.props.selectedLanguages,
};
handleLanguageToggle = (value, checked) => {
const { selectedLanguages } = this.state;
if (checked) {
this.setState({ selectedLanguages: selectedLanguages.add(value) });
} else {
this.setState({ selectedLanguages: selectedLanguages.delete(value) });
}
};
handleSubmit = () => {
this.props.onSubmit(this.state.selectedLanguages.toArray());
this.props.onClose();
}
renderItem (value) {
const language = this.props.languages.find(language => language[0] === value);
const checked = this.state.selectedLanguages.includes(value);
return (
<Option
key={value}
name='languages'
value={value}
label={language[1]}
checked={checked}
onToggle={this.handleLanguageToggle}
multiple
/>
);
}
render () {
const { acct, availableLanguages, selectedLanguages, intl, onClose } = this.props;
return (
<div className='modal-root__modal report-dialog-modal'>
<div className='report-modal__target'>
<IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
<FormattedMessage id='subscribed_languages.target' defaultMessage='Change subscribed languages for {target}' values={{ target: <strong>{acct}</strong> }} />
</div>
<div className='report-dialog-modal__container'>
<p className='report-dialog-modal__lead'><FormattedMessage id='subscribed_languages.lead' defaultMessage='Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.' /></p>
<div>
{availableLanguages.union(selectedLanguages).map(value => this.renderItem(value))}
</div>
<div className='flex-spacer' />
<div className='report-dialog-modal__actions'>
<Button disabled={is(this.state.selectedLanguages, this.props.selectedLanguages)} onClick={this.handleSubmit}><FormattedMessage id='subscribed_languages.save' defaultMessage='Save changes' /></Button>
</div>
</div>
</div>
);
}
}

@ -60,6 +60,7 @@ class ColumnsArea extends ImmutablePureComponent {
static contextTypes = { static contextTypes = {
router: PropTypes.object.isRequired, router: PropTypes.object.isRequired,
identity: PropTypes.object.isRequired,
}; };
static propTypes = { static propTypes = {
@ -212,11 +213,12 @@ class ColumnsArea extends ImmutablePureComponent {
render () { render () {
const { columns, children, singleColumn, isModalOpen, intl } = this.props; const { columns, children, singleColumn, isModalOpen, intl } = this.props;
const { shouldAnimate, renderComposePanel } = this.state; const { shouldAnimate, renderComposePanel } = this.state;
const { signedIn } = this.context.identity;
const columnIndex = getIndex(this.context.router.history.location.pathname); const columnIndex = getIndex(this.context.router.history.location.pathname);
if (singleColumn) { if (singleColumn) {
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>; const floatingActionButton = (!signedIn || shouldHideFAB(this.context.router.history.location.pathname)) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
const content = columnIndex !== -1 ? ( const content = columnIndex !== -1 ? (
<ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}> <ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}>

@ -10,6 +10,10 @@ import { changeComposing } from 'mastodon/actions/compose';
export default @connect() export default @connect()
class ComposePanel extends React.PureComponent { class ComposePanel extends React.PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
}; };
@ -23,11 +27,25 @@ class ComposePanel extends React.PureComponent {
} }
render() { render() {
const { signedIn } = this.context.identity;
return ( return (
<div className='compose-panel' onFocus={this.onFocus}> <div className='compose-panel' onFocus={this.onFocus}>
<SearchContainer openInRoute /> <SearchContainer openInRoute />
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer singleColumn /> {!signedIn && (
<React.Fragment>
<div className='flex-spacer' />
</React.Fragment>
)}
{signedIn && (
<React.Fragment>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer singleColumn />
</React.Fragment>
)}
<LinkFooter withHotkeys /> <LinkFooter withHotkeys />
</div> </div>
); );

@ -1,41 +0,0 @@
import { PureComponent } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { title } from 'mastodon/initial_state';
const mapStateToProps = state => ({
unread: state.getIn(['missed_updates', 'unread']),
});
export default @connect(mapStateToProps)
class DocumentTitle extends PureComponent {
static propTypes = {
unread: PropTypes.number.isRequired,
};
componentDidMount () {
this._sideEffects();
}
componentDidUpdate() {
this._sideEffects();
}
_sideEffects () {
const { unread } = this.props;
if (unread > 99) {
document.title = `(*) ${title}`;
} else if (unread > 0) {
document.title = `(${unread}) ${title}`;
} else {
document.title = title;
}
}
render () {
return null;
}
}

@ -49,20 +49,46 @@ class LinkFooter extends React.PureComponent {
render () { render () {
const { withHotkeys } = this.props; const { withHotkeys } = this.props;
const { signedIn, permissions } = this.context.identity;
const items = [];
if ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) {
items.push(<a key='invites' href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a>);
}
if (signedIn && withHotkeys) {
items.push(<Link key='hotkeys' to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link>);
}
if (signedIn) {
items.push(<a key='security' href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a>);
}
if (!limitedFederationMode) {
items.push(<a key='about' href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a>);
}
if (profileDirectory) {
items.push(<Link key='directory' to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link>);
}
items.push(<a key='apps' href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a>);
items.push(<a key='privacy-policy' href='/privacy-policy' target='_blank'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></a>);
if (signedIn) {
items.push(<a key='developers' href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a>);
}
items.push(<a key='docs' href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a>);
if (signedIn) {
items.push(<a key='logout' href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a>);
}
return ( return (
<div className='getting-started__footer'> <div className='getting-started__footer'>
<ul> <ul>
{((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>} <li>{items.reduce((prev, curr) => [prev, ' · ', curr])}</li>
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
{!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
{profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>}
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
</ul> </ul>
<p> <p>

@ -11,6 +11,7 @@ import VideoModal from './video_modal';
import BoostModal from './boost_modal'; import BoostModal from './boost_modal';
import AudioModal from './audio_modal'; import AudioModal from './audio_modal';
import ConfirmationModal from './confirmation_modal'; import ConfirmationModal from './confirmation_modal';
import SubscribedLanguagesModal from 'mastodon/features/subscribed_languages_modal';
import FocalPointModal from './focal_point_modal'; import FocalPointModal from './focal_point_modal';
import { import {
MuteModal, MuteModal,
@ -39,6 +40,7 @@ const MODAL_COMPONENTS = {
'LIST_ADDER': ListAdder, 'LIST_ADDER': ListAdder,
'COMPARE_HISTORY': CompareHistoryModal, 'COMPARE_HISTORY': CompareHistoryModal,
'FILTER': FilterModal, 'FILTER': FilterModal,
'SUBSCRIBED_LANGUAGES': () => Promise.resolve({ default: SubscribedLanguagesModal }),
}; };
export default class ModalRoot extends React.PureComponent { export default class ModalRoot extends React.PureComponent {

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { NavLink, withRouter } from 'react-router-dom'; import PropTypes from 'prop-types';
import { NavLink, Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { showTrends } from 'mastodon/initial_state'; import { showTrends } from 'mastodon/initial_state';
@ -7,30 +8,68 @@ import NotificationsCounterIcon from './notifications_counter_icon';
import FollowRequestsNavLink from './follow_requests_nav_link'; import FollowRequestsNavLink from './follow_requests_nav_link';
import ListPanel from './list_panel'; import ListPanel from './list_panel';
import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container'; import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
import Logo from 'mastodon/components/logo';
import SignInBanner from './sign_in_banner';
const NavigationPanel = () => ( export default class NavigationPanel extends React.Component {
<div className='navigation-panel'>
<NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
<FollowRequestsNavLink />
<NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
<NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='at' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
<ListPanel /> static contextTypes = {
router: PropTypes.object.isRequired,
identity: PropTypes.object.isRequired,
};
<hr /> render () {
const { signedIn } = this.context.identity;
<a className='column-link column-link--transparent' href='/settings/preferences'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a> return (
<a className='column-link column-link--transparent' href='/relationships'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a> <div className='navigation-panel'>
<Link to='/' className='column-link column-link--logo'><Logo /></Link>
{showTrends && <div className='flex-spacer' />} <hr />
{showTrends && <TrendsContainer />}
</div>
);
export default withRouter(NavigationPanel); {signedIn && (
<React.Fragment>
<NavLink className='column-link column-link--transparent' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
<FollowRequestsNavLink />
</React.Fragment>
)}
<NavLink className='column-link column-link--transparent' to='/explore' data-preview-title-id='explore.title' data-preview-icon='hashtag'><Icon className='column-link__icon' id='hashtag' fixedWidth /><FormattedMessage id='explore.title' defaultMessage='Explore' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
<NavLink className='column-link column-link--transparent' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
{!signedIn && (
<React.Fragment>
<hr />
<SignInBanner />
</React.Fragment>
)}
{signedIn && (
<React.Fragment>
<NavLink className='column-link column-link--transparent' to='/conversations'><Icon className='column-link__icon' id='at' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
<ListPanel />
<hr />
<a className='column-link column-link--transparent' href='/settings/preferences'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>
<a className='column-link column-link--transparent' href='/relationships'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>
</React.Fragment>
)}
{showTrends && (
<React.Fragment>
<div className='flex-spacer' />
<TrendsContainer />
</React.Fragment>
)}
</div>
);
}
}

@ -0,0 +1,13 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { registrationsOpen } from 'mastodon/initial_state';
const SignInBanner = () => (
<div className='sign-in-banner'>
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.' /></p>
<a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
<a href={registrationsOpen ? '/auth/sign_up' : 'https://joinmastodon.org/servers'} className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /></a>
</div>
);
export default SignInBanner;

@ -20,7 +20,6 @@ import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodo
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
import UploadArea from './components/upload_area'; import UploadArea from './components/upload_area';
import ColumnsAreaContainer from './containers/columns_area_container'; import ColumnsAreaContainer from './containers/columns_area_container';
import DocumentTitle from './components/document_title';
import PictureInPicture from 'mastodon/features/picture_in_picture'; import PictureInPicture from 'mastodon/features/picture_in_picture';
import { import {
Compose, Compose,
@ -53,8 +52,9 @@ import {
Explore, Explore,
FollowRecommendations, FollowRecommendations,
} from './util/async-components'; } from './util/async-components';
import { me } from '../../initial_state'; import { me, title } from '../../initial_state';
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
import { Helmet } from 'react-helmet';
// Dummy import, to make sure that <Status /> ends up in the application bundle. // Dummy import, to make sure that <Status /> ends up in the application bundle.
// Without this it ends up in ~8 very commonly used bundles. // Without this it ends up in ~8 very commonly used bundles.
@ -110,6 +110,10 @@ const keyMap = {
class SwitchingColumnsArea extends React.PureComponent { class SwitchingColumnsArea extends React.PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
location: PropTypes.object, location: PropTypes.object,
@ -145,12 +149,25 @@ class SwitchingColumnsArea extends React.PureComponent {
render () { render () {
const { children, mobile } = this.props; const { children, mobile } = this.props;
const redirect = mobile ? <Redirect from='/' to='/home' exact /> : <Redirect from='/' to='/getting-started' exact />; const { signedIn } = this.context.identity;
let redirect;
if (signedIn) {
if (mobile) {
redirect = <Redirect from='/' to='/home' exact />;
} else {
redirect = <Redirect from='/' to='/getting-started' exact />;
}
} else {
redirect = <Redirect from='/' to='/explore' exact />;
}
return ( return (
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}> <ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
<WrappedSwitch> <WrappedSwitch>
{redirect} {redirect}
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} /> <WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} /> <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
@ -208,6 +225,7 @@ class UI extends React.PureComponent {
static contextTypes = { static contextTypes = {
router: PropTypes.object.isRequired, router: PropTypes.object.isRequired,
identity: PropTypes.object.isRequired,
}; };
static propTypes = { static propTypes = {
@ -343,6 +361,8 @@ class UI extends React.PureComponent {
} }
componentDidMount () { componentDidMount () {
const { signedIn } = this.context.identity;
window.addEventListener('focus', this.handleWindowFocus, false); window.addEventListener('focus', this.handleWindowFocus, false);
window.addEventListener('blur', this.handleWindowBlur, false); window.addEventListener('blur', this.handleWindowBlur, false);
window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('beforeunload', this.handleBeforeUnload, false);
@ -359,16 +379,18 @@ class UI extends React.PureComponent {
} }
// On first launch, redirect to the follow recommendations page // On first launch, redirect to the follow recommendations page
if (this.props.firstLaunch) { if (signedIn && this.props.firstLaunch) {
this.context.router.history.replace('/start'); this.context.router.history.replace('/start');
this.props.dispatch(closeOnboarding()); this.props.dispatch(closeOnboarding());
} }
this.props.dispatch(fetchMarkers()); if (signedIn) {
this.props.dispatch(expandHomeTimeline()); this.props.dispatch(fetchMarkers());
this.props.dispatch(expandNotifications()); this.props.dispatch(expandHomeTimeline());
this.props.dispatch(expandNotifications());
setTimeout(() => this.props.dispatch(fetchRules()), 3000); setTimeout(() => this.props.dispatch(fetchRules()), 3000);
}
this.hotkeys.__mousetrap__.stopCallback = (e, element) => { this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName); return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
@ -546,7 +568,10 @@ class UI extends React.PureComponent {
<LoadingBarContainer className='loading-bar' /> <LoadingBarContainer className='loading-bar' />
<ModalContainer /> <ModalContainer />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} /> <UploadArea active={draggingOver} onClose={this.closeUploadModal} />
<DocumentTitle />
<Helmet>
<title>{title}</title>
</Helmet>
</div> </div>
</HotKeys> </HotKeys>
); );

@ -3,6 +3,7 @@ const initialState = element && JSON.parse(element.textContent);
const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop]; const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop];
export const domain = getMeta('domain');
export const reduceMotion = getMeta('reduce_motion'); export const reduceMotion = getMeta('reduce_motion');
export const autoPlayGif = getMeta('auto_play_gif'); export const autoPlayGif = getMeta('auto_play_gif');
export const displayMedia = getMeta('display_media'); export const displayMedia = getMeta('display_media');
@ -14,6 +15,7 @@ export const me = getMeta('me');
export const searchEnabled = getMeta('search_enabled'); export const searchEnabled = getMeta('search_enabled');
export const maxChars = (initialState && initialState.max_toot_chars) || 500; export const maxChars = (initialState && initialState.max_toot_chars) || 500;
export const limitedFederationMode = getMeta('limited_federation_mode'); export const limitedFederationMode = getMeta('limited_federation_mode');
export const registrationsOpen = getMeta('registrations_open');
export const repository = getMeta('repository'); export const repository = getMeta('repository');
export const source_url = getMeta('source_url'); export const source_url = getMeta('source_url');
export const version = getMeta('version'); export const version = getMeta('version');
@ -27,5 +29,6 @@ export const title = getMeta('title');
export const cropImages = getMeta('crop_images'); export const cropImages = getMeta('crop_images');
export const disableSwiping = getMeta('disable_swiping'); export const disableSwiping = getMeta('disable_swiping');
export const languages = initialState && initialState.languages; export const languages = initialState && initialState.languages;
export const server = initialState && initialState.server;
export default initialState; export default initialState;

@ -24,6 +24,7 @@
"account.follows_you": "Volg jou", "account.follows_you": "Volg jou",
"account.hide_reblogs": "Versteek hupstoot vanaf @{name}", "account.hide_reblogs": "Versteek hupstoot vanaf @{name}",
"account.joined": "{date} aangesluit", "account.joined": "{date} aangesluit",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Eienaarskap van die skakel was getoets op {date}", "account.link_verified_on": "Eienaarskap van die skakel was getoets op {date}",
"account.locked_info": "Die rekening se privaatheidstatus is gesluit. Die eienaar hersien handmatig wie hom/haar kan volg.", "account.locked_info": "Die rekening se privaatheidstatus is gesluit. Die eienaar hersien handmatig wie hom/haar kan volg.",
"account.media": "Media", "account.media": "Media",
@ -226,8 +227,8 @@
"getting_started.heading": "Getting started", "getting_started.heading": "Getting started",
"getting_started.invite": "Invite people", "getting_started.invite": "Invite people",
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Security", "getting_started.security": "Security",
"getting_started.terms": "Terms of service",
"hashtag.column_header.tag_mode.all": "and {additional}", "hashtag.column_header.tag_mode.all": "and {additional}",
"hashtag.column_header.tag_mode.any": "or {additional}", "hashtag.column_header.tag_mode.any": "or {additional}",
"hashtag.column_header.tag_mode.none": "without {additional}", "hashtag.column_header.tag_mode.none": "without {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Toots", "search_results.statuses": "Toots",
"search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}", "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Open moderation interface for @{name}", "status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this status in the moderation interface", "status.admin_status": "Open this status in the moderation interface",
"status.block": "Block @{name}", "status.block": "Block @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Show less for all", "status.show_less_all": "Show less for all",
"status.show_more": "Show more", "status.show_more": "Show more",
"status.show_more_all": "Show more for all", "status.show_more_all": "Show more for all",
"status.show_original": "Show original",
"status.show_thread": "Show thread", "status.show_thread": "Show thread",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Not available", "status.uncached_media_warning": "Not available",
"status.unmute_conversation": "Unmute conversation", "status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile", "status.unpin": "Unpin from profile",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Dismiss suggestion", "suggestions.dismiss": "Dismiss suggestion",
"suggestions.header": "You might be interested in…", "suggestions.header": "You might be interested in…",
"tabs_bar.federated_timeline": "Federated", "tabs_bar.federated_timeline": "Federated",

@ -24,6 +24,7 @@
"account.follows_you": "يُتابِعُك", "account.follows_you": "يُتابِعُك",
"account.hide_reblogs": "إخفاء مشاركات @{name}", "account.hide_reblogs": "إخفاء مشاركات @{name}",
"account.joined": "انضم في {date}", "account.joined": "انضم في {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "تمَّ التَّحقق مِن مِلْكيّة هذا الرابط بتاريخ {date}", "account.link_verified_on": "تمَّ التَّحقق مِن مِلْكيّة هذا الرابط بتاريخ {date}",
"account.locked_info": "تمَّ تعيين حالة خصوصية هذا الحساب إلى مُقفَل. يُراجع المالك يدويًا من يمكنه متابعته.", "account.locked_info": "تمَّ تعيين حالة خصوصية هذا الحساب إلى مُقفَل. يُراجع المالك يدويًا من يمكنه متابعته.",
"account.media": "وسائط", "account.media": "وسائط",
@ -59,7 +60,7 @@
"alert.unexpected.title": "المعذرة!", "alert.unexpected.title": "المعذرة!",
"announcement.announcement": "إعلان", "announcement.announcement": "إعلان",
"attachments_list.unprocessed": "(غير معالَج)", "attachments_list.unprocessed": "(غير معالَج)",
"audio.hide": "Hide audio", "audio.hide": "إخفاء المقطع الصوتي",
"autosuggest_hashtag.per_week": "{count} في الأسبوع", "autosuggest_hashtag.per_week": "{count} في الأسبوع",
"boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة", "boost_modal.combo": "يُمكنك الضّغط على {combo} لتخطي هذا في المرة المُقبلة",
"bundle_column_error.body": "لقد حدث خطأ ما أثناء تحميل هذا العنصر.", "bundle_column_error.body": "لقد حدث خطأ ما أثناء تحميل هذا العنصر.",
@ -203,7 +204,7 @@
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Expired filter!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Filter settings",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "صفحة الإعدادات",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filter added!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "does not apply to this context",
@ -226,8 +227,8 @@
"getting_started.heading": "استعدّ للبدء", "getting_started.heading": "استعدّ للبدء",
"getting_started.invite": "دعوة أشخاص", "getting_started.invite": "دعوة أشخاص",
"getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على جيت هب {github}.", "getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على جيت هب {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "الأمان", "getting_started.security": "الأمان",
"getting_started.terms": "شروط الخدمة",
"hashtag.column_header.tag_mode.all": "و {additional}", "hashtag.column_header.tag_mode.all": "و {additional}",
"hashtag.column_header.tag_mode.any": "أو {additional}", "hashtag.column_header.tag_mode.any": "أو {additional}",
"hashtag.column_header.tag_mode.none": "بدون {additional}", "hashtag.column_header.tag_mode.none": "بدون {additional}",
@ -347,7 +348,7 @@
"notification.update": "عدّلَ {name} منشورًا", "notification.update": "عدّلَ {name} منشورًا",
"notifications.clear": "امسح الإخطارات", "notifications.clear": "امسح الإخطارات",
"notifications.clear_confirmation": "أمتأكد من أنك تود مسح جل الإخطارات الخاصة بك و المتلقاة إلى حد الآن ؟", "notifications.clear_confirmation": "أمتأكد من أنك تود مسح جل الإخطارات الخاصة بك و المتلقاة إلى حد الآن ؟",
"notifications.column_settings.admin.report": "New reports:", "notifications.column_settings.admin.report": "التقارير الجديدة:",
"notifications.column_settings.admin.sign_up": "التسجيلات الجديدة:", "notifications.column_settings.admin.sign_up": "التسجيلات الجديدة:",
"notifications.column_settings.alert": "إشعارات سطح المكتب", "notifications.column_settings.alert": "إشعارات سطح المكتب",
"notifications.column_settings.favourite": "المُفَضَّلة:", "notifications.column_settings.favourite": "المُفَضَّلة:",
@ -454,8 +455,8 @@
"report.unfollow": "إلغاء متابعة @{name}", "report.unfollow": "إلغاء متابعة @{name}",
"report.unfollow_explanation": "أنت تتابع هذا الحساب، لإزالة مَنشوراته من تغذيَتِكَ الرئيسة ألغ متابعته.", "report.unfollow_explanation": "أنت تتابع هذا الحساب، لإزالة مَنشوراته من تغذيَتِكَ الرئيسة ألغ متابعته.",
"report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached", "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
"report_notification.categories.other": "Other", "report_notification.categories.other": "آخر",
"report_notification.categories.spam": "Spam", "report_notification.categories.spam": "مزعج",
"report_notification.categories.violation": "Rule violation", "report_notification.categories.violation": "Rule violation",
"report_notification.open": "Open report", "report_notification.open": "Open report",
"search.placeholder": "ابحث", "search.placeholder": "ابحث",
@ -471,7 +472,11 @@
"search_results.nothing_found": "تعذر العثور على نتائج تتضمن هذه المصطلحات", "search_results.nothing_found": "تعذر العثور على نتائج تتضمن هذه المصطلحات",
"search_results.statuses": "المنشورات", "search_results.statuses": "المنشورات",
"search_results.statuses_fts_disabled": "البحث عن المنشورات عن طريق المحتوى ليس مفعل في خادم ماستدون هذا.", "search_results.statuses_fts_disabled": "البحث عن المنشورات عن طريق المحتوى ليس مفعل في خادم ماستدون هذا.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, zero {} one {نتيجة} two {نتيجتين} few {نتائج} many {نتائج} other {نتائج}}", "search_results.total": "{count, number} {count, plural, zero {} one {نتيجة} two {نتيجتين} few {نتائج} many {نتائج} other {نتائج}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "افتح الواجهة الإدارية لـ @{name}", "status.admin_account": "افتح الواجهة الإدارية لـ @{name}",
"status.admin_status": "افتح هذا المنشور على واجهة الإشراف", "status.admin_status": "افتح هذا المنشور على واجهة الإشراف",
"status.block": "احجب @{name}", "status.block": "احجب @{name}",
@ -489,7 +494,7 @@
"status.favourite": "أضف إلى المفضلة", "status.favourite": "أضف إلى المفضلة",
"status.filter": "Filter this post", "status.filter": "Filter this post",
"status.filtered": "مُصفّى", "status.filtered": "مُصفّى",
"status.hide": "Hide toot", "status.hide": "اخف التبويق",
"status.history.created": "أنشأه {name} {date}", "status.history.created": "أنشأه {name} {date}",
"status.history.edited": "عدله {name} {date}", "status.history.edited": "عدله {name} {date}",
"status.load_more": "حمّل المزيد", "status.load_more": "حمّل المزيد",
@ -513,15 +518,21 @@
"status.report": "ابلِغ عن @{name}", "status.report": "ابلِغ عن @{name}",
"status.sensitive_warning": "محتوى حساس", "status.sensitive_warning": "محتوى حساس",
"status.share": "مشاركة", "status.share": "مشاركة",
"status.show_filter_reason": "Show anyway", "status.show_filter_reason": "إظهار على أي حال",
"status.show_less": "اعرض أقلّ", "status.show_less": "اعرض أقلّ",
"status.show_less_all": "طي الكل", "status.show_less_all": "طي الكل",
"status.show_more": "أظهر المزيد", "status.show_more": "أظهر المزيد",
"status.show_more_all": "توسيع الكل", "status.show_more_all": "توسيع الكل",
"status.show_original": "Show original",
"status.show_thread": "الكشف عن المحادثة", "status.show_thread": "الكشف عن المحادثة",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "غير متوفر", "status.uncached_media_warning": "غير متوفر",
"status.unmute_conversation": "فك الكتم عن المحادثة", "status.unmute_conversation": "فك الكتم عن المحادثة",
"status.unpin": "فك التدبيس من الصفحة التعريفية", "status.unpin": "فك التدبيس من الصفحة التعريفية",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "حفظ التغييرات",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "إلغاء الاقتراح", "suggestions.dismiss": "إلغاء الاقتراح",
"suggestions.header": "يمكن أن يهمك…", "suggestions.header": "يمكن أن يهمك…",
"tabs_bar.federated_timeline": "الموحَّد", "tabs_bar.federated_timeline": "الموحَّد",

@ -24,6 +24,7 @@
"account.follows_you": "Síguete", "account.follows_you": "Síguete",
"account.hide_reblogs": "Anubrir les comparticiones de @{name}", "account.hide_reblogs": "Anubrir les comparticiones de @{name}",
"account.joined": "Joined {date}", "account.joined": "Joined {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "La propiedá d'esti enllaz foi comprobada'l {date}", "account.link_verified_on": "La propiedá d'esti enllaz foi comprobada'l {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
@ -93,8 +94,8 @@
"community.column_settings.local_only": "Local only", "community.column_settings.local_only": "Local only",
"community.column_settings.media_only": "Namái multimedia", "community.column_settings.media_only": "Namái multimedia",
"community.column_settings.remote_only": "Remote only", "community.column_settings.remote_only": "Remote only",
"compose.language.change": "Change language", "compose.language.change": "Camudar la llingua",
"compose.language.search": "Search languages...", "compose.language.search": "Buscar llingües…",
"compose_form.direct_message_warning_learn_more": "Saber más", "compose_form.direct_message_warning_learn_more": "Saber más",
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "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.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
@ -149,7 +150,7 @@
"directory.recently_active": "Actividá recién", "directory.recently_active": "Actividá recién",
"embed.instructions": "Empotra esti estáu nun sitiu web copiando'l códigu d'embaxo.", "embed.instructions": "Empotra esti estáu nun sitiu web copiando'l códigu d'embaxo.",
"embed.preview": "Asina ye cómo va vese:", "embed.preview": "Asina ye cómo va vese:",
"emoji_button.activity": "Actividaes", "emoji_button.activity": "Actividá",
"emoji_button.clear": "Clear", "emoji_button.clear": "Clear",
"emoji_button.custom": "Custom", "emoji_button.custom": "Custom",
"emoji_button.flags": "Banderes", "emoji_button.flags": "Banderes",
@ -170,7 +171,7 @@
"empty_column.blocks": "Entá nun bloquiesti a nengún usuariu.", "empty_column.blocks": "Entá nun bloquiesti a nengún usuariu.",
"empty_column.bookmarked_statuses": "Entá nun tienes nengún barritu en Marcadores. Cuando amiestes unu, va amosase equí.", "empty_column.bookmarked_statuses": "Entá nun tienes nengún barritu en Marcadores. Cuando amiestes unu, va amosase equí.",
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
"empty_column.direct": "Entá nun tienes nengún mensaxe direutu. Cuando unvies o recibas dalgún, apaez equí.", "empty_column.direct": "Entá nun tienes nengún mensaxe direutu. Cuando unvies o recibas dalgún, apaecen equí.",
"empty_column.domain_blocks": "Entá nun hai dominios anubríos.", "empty_column.domain_blocks": "Entá nun hai dominios anubríos.",
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!", "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
"empty_column.favourited_statuses": "Entá nun tienes nengún barritu en Favoritos. Cuando amiestes unu, va amosase equí.", "empty_column.favourited_statuses": "Entá nun tienes nengún barritu en Favoritos. Cuando amiestes unu, va amosase equí.",
@ -226,8 +227,8 @@
"getting_started.heading": "Entamu", "getting_started.heading": "Entamu",
"getting_started.invite": "Convidar a persones", "getting_started.invite": "Convidar a persones",
"getting_started.open_source_notice": "Mastodon ye software de códigu abiertu. Pues collaborar o informar de fallos en GitHub: {github}.", "getting_started.open_source_notice": "Mastodon ye software de códigu abiertu. Pues collaborar o informar de fallos en GitHub: {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Axustes de la cuenta", "getting_started.security": "Axustes de la cuenta",
"getting_started.terms": "Términos del serviciu",
"hashtag.column_header.tag_mode.all": "y {additional}", "hashtag.column_header.tag_mode.all": "y {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "ensin {additional}", "hashtag.column_header.tag_mode.none": "ensin {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Nun se pudo atopar nada con esos términos de busca", "search_results.nothing_found": "Nun se pudo atopar nada con esos términos de busca",
"search_results.statuses": "Barritos", "search_results.statuses": "Barritos",
"search_results.statuses_fts_disabled": "Esti sirvidor de Mastodon tien activada la gueta de barritos pol so conteníu.", "search_results.statuses_fts_disabled": "Esti sirvidor de Mastodon tien activada la gueta de barritos pol so conteníu.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultáu} other {resultaos}}", "search_results.total": "{count, number} {count, plural, one {resultáu} other {resultaos}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Open moderation interface for @{name}", "status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this status in the moderation interface", "status.admin_status": "Open this status in the moderation interface",
"status.block": "Bloquiar a @{name}", "status.block": "Bloquiar a @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Amosar menos en too", "status.show_less_all": "Amosar menos en too",
"status.show_more": "Amosar más", "status.show_more": "Amosar más",
"status.show_more_all": "Amosar más en too", "status.show_more_all": "Amosar más en too",
"status.show_original": "Show original",
"status.show_thread": "Amosar el filu", "status.show_thread": "Amosar el filu",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Non disponible", "status.uncached_media_warning": "Non disponible",
"status.unmute_conversation": "Unmute conversation", "status.unmute_conversation": "Unmute conversation",
"status.unpin": "Desfixar del perfil", "status.unpin": "Desfixar del perfil",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Dismiss suggestion", "suggestions.dismiss": "Dismiss suggestion",
"suggestions.header": "Quiciabes t'interese…", "suggestions.header": "Quiciabes t'interese…",
"tabs_bar.federated_timeline": "Fediversu", "tabs_bar.federated_timeline": "Fediversu",

@ -18,12 +18,13 @@
"account.followers": "Последователи", "account.followers": "Последователи",
"account.followers.empty": "Все още никой не следва този потребител.", "account.followers.empty": "Все още никой не следва този потребител.",
"account.followers_counter": "{count, plural, one {{counter} Последовател} other {{counter} Последователи}}", "account.followers_counter": "{count, plural, one {{counter} Последовател} other {{counter} Последователи}}",
"account.following": "Following", "account.following": "Последвани",
"account.following_counter": "{count, plural, one {{counter} Последван} other {{counter} Последвани}}", "account.following_counter": "{count, plural, one {{counter} Последван} other {{counter} Последвани}}",
"account.follows.empty": "Този потребител все още не следва никого.", "account.follows.empty": "Този потребител все още не следва никого.",
"account.follows_you": "Твой последовател", "account.follows_you": "Твой последовател",
"account.hide_reblogs": "Скриване на споделяния от @{name}", "account.hide_reblogs": "Скриване на споделяния от @{name}",
"account.joined": "Joined {date}", "account.joined": "Присъединил се на {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Собствеността върху тази връзка е проверена на {date}", "account.link_verified_on": "Собствеността върху тази връзка е проверена на {date}",
"account.locked_info": "Този акаунт е поверително заключен. Собственикът преглежда ръчно кой може да го следва.", "account.locked_info": "Този акаунт е поверително заключен. Собственикът преглежда ръчно кой може да го следва.",
"account.media": "Мултимедия", "account.media": "Мултимедия",
@ -41,25 +42,25 @@
"account.statuses_counter": "{count, plural, one {{counter} Публикация} other {{counter} Публикации}}", "account.statuses_counter": "{count, plural, one {{counter} Публикация} other {{counter} Публикации}}",
"account.unblock": "Не блокирай", "account.unblock": "Не блокирай",
"account.unblock_domain": "Unhide {domain}", "account.unblock_domain": "Unhide {domain}",
"account.unblock_short": "Unblock", "account.unblock_short": "Отблокирай",
"account.unendorse": "Не включвайте в профила", "account.unendorse": "Не включвайте в профила",
"account.unfollow": "Не следвай", "account.unfollow": "Не следвай",
"account.unmute": "Раззаглушаване на @{name}", "account.unmute": "Раззаглушаване на @{name}",
"account.unmute_notifications": "Раззаглушаване на известия от @{name}", "account.unmute_notifications": "Раззаглушаване на известия от @{name}",
"account.unmute_short": "Unmute", "account.unmute_short": "Unmute",
"account_note.placeholder": "Click to add a note", "account_note.placeholder": "Click to add a note",
"admin.dashboard.daily_retention": "User retention rate by day after sign-up", "admin.dashboard.daily_retention": "Ниво на задържани на потребители след регистрация, в дни",
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up", "admin.dashboard.monthly_retention": "Ниво на задържани на потребители след регистрация, в месеци",
"admin.dashboard.retention.average": "Average", "admin.dashboard.retention.average": "Средно",
"admin.dashboard.retention.cohort": "Sign-up month", "admin.dashboard.retention.cohort": "Месец на регистрацията",
"admin.dashboard.retention.cohort_size": "New users", "admin.dashboard.retention.cohort_size": "Нови потребители",
"alert.rate_limited.message": "Моля, опитайте отново след {retry_time, time, medium}.", "alert.rate_limited.message": "Моля, опитайте отново след {retry_time, time, medium}.",
"alert.rate_limited.title": "Скоростта е ограничена", "alert.rate_limited.title": "Скоростта е ограничена",
"alert.unexpected.message": "Възникна неочаквана грешка.", "alert.unexpected.message": "Възникна неочаквана грешка.",
"alert.unexpected.title": "Опаа!", "alert.unexpected.title": "Опаа!",
"announcement.announcement": "Оповестяване", "announcement.announcement": "Оповестяване",
"attachments_list.unprocessed": "(unprocessed)", "attachments_list.unprocessed": "(необработен)",
"audio.hide": "Hide audio", "audio.hide": "Скриване на видеото",
"autosuggest_hashtag.per_week": "{count} на седмица", "autosuggest_hashtag.per_week": "{count} на седмица",
"boost_modal.combo": "Можете да натиснете {combo}, за да пропуснете това следващия път", "boost_modal.combo": "Можете да натиснете {combo}, за да пропуснете това следващия път",
"bundle_column_error.body": "Нещо се обърка при зареждането на този компонент.", "bundle_column_error.body": "Нещо се обърка при зареждането на този компонент.",
@ -71,7 +72,7 @@
"column.blocks": "Блокирани потребители", "column.blocks": "Блокирани потребители",
"column.bookmarks": "Отметки", "column.bookmarks": "Отметки",
"column.community": "Локална емисия", "column.community": "Локална емисия",
"column.direct": "Direct messages", "column.direct": "Лични съобщения",
"column.directory": "Преглед на профили", "column.directory": "Преглед на профили",
"column.domain_blocks": "Hidden domains", "column.domain_blocks": "Hidden domains",
"column.favourites": "Любими", "column.favourites": "Любими",
@ -93,10 +94,10 @@
"community.column_settings.local_only": "Само локално", "community.column_settings.local_only": "Само локално",
"community.column_settings.media_only": "Media only", "community.column_settings.media_only": "Media only",
"community.column_settings.remote_only": "Само дистанционно", "community.column_settings.remote_only": "Само дистанционно",
"compose.language.change": "Change language", "compose.language.change": "Смяна на езика",
"compose.language.search": "Search languages...", "compose.language.search": "Търсене на езици...",
"compose_form.direct_message_warning_learn_more": "Още информация", "compose_form.direct_message_warning_learn_more": "Още информация",
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", "compose_form.encryption_warning": "Поставете в Мастодон не са криптирани от край до край. Не споделяйте никаква чувствителна информация.",
"compose_form.hashtag_warning": "Тази публикация няма да бъде изброена под нито един хаштаг, тъй като е скрита. Само публични публикации могат да се търсят по хаштаг.", "compose_form.hashtag_warning": "Тази публикация няма да бъде изброена под нито един хаштаг, тъй като е скрита. Само публични публикации могат да се търсят по хаштаг.",
"compose_form.lock_disclaimer": "Вашият акаунт не е {locked}. Всеки може да ви последва, за да прегледа вашите публикации само за последователи.", "compose_form.lock_disclaimer": "Вашият акаунт не е {locked}. Всеки може да ви последва, за да прегледа вашите публикации само за последователи.",
"compose_form.lock_disclaimer.lock": "заключено", "compose_form.lock_disclaimer.lock": "заключено",
@ -107,9 +108,9 @@
"compose_form.poll.remove_option": "Премахване на този избор", "compose_form.poll.remove_option": "Премахване на този избор",
"compose_form.poll.switch_to_multiple": "Промяна на анкетата, за да се позволят множество възможни избора", "compose_form.poll.switch_to_multiple": "Промяна на анкетата, за да се позволят множество възможни избора",
"compose_form.poll.switch_to_single": "Промяна на анкетата, за да се позволи един възможен избор", "compose_form.poll.switch_to_single": "Промяна на анкетата, за да се позволи един възможен избор",
"compose_form.publish": "Publish", "compose_form.publish": "Публикувай",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Save changes", "compose_form.save_changes": "Запази промените",
"compose_form.sensitive.hide": "{count, plural, one {Маркиране на мултимедията като деликатна} other {Маркиране на мултимедиите като деликатни}}", "compose_form.sensitive.hide": "{count, plural, one {Маркиране на мултимедията като деликатна} other {Маркиране на мултимедиите като деликатни}}",
"compose_form.sensitive.marked": "{count, plural, one {Мултимедията е маркирана като деликатна} other {Мултимедиите са маркирани като деликатни}}", "compose_form.sensitive.marked": "{count, plural, one {Мултимедията е маркирана като деликатна} other {Мултимедиите са маркирани като деликатни}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Мултимедията не е маркирана като деликатна} other {Мултимедиите не са маркирани като деликатни}}", "compose_form.sensitive.unmarked": "{count, plural, one {Мултимедията не е маркирана като деликатна} other {Мултимедиите не са маркирани като деликатни}}",
@ -124,8 +125,8 @@
"confirmations.delete.message": "Are you sure you want to delete this status?", "confirmations.delete.message": "Are you sure you want to delete this status?",
"confirmations.delete_list.confirm": "Изтриване", "confirmations.delete_list.confirm": "Изтриване",
"confirmations.delete_list.message": "Сигурни ли сте, че искате да изтриете окончателно този списък?", "confirmations.delete_list.message": "Сигурни ли сте, че искате да изтриете окончателно този списък?",
"confirmations.discard_edit_media.confirm": "Discard", "confirmations.discard_edit_media.confirm": "Отмени",
"confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?", "confirmations.discard_edit_media.message": "Имате незапазени промени на описанието или прегледа на медията, отмяна въпреки това?",
"confirmations.domain_block.confirm": "Hide entire domain", "confirmations.domain_block.confirm": "Hide entire domain",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
"confirmations.logout.confirm": "Излизане", "confirmations.logout.confirm": "Излизане",
@ -143,14 +144,14 @@
"conversation.mark_as_read": "Маркиране като прочетено", "conversation.mark_as_read": "Маркиране като прочетено",
"conversation.open": "Преглед на разговор", "conversation.open": "Преглед на разговор",
"conversation.with": "С {names}", "conversation.with": "С {names}",
"directory.federated": "From known fediverse", "directory.federated": "От познат федивърс",
"directory.local": "Само от {domain}", "directory.local": "Само от {domain}",
"directory.new_arrivals": "Новодошли", "directory.new_arrivals": "Новодошли",
"directory.recently_active": "Наскоро активни", "directory.recently_active": "Наскоро активни",
"embed.instructions": "Embed this status on your website by copying the code below.", "embed.instructions": "Embed this status on your website by copying the code below.",
"embed.preview": "Ето как ще изглежда:", "embed.preview": "Ето как ще изглежда:",
"emoji_button.activity": "Дейност", "emoji_button.activity": "Дейност",
"emoji_button.clear": "Clear", "emoji_button.clear": "Изчисти",
"emoji_button.custom": "Персонализирано", "emoji_button.custom": "Персонализирано",
"emoji_button.flags": "Знамена", "emoji_button.flags": "Знамена",
"emoji_button.food": "Храна и напитки", "emoji_button.food": "Храна и напитки",
@ -170,16 +171,16 @@
"empty_column.blocks": "Не сте блокирали потребители все още.", "empty_column.blocks": "Не сте блокирали потребители все още.",
"empty_column.bookmarked_statuses": "Все още нямате отметнати публикации. Когато отметнете някоя, тя ще се покаже тук.", "empty_column.bookmarked_statuses": "Все още нямате отметнати публикации. Когато отметнете някоя, тя ще се покаже тук.",
"empty_column.community": "Локалната емисия е празна. Напишете нещо публично, за да започнете!", "empty_column.community": "Локалната емисия е празна. Напишете нещо публично, за да започнете!",
"empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.direct": "Все още нямате лични съобщения. Когато изпратите или получите ще се покаже тук.",
"empty_column.domain_blocks": "There are no hidden domains yet.", "empty_column.domain_blocks": "There are no hidden domains yet.",
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!", "empty_column.explore_statuses": "Няма нищо популярно в момента. Проверете пак по-късно!",
"empty_column.favourited_statuses": "Все още нямате любими публикации. Когато поставите някоя в любими, тя ще се покаже тук.", "empty_column.favourited_statuses": "Все още нямате любими публикации. Когато поставите някоя в любими, тя ще се покаже тук.",
"empty_column.favourites": "Все още никой не е поставил тази публикация в любими. Когато някой го направи, ще се покаже тук.", "empty_column.favourites": "Все още никой не е поставил тази публикация в любими. Когато някой го направи, ще се покаже тук.",
"empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.", "empty_column.follow_recommendations": "Изглежда, че няма генерирани предложения за вас. Можете да опитате да търсите за хора, които знаете или да разгледате популярните тагове.",
"empty_column.follow_requests": "Все още нямате заявки за последване. Когато получите такава, тя ще се покаже тук.", "empty_column.follow_requests": "Все още нямате заявки за последване. Когато получите такава, тя ще се покаже тук.",
"empty_column.hashtag": "В този хаштаг няма нищо все още.", "empty_column.hashtag": "В този хаштаг няма нищо все още.",
"empty_column.home": "Вашата начална емисия е празна! Посетете {public} или използвайте търсене, за да започнете и да се запознаете с други потребители.", "empty_column.home": "Вашата начална емисия е празна! Посетете {public} или използвайте търсене, за да започнете и да се запознаете с други потребители.",
"empty_column.home.suggestions": "See some suggestions", "empty_column.home.suggestions": "Виж някои предложения",
"empty_column.list": "There is nothing in this list yet.", "empty_column.list": "There is nothing in this list yet.",
"empty_column.lists": "Все още нямате списъци. Когато създадете такъв, той ще се покаже тук.", "empty_column.lists": "Все още нямате списъци. Когато създадете такъв, той ще се покаже тук.",
"empty_column.mutes": "Не сте заглушавали потребители все още.", "empty_column.mutes": "Не сте заглушавали потребители все още.",
@ -189,31 +190,31 @@
"error.unexpected_crash.explanation_addons": "Тази страница не може да се покаже правилно. Тази грешка вероятно е причинена от добавка на браузъра или инструменти за автоматичен превод.", "error.unexpected_crash.explanation_addons": "Тази страница не може да се покаже правилно. Тази грешка вероятно е причинена от добавка на браузъра или инструменти за автоматичен превод.",
"error.unexpected_crash.next_steps": "Опитайте да опресните страницата. Ако това не помогне, все още можете да използвате Mastodon чрез различен браузър или приложение.", "error.unexpected_crash.next_steps": "Опитайте да опресните страницата. Ако това не помогне, все още можете да използвате Mastodon чрез различен браузър или приложение.",
"error.unexpected_crash.next_steps_addons": "Опитайте да ги деактивирате и да опресните страницата. Ако това не помогне, може все още да използвате Mastodon чрез различен браузър или приложение.", "error.unexpected_crash.next_steps_addons": "Опитайте да ги деактивирате и да опресните страницата. Ако това не помогне, може все още да използвате Mastodon чрез различен браузър или приложение.",
"errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", "errors.unexpected_crash.copy_stacktrace": "Копиране на stacktrace-а в клипборда",
"errors.unexpected_crash.report_issue": "Сигнал за проблем", "errors.unexpected_crash.report_issue": "Сигнал за проблем",
"explore.search_results": "Search results", "explore.search_results": "Резултати от търсенето",
"explore.suggested_follows": "For you", "explore.suggested_follows": "За вас",
"explore.title": "Explore", "explore.title": "Разглеждане",
"explore.trending_links": "News", "explore.trending_links": "Новини",
"explore.trending_statuses": "Posts", "explore.trending_statuses": "Публикации",
"explore.trending_tags": "Hashtags", "explore.trending_tags": "Тагове",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Несъвпадение на контекста!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Изтекал филтър!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Настройки на филтър",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "страница с настройки",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Филтърът е добавен!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "не е приложимо за този контекст",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "изтекло",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Нова категория: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Търси или създай",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Изберете съществуваща категория или създайте нова",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Филтриране на поста",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Филтрирай пост",
"follow_recommendations.done": "Done", "follow_recommendations.done": "Готово",
"follow_recommendations.heading": "Следвайте хора, които харесвате, за да виждате техните съобщения! Ето някои предложения.", "follow_recommendations.heading": "Следвайте хора, които харесвате, за да виждате техните съобщения! Ето някои предложения.",
"follow_recommendations.lead": "Съобщения от хора, които следвате, ще се показват в хронологичен ред на вашата главна страница. Не се страхувайте, че ще сгрешите, по всяко време много лесно можете да спрете да ги следвате!", "follow_recommendations.lead": "Съобщения от хора, които следвате, ще се показват в хронологичен ред на вашата главна страница. Не се страхувайте, че ще сгрешите, по всяко време много лесно можете да спрете да ги следвате!",
"follow_request.authorize": "Упълномощаване", "follow_request.authorize": "Упълномощаване",
@ -226,8 +227,8 @@
"getting_started.heading": "Първи стъпки", "getting_started.heading": "Първи стъпки",
"getting_started.invite": "Поканване на хора", "getting_started.invite": "Поканване на хора",
"getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.", "getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Security", "getting_started.security": "Security",
"getting_started.terms": "Условия за ползване",
"hashtag.column_header.tag_mode.all": "и {additional}", "hashtag.column_header.tag_mode.all": "и {additional}",
"hashtag.column_header.tag_mode.any": "или {additional}", "hashtag.column_header.tag_mode.any": "или {additional}",
"hashtag.column_header.tag_mode.none": "без {additional}", "hashtag.column_header.tag_mode.none": "без {additional}",
@ -237,9 +238,9 @@
"hashtag.column_settings.tag_mode.any": "Някое от тези", "hashtag.column_settings.tag_mode.any": "Някое от тези",
"hashtag.column_settings.tag_mode.none": "Никое от тези", "hashtag.column_settings.tag_mode.none": "Никое от тези",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column", "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.follow": "Follow hashtag", "hashtag.follow": "Следване на хаштаг",
"hashtag.total_volume": "Total volume in the last {days, plural, one {day} other {{days} days}}", "hashtag.total_volume": "Пълно количество в {days, plural,one {последния ден} other {последните {days} дни}}",
"hashtag.unfollow": "Unfollow hashtag", "hashtag.unfollow": "Спиране на следване на хаштаг",
"home.column_settings.basic": "Основно", "home.column_settings.basic": "Основно",
"home.column_settings.show_reblogs": "Показване на споделяния", "home.column_settings.show_reblogs": "Показване на споделяния",
"home.column_settings.show_replies": "Показване на отговори", "home.column_settings.show_replies": "Показване на отговори",
@ -287,8 +288,8 @@
"lightbox.expand": "Разгъване на полето за преглед на изображение", "lightbox.expand": "Разгъване на полето за преглед на изображение",
"lightbox.next": "Напред", "lightbox.next": "Напред",
"lightbox.previous": "Назад", "lightbox.previous": "Назад",
"limited_account_hint.action": "Show profile anyway", "limited_account_hint.action": "Покажи профила въпреки това",
"limited_account_hint.title": "This profile has been hidden by the moderators of your server.", "limited_account_hint.title": "Този профил е скрит от модераторите на сървъра Ви.",
"lists.account.add": "Добавяне към списък", "lists.account.add": "Добавяне към списък",
"lists.account.remove": "Премахване от списък", "lists.account.remove": "Премахване от списък",
"lists.delete": "Изтриване на списък", "lists.delete": "Изтриване на списък",
@ -315,11 +316,11 @@
"navigation_bar.bookmarks": "Отметки", "navigation_bar.bookmarks": "Отметки",
"navigation_bar.community_timeline": "Локална емисия", "navigation_bar.community_timeline": "Локална емисия",
"navigation_bar.compose": "Композиране на нова публикация", "navigation_bar.compose": "Композиране на нова публикация",
"navigation_bar.direct": "Direct messages", "navigation_bar.direct": "Директни съобщения",
"navigation_bar.discover": "Откриване", "navigation_bar.discover": "Откриване",
"navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.domain_blocks": "Hidden domains",
"navigation_bar.edit_profile": "Редактирай профил", "navigation_bar.edit_profile": "Редактирай профил",
"navigation_bar.explore": "Explore", "navigation_bar.explore": "Разглеждане",
"navigation_bar.favourites": "Любими", "navigation_bar.favourites": "Любими",
"navigation_bar.filters": "Заглушени думи", "navigation_bar.filters": "Заглушени думи",
"navigation_bar.follow_requests": "Заявки за последване", "navigation_bar.follow_requests": "Заявки за последване",
@ -334,8 +335,8 @@
"navigation_bar.preferences": "Предпочитания", "navigation_bar.preferences": "Предпочитания",
"navigation_bar.public_timeline": "Публичен канал", "navigation_bar.public_timeline": "Публичен канал",
"navigation_bar.security": "Сигурност", "navigation_bar.security": "Сигурност",
"notification.admin.report": "{name} reported {target}", "notification.admin.report": "{name} докладва {target}",
"notification.admin.sign_up": "{name} signed up", "notification.admin.sign_up": "{name} се регистрира",
"notification.favourite": "{name} хареса твоята публикация", "notification.favourite": "{name} хареса твоята публикация",
"notification.follow": "{name} те последва", "notification.follow": "{name} те последва",
"notification.follow_request": "{name} поиска да ви последва", "notification.follow_request": "{name} поиска да ви последва",
@ -344,16 +345,16 @@
"notification.poll": "Анкета, в която сте гласували, приключи", "notification.poll": "Анкета, в която сте гласували, приключи",
"notification.reblog": "{name} сподели твоята публикация", "notification.reblog": "{name} сподели твоята публикация",
"notification.status": "{name} току-що публикува", "notification.status": "{name} току-що публикува",
"notification.update": "{name} edited a post", "notification.update": "{name} промени публикация",
"notifications.clear": "Изчистване на известия", "notifications.clear": "Изчистване на известия",
"notifications.clear_confirmation": "Сигурни ли сте, че искате да изчистите окончателно всичките си известия?", "notifications.clear_confirmation": "Сигурни ли сте, че искате да изчистите окончателно всичките си известия?",
"notifications.column_settings.admin.report": "New reports:", "notifications.column_settings.admin.report": "Нови доклади:",
"notifications.column_settings.admin.sign_up": "New sign-ups:", "notifications.column_settings.admin.sign_up": "Нови регистрации:",
"notifications.column_settings.alert": "Десктоп известия", "notifications.column_settings.alert": "Десктоп известия",
"notifications.column_settings.favourite": "Предпочитани:", "notifications.column_settings.favourite": "Предпочитани:",
"notifications.column_settings.filter_bar.advanced": "Показване на всички категории", "notifications.column_settings.filter_bar.advanced": "Показване на всички категории",
"notifications.column_settings.filter_bar.category": "Лента за бърз филтър", "notifications.column_settings.filter_bar.category": "Лента за бърз филтър",
"notifications.column_settings.filter_bar.show_bar": "Show filter bar", "notifications.column_settings.filter_bar.show_bar": "Покажи лентата с филтри",
"notifications.column_settings.follow": "Нови последователи:", "notifications.column_settings.follow": "Нови последователи:",
"notifications.column_settings.follow_request": "Нови заявки за последване:", "notifications.column_settings.follow_request": "Нови заявки за последване:",
"notifications.column_settings.mention": "Споменавания:", "notifications.column_settings.mention": "Споменавания:",
@ -363,9 +364,9 @@
"notifications.column_settings.show": "Покажи в колона", "notifications.column_settings.show": "Покажи в колона",
"notifications.column_settings.sound": "Пускане на звук", "notifications.column_settings.sound": "Пускане на звук",
"notifications.column_settings.status": "Нови публикации:", "notifications.column_settings.status": "Нови публикации:",
"notifications.column_settings.unread_notifications.category": "Unread notifications", "notifications.column_settings.unread_notifications.category": "Непрочетени известия",
"notifications.column_settings.unread_notifications.highlight": "Highlight unread notifications", "notifications.column_settings.unread_notifications.highlight": "Отбележи непрочетените уведомления",
"notifications.column_settings.update": "Edits:", "notifications.column_settings.update": "Редакции:",
"notifications.filter.all": "Всичко", "notifications.filter.all": "Всичко",
"notifications.filter.boosts": "Споделяния", "notifications.filter.boosts": "Споделяния",
"notifications.filter.favourites": "Любими", "notifications.filter.favourites": "Любими",
@ -389,15 +390,15 @@
"poll.total_votes": "{count, plural, one {# глас} other {# гласа}}", "poll.total_votes": "{count, plural, one {# глас} other {# гласа}}",
"poll.vote": "Гласуване", "poll.vote": "Гласуване",
"poll.voted": "Вие гласувахте за този отговор", "poll.voted": "Вие гласувахте за този отговор",
"poll.votes": "{votes, plural, one {# vote} other {# votes}}", "poll.votes": "{votes, plural, one {# глас} other {# гласа}}",
"poll_button.add_poll": "Добавяне на анкета", "poll_button.add_poll": "Добавяне на анкета",
"poll_button.remove_poll": "Премахване на анкета", "poll_button.remove_poll": "Премахване на анкета",
"privacy.change": "Adjust status privacy", "privacy.change": "Adjust status privacy",
"privacy.direct.long": "Post to mentioned users only", "privacy.direct.long": "Post to mentioned users only",
"privacy.direct.short": "Direct", "privacy.direct.short": "Само споменатите хора",
"privacy.private.long": "Post to followers only", "privacy.private.long": "Post to followers only",
"privacy.private.short": "Followers-only", "privacy.private.short": "Само последователи",
"privacy.public.long": "Visible for all", "privacy.public.long": "Видимо за всички",
"privacy.public.short": "Публично", "privacy.public.short": "Публично",
"privacy.unlisted.long": "Visible for all, but opted-out of discovery features", "privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
"privacy.unlisted.short": "Скрито", "privacy.unlisted.short": "Скрито",
@ -468,10 +469,14 @@
"search_results.accounts": "Хора", "search_results.accounts": "Хора",
"search_results.all": "All", "search_results.all": "All",
"search_results.hashtags": "Хаштагове", "search_results.hashtags": "Хаштагове",
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Не е намерено нищо за това търсене",
"search_results.statuses": "Публикации", "search_results.statuses": "Публикации",
"search_results.statuses_fts_disabled": "Търсенето на публикации по тяхното съдържание не е активирано за този Mastodon сървър.", "search_results.statuses_fts_disabled": "Търсенето на публикации по тяхното съдържание не е активирано за този Mastodon сървър.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {резултат} other {резултата}}", "search_results.total": "{count, number} {count, plural, one {резултат} other {резултата}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Отваряне на интерфейс за модериране за @{name}", "status.admin_account": "Отваряне на интерфейс за модериране за @{name}",
"status.admin_status": "Open this status in the moderation interface", "status.admin_status": "Open this status in the moderation interface",
"status.block": "Блокиране на @{name}", "status.block": "Блокиране на @{name}",
@ -482,16 +487,16 @@
"status.delete": "Изтриване", "status.delete": "Изтриване",
"status.detailed_status": "Подробен изглед на разговор", "status.detailed_status": "Подробен изглед на разговор",
"status.direct": "Директно съобщение към @{name}", "status.direct": "Директно съобщение към @{name}",
"status.edit": "Edit", "status.edit": "Редакция",
"status.edited": "Edited {date}", "status.edited": "Редактирано на {date}",
"status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", "status.edited_x_times": "Редактирано {count, plural,one {{count} път} other {{count} пъти}}",
"status.embed": "Вграждане", "status.embed": "Вграждане",
"status.favourite": "Предпочитани", "status.favourite": "Предпочитани",
"status.filter": "Filter this post", "status.filter": "Филтриране на поста",
"status.filtered": "Филтрирано", "status.filtered": "Филтрирано",
"status.hide": "Hide toot", "status.hide": "Скриване на поста",
"status.history.created": "{name} created {date}", "status.history.created": "{name} създаде {date}",
"status.history.edited": "{name} edited {date}", "status.history.edited": "{name} редактира {date}",
"status.load_more": "Зареждане на още", "status.load_more": "Зареждане на още",
"status.media_hidden": "Мултимедията е скрита", "status.media_hidden": "Мултимедията е скрита",
"status.mention": "Споменаване", "status.mention": "Споменаване",
@ -513,15 +518,21 @@
"status.report": "Докладване на @{name}", "status.report": "Докладване на @{name}",
"status.sensitive_warning": "Деликатно съдържание", "status.sensitive_warning": "Деликатно съдържание",
"status.share": "Споделяне", "status.share": "Споделяне",
"status.show_filter_reason": "Show anyway", "status.show_filter_reason": "Покажи въпреки това",
"status.show_less": "Покажи по-малко", "status.show_less": "Покажи по-малко",
"status.show_less_all": "Покажи по-малко за всички", "status.show_less_all": "Покажи по-малко за всички",
"status.show_more": "Покажи повече", "status.show_more": "Покажи повече",
"status.show_more_all": "Покажи повече за всички", "status.show_more_all": "Покажи повече за всички",
"status.show_original": "Show original",
"status.show_thread": "Показване на тема", "status.show_thread": "Показване на тема",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Не е налично", "status.uncached_media_warning": "Не е налично",
"status.unmute_conversation": "Раззаглушаване на разговор", "status.unmute_conversation": "Раззаглушаване на разговор",
"status.unpin": "Разкачане от профил", "status.unpin": "Разкачане от профил",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Отхвърляне на предложение", "suggestions.dismiss": "Отхвърляне на предложение",
"suggestions.header": "Може да се интересувате от…", "suggestions.header": "Може да се интересувате от…",
"tabs_bar.federated_timeline": "Обединен", "tabs_bar.federated_timeline": "Обединен",
@ -550,14 +561,14 @@
"upload_error.poll": "Качването на файлове не е позволено с анкети.", "upload_error.poll": "Качването на файлове не е позволено с анкети.",
"upload_form.audio_description": "Опишете за хора със загуба на слуха", "upload_form.audio_description": "Опишете за хора със загуба на слуха",
"upload_form.description": "Опишете за хора със зрителни увреждания", "upload_form.description": "Опишете за хора със зрителни увреждания",
"upload_form.description_missing": "No description added", "upload_form.description_missing": "Без добавено описание",
"upload_form.edit": "Редакция", "upload_form.edit": "Редакция",
"upload_form.thumbnail": "Промяна на миниизображението", "upload_form.thumbnail": "Промяна на миниизображението",
"upload_form.undo": "Отмяна", "upload_form.undo": "Отмяна",
"upload_form.video_description": "Опишете за хора със загуба на слуха или зрително увреждане", "upload_form.video_description": "Опишете за хора със загуба на слуха или зрително увреждане",
"upload_modal.analyzing_picture": "Анализ на снимка…", "upload_modal.analyzing_picture": "Анализ на снимка…",
"upload_modal.apply": "Прилагане", "upload_modal.apply": "Прилагане",
"upload_modal.applying": "Applying…", "upload_modal.applying": "Прилагане…",
"upload_modal.choose_image": "Избор на изображение", "upload_modal.choose_image": "Избор на изображение",
"upload_modal.description_placeholder": "Ах, чудна българска земьо, полюшвай цъфтящи жита", "upload_modal.description_placeholder": "Ах, чудна българска земьо, полюшвай цъфтящи жита",
"upload_modal.detect_text": "Откриване на текст от картина", "upload_modal.detect_text": "Откриване на текст от картина",

@ -24,6 +24,7 @@
"account.follows_you": "তোমাকে অনুসরণ করে", "account.follows_you": "তোমাকে অনুসরণ করে",
"account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন", "account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন",
"account.joined": "Joined {date}", "account.joined": "Joined {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিখে", "account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিখে",
"account.locked_info": "এই নিবন্ধনের গোপনীয়তার ক্ষেত্র তালা দেওয়া আছে। নিবন্ধনকারী অনুসরণ করার অনুমতি যাদেরকে দেবেন, শুধু তারাই অনুসরণ করতে পারবেন।", "account.locked_info": "এই নিবন্ধনের গোপনীয়তার ক্ষেত্র তালা দেওয়া আছে। নিবন্ধনকারী অনুসরণ করার অনুমতি যাদেরকে দেবেন, শুধু তারাই অনুসরণ করতে পারবেন।",
"account.media": "মিডিয়া", "account.media": "মিডিয়া",
@ -226,8 +227,8 @@
"getting_started.heading": "শুরু করা", "getting_started.heading": "শুরু করা",
"getting_started.invite": "অন্যদের আমন্ত্রণ করুন", "getting_started.invite": "অন্যদের আমন্ত্রণ করুন",
"getting_started.open_source_notice": "মাস্টাডন একটি মুক্ত সফটওয়্যার। তৈরিতে সাহায্য করতে বা কোনো সমস্যা সম্পর্কে জানাতে আমাদের গিটহাবে যেতে পারেন {github}।", "getting_started.open_source_notice": "মাস্টাডন একটি মুক্ত সফটওয়্যার। তৈরিতে সাহায্য করতে বা কোনো সমস্যা সম্পর্কে জানাতে আমাদের গিটহাবে যেতে পারেন {github}।",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "নিরাপত্তা", "getting_started.security": "নিরাপত্তা",
"getting_started.terms": "ব্যবহারের নিয়মাবলী",
"hashtag.column_header.tag_mode.all": "এবং {additional}", "hashtag.column_header.tag_mode.all": "এবং {additional}",
"hashtag.column_header.tag_mode.any": "অথবা {additional}", "hashtag.column_header.tag_mode.any": "অথবা {additional}",
"hashtag.column_header.tag_mode.none": "বাদ দিয়ে {additional}", "hashtag.column_header.tag_mode.none": "বাদ দিয়ে {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "টুট", "search_results.statuses": "টুট",
"search_results.statuses_fts_disabled": "তাদের সামগ্রী দ্বারা টুটগুলি অনুসন্ধান এই মস্তোডন সার্ভারে সক্ষম নয়।", "search_results.statuses_fts_disabled": "তাদের সামগ্রী দ্বারা টুটগুলি অনুসন্ধান এই মস্তোডন সার্ভারে সক্ষম নয়।",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {ফলাফল} other {ফলাফল}}", "search_results.total": "{count, number} {count, plural, one {ফলাফল} other {ফলাফল}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "@{name} র জন্য পরিচালনার ইন্টারফেসে ঢুকুন", "status.admin_account": "@{name} র জন্য পরিচালনার ইন্টারফেসে ঢুকুন",
"status.admin_status": "যায় লেখাটি পরিচালনার ইন্টারফেসে খুলুন", "status.admin_status": "যায় লেখাটি পরিচালনার ইন্টারফেসে খুলুন",
"status.block": "@{name} কে ব্লক করুন", "status.block": "@{name} কে ব্লক করুন",
@ -518,10 +523,16 @@
"status.show_less_all": "সবগুলোতে কম দেখতে", "status.show_less_all": "সবগুলোতে কম দেখতে",
"status.show_more": "আরো দেখাতে", "status.show_more": "আরো দেখাতে",
"status.show_more_all": "সবগুলোতে আরো দেখতে", "status.show_more_all": "সবগুলোতে আরো দেখতে",
"status.show_original": "Show original",
"status.show_thread": "আলোচনা দেখতে", "status.show_thread": "আলোচনা দেখতে",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "পাওয়া যাচ্ছে না", "status.uncached_media_warning": "পাওয়া যাচ্ছে না",
"status.unmute_conversation": "আলোচনার প্রজ্ঞাপন চালু করতে", "status.unmute_conversation": "আলোচনার প্রজ্ঞাপন চালু করতে",
"status.unpin": "নিজের পাতা থেকে পিন করে রাখাটির পিন খুলতে", "status.unpin": "নিজের পাতা থেকে পিন করে রাখাটির পিন খুলতে",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "সাহায্যের পরামর্শগুলো সরাতে", "suggestions.dismiss": "সাহায্যের পরামর্শগুলো সরাতে",
"suggestions.header": "আপনি হয়তোবা এগুলোতে আগ্রহী হতে পারেন…", "suggestions.header": "আপনি হয়তোবা এগুলোতে আগ্রহী হতে পারেন…",
"tabs_bar.federated_timeline": "যুক্তবিশ্ব", "tabs_bar.federated_timeline": "যুক্তবিশ্ব",

@ -24,6 +24,7 @@
"account.follows_you": "Ho heul", "account.follows_you": "Ho heul",
"account.hide_reblogs": "Kuzh toudoù rannet gant @{name}", "account.hide_reblogs": "Kuzh toudoù rannet gant @{name}",
"account.joined": "Amañ abaoe {date}", "account.joined": "Amañ abaoe {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Gwiriet eo bet perc'hennidigezh al liamm d'an deiziad-mañ : {date}", "account.link_verified_on": "Gwiriet eo bet perc'hennidigezh al liamm d'an deiziad-mañ : {date}",
"account.locked_info": "Prennet eo ar gont-mañ. Dibab a ra ar perc'henn ar re a c'hall heuliañ anezhi pe anezhañ.", "account.locked_info": "Prennet eo ar gont-mañ. Dibab a ra ar perc'henn ar re a c'hall heuliañ anezhi pe anezhañ.",
"account.media": "Media", "account.media": "Media",
@ -226,8 +227,8 @@
"getting_started.heading": "Loc'hañ", "getting_started.heading": "Loc'hañ",
"getting_started.invite": "Pediñ tud", "getting_started.invite": "Pediñ tud",
"getting_started.open_source_notice": "Mastodoñ zo ur meziant digor e darzh. Gallout a rit kenoberzhiañ dezhañ pe danevellañ kudennoù war GitHub e {github}.", "getting_started.open_source_notice": "Mastodoñ zo ur meziant digor e darzh. Gallout a rit kenoberzhiañ dezhañ pe danevellañ kudennoù war GitHub e {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Arventennoù ar gont", "getting_started.security": "Arventennoù ar gont",
"getting_started.terms": "Divizoù gwerzhañ hollek",
"hashtag.column_header.tag_mode.all": "ha {additional}", "hashtag.column_header.tag_mode.all": "ha {additional}",
"hashtag.column_header.tag_mode.any": "pe {additional}", "hashtag.column_header.tag_mode.any": "pe {additional}",
"hashtag.column_header.tag_mode.none": "hep {additional}", "hashtag.column_header.tag_mode.none": "hep {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "a doudoù", "search_results.statuses": "a doudoù",
"search_results.statuses_fts_disabled": "Klask toudoù dre oc'h endalc'h n'eo ket aotreet war ar servijer-mañ.", "search_results.statuses_fts_disabled": "Klask toudoù dre oc'h endalc'h n'eo ket aotreet war ar servijer-mañ.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {disoc'h} other {a zisoc'h}}", "search_results.total": "{count, number} {count, plural, one {disoc'h} other {a zisoc'h}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Digeriñ etrefas evezherezh evit @{name}", "status.admin_account": "Digeriñ etrefas evezherezh evit @{name}",
"status.admin_status": "Digeriñ an toud e-barzh an etrefas evezherezh", "status.admin_status": "Digeriñ an toud e-barzh an etrefas evezherezh",
"status.block": "Berzañ @{name}", "status.block": "Berzañ @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Diskouez nebeutoc'h evit an holl", "status.show_less_all": "Diskouez nebeutoc'h evit an holl",
"status.show_more": "Diskouez muioc'h", "status.show_more": "Diskouez muioc'h",
"status.show_more_all": "Diskouez miuoc'h evit an holl", "status.show_more_all": "Diskouez miuoc'h evit an holl",
"status.show_original": "Show original",
"status.show_thread": "Diskouez ar gaozeadenn", "status.show_thread": "Diskouez ar gaozeadenn",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Dihegerz", "status.uncached_media_warning": "Dihegerz",
"status.unmute_conversation": "Diguzhat ar gaozeadenn", "status.unmute_conversation": "Diguzhat ar gaozeadenn",
"status.unpin": "Dispilhennañ eus ar profil", "status.unpin": "Dispilhennañ eus ar profil",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Dilezel damvenegoù", "suggestions.dismiss": "Dilezel damvenegoù",
"suggestions.header": "Marteze e vefec'h dedenet gant…", "suggestions.header": "Marteze e vefec'h dedenet gant…",
"tabs_bar.federated_timeline": "Kevredet", "tabs_bar.federated_timeline": "Kevredet",

@ -24,6 +24,7 @@
"account.follows_you": "Et segueix", "account.follows_you": "Et segueix",
"account.hide_reblogs": "Amaga els impulsos de @{name}", "account.hide_reblogs": "Amaga els impulsos de @{name}",
"account.joined": "Membre des de {date}", "account.joined": "Membre des de {date}",
"account.languages": "Canviar les llengües subscrits",
"account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}", "account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}",
"account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.", "account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.",
"account.media": "Multimèdia", "account.media": "Multimèdia",
@ -197,22 +198,22 @@
"explore.trending_links": "Notícies", "explore.trending_links": "Notícies",
"explore.trending_statuses": "Publicacions", "explore.trending_statuses": "Publicacions",
"explore.trending_tags": "Etiquetes", "explore.trending_tags": "Etiquetes",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Aquesta categoria del filtr no aplica al context en el que has accedit a aquest apunt. Si vols que l'apunt sigui filtrat també en aquest context, hauràs d'editar el filtre.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "El context no coincideix!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "La categoria d'aquest filtre ha caducat, necesitaràs canviar la seva data de caducitat per a aplicar-la.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Filtre caducat!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Per a revisar i configurar aquesta categoria de filtre, ves a {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Configuració del filtre",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "pàgina de configuració",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Aquest apunt s'ha afegit a la següent categoria de filtre: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filtre afegit!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "no aplica en aquest context",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "caducat",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nova categoria: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Cerca o crea",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Usa una categoria existent o crea una nova",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtra aquest apunt",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtre un apunt",
"follow_recommendations.done": "Fet", "follow_recommendations.done": "Fet",
"follow_recommendations.heading": "Segueix a la gent de la que t'agradaria veure les seves publicacions! Aquí hi ha algunes recomanacions.", "follow_recommendations.heading": "Segueix a la gent de la que t'agradaria veure les seves publicacions! Aquí hi ha algunes recomanacions.",
"follow_recommendations.lead": "Les publicacions del usuaris que segueixes es mostraran en ordre cronològic en la teva línia de temps Inici. No tinguis por en cometre errors, pots fàcilment deixar de seguir-los en qualsevol moment!", "follow_recommendations.lead": "Les publicacions del usuaris que segueixes es mostraran en ordre cronològic en la teva línia de temps Inici. No tinguis por en cometre errors, pots fàcilment deixar de seguir-los en qualsevol moment!",
@ -226,8 +227,8 @@
"getting_started.heading": "Primers passos", "getting_started.heading": "Primers passos",
"getting_started.invite": "Convidar gent", "getting_started.invite": "Convidar gent",
"getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir-hi o informar de problemes a GitHub a {github}.", "getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir-hi o informar de problemes a GitHub a {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Configuració del compte", "getting_started.security": "Configuració del compte",
"getting_started.terms": "Condicions de servei",
"hashtag.column_header.tag_mode.all": "i {additional}", "hashtag.column_header.tag_mode.all": "i {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "sense {additional}", "hashtag.column_header.tag_mode.none": "sense {additional}",
@ -238,7 +239,7 @@
"hashtag.column_settings.tag_mode.none": "Cap daquests", "hashtag.column_settings.tag_mode.none": "Cap daquests",
"hashtag.column_settings.tag_toggle": "Inclou etiquetes addicionals per a aquesta columna", "hashtag.column_settings.tag_toggle": "Inclou etiquetes addicionals per a aquesta columna",
"hashtag.follow": "Segueix etiqueta", "hashtag.follow": "Segueix etiqueta",
"hashtag.total_volume": "Total volume in the last {days, plural, one {day} other {{days} days}}", "hashtag.total_volume": "Volumen total en els darrers {days, plural, one {day} other {{days} dies}}",
"hashtag.unfollow": "Deixa de seguir etiqueta", "hashtag.unfollow": "Deixa de seguir etiqueta",
"home.column_settings.basic": "Bàsic", "home.column_settings.basic": "Bàsic",
"home.column_settings.show_reblogs": "Mostra els impulsos", "home.column_settings.show_reblogs": "Mostra els impulsos",
@ -471,7 +472,11 @@
"search_results.nothing_found": "No s'ha pogut trobar res per a aquests termes de cerca", "search_results.nothing_found": "No s'ha pogut trobar res per a aquests termes de cerca",
"search_results.statuses": "Publicacions", "search_results.statuses": "Publicacions",
"search_results.statuses_fts_disabled": "La cerca de publicacions pel seu contingut no està habilitada en aquest servidor Mastodon.", "search_results.statuses_fts_disabled": "La cerca de publicacions pel seu contingut no està habilitada en aquest servidor Mastodon.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Obre l'interfície de moderació per a @{name}", "status.admin_account": "Obre l'interfície de moderació per a @{name}",
"status.admin_status": "Obrir aquesta publicació a la interfície de moderació", "status.admin_status": "Obrir aquesta publicació a la interfície de moderació",
"status.block": "Bloqueja @{name}", "status.block": "Bloqueja @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Editat {count, plural, one {{count} vegada} other {{count} vegades}}", "status.edited_x_times": "Editat {count, plural, one {{count} vegada} other {{count} vegades}}",
"status.embed": "Incrusta", "status.embed": "Incrusta",
"status.favourite": "Favorit", "status.favourite": "Favorit",
"status.filter": "Filter this post", "status.filter": "Filtre aquest apunt",
"status.filtered": "Filtrat", "status.filtered": "Filtrat",
"status.hide": "Amaga publicació", "status.hide": "Amaga publicació",
"status.history.created": "{name} ha creat {date}", "status.history.created": "{name} ha creat {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Mostrar-ne menys per a tot", "status.show_less_all": "Mostrar-ne menys per a tot",
"status.show_more": "Mostrar-ne més", "status.show_more": "Mostrar-ne més",
"status.show_more_all": "Mostrar-ne més per a tot", "status.show_more_all": "Mostrar-ne més per a tot",
"status.show_original": "Mostra l'original",
"status.show_thread": "Mostra el fil", "status.show_thread": "Mostra el fil",
"status.translate": "Tradueix",
"status.translated_from": "Traduït del: {lang}",
"status.uncached_media_warning": "No està disponible", "status.uncached_media_warning": "No està disponible",
"status.unmute_conversation": "No silenciïs la conversa", "status.unmute_conversation": "No silenciïs la conversa",
"status.unpin": "No fixis al perfil", "status.unpin": "No fixis al perfil",
"subscribed_languages.lead": "Només els apunts en les llengües seleccionades apareixeran en le teves línies de temps Inici i llista després del canvi. No en seleccionis cap per a rebre apunts en totes les llengües.",
"subscribed_languages.save": "Desa els canvis",
"subscribed_languages.target": "Canvia les llengües subscrites per a {target}",
"suggestions.dismiss": "Ignora el suggeriment", "suggestions.dismiss": "Ignora el suggeriment",
"suggestions.header": "És possible que estiguis interessat en…", "suggestions.header": "És possible que estiguis interessat en…",
"tabs_bar.federated_timeline": "Federat", "tabs_bar.federated_timeline": "Federat",
@ -538,7 +549,7 @@
"timeline_hint.resources.followers": "Seguidors", "timeline_hint.resources.followers": "Seguidors",
"timeline_hint.resources.follows": "Seguiments", "timeline_hint.resources.follows": "Seguiments",
"timeline_hint.resources.statuses": "Publicacions més antigues", "timeline_hint.resources.statuses": "Publicacions més antigues",
"trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}", "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} persones}} en els passats {days, plural, one {day} other {{days} dies}}",
"trends.trending_now": "En tendència", "trends.trending_now": "En tendència",
"ui.beforeunload": "El teu esborrany es perdrà si surts de Mastodon.", "ui.beforeunload": "El teu esborrany es perdrà si surts de Mastodon.",
"units.short.billion": "{count}B", "units.short.billion": "{count}B",

@ -24,6 +24,7 @@
"account.follows_you": "شوێنکەوتووەکانت", "account.follows_you": "شوێنکەوتووەکانت",
"account.hide_reblogs": "داشاردنی بووستەکان لە @{name}", "account.hide_reblogs": "داشاردنی بووستەکان لە @{name}",
"account.joined": "بەشداری {date}", "account.joined": "بەشداری {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "خاوەنداریەتی ئەم لینکە لە {date} چێک کراوە", "account.link_verified_on": "خاوەنداریەتی ئەم لینکە لە {date} چێک کراوە",
"account.locked_info": "تایبەتمەندی ئەم هەژمارەیە ڕیکخراوە بۆ قوفڵدراوە. خاوەنەکە بە دەستی پێداچوونەوە دەکات کە کێ دەتوانێت شوێنیان بکەوێت.", "account.locked_info": "تایبەتمەندی ئەم هەژمارەیە ڕیکخراوە بۆ قوفڵدراوە. خاوەنەکە بە دەستی پێداچوونەوە دەکات کە کێ دەتوانێت شوێنیان بکەوێت.",
"account.media": "میدیا", "account.media": "میدیا",
@ -226,8 +227,8 @@
"getting_started.heading": "دەست پێکردن", "getting_started.heading": "دەست پێکردن",
"getting_started.invite": "بانگهێشتکردنی خەڵک", "getting_started.invite": "بانگهێشتکردنی خەڵک",
"getting_started.open_source_notice": "ماستۆدۆن نەرمەکالایەکی سەرچاوەی کراوەیە. دەتوانیت بەشداری بکەیت یان گوزارشت بکەیت لەسەر کێشەکانی لە پەڕەی گیتهاب {github}.", "getting_started.open_source_notice": "ماستۆدۆن نەرمەکالایەکی سەرچاوەی کراوەیە. دەتوانیت بەشداری بکەیت یان گوزارشت بکەیت لەسەر کێشەکانی لە پەڕەی گیتهاب {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "ڕێکخستنەکانی هەژمارە", "getting_started.security": "ڕێکخستنەکانی هەژمارە",
"getting_started.terms": "مەرجەکانی خزمەتگوزاری",
"hashtag.column_header.tag_mode.all": "و {additional}", "hashtag.column_header.tag_mode.all": "و {additional}",
"hashtag.column_header.tag_mode.any": "یا {additional}", "hashtag.column_header.tag_mode.any": "یا {additional}",
"hashtag.column_header.tag_mode.none": "بەبێ {additional}", "hashtag.column_header.tag_mode.none": "بەبێ {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "توتەکان", "search_results.statuses": "توتەکان",
"search_results.statuses_fts_disabled": "گەڕانی توتەکان بە ناوەڕۆکیان لەسەر ئەم ڕاژەی ماستۆدۆن چالاک نەکراوە.", "search_results.statuses_fts_disabled": "گەڕانی توتەکان بە ناوەڕۆکیان لەسەر ئەم ڕاژەی ماستۆدۆن چالاک نەکراوە.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {دەرئەنجام} other {دەرئەنجام}}", "search_results.total": "{count, number} {count, plural, one {دەرئەنجام} other {دەرئەنجام}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "کردنەوەی میانڕەوی بەڕێوەبەر بۆ @{name}", "status.admin_account": "کردنەوەی میانڕەوی بەڕێوەبەر بۆ @{name}",
"status.admin_status": "ئەم توتە بکەوە لە ناو ڕووکاری بەڕیوەبەر", "status.admin_status": "ئەم توتە بکەوە لە ناو ڕووکاری بەڕیوەبەر",
"status.block": "@{name} ئاستەنگ بکە", "status.block": "@{name} ئاستەنگ بکە",
@ -518,10 +523,16 @@
"status.show_less_all": "هەمووی بچووک بکەوە", "status.show_less_all": "هەمووی بچووک بکەوە",
"status.show_more": "زیاتر نیشان بدە", "status.show_more": "زیاتر نیشان بدە",
"status.show_more_all": "زیاتر نیشان بدە بۆ هەمووی", "status.show_more_all": "زیاتر نیشان بدە بۆ هەمووی",
"status.show_original": "Show original",
"status.show_thread": "نیشاندانی گفتوگۆ", "status.show_thread": "نیشاندانی گفتوگۆ",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "بەردەست نیە", "status.uncached_media_warning": "بەردەست نیە",
"status.unmute_conversation": "گفتوگۆی بێدەنگ", "status.unmute_conversation": "گفتوگۆی بێدەنگ",
"status.unpin": "لە سەرەوە لایبە", "status.unpin": "لە سەرەوە لایبە",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "ڕەتکردنەوەی پێشنیار", "suggestions.dismiss": "ڕەتکردنەوەی پێشنیار",
"suggestions.header": "لەوانەیە حەزت لەمەش بێت…", "suggestions.header": "لەوانەیە حەزت لەمەش بێت…",
"tabs_bar.federated_timeline": "گشتی", "tabs_bar.federated_timeline": "گشتی",

@ -24,6 +24,7 @@
"account.follows_you": "Vi seguita", "account.follows_you": "Vi seguita",
"account.hide_reblogs": "Piattà spartere da @{name}", "account.hide_reblogs": "Piattà spartere da @{name}",
"account.joined": "Quì dapoi {date}", "account.joined": "Quì dapoi {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}", "account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}",
"account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.", "account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.",
"account.media": "Media", "account.media": "Media",
@ -226,8 +227,8 @@
"getting_started.heading": "Per principià", "getting_started.heading": "Per principià",
"getting_started.invite": "Invità ghjente", "getting_started.invite": "Invità ghjente",
"getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}.", "getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Sicurità", "getting_started.security": "Sicurità",
"getting_started.terms": "Cundizione di u serviziu",
"hashtag.column_header.tag_mode.all": "è {additional}", "hashtag.column_header.tag_mode.all": "è {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "senza {additional}", "hashtag.column_header.tag_mode.none": "senza {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Statuti", "search_results.statuses": "Statuti",
"search_results.statuses_fts_disabled": "A ricerca di i cuntinuti di i statuti ùn hè micca attivata nant'à stu servore Mastodon.", "search_results.statuses_fts_disabled": "A ricerca di i cuntinuti di i statuti ùn hè micca attivata nant'à stu servore Mastodon.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {risultatu} other {risultati}}", "search_results.total": "{count, number} {count, plural, one {risultatu} other {risultati}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Apre l'interfaccia di muderazione per @{name}", "status.admin_account": "Apre l'interfaccia di muderazione per @{name}",
"status.admin_status": "Apre stu statutu in l'interfaccia di muderazione", "status.admin_status": "Apre stu statutu in l'interfaccia di muderazione",
"status.block": "Bluccà @{name}", "status.block": "Bluccà @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Ripiegà tuttu", "status.show_less_all": "Ripiegà tuttu",
"status.show_more": "Slibrà", "status.show_more": "Slibrà",
"status.show_more_all": "Slibrà tuttu", "status.show_more_all": "Slibrà tuttu",
"status.show_original": "Show original",
"status.show_thread": "Vede u filu", "status.show_thread": "Vede u filu",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Micca dispunibule", "status.uncached_media_warning": "Micca dispunibule",
"status.unmute_conversation": "Ùn piattà più a cunversazione", "status.unmute_conversation": "Ùn piattà più a cunversazione",
"status.unpin": "Spuntarulà da u prufile", "status.unpin": "Spuntarulà da u prufile",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Righjittà a pruposta", "suggestions.dismiss": "Righjittà a pruposta",
"suggestions.header": "Site forse interessatu·a da…", "suggestions.header": "Site forse interessatu·a da…",
"tabs_bar.federated_timeline": "Glubale", "tabs_bar.federated_timeline": "Glubale",

@ -24,6 +24,7 @@
"account.follows_you": "Sleduje vás", "account.follows_you": "Sleduje vás",
"account.hide_reblogs": "Skrýt boosty od @{name}", "account.hide_reblogs": "Skrýt boosty od @{name}",
"account.joined": "Založen {date}", "account.joined": "Založen {date}",
"account.languages": "Změnit odebírané jazyky",
"account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}", "account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}",
"account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.", "account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.",
"account.media": "Média", "account.media": "Média",
@ -59,7 +60,7 @@
"alert.unexpected.title": "Jejda!", "alert.unexpected.title": "Jejda!",
"announcement.announcement": "Oznámení", "announcement.announcement": "Oznámení",
"attachments_list.unprocessed": "(nezpracováno)", "attachments_list.unprocessed": "(nezpracováno)",
"audio.hide": "Hide audio", "audio.hide": "Skrýt zvuk",
"autosuggest_hashtag.per_week": "{count} za týden", "autosuggest_hashtag.per_week": "{count} za týden",
"boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}", "boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
"bundle_column_error.body": "Při načítání této komponenty se něco pokazilo.", "bundle_column_error.body": "Při načítání této komponenty se něco pokazilo.",
@ -197,22 +198,22 @@
"explore.trending_links": "Zprávy", "explore.trending_links": "Zprávy",
"explore.trending_statuses": "Příspěvky", "explore.trending_statuses": "Příspěvky",
"explore.trending_tags": "Hashtagy", "explore.trending_tags": "Hashtagy",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Tato kategorie filtru se nevztahuje na kontext, ve kterém jste tento příspěvek otevřeli. Pokud chcete, aby byl příspěvek filtrován i v tomto kontextu, budete muset filtr upravit.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Kontext se neshoduje!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Tato kategorie filtrů vypršela, budete muset změnit datum vypršení platnosti, aby mohla být použita.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Vypršel filtr!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Chcete-li zkontrolovat a dále konfigurovat tuto kategorii filtru, přejděte na {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Nastavení filtru",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "stránka nastavení",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Tento příspěvek byl přidán do následující kategorie filtrů: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filtr přidán!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "nevztahuje se na tento kontext",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "vypršela platnost",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nová kategorie: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Vyhledat nebo vytvořit",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Použít existující kategorii nebo vytvořit novou kategorii",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtrovat tento příspěvek",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtrovat příspěvek",
"follow_recommendations.done": "Hotovo", "follow_recommendations.done": "Hotovo",
"follow_recommendations.heading": "Sledujte lidi, jejichž příspěvky chcete vidět! Tady jsou nějaké návrhy.", "follow_recommendations.heading": "Sledujte lidi, jejichž příspěvky chcete vidět! Tady jsou nějaké návrhy.",
"follow_recommendations.lead": "Příspěvky od lidí, které sledujete, se budou objevovat v chronologickém pořadí ve vaší domovské ose. Nebojte se, že uděláte chybu, můžete lidi stejně snadno kdykoliv přestat sledovat!", "follow_recommendations.lead": "Příspěvky od lidí, které sledujete, se budou objevovat v chronologickém pořadí ve vaší domovské ose. Nebojte se, že uděláte chybu, můžete lidi stejně snadno kdykoliv přestat sledovat!",
@ -226,8 +227,8 @@
"getting_started.heading": "Začínáme", "getting_started.heading": "Začínáme",
"getting_started.invite": "Pozvat lidi", "getting_started.invite": "Pozvat lidi",
"getting_started.open_source_notice": "Mastodon je otevřený software. Přispět do jeho vývoje nebo hlásit chyby můžete na GitHubu {github}.", "getting_started.open_source_notice": "Mastodon je otevřený software. Přispět do jeho vývoje nebo hlásit chyby můžete na GitHubu {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Nastavení účtu", "getting_started.security": "Nastavení účtu",
"getting_started.terms": "Podmínky používání",
"hashtag.column_header.tag_mode.all": "a {additional}", "hashtag.column_header.tag_mode.all": "a {additional}",
"hashtag.column_header.tag_mode.any": "nebo {additional}", "hashtag.column_header.tag_mode.any": "nebo {additional}",
"hashtag.column_header.tag_mode.none": "bez {additional}", "hashtag.column_header.tag_mode.none": "bez {additional}",
@ -237,9 +238,9 @@
"hashtag.column_settings.tag_mode.any": "Jakékoliv z těchto", "hashtag.column_settings.tag_mode.any": "Jakékoliv z těchto",
"hashtag.column_settings.tag_mode.none": "Žádné z těchto", "hashtag.column_settings.tag_mode.none": "Žádné z těchto",
"hashtag.column_settings.tag_toggle": "Zahrnout v tomto sloupci dodatečné tagy", "hashtag.column_settings.tag_toggle": "Zahrnout v tomto sloupci dodatečné tagy",
"hashtag.follow": "Follow hashtag", "hashtag.follow": "Sledovat hashtag",
"hashtag.total_volume": "Total volume in the last {days, plural, one {day} other {{days} days}}", "hashtag.total_volume": "Total volume in the last {days, plural, one {day} other {{days} days}}",
"hashtag.unfollow": "Unfollow hashtag", "hashtag.unfollow": "Zrušit sledování hashtagu",
"home.column_settings.basic": "Základní", "home.column_settings.basic": "Základní",
"home.column_settings.show_reblogs": "Zobrazit boosty", "home.column_settings.show_reblogs": "Zobrazit boosty",
"home.column_settings.show_replies": "Zobrazit odpovědi", "home.column_settings.show_replies": "Zobrazit odpovědi",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Pro tyto hledané výrazy nebylo nic nenalezeno", "search_results.nothing_found": "Pro tyto hledané výrazy nebylo nic nenalezeno",
"search_results.statuses": "Příspěvky", "search_results.statuses": "Příspěvky",
"search_results.statuses_fts_disabled": "Vyhledávání příspěvků podle jejich obsahu není na tomto Mastodon serveru povoleno.", "search_results.statuses_fts_disabled": "Vyhledávání příspěvků podle jejich obsahu není na tomto Mastodon serveru povoleno.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {výsledek} few {výsledky} many {výsledků} other {výsledků}}", "search_results.total": "{count, number} {count, plural, one {výsledek} few {výsledky} many {výsledků} other {výsledků}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Otevřít moderátorské rozhraní pro @{name}", "status.admin_account": "Otevřít moderátorské rozhraní pro @{name}",
"status.admin_status": "Otevřít tento příspěvek v moderátorském rozhraní", "status.admin_status": "Otevřít tento příspěvek v moderátorském rozhraní",
"status.block": "Zablokovat @{name}", "status.block": "Zablokovat @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Upraven {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}", "status.edited_x_times": "Upraven {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
"status.embed": "Vložit na web", "status.embed": "Vložit na web",
"status.favourite": "Oblíbit", "status.favourite": "Oblíbit",
"status.filter": "Filter this post", "status.filter": "Filtrovat tento příspěvek",
"status.filtered": "Filtrováno", "status.filtered": "Filtrováno",
"status.hide": "Skrýt příspěvek", "status.hide": "Skrýt příspěvek",
"status.history.created": "Uživatel {name} vytvořil {date}", "status.history.created": "Uživatel {name} vytvořil {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Zobrazit méně pro všechny", "status.show_less_all": "Zobrazit méně pro všechny",
"status.show_more": "Zobrazit více", "status.show_more": "Zobrazit více",
"status.show_more_all": "Zobrazit více pro všechny", "status.show_more_all": "Zobrazit více pro všechny",
"status.show_original": "Zobrazit původní",
"status.show_thread": "Zobrazit vlákno", "status.show_thread": "Zobrazit vlákno",
"status.translate": "Přeložit",
"status.translated_from": "Přeloženo z {lang}",
"status.uncached_media_warning": "Nedostupné", "status.uncached_media_warning": "Nedostupné",
"status.unmute_conversation": "Odkrýt konverzaci", "status.unmute_conversation": "Odkrýt konverzaci",
"status.unpin": "Odepnout z profilu", "status.unpin": "Odepnout z profilu",
"subscribed_languages.lead": "Po změně se objeví pouze příspěvky ve vybraných jazycích na vašem domě a zobrazí se seznam časových os. Pro příjem příspěvků ve všech jazycích nevyber žádnou.",
"subscribed_languages.save": "Uložit změny",
"subscribed_languages.target": "Změnit odebírané jazyky na {target}",
"suggestions.dismiss": "Odmítnout návrh", "suggestions.dismiss": "Odmítnout návrh",
"suggestions.header": "Mohlo by vás zajímat…", "suggestions.header": "Mohlo by vás zajímat…",
"tabs_bar.federated_timeline": "Federovaná", "tabs_bar.federated_timeline": "Federovaná",

@ -24,6 +24,7 @@
"account.follows_you": "Yn eich dilyn chi", "account.follows_you": "Yn eich dilyn chi",
"account.hide_reblogs": "Cuddio bwstiau o @{name}", "account.hide_reblogs": "Cuddio bwstiau o @{name}",
"account.joined": "Ymunodd {date}", "account.joined": "Ymunodd {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}", "account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}",
"account.locked_info": "Mae'r statws preifatrwydd cyfrif hwn wedi'i osod i gloi. Mae'r perchennog yn adolygu'r sawl sy'n gallu eu dilyn.", "account.locked_info": "Mae'r statws preifatrwydd cyfrif hwn wedi'i osod i gloi. Mae'r perchennog yn adolygu'r sawl sy'n gallu eu dilyn.",
"account.media": "Cyfryngau", "account.media": "Cyfryngau",
@ -226,8 +227,8 @@
"getting_started.heading": "Dechrau", "getting_started.heading": "Dechrau",
"getting_started.invite": "Gwahodd pobl", "getting_started.invite": "Gwahodd pobl",
"getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.", "getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Diogelwch", "getting_started.security": "Diogelwch",
"getting_started.terms": "Telerau Gwasanaeth",
"hashtag.column_header.tag_mode.all": "a {additional}", "hashtag.column_header.tag_mode.all": "a {additional}",
"hashtag.column_header.tag_mode.any": "neu {additional}", "hashtag.column_header.tag_mode.any": "neu {additional}",
"hashtag.column_header.tag_mode.none": "heb {additional}", "hashtag.column_header.tag_mode.none": "heb {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Methu dod o hyd i unrhyw beth ar gyfer y termau chwilio hyn", "search_results.nothing_found": "Methu dod o hyd i unrhyw beth ar gyfer y termau chwilio hyn",
"search_results.statuses": "Postiadau", "search_results.statuses": "Postiadau",
"search_results.statuses_fts_disabled": "Nid yw chwilio postiadau yn ôl eu cynnwys wedi'i alluogi ar y gweinydd Mastodon hwn.", "search_results.statuses_fts_disabled": "Nid yw chwilio postiadau yn ôl eu cynnwys wedi'i alluogi ar y gweinydd Mastodon hwn.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, zero {canlyniad} one {canlyniad} two {ganlyniad} other {o ganlyniadau}}", "search_results.total": "{count, number} {count, plural, zero {canlyniad} one {canlyniad} two {ganlyniad} other {o ganlyniadau}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Agor rhyngwyneb goruwchwylio ar gyfer @{name}", "status.admin_account": "Agor rhyngwyneb goruwchwylio ar gyfer @{name}",
"status.admin_status": "Agor y post hwn yn y rhyngwyneb goruwchwylio", "status.admin_status": "Agor y post hwn yn y rhyngwyneb goruwchwylio",
"status.block": "Blocio @{name}", "status.block": "Blocio @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Dangos llai i bawb", "status.show_less_all": "Dangos llai i bawb",
"status.show_more": "Dangos mwy", "status.show_more": "Dangos mwy",
"status.show_more_all": "Dangos mwy i bawb", "status.show_more_all": "Dangos mwy i bawb",
"status.show_original": "Show original",
"status.show_thread": "Dangos edefyn", "status.show_thread": "Dangos edefyn",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Dim ar gael", "status.uncached_media_warning": "Dim ar gael",
"status.unmute_conversation": "Dad-dawelu sgwrs", "status.unmute_conversation": "Dad-dawelu sgwrs",
"status.unpin": "Dadbinio o'r proffil", "status.unpin": "Dadbinio o'r proffil",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Diswyddo", "suggestions.dismiss": "Diswyddo",
"suggestions.header": "Efallai y bydd gennych ddiddordeb mewn…", "suggestions.header": "Efallai y bydd gennych ddiddordeb mewn…",
"tabs_bar.federated_timeline": "Ffederasiwn", "tabs_bar.federated_timeline": "Ffederasiwn",

@ -24,6 +24,7 @@
"account.follows_you": "Følger dig", "account.follows_you": "Følger dig",
"account.hide_reblogs": "Skjul boosts fra @{name}", "account.hide_reblogs": "Skjul boosts fra @{name}",
"account.joined": "Tilmeldt {date}", "account.joined": "Tilmeldt {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Ejerskab af dette link blev tjekket {date}", "account.link_verified_on": "Ejerskab af dette link blev tjekket {date}",
"account.locked_info": "Denne kontos fortrolighedsstatus er sat til låst. Ejeren bedømmer manuelt, hvem der kan følge vedkommende.", "account.locked_info": "Denne kontos fortrolighedsstatus er sat til låst. Ejeren bedømmer manuelt, hvem der kan følge vedkommende.",
"account.media": "Medier", "account.media": "Medier",
@ -197,22 +198,22 @@
"explore.trending_links": "Nyheder", "explore.trending_links": "Nyheder",
"explore.trending_statuses": "Indlæg", "explore.trending_statuses": "Indlæg",
"explore.trending_tags": "Hashtags", "explore.trending_tags": "Hashtags",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Denne filterkategori omfatter ikke konteksten, hvorunder dette indlæg er tilgået. Redigér filteret, hvis indlægget også ønskes filtreret i denne kontekst.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Kontekstmisforhold!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Denne filterkategori er udløbet. Ændr dens udløbsdato, for at anvende den.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Udløbet filter!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Gå til {settings_link} for at gennemse og yderligere opsætte denne filterkategori.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Filterindstillinger",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "indstillingsside",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Dette indlæg er nu føjet til flg. filterkategori: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filter tilføjet!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "gælder ikke for denne kontekst",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "udløbet",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Ny kategori: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Søg eller opret",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Vælg en eksisterende kategori eller opret en ny",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtrér dette indlæg",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtrér et indlæg",
"follow_recommendations.done": "Udført", "follow_recommendations.done": "Udført",
"follow_recommendations.heading": "Følg personer du gerne vil se indlæg fra! Her er nogle forslag.", "follow_recommendations.heading": "Følg personer du gerne vil se indlæg fra! Her er nogle forslag.",
"follow_recommendations.lead": "Indlæg, fra personer du følger, vil fremgå kronologisk ordnet i dit hjemmefeed. Vær ikke bange for at begå fejl, da du altid og meget nemt kan ændre dit valg!", "follow_recommendations.lead": "Indlæg, fra personer du følger, vil fremgå kronologisk ordnet i dit hjemmefeed. Vær ikke bange for at begå fejl, da du altid og meget nemt kan ændre dit valg!",
@ -226,8 +227,8 @@
"getting_started.heading": "Startmenu", "getting_started.heading": "Startmenu",
"getting_started.invite": "Invitér folk", "getting_started.invite": "Invitér folk",
"getting_started.open_source_notice": "Mastodon er open-source software. Du kan bidrage eller anmelde fejl via GitHub {github}.", "getting_started.open_source_notice": "Mastodon er open-source software. Du kan bidrage eller anmelde fejl via GitHub {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Kontoindstillinger", "getting_started.security": "Kontoindstillinger",
"getting_started.terms": "Tjenestevilkår",
"hashtag.column_header.tag_mode.all": "og {additional}", "hashtag.column_header.tag_mode.all": "og {additional}",
"hashtag.column_header.tag_mode.any": "eller {additional}", "hashtag.column_header.tag_mode.any": "eller {additional}",
"hashtag.column_header.tag_mode.none": "uden {additional}", "hashtag.column_header.tag_mode.none": "uden {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Ingen resultater for disse søgeord", "search_results.nothing_found": "Ingen resultater for disse søgeord",
"search_results.statuses": "Indlæg", "search_results.statuses": "Indlæg",
"search_results.statuses_fts_disabled": "Søgning på indlæg efter deres indhold ikke aktiveret på denne Mastodon-server.", "search_results.statuses_fts_disabled": "Søgning på indlæg efter deres indhold ikke aktiveret på denne Mastodon-server.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Åbn modereringsbrugerflade for @{name}", "status.admin_account": "Åbn modereringsbrugerflade for @{name}",
"status.admin_status": "Åbn dette indlæg i modereringsbrugerfladen", "status.admin_status": "Åbn dette indlæg i modereringsbrugerfladen",
"status.block": "Blokér @{name}", "status.block": "Blokér @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}", "status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
"status.embed": "Indlejr", "status.embed": "Indlejr",
"status.favourite": "Favorit", "status.favourite": "Favorit",
"status.filter": "Filter this post", "status.filter": "Filtrér dette indlæg",
"status.filtered": "Filtreret", "status.filtered": "Filtreret",
"status.hide": "Skjul indlæg", "status.hide": "Skjul indlæg",
"status.history.created": "{name} oprettet {date}", "status.history.created": "{name} oprettet {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Vis mindre for alle", "status.show_less_all": "Vis mindre for alle",
"status.show_more": "Vis mere", "status.show_more": "Vis mere",
"status.show_more_all": "Vis mere for alle", "status.show_more_all": "Vis mere for alle",
"status.show_original": "Vis original",
"status.show_thread": "Vis tråd", "status.show_thread": "Vis tråd",
"status.translate": "Oversæt",
"status.translated_from": "Oversat fra {lang}",
"status.uncached_media_warning": "Utilgængelig", "status.uncached_media_warning": "Utilgængelig",
"status.unmute_conversation": "Genaktivér samtale", "status.unmute_conversation": "Genaktivér samtale",
"status.unpin": "Frigør fra profil", "status.unpin": "Frigør fra profil",
"subscribed_languages.lead": "Kun indlæg på udvalgte sprog vil fremgå på Hjem og listetidslinjer efter ændringen. Vælg ingen for at modtage indlæg på alle sprog.",
"subscribed_languages.save": "Gem ændringer",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Afvis foreslag", "suggestions.dismiss": "Afvis foreslag",
"suggestions.header": "Du er måske interesseret i…", "suggestions.header": "Du er måske interesseret i…",
"tabs_bar.federated_timeline": "Fælles", "tabs_bar.federated_timeline": "Fælles",

@ -24,6 +24,7 @@
"account.follows_you": "Folgt dir", "account.follows_you": "Folgt dir",
"account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen", "account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen",
"account.joined": "Beigetreten am {date}", "account.joined": "Beigetreten am {date}",
"account.languages": "Abonnierte Sprachen ändern",
"account.link_verified_on": "Diesem Profil folgt niemand", "account.link_verified_on": "Diesem Profil folgt niemand",
"account.locked_info": "Der Privatsphärenstatus dieses Accounts wurde auf „gesperrt“ gesetzt. Die Person bestimmt manuell, wer ihm/ihr folgen darf.", "account.locked_info": "Der Privatsphärenstatus dieses Accounts wurde auf „gesperrt“ gesetzt. Die Person bestimmt manuell, wer ihm/ihr folgen darf.",
"account.media": "Medien", "account.media": "Medien",
@ -106,7 +107,7 @@
"compose_form.poll.option_placeholder": "Wahl {number}", "compose_form.poll.option_placeholder": "Wahl {number}",
"compose_form.poll.remove_option": "Wahl entfernen", "compose_form.poll.remove_option": "Wahl entfernen",
"compose_form.poll.switch_to_multiple": "Umfrage ändern, um mehrere Optionen zu erlauben", "compose_form.poll.switch_to_multiple": "Umfrage ändern, um mehrere Optionen zu erlauben",
"compose_form.poll.switch_to_single": "Umfrage ändern, um eine einzige Wahl zu erlauben", "compose_form.poll.switch_to_single": "Umfrage ändern, sodass nur eine einzige Auswahl erlaubt ist",
"compose_form.publish": "Veröffentlichen", "compose_form.publish": "Veröffentlichen",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Änderungen speichern", "compose_form.save_changes": "Änderungen speichern",
@ -175,20 +176,20 @@
"empty_column.explore_statuses": "Momentan ist nichts im Trend. Schau später wieder vorbei!", "empty_column.explore_statuses": "Momentan ist nichts im Trend. Schau später wieder vorbei!",
"empty_column.favourited_statuses": "Du hast noch keine favorisierten Tröts. Wenn du einen favorisierst, wird er hier erscheinen.", "empty_column.favourited_statuses": "Du hast noch keine favorisierten Tröts. Wenn du einen favorisierst, wird er hier erscheinen.",
"empty_column.favourites": "Noch niemand hat diesen Beitrag favorisiert. Sobald es jemand tut, wird das hier angezeigt.", "empty_column.favourites": "Noch niemand hat diesen Beitrag favorisiert. Sobald es jemand tut, wird das hier angezeigt.",
"empty_column.follow_recommendations": "Es sieht so aus, als könnten keine Vorschläge für dich generiert werden. Du kannst versuchen nach Leuten zu suchen, die du vielleicht kennst oder du kannst angesagte Hashtags erkunden.", "empty_column.follow_recommendations": "Es sieht so aus, als könnten keine Vorschläge für dich generiert werden. Du kannst versuchen, nach Leuten zu suchen, die du vielleicht kennst, oder du kannst angesagte Hashtags erkunden.",
"empty_column.follow_requests": "Du hast noch keine Folge-Anfragen. Sobald du eine erhältst, wird sie hier angezeigt.", "empty_column.follow_requests": "Du hast noch keine Folge-Anfragen. Sobald du eine erhältst, wird sie hier angezeigt.",
"empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.", "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
"empty_column.home": "Deine Startseite ist leer! Folge mehr Leuten, um sie zu füllen. {suggestions}", "empty_column.home": "Deine Startseite ist leer! Folge mehr Leuten, um sie zu füllen. {suggestions}",
"empty_column.home.suggestions": "Ein paar Vorschläge ansehen", "empty_column.home.suggestions": "Ein paar Vorschläge ansehen",
"empty_column.list": "Diese Liste ist derzeit leer. Wenn Konten auf dieser Liste neue Beiträge veröffentlichen werden sie hier erscheinen.", "empty_column.list": "Diese Liste ist derzeit leer. Wenn Konten auf dieser Liste neue Beiträge veröffentlichen werden sie hier erscheinen.",
"empty_column.lists": "Du hast noch keine Listen. Wenn du eine anlegst, wird sie hier angezeigt.", "empty_column.lists": "Du hast noch keine Listen. Wenn du eine anlegst, wird sie hier angezeigt werden.",
"empty_column.mutes": "Du hast keine Profile stummgeschaltet.", "empty_column.mutes": "Du hast keine Profile stummgeschaltet.",
"empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um ins Gespräch zu kommen.", "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um ins Gespräch zu kommen.",
"empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Servern, um die Zeitleiste aufzufüllen", "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Servern, um die Zeitleiste aufzufüllen",
"error.unexpected_crash.explanation": "Aufgrund eines Fehlers in unserem Code oder einer Browsereinkompatibilität konnte diese Seite nicht korrekt angezeigt werden.", "error.unexpected_crash.explanation": "Aufgrund eines Fehlers in unserem Code oder einer Browser-Inkompatibilität konnte diese Seite nicht korrekt angezeigt werden.",
"error.unexpected_crash.explanation_addons": "Diese Seite konnte nicht korrekt angezeigt werden. Dieser Fehler wird wahrscheinlich durch ein Browser-Add-on oder automatische Übersetzungswerkzeuge verursacht.", "error.unexpected_crash.explanation_addons": "Diese Seite konnte nicht korrekt angezeigt werden. Dieser Fehler wird wahrscheinlich durch ein Browser-Add-on oder automatische Übersetzungswerkzeuge verursacht.",
"error.unexpected_crash.next_steps": "Versuche die Seite zu aktualisieren. Wenn das nicht hilft, kannst du Mastodon über einen anderen Browser oder eine native App verwenden.", "error.unexpected_crash.next_steps": "Versuche, die Seite zu aktualisieren. Wenn das nicht hilft, kannst du Mastodon über einen anderen Browser oder eine native App verwenden.",
"error.unexpected_crash.next_steps_addons": "Versuche sie zu deaktivieren und lade dann die Seite neu. Wenn das Problem weiterhin besteht, solltest du Mastodon über einen anderen Browser oder eine native App nutzen.", "error.unexpected_crash.next_steps_addons": "Versuche, sie zu deaktivieren, und lade dann die Seite neu. Wenn das Problem weiterhin besteht, solltest du Mastodon über einen anderen Browser oder eine native App nutzen.",
"errors.unexpected_crash.copy_stacktrace": "Fehlerlog in die Zwischenablage kopieren", "errors.unexpected_crash.copy_stacktrace": "Fehlerlog in die Zwischenablage kopieren",
"errors.unexpected_crash.report_issue": "Problem melden", "errors.unexpected_crash.report_issue": "Problem melden",
"explore.search_results": "Suchergebnisse", "explore.search_results": "Suchergebnisse",
@ -197,22 +198,22 @@
"explore.trending_links": "Nachrichten", "explore.trending_links": "Nachrichten",
"explore.trending_statuses": "Beiträge", "explore.trending_statuses": "Beiträge",
"explore.trending_tags": "Hashtags", "explore.trending_tags": "Hashtags",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Diese Filterkategorie gilt nicht für den Kontext, in welchem du auf diesen Beitrag zugegriffen hast. Wenn der Beitrag auch in diesem Kontext gefiltert werden soll, musst du den Filter bearbeiten.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Kontext stimmt nicht überein!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Diese Filterkategrie ist abgelaufen, du musst das Ablaufdatum für diese Kategorie ändern.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Abgelaufener Filter!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Um diese Filterkategorie zu überprüfen und weiter zu konfigurieren, gehe zu {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Filtereinstellungen",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "Einstellungsseite",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Dieser Post wurde zu folgender Filterkategorie hinzugefügt: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filter hinzugefügt!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "gilt nicht für diesen Kontext",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "abgelaufen",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Neue Kategorie: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Suchen oder Erstellen",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Eine existierende Kategorie benutzen oder eine erstellen",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Diesen Beitrag filtern",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Einen Beitrag filtern",
"follow_recommendations.done": "Fertig", "follow_recommendations.done": "Fertig",
"follow_recommendations.heading": "Folge Leuten, von denen du Beiträge sehen möchtest! Hier sind einige Vorschläge.", "follow_recommendations.heading": "Folge Leuten, von denen du Beiträge sehen möchtest! Hier sind einige Vorschläge.",
"follow_recommendations.lead": "Beiträge von Personen, denen du folgst, werden in chronologischer Reihenfolge auf deiner Startseite angezeigt. Hab keine Angst, Fehler zu machen, du kannst den Leuten jederzeit wieder entfolgen!", "follow_recommendations.lead": "Beiträge von Personen, denen du folgst, werden in chronologischer Reihenfolge auf deiner Startseite angezeigt. Hab keine Angst, Fehler zu machen, du kannst den Leuten jederzeit wieder entfolgen!",
@ -226,16 +227,16 @@
"getting_started.heading": "Erste Schritte", "getting_started.heading": "Erste Schritte",
"getting_started.invite": "Leute einladen", "getting_started.invite": "Leute einladen",
"getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf GitHub unter {github} dazu beitragen oder Probleme melden.", "getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf GitHub unter {github} dazu beitragen oder Probleme melden.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Konto & Sicherheit", "getting_started.security": "Konto & Sicherheit",
"getting_started.terms": "Nutzungsbedingungen",
"hashtag.column_header.tag_mode.all": "und {additional}", "hashtag.column_header.tag_mode.all": "und {additional}",
"hashtag.column_header.tag_mode.any": "oder {additional}", "hashtag.column_header.tag_mode.any": "oder {additional}",
"hashtag.column_header.tag_mode.none": "ohne {additional}", "hashtag.column_header.tag_mode.none": "ohne {additional}",
"hashtag.column_settings.select.no_options_message": "Keine Vorschläge gefunden", "hashtag.column_settings.select.no_options_message": "Keine Vorschläge gefunden",
"hashtag.column_settings.select.placeholder": "Hashtags eintragen…", "hashtag.column_settings.select.placeholder": "Hashtags eintragen…",
"hashtag.column_settings.tag_mode.all": "All diese", "hashtag.column_settings.tag_mode.all": "All diese",
"hashtag.column_settings.tag_mode.any": "Eins von diesen", "hashtag.column_settings.tag_mode.any": "Eines von diesen",
"hashtag.column_settings.tag_mode.none": "Keins von diesen", "hashtag.column_settings.tag_mode.none": "Keines von diesen",
"hashtag.column_settings.tag_toggle": "Zusätzliche Hashtags für diese Spalte einfügen", "hashtag.column_settings.tag_toggle": "Zusätzliche Hashtags für diese Spalte einfügen",
"hashtag.follow": "Hashtag folgen", "hashtag.follow": "Hashtag folgen",
"hashtag.total_volume": "Gesamtes Aufkommen {days, plural, one {am letzten Tag} other {in den letzten {days} Tagen}}", "hashtag.total_volume": "Gesamtes Aufkommen {days, plural, one {am letzten Tag} other {in den letzten {days} Tagen}}",
@ -300,7 +301,7 @@
"lists.replies_policy.list": "Mitglieder der Liste", "lists.replies_policy.list": "Mitglieder der Liste",
"lists.replies_policy.none": "Niemand", "lists.replies_policy.none": "Niemand",
"lists.replies_policy.title": "Antworten anzeigen für:", "lists.replies_policy.title": "Antworten anzeigen für:",
"lists.search": "Suche nach Leuten denen du folgst", "lists.search": "Suche nach Leuten, denen du folgst",
"lists.subheading": "Deine Listen", "lists.subheading": "Deine Listen",
"load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}", "load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}",
"loading_indicator.label": "Wird geladen …", "loading_indicator.label": "Wird geladen …",
@ -341,7 +342,7 @@
"notification.follow_request": "{name} möchte dir folgen", "notification.follow_request": "{name} möchte dir folgen",
"notification.mention": "{name} hat dich erwähnt", "notification.mention": "{name} hat dich erwähnt",
"notification.own_poll": "Deine Umfrage ist beendet", "notification.own_poll": "Deine Umfrage ist beendet",
"notification.poll": "Eine Umfrage in der du abgestimmt hast ist vorbei", "notification.poll": "Eine Umfrage, an der du teilgenommen hast, ist vorbei",
"notification.reblog": "{name} hat deinen Beitrag geteilt", "notification.reblog": "{name} hat deinen Beitrag geteilt",
"notification.status": "{name} hat gerade etwas gepostet", "notification.status": "{name} hat gerade etwas gepostet",
"notification.update": "{name} bearbeitete einen Beitrag", "notification.update": "{name} bearbeitete einen Beitrag",
@ -430,7 +431,7 @@
"report.forward": "An {target} weiterleiten", "report.forward": "An {target} weiterleiten",
"report.forward_hint": "Dieses Konto gehört zu einem anderen Server. Soll eine anonymisierte Kopie der Meldung auch dorthin geschickt werden?", "report.forward_hint": "Dieses Konto gehört zu einem anderen Server. Soll eine anonymisierte Kopie der Meldung auch dorthin geschickt werden?",
"report.mute": "Stummschalten", "report.mute": "Stummschalten",
"report.mute_explanation": "Du wirst die Beiträge vom Konto nicht mehr sehen. Das Konto kann dir immernoch folgen und die Person hinter dem Konto wird deine Beiträge sehen können und nicht wissen, dass du sie stumm geschaltet hast.", "report.mute_explanation": "Du wirst die Beiträge vom Konto nicht mehr sehen. Das Konto kann dir immer noch folgen, und die Person hinter dem Konto wird deine Beiträge sehen können und nicht wissen, dass du sie stummgeschaltet hast.",
"report.next": "Weiter", "report.next": "Weiter",
"report.placeholder": "Zusätzliche Kommentare", "report.placeholder": "Zusätzliche Kommentare",
"report.reasons.dislike": "Das gefällt mir nicht", "report.reasons.dislike": "Das gefällt mir nicht",
@ -447,7 +448,7 @@
"report.statuses.title": "Gibt es Beiträge, die diesen Bericht unterstützen?", "report.statuses.title": "Gibt es Beiträge, die diesen Bericht unterstützen?",
"report.submit": "Absenden", "report.submit": "Absenden",
"report.target": "{target} melden", "report.target": "{target} melden",
"report.thanks.take_action": "Das sind deine Möglichkeiten, zu bestimmen, was du auf Mastodon sehen möchtest:", "report.thanks.take_action": "Das sind deine Möglichkeiten zu bestimmen, was du auf Mastodon sehen möchtest:",
"report.thanks.take_action_actionable": "Während wir dies überprüfen, kannst du gegen @{name} vorgehen:", "report.thanks.take_action_actionable": "Während wir dies überprüfen, kannst du gegen @{name} vorgehen:",
"report.thanks.title": "Möchtest du das nicht sehen?", "report.thanks.title": "Möchtest du das nicht sehen?",
"report.thanks.title_actionable": "Vielen Dank für die Meldung, wir werden uns das ansehen.", "report.thanks.title_actionable": "Vielen Dank für die Meldung, wir werden uns das ansehen.",
@ -460,7 +461,7 @@
"report_notification.open": "Meldung öffnen", "report_notification.open": "Meldung öffnen",
"search.placeholder": "Suche", "search.placeholder": "Suche",
"search_popout.search_format": "Fortgeschrittenes Suchformat", "search_popout.search_format": "Fortgeschrittenes Suchformat",
"search_popout.tips.full_text": "Einfache Texteingabe gibt Beiträge, die du geschrieben, favorisiert und geteilt hast zurück. Außerdem auch Beiträge in denen du erwähnt wurdest, aber auch passende Nutzernamen, Anzeigenamen oder Hashtags.", "search_popout.tips.full_text": "Einfache Texteingabe gibt Beiträge, die du geschrieben, favorisiert und geteilt hast, zurück; außerdem auch Beiträge, in denen du erwähnt wurdest, aber auch passende Nutzernamen, Anzeigenamen oder Hashtags.",
"search_popout.tips.hashtag": "Hashtag", "search_popout.tips.hashtag": "Hashtag",
"search_popout.tips.status": "Tröt", "search_popout.tips.status": "Tröt",
"search_popout.tips.text": "Einfache Texteingabe gibt Anzeigenamen, Benutzernamen und Hashtags zurück", "search_popout.tips.text": "Einfache Texteingabe gibt Anzeigenamen, Benutzernamen und Hashtags zurück",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Nichts für diese Suchbegriffe gefunden", "search_results.nothing_found": "Nichts für diese Suchbegriffe gefunden",
"search_results.statuses": "Beiträge", "search_results.statuses": "Beiträge",
"search_results.statuses_fts_disabled": "Die Suche für Beiträge nach ihrem Inhalt ist auf diesem Mastodon-Server deaktiviert.", "search_results.statuses_fts_disabled": "Die Suche für Beiträge nach ihrem Inhalt ist auf diesem Mastodon-Server deaktiviert.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Öffne Moderationsoberfläche für @{name}", "status.admin_account": "Öffne Moderationsoberfläche für @{name}",
"status.admin_status": "Öffne Beitrag in der Moderationsoberfläche", "status.admin_status": "Öffne Beitrag in der Moderationsoberfläche",
"status.block": "Blockiere @{name}", "status.block": "Blockiere @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "{count, plural, one {{count} mal} other {{count} mal}} bearbeitet", "status.edited_x_times": "{count, plural, one {{count} mal} other {{count} mal}} bearbeitet",
"status.embed": "Einbetten", "status.embed": "Einbetten",
"status.favourite": "Favorisieren", "status.favourite": "Favorisieren",
"status.filter": "Filter this post", "status.filter": "Diesen Beitrag filtern",
"status.filtered": "Gefiltert", "status.filtered": "Gefiltert",
"status.hide": "Tröt verbergen", "status.hide": "Tröt verbergen",
"status.history.created": "{name} erstellte {date}", "status.history.created": "{name} erstellte {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Alle Inhaltswarnungen zuklappen", "status.show_less_all": "Alle Inhaltswarnungen zuklappen",
"status.show_more": "Mehr anzeigen", "status.show_more": "Mehr anzeigen",
"status.show_more_all": "Alle Inhaltswarnungen aufklappen", "status.show_more_all": "Alle Inhaltswarnungen aufklappen",
"status.show_original": "Original anzeigen",
"status.show_thread": "Zeige Konversation", "status.show_thread": "Zeige Konversation",
"status.translate": "Übersetzen",
"status.translated_from": "Aus {lang} übersetzt",
"status.uncached_media_warning": "Nicht verfügbar", "status.uncached_media_warning": "Nicht verfügbar",
"status.unmute_conversation": "Stummschaltung von Konversation aufheben", "status.unmute_conversation": "Stummschaltung von Konversation aufheben",
"status.unpin": "Vom Profil lösen", "status.unpin": "Vom Profil lösen",
"subscribed_languages.lead": "Nur Beiträge in ausgewählten Sprachen werden nach der Änderung auf deiner Startseite und den Listen angezeigt. Wähle keine aus, um Beiträge in allen Sprachen zu erhalten.",
"subscribed_languages.save": "Änderungen speichern",
"subscribed_languages.target": "Abonnierte Sprachen für {target} ändern",
"suggestions.dismiss": "Empfehlung ausblenden", "suggestions.dismiss": "Empfehlung ausblenden",
"suggestions.header": "Du bist vielleicht interessiert an…", "suggestions.header": "Du bist vielleicht interessiert an…",
"tabs_bar.federated_timeline": "Föderation", "tabs_bar.federated_timeline": "Föderation",

@ -653,6 +653,18 @@
"defaultMessage": "Read more", "defaultMessage": "Read more",
"id": "status.read_more" "id": "status.read_more"
}, },
{
"defaultMessage": "Translated from {lang}",
"id": "status.translated_from"
},
{
"defaultMessage": "Show original",
"id": "status.show_original"
},
{
"defaultMessage": "Translate",
"id": "status.translate"
},
{ {
"defaultMessage": "Show more", "defaultMessage": "Show more",
"id": "status.show_more" "id": "status.show_more"
@ -1030,6 +1042,10 @@
"defaultMessage": "Open moderation interface for @{name}", "defaultMessage": "Open moderation interface for @{name}",
"id": "status.admin_account" "id": "status.admin_account"
}, },
{
"defaultMessage": "Change subscribed languages",
"id": "account.languages"
},
{ {
"defaultMessage": "Follows you", "defaultMessage": "Follows you",
"id": "account.follows_you" "id": "account.follows_you"
@ -1826,6 +1842,19 @@
}, },
{ {
"descriptors": [ "descriptors": [
{
"defaultMessage": "Nothing is trending right now. Check back later!",
"id": "empty_column.explore_statuses"
}
],
"path": "app/javascript/mastodon/features/explore/links.json"
},
{
"descriptors": [
{
"defaultMessage": "Search for {q}",
"id": "search_results.title"
},
{ {
"defaultMessage": "Could not find anything for these search terms", "defaultMessage": "Could not find anything for these search terms",
"id": "search_results.nothing_found" "id": "search_results.nothing_found"
@ -1858,6 +1887,24 @@
], ],
"path": "app/javascript/mastodon/features/explore/statuses.json" "path": "app/javascript/mastodon/features/explore/statuses.json"
}, },
{
"descriptors": [
{
"defaultMessage": "Nothing is trending right now. Check back later!",
"id": "empty_column.explore_statuses"
}
],
"path": "app/javascript/mastodon/features/explore/suggestions.json"
},
{
"descriptors": [
{
"defaultMessage": "Nothing is trending right now. Check back later!",
"id": "empty_column.explore_statuses"
}
],
"path": "app/javascript/mastodon/features/explore/tags.json"
},
{ {
"descriptors": [ "descriptors": [
{ {
@ -3018,6 +3065,27 @@
], ],
"path": "app/javascript/mastodon/features/report/comment.json" "path": "app/javascript/mastodon/features/report/comment.json"
}, },
{
"descriptors": [
{
"defaultMessage": "Public",
"id": "privacy.public.short"
},
{
"defaultMessage": "Unlisted",
"id": "privacy.unlisted.short"
},
{
"defaultMessage": "Followers-only",
"id": "privacy.private.short"
},
{
"defaultMessage": "Mentioned people only",
"id": "privacy.direct.short"
}
],
"path": "app/javascript/mastodon/features/report/components/status_check_box.json"
},
{ {
"descriptors": [ "descriptors": [
{ {
@ -3350,6 +3418,27 @@
], ],
"path": "app/javascript/mastodon/features/status/index.json" "path": "app/javascript/mastodon/features/status/index.json"
}, },
{
"descriptors": [
{
"defaultMessage": "Close",
"id": "lightbox.close"
},
{
"defaultMessage": "Change subscribed languages for {target}",
"id": "subscribed_languages.target"
},
{
"defaultMessage": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"id": "subscribed_languages.lead"
},
{
"defaultMessage": "Save changes",
"id": "subscribed_languages.save"
}
],
"path": "app/javascript/mastodon/features/subscribed_languages_modal/index.json"
},
{ {
"descriptors": [ "descriptors": [
{ {
@ -3633,8 +3722,8 @@
"id": "navigation_bar.apps" "id": "navigation_bar.apps"
}, },
{ {
"defaultMessage": "Terms of service", "defaultMessage": "Privacy Policy",
"id": "getting_started.terms" "id": "getting_started.privacy_policy"
}, },
{ {
"defaultMessage": "Developers", "defaultMessage": "Developers",
@ -3779,6 +3868,23 @@
], ],
"path": "app/javascript/mastodon/features/ui/components/report_modal.json" "path": "app/javascript/mastodon/features/ui/components/report_modal.json"
}, },
{
"descriptors": [
{
"defaultMessage": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"id": "sign_in_banner.text"
},
{
"defaultMessage": "Sign in",
"id": "sign_in_banner.sign_in"
},
{
"defaultMessage": "Create account",
"id": "sign_in_banner.create_account"
}
],
"path": "app/javascript/mastodon/features/ui/components/sign_in_banner.json"
},
{ {
"descriptors": [ "descriptors": [
{ {

@ -24,6 +24,7 @@
"account.follows_you": "Σε ακολουθεί", "account.follows_you": "Σε ακολουθεί",
"account.hide_reblogs": "Απόκρυψη προωθήσεων από @{name}", "account.hide_reblogs": "Απόκρυψη προωθήσεων από @{name}",
"account.joined": "Μέλος από τις {date}", "account.joined": "Μέλος από τις {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου ελέχθηκε την {date}", "account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου ελέχθηκε την {date}",
"account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.", "account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.",
"account.media": "Πολυμέσα", "account.media": "Πολυμέσα",
@ -226,8 +227,8 @@
"getting_started.heading": "Αφετηρία", "getting_started.heading": "Αφετηρία",
"getting_started.invite": "Προσκάλεσε κόσμο", "getting_started.invite": "Προσκάλεσε κόσμο",
"getting_started.open_source_notice": "Το Mastodon είναι ελεύθερο λογισμικό. Μπορείς να συνεισφέρεις ή να αναφέρεις ζητήματα στο GitHub στο {github}.", "getting_started.open_source_notice": "Το Mastodon είναι ελεύθερο λογισμικό. Μπορείς να συνεισφέρεις ή να αναφέρεις ζητήματα στο GitHub στο {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Ασφάλεια", "getting_started.security": "Ασφάλεια",
"getting_started.terms": "Όροι χρήσης",
"hashtag.column_header.tag_mode.all": "και {additional}", "hashtag.column_header.tag_mode.all": "και {additional}",
"hashtag.column_header.tag_mode.any": "ή {additional}", "hashtag.column_header.tag_mode.any": "ή {additional}",
"hashtag.column_header.tag_mode.none": "χωρίς {additional}", "hashtag.column_header.tag_mode.none": "χωρίς {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Τουτ", "search_results.statuses": "Τουτ",
"search_results.statuses_fts_disabled": "Η αναζήτηση τουτ βάσει του περιεχόμενού τους δεν είναι ενεργοποιημένη σε αυτό τον κόμβο.", "search_results.statuses_fts_disabled": "Η αναζήτηση τουτ βάσει του περιεχόμενού τους δεν είναι ενεργοποιημένη σε αυτό τον κόμβο.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, zero {αποτελέσματα} one {αποτέλεσμα} other {αποτελέσματα}}", "search_results.total": "{count, number} {count, plural, zero {αποτελέσματα} one {αποτέλεσμα} other {αποτελέσματα}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Άνοιγμα λειτουργίας διαμεσολάβησης για τον/την @{name}", "status.admin_account": "Άνοιγμα λειτουργίας διαμεσολάβησης για τον/την @{name}",
"status.admin_status": "Άνοιγμα αυτής της δημοσίευσης στη λειτουργία διαμεσολάβησης", "status.admin_status": "Άνοιγμα αυτής της δημοσίευσης στη λειτουργία διαμεσολάβησης",
"status.block": "Αποκλεισμός @{name}", "status.block": "Αποκλεισμός @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Δείξε λιγότερα για όλα", "status.show_less_all": "Δείξε λιγότερα για όλα",
"status.show_more": "Δείξε περισσότερα", "status.show_more": "Δείξε περισσότερα",
"status.show_more_all": "Δείξε περισσότερα για όλα", "status.show_more_all": "Δείξε περισσότερα για όλα",
"status.show_original": "Εμφάνιση αρχικού",
"status.show_thread": "Εμφάνιση νήματος", "status.show_thread": "Εμφάνιση νήματος",
"status.translate": "Μετάφραση",
"status.translated_from": "Μεταφράστηκε από {lang}",
"status.uncached_media_warning": "Μη διαθέσιμα", "status.uncached_media_warning": "Μη διαθέσιμα",
"status.unmute_conversation": "Διέκοψε την αποσιώπηση της συζήτησης", "status.unmute_conversation": "Διέκοψε την αποσιώπηση της συζήτησης",
"status.unpin": "Ξεκαρφίτσωσε από το προφίλ", "status.unpin": "Ξεκαρφίτσωσε από το προφίλ",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Αποθήκευση αλλαγών",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Απόρριψη πρότασης", "suggestions.dismiss": "Απόρριψη πρότασης",
"suggestions.header": "Ίσως να ενδιαφέρεσαι για…", "suggestions.header": "Ίσως να ενδιαφέρεσαι για…",
"tabs_bar.federated_timeline": "Ομοσπονδιακή", "tabs_bar.federated_timeline": "Ομοσπονδιακή",

@ -24,6 +24,7 @@
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",
"account.joined": "Joined {date}", "account.joined": "Joined {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
@ -226,8 +227,8 @@
"getting_started.heading": "Getting started", "getting_started.heading": "Getting started",
"getting_started.invite": "Invite people", "getting_started.invite": "Invite people",
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Security", "getting_started.security": "Security",
"getting_started.terms": "Terms of service",
"hashtag.column_header.tag_mode.all": "and {additional}", "hashtag.column_header.tag_mode.all": "and {additional}",
"hashtag.column_header.tag_mode.any": "or {additional}", "hashtag.column_header.tag_mode.any": "or {additional}",
"hashtag.column_header.tag_mode.none": "without {additional}", "hashtag.column_header.tag_mode.none": "without {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Posts", "search_results.statuses": "Posts",
"search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.", "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}", "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Open moderation interface for @{name}", "status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this status in the moderation interface", "status.admin_status": "Open this status in the moderation interface",
"status.block": "Block @{name}", "status.block": "Block @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Show less for all", "status.show_less_all": "Show less for all",
"status.show_more": "Show more", "status.show_more": "Show more",
"status.show_more_all": "Show more for all", "status.show_more_all": "Show more for all",
"status.show_original": "Show original",
"status.show_thread": "Show thread", "status.show_thread": "Show thread",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Not available", "status.uncached_media_warning": "Not available",
"status.unmute_conversation": "Unmute conversation", "status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile", "status.unpin": "Unpin from profile",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Dismiss suggestion", "suggestions.dismiss": "Dismiss suggestion",
"suggestions.header": "You might be interested in…", "suggestions.header": "You might be interested in…",
"tabs_bar.federated_timeline": "Federated", "tabs_bar.federated_timeline": "Federated",

@ -24,6 +24,7 @@
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",
"account.joined": "Joined {date}", "account.joined": "Joined {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
@ -230,8 +231,8 @@
"getting_started.heading": "Getting started", "getting_started.heading": "Getting started",
"getting_started.invite": "Invite people", "getting_started.invite": "Invite people",
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Account settings", "getting_started.security": "Account settings",
"getting_started.terms": "Terms of service",
"hashtag.column_header.tag_mode.all": "and {additional}", "hashtag.column_header.tag_mode.all": "and {additional}",
"hashtag.column_header.tag_mode.any": "or {additional}", "hashtag.column_header.tag_mode.any": "or {additional}",
"hashtag.column_header.tag_mode.none": "without {additional}", "hashtag.column_header.tag_mode.none": "without {additional}",
@ -476,7 +477,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Posts", "search_results.statuses": "Posts",
"search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.", "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}", "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Open moderation interface for @{name}", "status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this post in the moderation interface", "status.admin_status": "Open this post in the moderation interface",
"status.block": "Block @{name}", "status.block": "Block @{name}",
@ -523,10 +528,16 @@
"status.show_less_all": "Show less for all", "status.show_less_all": "Show less for all",
"status.show_more": "Show more", "status.show_more": "Show more",
"status.show_more_all": "Show more for all", "status.show_more_all": "Show more for all",
"status.show_original": "Show original",
"status.show_thread": "Show thread", "status.show_thread": "Show thread",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Not available", "status.uncached_media_warning": "Not available",
"status.unmute_conversation": "Unmute conversation", "status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile", "status.unpin": "Unpin from profile",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Dismiss suggestion", "suggestions.dismiss": "Dismiss suggestion",
"suggestions.header": "You might be interested in…", "suggestions.header": "You might be interested in…",
"tabs_bar.federated_timeline": "Federated", "tabs_bar.federated_timeline": "Federated",

@ -24,6 +24,7 @@
"account.follows_you": "Sekvas vin", "account.follows_you": "Sekvas vin",
"account.hide_reblogs": "Kaŝi la plusendojn de @{name}", "account.hide_reblogs": "Kaŝi la plusendojn de @{name}",
"account.joined": "Kuniĝis {date}", "account.joined": "Kuniĝis {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "La posedanto de tiu ligilo estis kontrolita je {date}", "account.link_verified_on": "La posedanto de tiu ligilo estis kontrolita je {date}",
"account.locked_info": "La privateco de tiu konto estas elektita kiel fermita. La posedanto povas mane akcepti tiun, kiu povas sekvi rin.", "account.locked_info": "La privateco de tiu konto estas elektita kiel fermita. La posedanto povas mane akcepti tiun, kiu povas sekvi rin.",
"account.media": "Aŭdovidaĵoj", "account.media": "Aŭdovidaĵoj",
@ -59,7 +60,7 @@
"alert.unexpected.title": "Aj!", "alert.unexpected.title": "Aj!",
"announcement.announcement": "Anonco", "announcement.announcement": "Anonco",
"attachments_list.unprocessed": "(neprilaborita)", "attachments_list.unprocessed": "(neprilaborita)",
"audio.hide": "Hide audio", "audio.hide": "Kaŝi aŭdion",
"autosuggest_hashtag.per_week": "{count} semajne", "autosuggest_hashtag.per_week": "{count} semajne",
"boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje", "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
"bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.", "bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
@ -202,16 +203,16 @@
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Expired filter!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Filtrilopcioj",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "opciopaĝo",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Filter added!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "does not apply to this context",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "eksvalidiĝinta",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nova klaso: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Serĉi aŭ krei",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtri ĉi afiŝo",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filter a post",
"follow_recommendations.done": "Farita", "follow_recommendations.done": "Farita",
"follow_recommendations.heading": "Sekvi la personojn kies mesaĝojn vi volas vidi! Jen iom da sugestoj.", "follow_recommendations.heading": "Sekvi la personojn kies mesaĝojn vi volas vidi! Jen iom da sugestoj.",
@ -226,8 +227,8 @@
"getting_started.heading": "Por komenci", "getting_started.heading": "Por komenci",
"getting_started.invite": "Inviti homojn", "getting_started.invite": "Inviti homojn",
"getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en GitHub je {github}.", "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en GitHub je {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Sekureco", "getting_started.security": "Sekureco",
"getting_started.terms": "Kondiĉoj de la servo",
"hashtag.column_header.tag_mode.all": "kaj {additional}", "hashtag.column_header.tag_mode.all": "kaj {additional}",
"hashtag.column_header.tag_mode.any": "aŭ {additional}", "hashtag.column_header.tag_mode.any": "aŭ {additional}",
"hashtag.column_header.tag_mode.none": "sen {additional}", "hashtag.column_header.tag_mode.none": "sen {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Povis trovi nenion por ĉi tiuj serĉaj terminoj", "search_results.nothing_found": "Povis trovi nenion por ĉi tiuj serĉaj terminoj",
"search_results.statuses": "Mesaĝoj", "search_results.statuses": "Mesaĝoj",
"search_results.statuses_fts_disabled": "Serĉi mesaĝojn laŭ enhavo ne estas ebligita en ĉi tiu Mastodon-servilo.", "search_results.statuses_fts_disabled": "Serĉi mesaĝojn laŭ enhavo ne estas ebligita en ĉi tiu Mastodon-servilo.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}", "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Malfermi la kontrolan interfacon por @{name}", "status.admin_account": "Malfermi la kontrolan interfacon por @{name}",
"status.admin_status": "Malfermi ĉi tiun mesaĝon en la kontrola interfaco", "status.admin_status": "Malfermi ĉi tiun mesaĝon en la kontrola interfaco",
"status.block": "Bloki @{name}", "status.block": "Bloki @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}", "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}",
"status.embed": "Enkorpigi", "status.embed": "Enkorpigi",
"status.favourite": "Aldoni al viaj preferaĵoj", "status.favourite": "Aldoni al viaj preferaĵoj",
"status.filter": "Filter this post", "status.filter": "Filtri ĉi afiŝo",
"status.filtered": "Filtrita", "status.filtered": "Filtrita",
"status.hide": "Kaŝi la mesaĝon", "status.hide": "Kaŝi la mesaĝon",
"status.history.created": "{name} kreis {date}", "status.history.created": "{name} kreis {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Montri malpli ĉiun", "status.show_less_all": "Montri malpli ĉiun",
"status.show_more": "Montri pli", "status.show_more": "Montri pli",
"status.show_more_all": "Montri pli ĉiun", "status.show_more_all": "Montri pli ĉiun",
"status.show_original": "Show original",
"status.show_thread": "Montri la mesaĝaron", "status.show_thread": "Montri la mesaĝaron",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Nedisponebla", "status.uncached_media_warning": "Nedisponebla",
"status.unmute_conversation": "Malsilentigi la konversacion", "status.unmute_conversation": "Malsilentigi la konversacion",
"status.unpin": "Depingli de profilo", "status.unpin": "Depingli de profilo",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Forigi la proponon", "suggestions.dismiss": "Forigi la proponon",
"suggestions.header": "Vi povus interesiĝi pri…", "suggestions.header": "Vi povus interesiĝi pri…",
"tabs_bar.federated_timeline": "Fratara", "tabs_bar.federated_timeline": "Fratara",

@ -24,6 +24,7 @@
"account.follows_you": "Te sigue", "account.follows_you": "Te sigue",
"account.hide_reblogs": "Ocultar adhesiones de @{name}", "account.hide_reblogs": "Ocultar adhesiones de @{name}",
"account.joined": "En este servidor desde {date}", "account.joined": "En este servidor desde {date}",
"account.languages": "Cambiar idiomas suscritos",
"account.link_verified_on": "La propiedad de este enlace fue verificada el {date}", "account.link_verified_on": "La propiedad de este enlace fue verificada el {date}",
"account.locked_info": "Esta cuenta es privada. El propietario manualmente revisa quién puede seguirle.", "account.locked_info": "Esta cuenta es privada. El propietario manualmente revisa quién puede seguirle.",
"account.media": "Medios", "account.media": "Medios",
@ -197,22 +198,22 @@
"explore.trending_links": "Noticias", "explore.trending_links": "Noticias",
"explore.trending_statuses": "Mensajes", "explore.trending_statuses": "Mensajes",
"explore.trending_tags": "Etiquetas", "explore.trending_tags": "Etiquetas",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que accediste a este mensaje. Si querés que el mensaje sea filtrado también en este contexto, vas a tener que editar el filtro.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "¡El contexto no coincide!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Esta categoría de filtro caducó; vas a necesitar cambiar la fecha de caducidad para que se aplique.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "¡Filtro caducado!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Para revisar y configurar esta categoría de filtros, visitá a la {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Configuración de filtro",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "página de configuración",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Este mensaje fue agregado a la siguiente categoría de filtros: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "¡Filtro agregado!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "no aplica a este contexto",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "expirado",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nueva categoría: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Buscar o crear",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Usar una categoría existente o crear una nueva",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtrar este mensaje",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtrar un mensaje",
"follow_recommendations.done": "Listo", "follow_recommendations.done": "Listo",
"follow_recommendations.heading": "¡Seguí cuentas cuyos mensajes te gustaría ver! Acá tenés algunas sugerencias.", "follow_recommendations.heading": "¡Seguí cuentas cuyos mensajes te gustaría ver! Acá tenés algunas sugerencias.",
"follow_recommendations.lead": "Los mensajes de las cuentas que seguís aparecerán en orden cronológico en la columna \"Inicio\". No tengás miedo de meter la pata, ¡podés dejar de seguir cuentas fácilmente en cualquier momento!", "follow_recommendations.lead": "Los mensajes de las cuentas que seguís aparecerán en orden cronológico en la columna \"Inicio\". No tengás miedo de meter la pata, ¡podés dejar de seguir cuentas fácilmente en cualquier momento!",
@ -226,8 +227,8 @@
"getting_started.heading": "Introducción", "getting_started.heading": "Introducción",
"getting_started.invite": "Invitar gente", "getting_started.invite": "Invitar gente",
"getting_started.open_source_notice": "Mastodon es software libre. Podés contribuir o informar errores en {github}.", "getting_started.open_source_notice": "Mastodon es software libre. Podés contribuir o informar errores en {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Configuración de la cuenta", "getting_started.security": "Configuración de la cuenta",
"getting_started.terms": "Términos del servicio",
"hashtag.column_header.tag_mode.all": "y {additional}", "hashtag.column_header.tag_mode.all": "y {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "sin {additional}", "hashtag.column_header.tag_mode.none": "sin {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda", "search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
"search_results.statuses": "Mensajes", "search_results.statuses": "Mensajes",
"search_results.statuses_fts_disabled": "No se pueden buscar mensajes por contenido en este servidor de Mastodon.", "search_results.statuses_fts_disabled": "No se pueden buscar mensajes por contenido en este servidor de Mastodon.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Abrir interface de moderación para @{name}", "status.admin_account": "Abrir interface de moderación para @{name}",
"status.admin_status": "Abrir este mensaje en la interface de moderación", "status.admin_status": "Abrir este mensaje en la interface de moderación",
"status.block": "Bloquear a @{name}", "status.block": "Bloquear a @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}", "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
"status.embed": "Insertar", "status.embed": "Insertar",
"status.favourite": "Marcar como favorito", "status.favourite": "Marcar como favorito",
"status.filter": "Filter this post", "status.filter": "Filtrar este mensaje",
"status.filtered": "Filtrado", "status.filtered": "Filtrado",
"status.hide": "Ocultar mensaje", "status.hide": "Ocultar mensaje",
"status.history.created": "Creado por {name} el {date}", "status.history.created": "Creado por {name} el {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Mostrar menos para todo", "status.show_less_all": "Mostrar menos para todo",
"status.show_more": "Mostrar más", "status.show_more": "Mostrar más",
"status.show_more_all": "Mostrar más para todo", "status.show_more_all": "Mostrar más para todo",
"status.show_original": "Mostrar original",
"status.show_thread": "Mostrar hilo", "status.show_thread": "Mostrar hilo",
"status.translate": "Traducir",
"status.translated_from": "Traducido desde el {lang}",
"status.uncached_media_warning": "No disponible", "status.uncached_media_warning": "No disponible",
"status.unmute_conversation": "Dejar de silenciar conversación", "status.unmute_conversation": "Dejar de silenciar conversación",
"status.unpin": "Dejar de fijar", "status.unpin": "Dejar de fijar",
"subscribed_languages.lead": "Después del cambio, sólo los mensajes en los idiomas seleccionados aparecerán en tu línea temporal Principal y en las líneas de tiempo de lista. No seleccionés ningún idioma para poder recibir mensajes en todos los idiomas.",
"subscribed_languages.save": "Guardar cambios",
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
"suggestions.dismiss": "Descartar sugerencia", "suggestions.dismiss": "Descartar sugerencia",
"suggestions.header": "Es posible que te interese…", "suggestions.header": "Es posible que te interese…",
"tabs_bar.federated_timeline": "Federada", "tabs_bar.federated_timeline": "Federada",

@ -24,6 +24,7 @@
"account.follows_you": "Te sigue", "account.follows_you": "Te sigue",
"account.hide_reblogs": "Ocultar retoots de @{name}", "account.hide_reblogs": "Ocultar retoots de @{name}",
"account.joined": "Se unió el {date}", "account.joined": "Se unió el {date}",
"account.languages": "Cambiar idiomas suscritos",
"account.link_verified_on": "El proprietario de este link fue comprobado el {date}", "account.link_verified_on": "El proprietario de este link fue comprobado el {date}",
"account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.", "account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.",
"account.media": "Multimedia", "account.media": "Multimedia",
@ -59,7 +60,7 @@
"alert.unexpected.title": "¡Ups!", "alert.unexpected.title": "¡Ups!",
"announcement.announcement": "Anuncio", "announcement.announcement": "Anuncio",
"attachments_list.unprocessed": "(sin procesar)", "attachments_list.unprocessed": "(sin procesar)",
"audio.hide": "Hide audio", "audio.hide": "Ocultar audio",
"autosuggest_hashtag.per_week": "{count} por semana", "autosuggest_hashtag.per_week": "{count} por semana",
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez", "boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
"bundle_column_error.body": "Algo salió mal al cargar este componente.", "bundle_column_error.body": "Algo salió mal al cargar este componente.",
@ -197,22 +198,22 @@
"explore.trending_links": "Noticias", "explore.trending_links": "Noticias",
"explore.trending_statuses": "Publicaciones", "explore.trending_statuses": "Publicaciones",
"explore.trending_tags": "Hashtags", "explore.trending_tags": "Hashtags",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que ha accedido a esta publlicación. Si quieres que la publicación sea filtrada también en este contexto, tendrás que editar el filtro.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "¡El contexto no coincide!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Esta categoría de filtro ha caducado, necesitará cambiar la fecha de caducidad para que se aplique.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "¡Filtro caducado!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Para revisar y configurar esta categoría de filtros, vaya a {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Ajustes de filtro",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "página de ajustes",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Esta publicación ha sido añadida a la siguiente categoría de filtros: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "¡Filtro añadido!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "no se aplica a este contexto",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "expirado",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nueva categoría: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Buscar o crear",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Usar una categoría existente o crear una nueva",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtrar esta publicación",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtrar una publicación",
"follow_recommendations.done": "Hecho", "follow_recommendations.done": "Hecho",
"follow_recommendations.heading": "¡Sigue a gente que publique cosas que te gusten! Aquí tienes algunas sugerencias.", "follow_recommendations.heading": "¡Sigue a gente que publique cosas que te gusten! Aquí tienes algunas sugerencias.",
"follow_recommendations.lead": "Las publicaciones de la gente a la que sigas aparecerán ordenadas cronológicamente en Inicio. No tengas miedo de cometer errores, ¡puedes dejarles de seguir en cualquier momento con la misma facilidad!", "follow_recommendations.lead": "Las publicaciones de la gente a la que sigas aparecerán ordenadas cronológicamente en Inicio. No tengas miedo de cometer errores, ¡puedes dejarles de seguir en cualquier momento con la misma facilidad!",
@ -226,8 +227,8 @@
"getting_started.heading": "Primeros pasos", "getting_started.heading": "Primeros pasos",
"getting_started.invite": "Invitar usuarios", "getting_started.invite": "Invitar usuarios",
"getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}.", "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Seguridad", "getting_started.security": "Seguridad",
"getting_started.terms": "Términos de servicio",
"hashtag.column_header.tag_mode.all": "y {additional}", "hashtag.column_header.tag_mode.all": "y {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "sin {additional}", "hashtag.column_header.tag_mode.none": "sin {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de busqueda", "search_results.nothing_found": "No se pudo encontrar nada para estos términos de busqueda",
"search_results.statuses": "Toots", "search_results.statuses": "Toots",
"search_results.statuses_fts_disabled": "Buscar toots por su contenido no está disponible en este servidor de Mastodon.", "search_results.statuses_fts_disabled": "Buscar toots por su contenido no está disponible en este servidor de Mastodon.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Abrir interfaz de moderación para @{name}", "status.admin_account": "Abrir interfaz de moderación para @{name}",
"status.admin_status": "Abrir este estado en la interfaz de moderación", "status.admin_status": "Abrir este estado en la interfaz de moderación",
"status.block": "Bloquear a @{name}", "status.block": "Bloquear a @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}", "status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}",
"status.embed": "Incrustado", "status.embed": "Incrustado",
"status.favourite": "Favorito", "status.favourite": "Favorito",
"status.filter": "Filter this post", "status.filter": "Filtrar esta publicación",
"status.filtered": "Filtrado", "status.filtered": "Filtrado",
"status.hide": "Ocultar publicación", "status.hide": "Ocultar publicación",
"status.history.created": "{name} creó {date}", "status.history.created": "{name} creó {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Mostrar menos para todo", "status.show_less_all": "Mostrar menos para todo",
"status.show_more": "Mostrar más", "status.show_more": "Mostrar más",
"status.show_more_all": "Mostrar más para todo", "status.show_more_all": "Mostrar más para todo",
"status.show_original": "Mostrar original",
"status.show_thread": "Mostrar hilo", "status.show_thread": "Mostrar hilo",
"status.translate": "Traducir",
"status.translated_from": "Traducido del {lang}",
"status.uncached_media_warning": "No disponible", "status.uncached_media_warning": "No disponible",
"status.unmute_conversation": "Dejar de silenciar conversación", "status.unmute_conversation": "Dejar de silenciar conversación",
"status.unpin": "Dejar de fijar", "status.unpin": "Dejar de fijar",
"subscribed_languages.lead": "Sólo los mensajes en los idiomas seleccionados aparecerán en su inicio y otras líneas de tiempo después del cambio. Seleccione ninguno para recibir mensajes en todos los idiomas.",
"subscribed_languages.save": "Guardar cambios",
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
"suggestions.dismiss": "Descartar sugerencia", "suggestions.dismiss": "Descartar sugerencia",
"suggestions.header": "Es posible que te interese…", "suggestions.header": "Es posible que te interese…",
"tabs_bar.federated_timeline": "Federado", "tabs_bar.federated_timeline": "Federado",

@ -24,6 +24,7 @@
"account.follows_you": "Te sigue", "account.follows_you": "Te sigue",
"account.hide_reblogs": "Ocultar retoots de @{name}", "account.hide_reblogs": "Ocultar retoots de @{name}",
"account.joined": "Se unió el {date}", "account.joined": "Se unió el {date}",
"account.languages": "Cambiar idiomas suscritos",
"account.link_verified_on": "El proprietario de este link fue comprobado el {date}", "account.link_verified_on": "El proprietario de este link fue comprobado el {date}",
"account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.", "account.locked_info": "El estado de privacidad de esta cuenta està configurado como bloqueado. El proprietario debe revisar manualmente quien puede seguirle.",
"account.media": "Multimedia", "account.media": "Multimedia",
@ -197,22 +198,22 @@
"explore.trending_links": "Noticias", "explore.trending_links": "Noticias",
"explore.trending_statuses": "Publicaciones", "explore.trending_statuses": "Publicaciones",
"explore.trending_tags": "Etiquetas", "explore.trending_tags": "Etiquetas",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que ha accedido a esta publlicación. Si quieres que la publicación sea filtrada también en este contexto, tendrás que editar el filtro.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "¡El contexto no coincide!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Esta categoría de filtro ha caducado, necesitará cambiar la fecha de caducidad para que se aplique.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "¡Filtro caducado!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Para revisar y configurar esta categoría de filtros, vaya a {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Ajustes de filtro",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "página de ajustes",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Esta publicación ha sido añadida a la siguiente categoría de filtros: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "¡Filtro añadido!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "no se aplica a este contexto",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "expirado",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Nueva categoría: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Buscar o crear",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Usar una categoría existente o crear una nueva",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filtrar esta publicación",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filtrar una publicación",
"follow_recommendations.done": "Hecho", "follow_recommendations.done": "Hecho",
"follow_recommendations.heading": "¡Sigue a gente que publique cosas que te gusten! Aquí tienes algunas sugerencias.", "follow_recommendations.heading": "¡Sigue a gente que publique cosas que te gusten! Aquí tienes algunas sugerencias.",
"follow_recommendations.lead": "Las publicaciones de la gente a la que sigas aparecerán ordenadas cronológicamente en Inicio. No tengas miedo de cometer errores, ¡puedes dejarles de seguir en cualquier momento con la misma facilidad!", "follow_recommendations.lead": "Las publicaciones de la gente a la que sigas aparecerán ordenadas cronológicamente en Inicio. No tengas miedo de cometer errores, ¡puedes dejarles de seguir en cualquier momento con la misma facilidad!",
@ -226,8 +227,8 @@
"getting_started.heading": "Primeros pasos", "getting_started.heading": "Primeros pasos",
"getting_started.invite": "Invitar usuarios", "getting_started.invite": "Invitar usuarios",
"getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}.", "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Seguridad", "getting_started.security": "Seguridad",
"getting_started.terms": "Términos de servicio",
"hashtag.column_header.tag_mode.all": "y {additional}", "hashtag.column_header.tag_mode.all": "y {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_header.tag_mode.none": "sin {additional}", "hashtag.column_header.tag_mode.none": "sin {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda", "search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
"search_results.statuses": "Publicaciones", "search_results.statuses": "Publicaciones",
"search_results.statuses_fts_disabled": "Buscar publicaciones por su contenido no está disponible en este servidor de Mastodon.", "search_results.statuses_fts_disabled": "Buscar publicaciones por su contenido no está disponible en este servidor de Mastodon.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Abrir interfaz de moderación para @{name}", "status.admin_account": "Abrir interfaz de moderación para @{name}",
"status.admin_status": "Abrir este estado en la interfaz de moderación", "status.admin_status": "Abrir este estado en la interfaz de moderación",
"status.block": "Bloquear a @{name}", "status.block": "Bloquear a @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}", "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
"status.embed": "Incrustado", "status.embed": "Incrustado",
"status.favourite": "Favorito", "status.favourite": "Favorito",
"status.filter": "Filter this post", "status.filter": "Filtrar esta publicación",
"status.filtered": "Filtrado", "status.filtered": "Filtrado",
"status.hide": "Ocultar publicación", "status.hide": "Ocultar publicación",
"status.history.created": "{name} creó {date}", "status.history.created": "{name} creó {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Mostrar menos para todo", "status.show_less_all": "Mostrar menos para todo",
"status.show_more": "Mostrar más", "status.show_more": "Mostrar más",
"status.show_more_all": "Mostrar más para todo", "status.show_more_all": "Mostrar más para todo",
"status.show_original": "Mostrar original",
"status.show_thread": "Mostrar hilo", "status.show_thread": "Mostrar hilo",
"status.translate": "Traducir",
"status.translated_from": "Traducido del {lang}",
"status.uncached_media_warning": "No disponible", "status.uncached_media_warning": "No disponible",
"status.unmute_conversation": "Dejar de silenciar conversación", "status.unmute_conversation": "Dejar de silenciar conversación",
"status.unpin": "Dejar de fijar", "status.unpin": "Dejar de fijar",
"subscribed_languages.lead": "Sólo los mensajes en los idiomas seleccionados aparecerán en su inicio y otras líneas de tiempo después del cambio. Seleccione ninguno para recibir mensajes en todos los idiomas.",
"subscribed_languages.save": "Guardar cambios",
"subscribed_languages.target": "Cambiar idiomas suscritos para {target}",
"suggestions.dismiss": "Descartar sugerencia", "suggestions.dismiss": "Descartar sugerencia",
"suggestions.header": "Es posible que te interese…", "suggestions.header": "Es posible que te interese…",
"tabs_bar.federated_timeline": "Federada", "tabs_bar.federated_timeline": "Federada",

@ -24,6 +24,7 @@
"account.follows_you": "Jälgib Teid", "account.follows_you": "Jälgib Teid",
"account.hide_reblogs": "Peida upitused kasutajalt @{name}", "account.hide_reblogs": "Peida upitused kasutajalt @{name}",
"account.joined": "Liitus {date}", "account.joined": "Liitus {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Selle lingi autorsust kontrolliti {date}", "account.link_verified_on": "Selle lingi autorsust kontrolliti {date}",
"account.locked_info": "Selle konto privaatsussätteks on lukustatud. Omanik vaatab manuaalselt üle, kes teda jägida saab.", "account.locked_info": "Selle konto privaatsussätteks on lukustatud. Omanik vaatab manuaalselt üle, kes teda jägida saab.",
"account.media": "Meedia", "account.media": "Meedia",
@ -226,8 +227,8 @@
"getting_started.heading": "Alustamine", "getting_started.heading": "Alustamine",
"getting_started.invite": "Kutsu inimesi", "getting_started.invite": "Kutsu inimesi",
"getting_started.open_source_notice": "Mastodon on avatud lähtekoodiga tarkvara. Saate panustada või teatada probleemidest GitHubis {github}.", "getting_started.open_source_notice": "Mastodon on avatud lähtekoodiga tarkvara. Saate panustada või teatada probleemidest GitHubis {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Turvalisus", "getting_started.security": "Turvalisus",
"getting_started.terms": "Kasutustingimused",
"hashtag.column_header.tag_mode.all": "ja {additional}", "hashtag.column_header.tag_mode.all": "ja {additional}",
"hashtag.column_header.tag_mode.any": "või {additional}", "hashtag.column_header.tag_mode.any": "või {additional}",
"hashtag.column_header.tag_mode.none": "ilma {additional}", "hashtag.column_header.tag_mode.none": "ilma {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Could not find anything for these search terms", "search_results.nothing_found": "Could not find anything for these search terms",
"search_results.statuses": "Tuudid", "search_results.statuses": "Tuudid",
"search_results.statuses_fts_disabled": "Tuutsude otsimine nende sisu järgi ei ole sellel Mastodoni serveril sisse lülitatud.", "search_results.statuses_fts_disabled": "Tuutsude otsimine nende sisu järgi ei ole sellel Mastodoni serveril sisse lülitatud.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {tulemus} other {tulemust}}", "search_results.total": "{count, number} {count, plural, one {tulemus} other {tulemust}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Ava moderaatoriliides kasutajale @{name}", "status.admin_account": "Ava moderaatoriliides kasutajale @{name}",
"status.admin_status": "Ava see staatus moderaatoriliites", "status.admin_status": "Ava see staatus moderaatoriliites",
"status.block": "Blokeeri @{name}", "status.block": "Blokeeri @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Näita vähem kõigile", "status.show_less_all": "Näita vähem kõigile",
"status.show_more": "Näita veel", "status.show_more": "Näita veel",
"status.show_more_all": "Näita enam kõigile", "status.show_more_all": "Näita enam kõigile",
"status.show_original": "Show original",
"status.show_thread": "Kuva lõim", "status.show_thread": "Kuva lõim",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Pole saadaval", "status.uncached_media_warning": "Pole saadaval",
"status.unmute_conversation": "Ära vaigista vestlust", "status.unmute_conversation": "Ära vaigista vestlust",
"status.unpin": "Kinnita profiililt lahti", "status.unpin": "Kinnita profiililt lahti",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Eira soovitust", "suggestions.dismiss": "Eira soovitust",
"suggestions.header": "Teid võib huvitada…", "suggestions.header": "Teid võib huvitada…",
"tabs_bar.federated_timeline": "Föderatiivne", "tabs_bar.federated_timeline": "Föderatiivne",

@ -24,6 +24,7 @@
"account.follows_you": "Jarraitzen dizu", "account.follows_you": "Jarraitzen dizu",
"account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak", "account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak",
"account.joined": "{date}(e)an elkartua", "account.joined": "{date}(e)an elkartua",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Esteka honen jabetzaren egiaztaketa data: {date}", "account.link_verified_on": "Esteka honen jabetzaren egiaztaketa data: {date}",
"account.locked_info": "Kontu honen pribatutasun egoera blokeatuta gisa ezarri da. Jabeak eskuz erabakitzen du nork jarraitu diezaioken.", "account.locked_info": "Kontu honen pribatutasun egoera blokeatuta gisa ezarri da. Jabeak eskuz erabakitzen du nork jarraitu diezaioken.",
"account.media": "Multimedia", "account.media": "Multimedia",
@ -226,8 +227,8 @@
"getting_started.heading": "Menua", "getting_started.heading": "Menua",
"getting_started.invite": "Gonbidatu jendea", "getting_started.invite": "Gonbidatu jendea",
"getting_started.open_source_notice": "Mastodon software librea da. Ekarpenak egin ditzakezu edo akatsen berri eman GitHub bidez: {github}.", "getting_started.open_source_notice": "Mastodon software librea da. Ekarpenak egin ditzakezu edo akatsen berri eman GitHub bidez: {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Segurtasuna", "getting_started.security": "Segurtasuna",
"getting_started.terms": "Erabilera baldintzak",
"hashtag.column_header.tag_mode.all": "eta {osagarria}", "hashtag.column_header.tag_mode.all": "eta {osagarria}",
"hashtag.column_header.tag_mode.any": "edo {osagarria}", "hashtag.column_header.tag_mode.any": "edo {osagarria}",
"hashtag.column_header.tag_mode.none": "gabe {osagarria}", "hashtag.column_header.tag_mode.none": "gabe {osagarria}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Ez da emaitzarik aurkitu bilaketa-termino horientzat", "search_results.nothing_found": "Ez da emaitzarik aurkitu bilaketa-termino horientzat",
"search_results.statuses": "Bidalketak", "search_results.statuses": "Bidalketak",
"search_results.statuses_fts_disabled": "Mastodon zerbitzari honek ez du bidalketen edukiaren bilaketa gaitu.", "search_results.statuses_fts_disabled": "Mastodon zerbitzari honek ez du bidalketen edukiaren bilaketa gaitu.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {emaitza} other {emaitza}}", "search_results.total": "{count, number} {count, plural, one {emaitza} other {emaitza}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea", "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea",
"status.admin_status": "Ireki bidalketa hau moderazio interfazean", "status.admin_status": "Ireki bidalketa hau moderazio interfazean",
"status.block": "Blokeatu @{name}", "status.block": "Blokeatu @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "Erakutsi denetarik gutxiago", "status.show_less_all": "Erakutsi denetarik gutxiago",
"status.show_more": "Erakutsi gehiago", "status.show_more": "Erakutsi gehiago",
"status.show_more_all": "Erakutsi denetarik gehiago", "status.show_more_all": "Erakutsi denetarik gehiago",
"status.show_original": "Show original",
"status.show_thread": "Erakutsi haria", "status.show_thread": "Erakutsi haria",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Ez eskuragarri", "status.uncached_media_warning": "Ez eskuragarri",
"status.unmute_conversation": "Desmututu elkarrizketa", "status.unmute_conversation": "Desmututu elkarrizketa",
"status.unpin": "Desfinkatu profiletik", "status.unpin": "Desfinkatu profiletik",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Errefusatu proposamena", "suggestions.dismiss": "Errefusatu proposamena",
"suggestions.header": "Hau interesatu dakizuke…", "suggestions.header": "Hau interesatu dakizuke…",
"tabs_bar.federated_timeline": "Federatua", "tabs_bar.federated_timeline": "Federatua",

@ -24,6 +24,7 @@
"account.follows_you": "پی می‌گیردتان", "account.follows_you": "پی می‌گیردتان",
"account.hide_reblogs": "نهفتن تقویت‌های @{name}", "account.hide_reblogs": "نهفتن تقویت‌های @{name}",
"account.joined": "پیوسته از {date}", "account.joined": "پیوسته از {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد", "account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد",
"account.locked_info": "این حساب خصوصی است. صاحبش تصمیم می‌گیرد که چه کسی پی‌گیرش باشد.", "account.locked_info": "این حساب خصوصی است. صاحبش تصمیم می‌گیرد که چه کسی پی‌گیرش باشد.",
"account.media": "رسانه", "account.media": "رسانه",
@ -226,8 +227,8 @@
"getting_started.heading": "آغاز کنید", "getting_started.heading": "آغاز کنید",
"getting_started.invite": "دعوت از دیگران", "getting_started.invite": "دعوت از دیگران",
"getting_started.open_source_notice": "ماستودون نرم‌افزاری آزاد است. می‌توانید روی {github} در آن مشارکت کرده یا مشکلاتش را گزارش دهید.", "getting_started.open_source_notice": "ماستودون نرم‌افزاری آزاد است. می‌توانید روی {github} در آن مشارکت کرده یا مشکلاتش را گزارش دهید.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "تنظیمات حساب", "getting_started.security": "تنظیمات حساب",
"getting_started.terms": "شرایط خدمات",
"hashtag.column_header.tag_mode.all": "و {additional}", "hashtag.column_header.tag_mode.all": "و {additional}",
"hashtag.column_header.tag_mode.any": "یا {additional}", "hashtag.column_header.tag_mode.any": "یا {additional}",
"hashtag.column_header.tag_mode.none": "بدون {additional}", "hashtag.column_header.tag_mode.none": "بدون {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "چیزی برای این عبارت جست‌وجو یافت نشد", "search_results.nothing_found": "چیزی برای این عبارت جست‌وجو یافت نشد",
"search_results.statuses": "فرسته‌ها", "search_results.statuses": "فرسته‌ها",
"search_results.statuses_fts_disabled": "جست‌وجوی محتوای فرسته‌ها در این کارساز ماستودون به کار انداخته نشده است.", "search_results.statuses_fts_disabled": "جست‌وجوی محتوای فرسته‌ها در این کارساز ماستودون به کار انداخته نشده است.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}", "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "گشودن واسط مدیریت برای @{name}", "status.admin_account": "گشودن واسط مدیریت برای @{name}",
"status.admin_status": "گشودن این فرسته در واسط مدیریت", "status.admin_status": "گشودن این فرسته در واسط مدیریت",
"status.block": "مسدود کردن @{name}", "status.block": "مسدود کردن @{name}",
@ -518,10 +523,16 @@
"status.show_less_all": "نمایش کمتر همه", "status.show_less_all": "نمایش کمتر همه",
"status.show_more": "نمایش بیشتر", "status.show_more": "نمایش بیشتر",
"status.show_more_all": "نمایش بیشتر همه", "status.show_more_all": "نمایش بیشتر همه",
"status.show_original": "Show original",
"status.show_thread": "نمایش رشته", "status.show_thread": "نمایش رشته",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "ناموجود", "status.uncached_media_warning": "ناموجود",
"status.unmute_conversation": "رفع خموشی گفت‌وگو", "status.unmute_conversation": "رفع خموشی گفت‌وگو",
"status.unpin": "برداشتن سنجاق از نمایه", "status.unpin": "برداشتن سنجاق از نمایه",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "نادیده گرفتن پیشنهاد", "suggestions.dismiss": "نادیده گرفتن پیشنهاد",
"suggestions.header": "شاید این هم برایتان جالب باشد…", "suggestions.header": "شاید این هم برایتان جالب باشد…",
"tabs_bar.federated_timeline": "همگانی", "tabs_bar.federated_timeline": "همگانی",

@ -24,6 +24,7 @@
"account.follows_you": "Seuraa sinua", "account.follows_you": "Seuraa sinua",
"account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}", "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}",
"account.joined": "Liittynyt {date}", "account.joined": "Liittynyt {date}",
"account.languages": "Change subscribed languages",
"account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}", "account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}",
"account.locked_info": "Tämän tilin yksityisyyden tila on asetettu lukituksi. Omistaja arvioi manuaalisesti, kuka voi seurata niitä.", "account.locked_info": "Tämän tilin yksityisyyden tila on asetettu lukituksi. Omistaja arvioi manuaalisesti, kuka voi seurata niitä.",
"account.media": "Media", "account.media": "Media",
@ -197,22 +198,22 @@
"explore.trending_links": "Uutiset", "explore.trending_links": "Uutiset",
"explore.trending_statuses": "Viestit", "explore.trending_statuses": "Viestit",
"explore.trending_tags": "Aihetunnisteet", "explore.trending_tags": "Aihetunnisteet",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "Tämä suodatinluokka ei koske asiayhteyttä, jossa olet käyttänyt tätä viestiä. Jos haluat, että viesti suodatetaan myös tässä yhteydessä, sinun on muokattava suodatinta.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Asiayhteys ei täsmää!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "Tämä suodatinluokka on vanhentunut ja sinun on muutettava viimeistä voimassaolon päivää, jotta sitä voidaan käyttää.",
"filter_modal.added.expired_title": "Expired filter!", "filter_modal.added.expired_title": "Vanhentunut suodatin!",
"filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.", "filter_modal.added.review_and_configure": "Voit tarkastella tätä suodatinluokkaa ja määrittää sen tarkemmin siirtymällä {settings_link}.",
"filter_modal.added.review_and_configure_title": "Filter settings", "filter_modal.added.review_and_configure_title": "Suodattimen asetukset",
"filter_modal.added.settings_link": "settings page", "filter_modal.added.settings_link": "asetukset sivu",
"filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.", "filter_modal.added.short_explanation": "Tämä viesti on lisätty seuraavaan suodatinluokkaan: {title}.",
"filter_modal.added.title": "Filter added!", "filter_modal.added.title": "Suodatin lisätty!",
"filter_modal.select_filter.context_mismatch": "does not apply to this context", "filter_modal.select_filter.context_mismatch": "ei sovellu tähän asiayhteyteen",
"filter_modal.select_filter.expired": "expired", "filter_modal.select_filter.expired": "vanhentunut",
"filter_modal.select_filter.prompt_new": "New category: {name}", "filter_modal.select_filter.prompt_new": "Uusi luokka: {name}",
"filter_modal.select_filter.search": "Search or create", "filter_modal.select_filter.search": "Etsi tai luo",
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Käytä olemassa olevaa luokkaa tai luo uusi luokka",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Suodata tämä viesti",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Suodata viesti",
"follow_recommendations.done": "Valmis", "follow_recommendations.done": "Valmis",
"follow_recommendations.heading": "Seuraa ihmisiä, joilta haluaisit nähdä julkaisuja! Tässä on muutamia ehdotuksia.", "follow_recommendations.heading": "Seuraa ihmisiä, joilta haluaisit nähdä julkaisuja! Tässä on muutamia ehdotuksia.",
"follow_recommendations.lead": "Seuraamiesi julkaisut näkyvät aikajärjestyksessä kotisyötteessä. Älä pelkää seurata vahingossa, voit lopettaa seuraamisen yhtä helposti!", "follow_recommendations.lead": "Seuraamiesi julkaisut näkyvät aikajärjestyksessä kotisyötteessä. Älä pelkää seurata vahingossa, voit lopettaa seuraamisen yhtä helposti!",
@ -226,8 +227,8 @@
"getting_started.heading": "Näin pääset alkuun", "getting_started.heading": "Näin pääset alkuun",
"getting_started.invite": "Kutsu ihmisiä", "getting_started.invite": "Kutsu ihmisiä",
"getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.", "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.",
"getting_started.privacy_policy": "Privacy Policy",
"getting_started.security": "Tiliasetukset", "getting_started.security": "Tiliasetukset",
"getting_started.terms": "Käyttöehdot",
"hashtag.column_header.tag_mode.all": "ja {additional}", "hashtag.column_header.tag_mode.all": "ja {additional}",
"hashtag.column_header.tag_mode.any": "tai {additional}", "hashtag.column_header.tag_mode.any": "tai {additional}",
"hashtag.column_header.tag_mode.none": "ilman {additional}", "hashtag.column_header.tag_mode.none": "ilman {additional}",
@ -471,7 +472,11 @@
"search_results.nothing_found": "Näille hakusanoille ei löytynyt mitään", "search_results.nothing_found": "Näille hakusanoille ei löytynyt mitään",
"search_results.statuses": "Viestit", "search_results.statuses": "Viestit",
"search_results.statuses_fts_disabled": "Viestien haku sisällön perusteella ei ole käytössä tällä Mastodon-palvelimella.", "search_results.statuses_fts_disabled": "Viestien haku sisällön perusteella ei ole käytössä tällä Mastodon-palvelimella.",
"search_results.title": "Search for {q}",
"search_results.total": "{count, number} {count, plural, one {tulos} other {tulokset}}", "search_results.total": "{count, number} {count, plural, one {tulos} other {tulokset}}",
"sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Sign in",
"sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
"status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}", "status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}",
"status.admin_status": "Avaa julkaisu moderointinäkymässä", "status.admin_status": "Avaa julkaisu moderointinäkymässä",
"status.block": "Estä @{name}", "status.block": "Estä @{name}",
@ -487,7 +492,7 @@
"status.edited_x_times": "Muokattu {count, plural, one {{count} aika} other {{count} kertaa}}", "status.edited_x_times": "Muokattu {count, plural, one {{count} aika} other {{count} kertaa}}",
"status.embed": "Upota", "status.embed": "Upota",
"status.favourite": "Tykkää", "status.favourite": "Tykkää",
"status.filter": "Filter this post", "status.filter": "Suodata tämä viesti",
"status.filtered": "Suodatettu", "status.filtered": "Suodatettu",
"status.hide": "Piilota toot", "status.hide": "Piilota toot",
"status.history.created": "{name} luotu {date}", "status.history.created": "{name} luotu {date}",
@ -518,10 +523,16 @@
"status.show_less_all": "Näytä vähemmän kaikista", "status.show_less_all": "Näytä vähemmän kaikista",
"status.show_more": "Näytä lisää", "status.show_more": "Näytä lisää",
"status.show_more_all": "Näytä lisää kaikista", "status.show_more_all": "Näytä lisää kaikista",
"status.show_original": "Show original",
"status.show_thread": "Näytä ketju", "status.show_thread": "Näytä ketju",
"status.translate": "Translate",
"status.translated_from": "Translated from {lang}",
"status.uncached_media_warning": "Ei saatavilla", "status.uncached_media_warning": "Ei saatavilla",
"status.unmute_conversation": "Poista keskustelun mykistys", "status.unmute_conversation": "Poista keskustelun mykistys",
"status.unpin": "Irrota profiilista", "status.unpin": "Irrota profiilista",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"suggestions.dismiss": "Hylkää ehdotus", "suggestions.dismiss": "Hylkää ehdotus",
"suggestions.header": "Saatat olla kiinnostunut myös…", "suggestions.header": "Saatat olla kiinnostunut myös…",
"tabs_bar.federated_timeline": "Yleinen", "tabs_bar.federated_timeline": "Yleinen",

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

Loading…
Cancel
Save