diff --git a/.circleci/config.yml b/.circleci/config.yml index 2355d9d7d81baeff71ae65ca83c80e569b1f7e90..ff8eb485986f0a9bba3b091eb1c6ca895b3e5249 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 aliases: - &defaults docker: - - image: circleci/ruby:2.6.0-stretch-node + - image: circleci/ruby:2.6-stretch-node environment: &ruby_environment BUNDLE_APP_CONFIG: ./.bundle/ DB_HOST: localhost @@ -105,14 +105,14 @@ jobs: install-ruby2.5: <<: *defaults docker: - - image: circleci/ruby:2.5.3-stretch-node + - image: circleci/ruby:2.5-stretch-node environment: *ruby_environment <<: *install_ruby_dependencies install-ruby2.4: <<: *defaults docker: - - image: circleci/ruby:2.4.5-stretch-node + - image: circleci/ruby:2.4-stretch-node environment: *ruby_environment <<: *install_ruby_dependencies @@ -131,40 +131,40 @@ jobs: test-ruby2.6: <<: *defaults docker: - - image: circleci/ruby:2.6.0-stretch-node + - image: circleci/ruby:2.6-stretch-node environment: *ruby_environment - image: circleci/postgres:10.6-alpine environment: POSTGRES_USER: root - - image: circleci/redis:5.0.3-alpine3.8 + - image: circleci/redis:5-alpine <<: *test_steps test-ruby2.5: <<: *defaults docker: - - image: circleci/ruby:2.5.3-stretch-node + - image: circleci/ruby:2.5-stretch-node environment: *ruby_environment - image: circleci/postgres:10.6-alpine environment: POSTGRES_USER: root - - image: circleci/redis:4.0.12-alpine + - image: circleci/redis:5-alpine <<: *test_steps test-ruby2.4: <<: *defaults docker: - - image: circleci/ruby:2.4.5-stretch-node + - image: circleci/ruby:2.4-stretch-node environment: *ruby_environment - image: circleci/postgres:10.6-alpine environment: POSTGRES_USER: root - - image: circleci/redis:4.0.12-alpine + - image: circleci/redis:5-alpine <<: *test_steps test-webui: <<: *defaults docker: - - image: circleci/node:8.15.0-stretch + - image: circleci/node:12.9-stretch steps: - *attach_workspace - run: ./bin/retry yarn test:jest @@ -173,9 +173,11 @@ jobs: <<: *defaults steps: - *attach_workspace + - *install_system_dependencies - run: bundle exec i18n-tasks check-normalized - run: bundle exec i18n-tasks unused -l en - run: bundle exec i18n-tasks check-consistent-interpolations + - run: bundle exec rake repo:check_locales_files workflows: version: 2 diff --git a/.env.nanobox b/.env.nanobox index b60b6ee689729902d408338e32b20224ad933a0b..cfbe487fba769387b622a860a177b07662045a82 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -11,24 +11,14 @@ DB_NAME=gonano DB_PASS=$DATA_DB_PASS DB_PORT=5432 -DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano +# DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano # Optional ElasticSearch configuration ES_ENABLED=true ES_HOST=$DATA_ELASTIC_HOST ES_PORT=9200 -# Optimizations -LD_PRELOAD=/data/lib/libjemalloc.so - -# ImageMagick optimizations -MAGICK_TEMPORARY_PATH=/app/tmp -MAGICK_MEMORY_LIMIT=128MiB -MAGICK_MAP_LIMIT=64MiB -MAGICK_TIME_LIMIT=15 -MAGICK_AREA_LIMIT=16MP -MAGICK_WIDTH_LIMIT=8KP -MAGICK_HEIGHT_LIMIT=8KP +BIND=0.0.0.0 # Federation # Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. @@ -84,6 +74,7 @@ SMTP_PORT=587 SMTP_LOGIN=$SMTP_LOGIN SMTP_PASSWORD=$SMTP_PASSWORD SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io +#SMTP_REPLY_TO= #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail #SMTP_AUTH_METHOD=plain @@ -97,9 +88,17 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # PAPERCLIP_ROOT_URL=/system # Optional asset host for multi-server setups +# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN +# if WEB_DOMAIN is not set. For example, the server may have the +# following header field: +# Access-Control-Allow-Origin: https://example.com/ # CDN_HOST=https://assets.example.com # S3 (optional) +# The attachment host must allow cross origin request from WEB_DOMAIN or +# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the +# following header field: +# Access-Control-Allow-Origin: https://192.168.1.123:9000/ # S3_ENABLED=true # S3_BUCKET= # AWS_ACCESS_KEY_ID= @@ -109,6 +108,8 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # S3_HOSTNAME=192.168.1.123:9000 # S3 (Minio Config (optional) Please check Minio instance for details) +# The attachment host must allow cross origin request - see the description +# above. # S3_ENABLED=true # S3_BUCKET= # AWS_ACCESS_KEY_ID= @@ -119,12 +120,30 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # S3_ENDPOINT= # S3_SIGNATURE_VERSION= +# Google Cloud Storage (optional) +# Use S3 compatible API. Since GCS does not support Multipart Upload, +# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload. +# The attachment host must allow cross origin request - see the description +# above. +# S3_ENABLED=true +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= +# S3_REGION= +# S3_PROTOCOL=https +# S3_HOSTNAME=storage.googleapis.com +# S3_ENDPOINT=https://storage.googleapis.com +# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes + # Swift (optional) +# The attachment host must allow cross origin request - see the description +# above. # SWIFT_ENABLED=true # SWIFT_USERNAME= # For Keystone V3, the value for SWIFT_TENANT should be the project name # SWIFT_TENANT= # SWIFT_PASSWORD= +# Some OpenStack V3 providers require PROJECT_ID (optional) +# SWIFT_PROJECT_ID= # Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid # issues with token rate-limiting during high load. # SWIFT_AUTH_URL= @@ -171,8 +190,8 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # The pam environment variable "email" is provided by: # https://github.com/devkral/pam_email_extractor # PAM_ENABLED=true -# Fallback Suffix for email address generation (nil by default) -# PAM_DEFAULT_SUFFIX=pam +# Fallback email domain for email address generation (LOCAL_DOMAIN by default) +# PAM_EMAIL_DOMAIN=example.com # Name of the pam service (pam "auth" section is evaluated) # PAM_DEFAULT_SERVICE=rpam # Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default) @@ -220,7 +239,14 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true # SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" -# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42" +# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241" +# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42" +# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4" # SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_VERIFIED= # SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= + +# Use HTTP proxy for outgoing request (optional) +# http_proxy=http://gateway.local:8118 +# Access control for hidden service. +# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true diff --git a/.env.production.sample b/.env.production.sample index d66b0505095821de1beb2f4a2e9a6c05b3454121..f9a8bb7c1b2ae5be0ed334e4d565395b9b7fb613 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -69,6 +69,7 @@ SMTP_PORT=587 SMTP_LOGIN= SMTP_PASSWORD= SMTP_FROM_ADDRESS=notifications@example.com +#SMTP_REPLY_TO= #SMTP_DOMAIN= # defaults to LOCAL_DOMAIN #SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail #SMTP_AUTH_METHOD=plain @@ -114,6 +115,20 @@ SMTP_FROM_ADDRESS=notifications@example.com # S3_ENDPOINT= # S3_SIGNATURE_VERSION= +# Google Cloud Storage (optional) +# Use S3 compatible API. Since GCS does not support Multipart Upload, +# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload. +# The attachment host must allow cross origin request - see the description +# above. +# S3_ENABLED=true +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= +# S3_REGION= +# S3_PROTOCOL=https +# S3_HOSTNAME=storage.googleapis.com +# S3_ENDPOINT=https://storage.googleapis.com +# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes + # Swift (optional) # The attachment host must allow cross origin request - see the description # above. @@ -163,7 +178,7 @@ STREAMING_CLUSTER_NUM=1 # LDAP_BIND_DN= # LDAP_PASSWORD= # LDAP_UID=cn -# LDAP_SEARCH_FILTER="%{uid}=%{email}" +# LDAP_SEARCH_FILTER=%{uid}=%{email} # PAM authentication (optional) # PAM authentication uses for the email generation the "email" pam variable diff --git a/.ruby-version b/.ruby-version index 6a6a3d8e35c7a98eaeb47d4212cc13d1d8938003..57cf282ebbc41ec4cd51601733bc26d60c2341d4 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.1 +2.6.5 diff --git a/AUTHORS.md b/AUTHORS.md index 8d3aaf480d5a25dddbe368e9f75b6c298bd97672..5f5985fba8328cd65216ff02ea9ffc5c11d462f7 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -5,89 +5,100 @@ Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon) and provided thanks to the work of the following contributors: * [Gargron](https://github.com/Gargron) -* [ykzts](https://github.com/ykzts) * [ThibG](https://github.com/ThibG) +* [ykzts](https://github.com/ykzts) +* [dependabot[bot]](https://github.com/apps/dependabot) * [akihikodaki](https://github.com/akihikodaki) +* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) * [mjankowski](https://github.com/mjankowski) -* [dependabot[bot]](https://github.com/apps/dependabot) * [unarist](https://github.com/unarist) -* [m4sk1n](https://github.com/m4sk1n) * [yiskah](https://github.com/yiskah) * [nolanlawson](https://github.com/nolanlawson) * [ysksn](https://github.com/ysksn) -* [sorin-davidoi](https://github.com/sorin-davidoi) * [abcang](https://github.com/abcang) +* [sorin-davidoi](https://github.com/sorin-davidoi) * [lynlynlynx](https://github.com/lynlynlynx) * [mayaeh](https://github.com/mayaeh) +* [m4sk1n](mailto:me@m4sk.in) +* [Marcin MikoÅ‚ajczak](mailto:me@m4sk.in) +* [Kjwon15](https://github.com/Kjwon15) * [renatolond](https://github.com/renatolond) * [alpaca-tc](https://github.com/alpaca-tc) +* [jeroenpraat](https://github.com/jeroenpraat) * [nclm](https://github.com/nclm) * [ineffyble](https://github.com/ineffyble) -* [jeroenpraat](https://github.com/jeroenpraat) +* [mabkenar](https://github.com/mabkenar) * [blackle](https://github.com/blackle) * [Quent-in](https://github.com/Quent-in) * [JantsoP](https://github.com/JantsoP) -* [Kjwon15](https://github.com/Kjwon15) -* [mabkenar](https://github.com/mabkenar) +* [zunda](https://github.com/zunda) * [nullkal](https://github.com/nullkal) * [yookoala](https://github.com/yookoala) +* [Aditoo17](https://github.com/Aditoo17) +* [Quenty31](https://github.com/Quenty31) +* [marek-lach](https://github.com/marek-lach) * [shuheiktgw](https://github.com/shuheiktgw) * [ashfurrow](https://github.com/ashfurrow) -* [zunda](https://github.com/zunda) -* [Quenty31](https://github.com/Quenty31) * [eramdam](https://github.com/eramdam) +* [noellabo](https://github.com/noellabo) * [takayamaki](https://github.com/takayamaki) +* [danhunsaker](https://github.com/danhunsaker) * [masarakki](https://github.com/masarakki) * [ticky](https://github.com/ticky) -* [danhunsaker](https://github.com/danhunsaker) * [ThisIsMissEm](https://github.com/ThisIsMissEm) * [hcmiya](https://github.com/hcmiya) * [stephenburgess8](https://github.com/stephenburgess8) * [Wonderfall](https://github.com/Wonderfall) * [matteoaquila](https://github.com/matteoaquila) * [yukimochi](https://github.com/yukimochi) +* [palindromordnilap](https://github.com/palindromordnilap) * [rkarabut](https://github.com/rkarabut) * [Artoria2e5](https://github.com/Artoria2e5) * [nightpool](https://github.com/nightpool) * [marrus-sh](https://github.com/marrus-sh) +* [hinaloe](https://github.com/hinaloe) * [krainboltgreene](https://github.com/krainboltgreene) * [pfigel](https://github.com/pfigel) * [Aldarone](https://github.com/Aldarone) * [BoFFire](https://github.com/BoFFire) * [clworld](https://github.com/clworld) +* [MasterGroosha](https://github.com/MasterGroosha) * [dracos](https://github.com/dracos) +* [MaciekBaron](https://github.com/MaciekBaron) * [SerCom_KC](mailto:sercom-kc@users.noreply.github.com) * [Sylvhem](https://github.com/Sylvhem) -* [MasterGroosha](https://github.com/MasterGroosha) +* [MitarashiDango](https://github.com/MitarashiDango) * [JeanGauthier](https://github.com/JeanGauthier) * [kschaper](https://github.com/kschaper) -* [MaciekBaron](https://github.com/MaciekBaron) -* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com) * [beatrix-bitrot](https://github.com/beatrix-bitrot) -* [Aditoo17](https://github.com/Aditoo17) +* [angristan](https://github.com/angristan) * [adbelle](https://github.com/adbelle) * [evanminto](https://github.com/evanminto) * [MightyPork](https://github.com/MightyPork) +* [ashleyhull-versent](mailto:ashley.hull@versent.com.au) * [yhirano55](https://github.com/yhirano55) * [rinsuki](https://github.com/rinsuki) * [camponez](https://github.com/camponez) -* [hinaloe](https://github.com/hinaloe) -* [SerCom-KC](https://github.com/SerCom-KC) +* [SerCom_KC](mailto:szescxz@gmail.com) * [aschmitz](https://github.com/aschmitz) +* [trwnh](https://github.com/trwnh) * [devkral](https://github.com/devkral) * [fpiesche](https://github.com/fpiesche) +* [hugogameiro](https://github.com/hugogameiro) * [gandaro](https://github.com/gandaro) * [johnsudaar](https://github.com/johnsudaar) +* [ariasuni](https://github.com/ariasuni) * [trebmuh](https://github.com/trebmuh) -* [Rakib Hasan](mailto:rmhasan@gmail.com) -* [ashleyhull-versent](https://github.com/ashleyhull-versent) +* [rmhasan](https://github.com/rmhasan) +* [kedamaDQ](https://github.com/kedamaDQ) * [lindwurm](https://github.com/lindwurm) * [victorhck](mailto:victorhck@geeko.site) * [voidsatisfaction](https://github.com/voidsatisfaction) +* [BenLubar](https://github.com/BenLubar) * [hikari-no-yume](https://github.com/hikari-no-yume) -* [angristan](https://github.com/angristan) * [seefood](https://github.com/seefood) * [jackjennings](https://github.com/jackjennings) +* [koyuawsmbrtn](https://github.com/koyuawsmbrtn) * [spla](mailto:spla@mastodont.cat) * [expenses](https://github.com/expenses) * [walf443](https://github.com/walf443) @@ -95,18 +106,17 @@ and provided thanks to the work of the following contributors: * [mistydemeo](https://github.com/mistydemeo) * [dunn](https://github.com/dunn) * [xqus](https://github.com/xqus) -* [hugogameiro](https://github.com/hugogameiro) -* [ariasuni](https://github.com/ariasuni) * [pfm-eyesightjp](https://github.com/pfm-eyesightjp) * [fakenine](https://github.com/fakenine) +* [Shleeble](https://github.com/Shleeble) * [tsuwatch](https://github.com/tsuwatch) * [victorhck](https://github.com/victorhck) -* [kedamaDQ](https://github.com/kedamaDQ) +* [mkljczk](https://github.com/mkljczk) +* [manuelviens](https://github.com/manuelviens) * [puckipedia](https://github.com/puckipedia) -* [trwnh](https://github.com/trwnh) * [fvh-P](https://github.com/fvh-P) +* [rtucker](https://github.com/rtucker) * [Anna e só](mailto:contraexemplos@gmail.com) -* [BenLubar](https://github.com/BenLubar) * [kazu9su](https://github.com/kazu9su) * [Komic](https://github.com/Komic) * [lmorchard](https://github.com/lmorchard) @@ -119,6 +129,7 @@ and provided thanks to the work of the following contributors: * [goofy-bz](mailto:goofy@babelzilla.org) * [kadiix](https://github.com/kadiix) * [kodacs](https://github.com/kodacs) +* [marcin mikoÅ‚ajczak](mailto:me@m4sk.in) * [JMendyk](https://github.com/JMendyk) * [KScl](https://github.com/KScl) * [sterdev](https://github.com/sterdev) @@ -129,30 +140,31 @@ and provided thanks to the work of the following contributors: * [northerner](https://github.com/northerner) * [fhemberger](https://github.com/fhemberger) * [greysteil](https://github.com/greysteil) -* [hensmith](https://github.com/hensmith) +* [hencatsmith](https://github.com/hencatsmith) * [d6rkaiz](https://github.com/d6rkaiz) * [Reverite](https://github.com/Reverite) * [JohnD28](https://github.com/JohnD28) * [znz](https://github.com/znz) -* [marek-lach](https://github.com/marek-lach) * [Naouak](https://github.com/Naouak) * [pawelngei](https://github.com/pawelngei) -* [rtucker](https://github.com/rtucker) * [reneklacan](https://github.com/reneklacan) * [ekiru](https://github.com/ekiru) -* [noellabo](https://github.com/noellabo) * [tcitworld](https://github.com/tcitworld) * [geta6](https://github.com/geta6) * [happycoloredbanana](https://github.com/happycoloredbanana) * [leopku](https://github.com/leopku) * [SansPseudoFix](https://github.com/SansPseudoFix) +* [salvadorpla](https://github.com/salvadorpla) * [tomfhowe](https://github.com/tomfhowe) * [noraworld](https://github.com/noraworld) * [theboss](https://github.com/theboss) +* [nzws](https://github.com/nzws) * [178inaba](https://github.com/178inaba) +* [xgess](https://github.com/xgess) * [alyssais](https://github.com/alyssais) -* [hiphref](https://github.com/hiphref) +* [aablinov](https://github.com/aablinov) * [stalker314314](https://github.com/stalker314314) +* [cutls](https://github.com/cutls) * [huertanix](https://github.com/huertanix) * [genesixx](https://github.com/genesixx) * [halkeye](https://github.com/halkeye) @@ -162,21 +174,24 @@ and provided thanks to the work of the following contributors: * [kmichl](https://github.com/kmichl) * [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name) * [saper](https://github.com/saper) +* [Dar13](https://github.com/Dar13) * [nevillepark](https://github.com/nevillepark) * [ornithocoder](https://github.com/ornithocoder) +* [pwoolcoc](https://github.com/pwoolcoc) * [pierreozoux](https://github.com/pierreozoux) * [qguv](https://github.com/qguv) * [Ram Lmn](mailto:ramlmn@users.noreply.github.com) -* [sascha-sl](https://github.com/sascha-sl) +* [aurelia-sl](https://github.com/aurelia-sl) * [harukasan](https://github.com/harukasan) * [stamak](https://github.com/stamak) -* [Technowix](mailto:technowix@users.noreply.github.com) +* [Technowix](https://github.com/Technowix) * [Zoeille](https://github.com/Zoeille) * [Thor Harald Johansen](mailto:thj@thj.no) * [0x70b1a5](https://github.com/0x70b1a5) * [gled-rs](https://github.com/gled-rs) * [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl) * [R0ckweb](https://github.com/R0ckweb) +* [unasuke](https://github.com/unasuke) * [caasi](https://github.com/caasi) * [chr-1x](https://github.com/chr-1x) * [esetomo](https://github.com/esetomo) @@ -184,8 +199,9 @@ and provided thanks to the work of the following contributors: * [hoodie](mailto:hoodiekitten@outlook.com) * [luzi82](https://github.com/luzi82) * [duxovni](https://github.com/duxovni) +* [slice](https://github.com/slice) * [tmm576](https://github.com/tmm576) -* [unsmell](https://github.com/unsmell) +* [unsmell](mailto:unsmell@users.noreply.github.com) * [valerauko](https://github.com/valerauko) * [chriswmartin](https://github.com/chriswmartin) * [vahnj](https://github.com/vahnj) @@ -193,21 +209,25 @@ and provided thanks to the work of the following contributors: * [AndreLewin](https://github.com/AndreLewin) * [0xflotus](https://github.com/0xflotus) * [redtachyons](https://github.com/redtachyons) +* [acid-chicken](https://github.com/acid-chicken) * [thurloat](https://github.com/thurloat) * [aaribaud](https://github.com/aaribaud) * [pointlessone](https://github.com/pointlessone) * [Andrew](mailto:andrewlchronister@gmail.com) +* [aurelien-reeves](https://github.com/aurelien-reeves) +* [AnaGelez](https://github.com/AnaGelez) * [estuans](https://github.com/estuans) * [dissolve](https://github.com/dissolve) * [PurpleBooth](https://github.com/PurpleBooth) * [bradurani](https://github.com/bradurani) * [wavebeem](https://github.com/wavebeem) * [bruwalfas](https://github.com/bruwalfas) -* [foxsan48](https://github.com/foxsan48) +* [LottieVixen](https://github.com/LottieVixen) * [wchristian](https://github.com/wchristian) * [muffinista](https://github.com/muffinista) * [cdutson](https://github.com/cdutson) * [farlistener](https://github.com/farlistener) +* [dariusk](https://github.com/dariusk) * [DavidLibeau](https://github.com/DavidLibeau) * [ddevault](https://github.com/ddevault) * [Fjoerfoks](https://github.com/Fjoerfoks) @@ -216,6 +236,7 @@ and provided thanks to the work of the following contributors: * [Gomasy](https://github.com/Gomasy) * [unstabler](https://github.com/unstabler) * [potato4d](https://github.com/potato4d) +* [Hanage999](https://github.com/Hanage999) * [h-izumi](https://github.com/h-izumi) * [ErikXXon](https://github.com/ErikXXon) * [ian-kelling](https://github.com/ian-kelling) @@ -231,21 +252,23 @@ and provided thanks to the work of the following contributors: * [Kaylee](mailto:kaylee@codethat.sucks) * [Kazhnuz](https://github.com/Kazhnuz) * [connyduck](https://github.com/connyduck) -* [Lindsey Bieda](mailto:lindseyb@users.noreply.github.com) +* [LindseyB](https://github.com/LindseyB) * [Lorenz Diener](mailto:halcyon@icosahedron.website) * [alimony](https://github.com/alimony) * [mig5](https://github.com/mig5) * [moritzheiber](https://github.com/moritzheiber) * [ndarville](https://github.com/ndarville) * [Abzol](https://github.com/Abzol) -* [pwoolcoc](https://github.com/pwoolcoc) +* [PatOnTheBack](https://github.com/PatOnTheBack) * [xPaw](https://github.com/xPaw) * [petzah](https://github.com/petzah) * [ignisf](https://github.com/ignisf) * [raymestalez](https://github.com/raymestalez) * [remram44](https://github.com/remram44) * [sts10](https://github.com/sts10) +* [SuperSandro2000](https://github.com/SuperSandro2000) * [u1-liquid](https://github.com/u1-liquid) +* [rosylilly](https://github.com/rosylilly) * [sim6](https://github.com/sim6) * [Sir-Boops](https://github.com/Sir-Boops) * [stemid](https://github.com/stemid) @@ -270,6 +293,7 @@ and provided thanks to the work of the following contributors: * [cpsdqs](https://github.com/cpsdqs) * [barzamin](https://github.com/barzamin) * [fhalna](https://github.com/fhalna) +* [highemerly](https://github.com/highemerly) * [haoyayoi](https://github.com/haoyayoi) * [ik11235](https://github.com/ik11235) * [kawax](https://github.com/kawax) @@ -279,6 +303,7 @@ and provided thanks to the work of the following contributors: * [mecab](https://github.com/mecab) * [nicobz25](https://github.com/nicobz25) * [oliverkeeble](https://github.com/oliverkeeble) +* [partev](https://github.com/partev) * [pinfort](https://github.com/pinfort) * [rbaumert](https://github.com/rbaumert) * [rhoio](https://github.com/rhoio) @@ -287,19 +312,17 @@ and provided thanks to the work of the following contributors: * [vjackson725](https://github.com/vjackson725) * [wxcafe](https://github.com/wxcafe) * [新都心(Neet Shin)](mailto:nucx@dio-vox.com) +* [clarfon](https://github.com/clarfon) * [cygnan](https://github.com/cygnan) * [Awea](https://github.com/Awea) * [halcy](https://github.com/halcy) -* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf) * [8398a7](https://github.com/8398a7) * [857b](https://github.com/857b) * [insom](https://github.com/insom) * [tachyons](https://github.com/tachyons) -* [acid-chicken](https://github.com/acid-chicken) * [Esteth](https://github.com/Esteth) * [unascribed](https://github.com/unascribed) * [Aguay-val](https://github.com/Aguay-val) -* [Akihiko Odaki](mailto:nekomanma@pixiv.co.jp) * [knu](https://github.com/knu) * [h3poteto](https://github.com/h3poteto) * [unleashed](https://github.com/unleashed) @@ -307,8 +330,8 @@ and provided thanks to the work of the following contributors: * [console-cowboy](https://github.com/console-cowboy) * [Alkarex](https://github.com/Alkarex) * [a2](https://github.com/a2) +* [alfiedotwtf](https://github.com/alfiedotwtf) * [0xa](https://github.com/0xa) -* [palindromordnilap](https://github.com/palindromordnilap) * [virtualpain](https://github.com/virtualpain) * [sapphirus](https://github.com/sapphirus) * [amandavisconti](https://github.com/amandavisconti) @@ -320,10 +343,9 @@ and provided thanks to the work of the following contributors: * [contraexemplo](https://github.com/contraexemplo) * [abackstrom](https://github.com/abackstrom) * [armandfardeau](https://github.com/armandfardeau) +* [raboof](https://github.com/raboof) * [jumbosushi](https://github.com/jumbosushi) -* [aurelien-reeves](https://github.com/aurelien-reeves) * [ayumin](https://github.com/ayumin) -* [BaptisteGelez](https://github.com/BaptisteGelez) * [bzg](https://github.com/bzg) * [benediktg](https://github.com/benediktg) * [blakebarnett](https://github.com/blakebarnett) @@ -337,15 +359,15 @@ and provided thanks to the work of the following contributors: * [DoubleMalt](https://github.com/DoubleMalt) * [Moosh-be](https://github.com/Moosh-be) * [Motoma](https://github.com/Motoma) -* [chriswk](https://github.com/chriswk) +* [Christopher Kolstad](mailto:christopher.kolstad@finn.no) * [csu](https://github.com/csu) -* [clarfon](https://github.com/clarfon) * [kklleemm](https://github.com/kklleemm) * [colindean](https://github.com/colindean) * [dachinat](https://github.com/dachinat) * [multiple-creatures](https://github.com/multiple-creatures) * [watilde](https://github.com/watilde) * [daprice](https://github.com/daprice) +* [da2x](https://github.com/da2x) * [dar5hak](https://github.com/dar5hak) * [kant](https://github.com/kant) * [maxolasersquad](https://github.com/maxolasersquad) @@ -354,7 +376,7 @@ and provided thanks to the work of the following contributors: * [davefp](https://github.com/davefp) * [yipdw](https://github.com/yipdw) * [debanshuk](https://github.com/debanshuk) -* [Derek Lewis](mailto:derekcecillewis@gmail.com) +* [DerekNonGeneric](https://github.com/DerekNonGeneric) * [dblandin](https://github.com/dblandin) * [Drew Gates](mailto:aranaur@users.noreply.github.com) * [dtschust](https://github.com/dtschust) @@ -366,11 +388,13 @@ and provided thanks to the work of the following contributors: * [ericblade](https://github.com/ericblade) * [mikoim](https://github.com/mikoim) * [espenronnevik](https://github.com/espenronnevik) +* [fabianonline](https://github.com/fabianonline) * [Finariel](https://github.com/Finariel) * [siuying](https://github.com/siuying) * [zoc](https://github.com/zoc) * [fwenzel](https://github.com/fwenzel) * [GenbuHase](https://github.com/GenbuHase) +* [nilsding](https://github.com/nilsding) * [hattori6789](https://github.com/hattori6789) * [algernon](https://github.com/algernon) * [Fastbyte01](https://github.com/Fastbyte01) @@ -386,17 +410,19 @@ and provided thanks to the work of the following contributors: * [suzukaze](https://github.com/suzukaze) * [Hiromi-Kai](https://github.com/Hiromi-Kai) * [hishamhm](https://github.com/hishamhm) +* [Slaynash](https://github.com/Slaynash) * [musashino205](https://github.com/musashino205) * [iwaim](https://github.com/iwaim) * [valrus](https://github.com/valrus) * [IMcD23](https://github.com/IMcD23) * [yi0713](https://github.com/yi0713) * [iblech](https://github.com/iblech) -* [usbsnowcrash](https://github.com/usbsnowcrash) +* [J Yeary](mailto:usbsnowcrash@users.noreply.github.com) * [jack-michaud](https://github.com/jack-michaud) * [Floppy](https://github.com/Floppy) * [loomchild](https://github.com/loomchild) * [jenkr55](https://github.com/jenkr55) +* [hyenagirl64](https://github.com/hyenagirl64) * [press5](https://github.com/press5) * [TrollDecker](https://github.com/TrollDecker) * [jmontane](https://github.com/jmontane) @@ -406,17 +432,17 @@ and provided thanks to the work of the following contributors: * [joshuap](https://github.com/joshuap) * [Tiwy57](https://github.com/Tiwy57) * [xuv](https://github.com/xuv) -* [June Sallou](mailto:jnsll@users.noreply.github.com) +* [Jnsll](https://github.com/Jnsll) * [j0k3r](https://github.com/j0k3r) * [KEINOS](https://github.com/KEINOS) * [futoase](https://github.com/futoase) -* [Pneumaticat](https://github.com/Pneumaticat) +* [pot8to](https://github.com/pot8to) * [Kit Redgrave](mailto:qwertyitis@gmail.com) * [Knut Erik](mailto:abjectio@users.noreply.github.com) * [mkody](https://github.com/mkody) * [k0ta0uchi](https://github.com/k0ta0uchi) * [KrzysiekJ](https://github.com/KrzysiekJ) -* [leowzukw](https://github.com/leowzukw) +* [Leo Wzukw](mailto:leowzukw@users.noreply.github.com) * [Tak](https://github.com/Tak) * [cacheflow](https://github.com/cacheflow) * [ldidry](https://github.com/ldidry) @@ -424,6 +450,7 @@ and provided thanks to the work of the following contributors: * [lfuelling](https://github.com/lfuelling) * [Grabacr07](https://github.com/Grabacr07) * [mistermantas](https://github.com/mistermantas) +* [MareenaKunjachan](https://github.com/MareenaKunjachan) * [mareklach](https://github.com/mareklach) * [wirehack7](https://github.com/wirehack7) * [martymcguire](https://github.com/martymcguire) @@ -431,50 +458,53 @@ and provided thanks to the work of the following contributors: * [otsune](https://github.com/otsune) * [mbugowski](https://github.com/mbugowski) * [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com) +* [madmath03](https://github.com/madmath03) * [matt-auckland](https://github.com/matt-auckland) * [webroo](https://github.com/webroo) -* [matthiasbeyer](https://github.com/matthiasbeyer) -* [mattjmattj](https://github.com/mattjmattj) -* [mtparet](https://github.com/mtparet) -* [maximeborges](https://github.com/maximeborges) -* [minacle](https://github.com/minacle) -* [michaeljdeeb](https://github.com/michaeljdeeb) -* [Themimitoof](https://github.com/Themimitoof) -* [cyweo](https://github.com/cyweo) +* [Matthias Beyer](mailto:mail@beyermatthias.de) +* [Matthias Jouan](mailto:matthias.jouan@gmail.com) +* [Matthieu Paret](mailto:matthieuparet69@gmail.com) +* [Maxime BORGES](mailto:maxime.borges@gmail.com) +* [Mayu Laierlence](mailto:minacle@live.com) +* [Michael Deeb](mailto:michaeldeeb@me.com) +* [Michael Vieira](mailto:dtox94@gmail.com) +* [Michel](mailto:michel@cyweo.com) * [Midgard](mailto:m1dgard@users.noreply.github.com) -* [mike-burns](https://github.com/mike-burns) -* [verymilan](https://github.com/verymilan) -* [milmazz](https://github.com/milmazz) -* [premist](https://github.com/premist) -* [Mnkai](https://github.com/Mnkai) -* [mitchhentges](https://github.com/mitchhentges) -* [mouse-reeve](https://github.com/mouse-reeve) -* [Mozinet-fr](https://github.com/Mozinet-fr) -* [lae](https://github.com/lae) -* [nosada](https://github.com/nosada) -* [Nanamachi](https://github.com/Nanamachi) -* [orinthe](https://github.com/orinthe) -* [NecroTechno](https://github.com/NecroTechno) -* [Dar13](https://github.com/Dar13) -* [ngerakines](https://github.com/ngerakines) -* [vonneudeck](https://github.com/vonneudeck) -* [Ninetailed](https://github.com/Ninetailed) -* [k24](https://github.com/k24) -* [noiob](https://github.com/noiob) -* [kwaio](https://github.com/kwaio) -* [norayr](https://github.com/norayr) -* [joyeusenoelle](https://github.com/joyeusenoelle) -* [OlivierNicole](https://github.com/OlivierNicole) -* [noppa](https://github.com/noppa) -* [Otakan951](https://github.com/Otakan951) -* [fahy](https://github.com/fahy) +* [Mike Burns](mailto:mburns@thoughtbot.com) +* [Milan](mailto:me@petabyteboy.de) +* [Milan*](mailto:tchncs@vivaldi.net) +* [Milton Mazzarri](mailto:milmazz@gmail.com) +* [Minku Lee](mailto:premist@me.com) +* [Minori Hiraoka](mailto:mnkai@users.noreply.github.com) +* [Mitchell Hentges](mailto:mitch9654@gmail.com) +* [Mostafa Ahangarha](mailto:ahangarha@users.noreply.github.com) +* [Mouse Reeve](mailto:mousereeve@riseup.net) +* [Mozinet](mailto:mozinet-fr@users.noreply.github.com) +* [Musee U](mailto:lae@users.noreply.github.com) +* [NOGISAKA Sadata](mailto:ngsksdt@gmail.com) +* [Naf](mailto:uenok.htc@gmail.com) +* [Nanamachi](mailto:town7.haruki@gmail.com) +* [Nathaniel Ekoniak](mailto:nekoniak@ennate.tech) +* [NecroTechno](mailto:necrotechno@riseup.net) +* [Nick Gerakines](mailto:nick@gerakines.net) +* [Nicolai von Neudeck](mailto:nicolai@vonneudeck.com) +* [Ninetailed](mailto:ninetailed@gmail.com) +* [Nishi, Keisuke](mailto:k24@users.noreply.github.com) +* [Noiob](mailto:noiob@users.noreply.github.com) +* [Nope Nope](mailto:hireme@kwaio.ninja) +* [Norayr Chilingarian](mailto:norayr@arnet.am) +* [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com) +* [Næ°](mailto:uenok.htc@gmail.com) +* [Olivier Nicole](mailto:olivierthnicole@gmail.com) +* [Oskari Noppa](mailto:noppa@users.noreply.github.com) +* [Otakan](mailto:otakan951@gmail.com) +* [Padraig Fahy](mailto:tech@padraigfahy.com) * [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com) * [Paul](mailto:naydex.mc+github@gmail.com) * [Pete Keen](mailto:pete@petekeen.net) * [Pierre-Morgan Gate](mailto:pgate@users.noreply.github.com) * [Ratmir Karabut](mailto:rkarabut@sfmodern.ru) * [Reto Kromer](mailto:retokromer@users.noreply.github.com) -* [Rey Tucker](mailto:git@reytucker.us) * [Rob Watson](mailto:rfwatson@users.noreply.github.com) * [Ryan Freebern](mailto:ryan@freebern.org) * [Ryan Wade](mailto:ryan.wade@protonmail.com) @@ -482,6 +512,7 @@ and provided thanks to the work of the following contributors: * [S.H](mailto:gamelinks007@gmail.com) * [Sadiq Saif](mailto:staticsafe@users.noreply.github.com) * [Sam Hewitt](mailto:hewittsamuel@gmail.com) +* [Sasha Sorokin](mailto:dafri.nochiterov8@gmail.com) * [Satoshi KOJIMA](mailto:skoji@mac.com) * [ScienJus](mailto:i@scienjus.com) * [Scott Larkin](mailto:scott@codeclimate.com) @@ -492,12 +523,10 @@ and provided thanks to the work of the following contributors: * [Shaun Gillies](mailto:me@shaungillies.net) * [Shin Adachi](mailto:shn@glucose.jp) * [Shin Kojima](mailto:shin@kojima.org) -* [Sho Kusano](mailto:rosylilly@aduca.org) * [Shouko Yu](mailto:imshouko@gmail.com) * [Sina Mashek](mailto:sina@mashek.xyz) * [Soshi Kato](mailto:mail@sossii.com) * [Spanky](mailto:2788886+spankyworks@users.noreply.github.com) -* [Stanislas](mailto:angristan@pm.me) * [StefOfficiel](mailto:pichard.stephane@free.fr) * [Steven Tappert](mailto:admin@dark-it.net) * [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com) @@ -506,6 +535,7 @@ and provided thanks to the work of the following contributors: * [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com) * [Takayuki KUSANO](mailto:github@tkusano.jp) * [TakesxiSximada](mailto:takesxi.sximada@gmail.com) +* [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com) * [TheInventrix](mailto:theinventrix@users.noreply.github.com) * [Thomas Alberola](mailto:thomas@needacoffee.fr) * [Toby Deshane](mailto:fortyseven@users.noreply.github.com) @@ -515,10 +545,12 @@ and provided thanks to the work of the following contributors: * [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com) * [Udo Kramer](mailto:optik@fluffel.io) * [Una](mailto:una@unascribed.com) +* [Ushitora Anqou](mailto:ushitora@anqou.net) * [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp) * [Valentin Lorentz](mailto:progval+git@progval.net) * [Vladimir Mincev](mailto:vladimir@canicinteractive.com) * [Waldir Pimenta](mailto:waldyrious@gmail.com) +* [Wenceslao Páez Chávez](mailto:wcpaez@gmail.com) * [Wesley Ellis](mailto:tahnok@gmail.com) * [Wiktor](mailto:wiktor@metacode.biz) * [Wonderfall](mailto:wonderfall@schrodinger.io) @@ -529,6 +561,7 @@ and provided thanks to the work of the following contributors: * [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp) * [Yanaken](mailto:yanakend@gmail.com) * [Yann Klis](mailto:yann.klis@gmail.com) +* [Yağızhan](mailto:35808275+yagizhan49@users.noreply.github.com) * [Yeechan Lu](mailto:wz.bluesnow@gmail.com) * [Yusuke Abe](mailto:moonset20@gmail.com) * [Zachary Spector](mailto:logicaldash@gmail.com) @@ -542,6 +575,7 @@ and provided thanks to the work of the following contributors: * [chrolis](mailto:chrolis@users.noreply.github.com) * [cormo](mailto:cormorant2+github@gmail.com) * [d0p1](mailto:dopi-sama@hush.com) +* [dxwc](mailto:dxwc@users.noreply.github.com) * [evilny0](mailto:evilny0@moomoocamp.net) * [febrezo](mailto:felixbrezo@gmail.com) * [fsubal](mailto:fsubal@users.noreply.github.com) @@ -550,6 +584,7 @@ and provided thanks to the work of the following contributors: * [gol-cha](mailto:info@mevo.xyz) * [hakoai](mailto:hk--76@qa2.so-net.ne.jp) * [haosbvnker](mailto:github@chaosbunker.com) +* [ichi_i](mailto:51489410+ichi-i@users.noreply.github.com) * [isati](mailto:phil@juchnowi.cz) * [jacob](mailto:jacobherringtondeveloper@gmail.com) * [jenn kaplan](mailto:me@jkap.io) @@ -561,7 +596,6 @@ and provided thanks to the work of the following contributors: * [karlyeurl](mailto:karl.yeurl@gmail.com) * [kedama](mailto:32974885+kedamadq@users.noreply.github.com) * [kodai](mailto:shirafuta.kodai@gmail.com) -* [koyu](mailto:me@koyu.space) * [kuro5hin](mailto:rusty@kuro5hin.org) * [luzpaz](mailto:luzpaz@users.noreply.github.com) * [maxypy](mailto:maxime@mpigou.fr) @@ -573,6 +607,7 @@ and provided thanks to the work of the following contributors: * [muan](mailto:muan@github.com) * [namelessGonbai](mailto:43787036+namelessgonbai@users.noreply.github.com) * [neetshin](mailto:neetshin@neetsh.in) +* [nzws](mailto:git-yuzu@svk.jp) * [rch850](mailto:rich850@gmail.com) * [roikale](mailto:roikale@users.noreply.github.com) * [rysiekpl](mailto:rysiek@hackerspace.pl) @@ -585,6 +620,8 @@ and provided thanks to the work of the following contributors: * [tateisu](mailto:tateisu@gmail.com) * [tmyt](mailto:shigure@refy.net) * [trevDev()](mailto:trev@trevdev.ca) +* [tsia](mailto:github@tsia.de) +* [umonaca](mailto:53662960+umonaca@users.noreply.github.com) * [utam0k](mailto:k0ma@utam0k.jp) * [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com) * [walfie](mailto:walfington@gmail.com) @@ -593,9 +630,10 @@ and provided thanks to the work of the following contributors: * [yoshipc](mailto:yoooo@yoshipc.net) * [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com) * [ã°ã‚“](mailto:detteiu0321@gmail.com) -* [ã¿ãŸã‚‰ã—ã ã‚“ã”](mailto:mitarashidango@users.noreply.github.com) +* [ãµã‚‹ãµã‚‹](mailto:frfs@users.noreply.github.com) * [りんã™ã](mailto:6533808+rinsuki@users.noreply.github.com) * [ヨイツã®è³¢ç‹¼ãƒ›ãƒ | 3rd style](mailto:horo@yoitsu.moe) +* [å”å®—å‹›](mailto:tangzongxun@hotmail.com) * [猫å¸è¡€é¬¼ãƒ‡ã‚£ãƒ•リス / 猫ãƒã‚P](mailto:deflis@gmail.com) * [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp) * [西å°å€‰å®ä¿¡](mailto:nishiko@mindia.jp) @@ -607,338 +645,122 @@ This document is provided for informational purposes only. Since it is only upda Following people have contributed to translation of Mastodon: -- **Albanian** - - Besnik Bleta - - Aditoo -- **Arabic** - - ButterflyOfFire - - Aditoo - - Amrz0 -- **Asturian** - - ButterflyOfFire - - Enol P. - - Aditoo -- **Basque** - - Osoitz - - Aditoo - - Aitzol - - ButterflyOfFire - - Peru Iparragirre - - Gorka Azkarate -- **Bengali** - - dxwc -- **Bulgarian** - - ButterflyOfFire - - Aditoo -- **Catalan** - - spla - - Aditoo - - ButterflyOfFire - - Joan Montané - - Jose Luis -- **Chinese (Hong Kong)** - - ButterflyOfFire - - Luzi Leung - - Aditoo -- **Chinese (Simplified)** - - Allen Zhong - - ButterflyOfFire - - SerCom_KC - - martialarts - - Kaitian Xie - - Aditoo - - pan93412 -- **Chinese (Traditional)** - - Aditoo - - ButterflyOfFire - - James58899 - - pan93412 - - S1ttidoe477 - - SHA265 - - Jeff Huang -- **Corsican** - - Alix D. R. - - Aditoo - - ButterflyOfFire -- **Croatian** - - ButterflyOfFire - - Aditoo -- **Czech** - - Aditoo - - Marek Ľach - - ButterflyOfFire -- **Danish** - - Einhjeriar - - Rasmus Sæderup - - Aditoo - - ButterflyOfFire -- **Dutch** - - Albakham - - ButterflyOfFire - - jeroenpraat - - rscmbbng - - Aditoo - - Jelv -- **English** - - ButterflyOfFire - - Renato "Lond" Cerqueira -- **English (United Kingdom)** - - Albakham -- **Esperanto** - - Aditoo - - ButterflyOfFire - - Becci Cat - - Jeong Arm - - Mélanie Chauvel - - Vanege - - Martin Bodin - - tuxayo/Victor Grousset -- **Finnish** - - ButterflyOfFire - - Mikko Poussu - - Taru Luojola - - S Heija - - Aditoo - - Jonne Arjoranta -- **French** - - Albakham - - Alix D. R. - - ButterflyOfFire - - codl - - Leia - - Alda Marteau-Hardi - - Mélanie Chauvel - - Paul Marques Mota - - azenet - - Olivier Humbert - - Aditoo - - Jonathan Chan - - Letiteuf55 - - Baptiste Jonglez - - goofy-mdn - - Jean-Baptiste Holcroft - - Technowix - - Martin Bodin - - Théodore - - Thibaut Girka - - Franck Paul - - Sylvhem -- **Galician** - - ButterflyOfFire - - Xose M. - - Aditoo - - manequim -- **Georgian** - - ButterflyOfFire - - Aditoo -- **German** - - Aditoo - - ButterflyOfFire - - Daniel - - averageunicorn - - Koyu Berteon - - larsreineke - - koyu - - Austin Jones - - lilo - - Benedikt Geißler - - ePirat - - Eugen Rochko - - Weblate Admin - - Patrick Figel -- **Greek** - - Dimitris Maroulidis - - Antonis - - Aditoo - - ButterflyOfFire - - Konstantinos Grevenitis -- **Hebrew** - - ButterflyOfFire - - Aditoo - - Ira - - Yaron Shahrabani -- **Hungarian** - - ButterflyOfFire - - Adam Paszternak - - Aditoo - - Tibike Miklós -- **Ido** - - ButterflyOfFire - - Aditoo -- **Indonesian** - - afachri - - ButterflyOfFire - - Dito Kurnia Pratama - - Eirworks - - Aditoo - - Alfiana Sibuea - - se7entime -- **Irish** - - Albakham - - Kevin Houlihan -- **Italian** - - Alessandro Levati - - Albakham - - ButterflyOfFire - - Marcin MikoÅ‚ajczak - - Aditoo - - Giuseppe Pignataro - - Stefano -- **Japanese** - - Hinaloe - - å°é³¥éŠã¾ã‚Šã‚ - - mayaeh - - osapon - - 森ã®åリスã®ãƒŸãƒ¼ã‚³ã®å¤§å†’険 - - Kumasun Morino - - Yamagishi Kazutoshi - - Aditoo - - ButterflyOfFire - - Jeong Arm - - unarist -- **Kazakh** - - arshat - - Aditoo -- **Korean** - - Aditoo - - Jeong Arm - - ButterflyOfFire - - Minori Hiraoka - - Yamagishi Kazutoshi -- **Lithuanian** - - Sarunas Medeikis -- **Malay** - - Muhammad Nur Hidayat (MNH48) - - Aditoo - - ButterflyOfFire -- **Norwegian (old code)** - - ButterflyOfFire - - Espen Rønnevik - - Aditoo - - Tale -- **Occitan** - - Aditoo - - ButterflyOfFire - - Quenti2 - - Quentà - - Maxenç -- **Persian** - - Masoud Abkenar - - Aditoo - - ButterflyOfFire -- **Polish** - - Aditoo - - Albakham - - ButterflyOfFire - - Stasiek Michalski - - Marcin MikoÅ‚ajczak - - Jakub Mendyk - - Marek Ľach - - krkk -- **Portuguese** - - Albakham - - João Pinheiro - - manequim - - Aditoo - - ButterflyOfFire - - Hugo Gameiro -- **Portuguese (Brazil)** - - Aditoo - - Albakham - - Anna e só - - Renato "Lond" Cerqueira - - André Andrade - - ButterflyOfFire -- **Romanian** - - adrianbblk - - ButterflyOfFire - - Aditoo -- **Russian** - - Albakham - - ButterflyOfFire - - Evgeny Petrov - - Aditoo - - Павел ГаÑтелло - - Andrew Zyabin - - Yaron Shahrabani -- **Serbian** - - Branko Kokanovic - - Burekz Finezt - - Aditoo - - ButterflyOfFire -- **Serbian (latin)** - - ButterflyOfFire - - Aditoo -- **Slovak** - - Aditoo - - ButterflyOfFire - - Ivan Pleva - - Marek Ľach - - Peter -- **Slovenian** - - Kristijan Tkalec - - Aditoo - - ButterflyOfFire -- **Spanish** - - Albakham - - ButterflyOfFire - - Carlos Mondragon - - Antón López - - Max Winkler - - Pablo de la Concepción Sanz - - Sergio Soriano - - Angeles Broullón - - Lothar Wolf - - Aditoo - - David Charte - - Emmanuel -- **Swedish** - - ButterflyOfFire - - Isak Holmström - - Shellkr - - Aditoo - - Elias MÃ¥rtenson - - Stefan Midjich - - Tim Stahel - - Jonas Hultén -- **Telugu** - - avndp - - Ranjith Tellakula - - Aditoo - - ButterflyOfFire - - Joseph Nuthalapati -- **Thai** - - ButterflyOfFire - - parnikkapore - - Thai Localization - - Aditoo -- **Turkish** - - Ali Demirtas - - ButterflyOfFire - - Aditoo -- **Ukrainian** - - alexcleac - - ButterflyOfFire - - Aditoo - - Ivan Verchenko -- **Welsh** - - carl morris - - Jaz-Michael King - - Owain Rhys Lewis - - Rhoslyn Prys - - Aditoo - - ButterflyOfFire - - Renato "Lond" Cerqueira - - Albakham - - Kevin Beynon -- **Armenian** - - Aditoo - - ButterflyOfFire -- **Latvian** - - Aditoo - - ButterflyOfFire - - Maigonis -- **Tamil** - - Aditoo - - ButterflyOfFire - - Prasanna Venkadesh +- Zoltán Gera (*Hungarian*) +- Kristijan Tkalec (*Slovenian*) +- Evert Prants (*Estonian*) +- borys_sh (*Ukrainian*) +- ButterflyOfFire (*Arabic; French*) +- Osoitz (*Basque*) +- oɹʇuÊž (*Spanish, Argentina*) +- koyu (*German*) +- Jeroen (*Dutch*) +- Muha Aliss (*Turkish*) +- å”å®—å‹› (*Chinese Simplified*) +- Jeong Arm (*Korean; Esperanto; Japanese*) +- Oguz Ersen (*Turkish*) +- spla (*Catalan*) +- Ramdziana F Y (*Indonesian*) +- Aditoo17 (*Czech*) +- Xosé M. (*Galician*) +- Roboron (*Spanish*) +- Alix Rossi (*Corsican; French*) +- Maya Minatsuki (*Japanese*) +- Masoud Abkenar (*Persian*) +- Thai Localization (*Thai*) +- Marek Ľach (*Slovak; Polish*) +- d5Ziif3K (*Ukrainian*) +- lamnatos (*Greek*) +- Emyn Nant Nefydd (*Welsh*) +- Diluns (*Occitan*) +- atarashiako (*Chinese Simplified*) +- 101010 (*Polish*) +- Yi-Jyun Pan (*Chinese Traditional*) +- silkevicious (*Italian*) +- FédiQuébec (*French*) +- Jaz-Michael King (*Welsh*) +- christalleras (*Norwegian Nynorsk*) +- tykayn (*French*) +- Alessandro Levati (*Italian*) +- carolinagiorno (*Portuguese, Brazilian*) +- taoxvx (*Danish*) +- sabri (*Spanish*) +- Sasha Sorokin (*Russian*) +- shioko (*Chinese Simplified*) +- Evgeny Petrov (*Russian*) +- ariasuni (*French; Esperanto*) +- Tiago Epifânio (*Portuguese*) +- dxwc (*Bengali*) +- liffon (*Swedish*) +- Vanege (*Esperanto*) +- Johan Schiff (*Swedish*) +- kat (*Ukrainian; Russian*) +- oti4500 (*Hungarian; Ukrainian*) +- Juan José Salvador Piedra (*Spanish*) +- diazepan (*Spanish*) +- SHeija (*Finnish*) +- Jack R (*Spanish*) +- Saederup92 (*Danish*) +- Stasiek Michalski (*Polish*) +- Dewi (*Breton; French*) +- cybergene (*Japanese*) +- AW Unad (*Indonesian*) +- Andrea Lo Iacono (*Italian*) +- Ray (*Spanish*) +- Unmual (*Spanish*) +- Ryo (*Korean*) +- juanda097 (*Spanish*) +- Anunnakey (*Macedonian*) +- Cutls (*Japanese*) +- erikstl (*Esperanto*) +- ruine (*Japanese*) +- MadeInSteak (*Finnish*) +- Sokratis Alichanidis (*Greek*) +- dragnucs2 (*Arabic*) +- frumble (*German*) +- Rikard Linde (*Swedish*) +- PPNplus (*Thai*) +- arethsu (*Swedish*) +- EPEMA YT (*German*) +- Rhys Harrison (*Esperanto*) +- KEINOS (*Japanese*) +- filippodb (*Italian*) +- JzshAC (*Chinese Simplified*) +- Rintan1 (*Japanese*) +- Antillion (*Spanish*) +- hiphipvargas (*Portuguese*) +- Ch. (*Korean*) +- tctovsli (*Norwegian Nynorsk*) +- vjasiegd (*Polish*) +- SamitiMed (*Thai*) +- umelard (*Hebrew*) +- ç¡«é…¸é¶ (*Japanese*) +- Adrián Lattes (*Spanish*) +- Hinaloe (*Japanese*) +- Renato "Lond" Cerqueira (*Portuguese, Brazilian*) +- parnikkapore (*Thai*) +- Marcin MikoÅ‚ajczak (*Polish*) +- 森ã®åリスã®ãƒŸãƒ¼ã‚³ã®å¤§å†’険 (*Japanese*) +- Marcepanek_ (*Polish*) +- Sahak Petrosyan (*Armenian*) +- Daniel Dimitrov (*Bulgarian*) +- Hugh Liu (*Chinese Simplified*) +- Rakino (*Chinese Simplified*) +- hussama (*Portuguese, Brazilian*) +- ThibG (*French*) +- SnDer (*Dutch*) +- PifyZ (*French*) +- eichkat3r (*German*) +- Karol Kosek (*Polish*) +- Akarshan Biswas (*Bengali*) +- Tradjincal (*French*) +- Steven Tappert (*German*) +- sergioaraujo1 (*Portuguese, Brazilian*) +- mmokhi (*Persian*) +- fedot (*Russian*) +- skaaarrr (*German*) +- JackXu (*Chinese Simplified*) +- Lukas Fülling (*German*) +- Zoé BÅ‘le (*German*) +- Dremski (*Bulgarian*) +- tamaina (*Japanese*) +- OpenAlgeria (*Arabic*) diff --git a/CHANGELOG.md b/CHANGELOG.md index a17fbf8f06385677eb05801b422998bb6613b9ea..b200747b12f5b81c4a476432e52a27f58aa006be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,235 @@ Changelog All notable changes to this project will be documented in this file. +## [3.0.1] - 2019-10-10 +### Added + +- Add `tootctl media usage` command ([Gargron](https://github.com/tootsuite/mastodon/pull/12115)) +- Add admin setting to auto-approve trending hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12122), [Gargron](https://github.com/tootsuite/mastodon/pull/12130)) + +### Changed + +- Change `tootctl media refresh` to skip already downloaded attachments ([Gargron](https://github.com/tootsuite/mastodon/pull/12118)) + +### Removed + +- Remove auto-silence behaviour from spam check ([Gargron](https://github.com/tootsuite/mastodon/pull/12117)) +- Remove HTML `lang` attribute from individual statuses in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12124)) +- Remove fallback to long description on sidebar and meta description ([Gargron](https://github.com/tootsuite/mastodon/pull/12119)) + +### Fixed + +- Fix preloaded JSON-LD context for identity not being used ([Gargron](https://github.com/tootsuite/mastodon/pull/12138)) +- Fix media editing modal changing dimensions once the image loads ([Gargron](https://github.com/tootsuite/mastodon/pull/12131)) +- Fix not showing whether a custom emoji has a local counterpart in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12135)) +- Fix attachment not being re-downloaded even if file is not stored ([Gargron](https://github.com/tootsuite/mastodon/pull/12125)) +- Fix old migration trying to use new column due to default status scope ([Gargron](https://github.com/tootsuite/mastodon/pull/12095)) +- Fix column back button missing for not found accounts ([trwnh](https://github.com/tootsuite/mastodon/pull/12094)) +- Fix issues with tootctl's parallelization and progress reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/12093), [Gargron](https://github.com/tootsuite/mastodon/pull/12097)) +- Fix existing user records with now-renamed `pt` locale ([Gargron](https://github.com/tootsuite/mastodon/pull/12092)) +- Fix hashtag timeline REST API accepting too many hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12091)) +- Fix `GET /api/v1/instance` REST APIs being unavailable in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12089)) +- Fix performance of home feed regeneration and merging ([Gargron](https://github.com/tootsuite/mastodon/pull/12084)) +- Fix ffmpeg performance issues due to stdout buffer overflow ([hugogameiro](https://github.com/tootsuite/mastodon/pull/12088)) +- Fix S3 adapter retrying failing uploads with exponential backoff ([Gargron](https://github.com/tootsuite/mastodon/pull/12085)) +- Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12074)) + +## [3.0.0] - 2019-10-03 +### Added + +- Add "not available" label to unloaded media attachments in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11715), [Gargron](https://github.com/tootsuite/mastodon/pull/11745)) +- **Add profile directory to web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11688), [mayaeh](https://github.com/tootsuite/mastodon/pull/11872)) + - Add profile directory opt-in federation + - Add profile directory REST API +- Add special alert for throttled requests in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11677)) +- Add confirmation modal when logging out from the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11671)) +- **Add audio player in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11644), [Gargron](https://github.com/tootsuite/mastodon/pull/11652), [Gargron](https://github.com/tootsuite/mastodon/pull/11654), [ThibG](https://github.com/tootsuite/mastodon/pull/11629), [Gargron](https://github.com/tootsuite/mastodon/pull/12056)) +- **Add autosuggestions for hashtags in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11422), [ThibG](https://github.com/tootsuite/mastodon/pull/11632), [Gargron](https://github.com/tootsuite/mastodon/pull/11764), [Gargron](https://github.com/tootsuite/mastodon/pull/11588), [Gargron](https://github.com/tootsuite/mastodon/pull/11442)) +- **Add media editing modal with OCR tool in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11563), [Gargron](https://github.com/tootsuite/mastodon/pull/11566), [ThibG](https://github.com/tootsuite/mastodon/pull/11575), [ThibG](https://github.com/tootsuite/mastodon/pull/11576), [Gargron](https://github.com/tootsuite/mastodon/pull/11577), [Gargron](https://github.com/tootsuite/mastodon/pull/11573), [Gargron](https://github.com/tootsuite/mastodon/pull/11571)) +- Add indicator of unread notifications to window title when web UI is out of focus ([Gargron](https://github.com/tootsuite/mastodon/pull/11560), [Gargron](https://github.com/tootsuite/mastodon/pull/11572)) +- Add indicator for which options you voted for in a poll in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11195)) +- **Add search results pagination to web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11409), [ThibG](https://github.com/tootsuite/mastodon/pull/11447)) +- **Add option to disable real-time updates in web UI ("slow mode")** ([Gargron](https://github.com/tootsuite/mastodon/pull/9984), [ykzts](https://github.com/tootsuite/mastodon/pull/11880), [ThibG](https://github.com/tootsuite/mastodon/pull/11883), [Gargron](https://github.com/tootsuite/mastodon/pull/11898), [ThibG](https://github.com/tootsuite/mastodon/pull/11859)) +- Add option to disable blurhash previews in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11188)) +- Add native smooth scrolling when supported in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11207)) +- Add scrolling to the search bar on focus in web UI ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12032)) +- Add refresh button to list of rebloggers/favouriters in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12031)) +- Add error description and button to copy stack trace to web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12033)) +- Add search and sort functions to hashtag admin UI ([mayaeh](https://github.com/tootsuite/mastodon/pull/11829), [Gargron](https://github.com/tootsuite/mastodon/pull/11897), [mayaeh](https://github.com/tootsuite/mastodon/pull/11875)) +- Add setting for default search engine indexing in admin UI ([brortao](https://github.com/tootsuite/mastodon/pull/11804)) +- Add account bio to account view in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11473)) +- **Add option to include reported statuses in warning e-mail from admin UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11639), [Gargron](https://github.com/tootsuite/mastodon/pull/11812), [Gargron](https://github.com/tootsuite/mastodon/pull/11741), [Gargron](https://github.com/tootsuite/mastodon/pull/11698), [mayaeh](https://github.com/tootsuite/mastodon/pull/11765)) +- Add number of pending accounts and pending hashtags to dashboard in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11514)) +- **Add account migration UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11846), [noellabo](https://github.com/tootsuite/mastodon/pull/11905), [noellabo](https://github.com/tootsuite/mastodon/pull/11907), [noellabo](https://github.com/tootsuite/mastodon/pull/11906), [noellabo](https://github.com/tootsuite/mastodon/pull/11902)) +- **Add table of contents to about page** ([Gargron](https://github.com/tootsuite/mastodon/pull/11885), [ykzts](https://github.com/tootsuite/mastodon/pull/11941), [ykzts](https://github.com/tootsuite/mastodon/pull/11895), [Kjwon15](https://github.com/tootsuite/mastodon/pull/11916)) +- **Add password challenge to 2FA settings, e-mail notifications** ([Gargron](https://github.com/tootsuite/mastodon/pull/11878)) +- **Add optional public list of domain blocks with comments** ([ThibG](https://github.com/tootsuite/mastodon/pull/11298), [ThibG](https://github.com/tootsuite/mastodon/pull/11515), [Gargron](https://github.com/tootsuite/mastodon/pull/11908)) +- Add an RSS feed for featured hashtags ([noellabo](https://github.com/tootsuite/mastodon/pull/10502)) +- Add explanations to featured hashtags UI and profile ([Gargron](https://github.com/tootsuite/mastodon/pull/11586)) +- **Add hashtag trends with admin and user settings** ([Gargron](https://github.com/tootsuite/mastodon/pull/11490), [Gargron](https://github.com/tootsuite/mastodon/pull/11502), [Gargron](https://github.com/tootsuite/mastodon/pull/11641), [Gargron](https://github.com/tootsuite/mastodon/pull/11594), [Gargron](https://github.com/tootsuite/mastodon/pull/11517), [mayaeh](https://github.com/tootsuite/mastodon/pull/11845), [Gargron](https://github.com/tootsuite/mastodon/pull/11774), [Gargron](https://github.com/tootsuite/mastodon/pull/11712), [Gargron](https://github.com/tootsuite/mastodon/pull/11791), [Gargron](https://github.com/tootsuite/mastodon/pull/11743), [Gargron](https://github.com/tootsuite/mastodon/pull/11740), [Gargron](https://github.com/tootsuite/mastodon/pull/11714), [ThibG](https://github.com/tootsuite/mastodon/pull/11631), [Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/11569), [Gargron](https://github.com/tootsuite/mastodon/pull/11524), [Gargron](https://github.com/tootsuite/mastodon/pull/11513)) + - Add hashtag usage breakdown to admin UI + - Add batch actions for hashtags to admin UI + - Add trends to web UI + - Add trends to public pages + - Add user preference to hide trends + - Add admin setting to disable trends +- **Add categories for custom emojis** ([Gargron](https://github.com/tootsuite/mastodon/pull/11196), [Gargron](https://github.com/tootsuite/mastodon/pull/11793), [Gargron](https://github.com/tootsuite/mastodon/pull/11920), [highemerly](https://github.com/tootsuite/mastodon/pull/11876)) + - Add custom emoji categories to emoji picker in web UI + - Add `category` to custom emojis in REST API + - Add batch actions for custom emojis in admin UI +- Add max image dimensions to error message ([raboof](https://github.com/tootsuite/mastodon/pull/11552)) +- Add aac, m4a, 3gp, amr, wma to allowed audio formats ([Gargron](https://github.com/tootsuite/mastodon/pull/11342), [umonaca](https://github.com/tootsuite/mastodon/pull/11687)) +- **Add search syntax for operators and phrases** ([Gargron](https://github.com/tootsuite/mastodon/pull/11411)) +- **Add REST API for managing featured hashtags** ([noellabo](https://github.com/tootsuite/mastodon/pull/11778)) +- **Add REST API for managing timeline read markers** ([Gargron](https://github.com/tootsuite/mastodon/pull/11762)) +- Add `exclude_unreviewed` param to `GET /api/v2/search` REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/11977)) +- Add `reason` param to `POST /api/v1/accounts` REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/12064)) +- **Add ActivityPub secure mode** ([Gargron](https://github.com/tootsuite/mastodon/pull/11269), [ThibG](https://github.com/tootsuite/mastodon/pull/11332), [ThibG](https://github.com/tootsuite/mastodon/pull/11295)) +- Add HTTP signatures to all outgoing ActivityPub GET requests ([Gargron](https://github.com/tootsuite/mastodon/pull/11284), [ThibG](https://github.com/tootsuite/mastodon/pull/11300)) +- Add support for ActivityPub Audio activities ([ThibG](https://github.com/tootsuite/mastodon/pull/11189)) +- Add ActivityPub actor representing the entire server ([ThibG](https://github.com/tootsuite/mastodon/pull/11321), [rtucker](https://github.com/tootsuite/mastodon/pull/11400), [ThibG](https://github.com/tootsuite/mastodon/pull/11561), [Gargron](https://github.com/tootsuite/mastodon/pull/11798)) +- **Add whitelist mode** ([Gargron](https://github.com/tootsuite/mastodon/pull/11291), [mayaeh](https://github.com/tootsuite/mastodon/pull/11634)) +- Add config of multipart threshold for S3 ([ykzts](https://github.com/tootsuite/mastodon/pull/11924), [ykzts](https://github.com/tootsuite/mastodon/pull/11944)) +- Add health check endpoint for web ([ykzts](https://github.com/tootsuite/mastodon/pull/11770), [ykzts](https://github.com/tootsuite/mastodon/pull/11947)) +- Add HTTP signature keyId to request log ([Gargron](https://github.com/tootsuite/mastodon/pull/11591)) +- Add `SMTP_REPLY_TO` environment variable ([hugogameiro](https://github.com/tootsuite/mastodon/pull/11718)) +- Add `tootctl preview_cards remove` command ([mayaeh](https://github.com/tootsuite/mastodon/pull/11320)) +- Add `tootctl media refresh` command ([Gargron](https://github.com/tootsuite/mastodon/pull/11775)) +- Add `tootctl cache recount` command ([Gargron](https://github.com/tootsuite/mastodon/pull/11597)) +- Add option to exclude suspended domains from `tootctl domains crawl` ([dariusk](https://github.com/tootsuite/mastodon/pull/11454)) +- Add parallelization to `tootctl search deploy` ([noellabo](https://github.com/tootsuite/mastodon/pull/12051)) +- Add soft delete for statuses for instant deletes through API ([Gargron](https://github.com/tootsuite/mastodon/pull/11623), [Gargron](https://github.com/tootsuite/mastodon/pull/11648)) +- Add rails-level JSON caching ([Gargron](https://github.com/tootsuite/mastodon/pull/11333), [Gargron](https://github.com/tootsuite/mastodon/pull/11271)) +- **Add request pool to improve delivery performance** ([Gargron](https://github.com/tootsuite/mastodon/pull/10353), [ykzts](https://github.com/tootsuite/mastodon/pull/11756)) +- Add concurrent connection attempts to resolved IP addresses ([ThibG](https://github.com/tootsuite/mastodon/pull/11757)) +- Add index for remember_token to improve login performance ([abcang](https://github.com/tootsuite/mastodon/pull/11881)) +- **Add more accurate hashtag search** ([Gargron](https://github.com/tootsuite/mastodon/pull/11579), [Gargron](https://github.com/tootsuite/mastodon/pull/11427), [Gargron](https://github.com/tootsuite/mastodon/pull/11448)) +- **Add more accurate account search** ([Gargron](https://github.com/tootsuite/mastodon/pull/11537), [Gargron](https://github.com/tootsuite/mastodon/pull/11580)) +- **Add a spam check** ([Gargron](https://github.com/tootsuite/mastodon/pull/11217), [Gargron](https://github.com/tootsuite/mastodon/pull/11806), [ThibG](https://github.com/tootsuite/mastodon/pull/11296)) +- Add new languages ([Gargron](https://github.com/tootsuite/mastodon/pull/12062)) + - Breton + - Spanish (Argentina) + - Estonian + - Macedonian + - New Norwegian +- Add NodeInfo endpoint ([Gargron](https://github.com/tootsuite/mastodon/pull/12002), [Gargron](https://github.com/tootsuite/mastodon/pull/12058)) + +### Changed + +- **Change conversations UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/11896)) +- Change dashboard to short number notation ([noellabo](https://github.com/tootsuite/mastodon/pull/11847), [noellabo](https://github.com/tootsuite/mastodon/pull/11911)) +- Change REST API `GET /api/v1/timelines/public` to require authentication when public preview is off ([ThibG](https://github.com/tootsuite/mastodon/pull/11802)) +- Change REST API `POST /api/v1/follow_requests/:id/(approve|reject)` to return relationship ([ThibG](https://github.com/tootsuite/mastodon/pull/11800)) +- Change rate limit for media proxy ([ykzts](https://github.com/tootsuite/mastodon/pull/11814)) +- Change unlisted custom emoji to not appear in autosuggestions ([Gargron](https://github.com/tootsuite/mastodon/pull/11818)) +- Change max length of media descriptions from 420 to 1500 characters ([Gargron](https://github.com/tootsuite/mastodon/pull/11819), [ThibG](https://github.com/tootsuite/mastodon/pull/11836)) +- **Change deletes to preserve soft-deleted statuses in unresolved reports** ([Gargron](https://github.com/tootsuite/mastodon/pull/11805)) +- **Change tootctl to use inline parallelization instead of Sidekiq** ([Gargron](https://github.com/tootsuite/mastodon/pull/11776)) +- **Change account deletion page to have better explanations** ([Gargron](https://github.com/tootsuite/mastodon/pull/11753), [Gargron](https://github.com/tootsuite/mastodon/pull/11763)) +- Change hashtag component in web UI to show numbers for 2 last days ([Gargron](https://github.com/tootsuite/mastodon/pull/11742), [Gargron](https://github.com/tootsuite/mastodon/pull/11755), [Gargron](https://github.com/tootsuite/mastodon/pull/11754)) +- Change OpenGraph description on sign-up page to reflect invite ([Gargron](https://github.com/tootsuite/mastodon/pull/11744)) +- Change layout of public profile directory to be the same as in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11705)) +- Change detailed status child ordering to sort self-replies on top ([ThibG](https://github.com/tootsuite/mastodon/pull/11686)) +- Change window resize handler to switch to/from mobile layout as soon as needed ([ThibG](https://github.com/tootsuite/mastodon/pull/11656)) +- Change icon button styles to make hover/focus states more obvious ([ThibG](https://github.com/tootsuite/mastodon/pull/11474)) +- Change contrast of status links that are not mentions or hashtags ([ThibG](https://github.com/tootsuite/mastodon/pull/11406)) +- **Change hashtags to preserve first-used casing** ([Gargron](https://github.com/tootsuite/mastodon/pull/11416), [Gargron](https://github.com/tootsuite/mastodon/pull/11508), [Gargron](https://github.com/tootsuite/mastodon/pull/11504), [Gargron](https://github.com/tootsuite/mastodon/pull/11507), [Gargron](https://github.com/tootsuite/mastodon/pull/11441)) +- **Change unconfirmed user login behaviour** ([Gargron](https://github.com/tootsuite/mastodon/pull/11375), [ThibG](https://github.com/tootsuite/mastodon/pull/11394), [Gargron](https://github.com/tootsuite/mastodon/pull/11860)) +- **Change single-column mode to scroll the whole page** ([Gargron](https://github.com/tootsuite/mastodon/pull/11359), [Gargron](https://github.com/tootsuite/mastodon/pull/11894), [Gargron](https://github.com/tootsuite/mastodon/pull/11891), [ThibG](https://github.com/tootsuite/mastodon/pull/11655), [Gargron](https://github.com/tootsuite/mastodon/pull/11463), [Gargron](https://github.com/tootsuite/mastodon/pull/11458), [ThibG](https://github.com/tootsuite/mastodon/pull/11395), [Gargron](https://github.com/tootsuite/mastodon/pull/11418)) +- Change `tootctl accounts follow` to only work with local accounts ([angristan](https://github.com/tootsuite/mastodon/pull/11592)) +- Change Dockerfile ([Shleeble](https://github.com/tootsuite/mastodon/pull/11710), [ykzts](https://github.com/tootsuite/mastodon/pull/11768), [Shleeble](https://github.com/tootsuite/mastodon/pull/11707)) +- Change supported Node versions to include v12 ([abcang](https://github.com/tootsuite/mastodon/pull/11706)) +- Change Portuguese language from `pt` to `pt-PT` ([Gargron](https://github.com/tootsuite/mastodon/pull/11820)) +- Change domain block silence to always require approval on follow ([ThibG](https://github.com/tootsuite/mastodon/pull/11975)) +- Change link preview fetcher to not perform a HEAD request first ([Gargron](https://github.com/tootsuite/mastodon/pull/12028)) +- Change `tootctl domains purge` to accept multiple domains at once ([Gargron](https://github.com/tootsuite/mastodon/pull/12046)) + +### Removed + +- **Remove OStatus support** ([Gargron](https://github.com/tootsuite/mastodon/pull/11205), [Gargron](https://github.com/tootsuite/mastodon/pull/11303), [Gargron](https://github.com/tootsuite/mastodon/pull/11460), [ThibG](https://github.com/tootsuite/mastodon/pull/11280), [ThibG](https://github.com/tootsuite/mastodon/pull/11278)) +- Remove Atom feeds and old URLs in the form of `GET /:username/updates/:id` ([Gargron](https://github.com/tootsuite/mastodon/pull/11247)) +- Remove WebP support ([angristan](https://github.com/tootsuite/mastodon/pull/11589)) +- Remove deprecated config options from Heroku and Scalingo ([ykzts](https://github.com/tootsuite/mastodon/pull/11925)) +- Remove deprecated REST API `GET /api/v1/search` API ([Gargron](https://github.com/tootsuite/mastodon/pull/11823)) +- Remove deprecated REST API `GET /api/v1/statuses/:id/card` ([Gargron](https://github.com/tootsuite/mastodon/pull/11213)) +- Remove deprecated REST API `POST /api/v1/notifications/dismiss?id=:id` ([Gargron](https://github.com/tootsuite/mastodon/pull/11214)) +- Remove deprecated REST API `GET /api/v1/timelines/direct` ([Gargron](https://github.com/tootsuite/mastodon/pull/11212)) + +### Fixed + +- Fix manifest warning ([ykzts](https://github.com/tootsuite/mastodon/pull/11767)) +- Fix admin UI for custom emoji not respecting GIF autoplay preference ([ThibG](https://github.com/tootsuite/mastodon/pull/11801)) +- Fix page body not being scrollable in admin/settings layout ([Gargron](https://github.com/tootsuite/mastodon/pull/11893)) +- Fix placeholder colors for inputs not being explicitly defined ([Gargron](https://github.com/tootsuite/mastodon/pull/11890)) +- Fix incorrect enclosure length in RSS ([tsia](https://github.com/tootsuite/mastodon/pull/11889)) +- Fix TOTP codes not being filtered from logs during enabling/disabling ([Gargron](https://github.com/tootsuite/mastodon/pull/11877)) +- Fix webfinger response not returning 410 when account is suspended ([Gargron](https://github.com/tootsuite/mastodon/pull/11869)) +- Fix ActivityPub Move handler queuing jobs that will fail if account is suspended ([Gargron](https://github.com/tootsuite/mastodon/pull/11864)) +- Fix SSO login not using existing account when e-mail is verified ([Gargron](https://github.com/tootsuite/mastodon/pull/11862)) +- Fix web UI allowing uploads past status limit via drag & drop ([Gargron](https://github.com/tootsuite/mastodon/pull/11863)) +- Fix expiring polls not being displayed as such in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11835)) +- Fix 2FA challenge and password challenge for non-database users ([Gargron](https://github.com/tootsuite/mastodon/pull/11831), [Gargron](https://github.com/tootsuite/mastodon/pull/11943)) +- Fix profile fields overflowing page width in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11828)) +- Fix web push subscriptions being deleted on rate limit or timeout ([Gargron](https://github.com/tootsuite/mastodon/pull/11826)) +- Fix display of long poll options in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11717), [ThibG](https://github.com/tootsuite/mastodon/pull/11833)) +- Fix search API not resolving URL when `type` is given ([Gargron](https://github.com/tootsuite/mastodon/pull/11822)) +- Fix hashtags being split by ZWNJ character ([Gargron](https://github.com/tootsuite/mastodon/pull/11821)) +- Fix scroll position resetting when opening media modals in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11815)) +- Fix duplicate HTML IDs on about page ([ThibG](https://github.com/tootsuite/mastodon/pull/11803)) +- Fix admin UI showing superfluous reject media/reports on suspended domain blocks ([ThibG](https://github.com/tootsuite/mastodon/pull/11749)) +- Fix ActivityPub context not being dynamically computed ([ThibG](https://github.com/tootsuite/mastodon/pull/11746)) +- Fix Mastodon logo style on hover on public pages' footer ([ThibG](https://github.com/tootsuite/mastodon/pull/11735)) +- Fix height of dashboard counters ([ThibG](https://github.com/tootsuite/mastodon/pull/11736)) +- Fix custom emoji animation on hover in web UI directory bios ([ThibG](https://github.com/tootsuite/mastodon/pull/11716)) +- Fix non-numbers being passed to Redis and causing an error ([Gargron](https://github.com/tootsuite/mastodon/pull/11697)) +- Fix error in REST API for an account's statuses ([Gargron](https://github.com/tootsuite/mastodon/pull/11700)) +- Fix uncaught error when resource param is missing in Webfinger request ([Gargron](https://github.com/tootsuite/mastodon/pull/11701)) +- Fix uncaught domain normalization error in remote follow ([Gargron](https://github.com/tootsuite/mastodon/pull/11703)) +- Fix uncaught 422 and 500 errors ([Gargron](https://github.com/tootsuite/mastodon/pull/11590), [Gargron](https://github.com/tootsuite/mastodon/pull/11811)) +- Fix uncaught parameter missing exceptions and missing error templates ([Gargron](https://github.com/tootsuite/mastodon/pull/11702)) +- Fix encoding error when checking e-mail MX records ([Gargron](https://github.com/tootsuite/mastodon/pull/11696)) +- Fix items in StatusContent render list not all having a key ([ThibG](https://github.com/tootsuite/mastodon/pull/11645)) +- Fix remote and staff-removed statuses leaving media behind for a day ([Gargron](https://github.com/tootsuite/mastodon/pull/11638)) +- Fix CSP needlessly allowing blob URLs in script-src ([ThibG](https://github.com/tootsuite/mastodon/pull/11620)) +- Fix ignoring whole status because of one invalid hashtag ([Gargron](https://github.com/tootsuite/mastodon/pull/11621)) +- Fix hidden statuses losing focus ([ThibG](https://github.com/tootsuite/mastodon/pull/11208)) +- Fix loading bar being obscured by other elements in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11598)) +- Fix multiple issues with replies collection for pages further than self-replies ([ThibG](https://github.com/tootsuite/mastodon/pull/11582)) +- Fix blurhash and autoplay not working on public pages ([Gargron](https://github.com/tootsuite/mastodon/pull/11585)) +- Fix 422 being returned instead of 404 when POSTing to unmatched routes ([Gargron](https://github.com/tootsuite/mastodon/pull/11574), [Gargron](https://github.com/tootsuite/mastodon/pull/11704)) +- Fix client-side resizing of image uploads ([ThibG](https://github.com/tootsuite/mastodon/pull/11570)) +- Fix short number formatting for numbers above million in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11559)) +- Fix ActivityPub and REST API queries setting cookies and preventing caching ([ThibG](https://github.com/tootsuite/mastodon/pull/11539), [ThibG](https://github.com/tootsuite/mastodon/pull/11557), [ThibG](https://github.com/tootsuite/mastodon/pull/11336), [ThibG](https://github.com/tootsuite/mastodon/pull/11331)) +- Fix some emojis in profile metadata labels are not emojified. ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/11534)) +- Fix account search always returning exact match on paginated results ([Gargron](https://github.com/tootsuite/mastodon/pull/11525)) +- Fix acct URIs with IDN domains not being resolved ([Gargron](https://github.com/tootsuite/mastodon/pull/11520)) +- Fix admin dashboard missing latest features ([Gargron](https://github.com/tootsuite/mastodon/pull/11505)) +- Fix jumping of toot date when clicking spoiler button ([ariasuni](https://github.com/tootsuite/mastodon/pull/11449)) +- Fix boost to original audience not working on mobile in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11371)) +- Fix handling of webfinger redirects in ResolveAccountService ([ThibG](https://github.com/tootsuite/mastodon/pull/11279)) +- Fix URLs appearing twice in errors of ActivityPub::DeliveryWorker ([Gargron](https://github.com/tootsuite/mastodon/pull/11231)) +- Fix support for HTTP proxies ([ThibG](https://github.com/tootsuite/mastodon/pull/11245)) +- Fix HTTP requests to IPv6 hosts ([ThibG](https://github.com/tootsuite/mastodon/pull/11240)) +- Fix error in ElasticSearch index import ([mayaeh](https://github.com/tootsuite/mastodon/pull/11192)) +- Fix duplicate account error when seeding development database ([ysksn](https://github.com/tootsuite/mastodon/pull/11366)) +- Fix performance of session clean-up scheduler ([abcang](https://github.com/tootsuite/mastodon/pull/11871)) +- Fix older migrations not running ([zunda](https://github.com/tootsuite/mastodon/pull/11377)) +- Fix URLs counting towards RTL detection ([ahangarha](https://github.com/tootsuite/mastodon/pull/11759)) +- Fix unnecessary status re-rendering in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/11211)) +- Fix http_parser.rb gem not being compiled when no network available ([petabyteboy](https://github.com/tootsuite/mastodon/pull/11444)) +- Fix muted text color not applying to all text ([trwnh](https://github.com/tootsuite/mastodon/pull/11996)) +- Fix follower/following lists resetting on back-navigation in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11986)) +- Fix n+1 query when approving multiple follow requests ([abcang](https://github.com/tootsuite/mastodon/pull/12004)) +- Fix records not being indexed into ElasticSearch sometimes ([Gargron](https://github.com/tootsuite/mastodon/pull/12024)) +- Fix needlessly indexing unsearchable statuses into ElasticSearch ([Gargron](https://github.com/tootsuite/mastodon/pull/12041)) +- Fix new user bootstrapping crashing when to-be-followed accounts are invalid ([ThibG](https://github.com/tootsuite/mastodon/pull/12037)) +- Fix featured hashtag URL being interpreted as media or replies tab ([Gargron](https://github.com/tootsuite/mastodon/pull/12048)) +- Fix account counters being overwritten by parallel writes ([Gargron](https://github.com/tootsuite/mastodon/pull/12045)) + +### Security + +- Fix performance of GIF re-encoding and always strip EXIF data from videos ([Gargron](https://github.com/tootsuite/mastodon/pull/12057)) + ## [2.9.3] - 2019-08-10 ### Added diff --git a/Dockerfile b/Dockerfile index d8c7e0f0c53ef2de2588aac1b8489e51439cafe1..e963674a55eda713d915e846401447e762edfacd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,22 +4,20 @@ FROM ubuntu:18.04 as build-dep SHELL ["bash", "-c"] # Install Node -ENV NODE_VER="8.15.0" +ENV NODE_VER="12.11.1" RUN echo "Etc/UTC" > /etc/localtime && \ apt update && \ - apt -y install wget make gcc g++ python && \ + apt -y install wget python && \ cd ~ && \ - wget https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER.tar.gz && \ - tar xf node-v$NODE_VER.tar.gz && \ - cd node-v$NODE_VER && \ - ./configure --prefix=/opt/node && \ - make -j$(nproc) > /dev/null && \ - make install + wget https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-x64.tar.gz && \ + tar xf node-v$NODE_VER-linux-x64.tar.gz && \ + rm node-v$NODE_VER-linux-x64.tar.gz && \ + mv node-v$NODE_VER-linux-x64 /opt/node # Install jemalloc -ENV JE_VER="5.1.0" +ENV JE_VER="5.2.1" RUN apt update && \ - apt -y install autoconf && \ + apt -y install make autoconf gcc g++ && \ cd ~ && \ wget https://github.com/jemalloc/jemalloc/archive/$JE_VER.tar.gz && \ tar xf $JE_VER.tar.gz && \ @@ -30,7 +28,7 @@ RUN apt update && \ make install_bin install_include install_lib # Install ruby -ENV RUBY_VER="2.6.1" +ENV RUBY_VER="2.6.5" ENV CPPFLAGS="-I/opt/jemalloc/include" ENV LDFLAGS="-L/opt/jemalloc/lib/" RUN apt update && \ diff --git a/Gemfile b/Gemfile index b72f550eb9ea3a32144ae7e9e78f30d0751b5fc5..6f1fcb6f104c78520cf9f7fc2117951f073eac77 100644 --- a/Gemfile +++ b/Gemfile @@ -5,17 +5,17 @@ ruby '>= 2.4.0', '< 2.7.0' gem 'pkg-config', '~> 1.3' -gem 'puma', '~> 3.12' +gem 'puma', '~> 4.2' gem 'rails', '~> 5.2.3' gem 'thor', '~> 0.20' gem 'hamlit-rails', '~> 0.2' gem 'pg', '~> 1.1' gem 'makara', '~> 0.4' -gem 'pghero', '~> 2.2' +gem 'pghero', '~> 2.3' gem 'dotenv-rails', '~> 2.7' -gem 'aws-sdk-s3', '~> 1.42', require: false +gem 'aws-sdk-s3', '~> 1.48', require: false gem 'fog-core', '<= 2.1.0' gem 'fog-openstack', '~> 0.3', require: false gem 'paperclip', '~> 6.0' @@ -24,15 +24,15 @@ gem 'streamio-ffmpeg', '~> 3.0' gem 'blurhash', '~> 0.1' gem 'active_model_serializers', '~> 0.10' -gem 'addressable', '~> 2.6' +gem 'addressable', '~> 2.7' gem 'bootsnap', '~> 1.4', require: false gem 'browser' gem 'charlock_holmes', '~> 0.7.6' gem 'iso-639' -gem 'chewy', '~> 5.0' +gem 'chewy', '~> 5.1' gem 'cld3', '~> 3.2.4' -gem 'devise', '~> 4.6' -gem 'devise-two-factor', '~> 3.0' +gem 'devise', '~> 4.7' +gem 'devise-two-factor', '~> 3.1' group :pam_authentication, optional: true do gem 'devise_pam_authenticatable2', '~> 9.2' @@ -43,54 +43,60 @@ gem 'omniauth-cas', '~> 1.1' gem 'omniauth-saml', '~> 1.10' gem 'omniauth', '~> 1.9' -gem 'doorkeeper', '~> 5.1' +gem 'discard', '~> 1.1' +gem 'doorkeeper', '~> 5.2' gem 'fast_blank', '~> 1.0' gem 'fastimage' gem 'goldfinger', '~> 2.1' gem 'hiredis', '~> 0.6' gem 'redis-namespace', '~> 1.5' +gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b' gem 'htmlentities', '~> 4.3' gem 'http', '~> 3.3' gem 'http_accept_language', '~> 2.1' -gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2' +gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true gem 'httplog', '~> 1.3' gem 'idn-ruby', require: 'idn' gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' -gem 'mime-types', '~> 3.2', require: 'mime/types/columnar' +gem 'mime-types', '~> 3.3', require: 'mime/types/columnar' +gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532' gem 'nokogiri', '~> 1.10' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.7' +gem 'oj', '~> 3.9' gem 'ostatus2', '~> 2.0' gem 'ox', '~> 2.11' +gem 'parslet' +gem 'parallel', '~> 1.17' gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' -gem 'pundit', '~> 2.0' +gem 'pundit', '~> 2.1' gem 'premailer-rails' -gem 'rack-attack', '~> 6.0' +gem 'rack-attack', '~> 6.1' gem 'rack-cors', '~> 1.0', require: 'rack/cors' gem 'rails-i18n', '~> 5.1' gem 'rails-settings-cached', '~> 0.6' gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis'] gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'rqrcode', '~> 0.10' -gem 'sanitize', '~> 5.0' +gem 'ruby-progressbar', '~> 1.10' +gem 'sanitize', '~> 5.1' gem 'sidekiq', '~> 5.2' gem 'sidekiq-scheduler', '~> 3.0' gem 'sidekiq-unique-jobs', '~> 6.0' gem 'sidekiq-bulk', '~>0.2.0' -gem 'simple-navigation', '~> 4.0' +gem 'simple-navigation', '~> 4.1' gem 'simple_form', '~> 4.1' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' gem 'stoplight', '~> 2.1.3' gem 'strong_migrations', '~> 0.4' -gem 'tty-command', '~> 0.8', require: false +gem 'tty-command', '~> 0.9', require: false gem 'tty-prompt', '~> 0.19', require: false gem 'twitter-text', '~> 1.14' gem 'tzinfo-data', '~> 1.2019' gem 'webpacker', '~> 4.0' gem 'webpush' -gem 'json-ld', '~> 3.0' +gem 'json-ld', git: 'https://github.com/ruby-rdf/json-ld.git', ref: 'e742697a0906e74e8bb777ef98137bc3955d981d' gem 'json-ld-preloaded', '~> 3.0' gem 'rdf-normalize', '~> 0.3' @@ -108,14 +114,14 @@ group :production, :test do end group :test do - gem 'capybara', '~> 3.24' + gem 'capybara', '~> 3.29' gem 'climate_control', '~> 0.2' - gem 'faker', '~> 1.9' + gem 'faker', '~> 2.5' gem 'microformats', '~> 4.1' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' - gem 'simplecov', '~> 0.16', require: false - gem 'webmock', '~> 3.6' + gem 'simplecov', '~> 0.17', require: false + gem 'webmock', '~> 3.7' gem 'parallel_tests', '~> 2.29' end @@ -128,9 +134,9 @@ group :development do gem 'letter_opener', '~> 1.7' gem 'letter_opener_web', '~> 1.3' gem 'memory_profiler' - gem 'rubocop', '~> 0.71', require: false - gem 'rubocop-rails', '~> 2.0', require: false - gem 'brakeman', '~> 4.5', require: false + gem 'rubocop', '~> 0.74', require: false + gem 'rubocop-rails', '~> 2.3', require: false + gem 'brakeman', '~> 4.6', require: false gem 'bundler-audit', '~> 0.6', require: false gem 'capistrano', '~> 3.11' @@ -148,3 +154,4 @@ group :production do end gem 'concurrent-ruby', require: false +gem 'connection_pool', require: false diff --git a/Gemfile.lock b/Gemfile.lock index bda915dfada1079d7c415b6cca7aae0aa9bfeeff..3c52f378fab1e7dc7ab426457a81e7a2b690660c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,11 @@ +GIT + remote: https://github.com/ianheggie/health_check + revision: 0b799ead604f900ed50685e9b2d469cd2befba5b + ref: 0b799ead604f900ed50685e9b2d469cd2befba5b + specs: + health_check (4.0.0.pre) + rails (>= 4.0) + GIT remote: https://github.com/rtomayko/posix-spawn revision: 58465d2e213991f8afb13b984854a49fcdcc980c @@ -5,13 +13,34 @@ GIT specs: posix-spawn (0.3.13) +GIT + remote: https://github.com/ruby-rdf/json-ld.git + revision: e742697a0906e74e8bb777ef98137bc3955d981d + ref: e742697a0906e74e8bb777ef98137bc3955d981d + specs: + json-ld (3.0.2) + htmlentities (~> 4.3) + json-canonicalization (~> 0.1) + link_header (~> 0.0, >= 0.0.8) + multi_json (~> 1.13) + rack (>= 1.6, < 3.0) + rdf (~> 3.0, >= 3.0.8) + GIT remote: https://github.com/tmm1/http_parser.rb revision: 54b17ba8c7d8d20a16dfc65d1775241833219cf2 ref: 54b17ba8c7d8d20a16dfc65d1775241833219cf2 + submodules: true specs: http_parser.rb (0.6.1) +GIT + remote: https://github.com/witgo/nilsimsa + revision: fd184883048b922b176939f851338d0a4971a532 + ref: fd184883048b922b176939f851338d0a4971a532 + specs: + nilsimsa (1.1.2) + GEM remote: https://rubygems.org/ specs: @@ -38,9 +67,9 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_model_serializers (0.10.9) - actionpack (>= 4.1, < 6) - activemodel (>= 4.1, < 6) + active_model_serializers (0.10.10) + actionpack (>= 4.1, < 6.1) + activemodel (>= 4.1, < 6.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) active_record_query_trace (1.6.2) @@ -62,9 +91,9 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) - airbrussh (1.3.0) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + airbrussh (1.3.4) sshkit (>= 1.6.1, != 1.7.0) annotate (2.7.5) activerecord (>= 3.2, < 7.0) @@ -76,17 +105,17 @@ GEM av (0.9.0) cocaine (~> 0.5.3) aws-eventstream (1.0.3) - aws-partitions (1.175.0) - aws-sdk-core (3.55.0) + aws-partitions (1.207.0) + aws-sdk-core (3.65.1) aws-eventstream (~> 1.0, >= 1.0.2) aws-partitions (~> 1.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.21.0) - aws-sdk-core (~> 3, >= 3.53.0) + aws-sdk-kms (1.24.0) + aws-sdk-core (~> 3, >= 3.61.1) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.42.0) - aws-sdk-core (~> 3, >= 3.53.0) + aws-sdk-s3 (1.48.0) + aws-sdk-core (~> 3, >= 3.61.1) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) aws-sigv4 (1.1.0) @@ -101,19 +130,19 @@ GEM debug_inspector (>= 0.0.1) blurhash (0.1.3) ffi (~> 1.10.0) - bootsnap (1.4.4) + bootsnap (1.4.5) msgpack (~> 1.0) - brakeman (4.5.1) - browser (2.5.3) + brakeman (4.6.1) + browser (2.6.1) builder (3.2.3) - bullet (6.0.0) + bullet (6.0.2) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) bundler-audit (0.6.1) bundler (>= 1.2.0, < 3) thor (~> 0.18) byebug (11.0.0) - capistrano (3.11.0) + capistrano (3.11.2) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -129,7 +158,7 @@ GEM sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (3.24.0) + capybara (3.29.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) @@ -140,7 +169,7 @@ GEM case_transform (0.2) activesupport charlock_holmes (0.7.6) - chewy (5.0.0) + chewy (5.1.0) activesupport (>= 4.0) elasticsearch (>= 2.0.0) elasticsearch-dsl @@ -156,64 +185,67 @@ GEM crack (0.4.3) safe_yaml (~> 1.0.0) crass (1.0.4) - css_parser (1.6.0) + css_parser (1.7.0) addressable debug_inspector (0.0.3) - derailed_benchmarks (1.3.5) + derailed_benchmarks (1.4.0) benchmark-ips (~> 2) get_process_mem (~> 0) heapy (~> 0) memory_profiler (~> 0) rack (>= 1) rake (> 10, < 13) + ruby-statistics (>= 2.1) thor (~> 0.19) - devise (4.6.2) + devise (4.7.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 6.0) + railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-two-factor (3.0.3) - activesupport (< 5.3) + devise-two-factor (3.1.0) + activesupport (< 6.1) attr_encrypted (>= 1.3, < 4, != 2) devise (~> 4.0) - railties (< 5.3) + railties (< 6.1) rotp (~> 2.0) devise_pam_authenticatable2 (9.2.0) devise (>= 4.0.0) rpam2 (~> 4.0) diff-lcs (1.3) - docile (1.3.0) + discard (1.1.0) + activerecord (>= 4.2, < 7) + docile (1.3.2) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) - doorkeeper (5.1.0) + doorkeeper (5.2.1) railties (>= 5) - dotenv (2.7.2) - dotenv-rails (2.7.2) - dotenv (= 2.7.2) + dotenv (2.7.5) + dotenv-rails (2.7.5) + dotenv (= 2.7.5) railties (>= 3.2, < 6.1) - elasticsearch (6.0.2) - elasticsearch-api (= 6.0.2) - elasticsearch-transport (= 6.0.2) - elasticsearch-api (6.0.2) + elasticsearch (7.3.0) + elasticsearch-api (= 7.3.0) + elasticsearch-transport (= 7.3.0) + elasticsearch-api (7.3.0) multi_json - elasticsearch-dsl (0.1.5) - elasticsearch-transport (6.0.2) + elasticsearch-dsl (0.1.8) + elasticsearch-transport (7.3.0) faraday multi_json encryptor (3.0.0) - equatable (0.5.0) + equatable (0.6.1) erubi (1.8.0) et-orbi (1.1.6) tzinfo excon (0.62.0) fabrication (2.20.2) - faker (1.9.3) - i18n (>= 0.7) - faraday (0.15.0) + faker (2.5.0) + i18n (~> 1.6.0) + faraday (0.15.4) multipart-post (>= 1.2, < 3) fast_blank (1.0.0) - fastimage (2.1.5) + fastimage (2.1.7) ffi (1.10.0) fog-core (2.1.0) builder @@ -234,7 +266,8 @@ GEM fuubar (2.4.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - get_process_mem (0.2.3) + get_process_mem (0.2.4) + ffi (~> 1.0) globalid (0.4.2) activesupport (>= 4.2.0) goldfinger (2.1.0) @@ -253,7 +286,7 @@ GEM railties (>= 4.0.1) hamster (3.0.0) concurrent-ruby (~> 1.0) - hashdiff (0.4.0) + hashdiff (1.0.0) hashie (3.6.0) heapy (0.1.4) highline (2.0.1) @@ -269,7 +302,7 @@ GEM domain_name (~> 0.5) http-form_data (2.1.1) http_accept_language (2.1.1) - httplog (1.3.1) + httplog (1.3.2) rack (>= 1.0) rainbow (>= 2.0.0) i18n (1.6.0) @@ -287,17 +320,15 @@ GEM idn-ruby (0.1.0) ipaddress (0.8.3) iso-639 (0.2.8) - jaro_winkler (1.5.2) + jaro_winkler (1.5.3) jmespath (1.4.0) - json (2.1.0) - json-ld (3.0.2) - multi_json (~> 1.12) - rdf (>= 2.2.8, < 4.0) - json-ld-preloaded (3.0.2) + json (2.2.0) + json-canonicalization (0.1.0) + json-ld-preloaded (3.0.4) json-ld (~> 3.0) multi_json (~> 1.12) rdf (~> 3.0) - jsonapi-renderer (0.2.0) + jsonapi-renderer (0.2.2) jwt (2.1.0) kaminari (1.1.1) activesupport (>= 4.1.0) @@ -336,37 +367,37 @@ GEM mimemagic (~> 0.3.2) mario-redis-lock (1.2.1) redis (>= 3.0.5) - memory_profiler (0.9.13) + memory_profiler (0.9.14) method_source (0.9.2) microformats (4.1.0) json (~> 2.1) nokogiri (~> 1.8, >= 1.8.3) - mime-types (3.2.2) + mime-types (3.3) mime-types-data (~> 3.2015) - mime-types-data (3.2018.0812) + mime-types-data (3.2019.0904) mimemagic (0.3.3) - mini_mime (1.0.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.11.3) - msgpack (1.2.10) + minitest (5.12.0) + msgpack (1.3.1) multi_json (1.13.1) - multipart-post (2.0.0) + multipart-post (2.1.1) necromancer (0.5.0) net-ldap (0.16.1) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (5.0.2) - nio4r (2.3.1) - nokogiri (1.10.3) + net-scp (2.0.0) + net-ssh (>= 2.6.5, < 6.0.0) + net-ssh (5.2.0) + nio4r (2.5.1) + nokogiri (1.10.4) mini_portile2 (~> 2.4.0) - nokogumbo (2.0.0) + nokogumbo (2.0.1) nokogiri (~> 1.8, >= 1.8.4) nsa (0.2.7) activesupport (>= 4.2, < 6) concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) - oj (3.7.12) + oj (3.9.1) omniauth (1.9.0) hashie (>= 3.4.6, < 3.7.0) rack (>= 1.6.2, < 3) @@ -393,23 +424,24 @@ GEM av (~> 0.9.0) paperclip (>= 2.5.2) parallel (1.17.0) - parallel_tests (2.29.0) + parallel_tests (2.29.2) parallel - parser (2.6.3.0) + parser (2.6.4.0) ast (~> 2.4.0) - pastel (0.7.2) - equatable (~> 0.5.0) - tty-color (~> 0.4.0) + parslet (1.8.2) + pastel (0.7.3) + equatable (~> 0.6) + tty-color (~> 0.5) pg (1.1.4) - pghero (2.2.1) - activerecord - pkg-config (1.3.7) + pghero (2.3.0) + activerecord (>= 5) + pkg-config (1.3.9) premailer (1.11.1) addressable css_parser (>= 1.6.0) htmlentities (>= 4.0.0) - premailer-rails (1.10.2) - actionmailer (>= 3, < 6) + premailer-rails (1.10.3) + actionmailer (>= 3) premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) pry (0.12.2) @@ -420,13 +452,14 @@ GEM pry (~> 0.10) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (3.1.0) - puma (3.12.1) - pundit (2.0.1) + public_suffix (4.0.1) + puma (4.2.0) + nio4r (~> 2.0) + pundit (2.1.0) activesupport (>= 3.0.0) raabro (1.1.6) rack (2.0.7) - rack-attack (6.0.0) + rack-attack (6.1.0) rack (>= 1.0, < 3) rack-cors (1.0.3) rack-protection (2.0.5) @@ -455,7 +488,7 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.4) + rails-html-sanitizer (1.2.0) loofah (~> 2.2, >= 2.2.2) rails-i18n (5.1.3) i18n (>= 0.7, < 2) @@ -469,13 +502,13 @@ GEM rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) rainbow (3.0.0) - rake (12.3.2) - rdf (3.0.9) + rake (12.3.3) + rdf (3.0.12) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.3.3) rdf (>= 2.2, < 4.0) - redis (4.1.2) + redis (4.1.3) redis-actionpack (5.0.2) actionpack (>= 4.0, < 6) redis-rack (>= 1, < 3) @@ -494,12 +527,12 @@ GEM redis-store (>= 1.2, < 2) redis-store (1.5.0) redis (>= 2.2, < 5) - regexp_parser (1.5.1) + regexp_parser (1.6.0) request_store (1.4.1) rack (>= 1.4) - responders (2.4.1) - actionpack (>= 4.2.0, < 6.0) - railties (>= 4.2.0, < 6.0) + responders (3.0.0) + actionpack (>= 5.0) + railties (>= 5.0) rotp (2.1.2) rpam2 (4.0.2) rqrcode (0.10.1) @@ -524,23 +557,24 @@ GEM rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) rspec-support (3.8.0) - rubocop (0.71.0) + rubocop (0.74.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.6) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rails (2.0.1) + rubocop-rails (2.3.2) rack (>= 1.1) - rubocop (>= 0.70.0) + rubocop (>= 0.72.0) ruby-progressbar (1.10.1) ruby-saml (1.9.0) nokogiri (>= 1.5.10) + ruby-statistics (2.1.1) rufus-scheduler (3.5.2) fugit (~> 1.1, >= 1.1.5) safe_yaml (1.0.5) - sanitize (5.0.0) + sanitize (5.1.0) crass (~> 1.0.2) nokogiri (>= 1.8.0) nokogumbo (~> 2.0) @@ -560,12 +594,12 @@ GEM concurrent-ruby (~> 1.0, >= 1.0.5) sidekiq (>= 4.0, < 7.0) thor (~> 0) - simple-navigation (4.0.5) + simple-navigation (4.1.0) activesupport (>= 2.3.2) simple_form (4.1.0) actionpack (>= 5.0) activemodel (>= 5.0) - simplecov (0.16.1) + simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) @@ -577,7 +611,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sshkit (1.17.0) + sshkit (1.20.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) stackprof (0.2.12) @@ -585,7 +619,7 @@ GEM stoplight (2.1.3) streamio-ffmpeg (3.0.2) multi_json (~> 1.8) - strong_migrations (0.4.0) + strong_migrations (0.4.1) activerecord (>= 5) temple (0.8.1) terminal-table (1.8.0) @@ -595,8 +629,8 @@ GEM thor (0.20.3) thread_safe (0.3.6) tilt (2.0.9) - tty-color (0.4.3) - tty-command (0.8.2) + tty-color (0.5.0) + tty-command (0.9.0) pastel (~> 0.7.0) tty-cursor (0.7.0) tty-prompt (0.19.0) @@ -612,7 +646,7 @@ GEM unf (~> 0.1.0) tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2019.1) + tzinfo-data (1.2019.3) tzinfo (>= 1.0.0) unf (0.1.4) unf_ext @@ -621,7 +655,7 @@ GEM uniform_notifier (1.12.1) warden (1.2.8) rack (>= 2.0.6) - webmock (3.6.0) + webmock (3.7.6) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -645,14 +679,14 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.10) active_record_query_trace (~> 1.6) - addressable (~> 2.6) + addressable (~> 2.7) annotate (~> 2.7) - aws-sdk-s3 (~> 1.42) + aws-sdk-s3 (~> 1.48) better_errors (~> 2.5) binding_of_caller (~> 0.7) blurhash (~> 0.1) bootsnap (~> 1.4) - brakeman (~> 4.5) + brakeman (~> 4.6) browser bullet (~> 6.0) bundler-audit (~> 0.6) @@ -660,20 +694,22 @@ DEPENDENCIES capistrano-rails (~> 1.4) capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) - capybara (~> 3.24) + capybara (~> 3.29) charlock_holmes (~> 0.7.6) - chewy (~> 5.0) + chewy (~> 5.1) cld3 (~> 3.2.4) climate_control (~> 0.2) concurrent-ruby + connection_pool derailed_benchmarks - devise (~> 4.6) - devise-two-factor (~> 3.0) + devise (~> 4.7) + devise-two-factor (~> 3.1) devise_pam_authenticatable2 (~> 9.2) - doorkeeper (~> 5.1) + discard (~> 1.1) + doorkeeper (~> 5.2) dotenv-rails (~> 2.7) fabrication (~> 2.20) - faker (~> 1.9) + faker (~> 2.5) fast_blank (~> 1.0) fastimage fog-core (<= 2.1.0) @@ -681,6 +717,7 @@ DEPENDENCIES fuubar (~> 2.4) goldfinger (~> 2.1) hamlit-rails (~> 0.2) + health_check! hiredis (~> 0.6) htmlentities (~> 4.3) http (~> 3.3) @@ -690,7 +727,7 @@ DEPENDENCIES i18n-tasks (~> 0.9) idn-ruby iso-639 - json-ld (~> 3.0) + json-ld! json-ld-preloaded (~> 3.0) kaminari (~> 1.1) letter_opener (~> 1.7) @@ -701,11 +738,12 @@ DEPENDENCIES mario-redis-lock (~> 1.2) memory_profiler microformats (~> 4.1) - mime-types (~> 3.2) + mime-types (~> 3.3) net-ldap (~> 0.10) + nilsimsa! nokogiri (~> 1.10) nsa (~> 0.2) - oj (~> 3.7) + oj (~> 3.9) omniauth (~> 1.9) omniauth-cas (~> 1.1) omniauth-saml (~> 1.10) @@ -713,18 +751,20 @@ DEPENDENCIES ox (~> 2.11) paperclip (~> 6.0) paperclip-av-transcoder (~> 0.6) + parallel (~> 1.17) parallel_tests (~> 2.29) + parslet pg (~> 1.1) - pghero (~> 2.2) + pghero (~> 2.3) pkg-config (~> 1.3) posix-spawn! premailer-rails private_address_check (~> 0.5) pry-byebug (~> 3.7) pry-rails (~> 0.3) - puma (~> 3.12) - pundit (~> 2.0) - rack-attack (~> 6.0) + puma (~> 4.2) + pundit (~> 2.1) + rack-attack (~> 6.1) rack-cors (~> 1.0) rails (~> 5.2.3) rails-controller-testing (~> 1.0) @@ -737,32 +777,33 @@ DEPENDENCIES rqrcode (~> 0.10) rspec-rails (~> 3.8) rspec-sidekiq (~> 3.0) - rubocop (~> 0.71) - rubocop-rails (~> 2.0) - sanitize (~> 5.0) + rubocop (~> 0.74) + rubocop-rails (~> 2.3) + ruby-progressbar (~> 1.10) + sanitize (~> 5.1) sidekiq (~> 5.2) sidekiq-bulk (~> 0.2.0) sidekiq-scheduler (~> 3.0) sidekiq-unique-jobs (~> 6.0) - simple-navigation (~> 4.0) + simple-navigation (~> 4.1) simple_form (~> 4.1) - simplecov (~> 0.16) + simplecov (~> 0.17) sprockets-rails (~> 3.2) stackprof stoplight (~> 2.1.3) streamio-ffmpeg (~> 3.0) strong_migrations (~> 0.4) thor (~> 0.20) - tty-command (~> 0.8) + tty-command (~> 0.9) tty-prompt (~> 0.19) twitter-text (~> 1.14) tzinfo-data (~> 1.2019) - webmock (~> 3.6) + webmock (~> 3.7) webpacker (~> 4.0) webpush RUBY VERSION - ruby 2.6.1p33 + ruby 2.6.5p114 BUNDLED WITH 1.17.3 diff --git a/README.md b/README.md index 2d18a4ee22aa06d717c752e3af55c5d8eea0d869..d50c1b3bc0fd8db8e56d063f9c0cd85186d881ae 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Private posts, locked accounts, phrase filtering, muting, blocking and all sorts **OAuth2 and a straightforward REST API** -Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choice! +Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Streaming APIs, resulting in a rich app ecosystem with a lot of choices! ## Deployment diff --git a/app.json b/app.json index 09adaac2c9fda1d1e21d56b17d5b728facf88232..211f17d812e221267284367ee30ce37b0efffe63 100644 --- a/app.json +++ b/app.json @@ -13,15 +13,6 @@ "description": "The domain that your Mastodon instance will run on (this can be appname.herokuapp.com or a custom domain)", "required": true }, - "LOCAL_HTTPS": { - "description": "Will your domain support HTTPS? (Automatic for herokuapp, requires manual configuration for custom domains)", - "value": "false", - "required": true - }, - "PAPERCLIP_SECRET": { - "description": "The secret key for storing media files", - "generator": "secret" - }, "SECRET_KEY_BASE": { "description": "The secret key base", "generator": "secret" diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb new file mode 100644 index 0000000000000000000000000000000000000000..b814e009e5f7792e73d56852f84f8c8d79af57a9 --- /dev/null +++ b/app/chewy/accounts_index.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class AccountsIndex < Chewy::Index + settings index: { refresh_interval: '5m' }, analysis: { + analyzer: { + content: { + tokenizer: 'whitespace', + filter: %w(lowercase asciifolding cjk_width), + }, + + edge_ngram: { + tokenizer: 'edge_ngram', + filter: %w(lowercase asciifolding cjk_width), + }, + }, + + tokenizer: { + edge_ngram: { + type: 'edge_ngram', + min_gram: 1, + max_gram: 15, + }, + }, + } + + define_type ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } do + root date_detection: false do + field :id, type: 'long' + + field :display_name, type: 'text', analyzer: 'content' do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' + end + + field :acct, type: 'text', analyzer: 'content', value: ->(account) { [account.username, account.domain].compact.join('@') } do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' + end + + field :following_count, type: 'long', value: ->(account) { account.following.local.count } + field :followers_count, type: 'long', value: ->(account) { account.followers.local.count } + field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at } + end + end +end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index f5983a5a5f489d119c373e9c32e814ec2afa633c..f5735421c7c90bccc616cacbbd89b312eeb4629c 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -31,19 +31,19 @@ class StatusesIndex < Chewy::Index }, } - define_type ::Status.unscoped.without_reblogs.includes(:media_attachments) do + define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments), delete_if: ->(status) { status.searchable_by.empty? } do crutch :mentions do |collection| - data = ::Mention.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id) + data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } end crutch :favourites do |collection| - data = ::Favourite.where(status_id: collection.map(&:id)).pluck(:status_id, :account_id) + data = ::Favourite.where(status_id: collection.map(&:id)).where(account: Account.local).pluck(:status_id, :account_id) data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } end crutch :reblogs do |collection| - data = ::Status.where(reblog_of_id: collection.map(&:id)).pluck(:reblog_of_id, :account_id) + data = ::Status.where(reblog_of_id: collection.map(&:id)).where(account: Account.local).pluck(:reblog_of_id, :account_id) data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } end @@ -51,7 +51,7 @@ class StatusesIndex < Chewy::Index field :id, type: 'long' field :account_id, type: 'long' - field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status_preloadable_poll.options : []).join("\n\n") } do + field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do field :stemmed, type: 'text', analyzer: 'content' end diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb new file mode 100644 index 0000000000000000000000000000000000000000..300fc128f63f0f4684af335d3a01142712cab477 --- /dev/null +++ b/app/chewy/tags_index.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class TagsIndex < Chewy::Index + settings index: { refresh_interval: '15m' }, analysis: { + analyzer: { + content: { + tokenizer: 'keyword', + filter: %w(lowercase asciifolding cjk_width), + }, + + edge_ngram: { + tokenizer: 'edge_ngram', + filter: %w(lowercase asciifolding cjk_width), + }, + }, + + tokenizer: { + edge_ngram: { + type: 'edge_ngram', + min_gram: 2, + max_gram: 15, + }, + }, + } + + define_type ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } do + root date_detection: false do + field :name, type: 'text', analyzer: 'content' do + field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' + end + + field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } + field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day[:accounts].to_i } } + field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } + end + end +end diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 9f608a851c6467df8f26d59a7dc6145b79f26b04..abd1ec0cb647296b08ee042e3c89a8cfe0d7946c 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -3,20 +3,46 @@ class AboutController < ApplicationController layout 'public' - before_action :set_instance_presenter, only: [:show, :more, :terms] + before_action :require_open_federation!, only: [:show, :more] + before_action :set_body_classes, only: :show + before_action :set_instance_presenter + before_action :set_expires_in, only: [:show, :more, :terms] - skip_before_action :check_user_permissions, only: [:more, :terms] + skip_before_action :require_functional!, only: [:more, :terms] - def show - @hide_navbar = true - end + def show; end + + def more + flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor] - def more; end + toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description) + + @contents = toc_generator.html + @table_of_contents = toc_generator.toc + @blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? + end def terms; end + helper_method :display_blocks? + helper_method :display_blocks_rationale? + helper_method :public_fetch_mode? + helper_method :new_user + private + def require_open_federation! + not_found if whitelist_mode? + end + + def display_blocks? + Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?) + end + + def display_blocks_rationale? + Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?) + end + def new_user User.new.tap do |user| user.build_account @@ -24,9 +50,15 @@ class AboutController < ApplicationController end end - helper_method :new_user - def set_instance_presenter @instance_presenter = InstancePresenter.new end + + def set_body_classes + @hide_navbar = true + end + + def set_expires_in + expires_in 0, public: true + end end diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index f25a997744a3859577e6e73b0936ed48b8c0e93a..f6c599b8624de4c465cd3224ccaef1ea7428c462 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -4,17 +4,22 @@ class AccountsController < ApplicationController PAGE_SIZE = 20 include AccountControllerConcern + include SignatureAuthentication before_action :set_cache_headers + before_action :set_body_classes + + skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format) } + skip_before_action :require_functional! def show respond_to do |format| format.html do - mark_cacheable! unless user_signed_in? + expires_in 0, public: true unless user_signed_in? - @body_classes = 'with-modals' @pinned_statuses = [] @endorsed_accounts = @account.endorsed_accounts.to_a.sample(4) + @featured_hashtags = @account.featured_tags.order(statuses_count: :desc) if current_account && @account.blocking?(current_account) @statuses = [] @@ -24,6 +29,7 @@ class AccountsController < ApplicationController @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses? @statuses = filtered_status_page(params) @statuses = cache_collection(@statuses, Status) + @rss_url = rss_url unless @statuses.empty? @older_url = older_url if @statuses.last.id > filtered_statuses.last.id @@ -31,30 +37,27 @@ class AccountsController < ApplicationController end end - format.atom do - mark_cacheable! - - @entries = @account.stream_entries.where(hidden: false).with_includes.without_local_only.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]) - render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? })) - end - format.rss do - mark_cacheable! + expires_in 1.minute, public: true - @statuses = cache_collection(default_statuses.without_local_only.without_reblogs.without_replies.limit(PAGE_SIZE), Status) - render xml: RSS::AccountSerializer.render(@account, @statuses) + @statuses = filtered_statuses.without_reblogs.without_local_only.without_replies.limit(PAGE_SIZE) + @statuses = cache_collection(@statuses, Status) + render xml: RSS::AccountSerializer.render(@account, @statuses, params[:tag]) end format.json do - render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do - ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter) - end + expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?) + render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to end end end private + def set_body_classes + @body_classes = 'with-modals' + end + def show_pinned_statuses? [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none? end @@ -101,6 +104,14 @@ class AccountsController < ApplicationController params[:username] end + def rss_url + if tag_requested? + short_account_tag_url(@account, params[:tag], format: 'rss') + else + short_account_url(@account, format: 'rss') + end + end + def older_url pagination_url(max_id: @statuses.last.id) end @@ -122,15 +133,15 @@ class AccountsController < ApplicationController end def media_requested? - request.path.ends_with?('/media') + request.path.ends_with?('/media') && !tag_requested? end def replies_requested? - request.path.ends_with?('/with_replies') + request.path.ends_with?('/with_replies') && !tag_requested? end def tag_requested? - request.path.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) + request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) end def filtered_status_page(params) @@ -140,4 +151,12 @@ class AccountsController < ApplicationController filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a end end + + def restrict_fields_to + if signed_request_account.present? || public_fetch_mode? + # Return all fields + else + %i(id type preferred_username inbox public_key endpoints) + end + end end diff --git a/app/controllers/activitypub/base_controller.rb b/app/controllers/activitypub/base_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..0c2591e9743ee534892adbbd8cc2329e5d1fd180 --- /dev/null +++ b/app/controllers/activitypub/base_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class ActivityPub::BaseController < Api::BaseController + skip_before_action :require_authenticated_user! + + private + + def set_cache_headers + response.headers['Vary'] = 'Signature' if authorized_fetch_mode? + end +end diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb index 012c3c53885846e6ca5d33387e2c6dc0d1926285..910fefb1c7ae3161650b13ebc370d71490ae4ded 100644 --- a/app/controllers/activitypub/collections_controller.rb +++ b/app/controllers/activitypub/collections_controller.rb @@ -1,30 +1,21 @@ # frozen_string_literal: true -class ActivityPub::CollectionsController < Api::BaseController +class ActivityPub::CollectionsController < ActivityPub::BaseController include SignatureVerification + include AccountOwnedConcern - before_action :set_account + before_action :require_signature!, if: :authorized_fetch_mode? before_action :set_size before_action :set_statuses before_action :set_cache_headers def show - render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do - ActiveModelSerializers::SerializableResource.new( - collection_presenter, - serializer: ActivityPub::CollectionSerializer, - adapter: ActivityPub::Adapter, - skip_activities: true - ) - end + expires_in 3.minutes, public: public_fetch_mode? + render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true end private - def set_account - @account = Account.find_local!(params[:account_username]) - end - def set_statuses @statuses = scope_for_collection @statuses = cache_collection(@statuses, Status) @@ -42,9 +33,9 @@ class ActivityPub::CollectionsController < Api::BaseController def scope_for_collection case params[:id] when 'featured' - @account.statuses.permitted_for(@account, signed_request_account).tap do |scope| - scope.merge!(@account.pinned_statuses) - end + return Status.none if @account.blocking?(signed_request_account) + + @account.pinned_statuses else raise ActiveRecord::RecordNotFound end diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb index a0b7532c2e0ccedd25ba86504d635eea4ec519ea..bcfc1e6d4234ed4646b3e25dab3f5e1b38303769 100644 --- a/app/controllers/activitypub/inboxes_controller.rb +++ b/app/controllers/activitypub/inboxes_controller.rb @@ -1,40 +1,44 @@ # frozen_string_literal: true -class ActivityPub::InboxesController < Api::BaseController +class ActivityPub::InboxesController < ActivityPub::BaseController include SignatureVerification include JsonLdHelper + include AccountOwnedConcern - before_action :set_account + before_action :skip_unknown_actor_delete + before_action :require_signature! def create - if unknown_deleted_account? - head 202 - elsif signed_request_account - upgrade_account - process_payload - head 202 - else - render plain: signature_verification_failure_reason, status: 401 - end + upgrade_account + process_payload + head 202 end private + def skip_unknown_actor_delete + head 202 if unknown_deleted_account? + end + def unknown_deleted_account? json = Oj.load(body, mode: :strict) - json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? + json.is_a?(Hash) && json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists? rescue Oj::ParseError false end - def set_account - @account = Account.find_local!(params[:account_username]) if params[:account_username] + def account_required? + params[:account_username].present? end def body return @body if defined?(@body) - @body = request.body.read.force_encoding('UTF-8') + + @body = request.body.read + @body.force_encoding('UTF-8') if @body.present? + request.body.rewind if request.body.respond_to?(:rewind) + @body end @@ -44,7 +48,6 @@ class ActivityPub::InboxesController < Api::BaseController ResolveAccountWorker.perform_async(signed_request_account.acct) end - Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed? DeliveryFailureTracker.track_inverse_success!(signed_request_account) end diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb index 5147afbf7822f8fb7651dc0b97081042a39a6a53..891756b7e6a8c7c5c2fd58e905978c5a47c079b6 100644 --- a/app/controllers/activitypub/outboxes_controller.rb +++ b/app/controllers/activitypub/outboxes_controller.rb @@ -1,26 +1,22 @@ # frozen_string_literal: true -class ActivityPub::OutboxesController < Api::BaseController +class ActivityPub::OutboxesController < ActivityPub::BaseController LIMIT = 20 include SignatureVerification + include AccountOwnedConcern - before_action :set_account + before_action :require_signature!, if: :authorized_fetch_mode? before_action :set_statuses before_action :set_cache_headers def show - expires_in 1.minute, public: true unless page_requested? - + expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' end private - def set_account - @account = Account.find_local!(params[:account_username]) - end - def outbox_presenter if page_requested? ActivityPub::CollectionPresenter.new( diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..c62061555bb6ffb1fd4211fd1be71bdb937fe229 --- /dev/null +++ b/app/controllers/activitypub/replies_controller.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +class ActivityPub::RepliesController < ActivityPub::BaseController + include SignatureAuthentication + include Authorization + include AccountOwnedConcern + + DESCENDANTS_LIMIT = 60 + + before_action :require_signature!, if: :authorized_fetch_mode? + before_action :set_status + before_action :set_cache_headers + before_action :set_replies + + def index + expires_in 0, public: public_fetch_mode? + render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true + end + + private + + def set_status + @status = @account.statuses.find(params[:status_id]) + authorize @status, :show? + rescue Mastodon::NotPermittedError + raise ActiveRecord::RecordNotFound + end + + def set_replies + @replies = page_params[:only_other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses + @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted]) + @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) + end + + def replies_collection_presenter + page = ActivityPub::CollectionPresenter.new( + id: account_status_replies_url(@account, @status, page_params), + type: :unordered, + part_of: account_status_replies_url(@account, @status), + next: next_page, + items: @replies.map { |status| status.local ? status : status.uri } + ) + + return page if page_requested? + + ActivityPub::CollectionPresenter.new( + id: account_status_replies_url(@account, @status), + type: :unordered, + first: page + ) + end + + def page_requested? + params[:page] == 'true' + end + + def next_page + only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT) + account_status_replies_url( + @account, + @status, + page: true, + min_id: only_other_accounts && !page_params[:only_other_accounts] ? nil : @replies&.last&.id, + only_other_accounts: only_other_accounts + ) + end + + def page_params + params_slice(:only_other_accounts, :min_id).merge(page: true) + end +end diff --git a/app/controllers/admin/account_actions_controller.rb b/app/controllers/admin/account_actions_controller.rb index a2cea461ee1950e2c971e3455378380407221a50..ea56fa0ac72295bea1a6c177a95af12cb88c5ad8 100644 --- a/app/controllers/admin/account_actions_controller.rb +++ b/app/controllers/admin/account_actions_controller.rb @@ -5,7 +5,7 @@ module Admin before_action :set_account def new - @account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true) + @account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true, include_statuses: true) @warning_presets = AccountWarningPreset.all end @@ -30,7 +30,7 @@ module Admin end def resource_params - params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification) + params.require(:admin_account_action).permit(:type, :report_id, :warning_preset_id, :text, :send_email_notification, :include_statuses) end end end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 0c7760d779932c83fe7ec18f9dbbc83f5f6602b0..68b6352f886faa839ac8d796a03a759a22fcb960 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,8 +2,8 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject] - before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] + before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject] + before_action :require_remote_account!, only: [:redownload] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] def index @@ -19,18 +19,6 @@ module Admin @warnings = @account.targeted_account_warnings.latest.custom end - def subscribe - authorize @account, :subscribe? - Pubsubhubbub::SubscribeWorker.perform_async(@account.id) - redirect_to admin_account_path(@account.id) - end - - def unsubscribe - authorize @account, :unsubscribe? - Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id) - redirect_to admin_account_path(@account.id) - end - def memorialize authorize @account, :memorialize? @account.memorialize! @@ -53,7 +41,7 @@ module Admin def reject authorize @account.user, :reject? - SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true) + SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false) redirect_to admin_pending_accounts_path end diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index f77699166769fd07cd21815444b9093adcc2bb3f..2af90f0513ce7b59a15f54210b22bcc6bb19a66d 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -2,19 +2,20 @@ module Admin class CustomEmojisController < BaseController - before_action :set_custom_emoji, except: [:index, :new, :create] - before_action :set_filter_params - include ObfuscateFilename + obfuscate_filename [:custom_emoji, :image] def index authorize :custom_emoji, :index? + @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]) + @form = Form::CustomEmojiBatch.new end def new authorize :custom_emoji, :create? + @custom_emoji = CustomEmoji.new end @@ -31,69 +32,17 @@ module Admin end end - def update - authorize @custom_emoji, :update? - - if @custom_emoji.update(resource_params) - log_action :update, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.updated_msg') - else - flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg') - end - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - - def destroy - authorize @custom_emoji, :destroy? - @custom_emoji.destroy! - log_action :destroy, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg') - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - - def copy - authorize @custom_emoji, :copy? - - emoji = CustomEmoji.find_or_initialize_by(domain: nil, - shortcode: @custom_emoji.shortcode) - emoji.image = @custom_emoji.image - - if emoji.save - log_action :create, emoji - flash[:notice] = I18n.t('admin.custom_emojis.copied_msg') - else - flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg') - end - - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - - def enable - authorize @custom_emoji, :enable? - @custom_emoji.update!(disabled: false) - log_action :enable, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg') - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - - def disable - authorize @custom_emoji, :disable? - @custom_emoji.update!(disabled: true) - log_action :disable, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg') - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) + def batch + @form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_custom_emojis_path(filter_params) end private - def set_custom_emoji - @custom_emoji = CustomEmoji.find(params[:id]) - end - - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys - end - def resource_params params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker) end @@ -103,12 +52,29 @@ module Admin end def filter_params - params.permit( - :local, - :remote, - :by_domain, - :shortcode - ) + params.slice(:local, :remote, :by_domain, :shortcode, :page).permit(:local, :remote, :by_domain, :shortcode, :page) + end + + def action_from_button + if params[:update] + 'update' + elsif params[:list] + 'list' + elsif params[:unlist] + 'unlist' + elsif params[:enable] + 'enable' + elsif params[:disable] + 'disable' + elsif params[:copy] + 'copy' + elsif params[:delete] + 'delete' + end + end + + def form_custom_emoji_batch_params + params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: []) end end end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index f23ed15086214372da97698925986669d6891636..7c2951acbb0e25d86501c279e4d8f2e94524fa35 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -5,6 +5,7 @@ module Admin class DashboardController < BaseController def index @users_count = User.count + @pending_users_count = User.pending.count @registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0 @logins_week = Redis.current.pfcount("activity:logins:#{current_week}") @interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0 @@ -19,7 +20,7 @@ module Admin @redis_version = redis_info['redis_version'] @reports_count = Report.unresolved.count @queue_backlog = Sidekiq::Stats.new.enqueued - @recent_users = User.confirmed.recent.includes(:account).limit(4) + @recent_users = User.confirmed.recent.includes(:account).limit(8) @database_size = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size'] @redis_size = redis_info['used_memory'] @ldap_enabled = ENV['LDAP_ENABLED'] == 'true' @@ -27,9 +28,14 @@ module Admin @saml_enabled = ENV['SAML_ENABLED'] == 'true' @pam_enabled = ENV['PAM_ENABLED'] == 'true' @hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' - @trending_hashtags = TrendingTags.get(7) + @trending_hashtags = TrendingTags.get(10, filtered: false) + @pending_tags_count = Tag.pending_review.count + @authorized_fetch = authorized_fetch_mode? + @whitelist_enabled = whitelist_mode? @profile_directory = Setting.profile_directory @timeline_preview = Setting.timeline_preview + @spam_check_enabled = Setting.spam_check_enabled + @trends_enabled = Setting.trends end private @@ -39,7 +45,13 @@ module Admin end def redis_info - @redis_info ||= Redis.current.info + @redis_info ||= begin + if Redis.current.is_a?(Redis::Namespace) + Redis.current.redis.info + else + Redis.current.info + end + end end end end diff --git a/app/controllers/admin/domain_allows_controller.rb b/app/controllers/admin/domain_allows_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..31be1978bbbb104ffc1820419bcce7694a40de10 --- /dev/null +++ b/app/controllers/admin/domain_allows_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Admin::DomainAllowsController < Admin::BaseController + before_action :set_domain_allow, only: [:destroy] + + def new + authorize :domain_allow, :create? + + @domain_allow = DomainAllow.new(domain: params[:_domain]) + end + + def create + authorize :domain_allow, :create? + + @domain_allow = DomainAllow.new(resource_params) + + if @domain_allow.save + log_action :create, @domain_allow + redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.created_msg') + else + render :new + end + end + + def destroy + authorize @domain_allow, :destroy? + UnallowDomainService.new.call(@domain_allow) + redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg') + end + + private + + def set_domain_allow + @domain_allow = DomainAllow.find(params[:id]) + end + + def resource_params + params.require(:domain_allow).permit(:domain) + end +end diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 7129656dabfe5e9674cff9cfd82800a468ec8d45..74a36b79ca3a2106308079e4f2065682445fb1bf 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -2,13 +2,17 @@ module Admin class DomainBlocksController < BaseController - before_action :set_domain_block, only: [:show, :destroy] + before_action :set_domain_block, only: [:show, :destroy, :edit, :update] def new authorize :domain_block, :create? @domain_block = DomainBlock.new(domain: params[:_domain]) end + def edit + authorize :domain_block, :create? + end + def create authorize :domain_block, :create? @@ -35,6 +39,22 @@ module Admin end end + def update + authorize :domain_block, :create? + + @domain_block.update(update_params) + + severity_changed = @domain_block.severity_changed? + + if @domain_block.save + DomainBlockWorker.perform_async(@domain_block.id, severity_changed) + log_action :create, @domain_block + redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg') + else + render :edit + end + end + def show authorize @domain_block, :show? end @@ -52,8 +72,12 @@ module Admin @domain_block = DomainBlock.find(params[:id]) end + def update_params + params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment) + end + def resource_params - params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports) + params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment) end end end diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index 7888e844fb5abc0a2840ca7795e9462870e28650..b47b18f8eb81087769f2521b0298902d42e2259a 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -2,6 +2,10 @@ module Admin class InstancesController < BaseController + before_action :set_domain_block, only: :show + before_action :set_domain_allow, only: :show + before_action :set_instance, only: :show + def index authorize :instance, :index? @@ -11,20 +15,40 @@ module Admin def show authorize :instance, :show? - @instance = Instance.new(Account.by_domain_accounts.find_by(domain: params[:id]) || DomainBlock.find_by!(domain: params[:id])) @following_count = Follow.where(account: Account.where(domain: params[:id])).count @followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count @reports_count = Report.where(target_account: Account.where(domain: params[:id])).count @blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count @available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url) @media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size) - @domain_block = DomainBlock.rule_for(params[:id]) + @private_comment = @domain_block&.private_comment + @public_comment = @domain_block&.public_comment end private + def set_domain_block + @domain_block = DomainBlock.rule_for(params[:id]) + end + + def set_domain_allow + @domain_allow = DomainAllow.rule_for(params[:id]) + end + + def set_instance + resource = Account.by_domain_accounts.find_by(domain: params[:id]) + resource ||= @domain_block + resource ||= @domain_allow + + if resource + @instance = Instance.new(resource) + else + not_found + end + end + def filtered_instances - InstanceFilter.new(filter_params).results + InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results end def paginated_instances diff --git a/app/controllers/admin/relays_controller.rb b/app/controllers/admin/relays_controller.rb index 1b02d3c361a9e77c49c2fc40e2e15fdf52afc05c..6fbb6e0630947a40fedab2a090ecebd1be7c9bdc 100644 --- a/app/controllers/admin/relays_controller.rb +++ b/app/controllers/admin/relays_controller.rb @@ -3,6 +3,7 @@ module Admin class RelaysController < BaseController before_action :set_relay, except: [:index, :new, :create] + before_action :require_signatures_enabled!, only: [:new, :create, :enable] def index authorize :relay, :update? @@ -11,7 +12,7 @@ module Admin def new authorize :relay, :update? - @relay = Relay.new(inbox_url: Relay::PRESET_RELAY) + @relay = Relay.new end def create @@ -54,5 +55,9 @@ module Admin def resource_params params.require(:relay).permit(:inbox_url) end + + def require_signatures_enabled! + redirect_to admin_relays_path, alert: I18n.t('admin.relays.signatures_not_enabled') if authorized_fetch_mode? + end end end diff --git a/app/controllers/admin/report_notes_controller.rb b/app/controllers/admin/report_notes_controller.rb index bcb3f20260dc8046436a692436d111b5f4894b10..b816c5b5d484ec88c4129cad3911aa2cf9d603e2 100644 --- a/app/controllers/admin/report_notes_controller.rb +++ b/app/controllers/admin/report_notes_controller.rb @@ -5,10 +5,10 @@ module Admin before_action :set_report_note, only: [:destroy] def create - authorize ReportNote, :create? + authorize :report_note, :create? @report_note = current_account.report_notes.new(resource_params) - @report = @report_note.report + @report = @report_note.report if @report_note.save if params[:create_and_resolve] @@ -26,9 +26,8 @@ module Admin redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg') else - @report_notes = @report.notes.latest - @report_history = @report.history - @form = Form::StatusBatch.new + @report_notes = (@report.notes.latest + @report.history + @report.target_account.targeted_account_warnings.latest.custom).sort_by(&:created_at) + @form = Form::StatusBatch.new render template: 'admin/reports/show' end diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index e9f4f2cfa3b35a67101d0957ee6a22f1f66f41fc..65341bbfbe74cc4259ce46cbc53145b41a667c14 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -2,43 +2,102 @@ module Admin class TagsController < BaseController - before_action :set_tags, only: :index - before_action :set_tag, except: :index - before_action :set_filter_params + before_action :set_tag, except: [:index, :batch, :approve_all, :reject_all] + before_action :set_usage_by_domain, except: [:index, :batch, :approve_all, :reject_all] + before_action :set_counters, except: [:index, :batch, :approve_all, :reject_all] def index authorize :tag, :index? + + @tags = filtered_tags.page(params[:page]) + @form = Form::TagBatch.new end - def hide - authorize @tag, :hide? - @tag.account_tag_stat.update!(hidden: true) - redirect_to admin_tags_path(@filter_params) + def batch + @form = Form::TagBatch.new(form_tag_batch_params.merge(current_account: current_account, action: action_from_button)) + @form.save + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.accounts.no_account_selected') + ensure + redirect_to admin_tags_path(filter_params) end - def unhide - authorize @tag, :unhide? - @tag.account_tag_stat.update!(hidden: false) - redirect_to admin_tags_path(@filter_params) + def approve_all + Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'approve').save + redirect_to admin_tags_path(filter_params) end - private + def reject_all + Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'reject').save + redirect_to admin_tags_path(filter_params) + end + + def show + authorize @tag, :show? + end + + def update + authorize @tag, :update? - def set_tags - @tags = Tag.discoverable - @tags.merge!(Tag.hidden) if filter_params[:hidden] + if @tag.update(tag_params.merge(reviewed_at: Time.now.utc)) + redirect_to admin_tag_path(@tag.id), notice: I18n.t('admin.tags.updated_msg') + else + render :show + end end + private + def set_tag @tag = Tag.find(params[:id]) end - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys + def set_usage_by_domain + @usage_by_domain = @tag.statuses + .with_public_visibility + .excluding_silenced_accounts + .where(Status.arel_table[:id].gteq(Mastodon::Snowflake.id_at(Time.now.utc.beginning_of_day))) + .joins(:account) + .group('accounts.domain') + .reorder('statuses_count desc') + .pluck('accounts.domain, count(*) AS statuses_count') + end + + def set_counters + @accounts_today = @tag.history.first[:accounts] + @accounts_week = Redis.current.pfcount(*current_week_days.map { |day| "activity:tags:#{@tag.id}:#{day}:accounts" }) + end + + def filtered_tags + TagFilter.new(filter_params).results end def filter_params - params.permit(:hidden) + params.slice(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name).permit(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name) + end + + def tag_params + params.require(:tag).permit(:name, :trendable, :usable, :listable) + end + + def current_week_days + now = Time.now.utc.beginning_of_day.to_date + + (Date.commercial(now.cwyear, now.cweek)..now).map do |date| + date.to_time(:utc).beginning_of_day.to_i + end + end + + def form_tag_batch_params + params.require(:form_tag_batch).permit(:action, tag_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:reject] + 'reject' + end end end end diff --git a/app/controllers/admin/two_factor_authentications_controller.rb b/app/controllers/admin/two_factor_authentications_controller.rb index 2577a4b17fff4b59c93a506e4f0fecca0cbcb4cc..0652c3a7a4a1235ebd43bbb1d8f3ef82cb247537 100644 --- a/app/controllers/admin/two_factor_authentications_controller.rb +++ b/app/controllers/admin/two_factor_authentications_controller.rb @@ -8,6 +8,7 @@ module Admin authorize @user, :disable_2fa? @user.disable_two_factor! log_action :disable_2fa, @user + UserMailer.two_factor_disabled(@user).deliver_later! redirect_to admin_accounts_path end diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index eca558f4216ee1f8e565867a6dfe39ee5420463b..33df75b374b388b8f9bf8cf276cf7574ad5ebd50 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -7,12 +7,15 @@ class Api::BaseController < ApplicationController include RateLimitHeaders skip_before_action :store_current_location - skip_before_action :check_user_permissions + skip_before_action :require_functional! + before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access? before_action :set_cache_headers protect_from_forgery with: :null_session + skip_around_action :set_locale + rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| render json: { error: e.to_s }, status: 422 end @@ -33,6 +36,14 @@ class Api::BaseController < ApplicationController render json: { error: 'This action is not allowed' }, status: 403 end + rescue_from Mastodon::RaceConditionError do + render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 + end + + rescue_from ActionController::ParameterMissing do |e| + render json: { error: e.to_s }, status: 400 + end + def doorkeeper_unauthorized_render_options(error: nil) { json: { error: (error.try(:description) || 'Not authorized') } } end @@ -69,6 +80,10 @@ class Api::BaseController < ApplicationController nil end + def require_authenticated_user! + render json: { error: 'This API requires an authenticated user' }, status: 401 unless current_user + end + def require_user! if !current_user render json: { error: 'This method requires an authenticated user' }, status: 422 @@ -94,4 +109,8 @@ class Api::BaseController < ApplicationController def set_cache_headers response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' end + + def disallow_unauthenticated_api_access? + authorized_fetch_mode? + end end diff --git a/app/controllers/api/proofs_controller.rb b/app/controllers/api/proofs_controller.rb index a84ad2014fe7320135257bdec73943ec55f47952..a98599eee668009f4d1d911fd364535698dedfae 100644 --- a/app/controllers/api/proofs_controller.rb +++ b/app/controllers/api/proofs_controller.rb @@ -1,10 +1,9 @@ # frozen_string_literal: true class Api::ProofsController < Api::BaseController - before_action :set_account + include AccountOwnedConcern + before_action :set_provider - before_action :check_account_approval - before_action :check_account_suspension def index render json: @account, serializer: @provider.serializer_class @@ -16,15 +15,7 @@ class Api::ProofsController < Api::BaseController @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound) end - def set_account - @account = Account.find_local!(params[:username]) - end - - def check_account_approval - not_found if @account.user_pending? - end - - def check_account_suspension - gone if @account.suspended? + def username_param + params[:username] end end diff --git a/app/controllers/api/push_controller.rb b/app/controllers/api/push_controller.rb deleted file mode 100644 index e04d19125b8d3ef67fd614e16216c5ee25539db3..0000000000000000000000000000000000000000 --- a/app/controllers/api/push_controller.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -class Api::PushController < Api::BaseController - include SignatureVerification - - def update - response, status = process_push_request - render plain: response, status: status - end - - private - - def process_push_request - case hub_mode - when 'subscribe' - Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds, verified_domain) - when 'unsubscribe' - Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback) - else - ["Unknown mode: #{hub_mode}", 422] - end - end - - def hub_mode - params['hub.mode'] - end - - def hub_topic - params['hub.topic'] - end - - def hub_callback - params['hub.callback'] - end - - def hub_lease_seconds - params['hub.lease_seconds'] - end - - def hub_secret - params['hub.secret'] - end - - def account_from_topic - if hub_topic.present? && local_domain? && account_feed_path? - Account.find_local(hub_topic_params[:username]) - end - end - - def hub_topic_params - @_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path) - end - - def hub_topic_uri - @_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize - end - - def local_domain? - TagManager.instance.web_domain?(hub_topic_domain) - end - - def verified_domain - return signed_request_account.domain if signed_request_account - end - - def hub_topic_domain - hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '') - end - - def account_feed_path? - hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom' - end -end diff --git a/app/controllers/api/salmon_controller.rb b/app/controllers/api/salmon_controller.rb deleted file mode 100644 index ac5f3268d8c78a28428369c0166f430d71515958..0000000000000000000000000000000000000000 --- a/app/controllers/api/salmon_controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class Api::SalmonController < Api::BaseController - include SignatureVerification - - before_action :set_account - respond_to :txt - - def update - if verify_payload? - process_salmon - head 202 - elsif payload.present? - render plain: signature_verification_failure_reason, status: 401 - else - head 400 - end - end - - private - - def set_account - @account = Account.find(params[:id]) - end - - def payload - @_payload ||= request.body.read - end - - def verify_payload? - payload.present? && VerifySalmonService.new.call(payload) - end - - def process_salmon - SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8')) - end -end diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb deleted file mode 100644 index 89007f3d6efbe6f26c26bb68d66a11f7e576e48d..0000000000000000000000000000000000000000 --- a/app/controllers/api/subscriptions_controller.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -class Api::SubscriptionsController < Api::BaseController - before_action :set_account - respond_to :txt - - def show - if subscription.valid?(params['hub.topic']) - @account.update(subscription_expires_at: future_expires) - render plain: encoded_challenge, status: 200 - else - head 404 - end - end - - def update - if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE']) - ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8')) - end - - head 200 - end - - private - - def subscription - @_subscription ||= @account.subscription( - api_subscription_url(@account.id) - ) - end - - def body - @_body ||= request.body.read - end - - def encoded_challenge - HTMLEntities.new.encode(params['hub.challenge']) - end - - def future_expires - Time.now.utc + lease_seconds_or_default - end - - def lease_seconds_or_default - (params['hub.lease_seconds'] || 1.day).to_i.seconds - end - - def set_account - @account = Account.find(params[:id]) - end -end diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 13cb4caf1203c03b018fa42bf152fbd3a8494b63..333db96186cf67ef2e4cf9046e1d0ec89b56fdb4 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -29,14 +29,13 @@ class Api::V1::Accounts::StatusesController < Api::BaseController def account_statuses statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses - statuses = statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) statuses.merge!(only_media_scope) if truthy_param?(:only_media) statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies) statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs) statuses.merge!(hashtag_scope) if params[:tagged].present? - statuses + statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) end def permitted_account_statuses @@ -58,6 +57,8 @@ class Api::V1::Accounts::StatusesController < Api::BaseController end def pinned_scope + return Status.none if @account.blocking?(current_account) + @account.pinned_statuses end diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index b0c62778e6563abe7894ff52e9493a1a377a89ab..d68d2715f71541ad619afdada6dad212f4a8fbfd 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -12,6 +12,8 @@ class Api::V1::AccountsController < Api::BaseController before_action :check_account_suspension, only: [:show] before_action :check_enabled_registrations, only: [:create] + skip_before_action :require_authenticated_user!, only: :create + respond_to :json def show @@ -31,7 +33,7 @@ class Api::V1::AccountsController < Api::BaseController def follow FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs)) - options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } + options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options) end @@ -76,7 +78,7 @@ class Api::V1::AccountsController < Api::BaseController end def account_params - params.permit(:username, :email, :password, :agreement, :locale) + params.permit(:username, :email, :password, :agreement, :locale, :reason) end def check_enabled_registrations diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb index c306180ca25169a355b980a0237e479e5e7eaa1f..c35ea5ab254ffe77f33f193ec9febb3818fae72b 100644 --- a/app/controllers/api/v1/admin/accounts_controller.rb +++ b/app/controllers/api/v1/admin/accounts_controller.rb @@ -58,7 +58,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController def reject authorize @account.user, :reject? - SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true) + SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false) render json: @account, serializer: REST::Admin::AccountSerializer end diff --git a/app/controllers/api/v1/apps_controller.rb b/app/controllers/api/v1/apps_controller.rb index e9f7a7291c15d9fb896388e5e52e0cfcb4a33bc5..97177547a2b9aec652d50162472e86fe9972d7a8 100644 --- a/app/controllers/api/v1/apps_controller.rb +++ b/app/controllers/api/v1/apps_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Api::V1::AppsController < Api::BaseController + skip_before_action :require_authenticated_user! + def create @app = Doorkeeper::Application.create!(application_options) render json: @app, serializer: REST::ApplicationSerializer diff --git a/app/controllers/api/v1/custom_emojis_controller.rb b/app/controllers/api/v1/custom_emojis_controller.rb index 1bb19a09d33ec4e3581f245154a001d2713b96d6..4e6d5d7c613a65da4203ceb4ce38f4f659fa64a0 100644 --- a/app/controllers/api/v1/custom_emojis_controller.rb +++ b/app/controllers/api/v1/custom_emojis_controller.rb @@ -6,8 +6,7 @@ class Api::V1::CustomEmojisController < Api::BaseController skip_before_action :set_cache_headers def index - render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do - ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer) - end + expires_in 3.minutes, public: true + render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.listed.includes(:category) } end end diff --git a/app/controllers/api/v1/directories_controller.rb b/app/controllers/api/v1/directories_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..c91543e3a3e969610a5b2d0b8a1c55808fcd1869 --- /dev/null +++ b/app/controllers/api/v1/directories_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Api::V1::DirectoriesController < Api::BaseController + before_action :require_enabled! + before_action :set_accounts + + def show + render json: @accounts, each_serializer: REST::AccountSerializer + end + + private + + def require_enabled! + return not_found unless Setting.profile_directory + end + + def set_accounts + @accounts = accounts_scope.offset(params[:offset]).limit(limit_param(DEFAULT_ACCOUNTS_LIMIT)) + end + + def accounts_scope + Account.discoverable.tap do |scope| + scope.merge!(Account.local) if truthy_param?(:local) + scope.merge!(Account.by_recent_status) if params[:order].blank? || params[:order] == 'active' + scope.merge!(Account.order(id: :desc)) if params[:order] == 'new' + scope.merge!(Account.not_excluded_by_account(current_account)) if current_account + scope.merge!(Account.not_domain_blocked_by_account(current_account)) if current_account && !truthy_param?(:local) + end + end +end diff --git a/app/controllers/api/v1/featured_tags/suggestions_controller.rb b/app/controllers/api/v1/featured_tags/suggestions_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..fb27ef88b93a95b7743cf66ac9d4b2ec4b4b6f89 --- /dev/null +++ b/app/controllers/api/v1/featured_tags/suggestions_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index + + before_action :require_user! + before_action :set_most_used_tags, only: :index + + respond_to :json + + def index + render json: @most_used_tags, each_serializer: REST::TagSerializer + end + + private + + def set_most_used_tags + @most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10) + end +end diff --git a/app/controllers/api/v1/featured_tags_controller.rb b/app/controllers/api/v1/featured_tags_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e4e836c9711dd4ab4fb346990d5611a95d5e5690 --- /dev/null +++ b/app/controllers/api/v1/featured_tags_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Api::V1::FeaturedTagsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index + before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index + + before_action :require_user! + before_action :set_featured_tags, only: :index + before_action :set_featured_tag, except: [:index, :create] + + def index + render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer + end + + def create + @featured_tag = current_account.featured_tags.new(featured_tag_params) + @featured_tag.reset_data + @featured_tag.save! + render json: @featured_tag, serializer: REST::FeaturedTagSerializer + end + + def destroy + @featured_tag.destroy! + render_empty + end + + private + + def set_featured_tag + @featured_tag = current_account.featured_tags.find(params[:id]) + end + + def set_featured_tags + @featured_tags = current_account.featured_tags.order(statuses_count: :desc) + end + + def featured_tag_params + params.permit(:name) + end +end diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb index e6888154e22217c23f79fd70d64ebd3c37cbd219..0ee6e531f07a4abacea52ccae938a14fdd92deb1 100644 --- a/app/controllers/api/v1/follow_requests_controller.rb +++ b/app/controllers/api/v1/follow_requests_controller.rb @@ -14,12 +14,12 @@ class Api::V1::FollowRequestsController < Api::BaseController def authorize AuthorizeFollowService.new.call(account, current_account) NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account)) - render_empty + render json: account, serializer: REST::RelationshipSerializer, relationships: relationships end def reject RejectFollowService.new.call(account, current_account) - render_empty + render json: account, serializer: REST::RelationshipSerializer, relationships: relationships end private @@ -28,6 +28,10 @@ class Api::V1::FollowRequestsController < Api::BaseController Account.find(params[:id]) end + def relationships(**options) + AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, options) + end + def load_accounts default_accounts.merge(paginated_follow_requests).to_a end diff --git a/app/controllers/api/v1/follows_controller.rb b/app/controllers/api/v1/follows_controller.rb deleted file mode 100644 index 5420c053367ec68d2998f8af98effb671b03dcc3..0000000000000000000000000000000000000000 --- a/app/controllers/api/v1/follows_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::FollowsController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'write:follows' } - before_action :require_user! - - respond_to :json - - def create - raise ActiveRecord::RecordNotFound if follow_params[:uri].blank? - - @account = FollowService.new.call(current_user.account, target_uri).try(:target_account) - - if @account.nil? - username, domain = target_uri.split('@') - @account = Account.find_remote!(username, domain) - end - - render json: @account, serializer: REST::AccountSerializer - end - - private - - def target_uri - follow_params[:uri].strip.gsub(/\A@/, '') - end - - def follow_params - params.permit(:uri) - end -end diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb index 09edfe365b07df3c057e3ca63f5dcb2a0c973ac3..b30e8464c3eb5d72563b9c42480dae10ac07bf96 100644 --- a/app/controllers/api/v1/instances/activity_controller.rb +++ b/app/controllers/api/v1/instances/activity_controller.rb @@ -2,12 +2,15 @@ class Api::V1::Instances::ActivityController < Api::BaseController before_action :require_enabled_api! + skip_before_action :set_cache_headers + skip_before_action :require_authenticated_user!, unless: :whitelist_mode? respond_to :json def show - render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity } + expires_in 1.day, public: true + render_with_cache json: :activity, expires_in: 1.day end private @@ -32,6 +35,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController end def require_enabled_api! - head 404 unless Setting.activity_api_enabled + head 404 unless Setting.activity_api_enabled && !whitelist_mode? end end diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb index a8891d126bfd379895bc9eeea87b691d7ba926cc..cc00d8a6bc3ae1b08d2b4093a853f27414806b73 100644 --- a/app/controllers/api/v1/instances/peers_controller.rb +++ b/app/controllers/api/v1/instances/peers_controller.rb @@ -2,17 +2,20 @@ class Api::V1::Instances::PeersController < Api::BaseController before_action :require_enabled_api! + skip_before_action :set_cache_headers + skip_before_action :require_authenticated_user!, unless: :whitelist_mode? respond_to :json def index - render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains } + expires_in 1.day, public: true + render_with_cache(expires_in: 1.day) { Account.remote.domains } end private def require_enabled_api! - head 404 unless Setting.peers_api_enabled + head 404 unless Setting.peers_api_enabled && !whitelist_mode? end end diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb index 8c83a180149cab1a7b85e62a1c2ce35fdd798fd1..c323b60b4292c89fab207db2882409f696f2a9af 100644 --- a/app/controllers/api/v1/instances_controller.rb +++ b/app/controllers/api/v1/instances_controller.rb @@ -2,11 +2,12 @@ class Api::V1::InstancesController < Api::BaseController respond_to :json + skip_before_action :set_cache_headers + skip_before_action :require_authenticated_user!, unless: :whitelist_mode? def show - render_cached_json('api:v1:instances', expires_in: 5.minutes) do - ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer) - end + expires_in 3.minutes, public: true + render_with_cache json: {}, serializer: REST::InstanceSerializer, root: 'instance' end end diff --git a/app/controllers/api/v1/markers_controller.rb b/app/controllers/api/v1/markers_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..28c2ec79168478bbb7b441a00fb1924f620a2348 --- /dev/null +++ b/app/controllers/api/v1/markers_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class Api::V1::MarkersController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:index] + before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, except: [:index] + + before_action :require_user! + + def index + @markers = current_user.markers.where(timeline: Array(params[:timeline])).each_with_object({}) { |marker, h| h[marker.timeline] = marker } + render json: serialize_map(@markers) + end + + def create + Marker.transaction do + @markers = {} + + resource_params.each_pair do |timeline, timeline_params| + @markers[timeline] = current_user.markers.find_or_initialize_by(timeline: timeline) + @markers[timeline].update!(timeline_params) + end + end + + render json: serialize_map(@markers) + rescue ActiveRecord::StaleObjectError + render json: { error: 'Conflict during update, please try again' }, status: 409 + end + + private + + def serialize_map(map) + serialized = {} + + map.each_pair do |key, value| + serialized[key] = ActiveModelSerializers::SerializableResource.new(value, serializer: REST::MarkerSerializer).as_json + end + + Oj.dump(serialized) + end + + def resource_params + params.slice(*Marker::TIMELINES).permit(*Marker::TIMELINES.map { |timeline| { timeline.to_sym => [:last_read_id] } }) + end +end diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index e182a9c6cf6245fb052b435416e2d667ba212390..1b0b4b05b2e72de95fd3b4836261d1aad25eda04 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -21,7 +21,7 @@ class Api::V1::ReportsController < Api::BaseController private def reported_status_ids - reported_account.statuses.find(status_ids).pluck(:id) + reported_account.statuses.with_discarded.find(status_ids).pluck(:id) end def status_ids diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb deleted file mode 100644 index 6131cbbb69fb1250ea102112ac51d0ebe04d200a..0000000000000000000000000000000000000000 --- a/app/controllers/api/v1/search_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::SearchController < Api::BaseController - include Authorization - - RESULTS_LIMIT = 20 - - before_action -> { doorkeeper_authorize! :read, :'read:search' } - before_action :require_user! - - respond_to :json - - def index - @search = Search.new(search_results) - render json: @search, serializer: REST::SearchSerializer - end - - private - - def search_results - SearchService.new.call( - params[:q], - current_account, - limit_param(RESULTS_LIMIT), - search_params.merge(resolve: truthy_param?(:resolve)) - ) - end - - def search_params - params.permit(:type, :offset, :min_id, :max_id, :account_id) - end -end diff --git a/app/controllers/api/v1/statuses/reblogs_controller.rb b/app/controllers/api/v1/statuses/reblogs_controller.rb index ed4f551003d670061f58f75382af1fb6aa8542d6..42381a37fdbe8ab35d38dc24932830372c4168c8 100644 --- a/app/controllers/api/v1/statuses/reblogs_controller.rb +++ b/app/controllers/api/v1/statuses/reblogs_controller.rb @@ -18,6 +18,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController @reblogs_map = { @status.id => false } authorize status_for_destroy, :unreblog? + status_for_destroy.discard RemovalWorker.perform_async(status_for_destroy.id) render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, reblogs_map: @reblogs_map) @@ -30,7 +31,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController end def status_for_destroy - current_user.account.statuses.where(reblog_of_id: params[:status_id]).first! + @status_for_destroy ||= current_user.account.statuses.where(reblog_of_id: params[:status_id]).first! end def reblog_params diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 82cc05685537d5dd0085d62b69a00bdbb42df052..cb454e286473707a8c779c058a534bbe4f6ab39f 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -5,8 +5,8 @@ class Api::V1::StatusesController < Api::BaseController before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:create, :destroy] - before_action :require_user!, except: [:show, :context, :card] - before_action :set_status, only: [:show, :context, :card] + before_action :require_user!, except: [:show, :context] + before_action :set_status, only: [:show, :context] respond_to :json @@ -33,16 +33,6 @@ class Api::V1::StatusesController < Api::BaseController render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id) end - def card - @card = @status.preview_cards.first - - if @card.nil? - render_empty - else - render json: @card, serializer: REST::PreviewCardSerializer - end - end - def create @status = PostStatusService.new.call(current_user.account, text: status_params[:status], @@ -64,7 +54,8 @@ class Api::V1::StatusesController < Api::BaseController @status = Status.where(account_id: current_user.account).find(params[:id]) authorize @status, :destroy? - RemovalWorker.perform_async(@status.id) + @status.discard + RemovalWorker.perform_async(@status.id, redraft: true) render json: @status, serializer: REST::StatusSerializer, source_requested: true end diff --git a/app/controllers/api/v1/streaming_controller.rb b/app/controllers/api/v1/streaming_controller.rb index 66b812e761f641f39301843a824b303ecf3268e3..ebb17608c107df1cef434cb42015cec6f53c3ac9 100644 --- a/app/controllers/api/v1/streaming_controller.rb +++ b/app/controllers/api/v1/streaming_controller.rb @@ -5,11 +5,17 @@ class Api::V1::StreamingController < Api::BaseController def index if Rails.configuration.x.streaming_api_base_url != request.host - uri = URI.parse(request.url) - uri.host = URI.parse(Rails.configuration.x.streaming_api_base_url).host - redirect_to uri.to_s, status: 301 + redirect_to streaming_api_url, status: 301 else - raise ActiveRecord::RecordNotFound + not_found end end + + private + + def streaming_api_url + Addressable::URI.parse(request.url).tap do |uri| + uri.host = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url).host + end.to_s + end end diff --git a/app/controllers/api/v1/timelines/direct_controller.rb b/app/controllers/api/v1/timelines/direct_controller.rb deleted file mode 100644 index d8a76d153a6c5167492af980a3e2562924d22131..0000000000000000000000000000000000000000 --- a/app/controllers/api/v1/timelines/direct_controller.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Timelines::DirectController < Api::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show] - before_action :require_user!, only: [:show] - after_action :insert_pagination_headers, unless: -> { @statuses.empty? } - - respond_to :json - - def show - @statuses = load_statuses - render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id) - end - - private - - def load_statuses - cached_direct_statuses - end - - def cached_direct_statuses - cache_collection direct_statuses, Status - end - - def direct_statuses - direct_timeline_statuses - end - - def direct_timeline_statuses - # this query requires built in pagination. - Status.as_direct_timeline( - current_account, - limit_param(DEFAULT_STATUSES_LIMIT), - params[:max_id], - params[:since_id], - true # returns array of cache_ids object - ) - end - - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def pagination_params(core_params) - params.permit(:local, :limit).merge(core_params) - end - - def next_path - api_v1_timelines_direct_url pagination_params(max_id: pagination_max_id) - end - - def prev_path - api_v1_timelines_direct_url pagination_params(since_id: pagination_since_id) - end - - def pagination_max_id - @statuses.last.id - end - - def pagination_since_id - @statuses.first.id - end -end diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb index fcd0757f1a87c0d6792cfb12de536d9ef46c4643..ff5ede1385234ea19196a9559fc6694017a06a53 100644 --- a/app/controllers/api/v1/timelines/home_controller.rb +++ b/app/controllers/api/v1/timelines/home_controller.rb @@ -13,7 +13,7 @@ class Api::V1::Timelines::HomeController < Api::BaseController render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), - status: regeneration_in_progress? ? 206 : 200 + status: account_home_feed.regenerating? ? 206 : 200 end private @@ -62,8 +62,4 @@ class Api::V1::Timelines::HomeController < Api::BaseController def pagination_since_id @statuses.first.id end - - def regeneration_in_progress? - Redis.current.exists("account:#{current_account.id}:regeneration") - end end diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb index aabe24324312421b5f9c0765b3a7c037907ab9df..ccc10f966ca64789600fa8397108568a6860d39e 100644 --- a/app/controllers/api/v1/timelines/public_controller.rb +++ b/app/controllers/api/v1/timelines/public_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Api::V1::Timelines::PublicController < Api::BaseController + before_action :require_user!, only: [:show], if: :require_auth? after_action :insert_pagination_headers, unless: -> { @statuses.empty? } respond_to :json @@ -12,6 +13,10 @@ class Api::V1::Timelines::PublicController < Api::BaseController private + def require_auth? + !Setting.timeline_preview + end + def load_statuses cached_public_statuses end diff --git a/app/controllers/api/v1/trends_controller.rb b/app/controllers/api/v1/trends_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..bcea9857e8fa719c0c4ee3c3ce3a92c336d02f94 --- /dev/null +++ b/app/controllers/api/v1/trends_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::TrendsController < Api::BaseController + before_action :set_tags + + respond_to :json + + def index + render json: @tags, each_serializer: REST::TagSerializer + end + + private + + def set_tags + @tags = TrendingTags.get(limit_param(10)) + end +end diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb index 9aa6edc6969eb2bbda1b7bbcea3ce00aa8062d58..cbd9b551d091ba52ad0ed8bd7c3629bb25d33d5e 100644 --- a/app/controllers/api/v2/search_controller.rb +++ b/app/controllers/api/v2/search_controller.rb @@ -1,8 +1,32 @@ # frozen_string_literal: true -class Api::V2::SearchController < Api::V1::SearchController +class Api::V2::SearchController < Api::BaseController + include Authorization + + RESULTS_LIMIT = 20 + + before_action -> { doorkeeper_authorize! :read, :'read:search' } + before_action :require_user! + + respond_to :json + def index @search = Search.new(search_results) - render json: @search, serializer: REST::V2::SearchSerializer + render json: @search, serializer: REST::SearchSerializer + end + + private + + def search_results + SearchService.new.call( + params[:q], + current_account, + limit_param(RESULTS_LIMIT), + search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed)) + ) + end + + def search_params + params.permit(:type, :offset, :min_id, :max_id, :account_id) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6b8411402beebba71d71eb87d7fdf03065f25ebc..bd3d137743f89339f8d7629a634a093435094c8e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,21 +10,29 @@ class ApplicationController < ActionController::Base include Localized include UserTrackingConcern include SessionTrackingConcern + include CacheConcern + include DomainControlHelper helper_method :current_account helper_method :current_session helper_method :current_theme helper_method :single_user_mode? helper_method :use_seamless_external_login? + helper_method :whitelist_mode? rescue_from ActionController::RoutingError, with: :not_found - rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity rescue_from ActionController::UnknownFormat, with: :not_acceptable + rescue_from ActionController::ParameterMissing, with: :bad_request + rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from Mastodon::NotPermittedError, with: :forbidden + rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error + rescue_from Mastodon::RaceConditionError, with: :service_unavailable before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? - before_action :check_user_permissions, if: :user_signed_in? + before_action :require_functional!, if: :user_signed_in? + + skip_before_action :verify_authenticity_token, only: :raise_not_found def raise_not_found raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}" @@ -33,7 +41,15 @@ class ApplicationController < ActionController::Base private def https_enabled? - Rails.env.production? + Rails.env.production? && !request.path.start_with?('/health') + end + + def authorized_fetch_mode? + ENV['AUTHORIZED_FETCH'] == 'true' || Rails.configuration.x.whitelist_mode + end + + def public_fetch_mode? + !authorized_fetch_mode? end def store_current_location @@ -48,8 +64,8 @@ class ApplicationController < ActionController::Base forbidden unless current_user&.staff? end - def check_user_permissions - forbidden if current_user.disabled? || current_user.account.suspended? + def require_functional! + redirect_to edit_user_registration_path unless current_user.functional? end def after_sign_out_path_for(_resource_or_scope) @@ -82,8 +98,20 @@ class ApplicationController < ActionController::Base respond_with_error(406) end + def bad_request + respond_with_error(400) + end + + def internal_server_error + respond_with_error(500) + end + + def service_unavailable + respond_with_error(503) + end + def single_user_mode? - @single_user_mode ||= Rails.configuration.x.single_user_mode && Account.exists? + @single_user_mode ||= Rails.configuration.x.single_user_mode && Account.where('id > 0').exists? end def use_seamless_external_login? @@ -107,51 +135,10 @@ class ApplicationController < ActionController::Base current_user.setting_theme end - def cache_collection(raw, klass) - return raw unless klass.respond_to?(:with_includes) - - raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) - cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id) - uncached_ids = raw.map(&:id) - cached_keys_with_value.keys - - klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) - - unless uncached_ids.empty? - uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item } - - uncached.each_value do |item| - Rails.cache.write(item, item) - end - end - - raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact - end - def respond_with_error(code) respond_to do |format| format.any { head code } format.html { render "errors/#{code}", layout: 'error', status: code } end end - - def render_cached_json(cache_key, **options) - options[:expires_in] ||= 3.minutes - cache_public = options.key?(:public) ? options.delete(:public) : true - content_type = options.delete(:content_type) || 'application/json' - - data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do - yield.to_json - end - - expires_in options[:expires_in], public: cache_public - render json: data, content_type: content_type - end - - def set_cache_headers - response.headers['Vary'] = 'Accept' - end - - def mark_cacheable! - expires_in 0, public: true - end end diff --git a/app/controllers/auth/challenges_controller.rb b/app/controllers/auth/challenges_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..060944240a221ae05facc18d4b25bc58470b1edb --- /dev/null +++ b/app/controllers/auth/challenges_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class Auth::ChallengesController < ApplicationController + include ChallengableConcern + + layout 'auth' + + before_action :authenticate_user! + + skip_before_action :require_functional! + + def create + if challenge_passed? + session[:challenge_passed_at] = Time.now.utc + redirect_to challenge_params[:return_to] + else + @challenge = Form::Challenge.new(return_to: challenge_params[:return_to]) + flash.now[:alert] = I18n.t('challenge.invalid_password') + render_challenge + end + end +end diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index c28c7471c0d1636b5f14f59400c4d8746a8d262b..89852526998dbedcedbcee3ace41ef724f4eb7e5 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -4,32 +4,36 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController layout 'auth' before_action :set_body_classes - before_action :set_user, only: [:finish_signup] + before_action :require_unconfirmed! - def finish_signup - return unless request.patch? && params[:user] + skip_before_action :require_functional! - if @user.update(user_params) - @user.skip_reconfirmation! - bypass_sign_in(@user) - redirect_to root_path, notice: I18n.t('devise.confirmations.send_instructions') - else - @show_errors = true - end + def new + super + + resource.email = current_user.unconfirmed_email || current_user.email if user_signed_in? end private - def set_user - @user = current_user + def require_unconfirmed! + redirect_to edit_user_registration_path if user_signed_in? && current_user.confirmed? && current_user.unconfirmed_email.blank? end def set_body_classes @body_classes = 'lighter' end - def user_params - params.require(:user).permit(:email) + def after_resending_confirmation_instructions_path_for(_resource_name) + if user_signed_in? + if current_user.confirmed? && current_user.approved? + edit_user_registration_path + else + auth_setup_path + end + else + new_user_session_path + end end def after_confirmation_path_for(_resource_name, user) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index bbf63bed304f9ea55e9ad8aee6a329a433420ae7..682c77016fd6cd07eb7cb6fd042ab9d7186c0aa7 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -27,7 +27,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController if resource.email_verified? root_path else - finish_signup_path + auth_setup_path(missing_email: '1') end end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 83797cf1f7600d1e09a57f67a492058ba1355e70..019caf9c1aac5fc329e4dec2c9654b0b1abd92e8 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -9,6 +9,9 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :set_sessions, only: [:edit, :update] before_action :set_instance_presenter, only: [:new, :create, :update] before_action :set_body_classes, only: [:new, :create, :edit, :update] + before_action :require_not_suspended!, only: [:update] + + skip_before_action :require_functional!, only: [:edit, :update] def new super(&:build_invite_request) @@ -43,7 +46,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def after_sign_up_path_for(_resource) - new_user_session_path + auth_setup_path end def after_sign_in_path_for(_resource) @@ -102,4 +105,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController def set_sessions @sessions = current_user.session_activations end + + def require_not_suspended! + forbidden if current_account.suspended? + end end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index fb8615c3134dc5b6db94ddf648b7f7bda1717340..f48b17c79935ceb3bb9141c1bab52fc9aa296a0d 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -6,8 +6,10 @@ class Auth::SessionsController < Devise::SessionsController layout 'auth' skip_before_action :require_no_authentication, only: [:create] - skip_before_action :check_user_permissions, only: [:destroy] + skip_before_action :require_functional! + prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] + before_action :set_instance_presenter, only: [:new] before_action :set_body_classes @@ -29,6 +31,7 @@ class Auth::SessionsController < Devise::SessionsController def destroy tmp_stored_location = stored_location_for(:user) super + session.delete(:challenge_passed_at) flash.delete(:notice) store_location_for(:user, tmp_stored_location) if continue_after? end @@ -38,12 +41,10 @@ class Auth::SessionsController < Devise::SessionsController def find_user if session[:otp_user_id] User.find(session[:otp_user_id]) - elsif user_params[:email] - if use_seamless_external_login? && Devise.check_at_sign && user_params[:email].index('@').nil? - User.joins(:account).find_by(accounts: { username: user_params[:email] }) - else - User.find_for_authentication(email: user_params[:email]) - end + else + user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication + user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication + user ||= User.find_for_authentication(email: user_params[:email]) end end @@ -70,13 +71,13 @@ class Auth::SessionsController < Devise::SessionsController end def two_factor_enabled? - find_user.try(:otp_required_for_login?) + find_user&.otp_required_for_login? end def valid_otp_attempt?(user) user.validate_and_consume_otp!(user_params[:otp_attempt]) || user.invalidate_otp_backup_code!(user_params[:otp_attempt]) - rescue OpenSSL::Cipher::CipherError => _error + rescue OpenSSL::Cipher::CipherError false end @@ -85,7 +86,10 @@ class Auth::SessionsController < Devise::SessionsController if user_params[:otp_attempt].present? && session[:otp_user_id] authenticate_with_two_factor_via_otp(user) - elsif user&.valid_password?(user_params[:password]) + elsif user.present? && (user.encrypted_password.blank? || user.valid_password?(user_params[:password])) + # If encrypted_password is blank, we got the user from LDAP or PAM, + # so credentials are already valid + prompt_for_two_factor(user) end end @@ -103,6 +107,7 @@ class Auth::SessionsController < Devise::SessionsController def prompt_for_two_factor(user) session[:otp_user_id] = user.id + @body_classes = 'lighter' render :two_factor end diff --git a/app/controllers/auth/setup_controller.rb b/app/controllers/auth/setup_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..46c5f2958174c3b853c7c5886f27ff0e6c6c406e --- /dev/null +++ b/app/controllers/auth/setup_controller.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +class Auth::SetupController < ApplicationController + layout 'auth' + + before_action :authenticate_user! + before_action :require_unconfirmed_or_pending! + before_action :set_body_classes + before_action :set_user + + skip_before_action :require_functional! + + def show + flash.now[:notice] = begin + if @user.pending? + I18n.t('devise.registrations.signed_up_but_pending') + else + I18n.t('devise.registrations.signed_up_but_unconfirmed') + end + end + end + + def update + # This allows updating the e-mail without entering a password as is required + # on the account settings page; however, we only allow this for accounts + # that were not confirmed yet + + if @user.update(user_params) + redirect_to auth_setup_path, notice: I18n.t('devise.confirmations.send_instructions') + else + render :show + end + end + + helper_method :missing_email? + + private + + def require_unconfirmed_or_pending! + redirect_to root_path if current_user.confirmed? && current_user.approved? + end + + def set_user + @user = current_user + end + + def set_body_classes + @body_classes = 'lighter' + end + + def user_params + params.require(:user).permit(:email) + end + + def missing_email? + truthy_param?(:missing_email) + end +end diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb index 1c422096c63516647c650a5943a6293196859d46..11eac0eb6bfc3f66dbd2f631711465431e2a4d6a 100644 --- a/app/controllers/concerns/account_controller_concern.rb +++ b/app/controllers/concerns/account_controller_concern.rb @@ -3,24 +3,19 @@ module AccountControllerConcern extend ActiveSupport::Concern + include AccountOwnedConcern + FOLLOW_PER_PAGE = 12 included do layout 'public' - before_action :set_account - before_action :check_account_approval - before_action :check_account_suspension before_action :set_instance_presenter - before_action :set_link_headers + before_action :set_link_headers, if: -> { request.format.nil? || request.format == :html } end private - def set_account - @account = Account.find_local!(username_param) - end - def set_instance_presenter @instance_presenter = InstancePresenter.new end @@ -29,27 +24,15 @@ module AccountControllerConcern response.headers['Link'] = LinkHeader.new( [ webfinger_account_link, - atom_account_url_link, actor_url_link, ] ) end - def username_param - params[:account_username] - end - def webfinger_account_link [ webfinger_account_url, - [%w(rel lrdd), %w(type application/xrd+xml)], - ] - end - - def atom_account_url_link - [ - account_url(@account, format: 'atom'), - [%w(rel alternate), %w(type application/atom+xml)], + [%w(rel lrdd), %w(type application/jrd+json)], ] end @@ -63,15 +46,4 @@ module AccountControllerConcern def webfinger_account_url webfinger_url(resource: @account.to_webfinger_s) end - - def check_account_approval - not_found if @account.user_pending? - end - - def check_account_suspension - if @account.suspended? - expires_in(3.minutes, public: true) - gone - end - end end diff --git a/app/controllers/concerns/account_owned_concern.rb b/app/controllers/concerns/account_owned_concern.rb new file mode 100644 index 0000000000000000000000000000000000000000..460f71f65faf8da8f90d00ef4a36d4c9960ece43 --- /dev/null +++ b/app/controllers/concerns/account_owned_concern.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module AccountOwnedConcern + extend ActiveSupport::Concern + + included do + before_action :authenticate_user!, if: -> { whitelist_mode? && request.format != :json } + before_action :set_account, if: :account_required? + before_action :check_account_approval, if: :account_required? + before_action :check_account_suspension, if: :account_required? + end + + private + + def account_required? + true + end + + def set_account + @account = Account.find_local!(username_param) + end + + def username_param + params[:account_username] + end + + def check_account_approval + not_found if @account.local? && @account.user_pending? + end + + def check_account_suspension + expires_in(3.minutes, public: true) && gone if @account.suspended? + end +end diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb new file mode 100644 index 0000000000000000000000000000000000000000..c7d25ae00c85e019093284d5ef288bdd8c9ef068 --- /dev/null +++ b/app/controllers/concerns/cache_concern.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module CacheConcern + extend ActiveSupport::Concern + + def render_with_cache(**options) + raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given? + + key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':') + expires_in = options.delete(:expires_in) || 3.minutes + body = Rails.cache.read(key, raw: true) + + if body + render(options.except(:json, :serializer, :each_serializer, :adapter, :fields).merge(json: body)) + else + if block_given? + options[:json] = yield + elsif options[:json].is_a?(Symbol) + options[:json] = send(options[:json]) + end + + render(options) + Rails.cache.write(key, response.body, expires_in: expires_in, raw: true) + end + end + + def set_cache_headers + response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature' + end + + def cache_collection(raw, klass) + return raw unless klass.respond_to?(:with_includes) + + raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) + cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id) + uncached_ids = raw.map(&:id) - cached_keys_with_value.keys + + klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) + + unless uncached_ids.empty? + uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item } + + uncached.each_value do |item| + Rails.cache.write(item, item) + end + end + + raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact + end +end diff --git a/app/controllers/concerns/challengable_concern.rb b/app/controllers/concerns/challengable_concern.rb new file mode 100644 index 0000000000000000000000000000000000000000..b29d90b3cc32d62c0d66e49d139ff44d6aee4841 --- /dev/null +++ b/app/controllers/concerns/challengable_concern.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# This concern is inspired by "sudo mode" on GitHub. It +# is a way to re-authenticate a user before allowing them +# to see or perform an action. +# +# Add `before_action :require_challenge!` to actions you +# want to protect. +# +# The user will be shown a page to enter the challenge (which +# is either the password, or just the username when no +# password exists). Upon passing, there is a grace period +# during which no challenge will be asked from the user. +# +# Accessing challenge-protected resources during the grace +# period will refresh the grace period. +module ChallengableConcern + extend ActiveSupport::Concern + + CHALLENGE_TIMEOUT = 1.hour.freeze + + def require_challenge! + return if skip_challenge? + + if challenge_passed_recently? + session[:challenge_passed_at] = Time.now.utc + return + end + + @challenge = Form::Challenge.new(return_to: request.url) + + if params.key?(:form_challenge) + if challenge_passed? + session[:challenge_passed_at] = Time.now.utc + return + else + flash.now[:alert] = I18n.t('challenge.invalid_password') + render_challenge + end + else + render_challenge + end + end + + def render_challenge + @body_classes = 'lighter' + render template: 'auth/challenges/new', layout: 'auth' + end + + def challenge_passed? + current_user.valid_password?(challenge_params[:current_password]) + end + + def skip_challenge? + current_user.encrypted_password.blank? + end + + def challenge_passed_recently? + session[:challenge_passed_at].present? && session[:challenge_passed_at] >= CHALLENGE_TIMEOUT.ago + end + + def challenge_params + params.require(:form_challenge).permit(:current_password, :return_to) + end +end diff --git a/app/controllers/concerns/export_controller_concern.rb b/app/controllers/concerns/export_controller_concern.rb index e20b71a30394a540332fd6f3d4a9c1ec710d215f..bfe990c827d1bf9cf085e05553fc1b375a372b84 100644 --- a/app/controllers/concerns/export_controller_concern.rb +++ b/app/controllers/concerns/export_controller_concern.rb @@ -5,7 +5,10 @@ module ExportControllerConcern included do before_action :authenticate_user! + before_action :require_not_suspended! before_action :load_export + + skip_before_action :require_functional! end private @@ -27,4 +30,8 @@ module ExportControllerConcern def export_filename "#{controller_name}.csv" end + + def require_not_suspended! + forbidden if current_account.suspended? + end end diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 90a57197c00bde2a1512e524887f69bba1b66640..ce353f1deefcccaf4df775ef3c52a56ab3e0b77f 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -5,12 +5,35 @@ module SignatureVerification extend ActiveSupport::Concern + include DomainControlHelper + + def require_signature! + render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account + end + def signed_request? request.headers['Signature'].present? end def signature_verification_failure_reason - return @signature_verification_failure_reason if defined?(@signature_verification_failure_reason) + @signature_verification_failure_reason + end + + def signature_verification_failure_code + @signature_verification_failure_code || 401 + end + + def signature_key_id + raw_signature = request.headers['Signature'] + signature_params = {} + + raw_signature.split(',').each do |part| + parsed_parts = part.match(/([a-z]+)="([^"]+)"/i) + next if parsed_parts.nil? || parsed_parts.size != 3 + signature_params[parsed_parts[1]] = parsed_parts[2] + end + + signature_params['keyId'] end def signed_request_account @@ -123,6 +146,13 @@ module SignatureVerification end def account_from_key_id(key_id) + domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id + + if domain_not_allowed?(domain) + @signature_verification_failure_code = 403 + return + end + if key_id.start_with?('acct:') stoplight_wrap_request { ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, '')) } elsif !ActivityPub::TagManager.instance.local_uri?(key_id) @@ -137,7 +167,7 @@ module SignatureVerification .with_fallback { nil } .with_threshold(1) .with_cool_off_time(5.minutes.seconds) - .with_error_handler { |error, handle| error.is_a?(HTTP::Error) ? 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 end diff --git a/app/controllers/concerns/status_controller_concern.rb b/app/controllers/concerns/status_controller_concern.rb new file mode 100644 index 0000000000000000000000000000000000000000..62a7cf5086a2186854ba814662d1e72de0f5eaf8 --- /dev/null +++ b/app/controllers/concerns/status_controller_concern.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module StatusControllerConcern + extend ActiveSupport::Concern + + ANCESTORS_LIMIT = 40 + DESCENDANTS_LIMIT = 60 + DESCENDANTS_DEPTH_LIMIT = 20 + + def create_descendant_thread(starting_depth, statuses) + depth = starting_depth + statuses.size + + if depth < DESCENDANTS_DEPTH_LIMIT + { + statuses: statuses, + starting_depth: starting_depth, + } + else + next_status = statuses.pop + + { + statuses: statuses, + starting_depth: starting_depth, + next_status: next_status, + } + end + end + + def set_ancestors + @ancestors = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : [] + @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift + end + + def set_descendants + @max_descendant_thread_id = params[:max_descendant_thread_id]&.to_i + @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i + + descendants = cache_collection( + @status.descendants( + DESCENDANTS_LIMIT, + current_account, + @max_descendant_thread_id, + @since_descendant_thread_id, + DESCENDANTS_DEPTH_LIMIT + ), + Status + ) + + @descendant_threads = [] + + if descendants.present? + statuses = [descendants.first] + starting_depth = 0 + + descendants.drop(1).each_with_index do |descendant, index| + if descendants[index].id == descendant.in_reply_to_id + statuses << descendant + else + @descendant_threads << create_descendant_thread(starting_depth, statuses) + + # The thread is broken, assume it's a reply to the root status + starting_depth = 0 + + # ... unless we can find its ancestor in one of the already-processed threads + @descendant_threads.reverse_each do |descendant_thread| + statuses = descendant_thread[:statuses] + + index = statuses.find_index do |thread_status| + thread_status.id == descendant.in_reply_to_id + end + + if index.present? + starting_depth = descendant_thread[:starting_depth] + index + 1 + break + end + end + + statuses = [descendant] + end + end + + @descendant_threads << create_descendant_thread(starting_depth, statuses) + end + + @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT + end +end diff --git a/app/controllers/custom_css_controller.rb b/app/controllers/custom_css_controller.rb index 6e80feaf837fac3c21d8ef8fd2c0b6f31d0c5b12..0a667a6a66009c99c61a6f6b311a9309ceae4add 100644 --- a/app/controllers/custom_css_controller.rb +++ b/app/controllers/custom_css_controller.rb @@ -2,10 +2,12 @@ class CustomCssController < ApplicationController skip_before_action :store_current_location + skip_before_action :require_functional! before_action :set_cache_headers def show + expires_in 3.minutes, public: true render plain: Setting.custom_css || '', content_type: 'text/css' end end diff --git a/app/controllers/directories_controller.rb b/app/controllers/directories_controller.rb index 5949076740fac499b0f3842bca6c3021b6c8d69f..750c835ddab471e1536327e6486ad19b1ddcd7e8 100644 --- a/app/controllers/directories_controller.rb +++ b/app/controllers/directories_controller.rb @@ -3,12 +3,14 @@ class DirectoriesController < ApplicationController layout 'public' - before_action :check_enabled + before_action :authenticate_user!, if: :whitelist_mode? + before_action :require_enabled! before_action :set_instance_presenter before_action :set_tag, only: :show - before_action :set_tags before_action :set_accounts + skip_before_action :require_functional! + def index render :index end @@ -19,21 +21,18 @@ class DirectoriesController < ApplicationController private - def check_enabled + def require_enabled! return not_found unless Setting.profile_directory end def set_tag - @tag = Tag.discoverable.find_by!(name: params[:id].downcase) - end - - def set_tags - @tags = Tag.discoverable.limit(30).reject { |tag| tag.cached_sample_accounts.empty? } + @tag = Tag.discoverable.find_normalized!(params[:id]) end def set_accounts - @accounts = Account.discoverable.by_recent_status.page(params[:page]).per(40).tap do |query| + @accounts = Account.local.discoverable.by_recent_status.page(params[:page]).per(20).tap do |query| query.merge!(Account.tagged_with(@tag.id)) if @tag + query.merge!(Account.not_excluded_by_account(current_account)) if current_account end end diff --git a/app/controllers/emojis_controller.rb b/app/controllers/emojis_controller.rb index 3feb081325fb1f21c640a3cdcf9b11655c080fb3..41f1e1c5ca07b307bbbd1ab0e07bb76726f7f374 100644 --- a/app/controllers/emojis_controller.rb +++ b/app/controllers/emojis_controller.rb @@ -7,9 +7,8 @@ class EmojisController < ApplicationController def show respond_to do |format| format.json do - render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do - ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter) - end + expires_in 3.minutes, public: true + render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter end end end diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index 415abe10c100756003126a7c602f9f94ac058f24..705ff412269c6cf30d4ee2477d54b2ed33ffe348 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -2,13 +2,18 @@ class FollowerAccountsController < ApplicationController include AccountControllerConcern + include SignatureVerification + before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :set_cache_headers + skip_around_action :set_locale, if: -> { request.format == :json } + skip_before_action :require_functional! + def index respond_to do |format| format.html do - mark_cacheable! unless user_signed_in? + expires_in 0, public: true unless user_signed_in? next if @account.user_hides_network? @@ -17,9 +22,9 @@ class FollowerAccountsController < ApplicationController end format.json do - raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network? + raise Mastodon::NotPermittedError if page_requested? && @account.user_hides_network? - expires_in 3.minutes, public: true if params[:page].blank? + expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, @@ -35,12 +40,16 @@ class FollowerAccountsController < ApplicationController @follows ||= Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account) end + def page_requested? + params[:page].present? + end + def page_url(page) account_followers_url(@account, page: page) unless page.nil? end def collection_presenter - if params[:page].present? + if page_requested? ActivityPub::CollectionPresenter.new( id: account_followers_url(@account, page: params.fetch(:page, 1)), type: :ordered, diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index 948725664196e4bebd19bf7641945bf86a44c1f6..968de980d1393724b9cf315c481305b1586d6971 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -2,13 +2,18 @@ class FollowingAccountsController < ApplicationController include AccountControllerConcern + include SignatureVerification + before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } before_action :set_cache_headers + skip_around_action :set_locale, if: -> { request.format == :json } + skip_before_action :require_functional! + def index respond_to do |format| format.html do - mark_cacheable! unless user_signed_in? + expires_in 0, public: true unless user_signed_in? next if @account.user_hides_network? @@ -17,9 +22,9 @@ class FollowingAccountsController < ApplicationController end format.json do - raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network? + raise Mastodon::NotPermittedError if page_requested? && @account.user_hides_network? - expires_in 3.minutes, public: true if params[:page].blank? + expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?) render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, @@ -35,12 +40,16 @@ class FollowingAccountsController < ApplicationController @follows ||= Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account) end + def page_requested? + params[:page].present? + end + def page_url(page) account_following_index_url(@account, page: page) unless page.nil? end def collection_presenter - if params[:page].present? + if page_requested? ActivityPub::CollectionPresenter.new( id: account_following_index_url(@account, page: params.fetch(:page, 1)), type: :ordered, diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 85622a7b59ecb52772d2875efed930faa20a9680..7c8a18d17cc48a452cccb5ba804887e3f390d06a 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -3,7 +3,6 @@ class HomeController < ApplicationController before_action :authenticate_user! before_action :set_referrer_policy_header - before_action :set_initial_state_json def index @body_classes = 'app-body' @@ -21,7 +20,7 @@ class HomeController < ApplicationController when 'statuses' status = Status.find_by(id: matches[2]) - if status && (status.public_visibility? || status.unlisted_visibility?) + if status&.distributable? redirect_to(ActivityPub::TagManager.instance.url_for(status)) return end @@ -39,26 +38,11 @@ class HomeController < ApplicationController redirect_to(matches ? tag_path(CGI.unescape(matches[:tag])) : default_redirect_path) end - def set_initial_state_json - serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) - @initial_state_json = serializable_resource.to_json - end - - def initial_state_params - { - settings: Web::Setting.find_by(user: current_user)&.data || {}, - push_subscription: current_account.user.web_push_subscription(current_session), - current_account: current_account, - token: current_session.token, - admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')), - } - end - def default_redirect_path - if request.path.start_with?('/web') + if request.path.start_with?('/web') || whitelist_mode? new_user_session_path elsif single_user_mode? - short_account_path(Account.local.without_suspended.first) + short_account_path(Account.local.without_suspended.where('id > 0').first) else about_path end diff --git a/app/controllers/instance_actors_controller.rb b/app/controllers/instance_actors_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..6f02d6a358f00cea153da012ad0f7a11de9fafea --- /dev/null +++ b/app/controllers/instance_actors_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class InstanceActorsController < ApplicationController + include AccountControllerConcern + + skip_around_action :set_locale + + def show + expires_in 10.minutes, public: true + render json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to + end + + private + + def set_account + @account = Account.find(-99) + end + + def restrict_fields_to + %i(id type preferred_username inbox public_key endpoints url manually_approves_followers) + end +end diff --git a/app/controllers/intents_controller.rb b/app/controllers/intents_controller.rb index 9f41cf48ac3183fe49414c6aa96418608e5e86e0..ca89fc7fe65f0fc09b933efcf95aa2b9c8527800 100644 --- a/app/controllers/intents_controller.rb +++ b/app/controllers/intents_controller.rb @@ -2,6 +2,7 @@ class IntentsController < ApplicationController before_action :check_uri + rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri def show diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index de5280305e90cd00877ea029b03a8047653cfe09..8d92147e276b7a5674949e895680952dbbfb739c 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -43,7 +43,7 @@ class InvitesController < ApplicationController end def resource_params - params.require(:invite).permit(:max_uses, :expires_in, :autofollow) + params.require(:invite).permit(:max_uses, :expires_in, :autofollow, :comment) end def set_body_classes diff --git a/app/controllers/manifests_controller.rb b/app/controllers/manifests_controller.rb index 332d845d8252c831a08bdde32fc100829d6d93cd..960510f60161e816ebdf73c3f140a5353b9858e9 100644 --- a/app/controllers/manifests_controller.rb +++ b/app/controllers/manifests_controller.rb @@ -2,8 +2,10 @@ class ManifestsController < ApplicationController skip_before_action :store_current_location + skip_before_action :require_functional! def show - render json: InstancePresenter.new, serializer: ManifestSerializer + expires_in 3.minutes, public: true + render json: InstancePresenter.new, serializer: ManifestSerializer, root: 'instance' end end diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index d44b52d2624d7994569f4850d7372103f6c494d2..05cf09c28af82c8d70a953728324b5af4edd1282 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -4,7 +4,9 @@ class MediaController < ApplicationController include Authorization skip_before_action :store_current_location + skip_before_action :require_functional! + before_action :authenticate_user!, if: :whitelist_mode? before_action :set_media_attachment before_action :verify_permitted_status! before_action :check_playable, only: :player @@ -31,7 +33,6 @@ class MediaController < ApplicationController def verify_permitted_status! authorize @media_attachment.status, :show? rescue Mastodon::NotPermittedError - # Reraise in order to get a 404 instead of a 403 error code raise ActiveRecord::RecordNotFound end diff --git a/app/controllers/media_proxy_controller.rb b/app/controllers/media_proxy_controller.rb index 8fc18dd0602a9067d16e25efa454578170038148..014b89de102c68649a94717b1d837caeb9d3e4b6 100644 --- a/app/controllers/media_proxy_controller.rb +++ b/app/controllers/media_proxy_controller.rb @@ -4,6 +4,13 @@ class MediaProxyController < ApplicationController include RoutingHelper skip_before_action :store_current_location + skip_before_action :require_functional! + + before_action :authenticate_user!, if: :whitelist_mode? + + rescue_from ActiveRecord::RecordInvalid, with: :not_found + rescue_from Mastodon::UnexpectedResponseError, with: :not_found + rescue_from HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, with: :internal_server_error def show RedisLock.acquire(lock_options) do |lock| diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index f3d2353669464c0e91af032fe46168fdfc31a14f..fb8389034b9b20cffeab89b0579000db992bdf7e 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -7,6 +7,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio before_action :authenticate_resource_owner! before_action :set_body_classes + skip_before_action :require_functional! + include Localized def destroy diff --git a/app/controllers/public_timelines_controller.rb b/app/controllers/public_timelines_controller.rb index 53d4472d88da355af4d67d23501117dbfb0fee35..1332ba16c2bac4b75664c03d3d641907689a3d50 100644 --- a/app/controllers/public_timelines_controller.rb +++ b/app/controllers/public_timelines_controller.rb @@ -3,25 +3,17 @@ class PublicTimelinesController < ApplicationController layout 'public' - before_action :check_enabled + before_action :authenticate_user!, if: :whitelist_mode? + before_action :require_enabled! before_action :set_body_classes before_action :set_instance_presenter - def show - respond_to do |format| - format.html do - @initial_state_json = ActiveModelSerializers::SerializableResource.new( - InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token), - serializer: InitialStateSerializer - ).to_json - end - end - end + def show; end private - def check_enabled - raise ActiveRecord::RecordNotFound unless Setting.timeline_preview + def require_enabled! + not_found unless Setting.timeline_preview end def set_body_classes diff --git a/app/controllers/remote_follow_controller.rb b/app/controllers/remote_follow_controller.rb index 8ba331cd16ca51e777c218b5f52b670c2aa2c866..db1604644f124cb5040f00f83399be9c1b1c0754 100644 --- a/app/controllers/remote_follow_controller.rb +++ b/app/controllers/remote_follow_controller.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true class RemoteFollowController < ApplicationController + include AccountOwnedConcern + layout 'modal' - before_action :set_account - before_action :gone, if: :suspended_account? before_action :set_body_classes + skip_before_action :require_functional! + def new @remote_follow = RemoteFollow.new(session_params) end @@ -29,15 +31,7 @@ class RemoteFollowController < ApplicationController end def session_params - { acct: session[:remote_follow] } - end - - def set_account - @account = Account.find_local!(params[:account_username]) - end - - def suspended_account? - @account.suspended? + { acct: session[:remote_follow] || current_account&.username } end def set_body_classes diff --git a/app/controllers/remote_interaction_controller.rb b/app/controllers/remote_interaction_controller.rb index cc6993c52b22236dd4b95a4aa698c5e5527e8eed..4073e7ac3faa94614526690233475c8fbe1a9506 100644 --- a/app/controllers/remote_interaction_controller.rb +++ b/app/controllers/remote_interaction_controller.rb @@ -5,10 +5,13 @@ class RemoteInteractionController < ApplicationController layout 'modal' + before_action :authenticate_user!, if: :whitelist_mode? before_action :set_interaction_type before_action :set_status before_action :set_body_classes + skip_before_action :require_functional! + def new @remote_follow = RemoteFollow.new(session_params) end @@ -31,14 +34,13 @@ class RemoteInteractionController < ApplicationController end def session_params - { acct: session[:remote_follow] } + { acct: session[:remote_follow] || current_account&.username } end def set_status @status = Status.find(params[:id]) authorize @status, :show? rescue Mastodon::NotPermittedError - # Reraise in order to get a 404 raise ActiveRecord::RecordNotFound end diff --git a/app/controllers/remote_unfollows_controller.rb b/app/controllers/remote_unfollows_controller.rb deleted file mode 100644 index af5943363ae57ff7701519e30518a08b6f38e4ca..0000000000000000000000000000000000000000 --- a/app/controllers/remote_unfollows_controller.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -class RemoteUnfollowsController < ApplicationController - layout 'modal' - - before_action :authenticate_user! - before_action :set_body_classes - - def create - @account = unfollow_attempt.try(:target_account) - - if @account.nil? - render :error - else - render :success - end - rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError - render :error - end - - private - - def unfollow_attempt - username, domain = acct_without_prefix.split('@') - UnfollowService.new.call(current_account, Account.find_remote!(username, domain)) - end - - def acct_without_prefix - acct_params.gsub(/\Aacct:/, '') - end - - def acct_params - params.fetch(:acct, '') - end - - def set_body_classes - @body_classes = 'modal-layout' - end -end diff --git a/app/controllers/settings/aliases_controller.rb b/app/controllers/settings/aliases_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..b7c9a409d1f8d7fb18adadbfcce531118d9c8a31 --- /dev/null +++ b/app/controllers/settings/aliases_controller.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class Settings::AliasesController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! + before_action :set_aliases, except: :destroy + before_action :set_alias, only: :destroy + + def index + @alias = current_account.aliases.build + end + + def create + @alias = current_account.aliases.build(resource_params) + + if @alias.save + ActivityPub::UpdateDistributionWorker.perform_async(current_account.id) + redirect_to settings_aliases_path, notice: I18n.t('aliases.created_msg') + else + render :index + end + end + + def destroy + @alias.destroy! + redirect_to settings_aliases_path, notice: I18n.t('aliases.deleted_msg') + end + + private + + def resource_params + params.require(:account_alias).permit(:acct) + end + + def set_alias + @alias = current_account.aliases.find(params[:id]) + end + + def set_aliases + @aliases = current_account.aliases.order(id: :desc).reject(&:new_record?) + end +end diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index dd19aadf636b6fd35541efdc4552ccf0e6b66bd9..15a59c999dffe219c06b7e2099ee1fe4fb2875db 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -5,18 +5,20 @@ class Settings::DeletesController < Settings::BaseController before_action :check_enabled_deletion before_action :authenticate_user! + before_action :require_not_suspended! + + skip_before_action :require_functional! def show @confirmation = Form::DeleteConfirmation.new end def destroy - if current_user.valid_password?(delete_params[:password]) - Admin::SuspensionWorker.perform_async(current_user.account_id, true) - sign_out + if challenge_passed? + destroy_account! redirect_to new_user_session_path, notice: I18n.t('deletes.success_msg') else - redirect_to settings_delete_path, alert: I18n.t('deletes.bad_password_msg') + redirect_to settings_delete_path, alert: I18n.t('deletes.challenge_not_passed') end end @@ -26,7 +28,25 @@ class Settings::DeletesController < Settings::BaseController redirect_to root_path unless Setting.open_deletion end - def delete_params - params.require(:form_delete_confirmation).permit(:password) + def resource_params + params.require(:form_delete_confirmation).permit(:password, :username) + end + + def require_not_suspended! + forbidden if current_account.suspended? + end + + def challenge_passed? + if current_user.encrypted_password.blank? + current_account.username == resource_params[:username] + else + current_user.valid_password?(resource_params[:password]) + end + end + + def destroy_account! + current_account.suspend! + Admin::SuspensionWorker.perform_async(current_user.account_id, true) + sign_out end end diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb index 3012fbf775600ad3a9dfb6149fab2e2eaf11ff04..0e93d07a9b2d509aa1738c855f4e2df49764af53 100644 --- a/app/controllers/settings/exports_controller.rb +++ b/app/controllers/settings/exports_controller.rb @@ -6,6 +6,9 @@ class Settings::ExportsController < Settings::BaseController layout 'admin' before_action :authenticate_user! + before_action :require_not_suspended! + + skip_before_action :require_functional! def show @export = Export.new(current_account) @@ -34,4 +37,8 @@ class Settings::ExportsController < Settings::BaseController def lock_options { redis: Redis.current, key: "backup:#{current_user.id}" } end + + def require_not_suspended! + forbidden if current_account.suspended? + end end diff --git a/app/controllers/settings/migration/redirects_controller.rb b/app/controllers/settings/migration/redirects_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..6e5b72ffba24806b9c6780bea095002f8a8c0584 --- /dev/null +++ b/app/controllers/settings/migration/redirects_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class Settings::Migration::RedirectsController < Settings::BaseController + layout 'admin' + + before_action :authenticate_user! + before_action :require_not_suspended! + + skip_before_action :require_functional! + + def new + @redirect = Form::Redirect.new + end + + def create + @redirect = Form::Redirect.new(resource_params.merge(account: current_account)) + + if @redirect.valid_with_challenge?(current_user) + current_account.update!(moved_to_account: @redirect.target_account) + ActivityPub::UpdateDistributionWorker.perform_async(current_account.id) + redirect_to settings_migration_path, notice: I18n.t('migrations.moved_msg', acct: current_account.moved_to_account.acct) + else + render :new + end + end + + def destroy + if current_account.moved_to_account_id.present? + current_account.update!(moved_to_account: nil) + ActivityPub::UpdateDistributionWorker.perform_async(current_account.id) + end + + redirect_to settings_migration_path, notice: I18n.t('migrations.cancelled_msg') + end + + private + + def resource_params + params.require(:form_redirect).permit(:acct, :current_password, :current_username) + end + + def require_not_suspended! + forbidden if current_account.suspended? + end +end diff --git a/app/controllers/settings/migrations_controller.rb b/app/controllers/settings/migrations_controller.rb index 59eb48779c142bd274d138be83936661b74042e7..68304bb513f3a62afd7e13a4dfb862b11d6770a0 100644 --- a/app/controllers/settings/migrations_controller.rb +++ b/app/controllers/settings/migrations_controller.rb @@ -4,31 +4,48 @@ class Settings::MigrationsController < Settings::BaseController layout 'admin' before_action :authenticate_user! + before_action :require_not_suspended! + before_action :set_migrations + before_action :set_cooldown + + skip_before_action :require_functional! def show - @migration = Form::Migration.new(account: current_account.moved_to_account) + @migration = current_account.migrations.build end - def update - @migration = Form::Migration.new(resource_params) + def create + @migration = current_account.migrations.build(resource_params) - if @migration.valid? && migration_account_changed? - current_account.update!(moved_to_account: @migration.account) - ActivityPub::UpdateDistributionWorker.perform_async(current_account.id) - redirect_to settings_migration_path, notice: I18n.t('migrations.updated_msg') + if @migration.save_with_challenge(current_user) + MoveService.new.call(@migration) + redirect_to settings_migration_path, notice: I18n.t('migrations.moved_msg', acct: current_account.moved_to_account.acct) else render :show end end + helper_method :on_cooldown? + private def resource_params - params.require(:migration).permit(:acct) + params.require(:account_migration).permit(:acct, :current_password, :current_username) + end + + def set_migrations + @migrations = current_account.migrations.includes(:target_account).order(id: :desc).reject(&:new_record?) + end + + def set_cooldown + @cooldown = current_account.migrations.within_cooldown.first + end + + def on_cooldown? + @cooldown.present? end - def migration_account_changed? - current_account.moved_to_account_id != @migration.account&.id && - current_account.id != @migration.account&.id + def require_not_suspended! + forbidden if current_account.suspended? end end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index be655cc287ec92ae747411bc7ac785e8fe413266..12b11c542c52f73be0c8a3c9bbfd60cf62da0964 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -55,7 +55,10 @@ class Settings::PreferencesController < Settings::BaseController :setting_aggregate_reblogs, :setting_show_application, :setting_advanced_layout, - notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account), + :setting_use_blurhash, + :setting_use_pending_items, + :setting_trends, + notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag), interactions: %i(must_be_follower must_be_following must_be_following_dm) ) end diff --git a/app/controllers/settings/sessions_controller.rb b/app/controllers/settings/sessions_controller.rb index 84ebb21f2cce31ff3014c12df957fc540680a3ca..df5ace80368ccc1f0c210c1b7fb1ad388cf5dfed 100644 --- a/app/controllers/settings/sessions_controller.rb +++ b/app/controllers/settings/sessions_controller.rb @@ -4,6 +4,8 @@ class Settings::SessionsController < Settings::BaseController before_action :authenticate_user! before_action :set_session, only: :destroy + skip_before_action :require_functional! + def destroy @session.destroy! flash[:notice] = I18n.t('sessions.revoke_success') diff --git a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb index 02652a36c988bd67e6aecbeb4a0ac6971d0a71ce..ef4df33390aff8403b0b63f8bc2881ed28a0063a 100644 --- a/app/controllers/settings/two_factor_authentication/confirmations_controller.rb +++ b/app/controllers/settings/two_factor_authentication/confirmations_controller.rb @@ -3,23 +3,30 @@ module Settings module TwoFactorAuthentication class ConfirmationsController < BaseController + include ChallengableConcern + layout 'admin' before_action :authenticate_user! + before_action :require_challenge! before_action :ensure_otp_secret + skip_before_action :require_functional! + def new prepare_two_factor_form end def create - if current_user.validate_and_consume_otp!(confirmation_params[:code]) + if current_user.validate_and_consume_otp!(confirmation_params[:otp_attempt]) flash.now[:notice] = I18n.t('two_factor_authentication.enabled_success') current_user.otp_required_for_login = true @recovery_codes = current_user.generate_otp_backup_codes! current_user.save! + UserMailer.two_factor_enabled(current_user).deliver_later! + render 'settings/two_factor_authentication/recovery_codes/index' else flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code') @@ -31,7 +38,7 @@ module Settings private def confirmation_params - params.require(:form_two_factor_confirmation).permit(:code) + params.require(:form_two_factor_confirmation).permit(:otp_attempt) end def prepare_two_factor_form diff --git a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb index 874bf532ba567f2cfeac6f109003c51fc89a0e13..0c4f5bff762c591c8c4d14f56de5671edbffc0d8 100644 --- a/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb +++ b/app/controllers/settings/two_factor_authentication/recovery_codes_controller.rb @@ -3,14 +3,22 @@ module Settings module TwoFactorAuthentication class RecoveryCodesController < BaseController + include ChallengableConcern + layout 'admin' before_action :authenticate_user! + before_action :require_challenge!, on: :create + + skip_before_action :require_functional! def create @recovery_codes = current_user.generate_otp_backup_codes! current_user.save! + + UserMailer.two_factor_recovery_codes_changed(current_user).deliver_later! flash.now[:notice] = I18n.t('two_factor_authentication.recovery_codes_regenerated') + render :index end end diff --git a/app/controllers/settings/two_factor_authentications_controller.rb b/app/controllers/settings/two_factor_authentications_controller.rb index e12c4307468653c19c5a0c5e8c7cd5dc6d2792f5..9118a79332c4ef6b50d7c9345b9dc669e5e8bf41 100644 --- a/app/controllers/settings/two_factor_authentications_controller.rb +++ b/app/controllers/settings/two_factor_authentications_controller.rb @@ -2,10 +2,15 @@ module Settings class TwoFactorAuthenticationsController < BaseController + include ChallengableConcern + layout 'admin' before_action :authenticate_user! before_action :verify_otp_required, only: [:create] + before_action :require_challenge!, only: [:create] + + skip_before_action :require_functional! def show @confirmation = Form::TwoFactorConfirmation.new @@ -21,6 +26,7 @@ module Settings if acceptable_code? current_user.otp_required_for_login = false current_user.save! + UserMailer.two_factor_disabled(current_user).deliver_later! redirect_to settings_two_factor_authentication_path else flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code') @@ -32,7 +38,7 @@ module Settings private def confirmation_params - params.require(:form_two_factor_confirmation).permit(:code) + params.require(:form_two_factor_confirmation).permit(:otp_attempt) end def verify_otp_required @@ -40,8 +46,8 @@ module Settings end def acceptable_code? - current_user.validate_and_consume_otp!(confirmation_params[:code]) || - current_user.invalidate_otp_backup_code!(confirmation_params[:code]) + current_user.validate_and_consume_otp!(confirmation_params[:otp_attempt]) || + current_user.invalidate_otp_backup_code!(confirmation_params[:otp_attempt]) end end end diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb index af605b98f701fce34dfbc5f15815d48134f5f888..6546b8497808c46e4ff95dbcf3a87ebb50d4f146 100644 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@ -6,26 +6,10 @@ class SharesController < ApplicationController before_action :authenticate_user! before_action :set_body_classes - def show - serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) - @initial_state_json = serializable_resource.to_json - end + def show; end private - def initial_state_params - text = [params[:title], params[:text], params[:url]].compact.join(' ') - - { - settings: Web::Setting.find_by(user: current_user)&.data || {}, - push_subscription: current_account.user.web_push_subscription(current_session), - current_account: current_account, - token: current_session.token, - admin: Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')), - text: text, - } - end - def set_body_classes @body_classes = 'modal-layout compose-standalone' end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index ef26691b296c34895b76cb8ea63297428ae1fc20..57bbeca6434bed4e58b81dea78f8a7103448abce 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -1,24 +1,25 @@ # frozen_string_literal: true class StatusesController < ApplicationController + include StatusControllerConcern include SignatureAuthentication include Authorization - - ANCESTORS_LIMIT = 40 - DESCENDANTS_LIMIT = 60 - DESCENDANTS_DEPTH_LIMIT = 20 + include AccountOwnedConcern layout 'public' - before_action :set_account + before_action :require_signature!, only: :show, if: -> { request.format == :json && authorized_fetch_mode? } before_action :set_status before_action :set_instance_presenter before_action :set_link_headers - before_action :check_account_suspension - before_action :redirect_to_original, only: [:show] - before_action :set_referrer_policy_header, only: [:show] + before_action :redirect_to_original, only: :show + before_action :set_referrer_policy_header, only: :show before_action :set_cache_headers - before_action :set_replies, only: [:replies] + before_action :set_body_classes + before_action :set_autoplay, only: :embed + + skip_around_action :set_locale, if: -> { request.format == :json } + skip_before_action :require_functional!, only: [:show, :embed] content_security_policy only: :embed do |p| p.frame_ancestors(false) @@ -28,27 +29,20 @@ class StatusesController < ApplicationController respond_to do |format| format.html do expires_in 10.seconds, public: true if current_account.nil? - - @body_classes = 'with-modals' - set_ancestors set_descendants - - render 'stream_entries/show' end format.json do - render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do - ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter) - end + expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? + render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter end end end def activity - render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do - ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter) - end + expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? + render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter end def embed @@ -56,130 +50,24 @@ class StatusesController < ApplicationController expires_in 180, public: true response.headers['X-Frame-Options'] = 'ALLOWALL' - @autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay]) - - render 'stream_entries/embed', layout: 'embedded' - end - def replies - render json: replies_collection_presenter, - serializer: ActivityPub::CollectionSerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json', - skip_activities: true + render layout: 'embedded' end private - def replies_collection_presenter - page = ActivityPub::CollectionPresenter.new( - id: replies_account_status_url(@account, @status, page_params), - type: :unordered, - part_of: replies_account_status_url(@account, @status), - next: next_page, - items: @replies.map { |status| status.local ? status : status.id } - ) - if page_requested? - page - else - ActivityPub::CollectionPresenter.new( - id: replies_account_status_url(@account, @status), - type: :unordered, - first: page - ) - end - end - - def create_descendant_thread(starting_depth, statuses) - depth = starting_depth + statuses.size - if depth < DESCENDANTS_DEPTH_LIMIT - { statuses: statuses, starting_depth: starting_depth } - else - next_status = statuses.pop - { statuses: statuses, starting_depth: starting_depth, next_status: next_status } - end - end - - def set_account - @account = Account.find_local!(params[:account_username]) - end - - def set_ancestors - @ancestors = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : [] - @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift - end - - def set_descendants - @max_descendant_thread_id = params[:max_descendant_thread_id]&.to_i - @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i - - descendants = cache_collection( - @status.descendants( - DESCENDANTS_LIMIT, - current_account, - @max_descendant_thread_id, - @since_descendant_thread_id, - DESCENDANTS_DEPTH_LIMIT - ), - Status - ) - - @descendant_threads = [] - - if descendants.present? - statuses = [descendants.first] - starting_depth = 0 - - descendants.drop(1).each_with_index do |descendant, index| - if descendants[index].id == descendant.in_reply_to_id - statuses << descendant - else - @descendant_threads << create_descendant_thread(starting_depth, statuses) - - # The thread is broken, assume it's a reply to the root status - starting_depth = 0 - - # ... unless we can find its ancestor in one of the already-processed threads - @descendant_threads.reverse_each do |descendant_thread| - statuses = descendant_thread[:statuses] - - index = statuses.find_index do |thread_status| - thread_status.id == descendant.in_reply_to_id - end - - if index.present? - starting_depth = descendant_thread[:starting_depth] + index + 1 - break - end - end - - statuses = [descendant] - end - end - - @descendant_threads << create_descendant_thread(starting_depth, statuses) - end - - @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT + def set_body_classes + @body_classes = 'with-modals' end def set_link_headers - response.headers['Link'] = LinkHeader.new( - [ - [account_stream_entry_url(@account, @status.stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]], - [ActivityPub::TagManager.instance.uri_for(@status), [%w(rel alternate), %w(type application/activity+json)]], - ] - ) + response.headers['Link'] = LinkHeader.new([[ActivityPub::TagManager.instance.uri_for(@status), [%w(rel alternate), %w(type application/activity+json)]]]) end def set_status - @status = @account.statuses.find(params[:id]) - @stream_entry = @status.stream_entry - @type = @stream_entry.activity_type.downcase - + @status = @account.statuses.find(params[:id]) authorize @status, :show? rescue Mastodon::NotPermittedError - # Reraise in order to get a 404 raise ActiveRecord::RecordNotFound end @@ -187,39 +75,15 @@ class StatusesController < ApplicationController @instance_presenter = InstancePresenter.new end - def check_account_suspension - gone if @account.suspended? - end - def redirect_to_original - redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog? + redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog? end def set_referrer_policy_header - return if @status.public_visibility? || @status.unlisted_visibility? - response.headers['Referrer-Policy'] = 'origin' - end - - def page_requested? - params[:page] == 'true' - end - - def set_replies - @replies = page_params[:other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses - @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted]) - @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) - end - - def next_page - last_reply = @replies.last - return if last_reply.nil? - same_account = last_reply.account_id == @account.id - return unless same_account || @replies.size == DESCENDANTS_LIMIT - same_account = false unless @replies.size == DESCENDANTS_LIMIT - replies_account_status_url(@account, @status, page: true, min_id: last_reply.id, other_accounts: !same_account) + response.headers['Referrer-Policy'] = 'origin' unless @status.distributable? end - def page_params - { page: true, other_accounts: params[:other_accounts], min_id: params[:min_id] }.compact + def set_autoplay + @autoplay = truthy_param?(:autoplay) end end diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb deleted file mode 100644 index 0f0be91342b0cb01c82b72e534dca53d2c31a71e..0000000000000000000000000000000000000000 --- a/app/controllers/stream_entries_controller.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true - -class StreamEntriesController < ApplicationController - include Authorization - include SignatureVerification - - layout 'public' - - before_action :set_account - before_action :set_stream_entry - before_action :set_link_headers - before_action :check_account_suspension - before_action :set_cache_headers - - def show - respond_to do |format| - format.html do - expires_in 5.minutes, public: true unless @stream_entry.hidden? - - redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) - end - - format.atom do - expires_in 3.minutes, public: true unless @stream_entry.hidden? || @stream_entry.local_only? - - render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true)) - end - end - end - - def embed - redirect_to embed_short_account_status_url(@account, @stream_entry.activity), status: 301 - end - - private - - def set_account - @account = Account.find_local!(params[:account_username]) - end - - def set_link_headers - response.headers['Link'] = LinkHeader.new( - [ - [account_stream_entry_url(@account, @stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]], - [ActivityPub::TagManager.instance.uri_for(@stream_entry.activity), [%w(rel alternate), %w(type application/activity+json)]], - ] - ) - end - - def set_stream_entry - @stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id]) - @type = 'status' - - raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil? - authorize @stream_entry.activity, :show? if @stream_entry.hidden? || @stream_entry.local_only? - rescue Mastodon::NotPermittedError - # Reraise in order to get a 404 - raise ActiveRecord::RecordNotFound - end - - def check_account_suspension - gone if @account.suspended? - end -end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 66b18490117833a176ded1f937776f226ec348b1..77d5661b8d596b94dc6a5d0012d966a0a106b368 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -1,25 +1,29 @@ # frozen_string_literal: true class TagsController < ApplicationController + include SignatureVerification + PAGE_SIZE = 20 layout 'public' + before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? } + before_action :authenticate_user!, if: :whitelist_mode? + before_action :set_tag before_action :set_body_classes before_action :set_instance_presenter - def show - @tag = Tag.find_normalized!(params[:id]) + skip_before_action :require_functional! + def show respond_to do |format| format.html do - @initial_state_json = ActiveModelSerializers::SerializableResource.new( - InitialStatePresenter.new(settings: {}, token: current_session&.token), - serializer: InitialStateSerializer - ).to_json + expires_in 0, public: true end format.rss do + expires_in 0, public: true + @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none)).limit(PAGE_SIZE) @statuses = cache_collection(@statuses, Status) @@ -27,19 +31,22 @@ class TagsController < ApplicationController end format.json do + expires_in 3.minutes, public: public_fetch_mode? + @statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id]) @statuses = cache_collection(@statuses, Status) - render json: collection_presenter, - serializer: ActivityPub::CollectionSerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json' + render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json' end end end private + def set_tag + @tag = Tag.usable.find_normalized!(params[:id]) + end + def set_body_classes @body_classes = 'with-modals' end diff --git a/app/controllers/well_known/host_meta_controller.rb b/app/controllers/well_known/host_meta_controller.rb index 5fb70288a2a593862b775640bd62705ef538bd5e..2e9298c4ae641a9d5b0188b600ec1e92b03e06c4 100644 --- a/app/controllers/well_known/host_meta_controller.rb +++ b/app/controllers/well_known/host_meta_controller.rb @@ -13,7 +13,7 @@ module WellKnown format.xml { render content_type: 'application/xrd+xml' } end - expires_in(3.days, public: true) + expires_in 3.days, public: true end end end diff --git a/app/controllers/well_known/keybase_proof_config_controller.rb b/app/controllers/well_known/keybase_proof_config_controller.rb index eb41e586f84843d7557afcc50952d43285bd4e0d..e1d43ecbe9a1ad9c18c5944abbd8b81a2596bb05 100644 --- a/app/controllers/well_known/keybase_proof_config_controller.rb +++ b/app/controllers/well_known/keybase_proof_config_controller.rb @@ -3,7 +3,7 @@ module WellKnown class KeybaseProofConfigController < ActionController::Base def show - render json: {}, serializer: ProofProvider::Keybase::ConfigSerializer + render json: {}, serializer: ProofProvider::Keybase::ConfigSerializer, root: 'keybase_config' end end end diff --git a/app/controllers/well_known/nodeinfo_controller.rb b/app/controllers/well_known/nodeinfo_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..11a699ebc82715a68c98e495d09ee862d9b85f96 --- /dev/null +++ b/app/controllers/well_known/nodeinfo_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module WellKnown + class NodeInfoController < ActionController::Base + include CacheConcern + + before_action { response.headers['Vary'] = 'Accept' } + + def index + expires_in 3.days, public: true + render_with_cache json: {}, serializer: NodeInfo::DiscoverySerializer, adapter: NodeInfo::Adapter, expires_in: 3.days, root: 'nodeinfo' + end + + def show + expires_in 30.minutes, public: true + render_with_cache json: {}, serializer: NodeInfo::Serializer, adapter: NodeInfo::Adapter, expires_in: 30.minutes, root: 'nodeinfo' + end + end +end diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb index 28654b61d8cec6c4233a06b81e6aad7f8f7edc69..480e58f3f043fca439dbfaa762ffb09bd9ee96c9 100644 --- a/app/controllers/well_known/webfinger_controller.rb +++ b/app/controllers/well_known/webfinger_controller.rb @@ -5,34 +5,26 @@ module WellKnown include RoutingHelper before_action { response.headers['Vary'] = 'Accept' } + before_action :set_account + before_action :check_account_suspension - def show - @account = Account.find_local!(username_from_resource) - - respond_to do |format| - format.any(:json, :html) do - render json: @account, serializer: WebfingerSerializer, content_type: 'application/jrd+json' - end + rescue_from ActiveRecord::RecordNotFound, ActionController::ParameterMissing, with: :not_found - format.xml do - render content_type: 'application/xrd+xml' - end - end - - expires_in(3.days, public: true) - rescue ActiveRecord::RecordNotFound - head 404 + def show + expires_in 3.days, public: true + render json: @account, serializer: WebfingerSerializer, content_type: 'application/jrd+json' end private - def username_from_resource - resource_user = resource_param + def set_account + @account = Account.find_local!(username_from_resource) + end + def username_from_resource + resource_user = resource_param username, domain = resource_user.split('@') - if Rails.configuration.x.alternate_domains.include?(domain) - resource_user = "#{username}@#{Rails.configuration.x.local_domain}" - end + resource_user = "#{username}@#{Rails.configuration.x.local_domain}" if Rails.configuration.x.alternate_domains.include?(domain) WebfingerResource.new(resource_user).username end @@ -40,5 +32,17 @@ module WellKnown def resource_param params.require(:resource) end + + def check_account_suspension + expires_in(3.minutes, public: true) && gone if @account.suspended? + end + + def not_found + head 404 + end + + def gone + head 410 + end end end diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index e5fbb1500e997dc82084941380453e8a6b863113..1daa607745987d23dc5c93fd66fcee52ad6888c2 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -89,7 +89,7 @@ module Admin::ActionLogsHelper when 'DomainBlock', 'EmailDomainBlock' link_to record.domain, "https://#{record.domain}" when 'Status' - link_to record.account.acct, TagManager.instance.url_for(record) + link_to record.account.acct, ActivityPub::TagManager.instance.url_for(record) when 'AccountWarning' link_to record.target_account.acct, admin_account_path(record.target_account_id) end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 0bda25974f0def86c916634bcd6c610fd3134f12..8af1683e76545b3ee27987d10d3d3678bd4722b0 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -5,15 +5,16 @@ module Admin::FilterHelper REPORT_FILTERS = %i(resolved account_id target_account_id).freeze INVITE_FILTER = %i(available expired).freeze CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze - TAGS_FILTERS = %i(hidden).freeze + TAGS_FILTERS = %i(directory reviewed unreviewed pending_review popular active name).freeze INSTANCES_FILTERS = %i(limited by_domain).freeze FOLLOWERS_FILTERS = %i(relationship status by_domain activity order).freeze FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS + INSTANCES_FILTERS + FOLLOWERS_FILTERS def filter_link_to(text, link_to_params, link_class_params = link_to_params) - new_url = filtered_url_for(link_to_params) + new_url = filtered_url_for(link_to_params) new_class = filtered_url_for(link_class_params) + link_to text, new_url, class: filter_link_class(new_class) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9d113263dfda1f4bb5a1ac84384c620fe5adde24..defd97609ac5cd5599560b1ec44b3fece7bd6f4b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -77,8 +77,12 @@ module ApplicationHelper content_tag(:i, nil, attributes.merge(class: class_names.join(' '))) end - def custom_emoji_tag(custom_emoji) - image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:") + def custom_emoji_tag(custom_emoji, animate = true) + if animate + image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:") + else + image_tag(custom_emoji.image.url(:static), class: 'emojione custom-emoji', alt: ":#{custom_emoji.shortcode}", 'data-original' => full_asset_url(custom_emoji.image.url), 'data-static' => full_asset_url(custom_emoji.image.url(:static))) + end end def opengraph(property, content) @@ -122,4 +126,25 @@ module ApplicationHelper text = word_wrap(text, line_width: line_width - 2, break_sequence: break_sequence) text.split("\n").map { |line| '> ' + line }.join("\n") end + + def render_initial_state + state_params = { + settings: { + known_fediverse: Setting.show_known_fediverse_at_about_page, + }, + + text: [params[:title], params[:text], params[:url]].compact.join(' '), + } + + if user_signed_in? + state_params[:settings] = state_params[:settings].merge(Web::Setting.find_by(user: current_user)&.data || {}) + state_params[:push_subscription] = current_account.user.web_push_subscription(current_session) + state_params[:current_account] = current_account + state_params[:token] = current_session.token + state_params[:admin] = Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) + end + + json = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(state_params), serializer: InitialStateSerializer).to_json + content_tag(:script, json_escape(json).html_safe, id: 'initial-state', type: 'application/json') + end end diff --git a/app/helpers/domain_control_helper.rb b/app/helpers/domain_control_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..067b2c2cd68f723098832f13ce8d032e6451ed9b --- /dev/null +++ b/app/helpers/domain_control_helper.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module DomainControlHelper + def domain_not_allowed?(uri_or_domain) + return if uri_or_domain.blank? + + domain = begin + if uri_or_domain.include?('://') + Addressable::URI.parse(uri_or_domain).domain + else + uri_or_domain + end + end + + if whitelist_mode? + !DomainAllow.allowed?(domain) + else + DomainBlock.blocked?(domain) + end + end + + def whitelist_mode? + Rails.configuration.x.whitelist_mode + end +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index df60b7dd7ac32e8c6dc373644d0556ee90d7a336..b66e827fee2282928a576ac7cd06385d185c1863 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -21,7 +21,7 @@ module HomeHelper end end else - link_to(path || TagManager.instance.url_for(account), class: 'account__display-name') do + link_to(path || ActivityPub::TagManager.instance.url_for(account), class: 'account__display-name') do content_tag(:div, class: 'account__avatar-wrapper') do content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url(current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url)})") end + diff --git a/app/helpers/instance_helper.rb b/app/helpers/instance_helper.rb index dd0b25f3ef9ec5096b47e6616e398e615ee68465..daacb535b6a570de65bf31ba16c4f0d242bd1775 100644 --- a/app/helpers/instance_helper.rb +++ b/app/helpers/instance_helper.rb @@ -8,4 +8,16 @@ module InstanceHelper def site_hostname @site_hostname ||= Addressable::URI.parse("//#{Rails.configuration.x.local_domain}").display_uri.host end + + def description_for_sign_up + prefix = begin + if @invite.present? + I18n.t('auth.description.prefix_invited_by_user', name: @invite.user.account.username) + else + I18n.t('auth.description.prefix_sign_up') + end + end + + safe_join([prefix, I18n.t('auth.description.suffix')], ' ') + end end diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index 5b40112755a48aa8fb548621084b40967534483d..1c473efa3f54ca6c44c18a826ad96e5fd694e28e 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -16,13 +16,15 @@ module JsonLdHelper # The url attribute can be a string, an array of strings, or an array of objects. # The objects could include a mimeType. Not-included mimeType means it's text/html. def url_to_href(value, preferred_type = nil) - single_value = if value.is_a?(Array) && !value.first.is_a?(String) - value.find { |link| preferred_type.nil? || ((link['mimeType'].presence || 'text/html') == preferred_type) } - elsif value.is_a?(Array) - value.first - else - value - end + single_value = begin + if value.is_a?(Array) && !value.first.is_a?(String) + value.find { |link| preferred_type.nil? || ((link['mimeType'].presence || 'text/html') == preferred_type) } + elsif value.is_a?(Array) + value.first + else + value + end + end if single_value.nil? || single_value.is_a?(String) single_value @@ -64,7 +66,9 @@ module JsonLdHelper def fetch_resource(uri, id, on_behalf_of = nil) unless id json = fetch_resource_without_id_validation(uri, on_behalf_of) + return unless json + uri = json['id'] end @@ -73,25 +77,20 @@ module JsonLdHelper end def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false) + on_behalf_of ||= Account.representative + build_request(uri, on_behalf_of).perform do |response| - unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error - raise Mastodon::UnexpectedResponseError, response - end - return body_to_json(response.body_with_limit) if response.code == 200 - end - # If request failed, retry without doing it on behalf of a user - return if on_behalf_of.nil? - build_request(uri).perform do |response| - unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error - raise Mastodon::UnexpectedResponseError, response - end - response.code == 200 ? body_to_json(response.body_with_limit) : nil + raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error + + body_to_json(response.body_with_limit) if response.code == 200 end end def body_to_json(body, compare_id: nil) json = body.is_a?(String) ? Oj.load(body, mode: :strict) : body + return if compare_id.present? && json['id'] != compare_id + json rescue Oj::ParseError nil @@ -105,35 +104,34 @@ module JsonLdHelper end end - private - def response_successful?(response) (200...300).cover?(response.code) end def response_error_unsalvageable?(response) - (400...500).cover?(response.code) && response.code != 429 + response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code)) end def build_request(uri, on_behalf_of = nil) - request = Request.new(:get, uri) - request.on_behalf_of(on_behalf_of) if on_behalf_of - request.add_headers('Accept' => 'application/activity+json, application/ld+json') - request + Request.new(:get, uri).tap do |request| + request.on_behalf_of(on_behalf_of) if on_behalf_of + request.add_headers('Accept' => 'application/activity+json, application/ld+json') + end end def load_jsonld_context(url, _options = {}, &_block) json = Rails.cache.fetch("jsonld:context:#{url}", expires_in: 30.days, raw: true) do request = Request.new(:get, url) request.add_headers('Accept' => 'application/ld+json') - request.perform do |res| raise JSON::LD::JsonLdError::LoadingDocumentFailed unless res.code == 200 && res.mime_type == 'application/ld+json' + res.body_with_limit end end - doc = JSON::LD::API::RemoteDocument.new(url, json) + doc = JSON::LD::API::RemoteDocument.new(json, documentUrl: url) + block_given? ? yield(doc) : doc end end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 92bc222ea9dfd96c023bcd196a7afa83da55d7e8..aa0a4d4674c9d8c39becbe6f1b5cfc54409aa1da 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -2,11 +2,11 @@ module SettingsHelper HUMAN_LOCALES = { - en: 'English', ar: 'العربية', ast: 'Asturianu', bg: 'БългарÑки', bn: 'বাংলা', + br: 'Breton', ca: 'Català ', co: 'Corsu', cs: 'ÄŒeÅ¡tina', @@ -14,8 +14,11 @@ module SettingsHelper da: 'Dansk', de: 'Deutsch', el: 'Ελληνικά', + en: 'English', eo: 'Esperanto', + 'es-AR': 'Español (Argentina)', es: 'Español', + et: 'Eesti', eu: 'Euskara', fa: 'ÙØ§Ø±Ø³ÛŒ', fi: 'Suomi', @@ -36,31 +39,34 @@ module SettingsHelper ko: '한êµì–´', lt: 'Lietuvių', lv: 'LatvieÅ¡u', + mk: 'МакедонÑки', ml: 'മലയാളം', ms: 'Bahasa Melayu', nl: 'Nederlands', + nn: 'Nynorsk', no: 'Norsk', oc: 'Occitan', pl: 'Polski', + 'pt-BR': 'Português (Brasil)', + 'pt-PT': 'Português (Portugal)', pt: 'Português', - 'pt-BR': 'Português do Brasil', ro: 'Română', ru: 'РуÑÑкий', sk: 'SlovenÄina', sl: 'SlovenÅ¡Äina', sq: 'Shqip', - sr: 'СрпÑки', 'sr-Latn': 'Srpski (latinica)', + sr: 'СрпÑки', sv: 'Svenska', ta: 'தமிழà¯', te: 'తెలà±à°—à±', th: 'ไทย', tr: 'Türkçe', uk: 'УкраїнÑька', - zh: '䏿–‡', 'zh-CN': 'ç®€ä½“ä¸æ–‡', 'zh-HK': 'ç¹é«”䏿–‡ï¼ˆé¦™æ¸¯ï¼‰', 'zh-TW': 'ç¹é«”䏿–‡ï¼ˆè‡ºç£ï¼‰', + zh: '䏿–‡', }.freeze def human_locale(locale) @@ -86,4 +92,12 @@ module SettingsHelper 'desktop' end end + + def compact_account_link_to(account) + return if account.nil? + + link_to ActivityPub::TagManager.instance.url_for(account), class: 'name-tag', title: account.acct do + safe_join([image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), content_tag(:span, account.acct, class: 'username')], ' ') + end + end end diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/statuses_helper.rb similarity index 87% rename from app/helpers/stream_entries_helper.rb rename to app/helpers/statuses_helper.rb index 02a860a74895399d509d9db05a8c093b5fce45bc..8380b3c4270d727419c8b51c572b1ed306cb9579 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module StreamEntriesHelper +module StatusesHelper EMBEDDED_CONTROLLER = 'statuses' EMBEDDED_ACTION = 'embed' @@ -34,6 +34,26 @@ module StreamEntriesHelper end end + def minimal_account_action_button(account) + if user_signed_in? + return if account.id == current_user.account_id + + if current_account.following?(account) || current_account.requested?(account) + link_to account_unfollow_path(account), class: 'icon-button active', data: { method: :post }, title: t('accounts.unfollow') do + fa_icon('user-times fw') + end + elsif !(account.memorial? || account.moved?) + link_to account_follow_path(account), class: "icon-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post }, title: t('accounts.follow') do + fa_icon('user-plus fw') + end + end + elsif !(account.memorial? || account.moved?) + link_to account_remote_follow_path(account), class: 'icon-button modal-button', target: '_new', title: t('accounts.follow') do + fa_icon('user-plus fw') + end + end + end + def svg_logo content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976') end @@ -109,11 +129,13 @@ module StreamEntriesHelper def status_text_summary(status) return if status.spoiler_text.blank? + I18n.t('statuses.content_warning', warning: status.spoiler_text) end def poll_summary(status) return unless status.preloadable_poll + status.preloadable_poll.options.map { |o| "[ ] #{o}" }.join("\n") end diff --git a/app/javascript/mastodon/actions/alerts.js b/app/javascript/mastodon/actions/alerts.js index ef2500e7bdd84fb6a3cd353991a07ae1371f5bd7..cd36d8007bcceeae91a90c9a8abce257d6f3d858 100644 --- a/app/javascript/mastodon/actions/alerts.js +++ b/app/javascript/mastodon/actions/alerts.js @@ -3,6 +3,8 @@ import { defineMessages } from 'react-intl'; const messages = defineMessages({ unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' }, + rateLimitedTitle: { id: 'alert.rate_limited.title', defaultMessage: 'Rate limited' }, + rateLimitedMessage: { id: 'alert.rate_limited.message', defaultMessage: 'Please retry after {retry_time, time, medium}.' }, }); export const ALERT_SHOW = 'ALERT_SHOW'; @@ -23,23 +25,29 @@ export function clearAlert() { }; }; -export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage) { +export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) { return { type: ALERT_SHOW, title, message, + message_values, }; }; export function showAlertForError(error) { if (error.response) { - const { data, status, statusText } = error.response; + const { data, status, statusText, headers } = error.response; if (status === 404 || status === 410) { // Skip these errors as they are reflected in the UI return { type: ALERT_NOOP }; } + if (status === 429 && headers['x-ratelimit-reset']) { + const reset_date = new Date(headers['x-ratelimit-reset']); + return showAlert(messages.rateLimitedTitle, messages.rateLimitedMessage, { 'retry_time': reset_date }); + } + let message = statusText; let title = `${status}`; diff --git a/app/javascript/mastodon/actions/app.js b/app/javascript/mastodon/actions/app.js new file mode 100644 index 0000000000000000000000000000000000000000..414968f7de47cfef7cf1abe27742efc03ed92959 --- /dev/null +++ b/app/javascript/mastodon/actions/app.js @@ -0,0 +1,10 @@ +export const APP_FOCUS = 'APP_FOCUS'; +export const APP_UNFOCUS = 'APP_UNFOCUS'; + +export const focusApp = () => ({ + type: APP_FOCUS, +}); + +export const unfocusApp = () => ({ + type: APP_UNFOCUS, +}); diff --git a/app/javascript/mastodon/actions/blocks.js b/app/javascript/mastodon/actions/blocks.js index 7000f5a71cb66bfd38744b3ee98915300d407c2b..fd9881302a652fc3fb36c8a5dfaa7a831a23c15d 100644 --- a/app/javascript/mastodon/actions/blocks.js +++ b/app/javascript/mastodon/actions/blocks.js @@ -1,6 +1,7 @@ import api, { getLinks } from '../api'; import { fetchRelationships } from './accounts'; import { importFetchedAccounts } from './importer'; +import { openModal } from './modal'; export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST'; export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS'; @@ -10,6 +11,8 @@ export const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST'; export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS'; export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL'; +export const BLOCKS_INIT_MODAL = 'BLOCKS_INIT_MODAL'; + export function fetchBlocks() { return (dispatch, getState) => { dispatch(fetchBlocksRequest()); @@ -83,3 +86,14 @@ export function expandBlocksFail(error) { error, }; }; + +export function initBlockModal(account) { + return dispatch => { + dispatch({ + type: BLOCKS_INIT_MODAL, + account, + }); + + dispatch(openModal('BLOCK')); + }; +} diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index d27901c1bb4e321739684379530dac64bb31f94d..2dcb81035de9c483f89a5132bc0c78191e3f7588 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -11,7 +11,7 @@ import { showAlertForError } from './alerts'; import { showAlert } from './alerts'; import { defineMessages } from 'react-intl'; -let cancelFetchComposeSuggestionsAccounts; +let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags; export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; @@ -236,7 +236,7 @@ export function uploadCompose(files) { progress[i] = loaded; dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total)); }, - }).then(({ data }) => dispatch(uploadComposeSuccess(data))); + }).then(({ data }) => dispatch(uploadComposeSuccess(data, f))); }).catch(error => dispatch(uploadComposeFail(error))); }; }; @@ -291,10 +291,11 @@ export function uploadComposeProgress(loaded, total) { }; }; -export function uploadComposeSuccess(media) { +export function uploadComposeSuccess(media, file) { return { type: COMPOSE_UPLOAD_SUCCESS, media: media, + file: file, skipLoading: true, }; }; @@ -327,10 +328,12 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => if (cancelFetchComposeSuggestionsAccounts) { cancelFetchComposeSuggestionsAccounts(); } + api(getState).get('/api/v1/accounts/search', { cancelToken: new CancelToken(cancel => { cancelFetchComposeSuggestionsAccounts = cancel; }), + params: { q: token.slice(1), resolve: false, @@ -351,9 +354,33 @@ const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => { dispatch(readyComposeSuggestionsEmojis(token, results)); }; -const fetchComposeSuggestionsTags = (dispatch, getState, token) => { +const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => { + if (cancelFetchComposeSuggestionsTags) { + cancelFetchComposeSuggestionsTags(); + } + dispatch(updateSuggestionTags(token)); -}; + + api(getState).get('/api/v2/search', { + cancelToken: new CancelToken(cancel => { + cancelFetchComposeSuggestionsTags = cancel; + }), + + params: { + type: 'hashtags', + q: token.slice(1), + resolve: false, + limit: 4, + exclude_unreviewed: true, + }, + }).then(({ data }) => { + dispatch(readyComposeSuggestionsTags(token, data.hashtags)); + }).catch(error => { + if (!isCancel(error)) { + dispatch(showAlertForError(error)); + } + }); +}, 200, { leading: true, trailing: true }); export function fetchComposeSuggestions(token) { return (dispatch, getState) => { @@ -387,20 +414,26 @@ export function readyComposeSuggestionsAccounts(token, accounts) { }; }; +export const readyComposeSuggestionsTags = (token, tags) => ({ + type: COMPOSE_SUGGESTIONS_READY, + token, + tags, +}); + export function selectComposeSuggestion(position, token, suggestion, path) { return (dispatch, getState) => { let completion, startPosition; - if (typeof suggestion === 'object' && suggestion.id) { + if (suggestion.type === 'emoji') { completion = suggestion.native || suggestion.colons; startPosition = position - 1; dispatch(useEmoji(suggestion)); - } else if (suggestion[0] === '#') { - completion = suggestion; + } else if (suggestion.type === 'hashtag') { + completion = `#${suggestion.name}`; startPosition = position - 1; - } else { - completion = getState().getIn(['accounts', suggestion, 'acct']); + } else if (suggestion.type === 'account') { + completion = getState().getIn(['accounts', suggestion.id, 'acct']); startPosition = position; } diff --git a/app/javascript/mastodon/actions/conversations.js b/app/javascript/mastodon/actions/conversations.js index c6e062ef73b2e5129aa9ebce8254c77f5d7b59a9..4ef654b1f902413a81a2ba0274e9252754e7836c 100644 --- a/app/javascript/mastodon/actions/conversations.js +++ b/app/javascript/mastodon/actions/conversations.js @@ -15,6 +15,10 @@ export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE'; export const CONVERSATIONS_READ = 'CONVERSATIONS_READ'; +export const CONVERSATIONS_DELETE_REQUEST = 'CONVERSATIONS_DELETE_REQUEST'; +export const CONVERSATIONS_DELETE_SUCCESS = 'CONVERSATIONS_DELETE_SUCCESS'; +export const CONVERSATIONS_DELETE_FAIL = 'CONVERSATIONS_DELETE_FAIL'; + export const mountConversations = () => ({ type: CONVERSATIONS_MOUNT, }); @@ -82,3 +86,27 @@ export const updateConversations = conversation => dispatch => { conversation, }); }; + +export const deleteConversation = conversationId => (dispatch, getState) => { + dispatch(deleteConversationRequest(conversationId)); + + api(getState).delete(`/api/v1/conversations/${conversationId}`) + .then(() => dispatch(deleteConversationSuccess(conversationId))) + .catch(error => dispatch(deleteConversationFail(conversationId, error))); +}; + +export const deleteConversationRequest = id => ({ + type: CONVERSATIONS_DELETE_REQUEST, + id, +}); + +export const deleteConversationSuccess = id => ({ + type: CONVERSATIONS_DELETE_SUCCESS, + id, +}); + +export const deleteConversationFail = (id, error) => ({ + type: CONVERSATIONS_DELETE_FAIL, + id, + error, +}); diff --git a/app/javascript/mastodon/actions/directory.js b/app/javascript/mastodon/actions/directory.js new file mode 100644 index 0000000000000000000000000000000000000000..4b2b6dd56dce58aa588f71b75a449bd804415afe --- /dev/null +++ b/app/javascript/mastodon/actions/directory.js @@ -0,0 +1,61 @@ +import api from '../api'; +import { importFetchedAccounts } from './importer'; +import { fetchRelationships } from './accounts'; + +export const DIRECTORY_FETCH_REQUEST = 'DIRECTORY_FETCH_REQUEST'; +export const DIRECTORY_FETCH_SUCCESS = 'DIRECTORY_FETCH_SUCCESS'; +export const DIRECTORY_FETCH_FAIL = 'DIRECTORY_FETCH_FAIL'; + +export const DIRECTORY_EXPAND_REQUEST = 'DIRECTORY_EXPAND_REQUEST'; +export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS'; +export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL'; + +export const fetchDirectory = params => (dispatch, getState) => { + dispatch(fetchDirectoryRequest()); + + api(getState).get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => { + dispatch(importFetchedAccounts(data)); + dispatch(fetchDirectorySuccess(data)); + dispatch(fetchRelationships(data.map(x => x.id))); + }).catch(error => dispatch(fetchDirectoryFail(error))); +}; + +export const fetchDirectoryRequest = () => ({ + type: DIRECTORY_FETCH_REQUEST, +}); + +export const fetchDirectorySuccess = accounts => ({ + type: DIRECTORY_FETCH_SUCCESS, + accounts, +}); + +export const fetchDirectoryFail = error => ({ + type: DIRECTORY_FETCH_FAIL, + error, +}); + +export const expandDirectory = params => (dispatch, getState) => { + dispatch(expandDirectoryRequest()); + + const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size; + + api(getState).get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => { + dispatch(importFetchedAccounts(data)); + dispatch(expandDirectorySuccess(data)); + dispatch(fetchRelationships(data.map(x => x.id))); + }).catch(error => dispatch(expandDirectoryFail(error))); +}; + +export const expandDirectoryRequest = () => ({ + type: DIRECTORY_EXPAND_REQUEST, +}); + +export const expandDirectorySuccess = accounts => ({ + type: DIRECTORY_EXPAND_SUCCESS, + accounts, +}); + +export const expandDirectoryFail = error => ({ + type: DIRECTORY_EXPAND_FAIL, + error, +}); diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 5e7e78e698484674d3b7814d6d820f4839b0ad6e..f7108fdb9017ced70a54db2b4dd6513e15ba9a9e 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -73,8 +73,9 @@ export function normalizePoll(poll) { const emojiMap = makeEmojiMap(normalPoll); - normalPoll.options = poll.options.map(option => ({ + normalPoll.options = poll.options.map((option, index) => ({ ...option, + voted: poll.own_votes && poll.own_votes.includes(index), title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap), })); diff --git a/app/javascript/mastodon/actions/markers.js b/app/javascript/mastodon/actions/markers.js new file mode 100644 index 0000000000000000000000000000000000000000..c3a5fe86f1460c51d38562a6ddfff41163e1b98b --- /dev/null +++ b/app/javascript/mastodon/actions/markers.js @@ -0,0 +1,30 @@ +export const submitMarkers = () => (dispatch, getState) => { + const accessToken = getState().getIn(['meta', 'access_token'], ''); + const params = {}; + + const lastHomeId = getState().getIn(['timelines', 'home', 'items', 0]); + const lastNotificationId = getState().getIn(['notifications', 'items', 0, 'id']); + + if (lastHomeId) { + params.home = { + last_read_id: lastHomeId, + }; + } + + if (lastNotificationId) { + params.notifications = { + last_read_id: lastNotificationId, + }; + } + + if (Object.keys(params).length === 0) { + return; + } + + const client = new XMLHttpRequest(); + + client.open('POST', '/api/v1/markers', false); + client.setRequestHeader('Content-Type', 'application/json'); + client.setRequestHeader('Authorization', `Bearer ${accessToken}`); + client.send(JSON.stringify(params)); +}; diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 56c952cb05866b3a0dbeaf97d4f06be7d4647842..58803d1ae5e442a2a59624ba4802c0eab540be44 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -12,6 +12,8 @@ import { defineMessages } from 'react-intl'; import { List as ImmutableList } from 'immutable'; import { unescapeHTML } from '../utils/html'; import { getFiltersRegex } from '../selectors'; +import { usePendingItems as preferPendingItems } from 'mastodon/initial_state'; +import compareId from 'mastodon/compare_id'; export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; @@ -22,8 +24,12 @@ export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; -export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; -export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; +export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; +export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; +export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; + +export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT'; +export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT'; defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, @@ -38,6 +44,10 @@ const fetchRelatedRelationships = (dispatch, notifications) => { } }; +export const loadPending = () => ({ + type: NOTIFICATIONS_LOAD_PENDING, +}); + export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true); @@ -69,6 +79,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) { dispatch({ type: NOTIFICATIONS_UPDATE, notification, + usePendingItems: preferPendingItems, meta: (playSound && !filtered) ? { sound: 'boop' } : undefined, }); @@ -122,10 +133,19 @@ export function expandNotifications({ maxId } = {}, done = noOp) { : excludeTypesFromFilter(activeFilter), }; - if (!maxId && notifications.get('items').size > 0) { - params.since_id = notifications.getIn(['items', 0, 'id']); + if (!params.max_id && (notifications.get('items', ImmutableList()).size + notifications.get('pendingItems', ImmutableList()).size) > 0) { + const a = notifications.getIn(['pendingItems', 0, 'id']); + const b = notifications.getIn(['items', 0, 'id']); + + if (a && b && compareId(a, b) > 0) { + params.since_id = a; + } else { + params.since_id = b || a; + } } + const isLoadingRecent = !!params.since_id; + dispatch(expandNotificationsRequest(isLoadingMore)); api(getState).get('/api/v1/notifications', { params }).then(response => { @@ -134,7 +154,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { dispatch(importFetchedAccounts(response.data.map(item => item.account))); dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status))); - dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore)); + dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems)); fetchRelatedRelationships(dispatch, response.data); done(); }).catch(error => { @@ -151,11 +171,13 @@ export function expandNotificationsRequest(isLoadingMore) { }; }; -export function expandNotificationsSuccess(notifications, next, isLoadingMore) { +export function expandNotificationsSuccess(notifications, next, isLoadingMore, isLoadingRecent, usePendingItems) { return { type: NOTIFICATIONS_EXPAND_SUCCESS, notifications, next, + isLoadingRecent: isLoadingRecent, + usePendingItems, skipLoading: !isLoadingMore, }; }; @@ -196,3 +218,11 @@ export function setFilter (filterType) { dispatch(saveSettings()); }; }; + +export const mountNotifications = () => ({ + type: NOTIFICATIONS_MOUNT, +}); + +export const unmountNotifications = () => ({ + type: NOTIFICATIONS_UNMOUNT, +}); diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js index 0974fdd15e6a5de30e318bade3ccbe4a73b58da4..a178faeadd9a128740d621676cb285017d4c6bba 100644 --- a/app/javascript/mastodon/actions/search.js +++ b/app/javascript/mastodon/actions/search.js @@ -10,6 +10,10 @@ export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST'; export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS'; export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL'; +export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST'; +export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS'; +export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL'; + export function changeSearch(value) { return { type: SEARCH_CHANGE, @@ -77,8 +81,50 @@ export function fetchSearchFail(error) { }; }; -export function showSearch() { - return { - type: SEARCH_SHOW, - }; +export const expandSearch = type => (dispatch, getState) => { + const value = getState().getIn(['search', 'value']); + const offset = getState().getIn(['search', 'results', type]).size; + + dispatch(expandSearchRequest()); + + api(getState).get('/api/v2/search', { + params: { + q: value, + type, + offset, + }, + }).then(({ data }) => { + if (data.accounts) { + dispatch(importFetchedAccounts(data.accounts)); + } + + if (data.statuses) { + dispatch(importFetchedStatuses(data.statuses)); + } + + dispatch(expandSearchSuccess(data, value, type)); + dispatch(fetchRelationships(data.accounts.map(item => item.id))); + }).catch(error => { + dispatch(expandSearchFail(error)); + }); }; + +export const expandSearchRequest = () => ({ + type: SEARCH_EXPAND_REQUEST, +}); + +export const expandSearchSuccess = (results, searchTerm, searchType) => ({ + type: SEARCH_EXPAND_SUCCESS, + results, + searchTerm, + searchType, +}); + +export const expandSearchFail = error => ({ + type: SEARCH_EXPAND_FAIL, + error, +}); + +export const showSearch = () => ({ + type: SEARCH_SHOW, +}); diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 06c21b96b7f95e016bab3961a5a9dc3ec579ffc2..bc2ac5e82371a75e6bc2f5e76a8f2a4db24868c6 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -1,6 +1,8 @@ import { importFetchedStatus, importFetchedStatuses } from './importer'; -import api, { getLinks } from '../api'; +import api, { getLinks } from 'mastodon/api'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import compareId from 'mastodon/compare_id'; +import { usePendingItems as preferPendingItems } from 'mastodon/initial_state'; export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; export const TIMELINE_DELETE = 'TIMELINE_DELETE'; @@ -10,10 +12,15 @@ export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST'; export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS'; export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL'; -export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP'; +export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP'; +export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING'; +export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; +export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'; -export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'; -export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; +export const loadPending = timeline => ({ + type: TIMELINE_LOAD_PENDING, + timeline, +}); export function updateTimeline(timeline, status, accept) { return dispatch => { @@ -27,6 +34,7 @@ export function updateTimeline(timeline, status, accept) { type: TIMELINE_UPDATE, timeline, status, + usePendingItems: preferPendingItems, }); }; }; @@ -71,8 +79,15 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) { return; } - if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) { - params.since_id = timeline.getIn(['items', 0]); + if (!params.max_id && !params.pinned && (timeline.get('items', ImmutableList()).size + timeline.get('pendingItems', ImmutableList()).size) > 0) { + const a = timeline.getIn(['pendingItems', 0]); + const b = timeline.getIn(['items', 0]); + + if (a && b && compareId(a, b) > 0) { + params.since_id = a; + } else { + params.since_id = b || a; + } } const isLoadingRecent = !!params.since_id; @@ -82,7 +97,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) { api(getState).get(path, { params }).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedStatuses(response.data)); - dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore)); + dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems)); done(); }).catch(error => { dispatch(expandTimelineFail(timelineId, error, isLoadingMore)); @@ -115,7 +130,7 @@ export function expandTimelineRequest(timeline, isLoadingMore) { }; }; -export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) { +export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) { return { type: TIMELINE_EXPAND_SUCCESS, timeline, @@ -123,6 +138,7 @@ export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadi next, partial, isLoadingRecent, + usePendingItems, skipLoading: !isLoadingMore, }; }; @@ -151,9 +167,8 @@ export function connectTimeline(timeline) { }; }; -export function disconnectTimeline(timeline) { - return { - type: TIMELINE_DISCONNECT, - timeline, - }; -}; +export const disconnectTimeline = timeline => ({ + type: TIMELINE_DISCONNECT, + timeline, + usePendingItems: preferPendingItems, +}); diff --git a/app/javascript/mastodon/actions/trends.js b/app/javascript/mastodon/actions/trends.js new file mode 100644 index 0000000000000000000000000000000000000000..853e4f60aec711f0d3437163fa3e8a680e0869cb --- /dev/null +++ b/app/javascript/mastodon/actions/trends.js @@ -0,0 +1,32 @@ +import api from '../api'; + +export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST'; +export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS'; +export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL'; + +export const fetchTrends = () => (dispatch, getState) => { + dispatch(fetchTrendsRequest()); + + api(getState) + .get('/api/v1/trends') + .then(({ data }) => dispatch(fetchTrendsSuccess(data))) + .catch(err => dispatch(fetchTrendsFail(err))); +}; + +export const fetchTrendsRequest = () => ({ + type: TRENDS_FETCH_REQUEST, + skipLoading: true, +}); + +export const fetchTrendsSuccess = trends => ({ + type: TRENDS_FETCH_SUCCESS, + trends, + skipLoading: true, +}); + +export const fetchTrendsFail = error => ({ + type: TRENDS_FETCH_FAIL, + error, + skipLoading: true, + skipAlert: true, +}); diff --git a/app/javascript/mastodon/compare_id.js b/app/javascript/mastodon/compare_id.js index aaff66481ac7fd1b8cf6f59a5bef1497e0482279..66cf51c4b67c11b53b93e4dff8c88f678d61ea33 100644 --- a/app/javascript/mastodon/compare_id.js +++ b/app/javascript/mastodon/compare_id.js @@ -1,10 +1,11 @@ -export default function compareId(id1, id2) { +export default function compareId (id1, id2) { if (id1 === id2) { return 0; } + if (id1.length === id2.length) { return id1 > id2 ? 1 : -1; } else { return id1.length > id2.length ? 1 : -1; } -} +}; diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.js b/app/javascript/mastodon/components/autosuggest_hashtag.js new file mode 100644 index 0000000000000000000000000000000000000000..e2f4e320dd1db663f8d416e7447216a19c0a56ca --- /dev/null +++ b/app/javascript/mastodon/components/autosuggest_hashtag.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { shortNumberFormat } from 'mastodon/utils/numbers'; +import { FormattedMessage } from 'react-intl'; + +export default class AutosuggestHashtag extends React.PureComponent { + + static propTypes = { + tag: PropTypes.shape({ + name: PropTypes.string.isRequired, + url: PropTypes.string, + history: PropTypes.array, + }).isRequired, + }; + + render () { + const { tag } = this.props; + const weeklyUses = tag.history && shortNumberFormat(tag.history.reduce((total, day) => total + (day.uses * 1), 0)); + + return ( + <div className='autosuggest-hashtag'> + <div className='autosuggest-hashtag__name'>#<strong>{tag.name}</strong></div> + {tag.history !== undefined && <div className='autosuggest-hashtag__uses'><FormattedMessage id='autosuggest_hashtag.per_week' defaultMessage='{count} per week' values={{ count: weeklyUses }} /></div>} + </div> + ); + } + +} diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.js index c7d965b53ac5e284662512054f59ef57fbca7b8c..6d2035add0184c51dfa1a4522240e8fe4845c781 100644 --- a/app/javascript/mastodon/components/autosuggest_input.js +++ b/app/javascript/mastodon/components/autosuggest_input.js @@ -1,6 +1,7 @@ import React from 'react'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import AutosuggestEmoji from './autosuggest_emoji'; +import AutosuggestHashtag from './autosuggest_hashtag'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import { isRtl } from '../rtl'; @@ -167,15 +168,15 @@ export default class AutosuggestInput extends ImmutablePureComponent { const { selectedSuggestion } = this.state; let inner, key; - if (typeof suggestion === 'object') { + if (suggestion.type === 'emoji') { inner = <AutosuggestEmoji emoji={suggestion} />; key = suggestion.id; - } else if (suggestion[0] === '#') { - inner = suggestion; - key = suggestion; - } else { - inner = <AutosuggestAccountContainer id={suggestion} />; - key = suggestion; + } else if (suggestion.type ==='hashtag') { + inner = <AutosuggestHashtag tag={suggestion} />; + key = suggestion.name; + } else if (suggestion.type === 'account') { + inner = <AutosuggestAccountContainer id={suggestion.id} />; + key = suggestion.id; } return ( diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index b070fe3e5156d65353c46cce00cf8fb7f0509a3d..ac2a6366a07025a506efeab7af5bf64ba1fc6165 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -1,6 +1,7 @@ import React from 'react'; import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import AutosuggestEmoji from './autosuggest_emoji'; +import AutosuggestHashtag from './autosuggest_hashtag'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import { isRtl } from '../rtl'; @@ -173,15 +174,15 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { const { selectedSuggestion } = this.state; let inner, key; - if (typeof suggestion === 'object') { + if (suggestion.type === 'emoji') { inner = <AutosuggestEmoji emoji={suggestion} />; key = suggestion.id; - } else if (suggestion[0] === '#') { - inner = suggestion; - key = suggestion; - } else { - inner = <AutosuggestAccountContainer id={suggestion} />; - key = suggestion; + } else if (suggestion.type === 'hashtag') { + inner = <AutosuggestHashtag tag={suggestion} />; + key = suggestion.name; + } else if (suggestion.type === 'account') { + inner = <AutosuggestAccountContainer id={suggestion.id} />; + key = suggestion.id; } return ( diff --git a/app/javascript/mastodon/components/avatar_composite.js b/app/javascript/mastodon/components/avatar_composite.js index 4a9a73c512e2cc0dc18649e331ea245c6bf913ca..5d5b897492fec082ba26453f98153b293a096b12 100644 --- a/app/javascript/mastodon/components/avatar_composite.js +++ b/app/javascript/mastodon/components/avatar_composite.js @@ -35,35 +35,35 @@ export default class AvatarComposite extends React.PureComponent { if (size === 2) { if (index === 0) { - right = '2px'; + right = '1px'; } else { - left = '2px'; + left = '1px'; } } else if (size === 3) { if (index === 0) { - right = '2px'; + right = '1px'; } else if (index > 0) { - left = '2px'; + left = '1px'; } if (index === 1) { - bottom = '2px'; + bottom = '1px'; } else if (index > 1) { - top = '2px'; + top = '1px'; } } else if (size === 4) { if (index === 0 || index === 2) { - right = '2px'; + right = '1px'; } if (index === 1 || index === 3) { - left = '2px'; + left = '1px'; } if (index < 2) { - bottom = '2px'; + bottom = '1px'; } else { - top = '2px'; + top = '1px'; } } @@ -88,7 +88,13 @@ export default class AvatarComposite extends React.PureComponent { return ( <div className='account__avatar-composite' style={{ width: `${size}px`, height: `${size}px` }}> - {accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))} + {accounts.take(4).map((account, i) => this.renderItem(account, Math.min(accounts.size, 4), i))} + + {accounts.size > 4 && ( + <span className='account__avatar-composite__label'> + +{accounts.size - 4} + </span> + )} </div> ); } diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.js index d453874636ed50261fabca67f0a61e2907111b06..55e3bfd5e0b4312e4cf8ade94b8a0556971f4810 100644 --- a/app/javascript/mastodon/components/column.js +++ b/app/javascript/mastodon/components/column.js @@ -8,10 +8,11 @@ export default class Column extends React.PureComponent { static propTypes = { children: PropTypes.node, label: PropTypes.string, + bindToDocument: PropTypes.bool, }; scrollTop () { - const scrollable = this.node.querySelector('.scrollable'); + const scrollable = this.props.bindToDocument ? document.scrollingElement : this.node.querySelector('.scrollable'); if (!scrollable) { return; @@ -33,11 +34,19 @@ export default class Column extends React.PureComponent { } componentDidMount () { - this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + if (this.props.bindToDocument) { + document.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + } else { + this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + } } componentWillUnmount () { - this.node.removeEventListener('wheel', this.handleWheel); + if (this.props.bindToDocument) { + document.removeEventListener('wheel', this.handleWheel); + } else { + this.node.removeEventListener('wheel', this.handleWheel); + } } render () { diff --git a/app/javascript/mastodon/components/column_back_button.js b/app/javascript/mastodon/components/column_back_button.js index f41045787e47edbc70db7f9d4cafefb1b933b831..d97622705e588b8951cbf0197c714ae0a37fe07e 100644 --- a/app/javascript/mastodon/components/column_back_button.js +++ b/app/javascript/mastodon/components/column_back_button.js @@ -2,6 +2,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import Icon from 'mastodon/components/icon'; +import { createPortal } from 'react-dom'; export default class ColumnBackButton extends React.PureComponent { @@ -9,6 +10,10 @@ export default class ColumnBackButton extends React.PureComponent { router: PropTypes.object, }; + static propTypes = { + multiColumn: PropTypes.bool, + }; + handleClick = () => { if (window.history && window.history.length === 1) { this.context.router.history.push('/'); @@ -18,12 +23,32 @@ export default class ColumnBackButton extends React.PureComponent { } render () { - return ( + const { multiColumn } = this.props; + + const component = ( <button onClick={this.handleClick} className='column-back-button'> <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> </button> ); + + if (multiColumn) { + return component; + } else { + // The portal container and the component may be rendered to the DOM in + // the same React render pass, so the container might not be available at + // the time `render()` is called. + const container = document.getElementById('tabs-bar__portal'); + if (container === null) { + // The container wasn't available, force a re-render so that the + // component can eventually be inserted in the container and not scroll + // with the rest of the area. + this.forceUpdate(); + return component; + } else { + return createPortal(component, container); + } + } } } diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index f33c689e7c6d1612767755a43201e62ed90906bc..0038995c8be0fb76fda78ea381a37c132bae57ca 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { createPortal } from 'react-dom'; import classNames from 'classnames'; import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import Icon from 'mastodon/components/icon'; @@ -28,6 +29,7 @@ class ColumnHeader extends React.PureComponent { showBackButton: PropTypes.bool, children: PropTypes.node, pinned: PropTypes.bool, + placeholder: PropTypes.bool, onPin: PropTypes.func, onMove: PropTypes.func, onClick: PropTypes.func, @@ -79,7 +81,7 @@ class ColumnHeader extends React.PureComponent { } render () { - const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage } } = this.props; + const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props; const { collapsed, animating } = this.state; const wrapperClassName = classNames('column-header__wrapper', { @@ -118,7 +120,7 @@ class ColumnHeader extends React.PureComponent { <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='text-btn column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button> </div> ); - } else if (multiColumn) { + } else if (multiColumn && this.props.onPin) { pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>; } @@ -140,13 +142,13 @@ class ColumnHeader extends React.PureComponent { collapsedContent.push(pinButton); } - if (children || multiColumn) { + if (children || (multiColumn && this.props.onPin)) { collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><Icon id='sliders' /></button>; } const hasTitle = icon && title; - return ( + const component = ( <div className={wrapperClassName}> <h1 className={buttonClassName}> {hasTitle && ( @@ -172,6 +174,24 @@ class ColumnHeader extends React.PureComponent { </div> </div> ); + + if (multiColumn || placeholder) { + return component; + } else { + // The portal container and the component may be rendered to the DOM in + // the same React render pass, so the container might not be available at + // the time `render()` is called. + const container = document.getElementById('tabs-bar__portal'); + if (container === null) { + // The container wasn't available, force a re-render so that the + // component can eventually be inserted in the container and not scroll + // with the rest of the area. + this.forceUpdate(); + return component; + } else { + return createPortal(component, container); + } + } } } diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.js index d1ca5bf756a3513be65736fadaae74e8671b557e..82543e1185ade5003dff9d1929545a0f1f6128ab 100644 --- a/app/javascript/mastodon/components/error_boundary.js +++ b/app/javascript/mastodon/components/error_boundary.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import illustration from '../../images/elephant_ui_disappointed.svg'; +import { FormattedMessage } from 'react-intl'; +import { version, source_url } from 'mastodon/initial_state'; export default class ErrorBoundary extends React.PureComponent { @@ -12,26 +13,53 @@ export default class ErrorBoundary extends React.PureComponent { hasError: false, stackTrace: undefined, componentStack: undefined, - } + }; - componentDidCatch(error, info) { + componentDidCatch (error, info) { this.setState({ hasError: true, stackTrace: error.stack, componentStack: info && info.componentStack, + copied: false, }); } + handleCopyStackTrace = () => { + const { stackTrace } = this.state; + const textarea = document.createElement('textarea'); + + textarea.textContent = stackTrace; + textarea.style.position = 'fixed'; + + document.body.appendChild(textarea); + + try { + textarea.select(); + document.execCommand('copy'); + } catch (e) { + + } finally { + document.body.removeChild(textarea); + } + + this.setState({ copied: true }); + setTimeout(() => this.setState({ copied: false }), 700); + } + render() { - const { hasError } = this.state; + const { hasError, copied } = this.state; if (!hasError) { return this.props.children; } return ( - <div> - <img src={illustration} alt='' /> + <div className='error-boundary'> + <div> + <p className='error-boundary__error'><FormattedMessage id='error.unexpected_crash.explanation' defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' /></p> + <p><FormattedMessage id='error.unexpected_crash.next_steps' defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' /></p> + <p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied && 'copied'}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p> + </div> </div> ); } diff --git a/app/javascript/mastodon/components/extended_video_player.js b/app/javascript/mastodon/components/extended_video_player.js deleted file mode 100644 index 009c0d559ad67e60e0789c03aacf1be84ad69a2a..0000000000000000000000000000000000000000 --- a/app/javascript/mastodon/components/extended_video_player.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -export default class ExtendedVideoPlayer extends React.PureComponent { - - static propTypes = { - src: PropTypes.string.isRequired, - alt: PropTypes.string, - width: PropTypes.number, - height: PropTypes.number, - time: PropTypes.number, - controls: PropTypes.bool.isRequired, - muted: PropTypes.bool.isRequired, - onClick: PropTypes.func, - }; - - handleLoadedData = () => { - if (this.props.time) { - this.video.currentTime = this.props.time; - } - } - - componentDidMount () { - this.video.addEventListener('loadeddata', this.handleLoadedData); - } - - componentWillUnmount () { - this.video.removeEventListener('loadeddata', this.handleLoadedData); - } - - setRef = (c) => { - this.video = c; - } - - handleClick = e => { - e.stopPropagation(); - const handler = this.props.onClick; - if (handler) handler(); - } - - render () { - const { src, muted, controls, alt } = this.props; - - return ( - <div className='extended-video-player'> - <video - ref={this.setRef} - src={src} - autoPlay - role='button' - tabIndex='0' - aria-label={alt} - title={alt} - muted={muted} - controls={controls} - loop={!controls} - onClick={this.handleClick} - /> - </div> - ); - } - -} diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.js new file mode 100644 index 0000000000000000000000000000000000000000..83cfae49c494d752759db8b969d5b4ef2b4c3889 --- /dev/null +++ b/app/javascript/mastodon/components/gifv.js @@ -0,0 +1,75 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +export default class GIFV extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + alt: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number, + onClick: PropTypes.func, + }; + + state = { + loading: true, + }; + + handleLoadedData = () => { + this.setState({ loading: false }); + } + + componentWillReceiveProps (nextProps) { + if (nextProps.src !== this.props.src) { + this.setState({ loading: true }); + } + } + + handleClick = e => { + const { onClick } = this.props; + + if (onClick) { + e.stopPropagation(); + onClick(); + } + } + + render () { + const { src, width, height, alt } = this.props; + const { loading } = this.state; + + return ( + <div className='gifv' style={{ position: 'relative' }}> + {loading && ( + <canvas + width={width} + height={height} + role='button' + tabIndex='0' + aria-label={alt} + title={alt} + onClick={this.handleClick} + /> + )} + + <video + src={src} + width={width} + height={height} + role='button' + tabIndex='0' + aria-label={alt} + title={alt} + muted + loop + autoPlay + playsInline + onClick={this.handleClick} + onLoadedData={this.handleLoadedData} + style={{ position: loading ? 'absolute' : 'static', top: 0, left: 0 }} + /> + </div> + ); + } + +} diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.js index f091d7893e6631e0f6c12983f8a139854d78051a..62d61326261b3ac5efb1e949ec198a929003a46b 100644 --- a/app/javascript/mastodon/components/hashtag.js +++ b/app/javascript/mastodon/components/hashtag.js @@ -12,11 +12,11 @@ const Hashtag = ({ hashtag }) => ( #<span>{hashtag.get('name')}</span> </Permalink> - <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']), count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))}</strong> }} /> + <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1, count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1)}</strong> }} /> </div> <div className='trends__item__current'> - {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))} + {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']) * 1 + hashtag.getIn(['history', 1, 'uses']) * 1)} </div> <div className='trends__item__sparkline'> diff --git a/app/javascript/mastodon/components/load_pending.js b/app/javascript/mastodon/components/load_pending.js new file mode 100644 index 0000000000000000000000000000000000000000..7e27024036a69c70a25fa227bfabbac04bf38aab --- /dev/null +++ b/app/javascript/mastodon/components/load_pending.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; + +export default class LoadPending extends React.PureComponent { + + static propTypes = { + onClick: PropTypes.func, + count: PropTypes.number, + } + + render() { + const { count } = this.props; + + return ( + <button className='load-more load-gap' onClick={this.props.onClick}> + <FormattedMessage id='load_pending' defaultMessage='{count, plural, one {# new item} other {# new items}}' values={{ count }} /> + </button> + ); + } + +} diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 77bac61eec9e2a20b3ff71a710af2618c8b26626..e8dd79af9e09cbb5a81995af0ce33630074516fe 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -6,7 +6,7 @@ import IconButton from './icon_button'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from '../is_mobile'; import classNames from 'classnames'; -import { autoPlayGif, displayMedia } from '../initial_state'; +import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state'; import { decode } from 'blurhash'; const messages = defineMessages({ @@ -81,6 +81,8 @@ class Item extends React.PureComponent { } _decode () { + if (!useBlurhash) return; + const hash = this.props.attachment.get('blurhash'); const pixels = decode(hash, 32, 32); @@ -157,7 +159,7 @@ class Item extends React.PureComponent { if (attachment.get('type') === 'unknown') { return ( <div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}> - <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }} title={attachment.get('description')}> + <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} target='_blank' style={{ cursor: 'pointer' }} title={attachment.get('description')}> <canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' /> </a> </div> @@ -313,15 +315,22 @@ class MediaGallery extends React.PureComponent { style.height = height; } - const size = media.take(4).size; + const size = media.take(4).size; + const uncached = media.every(attachment => attachment.get('type') === 'unknown'); if (this.isStandaloneEligible()) { children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />; } else { - children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible} />); + children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />); } - if (visible) { + if (uncached) { + spoilerButton = ( + <button type='button' disabled className='spoiler-button__overlay'> + <span className='spoiler-button__overlay__label'><FormattedMessage id='status.uncached_media_warning' defaultMessage='Not available' /></span> + </button> + ); + } else if (visible) { spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible)} icon='eye-slash' overlay onClick={this.handleOpen} />; } else { spoilerButton = ( @@ -333,7 +342,7 @@ class MediaGallery extends React.PureComponent { return ( <div className='media-gallery' style={style} ref={this.handleRef}> - <div className={classNames('spoiler-button', { 'spoiler-button--minified': visible })}> + <div className={classNames('spoiler-button', { 'spoiler-button--minified': visible && !uncached, 'spoiler-button--click-thru': uncached })}> {spoilerButton} </div> diff --git a/app/javascript/mastodon/components/missing_indicator.js b/app/javascript/mastodon/components/missing_indicator.js index 70d8c3b98412821662a9b148e4e5360faeafbee3..7b0101bab852aa875b136c0fd38f5ca23eac5232 100644 --- a/app/javascript/mastodon/components/missing_indicator.js +++ b/app/javascript/mastodon/components/missing_indicator.js @@ -1,17 +1,24 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; +import illustration from 'mastodon/../images/elephant_ui_disappointed.svg'; +import classNames from 'classnames'; -const MissingIndicator = () => ( - <div className='regeneration-indicator missing-indicator'> - <div> - <div className='regeneration-indicator__figure' /> +const MissingIndicator = ({ fullPage }) => ( + <div className={classNames('regeneration-indicator', { 'regeneration-indicator--without-header': fullPage })}> + <div className='regeneration-indicator__figure'> + <img src={illustration} alt='' /> + </div> - <div className='regeneration-indicator__label'> - <FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' /> - <FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' /> - </div> + <div className='regeneration-indicator__label'> + <FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' /> + <FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' /> </div> </div> ); +MissingIndicator.propTypes = { + fullPage: PropTypes.bool, +}; + export default MissingIndicator; diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js index 690f9ae5a076da1169b18314d9315adeb024357f..cdbcf8f709fdf0572dd1d70f6e68b06763f11a90 100644 --- a/app/javascript/mastodon/components/poll.js +++ b/app/javascript/mastodon/components/poll.js @@ -10,9 +10,11 @@ import spring from 'react-motion/lib/spring'; import escapeTextContentForBrowser from 'escape-html'; import emojify from 'mastodon/features/emoji/emoji'; import RelativeTimestamp from './relative_timestamp'; +import Icon from 'mastodon/components/icon'; const messages = defineMessages({ closed: { id: 'poll.closed', defaultMessage: 'Closed' }, + voted: { id: 'poll.voted', defaultMessage: 'You voted for this answer', description: 'Tooltip of the "voted" checkmark in polls' }, }); const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => { @@ -32,8 +34,38 @@ class Poll extends ImmutablePureComponent { state = { selected: {}, + expired: null, }; + static getDerivedStateFromProps (props, state) { + const { poll, intl } = props; + const expired = poll.get('expired') || (new Date(poll.get('expires_at'))).getTime() < intl.now(); + return (expired === state.expired) ? null : { expired }; + } + + componentDidMount () { + this._setupTimer(); + } + + componentDidUpdate () { + this._setupTimer(); + } + + componentWillUnmount () { + clearTimeout(this._timer); + } + + _setupTimer () { + const { poll, intl } = this.props; + clearTimeout(this._timer); + if (!this.state.expired) { + const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now(); + this._timer = setTimeout(() => { + this.setState({ expired: true }); + }, delay); + } + } + handleOptionChange = e => { const { target: { value } } = e; @@ -68,12 +100,13 @@ class Poll extends ImmutablePureComponent { this.props.dispatch(fetchPoll(this.props.poll.get('id'))); }; - renderOption (option, optionIndex) { - const { poll, disabled } = this.props; - const percent = poll.get('votes_count') === 0 ? 0 : (option.get('votes_count') / poll.get('votes_count')) * 100; - const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') > other.get('votes_count')); - const active = !!this.state.selected[`${optionIndex}`]; - const showResults = poll.get('voted') || poll.get('expired'); + renderOption (option, optionIndex, showResults) { + const { poll, disabled, intl } = this.props; + const pollVotesCount = poll.get('voters_count') || poll.get('votes_count'); + const percent = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100; + const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count')); + const active = !!this.state.selected[`${optionIndex}`]; + const voted = option.get('voted') || (poll.get('own_votes') && poll.get('own_votes').includes(optionIndex)); let titleEmojified = option.get('title_emojified'); if (!titleEmojified) { @@ -102,7 +135,10 @@ class Poll extends ImmutablePureComponent { /> {!showResults && <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} />} - {showResults && <span className='poll__number'>{Math.round(percent)}%</span>} + {showResults && <span className='poll__number'> + {!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />} + {Math.round(percent)}% + </span>} <span dangerouslySetInnerHTML={{ __html: titleEmojified }} /> </label> @@ -112,25 +148,34 @@ class Poll extends ImmutablePureComponent { render () { const { poll, intl } = this.props; + const { expired } = this.state; if (!poll) { return null; } - const timeRemaining = poll.get('expired') ? intl.formatMessage(messages.closed) : <RelativeTimestamp timestamp={poll.get('expires_at')} futureDate />; - const showResults = poll.get('voted') || poll.get('expired'); + const timeRemaining = expired ? intl.formatMessage(messages.closed) : <RelativeTimestamp timestamp={poll.get('expires_at')} futureDate />; + const showResults = poll.get('voted') || expired; const disabled = this.props.disabled || Object.entries(this.state.selected).every(item => !item); + let votesCount = null; + + if (poll.get('voters_count') !== null && poll.get('voters_count') !== undefined) { + votesCount = <FormattedMessage id='poll.total_people' defaultMessage='{count, plural, one {# person} other {# people}}' values={{ count: poll.get('voters_count') }} />; + } else { + votesCount = <FormattedMessage id='poll.total_votes' defaultMessage='{count, plural, one {# vote} other {# votes}}' values={{ count: poll.get('votes_count') }} />; + } + return ( <div className='poll'> <ul> - {poll.get('options').map((option, i) => this.renderOption(option, i))} + {poll.get('options').map((option, i) => this.renderOption(option, i, showResults))} </ul> <div className='poll__footer'> {!showResults && <button className='button button-secondary' disabled={disabled} 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>} - <FormattedMessage id='poll.total_votes' defaultMessage='{count, plural, one {# vote} other {# votes}}' values={{ count: poll.get('votes_count') }} /> + {votesCount} {poll.get('expires_at') && <span> · {timeRemaining}</span>} </div> </div> diff --git a/app/javascript/mastodon/components/radio_button.js b/app/javascript/mastodon/components/radio_button.js new file mode 100644 index 0000000000000000000000000000000000000000..0496fa2868ac7bfdfa294206cda3572362f163e8 --- /dev/null +++ b/app/javascript/mastodon/components/radio_button.js @@ -0,0 +1,35 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export default class RadioButton extends React.PureComponent { + + static propTypes = { + value: PropTypes.string.isRequired, + checked: PropTypes.bool, + name: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + label: PropTypes.node.isRequired, + }; + + render () { + const { name, value, checked, onChange, label } = this.props; + + return ( + <label className='radio-button'> + <input + name={name} + type='radio' + value={value} + checked={checked} + onChange={onChange} + /> + + <span className={classNames('radio-button__input', { checked })} /> + + <span>{label}</span> + </label> + ); + } + +} diff --git a/app/javascript/mastodon/components/regeneration_indicator.js b/app/javascript/mastodon/components/regeneration_indicator.js new file mode 100644 index 0000000000000000000000000000000000000000..faf88c6b5031ac6bcc45d39f05ece8f5f78b1a9f --- /dev/null +++ b/app/javascript/mastodon/components/regeneration_indicator.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import illustration from 'mastodon/../images/elephant_ui_working.svg'; + +const MissingIndicator = () => ( + <div className='regeneration-indicator'> + <div className='regeneration-indicator__figure'> + <img src={illustration} alt='' /> + </div> + + <div className='regeneration-indicator__label'> + <FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading…' /> + <FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' /> + </div> + </div> +); + +export default MissingIndicator; diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index 0376cf85accef9390512dc9d67baf71623d255a0..421756803c96fb2b1da7ec382243169e9ae1af5d 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -3,6 +3,7 @@ import { ScrollContainer } from 'react-router-scroll-4'; import PropTypes from 'prop-types'; import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container'; import LoadMore from './load_more'; +import LoadPending from './load_pending'; import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'; import { throttle } from 'lodash'; import { List as ImmutableList } from 'immutable'; @@ -21,6 +22,7 @@ export default class ScrollableList extends PureComponent { static propTypes = { scrollKey: PropTypes.string.isRequired, onLoadMore: PropTypes.func, + onLoadPending: PropTypes.func, onScrollToTop: PropTypes.func, onScroll: PropTypes.func, trackScroll: PropTypes.bool, @@ -28,10 +30,12 @@ export default class ScrollableList extends PureComponent { isLoading: PropTypes.bool, showLoading: PropTypes.bool, hasMore: PropTypes.bool, + numPending: PropTypes.number, prepend: PropTypes.node, alwaysPrepend: PropTypes.bool, emptyMessage: PropTypes.node, children: PropTypes.node, + bindToDocument: PropTypes.bool, }; static defaultProps = { @@ -47,7 +51,9 @@ export default class ScrollableList extends PureComponent { handleScroll = throttle(() => { if (this.node) { - const { scrollTop, scrollHeight, clientHeight } = this.node; + const scrollTop = this.getScrollTop(); + const scrollHeight = this.getScrollHeight(); + const clientHeight = this.getClientHeight(); const offset = scrollHeight - scrollTop - clientHeight; if (400 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) { @@ -77,9 +83,14 @@ export default class ScrollableList extends PureComponent { scrollToTopOnMouseIdle = false; setScrollTop = newScrollTop => { - if (this.node.scrollTop !== newScrollTop) { + if (this.getScrollTop() !== newScrollTop) { this.lastScrollWasSynthetic = true; - this.node.scrollTop = newScrollTop; + + if (this.props.bindToDocument) { + document.scrollingElement.scrollTop = newScrollTop; + } else { + this.node.scrollTop = newScrollTop; + } } }; @@ -97,7 +108,7 @@ export default class ScrollableList extends PureComponent { this.clearMouseIdleTimer(); this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); - if (!this.mouseMovedRecently && this.node.scrollTop === 0) { + if (!this.mouseMovedRecently && this.getScrollTop() === 0) { // Only set if we just started moving and are scrolled to the top. this.scrollToTopOnMouseIdle = true; } @@ -132,15 +143,27 @@ export default class ScrollableList extends PureComponent { } getScrollPosition = () => { - if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) { - return { height: this.node.scrollHeight, top: this.node.scrollTop }; + if (this.node && (this.getScrollTop() > 0 || this.mouseMovedRecently)) { + return { height: this.getScrollHeight(), top: this.getScrollTop() }; } else { return null; } } + getScrollTop = () => { + return this.props.bindToDocument ? document.scrollingElement.scrollTop : this.node.scrollTop; + } + + getScrollHeight = () => { + return this.props.bindToDocument ? document.scrollingElement.scrollHeight : this.node.scrollHeight; + } + + getClientHeight = () => { + return this.props.bindToDocument ? document.scrollingElement.clientHeight : this.node.clientHeight; + } + updateScrollBottom = (snapshot) => { - const newScrollTop = this.node.scrollHeight - snapshot; + const newScrollTop = this.getScrollHeight() - snapshot; this.setScrollTop(newScrollTop); } @@ -149,9 +172,10 @@ export default class ScrollableList extends PureComponent { const someItemInserted = React.Children.count(prevProps.children) > 0 && React.Children.count(prevProps.children) < React.Children.count(this.props.children) && this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); + const pendingChanged = (prevProps.numPending > 0) !== (this.props.numPending > 0); - if (someItemInserted && (this.node.scrollTop > 0 || this.mouseMovedRecently)) { - return this.node.scrollHeight - this.node.scrollTop; + if (pendingChanged || someItemInserted && (this.getScrollTop() > 0 || this.mouseMovedRecently)) { + return this.getScrollHeight() - this.getScrollTop(); } else { return null; } @@ -161,7 +185,7 @@ export default class ScrollableList extends PureComponent { // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. if (snapshot !== null) { - this.setScrollTop(this.node.scrollHeight - snapshot); + this.setScrollTop(this.getScrollHeight() - snapshot); } } @@ -175,6 +199,7 @@ export default class ScrollableList extends PureComponent { this.clearMouseIdleTimer(); this.detachScrollListener(); this.detachIntersectionObserver(); + detachFullscreenListener(this.onFullScreenChange); } @@ -194,13 +219,23 @@ export default class ScrollableList extends PureComponent { } attachScrollListener () { - this.node.addEventListener('scroll', this.handleScroll); - this.node.addEventListener('wheel', this.handleWheel); + if (this.props.bindToDocument) { + document.addEventListener('scroll', this.handleScroll); + document.addEventListener('wheel', this.handleWheel); + } else { + this.node.addEventListener('scroll', this.handleScroll); + this.node.addEventListener('wheel', this.handleWheel); + } } detachScrollListener () { - this.node.removeEventListener('scroll', this.handleScroll); - this.node.removeEventListener('wheel', this.handleWheel); + if (this.props.bindToDocument) { + document.removeEventListener('scroll', this.handleScroll); + document.removeEventListener('wheel', this.handleWheel); + } else { + this.node.removeEventListener('scroll', this.handleScroll); + this.node.removeEventListener('wheel', this.handleWheel); + } } getFirstChildKey (props) { @@ -225,12 +260,25 @@ export default class ScrollableList extends PureComponent { this.props.onLoadMore(); } + handleLoadPending = e => { + e.preventDefault(); + this.props.onLoadPending(); + // Prevent the weird scroll-jumping behavior, as we explicitly don't want to + // scroll to top, and we know the scroll height is going to change + this.scrollToTopOnMouseIdle = false; + this.lastScrollWasSynthetic = false; + this.clearMouseIdleTimer(); + this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); + this.mouseMovedRecently = true; + } + render () { - const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props; + const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props; const { fullscreen } = this.state; const childrenCount = React.Children.count(children); const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null; + const loadPending = (numPending > 0) ? <LoadPending count={numPending} onClick={this.handleLoadPending} /> : null; let scrollableArea = null; if (showLoading) { @@ -251,6 +299,8 @@ export default class ScrollableList extends PureComponent { <div role='feed' className='item-list'> {prepend} + {loadPending} + {React.Children.map(this.props.children, (child, index) => ( <IntersectionObserverArticleContainer key={child.key} diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 9b1035649d8b4ed6d0c8d6f7fb0f688933539cf7..b5606aca5a86578624b8e7a036443ee81382cefa 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -12,7 +12,7 @@ import AttachmentList from './attachment_list'; import Card from '../features/status/components/card'; import { injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { MediaGallery, Video } from '../features/ui/util/async-components'; +import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; import { HotKeys } from 'react-hotkeys'; import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; @@ -199,11 +199,15 @@ class Status extends ImmutablePureComponent { }; renderLoadingMediaGallery () { - return <div className='media_gallery' style={{ height: '110px' }} />; + return <div className='media-gallery' style={{ height: '110px' }} />; } renderLoadingVideoPlayer () { - return <div className='media-spoiler-video' style={{ height: '110px' }} />; + return <div className='video-player' style={{ height: '110px' }} />; + } + + renderLoadingAudioPlayer () { + return <div className='audio-player' style={{ height: '110px' }} />; } handleOpenVideo = (media, startTime) => { @@ -278,12 +282,27 @@ class Status extends ImmutablePureComponent { return null; } + const handlers = this.props.muted ? {} : { + reply: this.handleHotkeyReply, + favourite: this.handleHotkeyFavourite, + boost: this.handleHotkeyBoost, + mention: this.handleHotkeyMention, + open: this.handleHotkeyOpen, + openProfile: this.handleHotkeyOpenProfile, + moveUp: this.handleHotkeyMoveUp, + moveDown: this.handleHotkeyMoveDown, + toggleHidden: this.handleHotkeyToggleHidden, + toggleSensitive: this.handleHotkeyToggleSensitive, + }; + if (hidden) { return ( - <div ref={this.handleRef}> - {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} - {status.get('content')} - </div> + <HotKeys handlers={handlers}> + <div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'> + {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])} + {status.get('content')} + </div> + </HotKeys> ); } @@ -333,7 +352,23 @@ class Status extends ImmutablePureComponent { media={status.get('media_attachments')} /> ); - } else if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) { + } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { + const attachment = status.getIn(['media_attachments', 0]); + + media = ( + <Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > + {Component => ( + <Component + src={attachment.get('url')} + alt={attachment.get('description')} + duration={attachment.getIn(['meta', 'original', 'duration'], 0)} + peaks={[0]} + height={70} + /> + )} + </Bundle> + ); + } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { const attachment = status.getIn(['media_attachments', 0]); media = ( @@ -394,19 +429,6 @@ class Status extends ImmutablePureComponent { statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />; } - const handlers = this.props.muted ? {} : { - reply: this.handleHotkeyReply, - favourite: this.handleHotkeyFavourite, - boost: this.handleHotkeyBoost, - mention: this.handleHotkeyMention, - open: this.handleHotkeyOpen, - openProfile: this.handleHotkeyOpenProfile, - moveUp: this.handleHotkeyMoveUp, - moveDown: this.handleHotkeyMoveDown, - toggleHidden: this.handleHotkeyToggleHidden, - toggleSensitive: this.handleHotkeyToggleSensitive, - }; - return ( <HotKeys handlers={handlers}> <div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}> diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index ba6d3c296261d0e783dd7c7135b22a15e5a3cada..4ce9ec49f3374cc5780b472410735c9f8eb4123b 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -55,6 +55,7 @@ export default class StatusContent extends React.PureComponent { link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false); } else { link.setAttribute('title', link.href); + link.classList.add('unhandled-link'); } link.setAttribute('target', '_blank'); @@ -111,7 +112,7 @@ export default class StatusContent extends React.PureComponent { } onHashtagClick = (hashtag, e) => { - hashtag = hashtag.replace(/^#/, '').toLowerCase(); + hashtag = hashtag.replace(/^#/, ''); if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); @@ -215,22 +216,22 @@ export default class StatusContent extends React.PureComponent { return ( <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}> <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}> - <span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} /> + <span dangerouslySetInnerHTML={spoilerContent} /> {' '} <button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button> </p> {mentionsPlaceholder} - <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} /> + <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} /> {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />} </div> ); } else if (this.props.onClick) { const output = [ - <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}> - <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} /> + <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'> + <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} </div>, @@ -244,7 +245,7 @@ export default class StatusContent extends React.PureComponent { } else { return ( <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle}> - <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} /> + <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} </div> diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js index 745e6422d3d86893a3dc5b92365f87b312f21a1b..e1b370c913c4bbd101783ebb8b212e94e4561dcc 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.js @@ -1,12 +1,12 @@ import { debounce } from 'lodash'; import React from 'react'; -import { FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import StatusContainer from '../containers/status_container'; import ImmutablePureComponent from 'react-immutable-pure-component'; import LoadGap from './load_gap'; import ScrollableList from './scrollable_list'; +import RegenerationIndicator from 'mastodon/components/regeneration_indicator'; export default class StatusList extends ImmutablePureComponent { @@ -81,18 +81,7 @@ export default class StatusList extends ImmutablePureComponent { const { isLoading, isPartial } = other; if (isPartial) { - return ( - <div className='regeneration-indicator'> - <div> - <div className='regeneration-indicator__figure' /> - - <div className='regeneration-indicator__label'> - <FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading…' /> - <FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' /> - </div> - </div> - </div> - ); + return <RegenerationIndicator />; } let scrollableContent = (isLoading || statusIds.size > 0) ? ( diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 542b682821ee5cf9b2c9ec8ce177b34c63cca6c9..3ac58cf7c5e6785e597d59c6ae2f9b1005f94acc 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -12,6 +12,8 @@ import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; +import { previewState as previewMediaState } from 'mastodon/features/ui/components/media_modal'; +import { previewState as previewVideoState } from 'mastodon/features/ui/components/video_modal'; import initialState from '../initial_state'; import ErrorBoundary from '../components/error_boundary'; @@ -35,6 +37,10 @@ class MastodonMount extends React.PureComponent { showIntroduction: PropTypes.bool, }; + shouldUpdateScroll (_, { location }) { + return location.state !== previewMediaState && location.state !== previewVideoState; + } + render () { const { showIntroduction } = this.props; @@ -44,7 +50,7 @@ class MastodonMount extends React.PureComponent { return ( <BrowserRouter basename='/web'> - <ScrollContext> + <ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}> <Route path='/' component={UI} /> </ScrollContext> </BrowserRouter> diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.js index 51d4f0fed7a2522bd6a3c38d4d98d9b91ed4b35d..ba55ecbc7ba8f42227066f48e74685c1e60915f4 100644 --- a/app/javascript/mastodon/containers/media_container.js +++ b/app/javascript/mastodon/containers/media_container.js @@ -2,19 +2,22 @@ import React, { PureComponent, Fragment } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import { IntlProvider, addLocaleData } from 'react-intl'; -import { getLocale } from '../locales'; -import MediaGallery from '../components/media_gallery'; -import Video from '../features/video'; -import Card from '../features/status/components/card'; -import Poll from 'mastodon/components/poll'; -import ModalRoot from '../components/modal_root'; -import MediaModal from '../features/ui/components/media_modal'; import { List as ImmutableList, fromJS } from 'immutable'; +import { getLocale } from 'mastodon/locales'; +import { getScrollbarWidth } from 'mastodon/utils/scrollbar'; +import MediaGallery from 'mastodon/components/media_gallery'; +import Poll from 'mastodon/components/poll'; +import Hashtag from 'mastodon/components/hashtag'; +import ModalRoot from 'mastodon/components/modal_root'; +import MediaModal from 'mastodon/features/ui/components/media_modal'; +import Video from 'mastodon/features/video'; +import Card from 'mastodon/features/status/components/card'; +import Audio from 'mastodon/features/audio'; const { localeData, messages } = getLocale(); addLocaleData(localeData); -const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll }; +const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll, Hashtag, Audio }; export default class MediaContainer extends PureComponent { @@ -31,6 +34,8 @@ export default class MediaContainer extends PureComponent { handleOpenMedia = (media, index) => { document.body.classList.add('with-modals--active'); + document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; + this.setState({ media, index }); } @@ -38,11 +43,15 @@ export default class MediaContainer extends PureComponent { const media = ImmutableList([video]); document.body.classList.add('with-modals--active'); + document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; + this.setState({ media, time }); } handleCloseMedia = () => { document.body.classList.remove('with-modals--active'); + document.documentElement.style.marginRight = 0; + this.setState({ media: null, index: null, time: null }); } @@ -55,12 +64,13 @@ export default class MediaContainer extends PureComponent { {[].map.call(components, (component, i) => { const componentName = component.getAttribute('data-component'); const Component = MEDIA_COMPONENTS[componentName]; - const { media, card, poll, ...props } = JSON.parse(component.getAttribute('data-props')); + const { media, card, poll, hashtag, ...props } = JSON.parse(component.getAttribute('data-props')); Object.assign(props, { - ...(media ? { media: fromJS(media) } : {}), - ...(card ? { card: fromJS(card) } : {}), - ...(poll ? { poll: fromJS(poll) } : {}), + ...(media ? { media: fromJS(media) } : {}), + ...(card ? { card: fromJS(card) } : {}), + ...(poll ? { poll: fromJS(poll) } : {}), + ...(hashtag ? { hashtag: fromJS(hashtag) } : {}), ...(componentName === 'Video' ? { onOpenVideo: this.handleOpenVideo, @@ -74,6 +84,7 @@ export default class MediaContainer extends PureComponent { component, ); })} + <ModalRoot onClose={this.handleCloseMedia}> {this.state.media && ( <MediaModal diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index 86324b846811df4cd8a4b6b1a32deebbf301c552..fb22676e045281485331a7db1f8c11461fc03de9 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -1,4 +1,3 @@ -import React from 'react'; import { connect } from 'react-redux'; import Status from '../components/status'; import { makeGetStatus } from '../selectors'; @@ -15,7 +14,6 @@ import { pin, unpin, } from '../actions/interactions'; -import { blockAccount } from '../actions/accounts'; import { muteStatus, unmuteStatus, @@ -24,9 +22,10 @@ import { revealStatus, } from '../actions/statuses'; import { initMuteModal } from '../actions/mutes'; +import { initBlockModal } from '../actions/blocks'; import { initReport } from '../actions/reports'; import { openModal } from '../actions/modal'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import { boostModal, deleteModal } from '../initial_state'; import { showAlertForError } from '../actions/alerts'; @@ -35,10 +34,8 @@ const messages = defineMessages({ deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, - blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, - blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, }); const makeMapStateToProps = () => { @@ -56,6 +53,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onReply (status, router) { dispatch((_, getState) => { let state = getState(); + if (state.getIn(['compose', 'text']).trim().length !== 0) { dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.replyMessage), @@ -77,7 +75,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, onReblog (status, e) { - if (e.shiftKey || !boostModal) { + if ((e && e.shiftKey) || !boostModal) { this.onModalReblog(status); } else { dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog })); @@ -137,16 +135,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onBlock (status) { const account = status.get('account'); - dispatch(openModal('CONFIRM', { - message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, - confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.get('id'))), - secondary: intl.formatMessage(messages.blockAndReport), - onSecondary: () => { - dispatch(blockAccount(account.get('id'))); - dispatch(initReport(account, status)); - }, - })); + dispatch(initBlockModal(account)); }, onReport (status) { diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.js b/app/javascript/mastodon/features/account_gallery/components/media_item.js index 2609b96ffca1b362b6741e6acea73073e57ef7b8..b6eec22439663a83e58efd0b40f4f71210fcda6d 100644 --- a/app/javascript/mastodon/features/account_gallery/components/media_item.js +++ b/app/javascript/mastodon/features/account_gallery/components/media_item.js @@ -96,6 +96,12 @@ export default class MediaItem extends ImmutablePureComponent { if (attachment.get('type') === 'unknown') { // Skip + } else if (attachment.get('type') === 'audio') { + thumbnail = ( + <span className='account-gallery__item__icons'> + <Icon id='music' /> + </span> + ); } else if (attachment.get('type') === 'image') { const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0; @@ -113,6 +119,7 @@ export default class MediaItem extends ImmutablePureComponent { ); } else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) { const autoPlay = !isIOS() && autoPlayGif; + const label = attachment.get('type') === 'video' ? <Icon id='play' /> : 'GIF'; thumbnail = ( <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> @@ -129,7 +136,7 @@ export default class MediaItem extends ImmutablePureComponent { muted /> - <span className='media-gallery__gifv__label'>GIF</span> + <span className='media-gallery__gifv__label'>{label}</span> </div> ); } diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js index 5d6a53e18eb9b6f39bfc5b41ef506ca6e5c458d4..de481075c3c13ca1ed53314c02c252ff3c353749 100644 --- a/app/javascript/mastodon/features/account_gallery/index.js +++ b/app/javascript/mastodon/features/account_gallery/index.js @@ -56,6 +56,7 @@ class AccountGallery extends ImmutablePureComponent { isLoading: PropTypes.bool, hasMore: PropTypes.bool, isAccount: PropTypes.bool, + multiColumn: PropTypes.bool, }; state = { @@ -101,6 +102,8 @@ class AccountGallery extends ImmutablePureComponent { handleOpenMedia = attachment => { if (attachment.get('type') === 'video') { this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status') })); + } else if (attachment.get('type') === 'audio') { + this.props.dispatch(openModal('AUDIO', { media: attachment, status: attachment.get('status') })); } else { const media = attachment.getIn(['status', 'media_attachments']); const index = media.findIndex(x => x.get('id') === attachment.get('id')); @@ -116,7 +119,7 @@ class AccountGallery extends ImmutablePureComponent { } render () { - const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount } = this.props; + const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn } = this.props; const { width } = this.state; if (!isAccount) { @@ -143,7 +146,7 @@ class AccountGallery extends ImmutablePureComponent { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}> <div className='scrollable scrollable--flex' onScroll={this.handleScroll}> diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js index 4d4ae6e8243e1024c41cba8375bc4ef3f4f8fb51..8728b48068a9baa4ddde47f01cbd4ed39fde8798 100644 --- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js +++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js @@ -5,7 +5,6 @@ import Header from '../components/header'; import { followAccount, unfollowAccount, - blockAccount, unblockAccount, unmuteAccount, pinAccount, @@ -16,6 +15,7 @@ import { directCompose, } from '../../../actions/compose'; import { initMuteModal } from '../../../actions/mutes'; +import { initBlockModal } from '../../../actions/blocks'; import { initReport } from '../../../actions/reports'; import { openModal } from '../../../actions/modal'; import { blockDomain, unblockDomain } from '../../../actions/domain_blocks'; @@ -25,9 +25,7 @@ import { List as ImmutableList } from 'immutable'; const messages = defineMessages({ unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' }, - blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' }, - blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, }); const makeMapStateToProps = () => { @@ -64,16 +62,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ if (account.getIn(['relationship', 'blocking'])) { dispatch(unblockAccount(account.get('id'))); } else { - dispatch(openModal('CONFIRM', { - message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, - confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.get('id'))), - secondary: intl.formatMessage(messages.blockAndReport), - onSecondary: () => { - dispatch(blockAccount(account.get('id'))); - dispatch(initReport(account)); - }, - })); + dispatch(initBlockModal(account)); } }, diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js index 27581bfdc887a3aa3fc63ff69c390568b1e1434a..8d0cbe5a197b1f8331a85af1bd60191199932848 100644 --- a/app/javascript/mastodon/features/account_timeline/index.js +++ b/app/javascript/mastodon/features/account_timeline/index.js @@ -44,6 +44,7 @@ class AccountTimeline extends ImmutablePureComponent { withReplies: PropTypes.bool, blockedBy: PropTypes.bool, isAccount: PropTypes.bool, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -77,11 +78,12 @@ class AccountTimeline extends ImmutablePureComponent { } render () { - const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount } = this.props; + const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount, multiColumn } = this.props; if (!isAccount) { return ( <Column> + <ColumnBackButton multiColumn={multiColumn} /> <MissingIndicator /> </Column> ); @@ -99,7 +101,7 @@ class AccountTimeline extends ImmutablePureComponent { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <StatusList prepend={<HeaderContainer accountId={this.props.params.accountId} />} @@ -112,6 +114,7 @@ class AccountTimeline extends ImmutablePureComponent { onLoadMore={this.handleLoadMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.js new file mode 100644 index 0000000000000000000000000000000000000000..95e5675f3cf12e6adc72bdad9483fe4742bb6daf --- /dev/null +++ b/app/javascript/mastodon/features/audio/index.js @@ -0,0 +1,226 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import WaveSurfer from 'wavesurfer.js'; +import { defineMessages, injectIntl } from 'react-intl'; +import { formatTime } from 'mastodon/features/video'; +import Icon from 'mastodon/components/icon'; +import classNames from 'classnames'; +import { throttle } from 'lodash'; + +const messages = defineMessages({ + play: { id: 'video.play', defaultMessage: 'Play' }, + pause: { id: 'video.pause', defaultMessage: 'Pause' }, + mute: { id: 'video.mute', defaultMessage: 'Mute sound' }, + unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' }, +}); + +export default @injectIntl +class Audio extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + alt: PropTypes.string, + duration: PropTypes.number, + peaks: PropTypes.arrayOf(PropTypes.number), + height: PropTypes.number, + preload: PropTypes.bool, + editable: PropTypes.bool, + intl: PropTypes.object.isRequired, + }; + + state = { + currentTime: 0, + duration: null, + paused: true, + muted: false, + volume: 0.5, + }; + + // hard coded in components.scss + // any way to get ::before values programatically? + + volWidth = 50; + + volOffset = 70; + + volHandleOffset = v => { + const offset = v * this.volWidth + this.volOffset; + return (offset > 110) ? 110 : offset; + } + + setVolumeRef = c => { + this.volume = c; + } + + setWaveformRef = c => { + this.waveform = c; + } + + componentDidMount () { + if (this.waveform) { + this._updateWaveform(); + } + } + + componentDidUpdate (prevProps) { + if (this.waveform && prevProps.src !== this.props.src) { + this._updateWaveform(); + } + } + + componentWillUnmount () { + if (this.wavesurfer) { + this.wavesurfer.destroy(); + this.wavesurfer = null; + } + } + + _updateWaveform () { + const { src, height, duration, peaks, preload } = this.props; + + const progressColor = window.getComputedStyle(document.querySelector('.audio-player__progress-placeholder')).getPropertyValue('background-color'); + const waveColor = window.getComputedStyle(document.querySelector('.audio-player__wave-placeholder')).getPropertyValue('background-color'); + + if (this.wavesurfer) { + this.wavesurfer.destroy(); + this.loaded = false; + } + + const wavesurfer = WaveSurfer.create({ + container: this.waveform, + height, + barWidth: 3, + cursorWidth: 0, + progressColor, + waveColor, + backend: 'MediaElement', + interact: preload, + }); + + wavesurfer.setVolume(this.state.volume); + + if (preload) { + wavesurfer.load(src); + this.loaded = true; + } else { + wavesurfer.load(src, peaks, 'none', duration); + this.loaded = false; + } + + wavesurfer.on('ready', () => this.setState({ duration: Math.floor(wavesurfer.getDuration()) })); + wavesurfer.on('audioprocess', () => this.setState({ currentTime: Math.floor(wavesurfer.getCurrentTime()) })); + wavesurfer.on('pause', () => this.setState({ paused: true })); + wavesurfer.on('play', () => this.setState({ paused: false })); + wavesurfer.on('volume', volume => this.setState({ volume })); + wavesurfer.on('mute', muted => this.setState({ muted })); + + this.wavesurfer = wavesurfer; + } + + togglePlay = () => { + if (this.state.paused) { + if (!this.props.preload && !this.loaded) { + this.wavesurfer.createBackend(); + this.wavesurfer.createPeakCache(); + this.wavesurfer.load(this.props.src); + this.wavesurfer.toggleInteraction(); + this.loaded = true; + } + + this.wavesurfer.play(); + this.setState({ paused: false }); + } else { + this.wavesurfer.pause(); + this.setState({ paused: true }); + } + } + + toggleMute = () => { + this.wavesurfer.setMute(!this.state.muted); + } + + handleVolumeMouseDown = e => { + document.addEventListener('mousemove', this.handleMouseVolSlide, true); + document.addEventListener('mouseup', this.handleVolumeMouseUp, true); + document.addEventListener('touchmove', this.handleMouseVolSlide, true); + document.addEventListener('touchend', this.handleVolumeMouseUp, true); + + this.handleMouseVolSlide(e); + + e.preventDefault(); + e.stopPropagation(); + } + + handleVolumeMouseUp = () => { + document.removeEventListener('mousemove', this.handleMouseVolSlide, true); + document.removeEventListener('mouseup', this.handleVolumeMouseUp, true); + document.removeEventListener('touchmove', this.handleMouseVolSlide, true); + document.removeEventListener('touchend', this.handleVolumeMouseUp, true); + } + + handleMouseVolSlide = throttle(e => { + const rect = this.volume.getBoundingClientRect(); + const x = (e.clientX - rect.left) / this.volWidth; // x position within the element. + + if(!isNaN(x)) { + let slideamt = x; + + if (x > 1) { + slideamt = 1; + } else if(x < 0) { + slideamt = 0; + } + + this.wavesurfer.setVolume(slideamt); + } + }, 60); + + render () { + const { height, intl, alt, editable } = this.props; + const { paused, muted, volume, currentTime } = this.state; + + const volumeWidth = muted ? 0 : volume * this.volWidth; + const volumeHandleLoc = muted ? this.volHandleOffset(0) : this.volHandleOffset(volume); + + return ( + <div className={classNames('audio-player', { editable })}> + <div className='audio-player__progress-placeholder' style={{ display: 'none' }} /> + <div className='audio-player__wave-placeholder' style={{ display: 'none' }} /> + + <div + className='audio-player__waveform' + aria-label={alt} + title={alt} + style={{ height }} + ref={this.setWaveformRef} + /> + + <div className='video-player__controls active'> + <div className='video-player__buttons-bar'> + <div className='video-player__buttons left'> + <button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button> + <button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> + + <div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}> + <div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} /> + + <span + className={classNames('video-player__volume__handle')} + tabIndex='0' + style={{ left: `${volumeHandleLoc}px` }} + /> + </div> + + <span> + <span className='video-player__time-current'>{formatTime(currentTime)}</span> + <span className='video-player__time-sep'>/</span> + <span className='video-player__time-total'>{formatTime(this.state.duration || Math.floor(this.props.duration))}</span> + </span> + </div> + </div> + </div> + </div> + ); + } + +} diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js index 96a219c94720b2d34f635c1305e807aa7c2976eb..051431ed2933a138fe522c0c466701e48ec05020 100644 --- a/app/javascript/mastodon/features/blocks/index.js +++ b/app/javascript/mastodon/features/blocks/index.js @@ -32,6 +32,7 @@ class Blocks extends ImmutablePureComponent { accountIds: ImmutablePropTypes.list, hasMore: PropTypes.bool, intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -43,7 +44,7 @@ class Blocks extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, accountIds, shouldUpdateScroll, hasMore } = this.props; + const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn } = this.props; if (!accountIds) { return ( @@ -56,7 +57,7 @@ class Blocks extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />; return ( - <Column icon='ban' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <ScrollableList scrollKey='blocks' @@ -64,6 +65,7 @@ class Blocks extends ImmutablePureComponent { hasMore={hasMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {accountIds.map(id => <AccountContainer key={id} id={id} /> diff --git a/app/javascript/mastodon/features/community_timeline/components/column_settings.js b/app/javascript/mastodon/features/community_timeline/components/column_settings.js index 8250190a7adf1ebbc6aa88a24d22de8392d7cd7a..0cb6db8836b9bda09fcd78e3f89c80b789170322 100644 --- a/app/javascript/mastodon/features/community_timeline/components/column_settings.js +++ b/app/javascript/mastodon/features/community_timeline/components/column_settings.js @@ -20,7 +20,7 @@ class ColumnSettings extends React.PureComponent { return ( <div> <div className='column-settings__row'> - <SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />} /> + <SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} /> </div> </div> ); diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js index 7d26c98b0d30de457d75ddc02bed8ea626c7d609..30153cc15376000459beefd13247f1e4d5ce52ff 100644 --- a/app/javascript/mastodon/features/community_timeline/index.js +++ b/app/javascript/mastodon/features/community_timeline/index.js @@ -18,9 +18,10 @@ const mapStateToProps = (state, { onlyMedia, columnId }) => { const uuid = columnId; const columns = state.getIn(['settings', 'columns']); const index = columns.findIndex(c => c.get('uuid') === uuid); + const timelineState = state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`]); return { - hasUnread: state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'unread']) > 0, + hasUnread: !!timelineState && (timelineState.get('unread') > 0 || timelineState.get('pendingItems').size > 0), onlyMedia: (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'community', 'other', 'onlyMedia']), }; }; @@ -105,7 +106,7 @@ class CommunityTimeline extends React.PureComponent { const pinned = !!columnId; return ( - <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <ColumnHeader icon='users' active={hasUnread} @@ -126,6 +127,7 @@ class CommunityTimeline extends React.PureComponent { onLoadMore={this.handleLoadMore} emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/compose/components/action_bar.js b/app/javascript/mastodon/features/compose/components/action_bar.js index d0303dbfbbf9007d0985bcdb16e7931fe19da9f1..dd2632796c033ca9d5497cf9518b11df16f584be 100644 --- a/app/javascript/mastodon/features/compose/components/action_bar.js +++ b/app/javascript/mastodon/features/compose/components/action_bar.js @@ -23,9 +23,14 @@ class ActionBar extends React.PureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, + onLogout: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; + handleLogout = () => { + this.props.onLogout(); + } + render () { const { intl } = this.props; @@ -44,7 +49,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' }); menu.push(null); - menu.push({ text: intl.formatMessage(messages.logout), href: '/auth/sign_out', target: null, method: 'delete' }); + menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout }); return ( <div className='compose__action-bar'> diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index c1429c756fdbefb2e802592c543fb418a5adb951..e57c3c20c33309348f6023a3d8ccc6b3b9238c55 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -6,7 +6,7 @@ import Overlay from 'react-overlays/lib/Overlay'; import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; import detectPassiveEvents from 'detect-passive-events'; -import { buildCustomEmojis } from '../../emoji/emoji'; +import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji'; const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, @@ -31,19 +31,6 @@ let EmojiPicker, Emoji; // load asynchronously const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`; const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; -const categoriesSort = [ - 'recent', - 'custom', - 'people', - 'nature', - 'foods', - 'activity', - 'places', - 'objects', - 'symbols', - 'flags', -]; - class ModifierPickerMenu extends React.PureComponent { static propTypes = { @@ -241,8 +228,23 @@ class EmojiPickerMenu extends React.PureComponent { } const title = intl.formatMessage(messages.emoji); + const { modifierOpen } = this.state; + const categoriesSort = [ + 'recent', + 'people', + 'nature', + 'foods', + 'activity', + 'places', + 'objects', + 'symbols', + 'flags', + ]; + + categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(custom_emojis)).sort()); + return ( <div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}> <EmojiPicker diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.js b/app/javascript/mastodon/features/compose/components/navigation_bar.js index d8d49cb95cd5391409374d165f43b8f73abe1972..840d0a3da3abbc0b1c02fbb5db7c4841575ccaf1 100644 --- a/app/javascript/mastodon/features/compose/components/navigation_bar.js +++ b/app/javascript/mastodon/features/compose/components/navigation_bar.js @@ -12,6 +12,7 @@ export default class NavigationBar extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, + onLogout: PropTypes.func.isRequired, onClose: PropTypes.func, }; @@ -33,7 +34,7 @@ export default class NavigationBar extends ImmutablePureComponent { <div className='navigation-bar__actions'> <IconButton className='close' title='' icon='close' onClick={this.props.onClose} /> - <ActionBar account={this.props.account} /> + <ActionBar account={this.props.account} onLogout={this.props.onLogout} /> </div> </div> ); diff --git a/app/javascript/mastodon/features/compose/components/search.js b/app/javascript/mastodon/features/compose/components/search.js index 7f9edfeee30e75c2b1ad1fb49f390460c1900325..3e36a922be674fe738746941d38b4b7d9ce0d6de 100644 --- a/app/javascript/mastodon/features/compose/components/search.js +++ b/app/javascript/mastodon/features/compose/components/search.js @@ -60,12 +60,17 @@ class Search extends React.PureComponent { onShow: PropTypes.func.isRequired, openInRoute: PropTypes.bool, intl: PropTypes.object.isRequired, + singleColumn: PropTypes.bool, }; state = { expanded: false, }; + setRef = c => { + this.searchForm = c; + } + handleChange = (e) => { this.props.onChange(e.target.value); } @@ -95,6 +100,13 @@ class Search extends React.PureComponent { handleFocus = () => { this.setState({ expanded: true }); this.props.onShow(); + + if (this.searchForm && !this.props.singleColumn) { + const { left, right } = this.searchForm.getBoundingClientRect(); + if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) { + this.searchForm.scrollIntoView(); + } + } } handleBlur = () => { @@ -111,6 +123,7 @@ class Search extends React.PureComponent { <label> <span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span> <input + ref={this.setRef} className='search__input' type='text' placeholder={intl.formatMessage(messages.placeholder)} diff --git a/app/javascript/mastodon/features/compose/components/search_results.js b/app/javascript/mastodon/features/compose/components/search_results.js index 2f338dd2469a49856d8c66b96e55e4591e46d7d6..c85031519b6164c072cf228c9c58bc8a23ef85ba 100644 --- a/app/javascript/mastodon/features/compose/components/search_results.js +++ b/app/javascript/mastodon/features/compose/components/search_results.js @@ -8,6 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import Hashtag from '../../../components/hashtag'; import Icon from 'mastodon/components/icon'; import { searchEnabled } from '../../../initial_state'; +import LoadMore from 'mastodon/components/load_more'; const messages = defineMessages({ dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, @@ -20,15 +21,24 @@ class SearchResults extends ImmutablePureComponent { results: ImmutablePropTypes.map.isRequired, suggestions: ImmutablePropTypes.list.isRequired, fetchSuggestions: PropTypes.func.isRequired, + expandSearch: PropTypes.func.isRequired, dismissSuggestion: PropTypes.func.isRequired, searchTerm: PropTypes.string, intl: PropTypes.object.isRequired, }; componentDidMount () { - this.props.fetchSuggestions(); + if (this.props.searchTerm === '') { + this.props.fetchSuggestions(); + } } + handleLoadMoreAccounts = () => this.props.expandSearch('accounts'); + + handleLoadMoreStatuses = () => this.props.expandSearch('statuses'); + + handleLoadMoreHashtags = () => this.props.expandSearch('hashtags'); + render () { const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props; @@ -65,6 +75,8 @@ class SearchResults extends ImmutablePureComponent { <h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5> {results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)} + + {results.get('accounts').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} </div> ); } @@ -76,6 +88,18 @@ class SearchResults extends ImmutablePureComponent { <h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5> {results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)} + + {results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} + </div> + ); + } else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) { + statuses = ( + <div className='search-results__section'> + <h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5> + + <div className='search-results__info'> + <FormattedMessage id='search_results.statuses_fts_disabled' defaultMessage='Searching toots by their content is not enabled on this Mastodon server.' /> + </div> </div> ); } else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) { @@ -97,6 +121,8 @@ class SearchResults extends ImmutablePureComponent { <h5><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5> {results.get('hashtags').map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} + + {results.get('hashtags').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} </div> ); } diff --git a/app/javascript/mastodon/features/compose/components/text_icon_button.js b/app/javascript/mastodon/features/compose/components/text_icon_button.js index 9c8ffab1f93d84fc947d3321e40e4e94c0ce7311..f0b1335386ded402665a932603829dfa50b616b9 100644 --- a/app/javascript/mastodon/features/compose/components/text_icon_button.js +++ b/app/javascript/mastodon/features/compose/components/text_icon_button.js @@ -1,6 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; +const iconStyle = { + height: null, + lineHeight: '27px', + width: `${18 * 1.28571429}px`, +}; + export default class TextIconButton extends React.PureComponent { static propTypes = { @@ -20,7 +26,14 @@ export default class TextIconButton extends React.PureComponent { const { label, title, active, ariaControls } = this.props; return ( - <button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} aria-expanded={active} onClick={this.handleClick} aria-controls={ariaControls}> + <button + title={title} + aria-label={title} + className={`text-icon-button ${active ? 'active' : ''}`} + aria-expanded={active} + onClick={this.handleClick} + aria-controls={ariaControls} style={iconStyle} + > {label} </button> ); diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index 629cbc36ac34cff657ac860c4db18007ea611219..b9f0fbe3abdf3c0722ba52e8f1f97c78f3262097 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -4,16 +4,11 @@ import PropTypes from 'prop-types'; import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; -const messages = defineMessages({ - description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' }, -}); - -export default @injectIntl -class Upload extends ImmutablePureComponent { +export default class Upload extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, @@ -21,30 +16,10 @@ class Upload extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.map.isRequired, - intl: PropTypes.object.isRequired, onUndo: PropTypes.func.isRequired, - onDescriptionChange: PropTypes.func.isRequired, onOpenFocalPoint: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, - }; - - state = { - hovered: false, - focused: false, - dirtyDescription: null, }; - handleKeyDown = (e) => { - if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { - this.handleSubmit(); - } - } - - handleSubmit = () => { - this.handleInputBlur(); - this.props.onSubmit(this.context.router.history); - } - handleUndoClick = e => { e.stopPropagation(); this.props.onUndo(this.props.media.get('id')); @@ -55,69 +30,21 @@ class Upload extends ImmutablePureComponent { this.props.onOpenFocalPoint(this.props.media.get('id')); } - handleInputChange = e => { - this.setState({ dirtyDescription: e.target.value }); - } - - handleMouseEnter = () => { - this.setState({ hovered: true }); - } - - handleMouseLeave = () => { - this.setState({ hovered: false }); - } - - handleInputFocus = () => { - this.setState({ focused: true }); - } - - handleClick = () => { - this.setState({ focused: true }); - } - - handleInputBlur = () => { - const { dirtyDescription } = this.state; - - this.setState({ focused: false, dirtyDescription: null }); - - if (dirtyDescription !== null) { - this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription); - } - } - render () { - const { intl, media } = this.props; - const active = this.state.hovered || this.state.focused; - const description = this.state.dirtyDescription || (this.state.dirtyDescription !== '' && media.get('description')) || ''; + const { media } = this.props; const focusX = media.getIn(['meta', 'focus', 'x']); const focusY = media.getIn(['meta', 'focus', 'y']); const x = ((focusX / 2) + .5) * 100; const y = ((focusY / -2) + .5) * 100; return ( - <div className='compose-form__upload' tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick} role='button'> + <div className='compose-form__upload' tabIndex='0' role='button'> <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}> {({ scale }) => ( <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> - <div className={classNames('compose-form__upload__actions', { active })}> + <div className={classNames('compose-form__upload__actions', { active: true })}> <button className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> - {media.get('type') === 'image' && <button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='crosshairs' /> <FormattedMessage id='upload_form.focus' defaultMessage='Crop' /></button>} - </div> - - <div className={classNames('compose-form__upload-description', { active })}> - <label> - <span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span> - - <textarea - placeholder={intl.formatMessage(messages.description)} - value={description} - maxLength={420} - onFocus={this.handleInputFocus} - onChange={this.handleInputChange} - onBlur={this.handleInputBlur} - onKeyDown={this.handleKeyDown} - /> - </label> + <button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button> </div> </div> )} diff --git a/app/javascript/mastodon/features/compose/components/upload_form.js b/app/javascript/mastodon/features/compose/components/upload_form.js index 9ff2aa0fa631fdbcd64dfd3038d12e0166b08282..c6eac554e6cf6fbe7b13ee4631cf7fd82f3bf56a 100644 --- a/app/javascript/mastodon/features/compose/components/upload_form.js +++ b/app/javascript/mastodon/features/compose/components/upload_form.js @@ -4,6 +4,7 @@ import UploadProgressContainer from '../containers/upload_progress_container'; import ImmutablePureComponent from 'react-immutable-pure-component'; import UploadContainer from '../containers/upload_container'; import SensitiveButtonContainer from '../containers/sensitive_button_container'; +import { FormattedMessage } from 'react-intl'; export default class UploadForm extends ImmutablePureComponent { @@ -16,7 +17,7 @@ export default class UploadForm extends ImmutablePureComponent { return ( <div className='compose-form__upload-wrapper'> - <UploadProgressContainer /> + <UploadProgressContainer icon='upload' message={<FormattedMessage id='upload_progress.label' defaultMessage='Uploading…' />} /> <div className='compose-form__uploads-wrapper'> {mediaIds.map(id => ( diff --git a/app/javascript/mastodon/features/compose/components/upload_progress.js b/app/javascript/mastodon/features/compose/components/upload_progress.js index cbe58f573926954c42db1193e6b3b442a31a2d7e..b0bfe0c9a142bb991bcb5558c4c82c66d1ab23c6 100644 --- a/app/javascript/mastodon/features/compose/components/upload_progress.js +++ b/app/javascript/mastodon/features/compose/components/upload_progress.js @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; -import { FormattedMessage } from 'react-intl'; import Icon from 'mastodon/components/icon'; export default class UploadProgress extends React.PureComponent { @@ -10,10 +9,12 @@ export default class UploadProgress extends React.PureComponent { static propTypes = { active: PropTypes.bool, progress: PropTypes.number, + icon: PropTypes.string.isRequired, + message: PropTypes.node.isRequired, }; render () { - const { active, progress } = this.props; + const { active, progress, icon, message } = this.props; if (!active) { return null; @@ -22,11 +23,11 @@ export default class UploadProgress extends React.PureComponent { return ( <div className='upload-progress'> <div className='upload-progress__icon'> - <Icon id='upload' /> + <Icon id={icon} /> </div> <div className='upload-progress__message'> - <FormattedMessage id='upload_progress.label' defaultMessage='Uploading...' /> + {message} <div className='upload-progress__backdrop'> <Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}> diff --git a/app/javascript/mastodon/features/compose/containers/navigation_container.js b/app/javascript/mastodon/features/compose/containers/navigation_container.js index eb9f3ea45e6e4516c11d22aaa3a33832fc0549e9..8606a642e49781c3c1d8b799ad10ed6bc696686e 100644 --- a/app/javascript/mastodon/features/compose/containers/navigation_container.js +++ b/app/javascript/mastodon/features/compose/containers/navigation_container.js @@ -1,11 +1,29 @@ import { connect } from 'react-redux'; +import { defineMessages, injectIntl } from 'react-intl'; import NavigationBar from '../components/navigation_bar'; +import { logOut } from 'mastodon/utils/log_out'; +import { openModal } from 'mastodon/actions/modal'; import { me } from '../../../initial_state'; +const messages = defineMessages({ + logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' }, + logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' }, +}); + const mapStateToProps = state => { return { account: state.getIn(['accounts', me]), }; }; -export default connect(mapStateToProps)(NavigationBar); +const mapDispatchToProps = (dispatch, { intl }) => ({ + onLogout () { + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.logoutMessage), + confirm: intl.formatMessage(messages.logoutConfirm), + onConfirm: () => logOut(), + })); + }, +}); + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NavigationBar)); diff --git a/app/javascript/mastodon/features/compose/containers/search_results_container.js b/app/javascript/mastodon/features/compose/containers/search_results_container.js index e4d5f3420787c81184c529bc951e843243a46cfb..1f714ff8342cb267b07c22a163d1bca0d8bfcf0c 100644 --- a/app/javascript/mastodon/features/compose/containers/search_results_container.js +++ b/app/javascript/mastodon/features/compose/containers/search_results_container.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import SearchResults from '../components/search_results'; -import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestions'; +import { fetchSuggestions, dismissSuggestion } from 'mastodon/actions/suggestions'; +import { expandSearch } from 'mastodon/actions/search'; const mapStateToProps = state => ({ results: state.getIn(['search', 'results']), @@ -10,6 +11,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ fetchSuggestions: () => dispatch(fetchSuggestions()), + expandSearch: type => dispatch(expandSearch(type)), dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))), }); diff --git a/app/javascript/mastodon/features/compose/containers/upload_container.js b/app/javascript/mastodon/features/compose/containers/upload_container.js index b6d81f03ac6d9cf387f3e23d296f59f1d3c1b596..342b0c2a9cad55bed17ce03bfdf65c5bcc97c7a9 100644 --- a/app/javascript/mastodon/features/compose/containers/upload_container.js +++ b/app/javascript/mastodon/features/compose/containers/upload_container.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import Upload from '../components/upload'; -import { undoUploadCompose, changeUploadCompose } from '../../../actions/compose'; +import { undoUploadCompose } from '../../../actions/compose'; import { openModal } from '../../../actions/modal'; import { submitCompose } from '../../../actions/compose'; @@ -14,10 +14,6 @@ const mapDispatchToProps = dispatch => ({ dispatch(undoUploadCompose(id)); }, - onDescriptionChange: (id, description) => { - dispatch(changeUploadCompose(id, { description })); - }, - onOpenFocalPoint: id => { dispatch(openModal('FOCAL_POINT', { id })); }, diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index 0731abcf4d5a8c3245de61a5c78fb5ebc80fea6e..e2de8b0e6a20d1da4479c19a77dd201c434af682 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -12,9 +12,11 @@ import Motion from '../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import SearchResultsContainer from './containers/search_results_container'; import { changeComposing } from '../../actions/compose'; +import { openModal } from 'mastodon/actions/modal'; import elephantUIPlane from '../../../images/elephant_ui_plane.svg'; import { mascot } from '../../initial_state'; import Icon from 'mastodon/components/icon'; +import { logOut } from 'mastodon/utils/log_out'; const messages = defineMessages({ start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, @@ -25,6 +27,8 @@ const messages = defineMessages({ preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new toot' }, + logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' }, + logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' }, }); const mapStateToProps = (state, ownProps) => ({ @@ -61,6 +65,21 @@ class Compose extends React.PureComponent { } } + handleLogoutClick = e => { + const { dispatch, intl } = this.props; + + e.preventDefault(); + e.stopPropagation(); + + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.logoutMessage), + confirm: intl.formatMessage(messages.logoutConfirm), + onConfirm: () => logOut(), + })); + + return false; + } + onFocus = () => { this.props.dispatch(changeComposing(true)); } @@ -92,7 +111,7 @@ class Compose extends React.PureComponent { <Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><Icon id='globe' fixedWidth /></Link> )} <a href='/settings/preferences' className='drawer__tab' title={intl.formatMessage(messages.preferences)} aria-label={intl.formatMessage(messages.preferences)}><Icon id='cog' fixedWidth /></a> - <a href='/auth/sign_out' className='drawer__tab' data-method='delete' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)}><Icon id='sign-out' fixedWidth /></a> + <a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' fixedWidth /></a> </nav> ); } diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.js b/app/javascript/mastodon/features/direct_timeline/components/conversation.js index ffcd6d2811af8ac082a4cee79051e5073f2377c8..2cbaa0791be0fc55704e9eeb3d9eee32ca17d09e 100644 --- a/app/javascript/mastodon/features/direct_timeline/components/conversation.js +++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.js @@ -2,9 +2,29 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import StatusContainer from '../../../containers/status_container'; +import StatusContent from 'mastodon/components/status_content'; +import AttachmentList from 'mastodon/components/attachment_list'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; +import AvatarComposite from 'mastodon/components/avatar_composite'; +import Permalink from 'mastodon/components/permalink'; +import IconButton from 'mastodon/components/icon_button'; +import RelativeTimestamp from 'mastodon/components/relative_timestamp'; +import { HotKeys } from 'react-hotkeys'; +import { autoPlayGif } from 'mastodon/initial_state'; -export default class Conversation extends ImmutablePureComponent { +const messages = defineMessages({ + more: { id: 'status.more', defaultMessage: 'More' }, + open: { id: 'conversation.open', defaultMessage: 'View conversation' }, + reply: { id: 'status.reply', defaultMessage: 'Reply' }, + markAsRead: { id: 'conversation.mark_as_read', defaultMessage: 'Mark as read' }, + delete: { id: 'conversation.delete', defaultMessage: 'Delete conversation' }, + muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' }, + unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' }, +}); + +export default @injectIntl +class Conversation extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, @@ -13,25 +33,76 @@ export default class Conversation extends ImmutablePureComponent { static propTypes = { conversationId: PropTypes.string.isRequired, accounts: ImmutablePropTypes.list.isRequired, - lastStatusId: PropTypes.string, + lastStatus: ImmutablePropTypes.map, unread:PropTypes.bool.isRequired, onMoveUp: PropTypes.func, onMoveDown: PropTypes.func, markRead: PropTypes.func.isRequired, + delete: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, }; + _updateEmojis () { + const node = this.namesNode; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + handleClick = () => { if (!this.context.router) { return; } - const { lastStatusId, unread, markRead } = this.props; + const { lastStatus, unread, markRead } = this.props; if (unread) { markRead(); } - this.context.router.history.push(`/statuses/${lastStatusId}`); + this.context.router.history.push(`/statuses/${lastStatus.get('id')}`); + } + + handleMarkAsRead = () => { + this.props.markRead(); + } + + handleReply = () => { + this.props.reply(this.props.lastStatus, this.context.router.history); + } + + handleDelete = () => { + this.props.delete(); } handleHotkeyMoveUp = () => { @@ -42,22 +113,92 @@ export default class Conversation extends ImmutablePureComponent { this.props.onMoveDown(this.props.conversationId); } + handleConversationMute = () => { + this.props.onMute(this.props.lastStatus); + } + + handleShowMore = () => { + this.props.onToggleHidden(this.props.lastStatus); + } + + setNamesRef = (c) => { + this.namesNode = c; + } + render () { - const { accounts, lastStatusId, unread } = this.props; + const { accounts, lastStatus, unread, intl } = this.props; - if (lastStatusId === null) { + if (lastStatus === null) { return null; } + const menu = [ + { text: intl.formatMessage(messages.open), action: this.handleClick }, + null, + ]; + + menu.push({ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMute }); + + if (unread) { + menu.push({ text: intl.formatMessage(messages.markAsRead), action: this.handleMarkAsRead }); + menu.push(null); + } + + menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDelete }); + + const names = accounts.map(a => <Permalink to={`/accounts/${a.get('id')}`} href={a.get('url')} key={a.get('id')} title={a.get('acct')}><bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi></Permalink>).reduce((prev, cur) => [prev, ', ', cur]); + + const handlers = { + reply: this.handleReply, + open: this.handleClick, + moveUp: this.handleHotkeyMoveUp, + moveDown: this.handleHotkeyMoveDown, + toggleHidden: this.handleShowMore, + }; + return ( - <StatusContainer - id={lastStatusId} - unread={unread} - otherAccounts={accounts} - onMoveUp={this.handleHotkeyMoveUp} - onMoveDown={this.handleHotkeyMoveDown} - onClick={this.handleClick} - /> + <HotKeys handlers={handlers}> + <div className='conversation focusable muted' tabIndex='0'> + <div className='conversation__avatar'> + <AvatarComposite accounts={accounts} size={48} /> + </div> + + <div className='conversation__content'> + <div className='conversation__content__info'> + <div className='conversation__content__relative-time'> + <RelativeTimestamp timestamp={lastStatus.get('created_at')} /> + </div> + + <div className='conversation__content__names' ref={this.setNamesRef}> + <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} /> + </div> + </div> + + <StatusContent + status={lastStatus} + onClick={this.handleClick} + expanded={!lastStatus.get('hidden')} + onExpandedToggle={this.handleShowMore} + collapsable + /> + + {lastStatus.get('media_attachments').size > 0 && ( + <AttachmentList + compact + media={lastStatus.get('media_attachments')} + /> + )} + + <div className='status__action-bar'> + <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReply} /> + + <div className='status__action-bar-dropdown'> + <DropdownMenuContainer status={lastStatus} items={menu} icon='ellipsis-h' size={18} direction='right' title={intl.formatMessage(messages.more)} /> + </div> + </div> + </div> + </div> + </HotKeys> ); } diff --git a/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js b/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js index bd6f6bfb0176ce45ff43f85c883f06bf4e03f6c4..94cef81a7d2d5cb718ad9589428b59e5016fec80 100644 --- a/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js +++ b/app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js @@ -1,19 +1,74 @@ import { connect } from 'react-redux'; import Conversation from '../components/conversation'; -import { markConversationRead } from '../../../actions/conversations'; +import { markConversationRead, deleteConversation } from 'mastodon/actions/conversations'; +import { makeGetStatus } from 'mastodon/selectors'; +import { replyCompose } from 'mastodon/actions/compose'; +import { openModal } from 'mastodon/actions/modal'; +import { muteStatus, unmuteStatus, hideStatus, revealStatus } from 'mastodon/actions/statuses'; +import { defineMessages, injectIntl } from 'react-intl'; -const mapStateToProps = (state, { conversationId }) => { - const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId); +const messages = defineMessages({ + replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, + replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, +}); + +const mapStateToProps = () => { + const getStatus = makeGetStatus(); + + return (state, { conversationId }) => { + const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId); + const lastStatusId = conversation.get('last_status', null); - return { - accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)), - unread: conversation.get('unread'), - lastStatusId: conversation.get('last_status', null), + return { + accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)), + unread: conversation.get('unread'), + lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }), + }; }; }; -const mapDispatchToProps = (dispatch, { conversationId }) => ({ - markRead: () => dispatch(markConversationRead(conversationId)), +const mapDispatchToProps = (dispatch, { intl, conversationId }) => ({ + + markRead () { + dispatch(markConversationRead(conversationId)); + }, + + reply (status, router) { + dispatch((_, getState) => { + let state = getState(); + + if (state.getIn(['compose', 'text']).trim().length !== 0) { + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.replyMessage), + confirm: intl.formatMessage(messages.replyConfirm), + onConfirm: () => dispatch(replyCompose(status, router)), + })); + } else { + dispatch(replyCompose(status, router)); + } + }); + }, + + delete () { + dispatch(deleteConversation(conversationId)); + }, + + onMute (status) { + if (status.get('muted')) { + dispatch(unmuteStatus(status.get('id'))); + } else { + dispatch(muteStatus(status.get('id'))); + } + }, + + onToggleHidden (status) { + if (status.get('hidden')) { + dispatch(revealStatus(status.get('id'))); + } else { + dispatch(hideStatus(status.get('id'))); + } + }, + }); -export default connect(mapStateToProps, mapDispatchToProps)(Conversation); +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Conversation)); diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js index d202f3bfd904d86255010f995615399fe4978f41..5ce795760076f02c3734f3cc2f85b025ce4cd8b9 100644 --- a/app/javascript/mastodon/features/direct_timeline/index.js +++ b/app/javascript/mastodon/features/direct_timeline/index.js @@ -75,7 +75,7 @@ class DirectTimeline extends React.PureComponent { const pinned = !!columnId; return ( - <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <ColumnHeader icon='envelope' active={hasUnread} diff --git a/app/javascript/mastodon/features/directory/components/account_card.js b/app/javascript/mastodon/features/directory/components/account_card.js new file mode 100644 index 0000000000000000000000000000000000000000..50ad744501d4de44e11c831329b13912fecbdefb --- /dev/null +++ b/app/javascript/mastodon/features/directory/components/account_card.js @@ -0,0 +1,190 @@ +import React from 'react'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { makeGetAccount } from 'mastodon/selectors'; +import Avatar from 'mastodon/components/avatar'; +import DisplayName from 'mastodon/components/display_name'; +import Permalink from 'mastodon/components/permalink'; +import RelativeTimestamp from 'mastodon/components/relative_timestamp'; +import IconButton from 'mastodon/components/icon_button'; +import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; +import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state'; +import { shortNumberFormat } from 'mastodon/utils/numbers'; +import { followAccount, unfollowAccount, blockAccount, unblockAccount, unmuteAccount } from 'mastodon/actions/accounts'; +import { openModal } from 'mastodon/actions/modal'; +import { initMuteModal } from 'mastodon/actions/mutes'; + +const messages = defineMessages({ + follow: { id: 'account.follow', defaultMessage: 'Follow' }, + unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, + requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' }, + unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, + unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, +}); + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = (state, { id }) => ({ + account: getAccount(state, id), + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { intl }) => ({ + + onFollow (account) { + if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) { + if (unfollowModal) { + dispatch(openModal('CONFIRM', { + message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, + confirm: intl.formatMessage(messages.unfollowConfirm), + onConfirm: () => dispatch(unfollowAccount(account.get('id'))), + })); + } else { + dispatch(unfollowAccount(account.get('id'))); + } + } else { + dispatch(followAccount(account.get('id'))); + } + }, + + onBlock (account) { + if (account.getIn(['relationship', 'blocking'])) { + dispatch(unblockAccount(account.get('id'))); + } else { + dispatch(blockAccount(account.get('id'))); + } + }, + + onMute (account) { + if (account.getIn(['relationship', 'muting'])) { + dispatch(unmuteAccount(account.get('id'))); + } else { + dispatch(initMuteModal(account)); + } + }, + +}); + +export default @injectIntl +@connect(makeMapStateToProps, mapDispatchToProps) +class AccountCard extends ImmutablePureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + intl: PropTypes.object.isRequired, + onFollow: PropTypes.func.isRequired, + onBlock: PropTypes.func.isRequired, + onMute: PropTypes.func.isRequired, + }; + + _updateEmojis () { + const node = this.node; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + + handleFollow = () => { + this.props.onFollow(this.props.account); + } + + handleBlock = () => { + this.props.onBlock(this.props.account); + } + + handleMute = () => { + this.props.onMute(this.props.account); + } + + setRef = (c) => { + this.node = c; + } + + render () { + const { account, intl } = this.props; + + let buttons; + + if (account.get('id') !== me && account.get('relationship', null) !== null) { + const following = account.getIn(['relationship', 'following']); + const requested = account.getIn(['relationship', 'requested']); + const blocking = account.getIn(['relationship', 'blocking']); + const muting = account.getIn(['relationship', 'muting']); + + if (requested) { + buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />; + } else if (blocking) { + buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />; + } else if (muting) { + buttons = <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />; + } else if (!account.get('moved') || following) { + buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />; + } + } + + return ( + <div className='directory__card'> + <div className='directory__card__img'> + <img src={autoPlayGif ? account.get('header') : account.get('header_static')} alt='' /> + </div> + + <div className='directory__card__bar'> + <Permalink className='directory__card__bar__name' href={account.get('url')} to={`/accounts/${account.get('id')}`}> + <Avatar account={account} size={48} /> + <DisplayName account={account} /> + </Permalink> + + <div className='directory__card__bar__relationship account__relationship'> + {buttons} + </div> + </div> + + <div className='directory__card__extra' ref={this.setRef}> + <div className='account__header__content' dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }} /> + </div> + + <div className='directory__card__extra'> + <div className='accounts-table__count'>{shortNumberFormat(account.get('statuses_count'))} <small><FormattedMessage id='account.posts' defaultMessage='Toots' /></small></div> + <div className='accounts-table__count'>{shortNumberFormat(account.get('followers_count'))} <small><FormattedMessage id='account.followers' defaultMessage='Followers' /></small></div> + <div className='accounts-table__count'>{account.get('last_status_at') === null ? <FormattedMessage id='account.never_active' defaultMessage='Never' /> : <RelativeTimestamp timestamp={account.get('last_status_at')} />} <small><FormattedMessage id='account.last_status' defaultMessage='Last active' /></small></div> + </div> + </div> + ); + } + +} diff --git a/app/javascript/mastodon/features/directory/index.js b/app/javascript/mastodon/features/directory/index.js new file mode 100644 index 0000000000000000000000000000000000000000..2f91e759b554e5ba2c6c999b04746bb506f8cd37 --- /dev/null +++ b/app/javascript/mastodon/features/directory/index.js @@ -0,0 +1,171 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { defineMessages, injectIntl } from 'react-intl'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Column from 'mastodon/components/column'; +import ColumnHeader from 'mastodon/components/column_header'; +import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'mastodon/actions/columns'; +import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory'; +import { List as ImmutableList } from 'immutable'; +import AccountCard from './components/account_card'; +import RadioButton from 'mastodon/components/radio_button'; +import classNames from 'classnames'; +import LoadMore from 'mastodon/components/load_more'; +import { ScrollContainer } from 'react-router-scroll-4'; + +const messages = defineMessages({ + title: { id: 'column.directory', defaultMessage: 'Browse profiles' }, + recentlyActive: { id: 'directory.recently_active', defaultMessage: 'Recently active' }, + newArrivals: { id: 'directory.new_arrivals', defaultMessage: 'New arrivals' }, + local: { id: 'directory.local', defaultMessage: 'From {domain} only' }, + federated: { id: 'directory.federated', defaultMessage: 'From known fediverse' }, +}); + +const mapStateToProps = state => ({ + accountIds: state.getIn(['user_lists', 'directory', 'items'], ImmutableList()), + isLoading: state.getIn(['user_lists', 'directory', 'isLoading'], true), + domain: state.getIn(['meta', 'domain']), +}); + +export default @connect(mapStateToProps) +@injectIntl +class Directory extends React.PureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + isLoading: PropTypes.bool, + accountIds: ImmutablePropTypes.list.isRequired, + dispatch: PropTypes.func.isRequired, + shouldUpdateScroll: PropTypes.func, + columnId: PropTypes.string, + intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, + domain: PropTypes.string.isRequired, + params: PropTypes.shape({ + order: PropTypes.string, + local: PropTypes.bool, + }), + }; + + state = { + order: null, + local: null, + }; + + handlePin = () => { + const { columnId, dispatch } = this.props; + + if (columnId) { + dispatch(removeColumn(columnId)); + } else { + dispatch(addColumn('DIRECTORY', this.getParams(this.props, this.state))); + } + } + + getParams = (props, state) => ({ + order: state.order === null ? (props.params.order || 'active') : state.order, + local: state.local === null ? (props.params.local || false) : state.local, + }); + + handleMove = dir => { + const { columnId, dispatch } = this.props; + dispatch(moveColumn(columnId, dir)); + } + + handleHeaderClick = () => { + this.column.scrollTop(); + } + + componentDidMount () { + const { dispatch } = this.props; + dispatch(fetchDirectory(this.getParams(this.props, this.state))); + } + + componentDidUpdate (prevProps, prevState) { + const { dispatch } = this.props; + const paramsOld = this.getParams(prevProps, prevState); + const paramsNew = this.getParams(this.props, this.state); + + if (paramsOld.order !== paramsNew.order || paramsOld.local !== paramsNew.local) { + dispatch(fetchDirectory(paramsNew)); + } + } + + setRef = c => { + this.column = c; + } + + handleChangeOrder = e => { + const { dispatch, columnId } = this.props; + + if (columnId) { + dispatch(changeColumnParams(columnId, ['order'], e.target.value)); + } else { + this.setState({ order: e.target.value }); + } + } + + handleChangeLocal = e => { + const { dispatch, columnId } = this.props; + + if (columnId) { + dispatch(changeColumnParams(columnId, ['local'], e.target.value === '1')); + } else { + this.setState({ local: e.target.value === '1' }); + } + } + + handleLoadMore = () => { + const { dispatch } = this.props; + dispatch(expandDirectory(this.getParams(this.props, this.state))); + } + + render () { + const { isLoading, accountIds, intl, columnId, multiColumn, domain, shouldUpdateScroll } = this.props; + const { order, local } = this.getParams(this.props, this.state); + const pinned = !!columnId; + + const scrollableArea = ( + <div className='scrollable' style={{ background: 'transparent' }}> + <div className='filter-form'> + <div className='filter-form__column' role='group'> + <RadioButton name='order' value='active' label={intl.formatMessage(messages.recentlyActive)} checked={order === 'active'} onChange={this.handleChangeOrder} /> + <RadioButton name='order' value='new' label={intl.formatMessage(messages.newArrivals)} checked={order === 'new'} onChange={this.handleChangeOrder} /> + </div> + + <div className='filter-form__column' role='group'> + <RadioButton name='local' value='1' label={intl.formatMessage(messages.local, { domain })} checked={local} onChange={this.handleChangeLocal} /> + <RadioButton name='local' value='0' label={intl.formatMessage(messages.federated)} checked={!local} onChange={this.handleChangeLocal} /> + </div> + </div> + + <div className={classNames('directory__list', { loading: isLoading })}> + {accountIds.map(accountId => <AccountCard id={accountId} key={accountId} />)} + </div> + + <LoadMore onClick={this.handleLoadMore} visible={!isLoading} /> + </div> + ); + + return ( + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> + <ColumnHeader + icon='address-book-o' + title={intl.formatMessage(messages.title)} + onPin={this.handlePin} + onMove={this.handleMove} + onClick={this.handleHeaderClick} + pinned={pinned} + multiColumn={multiColumn} + /> + + {multiColumn && !pinned ? <ScrollContainer scrollKey='directory' shouldUpdateScroll={shouldUpdateScroll}>{scrollableArea}</ScrollContainer> : scrollableArea} + </Column> + ); + } + +} diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js index 7c075f5a5c5109cb8a268126cc1b863dd06ae5ef..482245c867cc9fdaccdb4775848af5f61c3b45d8 100644 --- a/app/javascript/mastodon/features/domain_blocks/index.js +++ b/app/javascript/mastodon/features/domain_blocks/index.js @@ -33,6 +33,7 @@ class Blocks extends ImmutablePureComponent { hasMore: PropTypes.bool, domains: ImmutablePropTypes.orderedSet, intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -44,7 +45,7 @@ class Blocks extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, domains, shouldUpdateScroll, hasMore } = this.props; + const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props; if (!domains) { return ( @@ -57,7 +58,7 @@ class Blocks extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />; return ( - <Column icon='minus-circle' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <ScrollableList scrollKey='domain_blocks' @@ -65,6 +66,7 @@ class Blocks extends ImmutablePureComponent { hasMore={hasMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {domains.map(domain => <DomainContainer key={domain} domain={domain} /> diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index fa91b50149fadcd4204b1024026fda6271eef34b..cd10e20b7ea8f00117813682c24ba4e06702d1a3 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -92,8 +92,11 @@ export const buildCustomEmojis = (customEmojis) => { keywords: [name], imageUrl: url, custom: true, + customCategory: emoji.get('category'), }); }); return emojis; }; + +export const categoriesFromEmojis = customEmojis => customEmojis.reduce((set, emoji) => set.add(emoji.get('category') ? `custom-${emoji.get('category')}` : 'custom'), new Set(['custom'])); diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js index 164fdcc0b8a69f599a63d9128095f9b9c89c095e..e4519a13e62266127c91e848e7d0c1fb90c83e4d 100644 --- a/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js +++ b/app/javascript/mastodon/features/emoji/emoji_mart_search_light.js @@ -74,7 +74,7 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo return [emojisList['-1']]; } - let values = value.toLowerCase().split(/[\s|,|\-|_]+/), + let values = value.toLowerCase().split(/[\s|,\-_]+/), allResults = []; if (values.length > 2) { diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js index fa9401b90ea16ff523129b50ce998ead6db4b988..db8a3f815f17295d1db6a3e8cbc9965b141a23b5 100644 --- a/app/javascript/mastodon/features/favourited_statuses/index.js +++ b/app/javascript/mastodon/features/favourited_statuses/index.js @@ -74,7 +74,7 @@ class Favourites extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />; return ( - <Column ref={this.setRef} label={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> <ColumnHeader icon='star' title={intl.formatMessage(messages.heading)} @@ -95,6 +95,7 @@ class Favourites extends ImmutablePureComponent { onLoadMore={this.handleLoadMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.js index d1ac229a279148e4a60a1e0fcc978220d92b634f..249e6a04424b471cec3a78f915068dc72e4c48a4 100644 --- a/app/javascript/mastodon/features/favourites/index.js +++ b/app/javascript/mastodon/features/favourites/index.js @@ -5,17 +5,23 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; import { fetchFavourites } from '../../actions/interactions'; -import { FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import AccountContainer from '../../containers/account_container'; import Column from '../ui/components/column'; -import ColumnBackButton from '../../components/column_back_button'; import ScrollableList from '../../components/scrollable_list'; +import Icon from 'mastodon/components/icon'; +import ColumnHeader from '../../components/column_header'; + +const messages = defineMessages({ + refresh: { id: 'refresh', defaultMessage: 'Refresh' }, +}); const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]), }); export default @connect(mapStateToProps) +@injectIntl class Favourites extends ImmutablePureComponent { static propTypes = { @@ -23,10 +29,14 @@ class Favourites extends ImmutablePureComponent { dispatch: PropTypes.func.isRequired, shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, + multiColumn: PropTypes.bool, + intl: PropTypes.object.isRequired, }; componentWillMount () { - this.props.dispatch(fetchFavourites(this.props.params.statusId)); + if (!this.props.accountIds) { + this.props.dispatch(fetchFavourites(this.props.params.statusId)); + } } componentWillReceiveProps (nextProps) { @@ -35,8 +45,12 @@ class Favourites extends ImmutablePureComponent { } } + handleRefresh = () => { + this.props.dispatch(fetchFavourites(this.props.params.statusId)); + } + render () { - const { shouldUpdateScroll, accountIds } = this.props; + const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props; if (!accountIds) { return ( @@ -49,13 +63,20 @@ class Favourites extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favourited this toot yet. When someone does, they will show up here.' />; return ( - <Column> - <ColumnBackButton /> + <Column bindToDocument={!multiColumn}> + <ColumnHeader + showBackButton + multiColumn={multiColumn} + extraButton={( + <button className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button> + )} + /> <ScrollableList scrollKey='favourites' shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} /> diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.js index 44624cb40665155a5330922c45b8c520962d5950..57ef441454707f191b61331b20765310b4f72788 100644 --- a/app/javascript/mastodon/features/follow_requests/index.js +++ b/app/javascript/mastodon/features/follow_requests/index.js @@ -32,6 +32,7 @@ class FollowRequests extends ImmutablePureComponent { hasMore: PropTypes.bool, accountIds: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -43,7 +44,7 @@ class FollowRequests extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, shouldUpdateScroll, accountIds, hasMore } = this.props; + const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn } = this.props; if (!accountIds) { return ( @@ -56,7 +57,7 @@ class FollowRequests extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />; return ( - <Column icon='user-plus' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='user-plus' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <ScrollableList scrollKey='follow_requests' @@ -64,6 +65,7 @@ class FollowRequests extends ImmutablePureComponent { hasMore={hasMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {accountIds.map(id => <AccountAuthorizeContainer key={id} id={id} /> diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js index e3387e1be8351241ae77caa383756b0821d1f623..9e635d2501cfe3a951ceea46b2d59073a163e3f1 100644 --- a/app/javascript/mastodon/features/followers/index.js +++ b/app/javascript/mastodon/features/followers/index.js @@ -36,11 +36,14 @@ class Followers extends ImmutablePureComponent { hasMore: PropTypes.bool, blockedBy: PropTypes.bool, isAccount: PropTypes.bool, + multiColumn: PropTypes.bool, }; componentWillMount () { - this.props.dispatch(fetchAccount(this.props.params.accountId)); - this.props.dispatch(fetchFollowers(this.props.params.accountId)); + if (!this.props.accountIds) { + this.props.dispatch(fetchAccount(this.props.params.accountId)); + this.props.dispatch(fetchFollowers(this.props.params.accountId)); + } } componentWillReceiveProps (nextProps) { @@ -55,7 +58,7 @@ class Followers extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props; + const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props; if (!isAccount) { return ( @@ -77,7 +80,7 @@ class Followers extends ImmutablePureComponent { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <ScrollableList scrollKey='followers' @@ -87,6 +90,7 @@ class Followers extends ImmutablePureComponent { prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />} alwaysPrepend emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {blockedBy ? [] : accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} /> diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js index 3bf89fb2bab7927f1f64e8c0644845c3cec9746f..284ae2c11ea86fbcac08686ab53f434c4f216229 100644 --- a/app/javascript/mastodon/features/following/index.js +++ b/app/javascript/mastodon/features/following/index.js @@ -36,11 +36,14 @@ class Following extends ImmutablePureComponent { hasMore: PropTypes.bool, blockedBy: PropTypes.bool, isAccount: PropTypes.bool, + multiColumn: PropTypes.bool, }; componentWillMount () { - this.props.dispatch(fetchAccount(this.props.params.accountId)); - this.props.dispatch(fetchFollowing(this.props.params.accountId)); + if (!this.props.accountIds) { + this.props.dispatch(fetchAccount(this.props.params.accountId)); + this.props.dispatch(fetchFollowing(this.props.params.accountId)); + } } componentWillReceiveProps (nextProps) { @@ -55,7 +58,7 @@ class Following extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props; + const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props; if (!isAccount) { return ( @@ -77,7 +80,7 @@ class Following extends ImmutablePureComponent { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <ScrollableList scrollKey='following' @@ -87,6 +90,7 @@ class Following extends ImmutablePureComponent { prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />} alwaysPrepend emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {blockedBy ? [] : accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} /> diff --git a/app/javascript/mastodon/features/generic_not_found/index.js b/app/javascript/mastodon/features/generic_not_found/index.js index 0290be47f9a4f0376d2a8d13f006f28421087ea9..41cd61a5f8b610f2b00260443dc372e01b2f7372 100644 --- a/app/javascript/mastodon/features/generic_not_found/index.js +++ b/app/javascript/mastodon/features/generic_not_found/index.js @@ -4,7 +4,7 @@ import MissingIndicator from '../../components/missing_indicator'; const GenericNotFound = () => ( <Column> - <MissingIndicator /> + <MissingIndicator fullPage /> </Column> ); diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.js new file mode 100644 index 0000000000000000000000000000000000000000..3b9a3075fbefeaba80ba820f27847eac4d08a563 --- /dev/null +++ b/app/javascript/mastodon/features/getting_started/components/trends.js @@ -0,0 +1,46 @@ +import React from 'react'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Hashtag from 'mastodon/components/hashtag'; +import { FormattedMessage } from 'react-intl'; + +export default class Trends extends ImmutablePureComponent { + + static defaultProps = { + loading: false, + }; + + static propTypes = { + trends: ImmutablePropTypes.list, + fetchTrends: PropTypes.func.isRequired, + }; + + componentDidMount () { + this.props.fetchTrends(); + this.refreshInterval = setInterval(() => this.props.fetchTrends(), 900 * 1000); + } + + componentWillUnmount () { + if (this.refreshInterval) { + clearInterval(this.refreshInterval); + } + } + + render () { + const { trends } = this.props; + + if (!trends || trends.isEmpty()) { + return null; + } + + return ( + <div className='getting-started__trends'> + <h4><FormattedMessage id='trends.trending_now' defaultMessage='Trending now' /></h4> + + {trends.take(3).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} + </div> + ); + } + +} diff --git a/app/javascript/mastodon/features/getting_started/containers/trends_container.js b/app/javascript/mastodon/features/getting_started/containers/trends_container.js new file mode 100644 index 0000000000000000000000000000000000000000..1df3fb4fe20d616b412e228f345d039f0bbbf410 --- /dev/null +++ b/app/javascript/mastodon/features/getting_started/containers/trends_container.js @@ -0,0 +1,13 @@ +import { connect } from 'react-redux'; +import { fetchTrends } from '../../../actions/trends'; +import Trends from '../components/trends'; + +const mapStateToProps = state => ({ + trends: state.getIn(['trends', 'items']), +}); + +const mapDispatchToProps = dispatch => ({ + fetchTrends: () => dispatch(fetchTrends()), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Trends); diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index fc7840ec190d1f635f0895d173ffd008367ca8f4..67ec7665b670c1fc36d64ce5999d75918d6f8245 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -7,12 +7,13 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { me, profile_directory } from '../../initial_state'; +import { me, profile_directory, showTrends } from '../../initial_state'; import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { List as ImmutableList } from 'immutable'; import NavigationBar from '../compose/components/navigation_bar'; import Icon from 'mastodon/components/icon'; import LinkFooter from 'mastodon/features/ui/components/link_footer'; +import TrendsContainer from './containers/trends_container'; const messages = defineMessages({ home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, @@ -76,16 +77,14 @@ class GettingStarted extends ImmutablePureComponent { }; componentDidMount () { - const { myAccount, fetchFollowRequests, multiColumn } = this.props; + const { fetchFollowRequests, multiColumn } = this.props; if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) { this.context.router.history.replace('/timelines/home'); return; } - if (myAccount.get('locked')) { - fetchFollowRequests(); - } + fetchFollowRequests(); } render () { @@ -106,7 +105,7 @@ class GettingStarted extends ImmutablePureComponent { if (profile_directory) { navItems.push( - <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} href='/explore' /> + <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' /> ); height += 48; @@ -119,7 +118,7 @@ class GettingStarted extends ImmutablePureComponent { height += 34; } else if (profile_directory) { navItems.push( - <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} href='/explore' /> + <ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' /> ); height += 48; @@ -133,7 +132,7 @@ class GettingStarted extends ImmutablePureComponent { height += 48*3; - if (myAccount.get('locked')) { + if (myAccount.get('locked') || unreadFollowRequests > 0) { navItems.push(<ColumnLink key={i++} icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />); height += 48; } @@ -148,7 +147,7 @@ class GettingStarted extends ImmutablePureComponent { } return ( - <Column label={intl.formatMessage(messages.menu)}> + <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.menu)}> {multiColumn && <div className='column-header__wrapper'> <h1 className='column-header'> <button> @@ -168,6 +167,8 @@ class GettingStarted extends ImmutablePureComponent { <LinkFooter withHotkeys={multiColumn} /> </div> + + {multiColumn && showTrends && <TrendsContainer />} </Column> ); } diff --git a/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js b/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js index c5098052ce23f1232a35947f5a41c70933e42ef0..5914bbeaf7880d31da7e761f10db6f935eab8a1b 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js +++ b/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js @@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch, { columnId }) => ({ }, onLoad (value) { - return api().get('/api/v2/search', { params: { q: value } }).then(response => { + return api().get('/api/v2/search', { params: { q: value, type: 'hashtags' } }).then(response => { return (response.data.hashtags || []).map((tag) => { return { value: tag.name, label: `#${tag.name}` }; }); diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.js index 0d3c97a6489cc9d4caa56bb09e98ed0a71c3ef36..28200e6c21f0f61a529c03090b2343449fd869a6 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/index.js +++ b/app/javascript/mastodon/features/hashtag_timeline/index.js @@ -135,7 +135,7 @@ class HashtagTimeline extends React.PureComponent { const pinned = !!columnId; return ( - <Column ref={this.setRef} label={`#${id}`}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> <ColumnHeader icon='hashtag' active={hasUnread} @@ -157,6 +157,7 @@ class HashtagTimeline extends React.PureComponent { onLoadMore={this.handleLoadMore} emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js index 097f91c16ca34472cd47c3fe91de9d627b915fdb..1cafb88eda51b76d844d79ca5ad26a36b7a800d8 100644 --- a/app/javascript/mastodon/features/home_timeline/index.js +++ b/app/javascript/mastodon/features/home_timeline/index.js @@ -98,7 +98,7 @@ class HomeTimeline extends React.PureComponent { const pinned = !!columnId; return ( - <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <ColumnHeader icon='home' active={hasUnread} @@ -119,6 +119,7 @@ class HomeTimeline extends React.PureComponent { timelineId='home' emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Visit {public} or use search to get started and meet other users.' values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js index 01b45652c5339aaf62dae187d3d54858fd9f71c1..90dc87cbb0b673eb8a13b332ff726bd9ad06f516 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js @@ -18,10 +18,10 @@ class KeyboardShortcuts extends ImmutablePureComponent { }; render () { - const { intl } = this.props; + const { intl, multiColumn } = this.props; return ( - <Column icon='question' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='question' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <div className='keyboard-shortcuts scrollable optionally-scrollable'> <table> diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js index 0db6d2228299e56eefcdccd73db7a4d7d3bc4948..f3205b2bff196466c48c5982dd6bc30367563cc8 100644 --- a/app/javascript/mastodon/features/list_timeline/index.js +++ b/app/javascript/mastodon/features/list_timeline/index.js @@ -148,14 +148,14 @@ class ListTimeline extends React.PureComponent { } else if (list === false) { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <MissingIndicator /> </Column> ); } return ( - <Column ref={this.setRef} label={title}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> <ColumnHeader icon='list-ul' active={hasUnread} @@ -184,6 +184,7 @@ class ListTimeline extends React.PureComponent { onLoadMore={this.handleLoadMore} emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.js index 015e21b6876ce5115cd47e4eb3c0799c3ae1b136..7f7f5009ca93890e332542be80198b77917e5167 100644 --- a/app/javascript/mastodon/features/lists/index.js +++ b/app/javascript/mastodon/features/lists/index.js @@ -40,6 +40,7 @@ class Lists extends ImmutablePureComponent { dispatch: PropTypes.func.isRequired, lists: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -47,7 +48,7 @@ class Lists extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, lists } = this.props; + const { intl, shouldUpdateScroll, lists, multiColumn } = this.props; if (!lists) { return ( @@ -60,7 +61,7 @@ class Lists extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.lists' defaultMessage="You don't have any lists yet. When you create one, it will show up here." />; return ( - <Column icon='list-ul' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='list-ul' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <NewListForm /> @@ -70,6 +71,7 @@ class Lists extends ImmutablePureComponent { shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />} + bindToDocument={!multiColumn} > {lists.map(list => <ColumnLink key={list.get('id')} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} /> diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.js index 4ed29a1ce6717907902022d515357c7c832cc03f..91dd268c1630497857cca86f6cc3d9ae8aee5614 100644 --- a/app/javascript/mastodon/features/mutes/index.js +++ b/app/javascript/mastodon/features/mutes/index.js @@ -32,6 +32,7 @@ class Mutes extends ImmutablePureComponent { hasMore: PropTypes.bool, accountIds: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -43,7 +44,7 @@ class Mutes extends ImmutablePureComponent { }, 300, { leading: true }); render () { - const { intl, shouldUpdateScroll, hasMore, accountIds } = this.props; + const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn } = this.props; if (!accountIds) { return ( @@ -56,7 +57,7 @@ class Mutes extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; return ( - <Column icon='volume-off' heading={intl.formatMessage(messages.heading)}> + <Column bindToDocument={!multiColumn} icon='volume-off' heading={intl.formatMessage(messages.heading)}> <ColumnBackButtonSlim /> <ScrollableList scrollKey='mutes' @@ -64,6 +65,7 @@ class Mutes extends ImmutablePureComponent { hasMore={hasMore} shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {accountIds.map(id => <AccountContainer key={id} id={id} /> diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.js b/app/javascript/mastodon/features/notifications/components/filter_bar.js index 3f3e6ab7d234dfde1db3d585220fc2617b83aa84..2fd28d832678d3425477b6549904504ae5ae4991 100644 --- a/app/javascript/mastodon/features/notifications/components/filter_bar.js +++ b/app/javascript/mastodon/features/notifications/components/filter_bar.js @@ -64,7 +64,7 @@ class FilterBar extends React.PureComponent { onClick={this.onClick('mention')} title={intl.formatMessage(tooltips.mentions)} > - <Icon id='at' fixedWidth /> + <Icon id='reply-all' fixedWidth /> </button> <button className={selectedFilter === 'favourite' ? 'active' : ''} diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.js index 7aec16d2ea71ed267e21e94c3c2648ab8d65eb27..e6f593ef89f606375cb88e0ca82f26045ffcb232 100644 --- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js +++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js @@ -11,6 +11,7 @@ export default class SettingToggle extends React.PureComponent { settingPath: PropTypes.array.isRequired, label: PropTypes.node.isRequired, onChange: PropTypes.func.isRequired, + defaultValue: PropTypes.bool, } onChange = ({ target }) => { @@ -18,12 +19,12 @@ export default class SettingToggle extends React.PureComponent { } render () { - const { prefix, settings, settingPath, label } = this.props; + const { prefix, settings, settingPath, label, defaultValue } = this.props; const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-'); return ( <div className='setting-toggle'> - <Toggle id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} /> + <Toggle id={id} checked={settings.getIn(settingPath, defaultValue)} onChange={this.onChange} onKeyDown={this.onKeyDown} /> <label htmlFor={id} className='setting-toggle__label'>{label}</label> </div> ); diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 006c4565750282b2fb8db24617edf5fddda9aab2..d16a0f33a4e1fb984ac649738533d14a43af3090 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Column from '../../components/column'; import ColumnHeader from '../../components/column_header'; -import { expandNotifications, scrollTopNotifications } from '../../actions/notifications'; +import { expandNotifications, scrollTopNotifications, loadPending, mountNotifications, unmountNotifications } from '../../actions/notifications'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import NotificationContainer from './containers/notification_container'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; @@ -39,8 +39,9 @@ const mapStateToProps = state => ({ showFilterBar: state.getIn(['settings', 'notifications', 'quickFilter', 'show']), notifications: getNotifications(state), isLoading: state.getIn(['notifications', 'isLoading'], true), - isUnread: state.getIn(['notifications', 'unread']) > 0, + isUnread: state.getIn(['notifications', 'unread']) > 0 || state.getIn(['notifications', 'pendingItems']).size > 0, hasMore: state.getIn(['notifications', 'hasMore']), + numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size, }); export default @connect(mapStateToProps) @@ -58,17 +59,23 @@ class Notifications extends React.PureComponent { isUnread: PropTypes.bool, multiColumn: PropTypes.bool, hasMore: PropTypes.bool, + numPending: PropTypes.number, }; static defaultProps = { trackScroll: true, }; + componentWillMount() { + this.props.dispatch(mountNotifications()); + } + componentWillUnmount () { this.handleLoadOlder.cancel(); this.handleScrollToTop.cancel(); this.handleScroll.cancel(); this.props.dispatch(scrollTopNotifications(false)); + this.props.dispatch(unmountNotifications()); } handleLoadGap = (maxId) => { @@ -80,6 +87,10 @@ class Notifications extends React.PureComponent { this.props.dispatch(expandNotifications({ maxId: last && last.get('id') })); }, 300, { leading: true }); + handleLoadPending = () => { + this.props.dispatch(loadPending()); + }; + handleScrollToTop = debounce(() => { this.props.dispatch(scrollTopNotifications(true)); }, 100); @@ -136,7 +147,7 @@ class Notifications extends React.PureComponent { } render () { - const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, showFilterBar } = this.props; + const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar } = this.props; const pinned = !!columnId; const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />; @@ -178,18 +189,21 @@ class Notifications extends React.PureComponent { isLoading={isLoading} showLoading={isLoading && notifications.size === 0} hasMore={hasMore} + numPending={numPending} emptyMessage={emptyMessage} onLoadMore={this.handleLoadOlder} + onLoadPending={this.handleLoadPending} onScrollToTop={this.handleScrollToTop} onScroll={this.handleScroll} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} > {scrollableContent} </ScrollableList> ); return ( - <Column ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> + <Column bindToDocument={!multiColumn} ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> <ColumnHeader icon='bell' active={isUnread} diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js index 98cdbda3c492c0c6d3005ff532270fa38d98d5fb..ad5c9cafc367fceec9ff966a8bdad7921c45f85a 100644 --- a/app/javascript/mastodon/features/pinned_statuses/index.js +++ b/app/javascript/mastodon/features/pinned_statuses/index.js @@ -28,6 +28,7 @@ class PinnedStatuses extends ImmutablePureComponent { statusIds: ImmutablePropTypes.list.isRequired, intl: PropTypes.object.isRequired, hasMore: PropTypes.bool.isRequired, + multiColumn: PropTypes.bool, }; componentWillMount () { @@ -43,16 +44,17 @@ class PinnedStatuses extends ImmutablePureComponent { } render () { - const { intl, shouldUpdateScroll, statusIds, hasMore } = this.props; + const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props; return ( - <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> + <Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> <ColumnBackButtonSlim /> <StatusList statusIds={statusIds} scrollKey='pinned_statuses' hasMore={hasMore} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js index 2b7d9c56f99519b08a92e751d05a1eccdc33c330..e7825e2366493bea810c6fb375bfca110b8135d9 100644 --- a/app/javascript/mastodon/features/public_timeline/index.js +++ b/app/javascript/mastodon/features/public_timeline/index.js @@ -105,7 +105,7 @@ class PublicTimeline extends React.PureComponent { const pinned = !!columnId; return ( - <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> + <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <ColumnHeader icon='globe' active={hasUnread} @@ -126,6 +126,7 @@ class PublicTimeline extends React.PureComponent { scrollKey={`public_timeline-${columnId}`} 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' />} shouldUpdateScroll={shouldUpdateScroll} + bindToDocument={!multiColumn} /> </Column> ); diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.js index c05d21c740451c9dd6753deea26ce0c373446150..9179e51dbb2437c7aacaa2b0a04c3f30ae504f9f 100644 --- a/app/javascript/mastodon/features/reblogs/index.js +++ b/app/javascript/mastodon/features/reblogs/index.js @@ -5,17 +5,23 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; import { fetchReblogs } from '../../actions/interactions'; -import { FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import AccountContainer from '../../containers/account_container'; import Column from '../ui/components/column'; -import ColumnBackButton from '../../components/column_back_button'; import ScrollableList from '../../components/scrollable_list'; +import Icon from 'mastodon/components/icon'; +import ColumnHeader from '../../components/column_header'; + +const messages = defineMessages({ + refresh: { id: 'refresh', defaultMessage: 'Refresh' }, +}); const mapStateToProps = (state, props) => ({ accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]), }); export default @connect(mapStateToProps) +@injectIntl class Reblogs extends ImmutablePureComponent { static propTypes = { @@ -23,10 +29,14 @@ class Reblogs extends ImmutablePureComponent { dispatch: PropTypes.func.isRequired, shouldUpdateScroll: PropTypes.func, accountIds: ImmutablePropTypes.list, + multiColumn: PropTypes.bool, + intl: PropTypes.object.isRequired, }; componentWillMount () { - this.props.dispatch(fetchReblogs(this.props.params.statusId)); + if (!this.props.accountIds) { + this.props.dispatch(fetchReblogs(this.props.params.statusId)); + } } componentWillReceiveProps(nextProps) { @@ -35,8 +45,12 @@ class Reblogs extends ImmutablePureComponent { } } + handleRefresh = () => { + this.props.dispatch(fetchReblogs(this.props.params.statusId)); + } + render () { - const { shouldUpdateScroll, accountIds } = this.props; + const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props; if (!accountIds) { return ( @@ -49,13 +63,20 @@ class Reblogs extends ImmutablePureComponent { const emptyMessage = <FormattedMessage id='status.reblogs.empty' defaultMessage='No one has boosted this toot yet. When someone does, they will show up here.' />; return ( - <Column> - <ColumnBackButton /> + <Column bindToDocument={!multiColumn}> + <ColumnHeader + showBackButton + multiColumn={multiColumn} + extraButton={( + <button className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button> + )} + /> <ScrollableList scrollKey='reblogs' shouldUpdateScroll={shouldUpdateScroll} emptyMessage={emptyMessage} + bindToDocument={!multiColumn} > {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} /> diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js index 59be364888c31d337f82080177d41ee4bf0759c3..a0838740a7dbe8feda2e5ea7100179707ad6d340 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.js +++ b/app/javascript/mastodon/features/status/components/detailed_status.js @@ -10,6 +10,7 @@ import { defineMessages, injectIntl, FormattedDate, FormattedNumber } from 'reac import Card from './card'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Video from '../../video'; +import Audio from '../../audio'; import scheduleIdleTask from '../../ui/util/schedule_idle_task'; import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; @@ -114,7 +115,19 @@ export default class DetailedStatus extends ImmutablePureComponent { } if (status.get('media_attachments').size > 0) { - if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) { + if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { + const attachment = status.getIn(['media_attachments', 0]); + + media = ( + <Audio + src={attachment.get('url')} + alt={attachment.get('description')} + duration={attachment.getIn(['meta', 'original', 'duration'], 0)} + height={110} + preload + /> + ); + } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { const attachment = status.getIn(['media_attachments', 0]); media = ( diff --git a/app/javascript/mastodon/features/status/containers/detailed_status_container.js b/app/javascript/mastodon/features/status/containers/detailed_status_container.js index 61e0c428a108c49443428f14d49b063dd655e08e..333c295dc6c11915dd220e69064f536a99a57388 100644 --- a/app/javascript/mastodon/features/status/containers/detailed_status_container.js +++ b/app/javascript/mastodon/features/status/containers/detailed_status_container.js @@ -1,4 +1,3 @@ -import React from 'react'; import { connect } from 'react-redux'; import DetailedStatus from '../components/detailed_status'; import { makeGetStatus } from '../../../selectors'; @@ -15,7 +14,6 @@ import { pin, unpin, } from '../../../actions/interactions'; -import { blockAccount } from '../../../actions/accounts'; import { muteStatus, unmuteStatus, @@ -24,9 +22,10 @@ import { revealStatus, } from '../../../actions/statuses'; import { initMuteModal } from '../../../actions/mutes'; +import { initBlockModal } from '../../../actions/blocks'; import { initReport } from '../../../actions/reports'; import { openModal } from '../../../actions/modal'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import { boostModal, deleteModal } from '../../../initial_state'; import { showAlertForError } from '../../../actions/alerts'; @@ -35,10 +34,8 @@ const messages = defineMessages({ deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, - blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, - blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, }); const makeMapStateToProps = () => { @@ -138,16 +135,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onBlock (status) { const account = status.get('account'); - dispatch(openModal('CONFIRM', { - message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, - confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.get('id'))), - secondary: intl.formatMessage(messages.blockAndReport), - onSecondary: () => { - dispatch(blockAccount(account.get('id'))); - dispatch(initReport(account, status)); - }, - })); + dispatch(initBlockModal(account)); }, onReport (status) { diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 0422111ae38e1a1e76a361e26f216dae888fa3b1..029057d4076e1279d17aa91cc09b7e2c9f1c75b4 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -23,7 +23,6 @@ import { mentionCompose, directCompose, } from '../../actions/compose'; -import { blockAccount } from '../../actions/accounts'; import { muteStatus, unmuteStatus, @@ -32,6 +31,7 @@ import { revealStatus, } from '../../actions/statuses'; import { initMuteModal } from '../../actions/mutes'; +import { initBlockModal } from '../../actions/blocks'; import { initReport } from '../../actions/reports'; import { makeGetStatus } from '../../selectors'; import { ScrollContainer } from 'react-router-scroll-4'; @@ -39,7 +39,7 @@ import ColumnBackButton from '../../components/column_back_button'; import ColumnHeader from '../../components/column_header'; import StatusContainer from '../../containers/status_container'; import { openModal } from '../../actions/modal'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { HotKeys } from 'react-hotkeys'; import { boostModal, deleteModal } from '../../initial_state'; @@ -52,13 +52,11 @@ const messages = defineMessages({ deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, - blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' }, hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' }, detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, - blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' }, }); const makeMapStateToProps = () => { @@ -84,28 +82,38 @@ const makeMapStateToProps = () => { const getDescendantsIds = createSelector([ (_, { id }) => id, state => state.getIn(['contexts', 'replies']), - ], (statusId, contextReplies) => { - let descendantsIds = Immutable.List(); - descendantsIds = descendantsIds.withMutations(mutable => { - const ids = [statusId]; + state => state.get('statuses'), + ], (statusId, contextReplies, statuses) => { + let descendantsIds = []; + const ids = [statusId]; - while (ids.length > 0) { - let id = ids.shift(); - const replies = contextReplies.get(id); + while (ids.length > 0) { + let id = ids.shift(); + const replies = contextReplies.get(id); - if (statusId !== id) { - mutable.push(id); - } + if (statusId !== id) { + descendantsIds.push(id); + } - if (replies) { - replies.reverse().forEach(reply => { - ids.unshift(reply); - }); - } + if (replies) { + replies.reverse().forEach(reply => { + ids.unshift(reply); + }); } - }); + } - return descendantsIds; + let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account')); + if (insertAt !== -1) { + descendantsIds.forEach((id, idx) => { + if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === statuses.get(id).get('account')) { + descendantsIds.splice(idx, 1); + descendantsIds.splice(insertAt, 0, id); + insertAt += 1; + } + }); + } + + return Immutable.List(descendantsIds); }); const mapStateToProps = (state, props) => { @@ -146,6 +154,7 @@ class Status extends ImmutablePureComponent { descendantsIds: ImmutablePropTypes.list, intl: PropTypes.object.isRequired, askReplyConfirmation: PropTypes.bool, + multiColumn: PropTypes.bool, domain: PropTypes.string.isRequired, }; @@ -285,19 +294,9 @@ class Status extends ImmutablePureComponent { } handleBlockClick = (status) => { - const { dispatch, intl } = this.props; + const { dispatch } = this.props; const account = status.get('account'); - - dispatch(openModal('CONFIRM', { - message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />, - confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.get('id'))), - secondary: intl.formatMessage(messages.blockAndReport), - onSecondary: () => { - dispatch(blockAccount(account.get('id'))); - dispatch(initReport(account, status)); - }, - })); + dispatch(initBlockModal(account)); } handleReport = (status) => { @@ -437,13 +436,13 @@ class Status extends ImmutablePureComponent { render () { let ancestors, descendants; - const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain } = this.props; + const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn } = this.props; const { fullscreen } = this.state; if (status === null) { return ( <Column> - <ColumnBackButton /> + <ColumnBackButton multiColumn={multiColumn} /> <MissingIndicator /> </Column> ); @@ -470,9 +469,10 @@ class Status extends ImmutablePureComponent { }; return ( - <Column label={intl.formatMessage(messages.detailedStatus)}> + <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.detailedStatus)}> <ColumnHeader showBackButton + multiColumn={multiColumn} extraButton={( <button className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll} aria-pressed={status.get('hidden') ? 'false' : 'true'}><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button> )} diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.js b/app/javascript/mastodon/features/ui/components/audio_modal.js new file mode 100644 index 0000000000000000000000000000000000000000..2300453d7c51181428bab2f50212f74488192b11 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/audio_modal.js @@ -0,0 +1,76 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import Audio from 'mastodon/features/audio'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { FormattedMessage } from 'react-intl'; +import { previewState } from './video_modal'; +import classNames from 'classnames'; +import Icon from 'mastodon/components/icon'; + +export default class AudioModal extends ImmutablePureComponent { + + static propTypes = { + media: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.map, + onClose: PropTypes.func.isRequired, + }; + + static contextTypes = { + router: PropTypes.object, + }; + + componentDidMount () { + if (this.context.router) { + const history = this.context.router.history; + + history.push(history.location.pathname, previewState); + + this.unlistenHistory = history.listen(() => { + this.props.onClose(); + }); + } + } + + componentWillUnmount () { + if (this.context.router) { + this.unlistenHistory(); + + if (this.context.router.history.location.state === previewState) { + this.context.router.history.goBack(); + } + } + } + + handleStatusClick = e => { + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.context.router.history.push(`/statuses/${this.props.status.get('id')}`); + } + } + + render () { + const { media, status } = this.props; + + return ( + <div className='modal-root__modal audio-modal'> + <div className='audio-modal__container'> + <Audio + src={media.get('url')} + alt={media.get('description')} + duration={media.getIn(['meta', 'original', 'duration'], 0)} + height={135} + preload + /> + </div> + + {status && ( + <div className={classNames('media-modal__meta')}> + <a href={status.get('url')} onClick={this.handleStatusClick}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> + </div> + )} + </div> + ); + } + +} diff --git a/app/javascript/mastodon/features/ui/components/block_modal.js b/app/javascript/mastodon/features/ui/components/block_modal.js new file mode 100644 index 0000000000000000000000000000000000000000..a07baeaa64a450768dc2ede4e01744550a3d3a2e --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/block_modal.js @@ -0,0 +1,103 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { injectIntl, FormattedMessage } from 'react-intl'; +import { makeGetAccount } from '../../../selectors'; +import Button from '../../../components/button'; +import { closeModal } from '../../../actions/modal'; +import { blockAccount } from '../../../actions/accounts'; +import { initReport } from '../../../actions/reports'; + + +const makeMapStateToProps = () => { + const getAccount = makeGetAccount(); + + const mapStateToProps = state => ({ + account: getAccount(state, state.getIn(['blocks', 'new', 'account_id'])), + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = dispatch => { + return { + onConfirm(account) { + dispatch(blockAccount(account.get('id'))); + }, + + onBlockAndReport(account) { + dispatch(blockAccount(account.get('id'))); + dispatch(initReport(account)); + }, + + onClose() { + dispatch(closeModal()); + }, + }; +}; + +export default @connect(makeMapStateToProps, mapDispatchToProps) +@injectIntl +class BlockModal extends React.PureComponent { + + static propTypes = { + account: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired, + onBlockAndReport: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + componentDidMount() { + this.button.focus(); + } + + handleClick = () => { + this.props.onClose(); + this.props.onConfirm(this.props.account); + } + + handleSecondary = () => { + this.props.onClose(); + this.props.onBlockAndReport(this.props.account); + } + + handleCancel = () => { + this.props.onClose(); + } + + setRef = (c) => { + this.button = c; + } + + render () { + const { account } = this.props; + + return ( + <div className='modal-root__modal block-modal'> + <div className='block-modal__container'> + <p> + <FormattedMessage + id='confirmations.block.message' + defaultMessage='Are you sure you want to block {name}?' + values={{ name: <strong>@{account.get('acct')}</strong> }} + /> + </p> + </div> + + <div className='block-modal__action-bar'> + <Button onClick={this.handleCancel} className='block-modal__cancel-button'> + <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' /> + </Button> + <Button onClick={this.handleSecondary} className='confirmation-modal__secondary-button'> + <FormattedMessage id='confirmations.block.block_and_report' defaultMessage='Block & Report' /> + </Button> + <Button onClick={this.handleClick} ref={this.setRef}> + <FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' /> + </Button> + </div> + </div> + ); + } + +} diff --git a/app/javascript/mastodon/features/ui/components/column_loading.js b/app/javascript/mastodon/features/ui/components/column_loading.js index 9503a7a1ac60da8b1c7425258020f9c07f241ce6..0cdfd05d80e2a4248b555093c1593f6fe551d648 100644 --- a/app/javascript/mastodon/features/ui/components/column_loading.js +++ b/app/javascript/mastodon/features/ui/components/column_loading.js @@ -21,7 +21,7 @@ export default class ColumnLoading extends ImmutablePureComponent { let { title, icon } = this.props; return ( <Column> - <ColumnHeader icon={icon} title={title} multiColumn={false} focusable={false} /> + <ColumnHeader icon={icon} title={title} multiColumn={false} focusable={false} placeholder /> <div className='scrollable' /> </Column> ); diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 042e44e43e9f46cb42518941a7544a25b41688bb..8a4e89b3de40c6c734515dffd0173f7eebddbc0c 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -12,7 +12,18 @@ import BundleContainer from '../containers/bundle_container'; import ColumnLoading from './column_loading'; import DrawerLoading from './drawer_loading'; import BundleColumnError from './bundle_column_error'; -import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components'; +import { + Compose, + Notifications, + HomeTimeline, + CommunityTimeline, + PublicTimeline, + HashtagTimeline, + DirectTimeline, + FavouritedStatuses, + ListTimeline, + Directory, +} from '../../ui/util/async-components'; import Icon from 'mastodon/components/icon'; import ComposePanel from './compose_panel'; import NavigationPanel from './navigation_panel'; @@ -30,6 +41,7 @@ const componentMap = { 'DIRECT': DirectTimeline, 'FAVOURITES': FavouritedStatuses, 'LIST': ListTimeline, + 'DIRECTORY': Directory, }; const messages = defineMessages({ diff --git a/app/javascript/mastodon/features/ui/components/document_title.js b/app/javascript/mastodon/features/ui/components/document_title.js new file mode 100644 index 0000000000000000000000000000000000000000..cd081b20c7e2c7c2ad30659ae3ce2d38cfe0b355 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/document_title.js @@ -0,0 +1,41 @@ +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; + } + +} diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.js index 982781db073d69319032ed809f6b01089e784c52..4679c9650fdc439bc3a2f49eea90e7ed8ba5f7b1 100644 --- a/app/javascript/mastodon/features/ui/components/embed_modal.js +++ b/app/javascript/mastodon/features/ui/components/embed_modal.js @@ -1,8 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage, injectIntl } from 'react-intl'; -import api from '../../../api'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; +import api from 'mastodon/api'; +import IconButton from 'mastodon/components/icon_button'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); export default @injectIntl class EmbedModal extends ImmutablePureComponent { @@ -50,13 +55,17 @@ class EmbedModal extends ImmutablePureComponent { } render () { + const { intl, onClose } = this.props; const { oembed } = this.state; return ( - <div className='modal-root__modal embed-modal'> - <h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4> + <div className='modal-root__modal report-modal embed-modal'> + <div className='report-modal__target'> + <IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} /> + <FormattedMessage id='status.embed' defaultMessage='Embed' /> + </div> - <div className='embed-modal__container'> + <div className='report-modal__container embed-modal__container' style={{ display: 'block' }}> <p className='hint'> <FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' /> </p> diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js index 7488a3598bc143608c9985b7b4329fc600b07187..3694ab9046a9123d2e9f0aee0e6d8153a4d7e33a 100644 --- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js +++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js @@ -1,11 +1,28 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { connect } from 'react-redux'; -import ImageLoader from './image_loader'; import classNames from 'classnames'; import { changeUploadCompose } from '../../../actions/compose'; import { getPointerPosition } from '../../video'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; +import IconButton from 'mastodon/components/icon_button'; +import Button from 'mastodon/components/button'; +import Video from 'mastodon/features/video'; +import Audio from 'mastodon/features/audio'; +import Textarea from 'react-textarea-autosize'; +import UploadProgress from 'mastodon/features/compose/components/upload_progress'; +import CharacterCounter from 'mastodon/features/compose/components/character_counter'; +import { length } from 'stringz'; +import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components'; +import GIFV from 'mastodon/components/gifv'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, + apply: { id: 'upload_modal.apply', defaultMessage: 'Apply' }, + placeholder: { id: 'upload_modal.description_placeholder', defaultMessage: 'A quick brown fox jumps over the lazy dog' }, +}); const mapStateToProps = (state, { id }) => ({ media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id), @@ -13,17 +30,56 @@ const mapStateToProps = (state, { id }) => ({ const mapDispatchToProps = (dispatch, { id }) => ({ - onSave: (x, y) => { - dispatch(changeUploadCompose(id, { focus: `${x.toFixed(2)},${y.toFixed(2)}` })); + onSave: (description, x, y) => { + dispatch(changeUploadCompose(id, { description, focus: `${x.toFixed(2)},${y.toFixed(2)}` })); }, }); +const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******') + .replace(/\n/g, ' ') + .replace(/\*\*\*\*\*\*/g, '\n\n'); + +const assetHost = process.env.CDN_HOST || ''; + +class ImageLoader extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + width: PropTypes.number, + height: PropTypes.number, + }; + + state = { + loading: true, + }; + + componentDidMount() { + const image = new Image(); + image.addEventListener('load', () => this.setState({ loading: false })); + image.src = this.props.src; + } + + render () { + const { loading } = this.state; + + if (loading) { + return <canvas width={this.props.width} height={this.props.height} />; + } else { + return <img {...this.props} alt='' />; + } + } + +} + export default @connect(mapStateToProps, mapDispatchToProps) +@injectIntl class FocalPointModal extends ImmutablePureComponent { static propTypes = { media: ImmutablePropTypes.map.isRequired, + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, }; state = { @@ -32,6 +88,10 @@ class FocalPointModal extends ImmutablePureComponent { focusX: 0, focusY: 0, dragging: false, + description: '', + dirty: false, + progress: 0, + loading: true, }; componentWillMount () { @@ -57,6 +117,14 @@ class FocalPointModal extends ImmutablePureComponent { this.setState({ dragging: true }); } + handleTouchStart = e => { + document.addEventListener('touchmove', this.handleMouseMove); + document.addEventListener('touchend', this.handleTouchEnd); + + this.updatePosition(e); + this.setState({ dragging: true }); + } + handleMouseMove = e => { this.updatePosition(e); } @@ -66,7 +134,13 @@ class FocalPointModal extends ImmutablePureComponent { document.removeEventListener('mouseup', this.handleMouseUp); this.setState({ dragging: false }); - this.props.onSave(this.state.focusX, this.state.focusY); + } + + handleTouchEnd = () => { + document.removeEventListener('touchmove', this.handleMouseMove); + document.removeEventListener('touchend', this.handleTouchEnd); + + this.setState({ dragging: false }); } updatePosition = e => { @@ -74,46 +148,166 @@ class FocalPointModal extends ImmutablePureComponent { const focusX = (x - .5) * 2; const focusY = (y - .5) * -2; - this.setState({ x, y, focusX, focusY }); + this.setState({ x, y, focusX, focusY, dirty: true }); } updatePositionFromMedia = media => { - const focusX = media.getIn(['meta', 'focus', 'x']); - const focusY = media.getIn(['meta', 'focus', 'y']); + const focusX = media.getIn(['meta', 'focus', 'x']); + const focusY = media.getIn(['meta', 'focus', 'y']); + const description = media.get('description') || ''; if (focusX && focusY) { const x = (focusX / 2) + .5; const y = (focusY / -2) + .5; - this.setState({ x, y, focusX, focusY }); + this.setState({ + x, + y, + focusX, + focusY, + description, + dirty: false, + }); } else { - this.setState({ x: 0.5, y: 0.5, focusX: 0, focusY: 0 }); + this.setState({ + x: 0.5, + y: 0.5, + focusX: 0, + focusY: 0, + description, + dirty: false, + }); } } + handleChange = e => { + this.setState({ description: e.target.value, dirty: true }); + } + + handleSubmit = () => { + this.props.onSave(this.state.description, this.state.focusX, this.state.focusY); + this.props.onClose(); + } + setRef = c => { this.node = c; } - render () { + handleTextDetection = () => { const { media } = this.props; - const { x, y, dragging } = this.state; + + this.setState({ detecting: true }); + + fetchTesseract().then(({ TesseractWorker }) => { + const worker = new TesseractWorker({ + workerPath: `${assetHost}/packs/ocr/worker.min.js`, + corePath: `${assetHost}/packs/ocr/tesseract-core.wasm.js`, + langPath: `${assetHost}/ocr/lang-data`, + }); + + let media_url = media.get('file'); + + if (window.URL && URL.createObjectURL) { + try { + media_url = URL.createObjectURL(media.get('file')); + } catch (error) { + console.error(error); + } + } + + worker.recognize(media_url) + .progress(({ progress }) => this.setState({ progress })) + .finally(() => worker.terminate()) + .then(({ text }) => this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false })) + .catch(() => this.setState({ detecting: false })); + }).catch(() => this.setState({ detecting: false })); + } + + render () { + const { media, intl, onClose } = this.props; + const { x, y, dragging, description, dirty, detecting, progress } = this.state; const width = media.getIn(['meta', 'original', 'width']) || null; const height = media.getIn(['meta', 'original', 'height']) || null; + const focals = ['image', 'gifv'].includes(media.get('type')); + + const previewRatio = 16/9; + const previewWidth = 200; + const previewHeight = previewWidth / previewRatio; return ( - <div className='modal-root__modal video-modal focal-point-modal'> - <div className={classNames('focal-point', { dragging })} ref={this.setRef}> - <ImageLoader - previewSrc={media.get('preview_url')} - src={media.get('url')} - width={width} - height={height} - /> - - <div className='focal-point__reticle' style={{ top: `${y * 100}%`, left: `${x * 100}%` }} /> - <div className='focal-point__overlay' onMouseDown={this.handleMouseDown} /> + <div className='modal-root__modal report-modal' style={{ maxWidth: 960 }}> + <div className='report-modal__target'> + <IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} /> + <FormattedMessage id='upload_modal.edit_media' defaultMessage='Edit media' /> + </div> + + <div className='report-modal__container'> + <div className='report-modal__comment'> + {focals && <p><FormattedMessage id='upload_modal.hint' defaultMessage='Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.' /></p>} + + <label className='setting-text-label' htmlFor='upload-modal__description'><FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' /></label> + + <div className='setting-text__wrapper'> + <Textarea + id='upload-modal__description' + className='setting-text light' + value={detecting ? '…' : description} + onChange={this.handleChange} + disabled={detecting} + autoFocus + /> + + <div className='setting-text__modifiers'> + <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={<FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />} /> + </div> + </div> + + <div className='setting-text__toolbar'> + <button disabled={detecting || media.get('type') !== 'image'} className='link-button' onClick={this.handleTextDetection}><FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' /></button> + <CharacterCounter max={1500} text={detecting ? '' : description} /> + </div> + + <Button disabled={!dirty || detecting || length(description) > 1500} text={intl.formatMessage(messages.apply)} onClick={this.handleSubmit} /> + </div> + + <div className='focal-point-modal__content'> + {focals && ( + <div className={classNames('focal-point', { dragging })} ref={this.setRef} onMouseDown={this.handleMouseDown} onTouchStart={this.handleTouchStart}> + {media.get('type') === 'image' && <ImageLoader src={media.get('url')} width={width} height={height} alt='' />} + {media.get('type') === 'gifv' && <GIFV src={media.get('url')} width={width} height={height} />} + + <div className='focal-point__preview'> + <strong><FormattedMessage id='upload_modal.preview_label' defaultMessage='Preview ({ratio})' values={{ ratio: '16:9' }} /></strong> + <div style={{ width: previewWidth, height: previewHeight, backgroundImage: `url(${media.get('preview_url')})`, backgroundSize: 'cover', backgroundPosition: `${x * 100}% ${y * 100}%` }} /> + </div> + + <div className='focal-point__reticle' style={{ top: `${y * 100}%`, left: `${x * 100}%` }} /> + <div className='focal-point__overlay' /> + </div> + )} + + {media.get('type') === 'video' && ( + <Video + preview={media.get('preview_url')} + blurhash={media.get('blurhash')} + src={media.get('url')} + detailed + inline + editable + /> + )} + + {media.get('type') === 'audio' && ( + <Audio + src={media.get('url')} + duration={media.getIn(['meta', 'original', 'duration'], 0)} + height={150} + preload + editable + /> + )} + </div> </div> </div> ); diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js index b481983dc88649fbb01580d9546ba979e4d41c17..2b9bd3875e259d9f0713a1cdc0a087e03786eb84 100644 --- a/app/javascript/mastodon/features/ui/components/link_footer.js +++ b/app/javascript/mastodon/features/ui/components/link_footer.js @@ -1,35 +1,72 @@ +import { connect } from 'react-redux'; import React from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { Link } from 'react-router-dom'; import { invitesEnabled, version, repository, source_url } from 'mastodon/initial_state'; +import { logOut } from 'mastodon/utils/log_out'; +import { openModal } from 'mastodon/actions/modal'; -const LinkFooter = ({ withHotkeys }) => ( - <div className='getting-started__footer'> - <ul> - {invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </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> - <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='/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' data-method='delete'><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li> - </ul> - - <p> - <FormattedMessage - id='getting_started.open_source_notice' - defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.' - values={{ github: <span><a href={source_url} rel='noopener' target='_blank'>{repository}</a> (v{version})</span> }} - /> - </p> - </div> -); - -LinkFooter.propTypes = { - withHotkeys: PropTypes.bool, -}; +const messages = defineMessages({ + logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' }, + logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' }, +}); + +const mapDispatchToProps = (dispatch, { intl }) => ({ + onLogout () { + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.logoutMessage), + confirm: intl.formatMessage(messages.logoutConfirm), + onConfirm: () => logOut(), + })); + }, +}); + +export default @injectIntl +@connect(null, mapDispatchToProps) +class LinkFooter extends React.PureComponent { + + static propTypes = { + withHotkeys: PropTypes.bool, + onLogout: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + handleLogoutClick = e => { + e.preventDefault(); + e.stopPropagation(); + + this.props.onLogout(); -export default LinkFooter; + return false; + } + + render () { + const { withHotkeys } = this.props; + + return ( + <div className='getting-started__footer'> + <ul> + {invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </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> + <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='/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> + + <p> + <FormattedMessage + id='getting_started.open_source_notice' + defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.' + values={{ github: <span><a href={source_url} rel='noopener' target='_blank'>{repository}</a> (v{version})</span> }} + /> + </p> + </div> + ); + } + +}; diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index da2ac5f26d5a4670bb0bd08f27e399af87b52463..a785551c0fd7ea3d18b731150111c4a8677d8743 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -3,13 +3,13 @@ import ReactSwipeableViews from 'react-swipeable-views'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import Video from 'mastodon/features/video'; -import ExtendedVideoPlayer from 'mastodon/components/extended_video_player'; import classNames from 'classnames'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import IconButton from 'mastodon/components/icon_button'; import ImmutablePureComponent from 'react-immutable-pure-component'; import ImageLoader from './image_loader'; import Icon from 'mastodon/components/icon'; +import GIFV from 'mastodon/components/gifv'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, @@ -169,10 +169,8 @@ class MediaModal extends ImmutablePureComponent { ); } else if (image.get('type') === 'gifv') { return ( - <ExtendedVideoPlayer + <GIFV src={image.get('url')} - muted - controls={false} width={width} height={height} key={image.get('preview_url')} @@ -228,7 +226,7 @@ class MediaModal extends ImmutablePureComponent { {status && ( <div className={classNames('media-modal__meta', { 'media-modal__meta--shifted': media.size > 1 })}> - <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> + <a href={status.get('url')} onClick={this.handleStatusClick}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> </div> )} diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index cc2ab6c8ce9c34c89cc6744b0d94619e0de8afff..5cf70a0cc2d7b880e660e1f1c2ea9125960f237b 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Base from '../../../components/modal_root'; +import { getScrollbarWidth } from 'mastodon/utils/scrollbar'; +import Base from 'mastodon/components/modal_root'; import BundleContainer from '../containers/bundle_container'; import BundleModalError from './bundle_modal_error'; import ModalLoading from './modal_loading'; @@ -8,10 +9,12 @@ import ActionsModal from './actions_modal'; import MediaModal from './media_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; +import AudioModal from './audio_modal'; import ConfirmationModal from './confirmation_modal'; import FocalPointModal from './focal_point_modal'; import { MuteModal, + BlockModal, ReportModal, EmbedModal, ListEditor, @@ -21,9 +24,11 @@ import { const MODAL_COMPONENTS = { 'MEDIA': () => Promise.resolve({ default: MediaModal }), 'VIDEO': () => Promise.resolve({ default: VideoModal }), + 'AUDIO': () => Promise.resolve({ default: AudioModal }), 'BOOST': () => Promise.resolve({ default: BoostModal }), 'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }), 'MUTE': MuteModal, + 'BLOCK': BlockModal, 'REPORT': ReportModal, 'ACTIONS': () => Promise.resolve({ default: ActionsModal }), 'EMBED': EmbedModal, @@ -47,8 +52,10 @@ export default class ModalRoot extends React.PureComponent { componentDidUpdate (prevProps, prevState, { visible }) { if (visible) { document.body.classList.add('with-modals--active'); + document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; } else { document.body.classList.remove('with-modals--active'); + document.documentElement.style.marginRight = 0; } } diff --git a/app/javascript/mastodon/features/ui/components/mute_modal.js b/app/javascript/mastodon/features/ui/components/mute_modal.js index ac356b42a7b26f19c0e90428debbd38a0567c7b5..852830c3c2c93fd7c87de88e029db557bda9b3e1 100644 --- a/app/javascript/mastodon/features/ui/components/mute_modal.js +++ b/app/javascript/mastodon/features/ui/components/mute_modal.js @@ -11,7 +11,6 @@ import { toggleHideNotifications } from '../../../actions/mutes'; const mapStateToProps = state => { return { - isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']), account: state.getIn(['mutes', 'new', 'account']), notifications: state.getIn(['mutes', 'new', 'notifications']), }; @@ -38,7 +37,6 @@ export default @connect(mapStateToProps, mapDispatchToProps) class MuteModal extends React.PureComponent { static propTypes = { - isSubmitting: PropTypes.bool.isRequired, account: PropTypes.object.isRequired, notifications: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, @@ -81,11 +79,16 @@ class MuteModal extends React.PureComponent { values={{ name: <strong>@{account.get('acct')}</strong> }} /> </p> - <div> - <label htmlFor='mute-modal__hide-notifications-checkbox'> + <p className='mute-modal__explanation'> + <FormattedMessage + id='confirmations.mute.explanation' + defaultMessage='This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.' + /> + </p> + <div className='setting-toggle'> + <Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} /> + <label className='setting-toggle__label' htmlFor='mute-modal__hide-notifications-checkbox'> <FormattedMessage id='mute_modal.hide_notifications' defaultMessage='Hide notifications from this user?' /> - {' '} - <Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} /> </label> </div> </div> diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js index ef3ad2e092f86e07936985041471a08906620e03..51e3ec037d1f161bd5950e12475be561276de99b 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.js +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js @@ -2,10 +2,11 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import Icon from 'mastodon/components/icon'; -import { profile_directory } from 'mastodon/initial_state'; +import { profile_directory, showTrends } from 'mastodon/initial_state'; import NotificationsCounterIcon from './notifications_counter_icon'; import FollowRequestsNavLink from './follow_requests_nav_link'; import ListPanel from './list_panel'; +import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container'; const NavigationPanel = () => ( <div className='navigation-panel'> @@ -17,6 +18,7 @@ const NavigationPanel = () => ( <NavLink className='column-link column-link--transparent' to='/timelines/direct'><Icon className='column-link__icon' id='envelope' 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='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink> + {profile_directory && <NavLink className='column-link column-link--transparent' to='/directory'><Icon className='column-link__icon' id='address-book-o' fixedWidth /><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></NavLink>} <ListPanel /> @@ -24,7 +26,9 @@ const NavigationPanel = () => ( <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> - {!!profile_directory && <a className='column-link column-link--transparent' href='/explore'><Icon className='column-link__icon' id='address-book-o' fixedWidth /><FormattedMessage id='navigation_bar.profile_directory' defaultMessage='Profile directory' /></a>} + + {showTrends && <div className='flex-spacer' />} + {showTrends && <TrendsContainer />} </div> ); diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js index 29583d3d798aaead2bde744233257f58f4c6aeed..1911da8ba3f58e5188d4f233aad1ff71d0dfae0d 100644 --- a/app/javascript/mastodon/features/ui/components/tabs_bar.js +++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js @@ -73,9 +73,13 @@ class TabsBar extends React.PureComponent { const { intl: { formatMessage } } = this.props; return ( - <nav className='tabs-bar' ref={this.setRef}> - {links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))} - </nav> + <div className='tabs-bar__wrapper'> + <nav className='tabs-bar' ref={this.setRef}> + {links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))} + </nav> + + <div id='tabs-bar__portal' /> + </div> ); } diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js index 213d31316c81af9df6f6a33d799ef851036985b2..f37fc796f83bceb000a9fa6d1d68043213952391 100644 --- a/app/javascript/mastodon/features/ui/components/video_modal.js +++ b/app/javascript/mastodon/features/ui/components/video_modal.js @@ -4,6 +4,8 @@ import PropTypes from 'prop-types'; import Video from 'mastodon/features/video'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import Icon from 'mastodon/components/icon'; export const previewState = 'previewVideoModal'; @@ -52,22 +54,25 @@ export default class VideoModal extends ImmutablePureComponent { render () { const { media, status, time, onClose } = this.props; - const link = status && <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>; - return ( <div className='modal-root__modal video-modal'> - <div> + <div className='video-modal__container'> <Video preview={media.get('preview_url')} blurhash={media.get('blurhash')} src={media.get('url')} startTime={time} onCloseVideo={onClose} - link={link} detailed alt={media.get('description')} /> </div> + + {status && ( + <div className={classNames('media-modal__meta')}> + <a href={status.get('url')} onClick={this.handleStatusClick}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> + </div> + )} </div> ); } diff --git a/app/javascript/mastodon/features/ui/containers/notifications_container.js b/app/javascript/mastodon/features/ui/containers/notifications_container.js index b60a0216f628e22ec047b572e104bba2a275c697..3819da3d850c1fa3a93a176f4132fa0ab359db03 100644 --- a/app/javascript/mastodon/features/ui/containers/notifications_container.js +++ b/app/javascript/mastodon/features/ui/containers/notifications_container.js @@ -11,7 +11,7 @@ const mapStateToProps = (state, { intl }) => { const value = notification[key]; if (typeof value === 'object') { - notification[key] = intl.formatMessage(value); + notification[key] = intl.formatMessage(value, notification[`${key}_values`]); } })); diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js index 3df5b7beacc07b7547e8b0b15c314d886ca60df9..7b8eb652b4af87511bab867f7c2396ddcb7f9b28 100644 --- a/app/javascript/mastodon/features/ui/containers/status_list_container.js +++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import StatusList from '../../../components/status_list'; -import { scrollTopTimeline } from '../../../actions/timelines'; +import { scrollTopTimeline, loadPending } from '../../../actions/timelines'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { createSelector } from 'reselect'; import { debounce } from 'lodash'; @@ -37,6 +37,7 @@ const makeMapStateToProps = () => { isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false), hasMore: state.getIn(['timelines', timelineId, 'hasMore']), + numPending: state.getIn(['timelines', timelineId, 'pendingItems'], ImmutableList()).size, }); return mapStateToProps; @@ -52,6 +53,8 @@ const mapDispatchToProps = (dispatch, { timelineId }) => ({ dispatch(scrollTopTimeline(timelineId, false)); }, 100), + onLoadPending: () => dispatch(loadPending(timelineId)), + }); export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList); diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 791133afd32ed905705ca2cdd108c538acc82a45..791ff9a2e5ae6efb3c2b1ba1b7a2c2331c2ea740 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -15,9 +15,12 @@ import { expandHomeTimeline } from '../../actions/timelines'; import { expandNotifications } from '../../actions/notifications'; import { fetchFilters } from '../../actions/filters'; import { clearHeight } from '../../actions/height_cache'; +import { focusApp, unfocusApp } from 'mastodon/actions/app'; +import { submitMarkers } from 'mastodon/actions/markers'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import UploadArea from './components/upload_area'; import ColumnsAreaContainer from './containers/columns_area_container'; +import DocumentTitle from './components/document_title'; import { Compose, Status, @@ -45,6 +48,7 @@ import { PinnedStatuses, Lists, Search, + Directory, } from './util/async-components'; import { me, forceSingleColumn } from '../../initial_state'; import { previewState as previewMediaState } from './components/media_modal'; @@ -62,6 +66,7 @@ const mapStateToProps = state => ({ isComposing: state.getIn(['compose', 'is_composing']), hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0, hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0, + canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4, dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null, }); @@ -110,12 +115,25 @@ class SwitchingColumnsArea extends React.PureComponent { componentWillMount () { window.addEventListener('resize', this.handleResize, { passive: true }); + + if (this.state.mobile || forceSingleColumn) { + document.body.classList.toggle('layout-single-column', true); + document.body.classList.toggle('layout-multiple-columns', false); + } else { + document.body.classList.toggle('layout-single-column', false); + document.body.classList.toggle('layout-multiple-columns', true); + } } - componentDidUpdate (prevProps) { + componentDidUpdate (prevProps, prevState) { if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) { this.node.handleChildrenContentChange(); } + + if (prevState.mobile !== this.state.mobile && !forceSingleColumn) { + document.body.classList.toggle('layout-single-column', this.state.mobile); + document.body.classList.toggle('layout-multiple-columns', !this.state.mobile); + } } componentWillUnmount () { @@ -126,14 +144,24 @@ class SwitchingColumnsArea extends React.PureComponent { return location.state !== previewMediaState && location.state !== previewVideoState; } - handleResize = debounce(() => { + handleLayoutChange = debounce(() => { // The cached heights are no longer accurate, invalidate this.props.onLayoutChange(); - - this.setState({ mobile: isMobile(window.innerWidth) }); }, 500, { trailing: true, - }); + }) + + handleResize = () => { + const mobile = isMobile(window.innerWidth); + + if (mobile !== this.state.mobile) { + this.handleLayoutChange.cancel(); + this.props.onLayoutChange(); + this.setState({ mobile }); + } else { + this.handleLayoutChange(); + } + } setRef = c => { this.node = c.getWrappedInstance(); @@ -163,6 +191,7 @@ class SwitchingColumnsArea extends React.PureComponent { <WrappedRoute path='/pinned' component={PinnedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} /> <WrappedRoute path='/search' component={Search} content={children} /> + <WrappedRoute path='/directory' component={Directory} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} /> <WrappedRoute path='/statuses/new' component={Compose} content={children} /> <WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} /> @@ -204,6 +233,7 @@ class UI extends React.PureComponent { isComposing: PropTypes.bool, hasComposingText: PropTypes.bool, hasMediaAttachments: PropTypes.bool, + canUploadMore: PropTypes.bool, location: PropTypes.object, intl: PropTypes.object.isRequired, dropdownMenuIsOpen: PropTypes.bool, @@ -213,8 +243,10 @@ class UI extends React.PureComponent { draggingOver: false, }; - handleBeforeUnload = (e) => { - const { intl, isComposing, hasComposingText, hasMediaAttachments } = this.props; + handleBeforeUnload = e => { + const { intl, dispatch, isComposing, hasComposingText, hasMediaAttachments } = this.props; + + dispatch(submitMarkers()); if (isComposing && (hasComposingText || hasMediaAttachments)) { // Setting returnValue to any string causes confirmation dialog. @@ -224,6 +256,14 @@ class UI extends React.PureComponent { } } + handleWindowFocus = () => { + this.props.dispatch(focusApp()); + } + + handleWindowBlur = () => { + this.props.dispatch(unfocusApp()); + } + handleLayoutChange = () => { // The cached heights are no longer accurate, invalidate this.props.dispatch(clearHeight()); @@ -240,13 +280,14 @@ class UI extends React.PureComponent { this.dragTargets.push(e.target); } - if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files')) { + if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore) { this.setState({ draggingOver: true }); } } handleDragOver = (e) => { if (this.dataTransferIsText(e.dataTransfer)) return false; + e.preventDefault(); e.stopPropagation(); @@ -261,12 +302,13 @@ class UI extends React.PureComponent { handleDrop = (e) => { if (this.dataTransferIsText(e.dataTransfer)) return; + e.preventDefault(); this.setState({ draggingOver: false }); this.dragTargets = []; - if (e.dataTransfer && e.dataTransfer.files.length >= 1) { + if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore) { this.props.dispatch(uploadCompose(e.dataTransfer.files)); } } @@ -285,7 +327,7 @@ class UI extends React.PureComponent { } dataTransferIsText = (dataTransfer) => { - return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1); + return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1); } closeUploadModal = () => { @@ -301,6 +343,8 @@ class UI extends React.PureComponent { } componentWillMount () { + window.addEventListener('focus', this.handleWindowFocus, false); + window.addEventListener('blur', this.handleWindowBlur, false); window.addEventListener('beforeunload', this.handleBeforeUnload, false); document.addEventListener('dragenter', this.handleDragEnter, false); @@ -330,7 +374,10 @@ class UI extends React.PureComponent { } componentWillUnmount () { + window.removeEventListener('focus', this.handleWindowFocus); + window.removeEventListener('blur', this.handleWindowBlur); window.removeEventListener('beforeunload', this.handleBeforeUnload); + document.removeEventListener('dragenter', this.handleDragEnter); document.removeEventListener('dragover', this.handleDragOver); document.removeEventListener('drop', this.handleDrop); @@ -489,6 +536,7 @@ class UI extends React.PureComponent { <LoadingBarContainer className='loading-bar' /> <ModalContainer /> <UploadArea active={draggingOver} onClose={this.closeUploadModal} /> + <DocumentTitle /> </div> </HotKeys> ); diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 6e8ed163a539a5c2bddb5b5d43213b17faf7ea9d..bb0fcb8593468acb8c7183726e4797d0ceb6d78f 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -106,6 +106,10 @@ export function MuteModal () { return import(/* webpackChunkName: "modals/mute_modal" */'../components/mute_modal'); } +export function BlockModal () { + return import(/* webpackChunkName: "modals/block_modal" */'../components/block_modal'); +} + export function ReportModal () { return import(/* webpackChunkName: "modals/report_modal" */'../components/report_modal'); } @@ -133,3 +137,15 @@ export function ListAdder () { export function Search () { return import(/*webpackChunkName: "features/search" */'../../search'); } + +export function Tesseract () { + return import(/*webpackChunkName: "tesseract" */'tesseract.js'); +} + +export function Audio () { + return import(/* webpackChunkName: "features/audio" */'../../audio'); +} + +export function Directory () { + return import(/* webpackChunkName: "features/directory" */'../../directory'); +} diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js index b0c4085277fc13fc572a953502edbab8406fc4e7..5fe4e956f879f628773f399d80a8475c23b50f30 100644 --- a/app/javascript/mastodon/features/video/index.js +++ b/app/javascript/mastodon/features/video/index.js @@ -5,7 +5,7 @@ import { fromJS, is } from 'immutable'; import { throttle } from 'lodash'; import classNames from 'classnames'; import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; -import { displayMedia } from '../../initial_state'; +import { displayMedia, useBlurhash } from '../../initial_state'; import Icon from 'mastodon/components/icon'; import { decode } from 'blurhash'; @@ -21,7 +21,7 @@ const messages = defineMessages({ exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' }, }); -const formatTime = secondsNum => { +export const formatTime = secondsNum => { let hours = Math.floor(secondsNum / 3600); let minutes = Math.floor((secondsNum - (hours * 3600)) / 60); let seconds = secondsNum - (hours * 3600) - (minutes * 60); @@ -101,6 +101,7 @@ class Video extends React.PureComponent { onCloseVideo: PropTypes.func, detailed: PropTypes.bool, inline: PropTypes.bool, + editable: PropTypes.bool, cacheWidth: PropTypes.func, visible: PropTypes.bool, onToggleVisibility: PropTypes.func, @@ -298,6 +299,8 @@ class Video extends React.PureComponent { } _decode () { + if (!useBlurhash) return; + const hash = this.props.blurhash; const pixels = decode(hash, 32, 32); @@ -373,7 +376,7 @@ class Video extends React.PureComponent { } render () { - const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link } = this.props; + const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link, editable } = this.props; const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; const progress = (currentTime / duration) * 100; @@ -411,7 +414,7 @@ class Video extends React.PureComponent { return ( <div role='menuitem' - className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen })} + className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, editable })} style={playerStyle} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} @@ -421,7 +424,7 @@ class Video extends React.PureComponent { > <canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': revealed })} /> - {revealed && <video + {(revealed || editable) && <video ref={this.setVideoRef} src={src} poster={preview} @@ -443,7 +446,7 @@ class Video extends React.PureComponent { onVolumeChange={this.handleVolumeChange} />} - <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed })}> + <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}> <button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}> <span className='spoiler-button__overlay__label'>{warning}</span> </button> @@ -487,7 +490,7 @@ class Video extends React.PureComponent { </div> <div className='video-player__buttons right'> - {!onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} + {(!onCloseVideo && !editable) && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} {(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>} {onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>} <button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button> diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index 125508c2345cad89188c36e552113b228837a897..56fb58546690992c51b27af59a3e50f8ce117034 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -20,5 +20,9 @@ export const mascot = getMeta('mascot'); export const profile_directory = getMeta('profile_directory'); export const isStaff = getMeta('is_staff'); export const forceSingleColumn = !getMeta('advanced_layout'); +export const useBlurhash = getMeta('use_blurhash'); +export const usePendingItems = getMeta('use_pending_items'); +export const showTrends = getMeta('trends'); +export const title = getMeta('title'); export default initialState; diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index de289b474ccd269f7dd657112f464a0f809c7a01..4dd9df55bc8d384251f4a772812172c026d7f37a 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -4,10 +4,11 @@ "account.block": "ØØ¸Ø± @{name}", "account.block_domain": "Ø¥Ø®ÙØ§Ø¡ كل شيئ قادم من اسم النطاق {domain}", "account.blocked": "Ù…ØØ¸ÙˆØ±", + "account.cancel_follow_request": "إلغاء طلب المتابَعة", "account.direct": "رسالة خاصة إلى @{name}", "account.domain_blocked": "النطاق مخÙÙŠ", - "account.edit_profile": "تعديل المل٠الشخصي", - "account.endorse": "خاصّية على المل٠الشخصي", + "account.edit_profile": "تعديل المل٠التعريÙÙŠ", + "account.endorse": "أوص٠به على ØµÙØØªÙƒ", "account.follow": "ØªØ§Ø¨ÙØ¹", "account.followers": "متابعون", "account.followers.empty": "لا Ø£ØØ¯ يتبع هذا Ø§Ù„ØØ³Ø§Ø¨ بعد.", @@ -15,28 +16,33 @@ "account.follows.empty": "هذا Ø§Ù„ØØ³Ø§Ø¨ لا يتبع Ø£ØØ¯Ù‹Ø§ بعد.", "account.follows_you": "يتابعك", "account.hide_reblogs": "Ø¥Ø®ÙØ§Ø¡ ترقيات @{name}", + "account.last_status": "آخر نشاط", "account.link_verified_on": "تم التØÙ‚Ù‚ Ù…ÙÙ† Ù…Ùلْكية هذا الرابط بتاريخ {date}", "account.locked_info": "تم تأمين خصوصية هذا Ø§Ù„ØØ³Ø§Ø¨ عبر Ù‚ÙÙ„. ØµØ§ØØ¨ Ø§Ù„ØØ³Ø§Ø¨ ÙŠÙØ±Ø§Ø¬Ùع يدويا طلبات المتابَعة Ùˆ الاشتراك Ø¨ØØ³Ø§Ø¨Ù‡.", "account.media": "وسائط", - "account.mention": "Ø£ÙØ°ÙƒÙر/ÙŠ @{name}", + "account.mention": "Ø£Ø°ÙƒÙØ± @{name}", "account.moved_to": "{name} انتقل إلى:", - "account.mute": "كتم @{name}", + "account.mute": "أكتم @{name}", "account.mute_notifications": "كتم الإخطارات من @{name}", "account.muted": "مكتوم", - "account.posts": "التبويقات", + "account.never_active": "أبدا", + "account.posts": "تبويقات", "account.posts_with_replies": "التبويقات Ùˆ الردود", - "account.report": "أبلغ/ÙŠ عن @{name}", + "account.report": "Ø§Ø¨Ù„ÙØº عن @{name}", "account.requested": "ÙÙŠ انتظار المواÙقة. اضْغَطْ/ÙŠ لإلغاء طلب المتابعة", - "account.share": "مشاركة ØØ³Ø§Ø¨ @{name}", - "account.show_reblogs": "عرض ترقيات @{name}", + "account.share": "شارك مل٠تعري٠@{name}", + "account.show_reblogs": "اعرض ترقيات @{name}", "account.unblock": "إلغاء Ø§Ù„ØØ¸Ø± عن @{name}", "account.unblock_domain": "ÙÙƒ الخْÙÙ‰ عن {domain}", - "account.unendorse": "إزالة ترويجه Ù…ÙÙ† المل٠الشخصي", + "account.unendorse": "أزل ترويجه Ù…ÙÙ† المل٠التعريÙÙŠ", "account.unfollow": "إلغاء المتابعة", "account.unmute": "إلغاء الكتم عن @{name}", "account.unmute_notifications": "إلغاء كتم إخطارات @{name}", + "alert.rate_limited.message": "يرجى إعادة Ø§Ù„Ù…ØØ§ÙˆÙ„Ø© بعد {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "لقد طرأ هناك خطأ غير متوقّع.", "alert.unexpected.title": "المعذرة!", + "autosuggest_hashtag.per_week": "{count} ÙÙŠ الأسبوع", "boost_modal.combo": "يمكنك/ÙŠ ضغط {combo} لتخطّي هذه ÙÙŠ المرّة القادمة", "bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تØÙ…يل هذا العنصر.", "bundle_column_error.retry": "إعادة Ø§Ù„Ù…ØØ§ÙˆÙ„Ø©", @@ -47,6 +53,7 @@ "column.blocks": "Ø§Ù„ØØ³Ø§Ø¨Ø§Øª Ø§Ù„Ù…ØØ¬ÙˆØ¨Ø©", "column.community": "الخيط العام المØÙ„ÙŠ", "column.direct": "الرسائل المباشرة", + "column.directory": "استعرض Ø§Ù„Ù…Ù„ÙØ§Øª التعريÙية", "column.domain_blocks": "النطاقات المخÙية", "column.favourites": "Ø§Ù„Ù…ÙØ¶Ù„Ø©", "column.follow_requests": "طلبات المتابعة", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "هل تود ØÙ‚ا ØØ°Ù هذه القائمة ØŸ", "confirmations.domain_block.confirm": "Ø¥Ø®ÙØ§Ø¡ اسم النطاق كاملا", "confirmations.domain_block.message": "متأكد من أنك تود ØØ¸Ø± اسم النطاق {domain} بالكامل ØŸ ÙÙŠ غالب الأØÙŠØ§Ù† ÙŠÙØ³ØªÙŽØØ³ÙŽÙ† كتم أو ØØ¸Ø± بعض Ø§Ù„ØØ³Ø§Ø¨Ø§Øª بدلا من ØØ¸Ø± نطاق بالكامل.\nلن تتمكن Ù…ÙÙ† رؤية Ù…ØØªÙˆÙ‰ هذا النطاق لا على خيوطك العمومية Ùˆ لا ÙÙŠ إشعاراتك. سو٠يتم كذلك إزالة ÙƒØ§ÙØ© متابعيك المنتمين إلى هذا النطاق.", + "confirmations.logout.confirm": "خروج", + "confirmations.logout.message": "متأكد من أنك تريد الخروج؟", "confirmations.mute.confirm": "أكتم", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ØŸ", "confirmations.redraft.confirm": "إزالة Ùˆ إعادة الصياغة", "confirmations.redraft.message": "هل أنت متأكد من أنك تريد ØØ°Ù هذا المنشور Ùˆ إعادة صياغته ØŸ سو٠تÙقد جميع الإعجابات Ùˆ الترقيات أما الردود المتصلة به ÙØ³ØªÙØµØ¨ÙØ يتيمة.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "الرد ÙÙŠ الØÙŠÙ† Ø³ÙˆÙ ÙŠÙØ¹ÙŠØ¯ كتابة الرسالة التي أنت بصدد كتابتها. متأكد من أنك تريد المواصلة؟", "confirmations.unfollow.confirm": "إلغاء المتابعة", "confirmations.unfollow.message": "متأكد من أنك تريد إلغاء متابعة {name} ØŸ", + "conversation.delete": "Ø§ØØ°Ù Ø§Ù„Ù…ØØ§Ø¯Ø«Ø©", + "conversation.mark_as_read": "اعتبرها كمقروءة", + "conversation.open": "اعرض Ø§Ù„Ù…ØØ§Ø¯Ø«Ø©", + "conversation.with": "بـ {names}", + "directory.federated": "Ù…ÙÙ† Ø§Ù„ÙØ¯ÙŠÙرس المعروÙ", + "directory.local": "Ù…ÙÙ† {domain} Ùقط", + "directory.new_arrivals": "Ø§Ù„ÙˆØ§ÙØ¯ÙˆÙ† Ø§Ù„Ø¬ÙØ¯Ø¯", + "directory.recently_active": "نشط مؤخرا", "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ Ø§Ù„Ø´ÙØ±Ø© أدناه.", "embed.preview": "هكذا ما سو٠يبدو عليه:", "emoji_button.activity": "الأنشطة", @@ -116,9 +134,9 @@ "emoji_button.search": "Ø§Ø¨ØØ«...", "emoji_button.search_results": "نتائج Ø§Ù„Ø¨ØØ«", "emoji_button.symbols": "رموز", - "emoji_button.travel": "أماكن Ùˆ Ø£Ø³ÙØ§Ø±", + "emoji_button.travel": "الأماكن ÙˆØ§Ù„Ø³ÙØ±", "empty_column.account_timeline": "ليس هناك تبويقات!", - "empty_column.account_unavailable": "المل٠الشخصي غير Ù…ØªÙˆÙØ±", + "empty_column.account_unavailable": "المل٠التعريÙÙŠ غير Ù…ØªÙˆÙØ±", "empty_column.blocks": "لم تقم Ø¨ØØ¸Ø± أي مستخدÙÙ… بعد.", "empty_column.community": "الخط العام المØÙ„ÙŠ ÙØ§Ø±Øº. أكتب شيئا ما للعامة كبداية!", "empty_column.direct": "لم تتلق أية رسالة خاصة Ù…Ø¨Ø§Ø´ÙØ±Ø© بعد. سو٠يتم عرض الرسائل المباشرة هنا إن قمت بإرسال ÙˆØ§ØØ¯Ø© أو تلقيت البعض منها.", @@ -134,6 +152,10 @@ "empty_column.mutes": "لم تقم بكتم أي مستخدم بعد.", "empty_column.notifications": "لم تتلق أي إشعار بعدÙ. ØªÙØ§Ø¹Ù„ مع المستخدمين الآخرين لإنشاء Ù…ØØ§Ø¯Ø«Ø©.", "empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط Ø§Ù„Ù…ØØ§Ø¯Ø«Ø§Øª", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "الإبلاغ عن خلل", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,7 +164,7 @@ "follow_request.authorize": "ترخيص", "follow_request.reject": "Ø±ÙØ¶", "getting_started.developers": "Ø§Ù„Ù…ÙØ·ÙˆÙ‘ÙØ±ÙˆÙ†", - "getting_started.directory": "دليل المستخدÙمين والمستخدÙمات", + "getting_started.directory": "دليل Ø§Ù„ØµÙØØ§Øª التعريÙية", "getting_started.documentation": "الدليل", "getting_started.heading": "استعدّ للبدء", "getting_started.invite": "دعوة أشخاص", @@ -158,9 +180,9 @@ "hashtag.column_settings.tag_mode.any": "أي كان Ù…ÙÙ† هذه", "hashtag.column_settings.tag_mode.none": "لا شيء Ù…ÙÙ† هذه", "hashtag.column_settings.tag_toggle": "إدراج الوسوم الإضاÙية لهذا العمود", - "home.column_settings.basic": "أساسية", + "home.column_settings.basic": "الأساسية", "home.column_settings.show_reblogs": "عرض الترقيات", - "home.column_settings.show_replies": "عرض الردود", + "home.column_settings.show_replies": "اعرض الردود", "intervals.full.days": "{number, plural, one {# يوم} other {# أيام}}", "intervals.full.hours": "{number, plural, one {# ساعة} other {# ساعات}}", "intervals.full.minutes": "{number, plural, one {# دقيقة} other {# دقائق}}", @@ -180,7 +202,7 @@ "introduction.interactions.reply.text": "يمكنكم الرد على تبويقاتكم Ùˆ تبويقات الآخرين على شكل سلسلة Ù…ØØ§Ø¯Ø«Ø©.", "introduction.welcome.action": "هيا بنا!", "introduction.welcome.headline": "الخطوات الأولى", - "introduction.welcome.text": "Ù…Ø±ØØ¨Ø§ بكم على Ø§Ù„ÙØ¯ÙŠÙرس! بعد Ù„ØØ¸Ø§Øª قليلة ØŒ سيكون بمقدوركم بث رسائل ÙˆØ§Ù„ØªØØ¯Ø« إلى أصدقائكم عبر تشكيلة واسعة من الخوادم Ø§Ù„Ù…Ø®ØªÙ„ÙØ©. هذا الخادم ØŒ {domain} ØŒ يستضي٠ملÙكم الشخصي ØŒ لذا يجب تذكر اسمه جيدا.", + "introduction.welcome.text": "Ù…Ø±ØØ¨Ø§ بكم على Ø§Ù„ÙØ¯ÙŠÙرس! بعد Ù„ØØ¸Ø§Øª قليلة ØŒ سيكون بمقدوركم بث رسائل ÙˆØ§Ù„ØªØØ¯Ø« إلى أصدقائكم عبر تشكيلة واسعة من الخوادم Ø§Ù„Ù…Ø®ØªÙ„ÙØ©. هذا الخادم ØŒ {domain} ØŒ ÙŠØ³ØªØ¶ÙŠÙ ØµÙØØªÙƒÙ… التعريÙية ØŒ لذا يجب تذكر اسمه جيدا.", "keyboard_shortcuts.back": "للعودة", "keyboard_shortcuts.blocked": "Ù„ÙØªØ قائمة المستخدمين Ø§Ù„Ù…ØØ¸ÙˆØ±ÙŠÙ†", "keyboard_shortcuts.boost": "للترقية", @@ -200,10 +222,10 @@ "keyboard_shortcuts.local": "Ù„ÙØªØ الخيط العام المØÙ„ÙŠ", "keyboard_shortcuts.mention": "لذÙكر الناشر", "keyboard_shortcuts.muted": "Ù„ÙØªØ قائمة المستخدÙمين المكتومين", - "keyboard_shortcuts.my_profile": "Ù„ÙØªØ ملÙÙƒ الشخصي", + "keyboard_shortcuts.my_profile": "Ù„ÙØªØ ملÙÙƒ التعريÙÙŠ", "keyboard_shortcuts.notifications": "Ù„ÙØªØ عمود الإشعارات", "keyboard_shortcuts.pinned": "Ù„ÙØªØ قائمة التبويقات المدبسة", - "keyboard_shortcuts.profile": "Ù„ÙØªØ رابط الناشر", + "keyboard_shortcuts.profile": "Ù„ÙØªØ المل٠التعريÙÙŠ للناشر", "keyboard_shortcuts.reply": "للردّ", "keyboard_shortcuts.requests": "Ù„ÙØªØ قائمة طلبات المتابعة", "keyboard_shortcuts.search": "للتركيز على Ø§Ù„Ø¨ØØ«", @@ -220,15 +242,16 @@ "lists.account.add": "أض٠إلى القائمة", "lists.account.remove": "Ø§ØØ°Ù من القائمة", "lists.delete": "Ø§ØØ°Ù القائمة", - "lists.edit": "تعديل القائمة", + "lists.edit": "عدّل القائمة", "lists.edit.submit": "تعديل العنوان", "lists.new.create": "إنشاء قائمة", "lists.new.title_placeholder": "عنوان القائمة الجديدة", "lists.search": "Ø¥Ø¨ØØ« ÙÙŠ قائمة Ø§Ù„ØØ³Ø§Ø¨Ø§Øª التي ØªÙØªØ§Ø¨Ùعها", "lists.subheading": "قوائمك", + "load_pending": "{count, plural, one {# عنصر جديد} other {# عناصر جديدة}}", "loading_indicator.label": "تØÙ…يل...", "media_gallery.toggle_visible": "عرض / Ø¥Ø®ÙØ§Ø¡", - "missing_indicator.label": "تعذر العثور عليه", + "missing_indicator.label": "غير موجود", "missing_indicator.sublabel": "تعذر العثور على هذا المورد", "mute_modal.hide_notifications": "هل تود Ø¥Ø®ÙØ§Ø¡ الإخطارات القادمة من هذا المستخدم ØŸ", "navigation_bar.apps": "تطبيقات الأجهزة المØÙ…ولة", @@ -238,7 +261,7 @@ "navigation_bar.direct": "الرسائل Ø§Ù„Ù…Ø¨Ø§Ø´ÙØ±Ø©", "navigation_bar.discover": "اكتشÙ", "navigation_bar.domain_blocks": "النطاقات المخÙية", - "navigation_bar.edit_profile": "تعديل المل٠الشخصي", + "navigation_bar.edit_profile": "عدّل المل٠التعريÙÙŠ", "navigation_bar.favourites": "Ø§Ù„Ù…ÙØ¶Ù„Ø©", "navigation_bar.filters": "الكلمات المكتومة", "navigation_bar.follow_requests": "طلبات المتابعة", @@ -251,21 +274,20 @@ "navigation_bar.personal": "شخصي", "navigation_bar.pins": "التبويقات المثبتة", "navigation_bar.preferences": "Ø§Ù„ØªÙØ¶ÙŠÙ„ات", - "navigation_bar.profile_directory": "دليل المستخدÙمين", "navigation_bar.public_timeline": "الخيط العام Ø§Ù„Ù…ÙˆØØ¯", "navigation_bar.security": "الأمان", "notification.favourite": "Ø£ÙØ¹Ø¬Ùب {name} بمنشورك", "notification.follow": "{name} يتابعك", "notification.mention": "{name} ذكرك", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "لقد إنتها تصويت شاركت Ùيه", "notification.reblog": "{name} قام بترقية تبويقك", "notifications.clear": "Ø§Ù…Ø³Ø Ø§Ù„Ø¥Ø®Ø·Ø§Ø±Ø§Øª", "notifications.clear_confirmation": "أمتأكد من أنك تود Ù…Ø³Ø Ø¬Ù„ الإخطارات الخاصة بك Ùˆ المتلقاة إلى ØØ¯ الآن ØŸ", "notifications.column_settings.alert": "إشعارات Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨", "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.show": "عرض", + "notifications.column_settings.filter_bar.show": "اعرض", "notifications.column_settings.follow": "متابعÙون Ø¬ÙØ¯Ùد:", "notifications.column_settings.mention": "الإشارات:", "notifications.column_settings.poll": "نتائج استطلاع الرأي:", @@ -282,8 +304,10 @@ "notifications.group": "{count} إشعارات", "poll.closed": "انتهى", "poll.refresh": "ØªØØ¯ÙŠØ«", + "poll.total_people": "{count, plural, one {# شخص} other {# أشخاص}}", "poll.total_votes": "{count, plural, one {# صوت} other {# أصوات}}", "poll.vote": "صَوّت", + "poll.voted": "لقد صوّتت على هذه الإجابة", "poll_button.add_poll": "Ø¥Ø¶Ø§ÙØ© استطلاع للرأي", "poll_button.remove_poll": "إزالة استطلاع الرأي", "privacy.change": "اضبط خصوصية المنشور", @@ -295,6 +319,7 @@ "privacy.public.short": "للعامة", "privacy.unlisted.long": "لا تقم بإدراجه على الخيوط العامة", "privacy.unlisted.short": "غير مدرج", + "refresh": "Ø£Ù†Ø¹ÙØ´", "regeneration_indicator.label": "جار٠التØÙ…يل…", "regeneration_indicator.sublabel": "جار٠تجهيز تغذية ØµÙØØªÙƒ الرئيسية!", "relative_time.days": "{number}ÙŠ", @@ -319,6 +344,7 @@ "search_results.accounts": "أشخاص", "search_results.hashtags": "Ø§Ù„ÙˆÙØ³ÙˆÙ…", "search_results.statuses": "التبويقات", + "search_results.statuses_fts_disabled": "Ø§Ù„Ø¨ØØ« ÙÙŠ التبويقات عن طريق Ø§Ù„Ù…ØØªÙˆÙ‰ ليس Ù…ÙØ¹Ù„ ÙÙŠ خادم ماستدون هذا.", "search_results.total": "{count, number} {count, plural, one {result} Ùˆ {results}}", "status.admin_account": "Ø§ÙØªØ الواجهة الإدارية لـ @{name}", "status.admin_status": "Ø§ÙØªØ هذا المنشور على واجهة الإشراÙ", @@ -340,7 +366,7 @@ "status.mute": "أكتم @{name}", "status.mute_conversation": "كتم Ø§Ù„Ù…ØØ§Ø¯Ø«Ø©", "status.open": "وسع هذه المشاركة", - "status.pin": "تدبيس على المل٠الشخصي", + "status.pin": "دبّسه على Ø§Ù„ØµÙØØ© التعريÙية", "status.pinned": "تبويق مثبَّت", "status.read_more": "اقرأ المزيد", "status.reblog": "رَقّÙÙŠ", @@ -358,8 +384,9 @@ "status.show_more": "أظهر المزيد", "status.show_more_all": "توسيع الكل", "status.show_thread": "الكش٠عن Ø§Ù„Ù…ØØ§Ø¯Ø«Ø©", + "status.uncached_media_warning": "غير Ù…ØªÙˆÙØ±", "status.unmute_conversation": "ÙÙƒ الكتم عن Ø§Ù„Ù…ØØ§Ø¯Ø«Ø©", - "status.unpin": "ÙÙƒ التدبيس من المل٠الشخصي", + "status.unpin": "ÙÙƒ التدبيس من Ø§Ù„ØµÙØØ© التعريÙية", "suggestions.dismiss": "إلغاء الاقتراØ", "suggestions.header": "يمكن أن يهمك…", "tabs_bar.federated_timeline": "الموØÙ‘َد", @@ -368,19 +395,27 @@ "tabs_bar.notifications": "الإخطارات", "tabs_bar.search": "Ø§Ù„Ø¨ØØ«", "time_remaining.days": "{number, plural, one {# يوم} other {# أيام}} متبقية", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.hours": "{number, plural, one {# ساعة} other {# ساعات}} متبقية", + "time_remaining.minutes": "{number, plural, one {# دقيقة} other {# دقائق}} متبقية", "time_remaining.moments": "Ù„ØØ¸Ø§Øª متبقية", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "time_remaining.seconds": "{number, plural, one {# ثانية} other {# ثوانÙ}} متبقية", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} آخرون {people}} ÙŠØªØØ¯Ø«ÙˆÙ†", + "trends.trending_now": "المتداولة الآن", "ui.beforeunload": "سو٠تÙقد مسودتك إن تركت ماستدون.", "upload_area.title": "Ø§Ø³ØØ¨ ثم Ø£Ùلت Ù„Ù„Ø±ÙØ¹", "upload_button.label": "Ø¥Ø¶Ø§ÙØ© وسائط ({formats})", "upload_error.limit": "لقد تم بلوغ Ø§Ù„ØØ¯ الأقصى Ø§Ù„Ù…Ø³Ù…ÙˆØ Ø¨Ù‡ لإرسال Ø§Ù„Ù…Ù„ÙØ§Øª.", "upload_error.poll": "لا يمكن إدراج Ù…Ù„ÙØ§Øª ÙÙŠ استطلاعات الرأي.", "upload_form.description": "وص٠للمعاقين بصريا", - "upload_form.focus": "قص", + "upload_form.edit": "تعديل", "upload_form.undo": "ØØ°Ù", + "upload_modal.analyzing_picture": "Ø¬Ø§Ø±Ù ÙØØµ الصورة…", + "upload_modal.apply": "طبّق", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "اكتش٠النص Ù…ÙÙ† الصورة", + "upload_modal.edit_media": "تعديل الوسائط", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "معاينة ({ratio})", "upload_progress.label": "ÙŠØ±ÙØ¹...", "video.close": "إغلاق الÙيديو", "video.exit_fullscreen": "الخروج من وضع الشاشة المليئة", diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json index d4706768c97298de043d055920b155035ee10f30..b2c98bb157731bb4532c1318f4a298705157bae4 100644 --- a/app/javascript/mastodon/locales/ast.json +++ b/app/javascript/mastodon/locales/ast.json @@ -4,6 +4,7 @@ "account.block": "Bloquiar a @{name}", "account.block_domain": "Anubrir tolo de {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Unviar un mensaxe direutu a @{name}", "account.domain_blocked": "Dominiu anubrÃu", "account.edit_profile": "Editar el perfil", @@ -15,6 +16,7 @@ "account.follows.empty": "Esti usuariu entá nun sigue a naide.", "account.follows_you": "SÃguete", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,10 +25,11 @@ "account.mute": "Silenciar a @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots y rempuestes", "account.report": "Report @{name}", - "account.requested": "Awaiting approval. Click to cancel follow request", + "account.requested": "Awaiting approval", "account.share": "Share @{name}'s profile", "account.show_reblogs": "Show boosts from @{name}", "account.unblock": "Desbloquiar a @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Asocedió un fallu inesperáu.", "alert.unexpected.title": "¡Ups!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Pues primir {combo} pa saltar esto la próxima vegada", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Usuarios bloquiaos", "column.community": "Llinia temporal llocal", "column.direct": "Mensaxes direutos", + "column.directory": "Browse profiles", "column.domain_blocks": "Dominios anubrÃos", "column.favourites": "Favoritos", "column.follow_requests": "Solicitúes de siguimientu", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Desfixar", "column_subheading.settings": "Axustes", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "Esti toot namái va unviase a los usuarios mentaos.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "¿De xuru que quies desaniciar dafechu esta llista?", "confirmations.domain_block.confirm": "Anubrir tol dominiu", "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "¿De xuru que quies silenciar a {name}?", "confirmations.redraft.confirm": "Desaniciar y reeditar", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "¿De xuru que quies dexar de siguir a {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Empotra esti estáu nun sitiu web copiando'l códigu d'embaxo.", "embed.preview": "Asina ye como va vese:", "emoji_button.activity": "Actividaes", @@ -134,6 +152,10 @@ "empty_column.mutes": "Entá nun silenciesti a dengún usuariu.", "empty_column.notifications": "Entá nun tienes dengún avisu. Interactua con otros p'aniciar la conversación.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "TÃtulu nuevu de la llista", "lists.search": "Guetar ente la xente que sigues", "lists.subheading": "Les tos llistes", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Cargando...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Nun s'alcontró", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Toots fixaos", "navigation_bar.preferences": "Preferencies", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Llinia temporal federada", "navigation_bar.security": "Seguranza", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} avisos", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Cargando…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Xente", "search_results.hashtags": "Etiquetes", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Amosar más", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Desfixar del perfil", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "El borrador va perdese si coles de Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Descripción pa discapacitaos visuales", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Desaniciar", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Xubiendo...", "video.close": "Zarrar el videu", "video.exit_fullscreen": "Colar de la pantalla completa", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index f534aa4baf0942a632aaffd24bfd7814cfe0019f..12b03796eb7371a3f321c02cb8b0193274ac6420 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -1,11 +1,12 @@ { - "account.add_or_remove_from_list": "Add or Remove from lists", - "account.badges.bot": "Bot", + "account.add_or_remove_from_list": "Добави или премахни от ÑпиÑъците", + "account.badges.bot": "бот", "account.block": "Блокирай", - "account.block_domain": "Hide everything from {domain}", - "account.blocked": "Blocked", + "account.block_domain": "Ñкрий вÑичко от (домейн)", + "account.blocked": "Блокирани", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", - "account.domain_blocked": "Domain hidden", + "account.domain_blocked": "Скрит домейн", "account.edit_profile": "Редактирай профила Ñи", "account.endorse": "Feature on profile", "account.follow": "ПоÑледвай", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Твой поÑледовател", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Mute @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Публикации", "account.posts_with_replies": "Toots with replies", "account.report": "Report @{name}", @@ -35,11 +38,14 @@ "account.unfollow": "Ðе Ñледвай", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You can press {combo} to skip this next time", "bundle_column_error.body": "Something went wrong while loading this component.", - "bundle_column_error.retry": "Try again", + "bundle_column_error.retry": "Опитай отново", "bundle_column_error.title": "Network error", "bundle_modal_error.close": "Close", "bundle_modal_error.message": "Something went wrong while loading this component.", @@ -47,11 +53,12 @@ "column.blocks": "Blocked users", "column.community": "Local timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", "column.home": "Ðачало", - "column.lists": "Lists", + "column.lists": "СпиÑъци", "column.mutes": "Muted users", "column.notifications": "ИзвеÑтиÑ", "column.pins": "Pinned toot", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Unpin", "column_subheading.settings": "Settings", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Зареждане...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "ПредпочитаниÑ", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Публичен канал", "navigation_bar.security": "Security", "notification.favourite": "{name} хареÑа твоÑта публикациÑ", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,15 +400,23 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Добави медиÑ", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "ОтмÑна", - "upload_progress.label": "Uploading...", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", "video.expand": "Expand video", diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json index fe81b9a3335c835eb8e7c79983d6799c50710b6a..67501844d1eee52857d93038faa6c21c828fa7ce 100644 --- a/app/javascript/mastodon/locales/bn.json +++ b/app/javascript/mastodon/locales/bn.json @@ -1,13 +1,14 @@ { "account.add_or_remove_from_list": "তালিকাতে আরো যà§à¦•à§à¦¤ বা মà§à¦›à§‡ ফেলà§à¦¨", "account.badges.bot": "রোবট", - "account.block": "@{name} বনà§à¦§ করà§à¦¨", + "account.block": "@{name} কে বনà§à¦§ করà§à¦¨", "account.block_domain": "{domain} থেকে সব সরিয়ে ফেলà§à¦¨", "account.blocked": "বনà§à¦§ করা হয়েছে", - "account.direct": "@{name}কে সরকারি লিখà§à¦¨", + "account.cancel_follow_request": "Cancel follow request", + "account.direct": "@{name} à¦à¦° কাছে সরকারি লেখা পাঠাতে", "account.domain_blocked": "ওয়েবসাইট সরিয়ে ফেলা হয়েছে", - "account.edit_profile": "নিজের পাতা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨", - "account.endorse": "নিজের পাতায় দেখান", + "account.edit_profile": "নিজের পাতা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করতে", + "account.endorse": "আপনার নিজের পাতায় দেখাতে", "account.follow": "অনà§à¦¸à¦°à¦£ করà§à¦¨", "account.followers": "অনà§à¦¸à¦°à¦£à¦•ারক", "account.followers.empty": "à¦à¦‡ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীকে কেও à¦à¦–নো অনà§à¦¸à¦°à¦£ করে না।", @@ -15,38 +16,44 @@ "account.follows.empty": "à¦à¦‡ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারী কাওকে à¦à¦–নো অনà§à¦¸à¦°à¦£ করেন না।", "account.follows_you": "আপনাকে অনà§à¦¸à¦°à¦£ করে", "account.hide_reblogs": "@{name}র সমরà§à¦¥à¦¨à¦—à§à¦²à¦¿ সরিয়ে ফেলà§à¦¨", + "account.last_status": "Last active", "account.link_verified_on": "à¦à¦‡ লিংকের মালিকানা চেক করা হয়েছে {date} তারিকে", "account.locked_info": "à¦à¦‡ নিবনà§à¦§à¦¨à§‡à¦° গোপনীয়তার কà§à¦·à§‡à¦¤à§à¦° তালা দেওয়া আছে। নিবনà§à¦§à¦¨à¦•ারী অনà§à¦¸à¦°à¦£ করার অনà§à¦®à¦¤à¦¿ যাদেরকে দেবেন, শà§à¦§à§ তারাই অনà§à¦¸à¦°à¦£ করতে পারবেন।", "account.media": "ছবি বা à¦à¦¿à¦¡à¦¿à¦“", - "account.mention": "@{name} কে উলà§à¦²à§‡à¦– করà§à¦¨", + "account.mention": "@{name} কে উলà§à¦²à§‡à¦– করতে", "account.moved_to": "{name} চলে গেছে à¦à¦–ানে:", - "account.mute": "@{name}র কারà§à¦¯à¦•à§à¦°à¦® সরিয়ে ফেলà§à¦¨", + "account.mute": "@{name} সব কারà§à¦¯à¦•à§à¦°à¦® আপনার সময়রেখা থেকে সরিয়ে ফেলতে", "account.mute_notifications": "@{name}র পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨ আপনার কাছ থেকে সরিয়ে ফেলà§à¦¨", "account.muted": "সরানো আছে", + "account.never_active": "Never", "account.posts": "টà§à¦Ÿ", "account.posts_with_replies": "টà§à¦Ÿ à¦à¦¬à¦‚ মতামত", - "account.report": "@{name}কে রিপোরà§à¦Ÿ করে দিন", + "account.report": "@{name} কে রিপোরà§à¦Ÿ করতে", "account.requested": "অনà§à¦®à¦¤à¦¿à¦° অপেকà§à¦·à¦¾à§Ÿ আছে। অনà§à¦¸à¦°à¦£ করার অনà§à¦°à§‹à¦§ বাতিল করতে à¦à¦–ানে কà§à¦²à¦¿à¦• করà§à¦¨", "account.share": "@{name}র পাতা অনà§à¦¯à¦¦à§‡à¦° দেখান", "account.show_reblogs": "@{name}র সমরà§à¦¥à¦¨à¦—à§à¦²à§‹ দেখà§à¦¨", "account.unblock": "@{name}র কারà§à¦¯à¦•লাপ আবার দেখà§à¦¨", "account.unblock_domain": "{domain}থেকে আবার দেখà§à¦¨", - "account.unendorse": "নিজের পাতায় à¦à¦Ÿà¦¾ দেখতে চান না", - "account.unfollow": "অনà§à¦¸à¦°à¦£ বনà§à¦§ করà§à¦¨", + "account.unendorse": "আপনার নিজের পাতায় à¦à¦Ÿà¦¾ না দেখাতে", + "account.unfollow": "অনà§à¦¸à¦°à¦£ না করতে", "account.unmute": "@{name}র কারà§à¦¯à¦•লাপ আবার দেখà§à¦¨", "account.unmute_notifications": "@{name}র পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨ দেওয়ার অনà§à¦®à¦¤à¦¿ দিন", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "অপà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ à¦à¦•টি সমসà§à¦¯à¦¾ হয়েছে।", "alert.unexpected.title": "ওহো!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "পরেরবার আপনি {combo} চাপ দিলে à¦à¦Ÿà¦¾à¦° শেষে চলে যেতে পারবেন", "bundle_column_error.body": "à¦à¦‡ অংশটি দেখতে যেয়ে কোনো সমসà§à¦¯à¦¾ হয়েছে।", "bundle_column_error.retry": "আবার চেষà§à¦Ÿà¦¾ করà§à¦¨", "bundle_column_error.title": "নেটওয়ারà§à¦•ের সমসà§à¦¯à¦¾ হচà§à¦›à§‡", "bundle_modal_error.close": "বনà§à¦§ করà§à¦¨", - "bundle_modal_error.message": "à¦à¦‡ অংশটি দেখতে যেয়ে কোনো সমসà§à¦¯à¦¾ হয়েছে।", + "bundle_modal_error.message": "à¦à¦‡ অংশটি দেখাতে যেয়ে কোনো সমসà§à¦¯à¦¾ হয়েছে।", "bundle_modal_error.retry": "আবার চেষà§à¦Ÿà¦¾ করà§à¦¨", "column.blocks": "যাদের বনà§à¦§ করে রাখা হয়েছে", "column.community": "সà§à¦¥à¦¾à¦¨à§€à¦¯à¦¼ সময়সারি", "column.direct": "সরাসরি লেখা", + "column.directory": "Browse profiles", "column.domain_blocks": "সরিয়ে ফেলা ওয়েবসাইট", "column.favourites": "পছনà§à¦¦à§‡à¦° গà§à¦²à§‹", "column.follow_requests": "অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦®à¦¤à¦¿ চেয়েছে যারা", @@ -77,12 +84,12 @@ "compose_form.poll.remove_option": "à¦à¦‡ বিকলà§à¦ªà¦Ÿà¦¿ মà§à¦›à§‡ ফেলà§à¦¨", "compose_form.publish": "টà§à¦Ÿ", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "à¦à¦‡ ছবি বা à¦à¦¿à¦¡à¦¿à¦“টি সংবেদনশীল হিসেবে চিহà§à¦¨à¦¿à¦¤ করতে", "compose_form.sensitive.marked": "à¦à¦‡ ছবি বা à¦à¦¿à¦¡à¦¿à¦“টি সংবেদনশীল হিসেবে চিহà§à¦¨à¦¿à¦¤ করা হয়েছে", "compose_form.sensitive.unmarked": "à¦à¦‡ ছবি বা à¦à¦¿à¦¡à¦¿à¦“টি সংবেদনশীল হিসেবে চিহà§à¦¨à¦¿à¦¤ করা হয়নি", "compose_form.spoiler.marked": "লেখাটি সাবধানতার পেছনে লà§à¦•ানো আছে", "compose_form.spoiler.unmarked": "লেখাটি লà§à¦•ানো নেই", - "compose_form.spoiler_placeholder": "আপনার সাবধানতা à¦à¦–ানে লিখà§à¦¨", + "compose_form.spoiler_placeholder": "আপনার লেখা দেখার সাবধানবাণী লিখà§à¦¨", "confirmation_modal.cancel": "বাতিল করà§à¦¨", "confirmations.block.block_and_report": "বনà§à¦§ করà§à¦¨ à¦à¦¬à¦‚ রিপোরà§à¦Ÿ করà§à¦¨", "confirmations.block.confirm": "বনà§à¦§ করà§à¦¨", @@ -93,14 +100,25 @@ "confirmations.delete_list.message": "আপনি কি নিশà§à¦šà¦¿à¦¤ যে আপনি à¦à¦‡ তালিকাটি সà§à¦¥à¦¾à¦¯à¦¼à¦¿à¦à¦¾à¦¬à§‡ মà§à¦›à§‡ ফেলতে চান ?", "confirmations.domain_block.confirm": "à¦à¦‡ ওয়েবসাইট থেকে সব সরান", "confirmations.domain_block.message": "আপনি কি সতà§à¦¯à¦¿ সতà§à¦¯à¦¿ নিশà§à¦šà¦¿à¦¤ যে {domain} ওয়েবসাইট থেকে সব সরাতে চান ? সাধারণত কিছৠলকà§à¦·à§à¦¯à¦¬à¦¸à§à¦¤à§ বনà§à¦§ à¦à¦¬à¦‚ সরানোযা যথেষà§à¦Ÿà¥¤ নিশà§à¦šà¦¿à¦¤ করলে ওই ওয়েবসাইট থেকে কোনোকিছৠকোনখানে দেখবেন না। যারা আপনাকে অনà§à¦¸à¦°à¦£ করে ওই ওয়েবসাইট থেকে তাদেরকেও মà§à¦›à§‡ ফেলা হবে।", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "সরিয়ে ফেলà§à¦¨", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "আপনি কি নিশà§à¦šà¦¿à¦¤ {name} সরিয়ে ফেলতে চান ?", "confirmations.redraft.confirm": "মà§à¦›à§‡ ফেলà§à¦¨ à¦à¦¬à¦‚ আবার সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨", "confirmations.redraft.message": "আপনি কি নিশà§à¦šà¦¿à¦¤ à¦à¦Ÿà¦¿ মà§à¦›à§‡ ফেলে à¦à¦¬à¦‚ আবার সমà§à¦ªà¦¾à¦¦à¦¨ করতে চান ? à¦à¦Ÿà¦¾à¦¤à§‡ যা পছনà§à¦¦à¦¿à¦¤, সমরà§à¦¥à¦¨ বা মতামত আছে সেগà§à¦²à§‹ নতà§à¦¨ লেখার সাথে যà§à¦•à§à¦¤ থাকবে না।", "confirmations.reply.confirm": "মতামত", "confirmations.reply.message": "à¦à¦–ন মতামত লিখতে গেলে আপনার à¦à¦–ন যেটা লিখছেন সেটা মà§à¦›à§‡ যাবে। আপনি নি নিশà§à¦šà¦¿à¦¤ à¦à¦Ÿà¦¾ করতে চান ?", - "confirmations.unfollow.confirm": "অনà§à¦¸à¦°à¦£ বনà§à¦§ করà§à¦¨", + "confirmations.unfollow.confirm": "অনà§à¦¸à¦°à¦£ করা বাতিল করতে", "confirmations.unfollow.message": "আপনি কি নিশà§à¦šà¦¿à¦¤ {name} কে আর অনà§à¦¸à¦°à¦£ করতে চান না ?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "à¦à¦‡ লেখাটি আপনার ওয়েবসাইটে যà§à¦•à§à¦¤ করতে নিচের কোডটি বেবহার করà§à¦¨à¥¤", "embed.preview": "সেটা দেখতে à¦à¦°à¦•ম হবে:", "emoji_button.activity": "কারà§à¦¯à¦•লাপ", @@ -134,6 +152,10 @@ "empty_column.mutes": "আপনি à¦à¦–নো কোনো বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীকে সরাননি।", "empty_column.notifications": "আপনার à¦à¦–নো কোনো পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨ নেই। কথোপকথন শà§à¦°à§ করতে, অনà§à¦¯à¦¦à§‡à¦° সাথে মেলামেশা করতে পারেন।", "empty_column.public": "à¦à¦–ানে à¦à¦–নো কিছৠনেই! পà§à¦°à¦•াশà§à¦¯ à¦à¦¾à¦¬à§‡ কিছৠলিখà§à¦¨ বা অনà§à¦¯ সারà§à¦à¦¾à¦° থেকে কাওকে অনà§à¦¸à¦°à¦£ করে à¦à¦‡ জায়গা à¦à¦°à§‡ ফেলà§à¦¨", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,11 +164,11 @@ "follow_request.authorize": "অনà§à¦®à¦¤à¦¿ দিন", "follow_request.reject": "পà§à¦°à¦¤à§à¦¯à¦¾à¦–à§à¦¯à¦¾à¦¨ করà§à¦¨", "getting_started.developers": "তৈরিকারকদের জনà§à¦¯", - "getting_started.directory": "নিজসà§à¦¬ পাতার তালিকা", + "getting_started.directory": "নিজসà§à¦¬-পাতাগà§à¦²à¦¿à¦° তালিকা", "getting_started.documentation": "নথিপতà§à¦°", "getting_started.heading": "শà§à¦°à§ করা", "getting_started.invite": "অনà§à¦¯à¦¦à§‡à¦° আমনà§à¦¤à§à¦°à¦£ করà§à¦¨", - "getting_started.open_source_notice": "মাসà§à¦Ÿà¦¾à¦¡à¦¨ à¦à¦•টি মà§à¦•à§à¦¤ সফটওয়à§à¦¯à¦¾à¦°à¥¤ আপনি তৈরিতে সাহাযà§à¦¯ করতে পারেন অথবা সমসà§à¦¯à¦¾ রিপোরà§à¦Ÿ করতে পারেন গিটহাবে {github}।", + "getting_started.open_source_notice": "মাসà§à¦Ÿà¦¾à¦¡à¦¨ à¦à¦•টি মà§à¦•à§à¦¤ সফটওয়à§à¦¯à¦¾à¦°à¥¤ তৈরিতে সাহাযà§à¦¯ করতে বা কোনো সমসà§à¦¯à¦¾ সমà§à¦ªà¦°à§à¦•ে জানাতে আমাদের গিটহাবে যেতে পারেন {github}।", "getting_started.security": "নিরাপতà§à¦¤à¦¾", "getting_started.terms": "বà§à¦¯à¦¬à¦¹à¦¾à¦°à§‡à¦° নিয়মাবলী", "hashtag.column_header.tag_mode.all": "à¦à¦¬à¦‚ {additional}", @@ -157,7 +179,7 @@ "hashtag.column_settings.tag_mode.all": "à¦à¦—à§à¦²à§‹ সব", "hashtag.column_settings.tag_mode.any": "à¦à¦° à¦à§‡à¦¤à¦°à§‡ যেকোনোটা", "hashtag.column_settings.tag_mode.none": "à¦à¦—à§à¦²à§‹à¦° à¦à¦•টাও না", - "hashtag.column_settings.tag_toggle": "আরো টà§à¦¯à¦¾à¦— à¦à¦‡ কলামে যà§à¦•à§à¦¤ করà§à¦¨", + "hashtag.column_settings.tag_toggle": "আরো টà§à¦¯à¦¾à¦— à¦à¦‡ কলামে যà§à¦•à§à¦¤ করতে", "home.column_settings.basic": "সাধারণ", "home.column_settings.show_reblogs": "সমরà§à¦¥à¦¨à¦—à§à¦²à§‹ দেখান", "home.column_settings.show_replies": "মতামত দেখান", @@ -200,7 +222,7 @@ "keyboard_shortcuts.local": "সà§à¦¥à¦¾à¦¨à§€à¦¯à¦¼ সময়রেখাতে যেতে", "keyboard_shortcuts.mention": "লেখককে উলà§à¦²à§‡à¦– করতে", "keyboard_shortcuts.muted": "বনà§à¦§ করা বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের তালিকা খà§à¦²à¦¤à§‡", - "keyboard_shortcuts.my_profile": "নিজের পাতা দেখতে", + "keyboard_shortcuts.my_profile": "আপনার নিজের পাতা দেখতে", "keyboard_shortcuts.notifications": "পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à§‡à¦° কলাম খà§à¦²à¦¤à§‡", "keyboard_shortcuts.pinned": "পিন দেওয়া টà§à¦Ÿà§‡à¦° তালিকা খà§à¦²à¦¤à§‡", "keyboard_shortcuts.profile": "লেখকের পাতা দেখতে", @@ -209,14 +231,14 @@ "keyboard_shortcuts.search": "খোà¦à¦œà¦¾à¦° অংশে ফোকাস করতে", "keyboard_shortcuts.start": "\"পà§à¦°à¦¥à¦® শà§à¦°à§à¦°\" কলাম বের করতে", "keyboard_shortcuts.toggle_hidden": "CW লেখা দেখতে বা লà§à¦•াতে", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "à¦à¦¿à¦¡à¦¿à¦“/ছবি দেখতে বা বনà§à¦§ করতে", "keyboard_shortcuts.toot": "নতà§à¦¨ à¦à¦•টা টà§à¦Ÿ লেখা শà§à¦°à§ করতে", "keyboard_shortcuts.unfocus": "লেখা বা খোà¦à¦œà¦¾à¦° জায়গায় ফোকাস না করতে", "keyboard_shortcuts.up": "তালিকার উপরের দিকে যেতে", "lightbox.close": "বনà§à¦§", "lightbox.next": "পরবরà§à¦¤à§€", "lightbox.previous": "পূরà§à¦¬à¦¬à¦°à§à¦¤à§€", - "lightbox.view_context": "View context", + "lightbox.view_context": "পà§à¦°à¦¸à¦™à§à¦—টি দেখতে", "lists.account.add": "তালিকাতে যà§à¦•à§à¦¤ করতে", "lists.account.remove": "তালিকা থেকে বাদ দিতে", "lists.delete": "তালিকা মà§à¦›à§‡ ফেলতে", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "তালিকার নতà§à¦¨ শিরোনাম দিতে", "lists.search": "যাদের অনà§à¦¸à¦°à¦£ করেন তাদের à¦à§‡à¦¤à¦°à§‡ খà§à¦à¦œà§à¦¨", "lists.subheading": "আপনার তালিকা", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "আসছে...", "media_gallery.toggle_visible": "দৃশà§à¦¯à¦¤à¦¾à¦° অবসà§à¦¥à¦¾ বদলান", "missing_indicator.label": "খà§à¦à¦œà§‡ পাওয়া যায়নি", @@ -235,14 +258,14 @@ "navigation_bar.blocks": "বনà§à¦§ করা বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারী", "navigation_bar.community_timeline": "সà§à¦¥à¦¾à¦¨à§€à¦¯à¦¼ সময়রেখা", "navigation_bar.compose": "নতà§à¦¨ টà§à¦Ÿ লিখà§à¦¨", - "navigation_bar.direct": "সরাসরি লেখা", + "navigation_bar.direct": "সরাসরি লেখাগà§à¦²à¦¿", "navigation_bar.discover": "ঘà§à¦°à§‡ দেখà§à¦¨", "navigation_bar.domain_blocks": "বনà§à¦§ করা ওয়েবসাইট", - "navigation_bar.edit_profile": "নিজের পাতা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨", + "navigation_bar.edit_profile": "নিজের পাতা সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করতে", "navigation_bar.favourites": "পছনà§à¦¦à§‡à¦°", "navigation_bar.filters": "বনà§à¦§ করা শবà§à¦¦", "navigation_bar.follow_requests": "অনà§à¦¸à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦—à§à¦²à¦¿", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "যাদেরকে অনà§à¦¸à¦°à¦£ করেন à¦à¦¬à¦‚ যারা তাকে অনà§à¦¸à¦°à¦£ করে", "navigation_bar.info": "à¦à¦‡ সারà§à¦à¦¾à¦° সমà§à¦ªà¦°à§à¦•ে", "navigation_bar.keyboard_shortcuts": "হটকীগà§à¦²à¦¿", "navigation_bar.lists": "তালিকাগà§à¦²à§‹", @@ -251,7 +274,6 @@ "navigation_bar.personal": "নিজসà§à¦¬", "navigation_bar.pins": "পিন দেওয়া টà§à¦Ÿ", "navigation_bar.preferences": "পছনà§à¦¦à¦¸à¦®à§‚হ", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "যà§à¦•à§à¦¤à¦¬à¦¿à¦¶à§à¦¬à§‡à¦° সময়রেখা", "navigation_bar.security": "নিরাপতà§à¦¤à¦¾", "notification.favourite": "{name} আপনার কারà§à¦¯à¦•à§à¦°à¦® পছনà§à¦¦ করেছেন", @@ -261,18 +283,18 @@ "notification.reblog": "{name} আপনার কারà§à¦¯à¦•à§à¦°à¦®à§‡ সমরà§à¦¥à¦¨ দেখিয়েছেন", "notifications.clear": "পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à¦—à§à¦²à§‹ মà§à¦›à§‡ ফেলতে", "notifications.clear_confirmation": "আপনি কি নিরà§à¦šà¦¿à¦¤ পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à¦—à§à¦²à§‹ মà§à¦›à§‡ ফেলতে চান ?", - "notifications.column_settings.alert": "কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡ পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨", + "notifications.column_settings.alert": "কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡ পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à¦—à§à¦²à¦¿", "notifications.column_settings.favourite": "পছনà§à¦¦à§‡à¦°:", - "notifications.column_settings.filter_bar.advanced": "সব শà§à¦°à§‡à¦£à§€à¦—à§à¦²à§‹ দেখতে", - "notifications.column_settings.filter_bar.category": "দà§à¦°à§à¦¤ ছাà¦à¦•নি বার", - "notifications.column_settings.filter_bar.show": "দেখতে", + "notifications.column_settings.filter_bar.advanced": "সব শà§à¦°à§‡à¦£à§€à¦—à§à¦²à§‹ দেখানো", + "notifications.column_settings.filter_bar.category": "সংকà§à¦·à¦¿à¦ªà§à¦¤ ছাà¦à¦•নি অংশ", + "notifications.column_settings.filter_bar.show": "দেখানো", "notifications.column_settings.follow": "নতà§à¦¨ অনà§à¦¸à¦°à¦£à¦•ারীরা:", "notifications.column_settings.mention": "পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à¦—à§à¦²à§‹:", "notifications.column_settings.poll": "নিরà§à¦¬à¦¾à¦šà¦¨à§‡à¦° ফলাফল:", - "notifications.column_settings.push": "পà§à¦¶ পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨", + "notifications.column_settings.push": "পà§à¦¶ পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨à¦—à§à¦²à¦¿", "notifications.column_settings.reblog": "সমরà§à¦¥à¦¨à¦—à§à¦²à§‹:", - "notifications.column_settings.show": "কলামে দেখান", - "notifications.column_settings.sound": "শবà§à¦¦ বাজাতে", + "notifications.column_settings.show": "কলামে দেখানো", + "notifications.column_settings.sound": "শবà§à¦¦ বাজানো", "notifications.filter.all": "সব", "notifications.filter.boosts": "সমরà§à¦¥à¦¨à¦—à§à¦²à§‹", "notifications.filter.favourites": "পছনà§à¦¦à§‡à¦° গà§à¦²à§‹", @@ -281,9 +303,11 @@ "notifications.filter.polls": "নিরà§à¦¬à¦¾à¦šà¦¨à§‡à¦° ফলাফল", "notifications.group": "{count} পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨", "poll.closed": "বনà§à¦§", - "poll.refresh": "আবার সতেজ করতে", + "poll.refresh": "বদলেছে কিনা দেখতে", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# à¦à§‹à¦Ÿ} other {# à¦à§‹à¦Ÿ}}", "poll.vote": "à¦à§‹à¦Ÿ", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "à¦à¦•টা নিরà§à¦¬à¦¾à¦šà¦¨ যোগ করতে", "poll_button.remove_poll": "নিরà§à¦¬à¦¾à¦šà¦¨ বাদ দিতে", "privacy.change": "লেখার গোপনীয়তা অবসà§à¦¥à¦¾ ঠিক করতে", @@ -295,6 +319,7 @@ "privacy.public.short": "সরà§à¦¬à¦œà¦¨à§€à¦¨ পà§à¦°à¦•াশà§à¦¯", "privacy.unlisted.long": "সরà§à¦¬à¦œà¦¨à§€à¦¨ পà§à¦°à¦•াশà§à¦¯ সময়রেখাতে না দেখাতে", "privacy.unlisted.short": "পà§à¦°à¦•াশà§à¦¯ নয়", + "refresh": "Refresh", "regeneration_indicator.label": "আসছে…", "regeneration_indicator.sublabel": "আপনার বাড়ির-সময়রেখা পà§à¦°à¦¸à§à¦¤à§‚ত করা হচà§à¦›à§‡!", "relative_time.days": "{number} দিন", @@ -319,6 +344,7 @@ "search_results.accounts": "মানà§à¦·", "search_results.hashtags": "হà§à¦¯à¦¾à¦¶à¦Ÿà§à¦¯à¦¾à¦—গà§à¦²à¦¿", "search_results.statuses": "টà§à¦Ÿ", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {ফলাফল} other {ফলাফল}}", "status.admin_account": "@{name} র জনà§à¦¯ পরিচালনার ইনà§à¦Ÿà¦¾à¦°à¦«à§‡à¦¸à§‡ ঢà§à¦•à§à¦¨", "status.admin_status": "যায় লেখাটি পরিচালনার ইনà§à¦Ÿà¦¾à¦°à¦«à§‡à¦¸à§‡ খà§à¦²à§à¦¨", @@ -328,7 +354,7 @@ "status.copy": "লেখাটির লিংক কপি করতে", "status.delete": "মà§à¦›à§‡ ফেলতে", "status.detailed_status": "বিসà§à¦¤à¦¾à¦°à¦¿à¦¤ কথোপকথনের হিসেবে দেখতে", - "status.direct": "@{name} কে সরাসরি পাঠান", + "status.direct": "@{name} কে সরাসরি লেখা পাঠাতে", "status.embed": "à¦à¦®à¦¬à§‡à¦¡ করতে", "status.favourite": "পছনà§à¦¦à§‡à¦° করতে", "status.filtered": "ছাà¦à¦•নিদিত", @@ -350,7 +376,7 @@ "status.redraft": "মà§à¦›à§‡ আবার নতà§à¦¨ করে লিখতে", "status.reply": "মতামত জানাতে", "status.replyAll": "লেখাযà§à¦•à§à¦¤ সবার কাছে মতামত জানাতে", - "status.report": "@{name}কে রিপোরà§à¦Ÿ করতে", + "status.report": "@{name} কে রিপোরà§à¦Ÿ করতে", "status.sensitive_warning": "সংবেদনশীল কিছà§", "status.share": "অনà§à¦¯à¦¦à§‡à¦° জানান", "status.show_less": "কম দেখতে", @@ -358,9 +384,10 @@ "status.show_more": "আরো দেখাতে", "status.show_more_all": "সবগà§à¦²à§‹à¦¤à§‡ আরো দেখতে", "status.show_thread": "আলোচনা দেখতে", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "আলোচনার পà§à¦°à¦œà§à¦žà¦¾à¦ªà¦¨ চালৠকরতে", "status.unpin": "নিজের পাতা থেকে পিন করে রাখাটির পিন খà§à¦²à¦¤à§‡", - "suggestions.dismiss": "সাহাযà§à¦¯à§‡à¦° জনà§à¦¯ পরামরà§à¦¶à¦—à§à¦²à§‹ সরাতে", + "suggestions.dismiss": "সাহাযà§à¦¯à§‡à¦° পরামরà§à¦¶à¦—à§à¦²à§‹ সরাতে", "suggestions.header": "আপনি হয়তোবা à¦à¦—à§à¦²à§‹à¦¤à§‡ আগà§à¦°à¦¹à§€ হতে পারেন…", "tabs_bar.federated_timeline": "যà§à¦•à§à¦¤à¦¬à¦¿à¦¶à§à¦¬", "tabs_bar.home": "বাড়ি", @@ -373,14 +400,22 @@ "time_remaining.moments": "সময় বাকি আছে", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} বাকি আছে", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} কথা বলছে", + "trends.trending_now": "Trending now", "ui.beforeunload": "যে পরà§à¦¯à¦¨à§à¦¤ à¦à¦Ÿà¦¾ লেখা হয়েছে, মাসà§à¦Ÿà¦¾à¦¡à¦¨ থেকে চলে গেলে à¦à¦Ÿà¦¾ মà§à¦›à§‡ যাবে।", "upload_area.title": "টেনে à¦à¦–ানে ছেড়ে দিলে à¦à¦–ানে যà§à¦•à§à¦¤ করা যাবে", - "upload_button.label": "ছবি বা à¦à¦¿à¦¡à¦¿à¦“ যà§à¦•à§à¦¤ করতে (à¦à¦¸à¦¬ ধরণের JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "ছবি বা à¦à¦¿à¦¡à¦¿à¦“ যà§à¦•à§à¦¤ করতে (à¦à¦¸à¦¬ ধরণের: JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "যা যà§à¦•à§à¦¤ করতে চাচà§à¦›à§‡à¦¨ সেটি বেশি বড়, à¦à¦–ানকার সরà§à¦¬à¦¾à¦§à¦¿à¦•ের মেমোরির উপরে চলে গেছে।", "upload_error.poll": "নিরà§à¦¬à¦¾à¦šà¦¨à¦•à§à¦·à§‡à¦¤à§à¦°à§‡ কোনো ফাইল যà§à¦•à§à¦¤ করা যাবেনা।", "upload_form.description": "যারা দেখতে পায়না তাদের জনà§à¦¯ à¦à¦Ÿà¦¾ বরà§à¦£à¦¨à¦¾ করতে", - "upload_form.focus": "সাধারণ দেখাটি পরিবরà§à¦¤à¦¨ করতে", + "upload_form.edit": "Edit", "upload_form.undo": "মà§à¦›à§‡ ফেলতে", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "যà§à¦•à§à¦¤ করতে পাঠানো হচà§à¦›à§‡...", "video.close": "à¦à¦¿à¦¡à¦¿à¦“টি বনà§à¦§ করতে", "video.exit_fullscreen": "পূরà§à¦£ পরà§à¦¦à¦¾ থেকে বাইরে বের হতে", diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json new file mode 100644 index 0000000000000000000000000000000000000000..db07f1ababed1973e5754c9e3595031409886abc --- /dev/null +++ b/app/javascript/mastodon/locales/br.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Ouzhpenn pe lemel ag ar listennadoù", + "account.badges.bot": "Robot", + "account.block": "Stankañ @{name}", + "account.block_domain": "Kuzh kement tra a {domain}", + "account.blocked": "Stanket", + "account.cancel_follow_request": "Nullañ ar pedad heuliañ", + "account.direct": "Kas ur c'hemennad da @{name}", + "account.domain_blocked": "Domani kuzhet", + "account.edit_profile": "Aozañ ar profil", + "account.endorse": "Lakaat war-wel war ar profil", + "account.follow": "Heuliañ", + "account.followers": "Heilour·ezed·ion", + "account.followers.empty": "Den na heul an implijour-mañ c'hoazh.", + "account.follows": "Koumanantoù", + "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows_you": "Ho heul", + "account.hide_reblogs": "Kuzh toudoù skignet gant @{name}", + "account.last_status": "Oberiantiz zivezhañ", + "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.media": "Media", + "account.mention": "Menegiñ @{name}", + "account.moved_to": "Dilojet en·he deus {name} da:", + "account.mute": "Kuzhat @{name}", + "account.mute_notifications": "Kuzh kemennoù a @{name}", + "account.muted": "Kuzhet", + "account.never_active": "Birviken", + "account.posts": "Toudoù", + "account.posts_with_replies": "Toudoù ha respontoù", + "account.report": "Disklêriañ @{name}", + "account.requested": "Awaiting approval", + "account.share": "Skignañ profil @{name}", + "account.show_reblogs": "Diskouez toudoù a @{name}", + "account.unblock": "Distankañ @{name}", + "account.unblock_domain": "Diguzh {domain}", + "account.unendorse": "Don't feature on profile", + "account.unfollow": "Diheuliañ", + "account.unmute": "Diguzhat @{name}", + "account.unmute_notifications": "Diguzhat kemennoù a @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", + "alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.", + "alert.unexpected.title": "C'hem !", + "autosuggest_hashtag.per_week": "{count} bep sizhun", + "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Klask endro", + "bundle_column_error.title": "Fazi rouedad", + "bundle_modal_error.close": "Serriñ", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Klask endro", + "column.blocks": "Implijour·ezed·ion stanket", + "column.community": "Red-amzer lec'hel", + "column.direct": "Kemennadoù prevez", + "column.directory": "Mont a-dreuz ar profiloù", + "column.domain_blocks": "Domani kuzhet", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Home", + "column.lists": "Lists", + "column.mutes": "Muted users", + "column.notifications": "Notifications", + "column.pins": "Pinned toot", + "column.public": "Federated timeline", + "column_back_button.label": "Back", + "column_header.hide_settings": "Hide settings", + "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveRight_settings": "Move column to the right", + "column_header.pin": "Pin", + "column_header.show_settings": "Show settings", + "column_header.unpin": "Unpin", + "column_subheading.settings": "Settings", + "community.column_settings.media_only": "Media only", + "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.", + "compose_form.direct_message_warning_learn_more": "Learn more", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", + "compose_form.lock_disclaimer.lock": "locked", + "compose_form.placeholder": "What is on your mind?", + "compose_form.poll.add_option": "Add a choice", + "compose_form.poll.duration": "Poll duration", + "compose_form.poll.option_placeholder": "Choice {number}", + "compose_form.poll.remove_option": "Remove this choice", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.marked": "Media is marked as sensitive", + "compose_form.sensitive.unmarked": "Media is not marked as sensitive", + "compose_form.spoiler.marked": "Text is hidden behind warning", + "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler_placeholder": "Write your warning here", + "confirmation_modal.cancel": "Cancel", + "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.confirm": "Block", + "confirmations.block.message": "Are you sure you want to block {name}?", + "confirmations.delete.confirm": "Delete", + "confirmations.delete.message": "Are you sure you want to delete this status?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", + "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.redraft.confirm": "Delete & redraft", + "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", + "confirmations.reply.confirm": "Reply", + "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Here is what it will look like:", + "emoji_button.activity": "Activity", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flags", + "emoji_button.food": "Food & Drink", + "emoji_button.label": "Insert emoji", + "emoji_button.nature": "Nature", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objects", + "emoji_button.people": "People", + "emoji_button.recent": "Frequently used", + "emoji_button.search": "Search...", + "emoji_button.search_results": "Search results", + "emoji_button.symbols": "Symbols", + "emoji_button.travel": "Travel & Places", + "empty_column.account_timeline": "No toots here!", + "empty_column.account_unavailable": "Profile unavailable", + "empty_column.blocks": "You haven't blocked any users yet.", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.domain_blocks": "There are no hidden domains yet.", + "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", + "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", + "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", + "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Reject", + "getting_started.developers": "Developers", + "getting_started.directory": "Profile directory", + "getting_started.documentation": "Documentation", + "getting_started.heading": "Getting started", + "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.security": "Security", + "getting_started.terms": "Terms of service", + "hashtag.column_header.tag_mode.all": "and {additional}", + "hashtag.column_header.tag_mode.any": "or {additional}", + "hashtag.column_header.tag_mode.none": "without {additional}", + "hashtag.column_settings.select.no_options_message": "No suggestions found", + "hashtag.column_settings.select.placeholder": "Enter hashtags…", + "hashtag.column_settings.tag_mode.all": "All of these", + "hashtag.column_settings.tag_mode.any": "Any of these", + "hashtag.column_settings.tag_mode.none": "None of these", + "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "home.column_settings.basic": "Basic", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "intervals.full.days": "{number, plural, one {# day} other {# days}}", + "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", + "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "introduction.federation.action": "Next", + "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.home.headline": "Home", + "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.local.headline": "Local", + "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.interactions.action": "Finish toot-orial!", + "introduction.interactions.favourite.headline": "Favourite", + "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.reblog.headline": "Boost", + "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reply.headline": "Reply", + "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.welcome.action": "Let's go!", + "introduction.welcome.headline": "First steps", + "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.favourites": "to open favourites list", + "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.local": "to open local timeline", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.muted": "to open muted users list", + "keyboard_shortcuts.my_profile": "to open your profile", + "keyboard_shortcuts.notifications": "to open notifications column", + "keyboard_shortcuts.pinned": "to open pinned toots list", + "keyboard_shortcuts.profile": "to open author's profile", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.start": "to open \"get started\" column", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Close", + "lightbox.next": "Next", + "lightbox.previous": "Previous", + "lightbox.view_context": "View context", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.edit.submit": "Change title", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "missing_indicator.sublabel": "This resource could not be found", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.apps": "Mobile apps", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Direct messages", + "navigation_bar.discover": "Discover", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.filters": "Muted words", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.info": "About this server", + "navigation_bar.keyboard_shortcuts": "Hotkeys", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Logout", + "navigation_bar.mutes": "Muted users", + "navigation_bar.personal": "Personal", + "navigation_bar.pins": "Pinned toots", + "navigation_bar.preferences": "Preferences", + "navigation_bar.public_timeline": "Federated timeline", + "navigation_bar.security": "Security", + "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", + "notification.mention": "{name} mentioned you", + "notification.poll": "A poll you have voted in has ended", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Clear notifications", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.filter_bar.advanced": "Display all categories", + "notifications.column_settings.filter_bar.category": "Quick filter bar", + "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.follow": "New followers:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "notifications.filter.all": "All", + "notifications.filter.boosts": "Boosts", + "notifications.filter.favourites": "Favourites", + "notifications.filter.follows": "Follows", + "notifications.filter.mentions": "Mentions", + "notifications.filter.polls": "Poll results", + "notifications.group": "{count} notifications", + "poll.closed": "Closed", + "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.vote": "Vote", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Add a poll", + "poll_button.remove_poll": "Remove poll", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "status.admin_account": "Open moderation interface for @{name}", + "status.admin_status": "Open this status in the moderation interface", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.copy": "Copy link to status", + "status.delete": "Delete", + "status.detailed_status": "Detailed conversation view", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.filtered": "Filtered", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.read_more": "Read more", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", + "status.redraft": "Delete & re-draft", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "suggestions.dismiss": "Dismiss suggestion", + "suggestions.header": "You might be interested in…", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "time_remaining.days": "{number, plural, one {# day} other {# days}} left", + "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", + "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.moments": "Moments remaining", + "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media ({formats})", + "upload_error.limit": "File upload limit exceeded.", + "upload_error.poll": "File upload not allowed with polls.", + "upload_form.description": "Describe for the visually impaired", + "upload_form.edit": "Edit", + "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 5f76bed2b46496aa272c6d12c550aff1f29028a3..e6d1bba1d3e3dc50ec28816a6daceae502474946 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -4,6 +4,7 @@ "account.block": "Bloqueja @{name}", "account.block_domain": "Amaga-ho tot de {domain}", "account.blocked": "Bloquejat", + "account.cancel_follow_request": "Anul·la la sol·licitud de seguiment", "account.direct": "Missatge directe @{name}", "account.domain_blocked": "Domini ocult", "account.edit_profile": "Editar el perfil", @@ -15,6 +16,7 @@ "account.follows.empty": "Aquest usuari encara no segueix a ningú.", "account.follows_you": "Et segueix", "account.hide_reblogs": "Amaga els impulsos de @{name}", + "account.last_status": "Darrer actiu", "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.media": "Mèdia", @@ -23,6 +25,7 @@ "account.mute": "Silencia @{name}", "account.mute_notifications": "Notificacions desactivades de @{name}", "account.muted": "Silenciat", + "account.never_active": "Mai", "account.posts": "Toots", "account.posts_with_replies": "Toots i respostes", "account.report": "Informe @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Deixa de seguir", "account.unmute": "Treure silenci de @{name}", "account.unmute_notifications": "Activar notificacions de @{name}", + "alert.rate_limited.message": "Si us plau torna-ho a provar després de {retry_time, time, medium}.", + "alert.rate_limited.title": "LÃmit de freqüència", "alert.unexpected.message": "S'ha produït un error inesperat.", "alert.unexpected.title": "Vaja!", + "autosuggest_hashtag.per_week": "{count} per setmana", "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop", "bundle_column_error.body": "S'ha produït un error en carregar aquest component.", "bundle_column_error.retry": "Torna-ho a provar", @@ -47,6 +53,7 @@ "column.blocks": "Usuaris bloquejats", "column.community": "LÃnia de temps local", "column.direct": "Missatges directes", + "column.directory": "Navega els perfils", "column.domain_blocks": "Dominis ocults", "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Està s segur que vols suprimir permanentment aquesta llista?", "confirmations.domain_block.confirm": "Amaga tot el domini", "confirmations.domain_block.message": "Està s segur, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar uns pocs objectius és suficient i preferible. No veurà s contingut d’aquest domini en cap de les lÃnies públiques ni en les notificacions. Els teus seguidors d’aquest domini seran eliminats.", + "confirmations.logout.confirm": "Tancar sessió", + "confirmations.logout.message": "Segur que vols tancar la sessió?", "confirmations.mute.confirm": "Silencia", + "confirmations.mute.explanation": "Això amagarà les seves publicacions i les que els mencionen però encara els permetrà veure les teves i seguir-te.", "confirmations.mute.message": "Està s segur que vols silenciar {name}?", "confirmations.redraft.confirm": "Esborrar i refer", "confirmations.redraft.message": "Està s segur que vols esborrar aquest toot i tornar a redactar-lo? Perderà s totes els impulsos i favorits, i les respostes al toot original es quedaran orfes.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Responen ara es sobreescriurà el missatge que està s editant. Està s segur que vols continuar?", "confirmations.unfollow.confirm": "Deixa de seguir", "confirmations.unfollow.message": "Està s segur que vols deixar de seguir {name}?", + "conversation.delete": "Elimina la conversa", + "conversation.mark_as_read": "Marca com a llegida", + "conversation.open": "Veure conversa", + "conversation.with": "Amb {names}", + "directory.federated": "Del fedivers conegut", + "directory.local": "Només de {domain}", + "directory.new_arrivals": "Arribades noves", + "directory.recently_active": "Recentment actius", "embed.instructions": "Incrusta aquest toot al lloc web copiant el codi a continuació.", "embed.preview": "Aquà tenim quin aspecte tindrá:", "emoji_button.activity": "Activitat", @@ -134,6 +152,10 @@ "empty_column.mutes": "Encara no has silenciat cap usuari.", "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.", "empty_column.public": "No hi ha res aquÃ! Escriu públicament alguna cosa o manualment segueix usuaris d'altres servidors per omplir-ho", + "error.unexpected_crash.explanation": "A causa d'un bug en el nostre codi o un problema de compatibilitat del navegador, aquesta pà gina no podria ser mostrada correctament.", + "error.unexpected_crash.next_steps": "Prova recarregant la pà gina. Si això no ajuda encara pots ser capaç d'utilitzar Mastodont a través d'un navegador diferent o app nativa.", + "errors.unexpected_crash.copy_stacktrace": "Còpia stacktrace al porta-retalls", + "errors.unexpected_crash.report_issue": "Informa d'un problema", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Nova llista", "lists.search": "Cercar entre les persones que segueixes", "lists.subheading": "Les teves llistes", + "load_pending": "{count, plural, one {# element nou} other {# elements nous}}", "loading_indicator.label": "Carregant...", "media_gallery.toggle_visible": "Alternar visibilitat", "missing_indicator.label": "No trobat", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Toots fixats", "navigation_bar.preferences": "Preferències", - "navigation_bar.profile_directory": "Directori de perfils", "navigation_bar.public_timeline": "LÃnia de temps federada", "navigation_bar.security": "Seguretat", "notification.favourite": "{name} ha afavorit el teu estat", @@ -282,8 +304,10 @@ "notifications.group": "{count} notificacions", "poll.closed": "Finalitzada", "poll.refresh": "Actualitza", + "poll.total_people": "{count, plural, one {# persona} other {# persones}}", "poll.total_votes": "{count, plural, one {# vot} other {# vots}}", "poll.vote": "Vota", + "poll.voted": "Vas votar per aquesta resposta", "poll_button.add_poll": "Afegeix una enquesta", "poll_button.remove_poll": "Elimina l'enquesta", "privacy.change": "Ajusta l'estat de privacitat", @@ -295,6 +319,7 @@ "privacy.public.short": "Públic", "privacy.unlisted.long": "No publicar en lÃnies de temps públiques", "privacy.unlisted.short": "No llistat", + "refresh": "Actualitza", "regeneration_indicator.label": "Carregant…", "regeneration_indicator.sublabel": "S'està preparant la lÃnia de temps Inici!", "relative_time.days": "fa {number} dies", @@ -319,6 +344,7 @@ "search_results.accounts": "Gent", "search_results.hashtags": "Etiquetes", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "La cerca de toots pel seu contingut no està habilitada en aquest servidor Mastodon.", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "status.admin_account": "Obre l'interfÃcie de moderació per a @{name}", "status.admin_status": "Obre aquest toot a la interfÃcie de moderació", @@ -358,6 +384,7 @@ "status.show_more": "Mostra més", "status.show_more_all": "Mostra més per a tot", "status.show_thread": "Mostra el fil", + "status.uncached_media_warning": "No està disponible", "status.unmute_conversation": "Activar conversació", "status.unpin": "Deslliga del perfil", "suggestions.dismiss": "Descartar suggeriment", @@ -372,15 +399,23 @@ "time_remaining.minutes": "{number, plural, one {# minut} other {# minuts}} restants", "time_remaining.moments": "Moments restants", "time_remaining.seconds": "{number, plural, one {# segon} other {# segons}} restants", - "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {gent}} talking", + "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {persones}} parlant-hi", + "trends.trending_now": "Ara en tendència", "ui.beforeunload": "El teu esborrany es perdrà si surts de Mastodon.", "upload_area.title": "Arrossega i deixa anar per a carregar", "upload_button.label": "Afegir multimèdia (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "S'ha superat el lÃmit de cà rrega d'arxius.", "upload_error.poll": "No es permet l'enviament de fitxers en les enquestes.", "upload_form.description": "Descriure els problemes visuals", - "upload_form.focus": "Modificar la previsualització", + "upload_form.edit": "Edita", "upload_form.undo": "Esborra", + "upload_modal.analyzing_picture": "Analitzant imatge…", + "upload_modal.apply": "Aplica", + "upload_modal.description_placeholder": "Uns salts rà pids de guineu marró sobre el gos gandul", + "upload_modal.detect_text": "Detecta el text de l'imatge", + "upload_modal.edit_media": "Editar multimèdia", + "upload_modal.hint": "Fes clic o arrossega el cercle en la previsualització per escollir el punt focal que sempre serà visible de totes les miniatures.", + "upload_modal.preview_label": "Previsualitza ({ratio})", "upload_progress.label": "Pujant...", "video.close": "Tancar el vÃdeo", "video.exit_fullscreen": "Sortir de pantalla completa", diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json index 369b8ce80ad214df08c172b8de649a1336774003..8321ffe5a365427718ce2ed08942f4fdd7cb62f3 100644 --- a/app/javascript/mastodon/locales/co.json +++ b/app/javascript/mastodon/locales/co.json @@ -1,9 +1,10 @@ { - "account.add_or_remove_from_list": "Aghjustà o toglie da e liste", + "account.add_or_remove_from_list": "Aghjunghje o toglie da e liste", "account.badges.bot": "Bot", "account.block": "Bluccà @{name}", "account.block_domain": "Piattà tuttu da {domain}", "account.blocked": "Bluccatu", + "account.cancel_follow_request": "Annullà a dumanda d'abbunamentu", "account.direct": "Missaghju direttu @{name}", "account.domain_blocked": "Duminiu piattatu", "account.edit_profile": "Mudificà u prufile", @@ -15,6 +16,7 @@ "account.follows.empty": "St'utilizatore ùn seguita nisunu.", "account.follows_you": "Vi seguita", "account.hide_reblogs": "Piattà spartere da @{name}", + "account.last_status": "Ultima attività ", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Piattà @{name}", "account.mute_notifications": "Piattà nutificazione da @{name}", "account.muted": "Piattatu", + "account.never_active": "Mai", "account.posts": "Statuti", "account.posts_with_replies": "Statuti è risposte", "account.report": "Palisà @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Ùn siguità più", "account.unmute": "Ùn piattà più @{name}", "account.unmute_notifications": "Ùn piattà più nutificazione da @{name}", + "alert.rate_limited.message": "Pruvate ancu dop'à {retry_time, time, medium}.", + "alert.rate_limited.title": "Ghjettu limitatu", "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.", "alert.unexpected.title": "Uups!", + "autosuggest_hashtag.per_week": "{count} per settimana", "boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta", "bundle_column_error.body": "C'hè statu un prublemu caricandu st'elementu.", "bundle_column_error.retry": "Pruvà torna", @@ -47,6 +53,7 @@ "column.blocks": "Utilizatori bluccati", "column.community": "Linea pubblica lucale", "column.direct": "Missaghji diretti", + "column.directory": "Percorre i prufili", "column.domain_blocks": "Duminii piattati", "column.favourites": "Favuriti", "column.follow_requests": "Dumande d'abbunamentu", @@ -71,7 +78,7 @@ "compose_form.lock_disclaimer": "U vostru contu ùn hè micca {locked}. Tuttu u mondu pò seguitavi è vede i vostri statuti privati.", "compose_form.lock_disclaimer.lock": "privatu", "compose_form.placeholder": "À chè pensate?", - "compose_form.poll.add_option": "Aghjustà una scelta", + "compose_form.poll.add_option": "Aghjunghje scelta", "compose_form.poll.duration": "Durata di u scandagliu", "compose_form.poll.option_placeholder": "Scelta {number}", "compose_form.poll.remove_option": "Toglie sta scelta", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Site sicuru·a che vulete supprime sta lista?", "confirmations.domain_block.confirm": "Piattà tuttu u duminiu", "confirmations.domain_block.message": "Site sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà . Ùn viderete più nunda da quallà indè e linee pubbliche o e nutificazione. I vostri abbunati da stu duminiu saranu tolti.", + "confirmations.logout.confirm": "Scunnettassi", + "confirmations.logout.message": "Site sicuru·a che vulete scunnettà vi?", "confirmations.mute.confirm": "Piattà ", + "confirmations.mute.explanation": "Quessu hà da piattà i statuti da sta persona è i posti chì a mintuvanu, mà ellu·a puderà sempre vede i vostri statuti è siguità vi.", "confirmations.mute.message": "Site sicuru·a che vulete piattà @{name}?", "confirmations.redraft.confirm": "Sguassà è riscrive", "confirmations.redraft.message": "Site sicuru·a chè vulete sguassà stu statutu è riscrivelu? I favuriti è spartere saranu persi, è e risposte diventeranu orfane.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Risponde avà sguasserà u missaghju chì scrivite. Site sicuru·a chì vulete cuntinuà ?", "confirmations.unfollow.confirm": "Disabbunassi", "confirmations.unfollow.message": "Site sicuru·a ch'ùn vulete più siguità @{name}?", + "conversation.delete": "Sguassà a cunversazione", + "conversation.mark_as_read": "Marcà cum'è lettu", + "conversation.open": "Vede a cunversazione", + "conversation.with": "Cù {names}", + "directory.federated": "Da u fediverse cunisciutu", + "directory.local": "Solu da {domain}", + "directory.new_arrivals": "Ultimi arrivi", + "directory.recently_active": "Attività ricente", "embed.instructions": "Integrà stu statutu à u vostru situ cù u codice quì sottu.", "embed.preview": "Assumiglierà à qualcosa cusì:", "emoji_button.activity": "Attività ", @@ -123,8 +141,8 @@ "empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!", "empty_column.direct": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.", "empty_column.domain_blocks": "Ùn c'hè manc'un duminiu bluccatu avà .", - "empty_column.favourited_statuses": "Ùn avete manc'unu statutu favuritu. Quandu aghjusterate unu à i vostri favuriti, sarà mustratu quì.", - "empty_column.favourites": "Nisunu hà aghjustatu stu statutu à i so favuriti. Quandu qualch'unu farà quessa, u so contu sarà mustratu quì.", + "empty_column.favourited_statuses": "Ùn avete manc'unu statutu favuritu. Quandu aghjunghjerate unu à i vostri favuriti, sarà mustratu quì.", + "empty_column.favourites": "Nisunu hà aghjuntu stu statutu à i so favuriti. Quandu qualch'unu farà quessa, u so contu sarà mustratu quì.", "empty_column.follow_requests": "Ùn avete manc'una dumanda d'abbunamentu. Quandu averete una, sarà mustrata quì.", "empty_column.hashtag": "Ùn c'hè ancu nunda quì.", "empty_column.home": "A vostr'accolta hè viota! Pudete andà nant'à {public} o pruvà a ricerca per truvà parsone da siguità .", @@ -134,6 +152,10 @@ "empty_column.mutes": "Per avà ùn avete manc'un utilizatore piattatu.", "empty_column.notifications": "Ùn avete ancu nisuna nutificazione. Interact with others to start the conversation.", "empty_column.public": "Ùn c'hè nunda quì! Scrivete qualcosa in pubblicu o seguitate utilizatori d'altri servori per empie a linea pubblica", + "error.unexpected_crash.explanation": "In ragione d'un bug indè u nostru codice o un prublemu di cumpatibilità cù quessu navigatore, sta pagina ùn hè micca pussuta esse affissata currettamente.", + "error.unexpected_crash.next_steps": "Pruvate d'attualizà sta pagina. S'ellu persiste u prublemu, pudete forse sempre accede à Mastodon dapoi un'alltru navigatore o applicazione.", + "errors.unexpected_crash.copy_stacktrace": "Cupià stacktrace nant'à u fermacarta", + "errors.unexpected_crash.report_issue": "Palisà prublemu", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -222,10 +244,11 @@ "lists.delete": "Supprime a lista", "lists.edit": "Mudificà a lista", "lists.edit.submit": "Cambià u titulu", - "lists.new.create": "Aghjustà una lista", + "lists.new.create": "Aghjunghje", "lists.new.title_placeholder": "Titulu di a lista", "lists.search": "Circà indè i vostr'abbunamenti", "lists.subheading": "E vo liste", + "load_pending": "{count, plural, one {# entrata nova} other {# entrate nove}}", "loading_indicator.label": "Caricamentu...", "media_gallery.toggle_visible": "Cambià a visibilità ", "missing_indicator.label": "Micca trovu", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Persunale", "navigation_bar.pins": "Statuti puntarulati", "navigation_bar.preferences": "Preferenze", - "navigation_bar.profile_directory": "Annuariu di i prufili", "navigation_bar.public_timeline": "Linea pubblica glubale", "navigation_bar.security": "Sicurità ", "notification.favourite": "{name} hà aghjuntu u vostru statutu à i so favuriti", @@ -282,9 +304,11 @@ "notifications.group": "{count} nutificazione", "poll.closed": "Chjosu", "poll.refresh": "Attualizà ", + "poll.total_people": "{count, plural, one {# persona} other {# persone}}", "poll.total_votes": "{count, plural, one {# votu} other {# voti}}", "poll.vote": "Vutà ", - "poll_button.add_poll": "Aghjustà un scandagliu", + "poll.voted": "Avete vutatu per sta risposta", + "poll_button.add_poll": "Aghjunghje", "poll_button.remove_poll": "Toglie u scandagliu", "privacy.change": "Mudificà a cunfidenzialità di u statutu", "privacy.direct.long": "Mandà solu à quelli chì so mintuvati", @@ -295,6 +319,7 @@ "privacy.public.short": "Pubblicu", "privacy.unlisted.long": "Ùn mette micca nant'à e linee pubbliche", "privacy.unlisted.short": "Micca listatu", + "refresh": "Attualizà ", "regeneration_indicator.label": "Caricamentu…", "regeneration_indicator.sublabel": "Priparazione di a vostra pagina d'accolta!", "relative_time.days": "{number}ghj", @@ -319,6 +344,7 @@ "search_results.accounts": "Ghjente", "search_results.hashtags": "Hashtag", "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.total": "{count, number} {count, plural, one {risultatu} other {risultati}}", "status.admin_account": "Apre l'interfaccia di muderazione per @{name}", "status.admin_status": "Apre stu statutu in l'interfaccia di muderazione", @@ -358,6 +384,7 @@ "status.show_more": "Slibrà ", "status.show_more_all": "Slibrà tuttu", "status.show_thread": "Vede u filu", + "status.uncached_media_warning": "Micca dispunibule", "status.unmute_conversation": "Ùn piattà più a cunversazione", "status.unpin": "Spuntarulà da u prufile", "suggestions.dismiss": "Righjittà a pruposta", @@ -373,14 +400,22 @@ "time_remaining.moments": "Ci fermanu qualchi mumentu", "time_remaining.seconds": "{number, plural, one {# siconda ferma} other {# siconde fermanu}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} parlanu", + "trends.trending_now": "Tindenze d'avà ", "ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.", "upload_area.title": "Drag & drop per caricà un fugliale", "upload_button.label": "Aghjunghje un media (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Limita di caricamentu di fugliali trapassata.", "upload_error.poll": "Ùn si pò micca caricà fugliali cù i scandagli.", "upload_form.description": "Discrive per i malvistosi", - "upload_form.focus": "Cambià a vista", + "upload_form.edit": "Mudificà ", "upload_form.undo": "Sguassà ", + "upload_modal.analyzing_picture": "Analisi di u ritrattu…", + "upload_modal.apply": "Affettà ", + "upload_modal.description_placeholder": "Chì tempi brevi ziu, quandu solfeghji", + "upload_modal.detect_text": "Ditettà testu da u ritrattu", + "upload_modal.edit_media": "Cambià media", + "upload_modal.hint": "Cliccate o sguillate u chjerchju nant'à a vista per sceglie u puntu fucale chì sarà sempre in vista indè tutte e miniature.", + "upload_modal.preview_label": "Vista ({ratio})", "upload_progress.label": "Caricamentu...", "video.close": "Chjudà a video", "video.exit_fullscreen": "Caccià u pienu screnu", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index fa94081d4c23372fe0d36887ec292c5173a8437f..ed0588e7c7a0af87949f58a10c936623fe626af4 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -4,6 +4,7 @@ "account.block": "Zablokovat uživatele @{name}", "account.block_domain": "Skrýt vÅ¡e z {domain}", "account.blocked": "Blokován/a", + "account.cancel_follow_request": "ZruÅ¡it požadavek o sledovánÃ", "account.direct": "Poslat pÅ™Ãmou zprávu uživateli @{name}", "account.domain_blocked": "Doména skryta", "account.edit_profile": "Upravit profil", @@ -15,6 +16,7 @@ "account.follows.empty": "Tento uživatel jeÅ¡tÄ› nikoho nesleduje.", "account.follows_you": "Sleduje vás", "account.hide_reblogs": "Skrýt boosty od uživatele @{name}", + "account.last_status": "Naposledy aktivnÃ", "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.media": "Média", @@ -23,6 +25,7 @@ "account.mute": "Skrýt uživatele @{name}", "account.mute_notifications": "Skrýt oznámenà od uživatele @{name}", "account.muted": "Skryt/a", + "account.never_active": "Nikdy", "account.posts": "Tooty", "account.posts_with_replies": "Tooty a odpovÄ›di", "account.report": "Nahlásit uživatele @{name}", @@ -35,9 +38,12 @@ "account.unfollow": "PÅ™estat sledovat", "account.unmute": "Odkrýt uživatele @{name}", "account.unmute_notifications": "Odkrýt oznámenà od uživatele @{name}", + "alert.rate_limited.message": "ProsÃm zkuste to znovu za {retry_time, time, medium}.", + "alert.rate_limited.title": "Rychlost omezena", "alert.unexpected.message": "Objevila se neoÄekávaná chyba.", "alert.unexpected.title": "Jejda!", - "boost_modal.combo": "PÅ™ÃÅ¡tÄ› můžete pro pÅ™eskoÄenà kliknout na {combo}", + "autosuggest_hashtag.per_week": "{count} za týden", + "boost_modal.combo": "PÅ™ÃÅ¡tÄ› můžete pro pÅ™eskoÄenà stisknout {combo}", "bundle_column_error.body": "PÅ™i naÄÃtánà tohoto komponentu se nÄ›co pokazilo.", "bundle_column_error.retry": "Zkuste to znovu", "bundle_column_error.title": "Chyba sÃtÄ›", @@ -47,6 +53,7 @@ "column.blocks": "Blokovanà uživatelé", "column.community": "MÃstnà Äasová osa", "column.direct": "PÅ™Ãmé zprávy", + "column.directory": "Prozkoumat profily", "column.domain_blocks": "Skryté domény", "column.favourites": "OblÃbené", "column.follow_requests": "Požadavky o sledovánÃ", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Jste si jistý/á, že chcete tento seznam navždy smazat?", "confirmations.domain_block.confirm": "Skrýt celou doménu", "confirmations.domain_block.message": "Jste si opravdu, opravdu jistý/á, že chcete blokovat celou doménu {domain}? Ve vÄ›tÅ¡inÄ› pÅ™Ãpadů staÄà zablokovat nebo skrýt pár konkrétnÃch uživatelů, což se doporuÄuje. Z této domény neuvidÃte obsah v žádné veÅ™ejné Äasové ose ani v oznámenÃch. VaÅ¡i sledujÃcà z této domény budou odstranÄ›ni.", + "confirmations.logout.confirm": "Odhlásit", + "confirmations.logout.message": "Jste si jistý/á, že se chcete odhlásit?", "confirmations.mute.confirm": "Skrýt", + "confirmations.mute.explanation": "Tohle skryje jeho pÅ™ÃspÄ›vky a pÅ™ÃspÄ›vky, které ho zmiňujÃ, ale uživatel pořád bude moci vidÄ›t vaÅ¡e pÅ™ÃspÄ›vky a sledovat vás.", "confirmations.mute.message": "Jste si jistý/á, že chcete skrýt uživatele {name}?", "confirmations.redraft.confirm": "Smazat a pÅ™epsat", "confirmations.redraft.message": "Jste si jistý/á, že chcete smazat a pÅ™epsat tento toot? OblÃbenà a boosty budou ztraceny a odpovÄ›di na původnà pÅ™ÃspÄ›vek budou opuÅ¡tÄ›ny.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "OdpovÄ›zenÃm nynà pÅ™epÃÅ¡ete zprávu, kterou aktuálnÄ› pÃÅ¡ete. Jste si jistý/á, že chcete pokraÄovat?", "confirmations.unfollow.confirm": "PÅ™estat sledovat", "confirmations.unfollow.message": "jste si jistý/á, že chcete pÅ™estat sledovat uživatele {name}?", + "conversation.delete": "Smazat konverzaci", + "conversation.mark_as_read": "OznaÄit jako pÅ™eÄtenou", + "conversation.open": "Zobrazit konverzaci", + "conversation.with": "S {names}", + "directory.federated": "Ze známého fedivesmÃru", + "directory.local": "Pouze z {domain}", + "directory.new_arrivals": "NovÄ› pÅ™ÃchozÃ", + "directory.recently_active": "Nedávno aktivnÃ", "embed.instructions": "Pro pÅ™idánà tootu na vaÅ¡i webovou stránku zkopÃrujte nÞe uvedený kód.", "embed.preview": "Takhle to bude vypadat:", "emoji_button.activity": "Aktivita", @@ -134,6 +152,10 @@ "empty_column.mutes": "JeÅ¡tÄ› jste neskryl/a žádné uživatele.", "empty_column.notifications": "JeÅ¡tÄ› nemáte žádná oznámenÃ. ZaÄnÄ›te konverzaci komunikovánÃm s ostatnÃmi.", "empty_column.public": "Tady nic nenÃ! NapiÅ¡te nÄ›co veÅ™ejnÄ›, nebo zaÄnÄ›te ruÄnÄ› sledovat uživatele z jiných serverů, aby tu nÄ›co pÅ™ibylo", + "error.unexpected_crash.explanation": "Kvůli chybÄ› v naÅ¡em kódu nebo problému s kompatibilitou prohlÞeÄe nemohla být tato stránka naÄtena správnÄ›.", + "error.unexpected_crash.next_steps": "Zkuste obnovit stránku. Pokud to nepomůže, budete možná moci dále použÃvat Mastodon pomocà jiného prohlÞeÄe nebo nativnà aplikace.", + "errors.unexpected_crash.copy_stacktrace": "ZkopÃrovat stacktrace do schránky", + "errors.unexpected_crash.report_issue": "Nahlásit problém", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -147,7 +169,7 @@ "getting_started.heading": "ZaÄÃnáme", "getting_started.invite": "Pozvat lidi", "getting_started.open_source_notice": "Mastodon je otevÅ™ený software. Na GitHubu k nÄ›mu můžete pÅ™ispÄ›t nebo nahlásit chyby: {github}.", - "getting_started.security": "ZabezpeÄenÃ", + "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.any": "nebo {additional}", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Název nového seznamu", "lists.search": "Hledejte mezi lidmi, které sledujete", "lists.subheading": "VaÅ¡e seznamy", + "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}", "loading_indicator.label": "NaÄÃtám…", "media_gallery.toggle_visible": "PÅ™epÃnat viditelnost", "missing_indicator.label": "Nenalezeno", @@ -251,7 +274,6 @@ "navigation_bar.personal": "OsobnÃ", "navigation_bar.pins": "PÅ™ipnuté tooty", "navigation_bar.preferences": "PÅ™edvolby", - "navigation_bar.profile_directory": "Adresář profilů", "navigation_bar.public_timeline": "Federovaná Äasová osa", "navigation_bar.security": "ZabezpeÄenÃ", "notification.favourite": "{name} si oblÃbil/a váš toot", @@ -282,8 +304,10 @@ "notifications.group": "{count} oznámenÃ", "poll.closed": "UzavÅ™ena", "poll.refresh": "Obnovit", + "poll.total_people": "{count, plural, one {# ÄlovÄ›k} few {# lidé} many {# lidÃ} other {# lidÃ}}", "poll.total_votes": "{count, plural, one {# hlas} few {# hlasy} many {# hlasu} other {# hlasů}}", "poll.vote": "Hlasovat", + "poll.voted": "Pro tuto odpovÄ›Ä jste hlasoval/a", "poll_button.add_poll": "PÅ™idat anketu", "poll_button.remove_poll": "Odstranit anketu", "privacy.change": "ZmÄ›nit soukromà tootu", @@ -295,6 +319,7 @@ "privacy.public.short": "VeÅ™ejný", "privacy.unlisted.long": "Neodeslat na veÅ™ejné Äasové osy", "privacy.unlisted.short": "Neuvedený", + "refresh": "Obnovit", "regeneration_indicator.label": "NaÄÃtám…", "regeneration_indicator.sublabel": "Váš domovský proud se pÅ™ipravuje!", "relative_time.days": "{number} d", @@ -319,6 +344,7 @@ "search_results.accounts": "Lidé", "search_results.hashtags": "Hashtagy", "search_results.statuses": "Tooty", + "search_results.statuses_fts_disabled": "Vyhledávánà tootů podle jejich obsahu nenà na tomto serveru Mastodon povoleno.", "search_results.total": "{count, number} {count, plural, one {výsledek} few {výsledky} many {výsledku} other {výsledků}}", "status.admin_account": "OtevÅ™Ãt moderátorské rozhranà pro uživatele @{name}", "status.admin_status": "OtevÅ™Ãt tento toot v moderátorském rozhranÃ", @@ -358,6 +384,7 @@ "status.show_more": "Zobrazit vÃce", "status.show_more_all": "Zobrazit vÃce pro vÅ¡echny", "status.show_thread": "Zobrazit vlákno", + "status.uncached_media_warning": "Nedostupné", "status.unmute_conversation": "Odkrýt konverzaci", "status.unpin": "Odepnout z profilu", "suggestions.dismiss": "OdmÃtnout návrh", @@ -373,14 +400,22 @@ "time_remaining.moments": "Zbývá nÄ›kolik sekund", "time_remaining.seconds": "{number, plural, one {Zbývá # sekunda} few {Zbývajà # sekundy} many {Zbývá # sekundy} other {Zbývá # sekund}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {ÄlovÄ›k} few {lidé} many {lidÃ} other {lidÃ}} hovoÅ™Ã", + "trends.trending_now": "Aktuálnà trendy", "ui.beforeunload": "Váš koncept se ztratÃ, pokud Mastodon opustÃte.", "upload_area.title": "PÅ™etaženÃm nahrajete", "upload_button.label": "PÅ™idat média (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Byl pÅ™ekroÄen limit nahraných souborů.", "upload_error.poll": "Nahrávánà souborů nenà povoleno u anket.", "upload_form.description": "Popis pro zrakovÄ› postižené", - "upload_form.focus": "ZmÄ›nit náhled", + "upload_form.edit": "Upravit", "upload_form.undo": "Smazat", + "upload_modal.analyzing_picture": "Analyzuji obrázek…", + "upload_modal.apply": "PoužÃt", + "upload_modal.description_placeholder": "PÅ™ÃliÅ¡ žluÅ¥ouÄký kůň úpÄ›l Äábelské ódy", + "upload_modal.detect_text": "Detekovat text z obrázku", + "upload_modal.edit_media": "Upravit média", + "upload_modal.hint": "KliknutÃm na nebo pÅ™etáhnutÃm kruhu na náhledu vyberte bod soustÅ™edÄ›nÃ, který bude vždy zobrazen na vÅ¡ech náhledech.", + "upload_modal.preview_label": "Náhled ({ratio})", "upload_progress.label": "Nahrávám…", "video.close": "ZavÅ™Ãt video", "video.exit_fullscreen": "UkonÄit celou obrazovku", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index f16d7056a28bd5732c11d4784cbcd55362b45d49..aeb919f5fb8da086f24b75e7fda755e746c37f9d 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -4,6 +4,7 @@ "account.block": "Blocio @{name}", "account.block_domain": "Cuddio popeth rhag {domain}", "account.blocked": "Blociwyd", + "account.cancel_follow_request": "Canslo cais dilyn", "account.direct": "Neges breifat @{name}", "account.domain_blocked": "Parth wedi ei guddio", "account.edit_profile": "Golygu proffil", @@ -15,6 +16,7 @@ "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.", "account.follows_you": "Yn eich dilyn chi", "account.hide_reblogs": "Cuddio bwstiau o @{name}", + "account.last_status": "Gweithredol olaf", "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.media": "Cyfryngau", @@ -23,6 +25,7 @@ "account.mute": "Tawelu @{name}", "account.mute_notifications": "Cuddio hysbysiadau o @{name}", "account.muted": "Distewyd", + "account.never_active": "Byth", "account.posts": "Tŵtiau", "account.posts_with_replies": "Tŵtiau ac atebion", "account.report": "Adrodd @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Dad-ddilyn", "account.unmute": "Dad-dawelu @{name}", "account.unmute_notifications": "Dad-dawelu hysbysiadau o @{name}", + "alert.rate_limited.message": "Ceisiwch eto ar ôl {retry_time, time, medium}.", + "alert.rate_limited.title": "Cyfradd gyfyngedig", "alert.unexpected.message": "Digwyddodd gwall annisgwyl.", "alert.unexpected.title": "Wps!", + "autosuggest_hashtag.per_week": "{count} yr wythnos", "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa", "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.", "bundle_column_error.retry": "Ceisiwch eto", @@ -47,6 +53,7 @@ "column.blocks": "Defnyddwyr a flociwyd", "column.community": "Ffrwd lleol", "column.direct": "Negeseuon preifat", + "column.directory": "Pori proffiliau", "column.domain_blocks": "Parthau cuddiedig", "column.favourites": "Ffefrynnau", "column.follow_requests": "Ceisiadau dilyn", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?", "confirmations.domain_block.confirm": "Cuddio parth cyfan", "confirmations.domain_block.message": "A ydych yn hollol, hollol sicr eich bod am flocio y {domain} cyfan? Yn y nifer helaeth o achosion mae blocio neu tawelu ambell gyfrif yn ddigonol ac yn well. Ni fyddwch yn gweld cynnwys o'r parth hwnnw mewn unrhyw ffrydiau cyhoeddus na chwaith yn eich hysbysiadau. Bydd hyn yn cael gwared o'ch dilynwyr o'r parth hwnnw.", + "confirmations.logout.confirm": "Allgofnodi", + "confirmations.logout.message": "Ydych chi'n siŵr eich bod am allgofnodi?", "confirmations.mute.confirm": "Tawelu", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?", "confirmations.redraft.confirm": "Dileu & ailddrafftio", "confirmations.redraft.message": "Ydych chi'n siwr eich bod eisiau dileu y tŵt hwn a'i ailddrafftio? Bydd ffefrynnau a bwstiau'n cael ei colli, a bydd ymatebion i'r tŵt gwreiddiol yn cael eu hamddifadu.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Bydd ateb nawr yn cymryd lle y neges yr ydych yn cyfansoddi ar hyn o bryd. Ydych chi'n sicr yr ydych am barhau?", "confirmations.unfollow.confirm": "Dad-ddilynwch", "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?", + "conversation.delete": "Dileu sgwrs", + "conversation.mark_as_read": "Nodi fel wedi'i ddarllen", + "conversation.open": "Gweld sgwrs", + "conversation.with": "Gyda {names}", + "directory.federated": "O ffedysawd hysbys", + "directory.local": "O {domain} yn unig", + "directory.new_arrivals": "Newydd-ddyfodiaid", + "directory.recently_active": "Yn weithredol yn ddiweddar", "embed.instructions": "Mewnblannwch y tŵt hwn ar eich gwefan drwy gopïo'r côd isod.", "embed.preview": "Dyma sut olwg fydd arno:", "emoji_button.activity": "Gweithgarwch", @@ -134,6 +152,10 @@ "empty_column.mutes": "Nid ydych wedi tawelu unrhyw ddefnyddwyr eto.", "empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ac eraill i ddechrau'r sgwrs.", "empty_column.public": "Does dim byd yma! Ysgrifennwch rhywbeth yn gyhoeddus, neu dilynwch ddefnyddwyr o achosion eraill i'w lenwi", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Teitl rhestr newydd", "lists.search": "Chwilio ymysg pobl yr ydych yn ei ddilyn", "lists.subheading": "Eich rhestrau", + "load_pending": "{count, plural, one {# eitem newydd} other {# eitemau newydd}}", "loading_indicator.label": "Llwytho...", "media_gallery.toggle_visible": "Toglo gwelededd", "missing_indicator.label": "Heb ei ganfod", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personol", "navigation_bar.pins": "Tŵtiau wedi eu pinio", "navigation_bar.preferences": "Dewisiadau", - "navigation_bar.profile_directory": "Cyfeiriadur Proffil", "navigation_bar.public_timeline": "Ffrwd y ffederasiwn", "navigation_bar.security": "Diogelwch", "notification.favourite": "hoffodd {name} eich tŵt", @@ -282,8 +304,10 @@ "notifications.group": "{count} o hysbysiadau", "poll.closed": "Ar gau", "poll.refresh": "Adnewyddu", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# bleidlais} other {# o bleidleisiau}}", "poll.vote": "Pleidleisio", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Ychwanegu pleidlais", "poll_button.remove_poll": "Tynnu pleidlais", "privacy.change": "Addasu preifatrwdd y tŵt", @@ -295,6 +319,7 @@ "privacy.public.short": "Cyhoeddus", "privacy.unlisted.long": "Peidio a chyhoeddi i ffrydiau cyhoeddus", "privacy.unlisted.short": "Heb ei restru", + "refresh": "Refresh", "regeneration_indicator.label": "Llwytho…", "regeneration_indicator.sublabel": "Mae eich ffrwd cartref yn cael ei baratoi!", "relative_time.days": "{number}dydd", @@ -319,6 +344,7 @@ "search_results.accounts": "Pobl", "search_results.hashtags": "Hanshnodau", "search_results.statuses": "Tŵtiau", + "search_results.statuses_fts_disabled": "Nid yw chwilio Tŵtiau yn ôl eu cynnwys wedi'i alluogi ar y gweinydd Mastodon hwn.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Agor rhyngwyneb goruwchwylio ar gyfer @{name}", "status.admin_status": "Agor y tŵt yn y rhyngwyneb goruwchwylio", @@ -358,6 +384,7 @@ "status.show_more": "Dangos mwy", "status.show_more_all": "Dangos mwy i bawb", "status.show_thread": "Dangos edefyn", + "status.uncached_media_warning": "Dim ar gael", "status.unmute_conversation": "Dad-dawelu sgwrs", "status.unpin": "Dadbinio o'r proffil", "suggestions.dismiss": "Diswyddo", @@ -373,14 +400,22 @@ "time_remaining.moments": "Munudau ar ôl", "time_remaining.seconds": "{number, plural, one {# eiliad} other {# o eiliadau}} ar ôl", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} yn siarad", + "trends.trending_now": "Yn tueddu nawr", "ui.beforeunload": "Mi fyddwch yn colli eich drafft os gadewch Mastodon.", "upload_area.title": "Llusgwch & gollwing i uwchlwytho", "upload_button.label": "Ychwanegwch gyfryngau (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Wedi mynd heibio'r uchafswm terfyn uwchlwytho.", "upload_error.poll": "Nid oes modd uwchlwytho ffeiliau â phleidleisiau.", "upload_form.description": "Disgrifio i'r rheini a nam ar ei golwg", - "upload_form.focus": "Newid rhagolwg", + "upload_form.edit": "Golygu", "upload_form.undo": "Dileu", + "upload_modal.analyzing_picture": "Dadansoddi llun…", + "upload_modal.apply": "Gweithredu", + "upload_modal.description_placeholder": "Mae ei phen bach llawn jocs, 'run peth a fy nghot golff, rhai dyddiau", + "upload_modal.detect_text": "Canfod testun o'r llun", + "upload_modal.edit_media": "Golygu cyfryngau", + "upload_modal.hint": "Cliciwch neu llusgwch y cylch ar y rhagolwg i ddewis y canolbwynt a fydd bob amser i'w weld ar bob mân-lunau.", + "upload_modal.preview_label": "Rhagolwg ({ratio})", "upload_progress.label": "Uwchlwytho...", "video.close": "Cau fideo", "video.exit_fullscreen": "Gadael sgrîn llawn", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index c478f295338b376053e39ce4b2e9f931e1fd29e2..64088fd6e8e01df82f864774a7dab7d17ec439fd 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -4,6 +4,7 @@ "account.block": "Bloker @{name}", "account.block_domain": "Skjul alt fra {domain}", "account.blocked": "Blokeret", + "account.cancel_follow_request": "Annullér følgeranmodning", "account.direct": "Send en direkte besked til @{name}", "account.domain_blocked": "Domænet er blevet skjult", "account.edit_profile": "Rediger profil", @@ -15,14 +16,16 @@ "account.follows.empty": "Denne bruger følger endnu ikke nogen.", "account.follows_you": "Følger dig", "account.hide_reblogs": "Skjul fremhævelserne fra @{name}", + "account.last_status": "Sidst aktiv", "account.link_verified_on": "Ejerskabet af dette link blev tjekket den %{date}", - "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", + "account.locked_info": "Denne kontos privatlivsstatus er sat til lÃ¥st. Ejeren bedømmer manuelt, hvem der kan følge dem.", "account.media": "Medie", "account.mention": "Nævn @{name}", "account.moved_to": "{name} er flyttet til:", "account.mute": "Dæmp @{name}", "account.mute_notifications": "Dæmp notifikationer fra @{name}", "account.muted": "Dæmpet", + "account.never_active": "Aldrig", "account.posts": "Trut", "account.posts_with_replies": "Trut og svar", "account.report": "Rapporter @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Følg ikke længere", "account.unmute": "Fjern dæmpningen af @{name}", "account.unmute_notifications": "Fjern dæmpningen af notifikationer fra @{name}", + "alert.rate_limited.message": "Prøv venligst igen efter {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Der opstod en uventet fejl.", "alert.unexpected.title": "Ups!", + "autosuggest_hashtag.per_week": "{count} per uge", "boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang", "bundle_column_error.body": "Noget gik galt under indlæsningen af dette komponent.", "bundle_column_error.retry": "Prøv igen", @@ -47,6 +53,7 @@ "column.blocks": "Blokerede brugere", "column.community": "Lokal tidslinje", "column.direct": "Direkte beskeder", + "column.directory": "Gennemse profiler", "column.domain_blocks": "Skjulte domæner", "column.favourites": "Favoritter", "column.follow_requests": "Anmodning om at følge", @@ -71,20 +78,20 @@ "compose_form.lock_disclaimer": "Din konto er ikke {locked}. Alle kan følge dig for at se dine følger-kun indlæg.", "compose_form.lock_disclaimer.lock": "lÃ¥st", "compose_form.placeholder": "Hvad har du pÃ¥ hjertet?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", + "compose_form.poll.add_option": "Tilføj valgmulighed", + "compose_form.poll.duration": "Afstemningens varighed", + "compose_form.poll.option_placeholder": "Valgmulighed {number}", + "compose_form.poll.remove_option": "Fjern denne valgmulighed", "compose_form.publish": "Trut", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Markér medie som følsomt", "compose_form.sensitive.marked": "Medie er markeret som værende følsomt", "compose_form.sensitive.unmarked": "Mediet er ikke markeret som værende følsomt", "compose_form.spoiler.marked": "Teksten er skjult bag en advarsel", "compose_form.spoiler.unmarked": "Teksten er ikke skjult", "compose_form.spoiler_placeholder": "Skriv din advarsel her", "confirmation_modal.cancel": "Annuller", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "Blokér og anmeld", "confirmations.block.confirm": "Bloker", "confirmations.block.message": "Er du sikker pÃ¥, du vil blokere {name}?", "confirmations.delete.confirm": "Slet", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Er du sikker pÃ¥, du vil slette denne liste?", "confirmations.domain_block.confirm": "Skjul helt domæne", "confirmations.domain_block.message": "Er du helt sikker pÃ¥ du vil blokere hele {domain} domænet? I de fleste tilfælde vil fÃ¥ specifikke blokeringer eller dæmpninger være nok og at fortrække. Du vil ikke se indhold fra det domæne hverken pÃ¥ offentlige tidslinjer eller i dine notifikationer. Dine følgere fra det domæne vil blive fjernet.", + "confirmations.logout.confirm": "Log ud", + "confirmations.logout.message": "Er du sikker pÃ¥ du vil logge ud?", "confirmations.mute.confirm": "Dæmp", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Er du sikker pÃ¥, du vil dæmpe {name}?", "confirmations.redraft.confirm": "Slet & omskriv", "confirmations.redraft.message": "Er du sikker pÃ¥, du vil slette denne status og omskrive den? Favoritter og fremhævelser vil gÃ¥ tabt og svar til det oprindelige opslag vil blive forældreløse.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Hvis du svarer nu vil du overskrive den besked du er ved at skrive. Er du sikker pÃ¥, du vil fortsætte?", "confirmations.unfollow.confirm": "Følg ikke længere", "confirmations.unfollow.message": "Er du sikker pÃ¥, du ikke længere vil følge {name}?", + "conversation.delete": "Slet samtale", + "conversation.mark_as_read": "Marker som læst", + "conversation.open": "Vis samtale", + "conversation.with": "Med {names}", + "directory.federated": "Fra kendt fedivers", + "directory.local": "Kun fra {domain}", + "directory.new_arrivals": "Nye ankomster", + "directory.recently_active": "Senest aktiv", "embed.instructions": "Indlejre denne status pÃ¥ din side ved at kopiere nedenstÃ¥ende kode.", "embed.preview": "Det kommer til at se sÃ¥ledes ud:", "emoji_button.activity": "Aktivitet", @@ -111,14 +129,14 @@ "emoji_button.nature": "Natur", "emoji_button.not_found": "Ingen emojos!! (╯°□°)╯︵ â”»â”â”»", "emoji_button.objects": "Objekter", - "emoji_button.people": "Mennesker", + "emoji_button.people": "Personer", "emoji_button.recent": "Oftest brugt", "emoji_button.search": "Søg...", "emoji_button.search_results": "Søgeresultater", "emoji_button.symbols": "Symboler", "emoji_button.travel": "Rejser & steder", "empty_column.account_timeline": "Ingen bidrag her!", - "empty_column.account_unavailable": "Profile unavailable", + "empty_column.account_unavailable": "Profil utilgængelig", "empty_column.blocks": "Du har ikke blokeret nogen endnu.", "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at starte lavinen!", "empty_column.direct": "Du har endnu ingen direkte beskeder. NÃ¥r du sender eller modtager en, vil den vises her.", @@ -134,6 +152,10 @@ "empty_column.mutes": "Du har endnu ikke dæmpet nogen som helst bruger.", "empty_column.notifications": "Du har endnu ingen notifikationer. Tag ud og bland dig med folkemængden for at starte samtalen.", "empty_column.public": "Der er ikke noget at se her! Skriv noget offentligt eller start ud med manuelt at følge brugere fra andre server for at udfylde tomrummet", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,7 +164,7 @@ "follow_request.authorize": "Godkend", "follow_request.reject": "Afvis", "getting_started.developers": "Udviklere", - "getting_started.directory": "Profile directory", + "getting_started.directory": "Profilliste", "getting_started.documentation": "Dokumentation", "getting_started.heading": "Kom igang", "getting_started.invite": "Inviter folk", @@ -152,35 +174,35 @@ "hashtag.column_header.tag_mode.all": "og {additional}", "hashtag.column_header.tag_mode.any": "eller {additional}", "hashtag.column_header.tag_mode.none": "uden {additional}", - "hashtag.column_settings.select.no_options_message": "No suggestions found", - "hashtag.column_settings.select.placeholder": "Enter hashtags…", - "hashtag.column_settings.tag_mode.all": "All of these", - "hashtag.column_settings.tag_mode.any": "Any of these", - "hashtag.column_settings.tag_mode.none": "None of these", + "hashtag.column_settings.select.no_options_message": "Ingen forslag fundet", + "hashtag.column_settings.select.placeholder": "Indtast hashtags…", + "hashtag.column_settings.tag_mode.all": "Alle disse", + "hashtag.column_settings.tag_mode.any": "Nogle af disse", + "hashtag.column_settings.tag_mode.none": "Ingen af disse", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", "home.column_settings.basic": "Grundlæggende", "home.column_settings.show_reblogs": "Vis fremhævelser", "home.column_settings.show_replies": "Vis svar", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.days": "{number, plural, one {# dag} other {# dage}}", + "intervals.full.hours": "{number, plural, one {# time} other {# timer}}", + "intervals.full.minutes": "{number, plural, one {# minut} other {# minutter}}", "introduction.federation.action": "Næste", - "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.headline": "Fælles", "introduction.federation.federated.text": "Offentlige bidrag fra andre servere af fediversen vil komme til syne i den federated timeline.", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.federation.local.headline": "Local", - "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.federation.home.headline": "Hjem", + "introduction.federation.home.text": "Statusser fra personer du følger vil blive vist i dit hjemmefeed. Du kan følge alle pÃ¥ enhver server!", + "introduction.federation.local.headline": "Lokal", + "introduction.federation.local.text": "Offentlige statusser fra personer pÃ¥ samme server som dig vil blive vist i det lokale feed.", "introduction.interactions.action": "Slut tutorial!", "introduction.interactions.favourite.headline": "Favorisere", - "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.favourite.text": "Du kan gemme en status til senere (og vise forfatteren at du kunne lide den) ved at favorisere den.", "introduction.interactions.reblog.headline": "Boost", - "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reblog.text": "Du kan delete andres statusser med dine følgere ved at booste dem.", "introduction.interactions.reply.headline": "Svar", "introduction.interactions.reply.text": "Du kan svare andres og din egen bidrag, hvilke vil kæde dem sammen i en konversation.", "introduction.welcome.action": "Læd os gÃ¥!", "introduction.welcome.headline": "Første skridt", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "introduction.welcome.text": "Velkommen til fediverset! Om fÃ¥ øjeblikke vil du kunne dele statusser og tale med dine venner pÃ¥ en bred vifte af servere. Men denne server, {domain}, er speciel. Det er pÃ¥ denne server at din profil har hjemme sÃ¥ husk dens navn.", "keyboard_shortcuts.back": "for at navigere dig tilbage", "keyboard_shortcuts.blocked": "for at Ã¥bne listen over blokerede brugere", "keyboard_shortcuts.boost": "for at fremhæve", @@ -209,23 +231,24 @@ "keyboard_shortcuts.search": "for at fokusere søgningen", "keyboard_shortcuts.start": "for at Ã¥bne \"kom igen\" kolonnen", "keyboard_shortcuts.toggle_hidden": "for at vise/skjule tekst bag CW", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "for at vise/skjule medier", "keyboard_shortcuts.toot": "for at pÃ¥begynde et helt nyt trut", "keyboard_shortcuts.unfocus": "for at fjerne fokus fra skriveomrÃ¥de/søgning", "keyboard_shortcuts.up": "for at bevæge dig op ad listen", "lightbox.close": "Luk", "lightbox.next": "Næste", "lightbox.previous": "Forrige", - "lightbox.view_context": "View context", + "lightbox.view_context": "Vis kontekst", "lists.account.add": "Tilføj til liste", "lists.account.remove": "Fjern fra liste", "lists.delete": "Slet liste", "lists.edit": "Rediger liste", - "lists.edit.submit": "Change title", + "lists.edit.submit": "Skift titel", "lists.new.create": "Tilføj liste", "lists.new.title_placeholder": "Ny liste titel", "lists.search": "Søg iblandt folk du følger", "lists.subheading": "Dine lister", + "load_pending": "{count, plural, one {# nyt punkt} other {# nye punkter}}", "loading_indicator.label": "Indlæser...", "media_gallery.toggle_visible": "Ændre synlighed", "missing_indicator.label": "Ikke fundet", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favoritter", "navigation_bar.filters": "Dæmpede ord", "navigation_bar.follow_requests": "Følgeanmodninger", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Følger og følgere", "navigation_bar.info": "Om denne instans", "navigation_bar.keyboard_shortcuts": "Hurtigtast", "navigation_bar.lists": "Lister", @@ -251,50 +274,52 @@ "navigation_bar.personal": "Personligt", "navigation_bar.pins": "Fastgjorte trut", "navigation_bar.preferences": "Præferencer", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Fælles tidslinje", "navigation_bar.security": "Sikkerhed", "notification.favourite": "{name} favoriserede din status", "notification.follow": "{name} fulgte dig", "notification.mention": "{name} nævnte dig", - "notification.poll": "A poll you have voted in has ended", - "notification.reblog": "{name} fremhævede din status", + "notification.poll": "En afstemning, du stemte i, er slut", + "notification.reblog": "{name} boostede din status", "notifications.clear": "Ryd notifikationer", "notifications.clear_confirmation": "Er du sikker pÃ¥, du vil rydde alle dine notifikationer permanent?", - "notifications.column_settings.alert": "Skrivebords notifikationer", + "notifications.column_settings.alert": "Skrivebordsnotifikationer", "notifications.column_settings.favourite": "Favoritter:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Vis alle kategorier", + "notifications.column_settings.filter_bar.category": "Hurtigfilter", + "notifications.column_settings.filter_bar.show": "Vis", "notifications.column_settings.follow": "Nye følgere:", - "notifications.column_settings.mention": "Omtale:", - "notifications.column_settings.poll": "Poll results:", - "notifications.column_settings.push": "Push notifikationer", - "notifications.column_settings.reblog": "Fremhævelser:", + "notifications.column_settings.mention": "Statusser der nævner dig:", + "notifications.column_settings.poll": "Afstemningsresultat:", + "notifications.column_settings.push": "Pushnotifikationer", + "notifications.column_settings.reblog": "Boosts:", "notifications.column_settings.show": "Vis i kolonne", "notifications.column_settings.sound": "Afspil lyd", "notifications.filter.all": "Alle", "notifications.filter.boosts": "Boosts", "notifications.filter.favourites": "Favoritter", "notifications.filter.follows": "Følger", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", + "notifications.filter.mentions": "Statusser der nævner dig", + "notifications.filter.polls": "Afstemningsresultat", "notifications.group": "{count} notifikationer", - "poll.closed": "Closed", - "poll.refresh": "Refresh", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", - "privacy.change": "Ændre status privatliv", - "privacy.direct.long": "Post til kun de nævnte brugere", + "poll.closed": "Lukket", + "poll.refresh": "Opdatér", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# stemme} other {# stemmer}}", + "poll.vote": "Stem", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Tilføj en afstemning", + "poll_button.remove_poll": "Fjern afstemning", + "privacy.change": "Skift status visningsindstillinger", + "privacy.direct.long": "Udgiv kun til nævnte brugere", "privacy.direct.short": "Direkte", - "privacy.private.long": "Post kun til følgere", + "privacy.private.long": "Udgiv kun til følgere", "privacy.private.short": "Kun for følgere", - "privacy.public.long": "Post til offentlige tidslinjer", + "privacy.public.long": "Udgiv pÃ¥ offentlige tidslinjer", "privacy.public.short": "Offentligt", - "privacy.unlisted.long": "Post ikke til offentlige tidslinjer", + "privacy.unlisted.long": "Udgiv ikke pÃ¥ offentlige tidslinjer", "privacy.unlisted.short": "Ikke listet", + "refresh": "Refresh", "regeneration_indicator.label": "Indlæser…", "regeneration_indicator.sublabel": "Din startside er ved at blive forberedt!", "relative_time.days": "{number}d", @@ -305,31 +330,32 @@ "reply_indicator.cancel": "Annuller", "report.forward": "Videresend til {target}", "report.forward_hint": "Kontoen er fra en anden server. Vil du ogsÃ¥ sende en anonym kopi af anmeldelsen dertil?", - "report.hint": "Anmeldelsen vil blive sendt til moderatorene af din instans. Du kan give en forklaring for hvorfor du anmelder denne konto nedenfor:", + "report.hint": "Anmeldelsen vil blive sendt til moderatorene af din instans. Du kan give en forklaring pÃ¥ hvorfor du anmelder denne konto nedenfor:", "report.placeholder": "Yderligere kommentarer", "report.submit": "Indsend", "report.target": "Anmelder {target}", "search.placeholder": "Søg", "search_popout.search_format": "Avanceret søgeformat", - "search_popout.tips.full_text": "Simpel tekst returnerer statusser du har skrevet, favoriseret, fremhævet, eller er blevet nævnt i, lige sÃ¥ vel som matchende brugernavne, visningsnavne, og hashtags.", - "search_popout.tips.hashtag": "emnetag", + "search_popout.tips.full_text": "Simpel tekst returnerer statusser du har skrevet, favoriseret, boostet, eller er blevet nævnt i sÃ¥vel som matchende brugernavne, profilnavne, og hashtags.", + "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "status", - "search_popout.tips.text": "Simpelt tekst returnerer passende visningsnavne, brugernavne og hashtags", + "search_popout.tips.text": "Simpel tekst returnerer matchende profilnavne, brugernavne og hashtags", "search_popout.tips.user": "bruger", - "search_results.accounts": "Folk", - "search_results.hashtags": "Emnetags", + "search_results.accounts": "Personer", + "search_results.hashtags": "Hashtags", "search_results.statuses": "Trut", - "search_results.total": "{count, number} {count, plural, et {result} andre {results}}", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this status in the moderation interface", + "search_results.statuses_fts_disabled": "Denne Mastodonserver har ikke aktiveret for søgning af statusser via deres indhold.", + "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}", + "status.admin_account": "Ã…ben modereringsvisning for @{name}", + "status.admin_status": "Ã…ben denne status i modereringsvisningen", "status.block": "Bloker @{name}", - "status.cancel_reblog_private": "Fremhæv ikke længere", - "status.cannot_reblog": "Denne post kan ikke fremhæves", - "status.copy": "Copy link to status", + "status.cancel_reblog_private": "Fjern boost", + "status.cannot_reblog": "Denne post kan ikke boostes", + "status.copy": "Kopiér link til status", "status.delete": "Slet", "status.detailed_status": "Detaljeret visning af samtale", "status.direct": "Send direkte besked til @{name}", - "status.embed": "Indlejre", + "status.embed": "Integrér", "status.favourite": "Favorit", "status.filtered": "Filtreret", "status.load_more": "Indlæs mere", @@ -343,13 +369,13 @@ "status.pin": "Fastgør til profil", "status.pinned": "Fastgjort trut", "status.read_more": "Læs mere", - "status.reblog": "Fremhæv", - "status.reblog_private": "Fremhæv til oprindeligt publikum", - "status.reblogged_by": "{name} fremhævede", - "status.reblogs.empty": "Der er endnu ingen der har fremhævet dette trut. NÃ¥r der er nogen der gør, vil det blive vist her.", + "status.reblog": "Boost", + "status.reblog_private": "Boost til det oprindelige publikum", + "status.reblogged_by": "{name} boostede", + "status.reblogs.empty": "Der er endnu ingen der har boostet dette trut. NÃ¥r der er nogen der gør, vil det blive vist her.", "status.redraft": "Slet og omskriv", - "status.reply": "Svar", - "status.replyAll": "Svar samtale", + "status.reply": "Besvar", + "status.replyAll": "Besvar samtale", "status.report": "Anmeld @{name}", "status.sensitive_warning": "Følsomt indhold", "status.share": "Del", @@ -357,33 +383,42 @@ "status.show_less_all": "Vis mindre for alle", "status.show_more": "Vis mere", "status.show_more_all": "Vis mere for alle", - "status.show_thread": "Show thread", - "status.unmute_conversation": "Fjern dæmpningen fra samtale", - "status.unpin": "Fjern som fastgjort fra profil", - "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", + "status.show_thread": "Vis trÃ¥d", + "status.uncached_media_warning": "Ikke tilgængelig", + "status.unmute_conversation": "Genaktivér samtale", + "status.unpin": "Frigør fra profil", + "suggestions.dismiss": "Afvis foreslag", + "suggestions.header": "Du er mÃ¥ske interesseret i…", "tabs_bar.federated_timeline": "Fælles", "tabs_bar.home": "Hjem", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Notifikationer", "tabs_bar.search": "Søg", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} snakker", + "time_remaining.days": "{number, plural, one {# dag} other {# dage}} tilbage", + "time_remaining.hours": "{number, plural, one {# time} other {# timer}} tilbage", + "time_remaining.minutes": "{number, plural, one {# minut} other {# minutter}} tilbage", + "time_remaining.moments": "FÃ¥ øjeblikke tilbage", + "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekunder}} tilbage", + "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {personer}} snakker", + "trends.trending_now": "Hot lige nu", "ui.beforeunload": "Din kladde vil gÃ¥ tabt hvis du forlader Mastodon.", "upload_area.title": "Træk og slip for at uploade", "upload_button.label": "Tilføj medie (JPEG, PNG, GIF, WebM, MP4, MOV)", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", - "upload_form.description": "Beskriv for de svagtseende", - "upload_form.focus": "Beskær", + "upload_error.limit": "Uploadgrænse overskredet.", + "upload_error.poll": "Filupload ikke tilladt sammen med afstemninger.", + "upload_form.description": "Beskriv for svagtseende", + "upload_form.edit": "Redigér", "upload_form.undo": "Slet", + "upload_modal.analyzing_picture": "Analyserer billede…", + "upload_modal.apply": "Anvend", + "upload_modal.description_placeholder": "En hurtig brun ræv hopper over den dovne hund", + "upload_modal.detect_text": "Find tekst i billede pÃ¥ automatisk vis", + "upload_modal.edit_media": "Redigér medie", + "upload_modal.hint": "Klik eller træk cirklen pÃ¥ billedet for at vælge et fokuspunkt.", + "upload_modal.preview_label": "ForhÃ¥ndsvisning ({ratio})", "upload_progress.label": "Uploader...", "video.close": "Luk video", - "video.exit_fullscreen": "GÃ¥ ud af fuldskærm", + "video.exit_fullscreen": "Forlad fuldskærm", "video.expand": "Udvid video", "video.fullscreen": "Fuldskærm", "video.hide": "Skjul video", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index f84b420fe3a4c2f9867a132cfca71b4d7c1a3ce2..077cb729a99f1fbed5b388fbc8d5fd03917b345d 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -4,17 +4,19 @@ "account.block": "@{name} blockieren", "account.block_domain": "Alles von {domain} verstecken", "account.blocked": "Blockiert", + "account.cancel_follow_request": "Folgeanfrage abbrechen", "account.direct": "Direktnachricht an @{name}", "account.domain_blocked": "Domain versteckt", "account.edit_profile": "Profil bearbeiten", "account.endorse": "Auf Profil hervorheben", "account.follow": "Folgen", - "account.followers": "Folger_innen", + "account.followers": "Folgende", "account.followers.empty": "Diesem Profil folgt noch niemand.", "account.follows": "Folgt", "account.follows.empty": "Dieses Profil folgt noch niemandem.", "account.follows_you": "Folgt dir", "account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen", + "account.last_status": "Zuletzt aktiv", "account.link_verified_on": "Besitz dieses Links wurde geprüft am {date}", "account.locked_info": "Der Privatsphärenstatus dieses Accounts wurde auf gesperrt gesetzt. Die Person bestimmt manuell wer ihm/ihr folgen darf.", "account.media": "Medien", @@ -23,6 +25,7 @@ "account.mute": "@{name} stummschalten", "account.mute_notifications": "Benachrichtigungen von @{name} verbergen", "account.muted": "Stummgeschaltet", + "account.never_active": "Nie", "account.posts": "Beiträge", "account.posts_with_replies": "Beiträge und Antworten", "account.report": "@{name} melden", @@ -35,8 +38,11 @@ "account.unfollow": "Entfolgen", "account.unmute": "@{name} nicht mehr stummschalten", "account.unmute_notifications": "Benachrichtigungen von @{name} einschalten", + "alert.rate_limited.message": "Bitte versuche es nach {retry_time, time, medium}.", + "alert.rate_limited.title": "Anfragelimit überschritten", "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.", "alert.unexpected.title": "Hoppla!", + "autosuggest_hashtag.per_week": "{count} pro Woche", "boost_modal.combo": "Drücke {combo}, um dieses Fenster zu überspringen", "bundle_column_error.body": "Etwas ist beim Laden schiefgelaufen.", "bundle_column_error.retry": "Erneut versuchen", @@ -47,6 +53,7 @@ "column.blocks": "Blockierte Profile", "column.community": "Lokale Zeitleiste", "column.direct": "Direktnachrichten", + "column.directory": "Profile durchsuchen", "column.domain_blocks": "Versteckte Domains", "column.favourites": "Favoriten", "column.follow_requests": "Folgeanfragen", @@ -92,8 +99,11 @@ "confirmations.delete_list.confirm": "Löschen", "confirmations.delete_list.message": "Bist du dir sicher, dass du diese Liste permanent löschen möchtest?", "confirmations.domain_block.confirm": "Die ganze Domain verbergen", - "confirmations.domain_block.message": "Bist du dir wirklich sicher, dass du die ganze Domain {domain} blockieren willst? In den meisten Fällen reichen ein paar gezielte Blockierungen oder Stummschaltungen aus. Nach der Blockierung wirst du nichts mehr von dieser Domain in öffentlichen Zeitleisten oder Benachrichtigungen sehen. Deine Folger_innen von dieser Domain werden auch entfernt.", + "confirmations.domain_block.message": "Bist du dir wirklich sicher, dass du die ganze Domain {domain} blockieren willst? In den meisten Fällen reichen ein paar gezielte Blockierungen oder Stummschaltungen aus. Du wirst den Inhalt von dieser Domain nicht in irgendwelchen öffentlichen Timelines oder den Benachrichtigungen finden. Deine Folgenden von dieser Domain werden entfernt.", + "confirmations.logout.confirm": "Abmelden", + "confirmations.logout.message": "Bist du sicher, dass du dich abmelden möchtest?", "confirmations.mute.confirm": "Stummschalten", + "confirmations.mute.explanation": "Dies wird Beiträge von dieser Person und Beiträge, die diese Person erwähnen, ausblenden, aber es wird der Person trotzdem erlauben, deine Beiträge zu sehen und dir zu folgen.", "confirmations.mute.message": "Bist du dir sicher, dass du {name} stummschalten möchtest?", "confirmations.redraft.confirm": "Löschen und neu erstellen", "confirmations.redraft.message": "Bist du dir sicher, dass du diesen Beitrag löschen und neu erstellen möchtest? Favorisierungen, geteilte Beiträge und Antworten werden verloren gehen.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Wenn du jetzt antwortest wird es die gesamte Nachricht verwerfen, die du gerade schreibst. Möchtest du wirklich fortfahren?", "confirmations.unfollow.confirm": "Entfolgen", "confirmations.unfollow.message": "Bist du dir sicher, dass du {name} entfolgen möchtest?", + "conversation.delete": "Unterhaltung löschen", + "conversation.mark_as_read": "Als gelesen markieren", + "conversation.open": "Unterhaltung anzeigen", + "conversation.with": "Mit {names}", + "directory.federated": "Aus dem Fediverse", + "directory.local": "Nur von {domain}", + "directory.new_arrivals": "Neue Benutzer", + "directory.recently_active": "Kürzlich aktiv", "embed.instructions": "Du kannst diesen Beitrag auf deiner Webseite einbetten, indem du den folgenden Code einfügst.", "embed.preview": "So wird es aussehen:", "emoji_button.activity": "Aktivitäten", @@ -134,6 +152,10 @@ "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.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.next_steps": "Versuche die Seite zu aktualisieren. Wenn das nicht hilft, kannst du Mastodon über einen anderen Browser oder eine native App verwenden.", + "errors.unexpected_crash.copy_stacktrace": "Fehlerlog in die Zwischenablage kopieren", + "errors.unexpected_crash.report_issue": "Problem melden", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -173,7 +195,7 @@ "introduction.federation.local.text": "Öffentliche Beiträge von Leuten auf demselben Server wie du erscheinen in der lokalen Zeitleiste.", "introduction.interactions.action": "Tutorial beenden!", "introduction.interactions.favourite.headline": "Favorisieren", - "introduction.interactions.favourite.text": "Du kannst Beitrage für später speichern und ihre Autor_innen wissen lassen, dass sie dir gefallen haben, indem du sie favorisierst.", + "introduction.interactions.favourite.text": "Du kannst Beitrage für später speichern und ihre Autoren wissen lassen, dass sie dir gefallen haben, indem du sie favorisierst.", "introduction.interactions.reblog.headline": "Teilen", "introduction.interactions.reblog.text": "Du kannst Beiträge anderer mit deinen Followern teilen, indem du sie teilst.", "introduction.interactions.reply.headline": "Antworten", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Neuer Titel der Liste", "lists.search": "Suche nach Leuten denen du folgst", "lists.subheading": "Deine Listen", + "load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}", "loading_indicator.label": "Wird geladen …", "media_gallery.toggle_visible": "Sichtbarkeit umschalten", "missing_indicator.label": "Nicht gefunden", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favoriten", "navigation_bar.filters": "Stummgeschaltene Wörter", "navigation_bar.follow_requests": "Folgeanfragen", - "navigation_bar.follows_and_followers": "Folger_innen und Gefolgte", + "navigation_bar.follows_and_followers": "Folgende und Gefolgte", "navigation_bar.info": "Über diesen Server", "navigation_bar.keyboard_shortcuts": "Tastenkombinationen", "navigation_bar.lists": "Listen", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Persönlich", "navigation_bar.pins": "Angeheftete Beiträge", "navigation_bar.preferences": "Einstellungen", - "navigation_bar.profile_directory": "Profilverzeichnis", "navigation_bar.public_timeline": "Föderierte Zeitleiste", "navigation_bar.security": "Sicherheit", "notification.favourite": "{name} hat deinen Beitrag favorisiert", @@ -266,7 +288,7 @@ "notifications.column_settings.filter_bar.advanced": "Zeige alle Kategorien an", "notifications.column_settings.filter_bar.category": "Schnellfilterleiste", "notifications.column_settings.filter_bar.show": "Anzeigen", - "notifications.column_settings.follow": "Neue Folger_innen:", + "notifications.column_settings.follow": "Neue Folgende:", "notifications.column_settings.mention": "Erwähnungen:", "notifications.column_settings.poll": "Ergebnisse von Umfragen:", "notifications.column_settings.push": "Push-Benachrichtigungen", @@ -276,25 +298,28 @@ "notifications.filter.all": "Alle", "notifications.filter.boosts": "Geteilte Beiträge", "notifications.filter.favourites": "Favorisierungen", - "notifications.filter.follows": "Folger_innen", + "notifications.filter.follows": "Folgt", "notifications.filter.mentions": "Erwähnungen", "notifications.filter.polls": "Ergebnisse der Umfrage", "notifications.group": "{count} Benachrichtigungen", "poll.closed": "Geschlossen", "poll.refresh": "Aktualisieren", + "poll.total_people": "{count, plural, one {# Person} other {# Personen}}", "poll.total_votes": "{count, plural, one {# Stimme} other {# Stimmen}}", "poll.vote": "Abstimmen", + "poll.voted": "Du hast dafür gestimmt", "poll_button.add_poll": "Eine Umfrage erstellen", "poll_button.remove_poll": "Umfrage entfernen", "privacy.change": "Sichtbarkeit des Beitrags anpassen", "privacy.direct.long": "Wird an erwähnte Profile gesendet", "privacy.direct.short": "Direktnachricht", - "privacy.private.long": "Wird nur für deine Folger_innen sichtbar sein", - "privacy.private.short": "Nur für Folger_innen", + "privacy.private.long": "Wird nur für deine Folgende sichtbar sein", + "privacy.private.short": "Nur für Folgende", "privacy.public.long": "Wird in öffentlichen Zeitleisten erscheinen", "privacy.public.short": "Öffentlich", "privacy.unlisted.long": "Wird in öffentlichen Zeitleisten nicht gezeigt", "privacy.unlisted.short": "Nicht gelistet", + "refresh": "Aktualisieren", "regeneration_indicator.label": "Laden…", "regeneration_indicator.sublabel": "Deine Startseite wird gerade vorbereitet!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Personen", "search_results.hashtags": "Hashtags", "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.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", "status.admin_account": "Öffne Moderationsoberfläche für @{name}", "status.admin_status": "Öffne Beitrag in der Moderationsoberfläche", @@ -358,6 +384,7 @@ "status.show_more": "Mehr anzeigen", "status.show_more_all": "Alle Inhaltswarnungen aufklappen", "status.show_thread": "Zeige Konversation", + "status.uncached_media_warning": "Nicht verfügbar", "status.unmute_conversation": "Stummschaltung von Konversation aufheben", "status.unpin": "Vom Profil lösen", "suggestions.dismiss": "Empfehlung ausblenden", @@ -373,14 +400,22 @@ "time_remaining.moments": "Schließt in Kürze", "time_remaining.seconds": "{number, plural, one {# Sekunde} other {# Sekunden}} verbleibend", "trends.count_by_accounts": "{count} {rawCount, plural, eine {Person} other {Personen}} reden darüber", + "trends.trending_now": "In den Trends", "ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.", "upload_area.title": "Zum Hochladen hereinziehen", "upload_button.label": "Mediendatei hinzufügen ({formats})", "upload_error.limit": "Dateiupload-Limit erreicht.", "upload_error.poll": "Dateiuploads sind in Kombination mit Umfragen nicht erlaubt.", "upload_form.description": "Für Menschen mit Sehbehinderung beschreiben", - "upload_form.focus": "Vorschaubild bearbeiten", + "upload_form.edit": "Bearbeiten", "upload_form.undo": "Löschen", + "upload_modal.analyzing_picture": "Analysiere Bild…", + "upload_modal.apply": "Übernehmen", + "upload_modal.description_placeholder": "Franz jagt im komplett verwahrlosten Taxi quer durch Bayern", + "upload_modal.detect_text": "Text aus Bild erkennen", + "upload_modal.edit_media": "Medien bearbeiten", + "upload_modal.hint": "Klicke oder ziehe den Kreis auf die Vorschau, um den Brennpunkt auszuwählen, der immer auf allen Vorschaubilder angezeigt wird.", + "upload_modal.preview_label": "Vorschau ({ratio})", "upload_progress.label": "Wird hochgeladen …", "video.close": "Video schließen", "video.exit_fullscreen": "Vollbild verlassen", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 236d573e87c22fccd409d7e833acdae66eca603b..62d822615e79993e6df0cc02a8c8114bf43f897b 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -8,6 +8,14 @@ { "defaultMessage": "An unexpected error occurred.", "id": "alert.unexpected.message" + }, + { + "defaultMessage": "Rate limited", + "id": "alert.rate_limited.title" + }, + { + "defaultMessage": "Please retry after {retry_time, time, medium}.", + "id": "alert.rate_limited.message" } ], "path": "app/javascript/mastodon/actions/alerts.json" @@ -71,6 +79,15 @@ ], "path": "app/javascript/mastodon/components/account.json" }, + { + "descriptors": [ + { + "defaultMessage": "{count} per week", + "id": "autosuggest_hashtag.per_week" + } + ], + "path": "app/javascript/mastodon/components/autosuggest_hashtag.json" + }, { "descriptors": [ { @@ -131,6 +148,27 @@ ], "path": "app/javascript/mastodon/components/domain.json" }, + { + "descriptors": [ + { + "defaultMessage": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "id": "error.unexpected_crash.explanation" + }, + { + "defaultMessage": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "id": "error.unexpected_crash.next_steps" + }, + { + "defaultMessage": "Report issue", + "id": "errors.unexpected_crash.report_issue" + }, + { + "defaultMessage": "Copy stacktrace to clipboard", + "id": "errors.unexpected_crash.copy_stacktrace" + } + ], + "path": "app/javascript/mastodon/components/error_boundary.json" + }, { "descriptors": [ { @@ -158,6 +196,15 @@ ], "path": "app/javascript/mastodon/components/load_more.json" }, + { + "descriptors": [ + { + "defaultMessage": "{count, plural, one {# new item} other {# new items}}", + "id": "load_pending" + } + ], + "path": "app/javascript/mastodon/components/load_pending.json" + }, { "descriptors": [ { @@ -173,6 +220,10 @@ "defaultMessage": "Toggle visibility", "id": "media_gallery.toggle_visible" }, + { + "defaultMessage": "Not available", + "id": "status.uncached_media_warning" + }, { "defaultMessage": "Sensitive content", "id": "status.sensitive_warning" @@ -203,6 +254,19 @@ "defaultMessage": "Closed", "id": "poll.closed" }, + { + "defaultMessage": "You voted for this answer", + "description": "Tooltip of the \"voted\" checkmark in polls", + "id": "poll.voted" + }, + { + "defaultMessage": "{count, plural, one {# person} other {# people}}", + "id": "poll.total_people" + }, + { + "defaultMessage": "{count, plural, one {# vote} other {# votes}}", + "id": "poll.total_votes" + }, { "defaultMessage": "Vote", "id": "poll.vote" @@ -210,13 +274,22 @@ { "defaultMessage": "Refresh", "id": "poll.refresh" + } + ], + "path": "app/javascript/mastodon/components/poll.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Loading…", + "id": "regeneration_indicator.label" }, { - "defaultMessage": "{count, plural, one {# vote} other {# votes}}", - "id": "poll.total_votes" + "defaultMessage": "Your home feed is being prepared!", + "id": "regeneration_indicator.sublabel" } ], - "path": "app/javascript/mastodon/components/poll.json" + "path": "app/javascript/mastodon/components/regeneration_indicator.json" }, { "descriptors": [ @@ -389,19 +462,6 @@ ], "path": "app/javascript/mastodon/components/status_content.json" }, - { - "descriptors": [ - { - "defaultMessage": "Loading…", - "id": "regeneration_indicator.label" - }, - { - "defaultMessage": "Your home feed is being prepared!", - "id": "regeneration_indicator.sublabel" - } - ], - "path": "app/javascript/mastodon/components/status_list.json" - }, { "descriptors": [ { @@ -467,10 +527,6 @@ "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", "id": "confirmations.redraft.message" }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - }, { "defaultMessage": "Reply", "id": "confirmations.reply.confirm" @@ -478,14 +534,6 @@ { "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "id": "confirmations.reply.message" - }, - { - "defaultMessage": "Block & Report", - "id": "confirmations.block.block_and_report" - }, - { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" } ], "path": "app/javascript/mastodon/containers/status_container.json" @@ -522,26 +570,14 @@ "defaultMessage": "Unfollow", "id": "confirmations.unfollow.confirm" }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - }, { "defaultMessage": "Hide entire domain", "id": "confirmations.domain_block.confirm" }, - { - "defaultMessage": "Block & Report", - "id": "confirmations.block.block_and_report" - }, { "defaultMessage": "Are you sure you want to unfollow {name}?", "id": "confirmations.unfollow.message" }, - { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" - }, { "defaultMessage": "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", "id": "confirmations.domain_block.message" @@ -572,6 +608,10 @@ "defaultMessage": "Follow", "id": "account.follow" }, + { + "defaultMessage": "Cancel follow request", + "id": "account.cancel_follow_request" + }, { "defaultMessage": "Awaiting approval. Click to cancel follow request", "id": "account.requested" @@ -723,6 +763,27 @@ ], "path": "app/javascript/mastodon/features/account/components/header.json" }, + { + "descriptors": [ + { + "defaultMessage": "Play", + "id": "video.play" + }, + { + "defaultMessage": "Pause", + "id": "video.pause" + }, + { + "defaultMessage": "Mute sound", + "id": "video.mute" + }, + { + "defaultMessage": "Unmute sound", + "id": "video.unmute" + } + ], + "path": "app/javascript/mastodon/features/audio/index.json" + }, { "descriptors": [ { @@ -739,7 +800,7 @@ { "descriptors": [ { - "defaultMessage": "Media Only", + "defaultMessage": "Media only", "id": "community.column_settings.media_only" } ], @@ -799,6 +860,10 @@ { "defaultMessage": "Muted words", "id": "navigation_bar.filters" + }, + { + "defaultMessage": "Logout", + "id": "navigation_bar.logout" } ], "path": "app/javascript/mastodon/features/compose/components/action_bar.json" @@ -1033,6 +1098,10 @@ "defaultMessage": "Toots", "id": "search_results.statuses" }, + { + "defaultMessage": "Searching toots by their content is not enabled on this Mastodon server.", + "id": "search_results.statuses_fts_disabled" + }, { "defaultMessage": "Hashtags", "id": "search_results.hashtags" @@ -1089,29 +1158,38 @@ { "descriptors": [ { - "defaultMessage": "Uploading...", + "defaultMessage": "Uploading…", "id": "upload_progress.label" } ], - "path": "app/javascript/mastodon/features/compose/components/upload_progress.json" + "path": "app/javascript/mastodon/features/compose/components/upload_form.json" }, { "descriptors": [ - { - "defaultMessage": "Describe for the visually impaired", - "id": "upload_form.description" - }, { "defaultMessage": "Delete", "id": "upload_form.undo" }, { - "defaultMessage": "Crop", - "id": "upload_form.focus" + "defaultMessage": "Edit", + "id": "upload_form.edit" } ], "path": "app/javascript/mastodon/features/compose/components/upload.json" }, + { + "descriptors": [ + { + "defaultMessage": "Are you sure you want to log out?", + "id": "confirmations.logout.message" + }, + { + "defaultMessage": "Log out", + "id": "confirmations.logout.confirm" + } + ], + "path": "app/javascript/mastodon/features/compose/containers/navigation_container.json" + }, { "descriptors": [ { @@ -1200,10 +1278,68 @@ { "defaultMessage": "Compose new toot", "id": "navigation_bar.compose" + }, + { + "defaultMessage": "Are you sure you want to log out?", + "id": "confirmations.logout.message" + }, + { + "defaultMessage": "Log out", + "id": "confirmations.logout.confirm" } ], "path": "app/javascript/mastodon/features/compose/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "More", + "id": "status.more" + }, + { + "defaultMessage": "View conversation", + "id": "conversation.open" + }, + { + "defaultMessage": "Reply", + "id": "status.reply" + }, + { + "defaultMessage": "Mark as read", + "id": "conversation.mark_as_read" + }, + { + "defaultMessage": "Delete conversation", + "id": "conversation.delete" + }, + { + "defaultMessage": "Mute conversation", + "id": "status.mute_conversation" + }, + { + "defaultMessage": "Unmute conversation", + "id": "status.unmute_conversation" + }, + { + "defaultMessage": "With {names}", + "id": "conversation.with" + } + ], + "path": "app/javascript/mastodon/features/direct_timeline/components/conversation.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Reply", + "id": "confirmations.reply.confirm" + }, + { + "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "id": "confirmations.reply.message" + } + ], + "path": "app/javascript/mastodon/features/direct_timeline/containers/conversation_container.json" + }, { "descriptors": [ { @@ -1217,6 +1353,76 @@ ], "path": "app/javascript/mastodon/features/direct_timeline/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Follow", + "id": "account.follow" + }, + { + "defaultMessage": "Unfollow", + "id": "account.unfollow" + }, + { + "defaultMessage": "Awaiting approval", + "id": "account.requested" + }, + { + "defaultMessage": "Unblock @{name}", + "id": "account.unblock" + }, + { + "defaultMessage": "Unmute @{name}", + "id": "account.unmute" + }, + { + "defaultMessage": "Are you sure you want to unfollow {name}?", + "id": "confirmations.unfollow.message" + }, + { + "defaultMessage": "Toots", + "id": "account.posts" + }, + { + "defaultMessage": "Followers", + "id": "account.followers" + }, + { + "defaultMessage": "Never", + "id": "account.never_active" + }, + { + "defaultMessage": "Last active", + "id": "account.last_status" + } + ], + "path": "app/javascript/mastodon/features/directory/components/account_card.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Browse profiles", + "id": "column.directory" + }, + { + "defaultMessage": "Recently active", + "id": "directory.recently_active" + }, + { + "defaultMessage": "New arrivals", + "id": "directory.new_arrivals" + }, + { + "defaultMessage": "From {domain} only", + "id": "directory.local" + }, + { + "defaultMessage": "From known fediverse", + "id": "directory.federated" + } + ], + "path": "app/javascript/mastodon/features/directory/index.json" + }, { "descriptors": [ { @@ -1249,6 +1455,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Refresh", + "id": "refresh" + }, { "defaultMessage": "No one has favourited this toot yet. When someone does, they will show up here.", "id": "empty_column.favourites" @@ -1308,6 +1518,15 @@ ], "path": "app/javascript/mastodon/features/following/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Trending now", + "id": "trends.trending_now" + } + ], + "path": "app/javascript/mastodon/features/getting_started/components/trends.json" + }, { "descriptors": [ { @@ -1953,6 +2172,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Refresh", + "id": "refresh" + }, { "defaultMessage": "No one has boosted this toot yet. When someone does, they will show up here.", "id": "status.reblogs.empty" @@ -2080,10 +2303,6 @@ "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", "id": "confirmations.redraft.message" }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - }, { "defaultMessage": "Reply", "id": "confirmations.reply.confirm" @@ -2091,14 +2310,6 @@ { "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "id": "confirmations.reply.message" - }, - { - "defaultMessage": "Block & Report", - "id": "confirmations.block.block_and_report" - }, - { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" } ], "path": "app/javascript/mastodon/features/status/containers/detailed_status_container.json" @@ -2121,10 +2332,6 @@ "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", "id": "confirmations.redraft.message" }, - { - "defaultMessage": "Block", - "id": "confirmations.block.confirm" - }, { "defaultMessage": "Show more for all", "id": "status.show_more_all" @@ -2144,17 +2351,39 @@ { "defaultMessage": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "id": "confirmations.reply.message" + } + ], + "path": "app/javascript/mastodon/features/status/index.json" + }, + { + "descriptors": [ + { + "defaultMessage": "View context", + "id": "lightbox.view_context" + } + ], + "path": "app/javascript/mastodon/features/ui/components/audio_modal.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Are you sure you want to block {name}?", + "id": "confirmations.block.message" + }, + { + "defaultMessage": "Cancel", + "id": "confirmation_modal.cancel" }, { "defaultMessage": "Block & Report", "id": "confirmations.block.block_and_report" }, { - "defaultMessage": "Are you sure you want to block {name}?", - "id": "confirmations.block.message" + "defaultMessage": "Block", + "id": "confirmations.block.confirm" } ], - "path": "app/javascript/mastodon/features/status/index.json" + "path": "app/javascript/mastodon/features/ui/components/block_modal.json" }, { "descriptors": [ @@ -2227,6 +2456,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Close", + "id": "lightbox.close" + }, { "defaultMessage": "Embed", "id": "status.embed" @@ -2242,6 +2475,47 @@ ], "path": "app/javascript/mastodon/features/ui/components/embed_modal.json" }, + { + "descriptors": [ + { + "defaultMessage": "Close", + "id": "lightbox.close" + }, + { + "defaultMessage": "Apply", + "id": "upload_modal.apply" + }, + { + "defaultMessage": "A quick brown fox jumps over the lazy dog", + "id": "upload_modal.description_placeholder" + }, + { + "defaultMessage": "Edit media", + "id": "upload_modal.edit_media" + }, + { + "defaultMessage": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "id": "upload_modal.hint" + }, + { + "defaultMessage": "Describe for the visually impaired", + "id": "upload_form.description" + }, + { + "defaultMessage": "Analyzing picture…", + "id": "upload_modal.analyzing_picture" + }, + { + "defaultMessage": "Detect text from picture", + "id": "upload_modal.detect_text" + }, + { + "defaultMessage": "Preview ({ratio})", + "id": "upload_modal.preview_label" + } + ], + "path": "app/javascript/mastodon/features/ui/components/focal_point_modal.json" + }, { "descriptors": [ { @@ -2253,6 +2527,14 @@ }, { "descriptors": [ + { + "defaultMessage": "Are you sure you want to log out?", + "id": "confirmations.logout.message" + }, + { + "defaultMessage": "Log out", + "id": "confirmations.logout.confirm" + }, { "defaultMessage": "Invite people", "id": "getting_started.invite" @@ -2323,6 +2605,10 @@ "defaultMessage": "Are you sure you want to mute {name}?", "id": "confirmations.mute.message" }, + { + "defaultMessage": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "id": "confirmations.mute.explanation" + }, { "defaultMessage": "Hide notifications from this user?", "id": "mute_modal.hide_notifications" @@ -2368,6 +2654,10 @@ "defaultMessage": "Lists", "id": "navigation_bar.lists" }, + { + "defaultMessage": "Profile directory", + "id": "getting_started.directory" + }, { "defaultMessage": "Preferences", "id": "navigation_bar.preferences" @@ -2375,10 +2665,6 @@ { "defaultMessage": "Follows and followers", "id": "navigation_bar.follows_and_followers" - }, - { - "defaultMessage": "Profile directory", - "id": "navigation_bar.profile_directory" } ], "path": "app/javascript/mastodon/features/ui/components/navigation_panel.json" diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 50d7b1d7024e2987d29de4e295030655be5abc63..e395c8ba991168b92371cfdcf8c29a657e556218 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -1,9 +1,10 @@ { "account.add_or_remove_from_list": "Î Ïοσθήκη ή ΑφαίÏεση από λίστες", "account.badges.bot": "Μποτ", - "account.block": "Αποκλισμός @{name}", + "account.block": "Αποκλεισμός @{name}", "account.block_domain": "ΑπόκÏυψε τα πάντα από το {domain}", "account.blocked": "ΑποκλεισμÎνος/η", + "account.cancel_follow_request": "ΑκÏÏωση αιτήματος παÏακολοÏθησης", "account.direct": "Î Ïοσωπικό μήνυμα Ï€Ïος @{name}", "account.domain_blocked": "ΚÏυμμÎνος τομÎας", "account.edit_profile": "ΕπεξεÏγασία Ï€Ïοφίλ", @@ -15,6 +16,7 @@ "account.follows.empty": "Αυτός ο χÏήστης δεν ακολουθεί κανÎναν ακόμα.", "account.follows_you": "Σε ακολουθεί", "account.hide_reblogs": "ΑπόκÏυψη Ï€Ïοωθήσεων από @{name}", + "account.last_status": "Τελευταία δÏαστηÏιότητα", "account.link_verified_on": "Η ιδιοκτησία Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… συνδÎσμου ελÎχθηκε την {date}", "account.locked_info": "Η κατάσταση αποÏÏήτου Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÎµÎ¯Î½Î±Î¹ κλειδωμÎνη. Ο ιδιοκτήτης επιβεβαιώνει χειÏοκίνητα ποιος μποÏεί να τον ακολουθήσει.", "account.media": "ΠολυμÎσα", @@ -23,6 +25,7 @@ "account.mute": "Σώπασε @{name}", "account.mute_notifications": "Σώπασε τις ειδοποιήσεις από @{name}", "account.muted": "ΑποσιωπημÎνος/η", + "account.never_active": "ΠοτÎ", "account.posts": "Τουτ", "account.posts_with_replies": "Τουτ και απαντήσεις", "account.report": "Κατάγγειλε @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Διακοπή παÏακολοÏθησης", "account.unmute": "Διακοπή αποσιώπησης @{name}", "account.unmute_notifications": "Διακοπή αποσιώπησης ειδοποιήσεων του/της @{name}", + "alert.rate_limited.message": "ΠαÏακαλοÏμε δοκίμασε ξανά Î±Ï†Î¿Ï Ï€ÎµÏάσει η {retry_time, time, medium}.", + "alert.rate_limited.title": "ΠεÏιοÏισμός συχνότητας", "alert.unexpected.message": "Î ÏοÎκυψε απÏοσδόκητο σφάλμα.", "alert.unexpected.title": "Εεπ!", + "autosuggest_hashtag.per_week": "{count} ανα εβδομάδα", "boost_modal.combo": "ΜποÏείς να πατήσεις {combo} για να το Ï€ÏοσπεÏάσεις αυτό την επόμενη φοÏά", "bundle_column_error.body": "Κάτι πήγε στÏαβά ενώ φοÏτωνόταν αυτό το στοιχείο.", "bundle_column_error.retry": "Δοκίμασε ξανά", @@ -47,6 +53,7 @@ "column.blocks": "ΑποκλεισμÎνοι χÏήστες", "column.community": "Τοπική Ïοή", "column.direct": "Î Ïοσωπικά μηνÏματα", + "column.directory": "Δες Ï€Ïοφίλ", "column.domain_blocks": "ΚÏυμμÎνοι τομείς", "column.favourites": "ΑγαπημÎνα", "column.follow_requests": "Αιτήματα ακολοÏθησης", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "ΣίγουÏα θες να διαγÏάψεις οÏιστικά αυτή τη λίστα;", "confirmations.domain_block.confirm": "ΑπόκÏυψη ολόκληÏου του τομÎα", "confirmations.domain_block.message": "ΣίγουÏα θες να μπλοκάÏεις ολόκληÏο το {domain}; Συνήθως μεÏικά εστιασμÎνα μπλοκ ή αποσιωπήσεις επαÏκοÏν και Ï€ÏοτιμοÏνται. Δεν θα βλÎπεις πεÏιεχόμενο από αυτό τον κόμβο σε καμία δημόσια Ïοή, οÏτε στις ειδοποιήσεις σου. Όσους ακόλουθους Îχεις αυτό αυτό τον κόμβο θα αφαιÏεθοÏν.", + "confirmations.logout.confirm": "ΑποσÏνδεση", + "confirmations.logout.message": "ΣίγουÏα θÎλεις να αποσυνδεθείς;", "confirmations.mute.confirm": "Αποσιώπηση", + "confirmations.mute.explanation": "Αυτό θα κÏÏψει τις δημοσιεÏσεις τους και τις δημοσιεÏσεις που τους αναφÎÏουν, αλλά θα συνεχίσουν να μποÏοÏν να βλÎπουν τις δημοσιεÏσεις σου και να σε ακολουθοÏν.", "confirmations.mute.message": "ΣίγουÏα θες να αποσιωπήσεις {name};", "confirmations.redraft.confirm": "ΔιαγÏαφή & ξαναγÏάψιμο", "confirmations.redraft.message": "ΣίγουÏα θÎλεις να σβήσεις αυτή την κατάσταση και να την ξαναγÏάψεις; Οι αναφοÏÎÏ‚ και τα αγαπημÎνα της θα χαθοÏν ενώ οι απαντήσεις Ï€Ïος αυτή θα μείνουν οÏφανÎÏ‚.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Απαντώντας τώÏα θα αντικαταστήσεις το κείμενο που ήδη γÏάφεις. ΣίγουÏα θÎλεις να συνεχίσεις;", "confirmations.unfollow.confirm": "Διακοπή παÏακολοÏθησης", "confirmations.unfollow.message": "ΣίγουÏα θες να πάψεις να ακολουθείς τον/την {name};", + "conversation.delete": "ΔιαγÏαφή συζήτησης", + "conversation.mark_as_read": "Σήμανση ως αναγνωσμÎνο", + "conversation.open": "Î Ïοβολή συνομιλίας", + "conversation.with": "Με {names}", + "directory.federated": "Από το γνωστό fediverse", + "directory.local": "Μόνο από {domain}", + "directory.new_arrivals": "ÎÎες αφίξεις", + "directory.recently_active": "Î Ïόσφατα ενεÏγοί", "embed.instructions": "Ενσωματώστε αυτή την κατάσταση στην ιστοσελίδα σας αντιγÏάφοντας τον παÏακάτω κώδικα.", "embed.preview": "ΟÏίστε πως θα φαίνεται:", "emoji_button.activity": "ΔÏαστηÏιότητα", @@ -113,7 +131,7 @@ "emoji_button.objects": "Αντικείμενα", "emoji_button.people": "ΆνθÏωποι", "emoji_button.recent": "Δημοφιλή", - "emoji_button.search": "Αναζήτηση…", + "emoji_button.search": "Αναζήτηση...", "emoji_button.search_results": "ΑποτελÎσματα αναζήτησης", "emoji_button.symbols": "ΣÏμβολα", "emoji_button.travel": "Ταξίδια & Τοποθεσίες", @@ -134,6 +152,10 @@ "empty_column.mutes": "Δεν Îχεις αποσιωπήσει κανÎνα χÏήστη ακόμα.", "empty_column.notifications": "Δεν Îχεις ειδοποιήσεις ακόμα. ΑλληλεπίδÏασε με άλλους χÏήστες για να ξεκινήσεις την κουβÎντα.", "empty_column.public": "Δεν υπάÏχει τίποτα εδώ! ΓÏάψε κάτι δημόσιο, ή ακολοÏθησε χειÏοκίνητα χÏήστες από άλλους κόμβους για να τη γεμίσεις", + "error.unexpected_crash.explanation": "Είτε λόγω λάθους στον κώδικά μας ή λόγω ασυμβατότητας με τον browser, η σελίδα δε μπόÏεσε να εμφανιστεί σωστά.", + "error.unexpected_crash.next_steps": "Δοκίμασε να ανανεώσεις τη σελίδα. Αν αυτό δε βοηθήσει, ίσως να μποÏÎσεις να χÏησιμοποιήσεις το Mastodon μÎσω διαφοÏÎµÏ„Î¹ÎºÎ¿Ï browser ή κάποιας εφαÏμογής.", + "errors.unexpected_crash.copy_stacktrace": "ΑντιγÏαφή μηνυμάτων κώδικα στο Ï€ÏόχειÏο", + "errors.unexpected_crash.report_issue": "ΑναφοÏά Ï€Ïοβλήματος", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -158,7 +180,7 @@ "hashtag.column_settings.tag_mode.any": "Οποιοδήποτε από αυτά", "hashtag.column_settings.tag_mode.none": "ΚανÎνα από αυτά", "hashtag.column_settings.tag_toggle": "Î Ïοσθήκη επιπλÎον ταμπελών για την κολώνα", - "home.column_settings.basic": "Βασικά", + "home.column_settings.basic": "ΒασικÎÏ‚ Ïυθμίσεις", "home.column_settings.show_reblogs": "Εμφάνιση Ï€Ïοωθήσεων", "home.column_settings.show_replies": "Εμφάνιση απαντήσεων", "intervals.full.days": "{number, plural, one {# μÎÏα} other {# μÎÏες}}", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Τίτλος νÎας λίστα", "lists.search": "Αναζήτησε Î¼ÎµÏ„Î±Î¾Ï Ï„Ï‰Î½ ανθÏώπων που ακουλουθείς", "lists.subheading": "Οι λίστες σου", + "load_pending": "{count, plural, one {# νÎο} other {# νÎα}}", "loading_indicator.label": "ΦοÏτώνει...", "media_gallery.toggle_visible": "Εναλλαγή οÏατότητας", "missing_indicator.label": "Δε βÏÎθηκε", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "ΑγαπημÎνα", "navigation_bar.filters": "ΑποσιωπημÎνες λÎξεις", "navigation_bar.follow_requests": "Αιτήματα ακολοÏθησης", - "navigation_bar.follows_and_followers": "Ακολουθεί και ακολουθείται", + "navigation_bar.follows_and_followers": "Ακολουθείς και σε ακολουθοÏν", "navigation_bar.info": "ΠληÏοφοÏίες κόμβου", "navigation_bar.keyboard_shortcuts": "ΣυντομεÏσεις", "navigation_bar.lists": "Λίστες", @@ -251,13 +274,12 @@ "navigation_bar.personal": "Î Ïοσωπικά", "navigation_bar.pins": "ΚαÏφιτσωμÎνα τουτ", "navigation_bar.preferences": "Î Ïοτιμήσεις", - "navigation_bar.profile_directory": "Κατάλογος λογαÏιασμών", "navigation_bar.public_timeline": "Ομοσπονδιακή Ïοή", "navigation_bar.security": "Ασφάλεια", "notification.favourite": "Ο/Η {name} σημείωσε ως αγαπημÎνη την κατάστασή σου", "notification.follow": "Ο/Η {name} σε ακολοÏθησε", "notification.mention": "Ο/Η {name} σε ανÎφεÏε", - "notification.poll": "Έλαβε Ï„Îλος μια από τις ψηφοφοÏίες που συμμετείχες", + "notification.poll": "Τελείωσε μια από τις ψηφοφοÏίες που συμμετείχες", "notification.reblog": "Ο/Η {name} Ï€Ïοώθησε την κατάστασή σου", "notifications.clear": "ΚαθαÏισμός ειδοποιήσεων", "notifications.clear_confirmation": "ΣίγουÏα θÎλεις να καθαÏίσεις όλες τις ειδοποιήσεις σου;", @@ -282,8 +304,10 @@ "notifications.group": "{count} ειδοποιήσεις", "poll.closed": "Κλειστή", "poll.refresh": "ΑνανÎωση", + "poll.total_people": "{count, plural, one {# άτομο} other {# άτομα}}", "poll.total_votes": "{count, plural, one {# ψήφος} other {# ψήφοι}}", "poll.vote": "Ψήφισε", + "poll.voted": "Ψηφίσατε αυτήν την απάντηση", "poll_button.add_poll": "Î Ïοσθήκη δημοσκόπησης", "poll_button.remove_poll": "ΑφαίÏεση δημοσκόπησης", "privacy.change": "Î ÏοσαÏμογή ιδιωτικότητας δημοσίευσης", @@ -295,6 +319,7 @@ "privacy.public.short": "Δημόσιο", "privacy.unlisted.long": "Μην δημοσιεÏσεις στις δημόσιες ÏοÎÏ‚", "privacy.unlisted.short": "Μη καταχωÏημÎνα", + "refresh": "ΑνανÎωση", "regeneration_indicator.label": "ΦοÏτώνει…", "regeneration_indicator.sublabel": "Η αÏχική σου Ïοή ετοιμάζεται!", "relative_time.days": "{number}η", @@ -319,6 +344,7 @@ "search_results.accounts": "ΆνθÏωποι", "search_results.hashtags": "ΤαμπÎλες", "search_results.statuses": "Τουτ", + "search_results.statuses_fts_disabled": "Η αναζήτηση τουτ βάσει του πεÏÎ¹ÎµÏ‡ÏŒÎ¼ÎµÎ½Î¿Ï Ï„Î¿Ï…Ï‚ δεν είναι ενεÏγοποιημÎνη σε αυτό τον κόμβο.", "search_results.total": "{count, number} {count, plural, zero {αποτελÎσματα} one {αποτÎλεσμα} other {αποτελÎσματα}}", "status.admin_account": "Άνοιγμα λειτουÏγίας διαμεσολάβησης για τον/την @{name}", "status.admin_status": "Άνοιγμα αυτής της δημοσίευσης στη λειτουÏγία διαμεσολάβησης", @@ -358,13 +384,14 @@ "status.show_more": "Δείξε πεÏισσότεÏα", "status.show_more_all": "Δείξε πεÏισσότεÏα για όλα", "status.show_thread": "Εμφάνιση νήματος", + "status.uncached_media_warning": "Μη διαθÎσιμα", "status.unmute_conversation": "ΔιÎκοψε την αποσιώπηση της συζήτησης", "status.unpin": "ΞεκαÏφίτσωσε από το Ï€Ïοφίλ", "suggestions.dismiss": "ΑπόÏÏιψη Ï€Ïότασης", "suggestions.header": "Ίσως να ενδιαφÎÏεσαι για…", "tabs_bar.federated_timeline": "Ομοσπονδιακή", "tabs_bar.home": "ΑÏχική", - "tabs_bar.local_timeline": "Τοπικά", + "tabs_bar.local_timeline": "Τοπική", "tabs_bar.notifications": "Ειδοποιήσεις", "tabs_bar.search": "Αναζήτηση", "time_remaining.days": "απομÎνουν {number, plural, one {# ημÎÏα} other {# ημÎÏες}}", @@ -373,14 +400,22 @@ "time_remaining.moments": "ΑπομÎνουν στιγμÎÏ‚", "time_remaining.seconds": "απομÎνουν {number, plural, one {# δευτεÏόλεπτο} other {# δευτεÏόλεπτα}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} μιλάνε", + "trends.trending_now": "Δημοφιλή τώÏα", "ui.beforeunload": "Το Ï€ÏοσχÎδιό σου θα χαθεί αν φÏγεις από το Mastodon.", "upload_area.title": "Drag & drop για να ανεβάσεις", - "upload_button.label": "Î Ïόσθεσε πολυμÎσα (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Î Ïόσθεσε πολυμÎσα ({formats})", "upload_error.limit": "ΥπÎÏβαση οÏίου μεγÎθους ανεβασμÎνων αÏχείων.", "upload_error.poll": "Στις δημοσκοπήσεις δεν επιτÏÎπεται η μεταφόÏτωση αÏχείου.", "upload_form.description": "ΠεÏιÎγÏαψε για όσους & όσες Îχουν Ï€Ïοβλήματα ÏŒÏασης", - "upload_form.focus": "Αλλαγή Ï€Ïοεπισκόπησης", + "upload_form.edit": "ΕνημÎÏωση", "upload_form.undo": "ΔιαγÏαφή", + "upload_modal.analyzing_picture": "Ανάλυση εικόνας…", + "upload_modal.apply": "ΕφαÏμογή", + "upload_modal.description_placeholder": "ΛÏκος μαÏÏος και ισχνός του πατÎÏα του καημός", + "upload_modal.detect_text": "ΑναγνώÏιση κειμÎνου από την εικόνα", + "upload_modal.edit_media": "ΕπεξεÏγασία ΠολυμÎσων", + "upload_modal.hint": "Κάνε κλικ ή σείÏε τον κÏκλο στην Ï€Ïοεπισκόπηση για να επιλÎξεις το σημείο εστίασης που θα είναι πάντα εμφανÎÏ‚ σε όλες τις μικÏογÏαφίες.", + "upload_modal.preview_label": "Î Ïοεπισκόπηση ({ratio})", "upload_progress.label": "Ανεβαίνει...", "video.close": "Κλείσε το βίντεο", "video.exit_fullscreen": "Έξοδος από πλήÏη οθόνη", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index acb7393265a36218fd1a59c8ada74718583f5717..29070422dbdecc12ed99cabb9cc6afa79d8a4d2e 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -4,6 +4,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Edit profile", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Follows you", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Mute @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.report": "Report @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You can press {combo} to skip this next time", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blocked users", "column.community": "Local timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -147,7 +169,7 @@ "getting_started.heading": "Getting started", "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.security": "Security", + "getting_started.security": "Account settings", "getting_started.terms": "Terms of service", "hashtag.column_header.tag_mode.all": "and {additional}", "hashtag.column_header.tag_mode.any": "or {additional}", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Loading...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferences", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federated timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not post to public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media ({formats})", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Change preview", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Uploading...", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 709bd093da39258f9e7a7b97cb9c6e0b749bfc5f..0835ae5b07b6edd31039c163459ad10ce35b727c 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -4,6 +4,7 @@ "account.block": "Bloki @{name}", "account.block_domain": "KaÅi ĉion de {domain}", "account.blocked": "Blokita", + "account.cancel_follow_request": "Nuligi peton de sekvado", "account.direct": "Rekte mesaÄi @{name}", "account.domain_blocked": "Domajno kaÅita", "account.edit_profile": "Redakti profilon", @@ -15,14 +16,16 @@ "account.follows.empty": "Tiu uzanto ankoraÅ ne sekvas iun.", "account.follows_you": "Sekvas vin", "account.hide_reblogs": "KaÅi diskonigojn de @{name}", + "account.last_status": "Laste aktiva", "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.media": "AÅdovidaĵoj", "account.mention": "Mencii @{name}", "account.moved_to": "{name} moviÄis al:", "account.mute": "Silentigi @{name}", - "account.mute_notifications": "Silentigi sciigojn el @{name}", + "account.mute_notifications": "Silentigi sciigojn de @{name}", "account.muted": "Silentigita", + "account.never_active": "Neniam", "account.posts": "MesaÄoj", "account.posts_with_replies": "Kun respondoj", "account.report": "Signali @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Ne plu sekvi", "account.unmute": "Malsilentigi @{name}", "account.unmute_notifications": "Malsilentigi sciigojn de @{name}", + "alert.rate_limited.message": "Bonvolu reprovi post {retry_time, time, medium}.", + "alert.rate_limited.title": "MesaÄkvante limigita", "alert.unexpected.message": "Neatendita eraro okazis.", "alert.unexpected.title": "Ups!", + "autosuggest_hashtag.per_week": "{count} semajne", "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.retry": "Bonvolu reprovi", @@ -47,6 +53,7 @@ "column.blocks": "Blokitaj uzantoj", "column.community": "Loka tempolinio", "column.direct": "Rektaj mesaÄoj", + "column.directory": "Trarigardi profilojn", "column.domain_blocks": "KaÅitaj domajnoj", "column.favourites": "Stelumoj", "column.follow_requests": "Petoj de sekvado", @@ -71,20 +78,20 @@ "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaÄojn, kiuj estas nur por sekvantoj.", "compose_form.lock_disclaimer.lock": "Ålosita", "compose_form.placeholder": "Pri kio vi pensas?", - "compose_form.poll.add_option": "Aldoni elekto", - "compose_form.poll.duration": "Balotenketo daÅro", - "compose_form.poll.option_placeholder": "elekto {number}", - "compose_form.poll.remove_option": "Forigi ĉi tiu elekton", + "compose_form.poll.add_option": "Aldoni elekteblon", + "compose_form.poll.duration": "Balotenketa daÅro", + "compose_form.poll.option_placeholder": "Elekteblo {number}", + "compose_form.poll.remove_option": "Forigi ĉi tiu elekteblon", "compose_form.publish": "Hup", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Marki aÅdovidaĵojn kiel tiklaj", + "compose_form.sensitive.hide": "Marki la aÅdovidaĵojn kiel tiklaj", "compose_form.sensitive.marked": "AÅdovidaĵo markita tikla", "compose_form.sensitive.unmarked": "AÅdovidaĵo ne markita tikla", "compose_form.spoiler.marked": "Teksto kaÅita malantaÅ averto", "compose_form.spoiler.unmarked": "Teksto ne kaÅita", "compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie", "confirmation_modal.cancel": "Nuligi", - "confirmations.block.block_and_report": "Bloki & Signali", + "confirmations.block.block_and_report": "Bloki kaj signali", "confirmations.block.confirm": "Bloki", "confirmations.block.message": "Ĉu vi certas, ke vi volas bloki {name}?", "confirmations.delete.confirm": "Forigi", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Ĉu vi certas, ke vi volas porĉiame forigi ĉi tiun liston?", "confirmations.domain_block.confirm": "KaÅi la tutan domajnon", "confirmations.domain_block.message": "Ĉu vi vere, vere certas, ke vi volas tute bloki {domain}? Plej ofte, trafa blokado kaj silentigado sufiĉas kaj preferindas. Vi ne vidos enhavon de tiu domajno en publika tempolinio aÅ en viaj sciigoj. Viaj sekvantoj de tiu domajno estos forigitaj.", + "confirmations.logout.confirm": "Elsaluti", + "confirmations.logout.message": "Ĉu vi certas ke vi volas elsaluti?", "confirmations.mute.confirm": "Silentigi", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Ĉu vi certas, ke vi volas silentigi {name}?", "confirmations.redraft.confirm": "Forigi kaj reskribi", "confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun mesaÄon kaj reskribi Äin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaÄo estos senparentaj.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Respondi nun anstataÅigos la mesaÄon, kiun vi nun skribas. Ĉu vi certas, ke vi volas daÅrigi?", "confirmations.unfollow.confirm": "Ne plu sekvi", "confirmations.unfollow.message": "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?", + "conversation.delete": "Forigi konversacion", + "conversation.mark_as_read": "Marki legita", + "conversation.open": "Vidi konversacion", + "conversation.with": "Kun {names}", + "directory.federated": "El konata fediverso", + "directory.local": "Nur de {domain}", + "directory.new_arrivals": "Novaj alvenoj", + "directory.recently_active": "Lastatempe aktiva", "embed.instructions": "Enkorpigu ĉi tiun mesaÄon en vian retejon per kopio de la suba kodo.", "embed.preview": "Äœi aperos tiel:", "emoji_button.activity": "Agadoj", @@ -134,6 +152,10 @@ "empty_column.mutes": "Vi ne ankoraÅ silentigis iun uzanton.", "empty_column.notifications": "Vi ankoraÅ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.", "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aÅ mane sekvu uzantojn de aliaj serviloj por plenigi la publikan tempolinion", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Raporti problemon", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -157,7 +179,7 @@ "hashtag.column_settings.tag_mode.all": "Ĉiuj", "hashtag.column_settings.tag_mode.any": "Iu ajn", "hashtag.column_settings.tag_mode.none": "Neniu", - "hashtag.column_settings.tag_toggle": "Inkluzivi pluajn etikedojn por ĉi tiu kolumno", + "hashtag.column_settings.tag_toggle": "Aldoni pliajn etikedojn por ĉi tiu kolumno", "home.column_settings.basic": "Bazaj agordoj", "home.column_settings.show_reblogs": "Montri diskonigojn", "home.column_settings.show_replies": "Montri respondojn", @@ -165,9 +187,9 @@ "intervals.full.hours": "{number, plural, one {# horo} other {# horoj}}", "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutoj}}", "introduction.federation.action": "Sekva", - "introduction.federation.federated.headline": "Federacio", + "introduction.federation.federated.headline": "Fratara", "introduction.federation.federated.text": "Publikaj mesaÄoj el aliaj serviloj de la Fediverse aperos en la fratara tempolinio.", - "introduction.federation.home.headline": "Heimo", + "introduction.federation.home.headline": "Hejmo", "introduction.federation.home.text": "MesaÄoj de homoj, kiujn vi sekvas, aperos en via hejma fluo. Vi povas sekvi iun ajn de ajna servilo!", "introduction.federation.local.headline": "Loka", "introduction.federation.local.text": "Publikaj mesaÄoj de homoj de via servilo aperos en la loka tempolinio.", @@ -216,7 +238,7 @@ "lightbox.close": "Fermi", "lightbox.next": "Sekva", "lightbox.previous": "AntaÅa", - "lightbox.view_context": "Vidi kontekston", + "lightbox.view_context": "Vidi kuntekston", "lists.account.add": "Aldoni al la listo", "lists.account.remove": "Forigi de la listo", "lists.delete": "Forigi la liston", @@ -226,11 +248,12 @@ "lists.new.title_placeholder": "Titolo de la nova listo", "lists.search": "Serĉi inter la homoj, kiujn vi sekvas", "lists.subheading": "Viaj listoj", + "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}", "loading_indicator.label": "Åœargado…", "media_gallery.toggle_visible": "Baskuligi videblecon", "missing_indicator.label": "Ne trovita", "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita", - "mute_modal.hide_notifications": "Ĉu vi volas kaÅi la sciigojn el ĉi tiu uzanto?", + "mute_modal.hide_notifications": "Ĉu vi volas kaÅi la sciigojn de ĉi tiu uzanto?", "navigation_bar.apps": "Telefonaj aplikaĵoj", "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.community_timeline": "Loka tempolinio", @@ -251,13 +274,12 @@ "navigation_bar.personal": "Persone", "navigation_bar.pins": "Alpinglitaj mesaÄoj", "navigation_bar.preferences": "Preferoj", - "navigation_bar.profile_directory": "Profilujo", "navigation_bar.public_timeline": "Fratara tempolinio", "navigation_bar.security": "Sekureco", "notification.favourite": "{name} stelumis vian mesaÄon", "notification.follow": "{name} eksekvis vin", "notification.mention": "{name} menciis vin", - "notification.poll": "Balotenketo ke vi balotis estas finita", + "notification.poll": "Partoprenita balotenketo finiÄis", "notification.reblog": "{name} diskonigis vian mesaÄon", "notifications.clear": "ForviÅi sciigojn", "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviÅi ĉiujn viajn sciigojn?", @@ -268,7 +290,7 @@ "notifications.column_settings.filter_bar.show": "Montri", "notifications.column_settings.follow": "Novaj sekvantoj:", "notifications.column_settings.mention": "Mencioj:", - "notifications.column_settings.poll": "Balotenketo rezulto:", + "notifications.column_settings.poll": "Balotenketaj rezultoj:", "notifications.column_settings.push": "PuÅsciigoj", "notifications.column_settings.reblog": "Diskonigoj:", "notifications.column_settings.show": "Montri en kolumno", @@ -278,12 +300,14 @@ "notifications.filter.favourites": "Stelumoj", "notifications.filter.follows": "Sekvoj", "notifications.filter.mentions": "Mencioj", - "notifications.filter.polls": "Balotenketoj rezultoj", + "notifications.filter.polls": "Balotenketaj rezultoj", "notifications.group": "{count} sciigoj", "poll.closed": "Finita", "poll.refresh": "Aktualigi", + "poll.total_people": "{count, plural, one {# homo} other {# homoj}}", "poll.total_votes": "{count, plural, one {# voĉdono} other {# voĉdonoj}}", "poll.vote": "Voĉdoni", + "poll.voted": "Vi elektis por ĉi tiu respondo", "poll_button.add_poll": "Aldoni balotenketon", "poll_button.remove_poll": "Forigi balotenketon", "privacy.change": "Agordi mesaÄan privatecon", @@ -295,6 +319,7 @@ "privacy.public.short": "Publika", "privacy.unlisted.long": "Ne afiÅi en publikaj tempolinioj", "privacy.unlisted.short": "Nelistigita", + "refresh": "RefreÅigu", "regeneration_indicator.label": "Åœargado…", "regeneration_indicator.sublabel": "Via hejma fluo pretiÄas!", "relative_time.days": "{number}t", @@ -319,11 +344,12 @@ "search_results.accounts": "Homoj", "search_results.hashtags": "Kradvortoj", "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.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}", "status.admin_account": "Malfermi la kontrolan interfacon por @{name}", "status.admin_status": "Malfermi ĉi tiun mesaÄon en la kontrola interfaco", "status.block": "Bloki @{name}", - "status.cancel_reblog_private": "Eksdiskonigi", + "status.cancel_reblog_private": "Ne plu diskonigi", "status.cannot_reblog": "Ĉi tiu mesaÄo ne diskonigeblas", "status.copy": "Kopii la ligilon al la mesaÄo", "status.delete": "Forigi", @@ -358,6 +384,7 @@ "status.show_more": "Grandigi", "status.show_more_all": "Grandigi ĉiujn", "status.show_thread": "Montri la fadenon", + "status.uncached_media_warning": "Nedisponebla", "status.unmute_conversation": "Malsilentigi la konversacion", "status.unpin": "Depingli de profilo", "suggestions.dismiss": "Forigi la proponon", @@ -367,26 +394,34 @@ "tabs_bar.local_timeline": "Loka tempolinio", "tabs_bar.notifications": "Sciigoj", "tabs_bar.search": "Serĉi", - "time_remaining.days": "{number, plural, one {# tago} other {# tagoj}} restanta", - "time_remaining.hours": "{number, plural, one {# horo} other {# horoj}} restanta", - "time_remaining.minutes": "{number, plural, one {# minuto} other {# minutoj}} restanta", - "time_remaining.moments": "Momento restanta", - "time_remaining.seconds": "{number, plural, one {# sekundo} other {# sekundoj}} restanta", + "time_remaining.days": "{number, plural, one {# tago} other {# tagoj}} restas", + "time_remaining.hours": "{number, plural, one {# horo} other {# horoj}} restas", + "time_remaining.minutes": "{number, plural, one {# minuto} other {# minutoj}} restas", + "time_remaining.moments": "Momenteto restas", + "time_remaining.seconds": "{number, plural, one {# sekundo} other {# sekundoj}} restas", "trends.count_by_accounts": "{count} {rawCount, plural, one {persono} other {personoj}} parolas", + "trends.trending_now": "Nunaj furoraĵoj", "ui.beforeunload": "Via malneto perdiÄos se vi eliras de Mastodon.", "upload_area.title": "Altreni kaj lasi por alÅuti", "upload_button.label": "Aldoni aÅdovidaĵon (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Limo de dosiera alÅutado transpasita.", - "upload_error.poll": "AlÅuto de dosiero ne permisita kun balotenketo", + "upload_error.poll": "AlÅuto de dosiero ne permesita kun balotenketo.", "upload_form.description": "Priskribi por misvidantaj homoj", - "upload_form.focus": "AntaÅvido de ÅanÄo", + "upload_form.edit": "Redakti", "upload_form.undo": "Forigi", + "upload_modal.analyzing_picture": "Bilda analizado…", + "upload_modal.apply": "Apliki", + "upload_modal.description_placeholder": "LaÅ Ludoviko Zamenhof bongustas freÅa ĉeÄ¥a manÄaĵo kun spicoj", + "upload_modal.detect_text": "Detekti tekston de la bildo", + "upload_modal.edit_media": "Redakti aÅdovidaĵon", + "upload_modal.hint": "Klaku aÅ trenu la cirklon en la antaÅvidilo por elekti la fokuspunkton kiu ĉiam videblos en ĉiuj etigitaj bildoj.", + "upload_modal.preview_label": "AntaÅvido ({ratio})", "upload_progress.label": "AlÅutado…", - "video.close": "Fermi videon", + "video.close": "Fermi la videon", "video.exit_fullscreen": "Eksigi plenekrana", - "video.expand": "Grandigi videon", + "video.expand": "Grandigi la videon", "video.fullscreen": "Igi plenekrana", - "video.hide": "KaÅi videon", + "video.hide": "KaÅi la videon", "video.mute": "Silentigi", "video.pause": "PaÅzi", "video.play": "Ekigi", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json new file mode 100644 index 0000000000000000000000000000000000000000..d46063b182baa6a5c62e27ac16ff199e6c38a20a --- /dev/null +++ b/app/javascript/mastodon/locales/es-AR.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Agregar o quitar de las listas", + "account.badges.bot": "Bot", + "account.block": "Bloquear a @{name}", + "account.block_domain": "Ocultar todo de {domain}", + "account.blocked": "Bloqueado", + "account.cancel_follow_request": "Cancelar la solicitud de seguimiento", + "account.direct": "Mensaje directo a @{name}", + "account.domain_blocked": "Dominio oculto", + "account.edit_profile": "Editar perfil", + "account.endorse": "Destacar en el perfil", + "account.follow": "Seguir", + "account.followers": "Seguidores", + "account.followers.empty": "TodavÃa nadie sigue a este usuario.", + "account.follows": "Sigue", + "account.follows.empty": "TodavÃa este usuario no sigue a nadie.", + "account.follows_you": "Te sigue", + "account.hide_reblogs": "Ocultar retoots de @{name}", + "account.last_status": "Última actividad", + "account.link_verified_on": "La propiedad de este enlace fue verificada el {date}", + "account.locked_info": "El estado de privacidad de esta cuenta está establecido como bloqueado. El propietario manualmente revisa quién puede seguirle.", + "account.media": "Medios", + "account.mention": "Mencionar a @{name}", + "account.moved_to": "{name} se ha muó a:", + "account.mute": "Silenciar a @{name}", + "account.mute_notifications": "Silenciar notificaciones de @{name}", + "account.muted": "Silenciado", + "account.never_active": "Nunca", + "account.posts": "Toots", + "account.posts_with_replies": "Toots con respuestas", + "account.report": "Denunciar a @{name}", + "account.requested": "Esperando aprobación. Hacé clic para cancelar la solicitud de seguimiento.", + "account.share": "Compartir el perfil de @{name}", + "account.show_reblogs": "Mostrar retoots de @{name}", + "account.unblock": "Desbloquear a @{name}", + "account.unblock_domain": "Mostrar {domain}", + "account.unendorse": "No destacar en el perfil", + "account.unfollow": "Dejar de seguir", + "account.unmute": "Dejar de silenciar a @{name}", + "account.unmute_notifications": "Dejar de silenciar las notificaciones de @{name}", + "alert.rate_limited.message": "Por favor, reintentá después de las {retry_time, time, medium}.", + "alert.rate_limited.title": "Tarifa limitada", + "alert.unexpected.message": "Ocurrió un error inesperado.", + "alert.unexpected.title": "¡Epa!", + "autosuggest_hashtag.per_week": "{count} por semana", + "boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez", + "bundle_column_error.body": "Algo salió mal al cargar este componente.", + "bundle_column_error.retry": "Intentá de nuevo", + "bundle_column_error.title": "Error de red", + "bundle_modal_error.close": "Cerrar", + "bundle_modal_error.message": "Algo salió mal al cargar este componente.", + "bundle_modal_error.retry": "Intentá de nuevo", + "column.blocks": "Usuarios bloqueados", + "column.community": "LÃnea temporal local", + "column.direct": "Mensajes directos", + "column.directory": "Explorar perfiles", + "column.domain_blocks": "Dominios ocultos", + "column.favourites": "Favoritos", + "column.follow_requests": "Solicitudes de seguimiento", + "column.home": "Principal", + "column.lists": "Listas", + "column.mutes": "Usuarios silenciados", + "column.notifications": "Notificaciones", + "column.pins": "Toots fijados", + "column.public": "LÃnea temporal federada", + "column_back_button.label": "Volver", + "column_header.hide_settings": "Ocultar configuración", + "column_header.moveLeft_settings": "Mover columna a la izquierda", + "column_header.moveRight_settings": "Mover columna a la derecha", + "column_header.pin": "Fijar", + "column_header.show_settings": "Mostrar configuración", + "column_header.unpin": "Dejar de fijar", + "column_subheading.settings": "Configuración", + "community.column_settings.media_only": "Sólo medios", + "compose_form.direct_message_warning": "Este toot sólo será enviado a los usuarios mencionados.", + "compose_form.direct_message_warning_learn_more": "Aprendé más", + "compose_form.hashtag_warning": "Este toot no se mostrará bajo hashtags porque no es público. Sólo los toots públicos se pueden buscar por hashtag.", + "compose_form.lock_disclaimer": "Tu cuenta no está {locked}. Todos pueden seguirte para ver tus toots marcados como \"sólo para seguidores\".", + "compose_form.lock_disclaimer.lock": "bloqueada", + "compose_form.placeholder": "¿Qué onda?", + "compose_form.poll.add_option": "Agregá una opción", + "compose_form.poll.duration": "Duración de la encuesta", + "compose_form.poll.option_placeholder": "Opción {number}", + "compose_form.poll.remove_option": "Quitá esta opción", + "compose_form.publish": "Tootear", + "compose_form.publish_loud": "¡{publish}!", + "compose_form.sensitive.hide": "Marcar medio como sensible", + "compose_form.sensitive.marked": "El medio se marcó como sensible", + "compose_form.sensitive.unmarked": "El medio no está marcado como sensible", + "compose_form.spoiler.marked": "El texto está oculto detrás de la advertencia", + "compose_form.spoiler.unmarked": "El texto no está oculto", + "compose_form.spoiler_placeholder": "Escribà tu advertencia acá", + "confirmation_modal.cancel": "Cancelar", + "confirmations.block.block_and_report": "Bloquear y denunciar", + "confirmations.block.confirm": "Bloquear", + "confirmations.block.message": "¿Estás seguro que querés bloquear a {name}?", + "confirmations.delete.confirm": "Eliminar", + "confirmations.delete.message": "¿Estás seguro que querés eliminar este estado?", + "confirmations.delete_list.confirm": "Eliminar", + "confirmations.delete_list.message": "¿Estás seguro que querés eliminar permanentemente esta lista?", + "confirmations.domain_block.confirm": "Ocultar dominio entero", + "confirmations.domain_block.message": "¿Estás completamente seguro que querés bloquear el {domain} entero? En la mayorÃa de los casos, unos cuantos bloqueos y silenciados puntuales son suficientes y preferibles. No vas a ver contenido de ese dominio en ninguna de tus lÃneas temporales o en tus notificaciones. Tus seguidores de ese dominio serán quitados.", + "confirmations.logout.confirm": "Cerrar sesión", + "confirmations.logout.message": "¿Estás seguro que querés cerrar la sesión?", + "confirmations.mute.confirm": "Silenciar", + "confirmations.mute.explanation": "Esto ocultará mensajes de ellos y mensajes que los mencionen, pero todavÃa les permitirá ver tus mensajes o seguirte.", + "confirmations.mute.message": "¿Estás seguro que querés silenciar a {name}?", + "confirmations.redraft.confirm": "Eliminar toot original y editarlo", + "confirmations.redraft.message": "¿Estás seguro que querés eliminar este estado y volver a editarlo? Se perderán las veces marcadas como favoritos y los retoots, y las respuestas a la publicación original quedarán huérfanas.", + "confirmations.reply.confirm": "Responder", + "confirmations.reply.message": "Responder ahora sobreescribirá el mensaje que estás redactando actualmente. ¿Estás seguro que querés seguir?", + "confirmations.unfollow.confirm": "Dejar de seguir", + "confirmations.unfollow.message": "¿Estás seguro que querés dejar de seguir a {name}?", + "conversation.delete": "Eliminar conversación", + "conversation.mark_as_read": "Marcar como leÃdo", + "conversation.open": "Ver conversación", + "conversation.with": "Con {names}", + "directory.federated": "Desde fediverso conocido", + "directory.local": "Sólo de {domain}", + "directory.new_arrivals": "Recién llegados", + "directory.recently_active": "Recientemente activo", + "embed.instructions": "Insertá este toot a tu sitio web copiando el código de abajo.", + "embed.preview": "Asà es cómo se verá:", + "emoji_button.activity": "Actividad", + "emoji_button.custom": "Personalizado", + "emoji_button.flags": "Banderas", + "emoji_button.food": "Comida y bebida", + "emoji_button.label": "Insertar emoji", + "emoji_button.nature": "Naturaleza", + "emoji_button.not_found": "¡¡No emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objetos", + "emoji_button.people": "Gente", + "emoji_button.recent": "Usados frecuentemente", + "emoji_button.search": "Buscar…", + "emoji_button.search_results": "Resultados de búsqueda", + "emoji_button.symbols": "SÃmbolos", + "emoji_button.travel": "Viajes y lugares", + "empty_column.account_timeline": "¡No hay toots aquÃ!", + "empty_column.account_unavailable": "Perfil no disponible", + "empty_column.blocks": "TodavÃa no bloqueaste a ningún usuario.", + "empty_column.community": "La lÃnea temporal local está vacÃa. ¡Escribà algo en modo público para que se empiece a correr la bola!", + "empty_column.direct": "TodavÃa no tenés ningún mensaje directo. Cuando enviés o recibás uno, se mostrará acá.", + "empty_column.domain_blocks": "TodavÃa no hay dominios ocultos.", + "empty_column.favourited_statuses": "TodavÃa no tenés toots favoritos. Cuando marqués uno como favorito, se mostrará acá.", + "empty_column.favourites": "TodavÃa nadie marcó este toot como favorito. Cuando alguien lo haga, se mostrará acá.", + "empty_column.follow_requests": "TodavÃa no tenés ninguna solicitud de seguimiento. Cuando recibás una, se mostrará acá.", + "empty_column.hashtag": "TodavÃa no hay nada con esta etiqueta.", + "empty_column.home": "¡Tu lÃnea temporal principal está vacÃa! Visitá {public} o usá la búsqueda para comenzar y encontrar a otros usuarios.", + "empty_column.home.public_timeline": "la lÃnea temporal pública", + "empty_column.list": "TodavÃa no hay nada en esta lista. Cuando miembros de esta lista envÃen nuevos toots, se mostrarán acá.", + "empty_column.lists": "TodavÃa no tienes ninguna lista. Cuando creés una, se mostrará acá.", + "empty_column.mutes": "TodavÃa no silenciaste a ningún usuario.", + "empty_column.notifications": "TodavÃa no tenés ninguna notificación. Interactuá con otros para iniciar la conversación.", + "empty_column.public": "¡Naranja! Escribà algo públicamente, o seguà usuarios manualmente de otros servidores para ir llenando esta lÃnea temporal.", + "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador web, esta página no se pudo mostrar correctamente.", + "error.unexpected_crash.next_steps": "Intentá recargar la página. Si eso no ayuda, podés usar Mastodon a través de un navegador web diferente o aplicación nativa.", + "errors.unexpected_crash.copy_stacktrace": "Copiar stacktrace al portapapeles", + "errors.unexpected_crash.report_issue": "Informar problema", + "follow_request.authorize": "Autorizar", + "follow_request.reject": "Rechazar", + "getting_started.developers": "Desarrolladores", + "getting_started.directory": "Directorio de perfiles", + "getting_started.documentation": "Documentación", + "getting_started.heading": "Introducción", + "getting_started.invite": "Invitar usuarios", + "getting_started.open_source_notice": "Mastodon es software libre. Podés contribuir o informar errores en {github}.", + "getting_started.security": "Seguridad", + "getting_started.terms": "Términos del servicio", + "hashtag.column_header.tag_mode.all": "y {additional}", + "hashtag.column_header.tag_mode.any": "o {additional}", + "hashtag.column_header.tag_mode.none": "sin {additional}", + "hashtag.column_settings.select.no_options_message": "No se encontraron sugerencias", + "hashtag.column_settings.select.placeholder": "Introducà etiquetas…", + "hashtag.column_settings.tag_mode.all": "Todas estas", + "hashtag.column_settings.tag_mode.any": "Cualquiera de estas", + "hashtag.column_settings.tag_mode.none": "Ninguna de estas", + "hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionales para esta columna", + "home.column_settings.basic": "Básico", + "home.column_settings.show_reblogs": "Mostrar retoots", + "home.column_settings.show_replies": "Mostrar respuestas", + "intervals.full.days": "{number, plural, one {# dÃa} other {# dÃas}}", + "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}", + "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", + "introduction.federation.action": "Siguiente", + "introduction.federation.federated.headline": "Federado", + "introduction.federation.federated.text": "Los toots públicos de otros servidores del fediverso aparecerán en la lÃnea temporal federada.", + "introduction.federation.home.headline": "Principal", + "introduction.federation.home.text": "Los toots de las personas que seguÃss aparecerán en tu lÃnea temporal principal. ¡Podés seguir a cualquiera en cualquier servidor!", + "introduction.federation.local.headline": "Local", + "introduction.federation.local.text": "Los toots públicos de las personas en el mismo servidor aparecerán en la lÃnea temporal local.", + "introduction.interactions.action": "¡Terminar tutorial!", + "introduction.interactions.favourite.headline": "Favorito", + "introduction.interactions.favourite.text": "Podés guardar un toot para más tarde, y hacerle saber al autor que te gustó, marcándolo como favorito.", + "introduction.interactions.reblog.headline": "Retootear", + "introduction.interactions.reblog.text": "Podés compartir los toots de otras personas con tus seguidores retooteando los mismos.", + "introduction.interactions.reply.headline": "Responder", + "introduction.interactions.reply.text": "Podés responder a tus propios toots y los de otras personas, que se encadenarán juntos en una conversación.", + "introduction.welcome.action": "¡Dale!", + "introduction.welcome.headline": "Primeros pasos", + "introduction.welcome.text": "¡Bienvenido al fediverso! En unos pocos minutos, vas a poder transmitir mensajes y hablar con tus amigos a través de una amplia variedad de servidores. Pero este servidor, {domain}, es especial: aloja tu perfil, asà que acordate de su nombre.", + "keyboard_shortcuts.back": "para volver", + "keyboard_shortcuts.blocked": "para abrir la lista de usuarios bloqueados", + "keyboard_shortcuts.boost": "para retootear", + "keyboard_shortcuts.column": "para enfocar un estado en una de las columnas", + "keyboard_shortcuts.compose": "para enfocar el área de texto de redacción", + "keyboard_shortcuts.description": "Descripción", + "keyboard_shortcuts.direct": "para abrir columna de mensajes directos", + "keyboard_shortcuts.down": "para bajar en la lista", + "keyboard_shortcuts.enter": "para abrir el estado", + "keyboard_shortcuts.favourite": "para marcar como favorito", + "keyboard_shortcuts.favourites": "para abrir la lista de favoritos", + "keyboard_shortcuts.federated": "para abrir la lÃnea temporal federada", + "keyboard_shortcuts.heading": "Atajos de teclado", + "keyboard_shortcuts.home": "para abrir la lÃnea temporal principal", + "keyboard_shortcuts.hotkey": "Combinación", + "keyboard_shortcuts.legend": "para mostrar este texto", + "keyboard_shortcuts.local": "para abrir la lÃnea temporal local", + "keyboard_shortcuts.mention": "para mencionar al autor", + "keyboard_shortcuts.muted": "abrir la lista de usuarios silenciados", + "keyboard_shortcuts.my_profile": "para abrir tu perfil", + "keyboard_shortcuts.notifications": "para abrir la columna de notificaciones", + "keyboard_shortcuts.pinned": "para abrir lista de toots fijados", + "keyboard_shortcuts.profile": "para abrir el perfil del autor", + "keyboard_shortcuts.reply": "para responder", + "keyboard_shortcuts.requests": "para abrir la lista de solicitudes de seguimiento", + "keyboard_shortcuts.search": "para enfocar la búsqueda", + "keyboard_shortcuts.start": "para abrir la columna \"Introducción\"", + "keyboard_shortcuts.toggle_hidden": "para mostrar/ocultar el texto detrás de la advertencia de contenido", + "keyboard_shortcuts.toggle_sensitivity": "para mostrar/ocultar los medios", + "keyboard_shortcuts.toot": "para comenzar un toot nuevo", + "keyboard_shortcuts.unfocus": "para quitar el enfoque del área de texto de redacción o de búsqueda", + "keyboard_shortcuts.up": "para subir en la lista", + "lightbox.close": "Cerrar", + "lightbox.next": "Siguiente", + "lightbox.previous": "Anterior", + "lightbox.view_context": "Ver contexto", + "lists.account.add": "Agregar a lista", + "lists.account.remove": "Quitar de lista", + "lists.delete": "Eliminar lista", + "lists.edit": "Editar lista", + "lists.edit.submit": "Cambiar tÃtulo", + "lists.new.create": "Agregar lista", + "lists.new.title_placeholder": "Nuevo tÃtulo de lista", + "lists.search": "Buscar entre la gente que seguÃs", + "lists.subheading": "Tus listas", + "load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}", + "loading_indicator.label": "Cargando…", + "media_gallery.toggle_visible": "Cambiar visibilidad", + "missing_indicator.label": "No se encontró", + "missing_indicator.sublabel": "No se encontró este recurso", + "mute_modal.hide_notifications": "¿Querés ocultar las notificaciones de este usuario?", + "navigation_bar.apps": "Aplicaciones móviles", + "navigation_bar.blocks": "Usuarios bloqueados", + "navigation_bar.community_timeline": "LÃnea temporal local", + "navigation_bar.compose": "Redactar un nuevo toot", + "navigation_bar.direct": "Mensajes directos", + "navigation_bar.discover": "Descubrir", + "navigation_bar.domain_blocks": "Dominios ocultos", + "navigation_bar.edit_profile": "Editar perfil", + "navigation_bar.favourites": "Favoritos", + "navigation_bar.filters": "Palabras silenciadas", + "navigation_bar.follow_requests": "Solicitudes de seguimiento", + "navigation_bar.follows_and_followers": "Personas seguidas y seguidores", + "navigation_bar.info": "Acerca de este servidor", + "navigation_bar.keyboard_shortcuts": "Atajos", + "navigation_bar.lists": "Listas", + "navigation_bar.logout": "Cerrar sesión", + "navigation_bar.mutes": "Usuarios silenciados", + "navigation_bar.personal": "Personal", + "navigation_bar.pins": "Toots fijados", + "navigation_bar.preferences": "Configuración", + "navigation_bar.public_timeline": "LÃnea temporal federada", + "navigation_bar.security": "Seguridad", + "notification.favourite": "{name} marcó tu estado como favorito", + "notification.follow": "{name} te empezó a seguir", + "notification.mention": "{name} te mencionó", + "notification.poll": "Finalizó una encuesta en la que votaste", + "notification.reblog": "{name} retooteó tu estado", + "notifications.clear": "Limpiar notificaciones", + "notifications.clear_confirmation": "¿Estás seguro que querés limpiar todas tus notificaciones permanentemente?", + "notifications.column_settings.alert": "Notificaciones de escritorio", + "notifications.column_settings.favourite": "Favoritos:", + "notifications.column_settings.filter_bar.advanced": "Mostrar todas las categorÃas", + "notifications.column_settings.filter_bar.category": "Barra de filtrado rápido", + "notifications.column_settings.filter_bar.show": "Mostrar", + "notifications.column_settings.follow": "Nuevos seguidores:", + "notifications.column_settings.mention": "Menciones:", + "notifications.column_settings.poll": "Resultados de la encuesta:", + "notifications.column_settings.push": "Notificaciones push", + "notifications.column_settings.reblog": "Retoots:", + "notifications.column_settings.show": "Mostrar en columna", + "notifications.column_settings.sound": "Reproducir sonido", + "notifications.filter.all": "Todas", + "notifications.filter.boosts": "Retoots", + "notifications.filter.favourites": "Favoritos", + "notifications.filter.follows": "Seguidores", + "notifications.filter.mentions": "Menciones", + "notifications.filter.polls": "Resultados de la encuesta", + "notifications.group": "{count} notificaciones", + "poll.closed": "Cerrada", + "poll.refresh": "Refrescar", + "poll.total_people": "{count, plural, one {# persona} other {# personas}}", + "poll.total_votes": "{count, plural, one {# voto} other {# votos}}", + "poll.vote": "Votar", + "poll.voted": "Votaste esta opción", + "poll_button.add_poll": "Agregar una encuesta", + "poll_button.remove_poll": "Quitar encuesta", + "privacy.change": "Configurar privacidad de estado", + "privacy.direct.long": "Enviar toot sólo a los usuarios mencionados", + "privacy.direct.short": "Directo", + "privacy.private.long": "Enviar toot sólo a los seguidores", + "privacy.private.short": "Sólo a seguidores", + "privacy.public.long": "Enviar toot a las lÃneas temporales públicas", + "privacy.public.short": "Público", + "privacy.unlisted.long": "No enviar toot a las lÃneas temporales públicas", + "privacy.unlisted.short": "No listado", + "refresh": "Refrescar", + "regeneration_indicator.label": "Cargando…", + "regeneration_indicator.sublabel": "¡Se está preparando tu lÃnea temporal principal!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "recién", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancelar", + "report.forward": "Reenviar a {target}", + "report.forward_hint": "La cuenta es de otro servidor. ¿Querés enviar una copia anonimizada del informe también ahÃ?", + "report.hint": "La denuncia se enviará a los moderadores de tu servidor. Podés proporcionar una explicación de por qué estás denunciando esta cuenta, a continuación:", + "report.placeholder": "Comentarios adicionales", + "report.submit": "Enviar", + "report.target": "Denunciando a {target}", + "search.placeholder": "Buscar", + "search_popout.search_format": "Formato de búsqueda avanzada", + "search_popout.tips.full_text": "Las búsquedas de texto simple devuelven los estados que escribiste, los marcados como favoritos, los retooteados o en los que te mencionaron, asà como nombres usuarios, nombres mostrados y etiquetas.", + "search_popout.tips.hashtag": "etiqueta", + "search_popout.tips.status": "estado", + "search_popout.tips.text": "Las búsquedas de texto simple devuelven nombres de usuarios, nombres mostrados y etiquetas que coincidan", + "search_popout.tips.user": "usuario", + "search_results.accounts": "Gente", + "search_results.hashtags": "Etiquetas", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "No se puede buscar toots por contenido en este servidor de Mastodon.", + "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", + "status.admin_account": "Abrir interface de moderación para @{name}", + "status.admin_status": "Abrir este estado en la interface de moderación", + "status.block": "Bloquear a @{name}", + "status.cancel_reblog_private": "Quitar retoot", + "status.cannot_reblog": "No se puede retootear este toot", + "status.copy": "Copiar enlace al estado", + "status.delete": "Eliminar", + "status.detailed_status": "Vista de conversación detallada", + "status.direct": "Mensaje directo a @{name}", + "status.embed": "Insertar", + "status.favourite": "Favorito", + "status.filtered": "Filtrado", + "status.load_more": "Cargar más", + "status.media_hidden": "Medios ocultos", + "status.mention": "Mencionar a @{name}", + "status.more": "Más", + "status.mute": "Silenciar a @{name}", + "status.mute_conversation": "Silenciar conversación", + "status.open": "Expandir este estado", + "status.pin": "Pin en el perfil", + "status.pinned": "Toot fijado", + "status.read_more": "Leer más", + "status.reblog": "Retootear", + "status.reblog_private": "Retootear a la audiencia original", + "status.reblogged_by": "Retooteado por {name}", + "status.reblogs.empty": "TodavÃa nadie retooteó este toot. Cuando alguien lo haga, se mostrará acá.", + "status.redraft": "Eliminar toot original y editarlo", + "status.reply": "Responder", + "status.replyAll": "Responder al hilo", + "status.report": "Denunciar a @{name}", + "status.sensitive_warning": "Contenido sensible", + "status.share": "Compartir", + "status.show_less": "Mostrar menos", + "status.show_less_all": "Mostrar menos para todo", + "status.show_more": "Mostrar más", + "status.show_more_all": "Mostrar más para todo", + "status.show_thread": "Mostrar hilo", + "status.uncached_media_warning": "No disponible", + "status.unmute_conversation": "Dejar de silenciar conversación", + "status.unpin": "Desmarcar del perfil", + "suggestions.dismiss": "Descartar sugerencia", + "suggestions.header": "Es posible que te interese…", + "tabs_bar.federated_timeline": "Federado", + "tabs_bar.home": "Principal", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notificaciones", + "tabs_bar.search": "Buscar", + "time_remaining.days": "{number, plural,one {queda # dÃa} other {quedan # dÃas}}", + "time_remaining.hours": "{number, plural,one {queda # hora} other {quedan # horas}}", + "time_remaining.minutes": "{number, plural,one {queda # minuto} other {quedan # minutos}}", + "time_remaining.moments": "Momentos restantes", + "time_remaining.seconds": "{number, plural,one {queda # segundo} other {quedan # segundos}}", + "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {personas}} hablando", + "trends.trending_now": "Tendencia ahora", + "ui.beforeunload": "Tu borrador se perderá si abandonás Mastodon.", + "upload_area.title": "Para subir, arrastrá y soltá", + "upload_button.label": "Agregar medios ({formats})", + "upload_error.limit": "Se excedió el lÃmite de subida de archivos.", + "upload_error.poll": "No se permite la subida de archivos en encuestas.", + "upload_form.description": "Agregar descripción para los usuarios con dificultades visuales", + "upload_form.edit": "Editar", + "upload_form.undo": "Eliminar", + "upload_modal.analyzing_picture": "Analizando imagen…", + "upload_modal.apply": "Aplicar", + "upload_modal.description_placeholder": "El veloz murciélago hindú comÃa feliz cardillo y kiwi. La cigüeña tocaba el saxofón detrás del palenque de paja.", + "upload_modal.detect_text": "Detectar texto de la imagen", + "upload_modal.edit_media": "Editar medio", + "upload_modal.hint": "Hacé clic o arrastrá el cÃrculo en la previsualización para elegir el punto focal que siempre estará a la vista en todas las miniaturas.", + "upload_modal.preview_label": "Previsualización ({ratio})", + "upload_progress.label": "Subiendo…", + "video.close": "Cerrar video", + "video.exit_fullscreen": "Salir de pantalla completa", + "video.expand": "Expandir vÃdeo", + "video.fullscreen": "Pantalla completa", + "video.hide": "Ocultar video", + "video.mute": "Silenciar sonido", + "video.pause": "Pausar", + "video.play": "Reproducir", + "video.unmute": "Dejar de silenciar sonido" +} diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 0f9d45bd66b8cda6bc7dae9a264df2481cd50947..d964716faf3f63731579952c3d18a1270fdc8e64 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -1,21 +1,23 @@ { - "account.add_or_remove_from_list": "Agregar o eliminar de las listas", + "account.add_or_remove_from_list": "Agregar o eliminar de listas", "account.badges.bot": "Bot", - "account.block": "Bloquear", + "account.block": "Bloquear a @{name}", "account.block_domain": "Ocultar todo de {domain}", "account.blocked": "Bloqueado", - "account.direct": "Direct Message @{name}", + "account.cancel_follow_request": "Cancelar la solicitud de seguimiento", + "account.direct": "Mensaje directo a @{name}", "account.domain_blocked": "Dominio oculto", "account.edit_profile": "Editar perfil", "account.endorse": "Mostrar en perfil", "account.follow": "Seguir", "account.followers": "Seguidores", - "account.followers.empty": "Nadie sigue a este usuario todavÃa.", + "account.followers.empty": "TodavÃa nadie sigue a este usuario.", "account.follows": "Sigue", "account.follows.empty": "Este usuario todavÃa no sigue a nadie.", "account.follows_you": "Te sigue", "account.hide_reblogs": "Ocultar retoots de @{name}", - "account.link_verified_on": "El proprietario de este link fue verificado el {date}", + "account.last_status": "Última actividad", + "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.media": "Multimedia", "account.mention": "Mencionar a @{name}", @@ -23,6 +25,7 @@ "account.mute": "Silenciar a @{name}", "account.mute_notifications": "Silenciar notificaciones de @{name}", "account.muted": "Silenciado", + "account.never_active": "Nunca", "account.posts": "Toots", "account.posts_with_replies": "Toots con respuestas", "account.report": "Reportar a @{name}", @@ -35,9 +38,12 @@ "account.unfollow": "Dejar de seguir", "account.unmute": "Dejar de silenciar a @{name}", "account.unmute_notifications": "Dejar de silenciar las notificaciones de @{name}", + "alert.rate_limited.message": "Por favor reintente después de {retry_time, time, medium}.", + "alert.rate_limited.title": "Tarifa limitada", "alert.unexpected.message": "Hubo un error inesperado.", "alert.unexpected.title": "¡Ups!", - "boost_modal.combo": "Puedes presionar {combo} para saltear este aviso la próxima vez", + "autosuggest_hashtag.per_week": "{count} por semana", + "boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez", "bundle_column_error.body": "Algo salió mal al cargar este componente.", "bundle_column_error.retry": "Inténtalo de nuevo", "bundle_column_error.title": "Error de red", @@ -47,17 +53,18 @@ "column.blocks": "Usuarios bloqueados", "column.community": "LÃnea de tiempo local", "column.direct": "Mensajes directos", - "column.domain_blocks": "Dominios ocultos", + "column.directory": "Buscar perfiles", + "column.domain_blocks": "Dominios ocultados", "column.favourites": "Favoritos", "column.follow_requests": "Solicitudes de seguimiento", "column.home": "Inicio", "column.lists": "Listas", "column.mutes": "Usuarios silenciados", "column.notifications": "Notificaciones", - "column.pins": "Toot fijado", - "column.public": "Historia federada", + "column.pins": "Toots fijados", + "column.public": "LÃnea de tiempo federada", "column_back_button.label": "Atrás", - "column_header.hide_settings": "Ocultar ajustes", + "column_header.hide_settings": "Ocultar configuración", "column_header.moveLeft_settings": "Mover columna a la izquierda", "column_header.moveRight_settings": "Mover columna a la derecha", "column_header.pin": "Fijar", @@ -93,14 +100,25 @@ "confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?", "confirmations.domain_block.confirm": "Ocultar dominio entero", "confirmations.domain_block.message": "¿Seguro de que quieres bloquear al dominio {domain} entero? En general unos cuantos bloqueos y silenciados concretos es suficiente y preferible.", + "confirmations.logout.confirm": "Cerrar sesión", + "confirmations.logout.message": "¿Estás seguro de querer cerrar la sesión?", "confirmations.mute.confirm": "Silenciar", + "confirmations.mute.explanation": "Esto esconderá las publicaciones de ellos y en las que los has mencionado, pero les permitirá ver tus mensajes y seguirte.", "confirmations.mute.message": "¿Estás seguro de que quieres silenciar a {name}?", "confirmations.redraft.confirm": "Borrar y volver a borrador", "confirmations.redraft.message": "Estás seguro de que quieres borrar este estado y volverlo a borrador? Perderás todas las respuestas, impulsos y favoritos asociados a él, y las respuestas a la publicación original quedarán huérfanos.", "confirmations.reply.confirm": "Responder", - "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.reply.message": "Responder sobrescribirá el mensaje que estás escribiendo. ¿Estás seguro de que deseas continuar?", "confirmations.unfollow.confirm": "Dejar de seguir", "confirmations.unfollow.message": "¿Estás seguro de que quieres dejar de seguir a {name}?", + "conversation.delete": "Borrar conversación", + "conversation.mark_as_read": "Marcar como leÃdo", + "conversation.open": "Ver conversación", + "conversation.with": "Con {names}", + "directory.federated": "Desde el fediverso conocido", + "directory.local": "Sólo de {domain}", + "directory.new_arrivals": "Recién llegados", + "directory.recently_active": "Recientemente activo", "embed.instructions": "Añade este toot a tu sitio web con el siguiente código.", "embed.preview": "Asà es como se verá:", "emoji_button.activity": "Actividad", @@ -134,6 +152,10 @@ "empty_column.mutes": "Aún no has silenciado a ningún usuario.", "empty_column.notifications": "No tienes ninguna notificación aún. Interactúa con otros para empezar una conversación.", "empty_column.public": "¡No hay nada aquÃ! Escribe algo públicamente, o sigue usuarios de otras instancias manualmente para llenarlo", + "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador, esta página no se ha podido mostrar correctamente.", + "error.unexpected_crash.next_steps": "Intenta actualizar la página. Si eso no ayuda, es posible que puedas usar Mastodon a través de otro navegador o aplicación nativa.", + "errors.unexpected_crash.copy_stacktrace": "Copiar el seguimiento de pila en el portapapeles", + "errors.unexpected_crash.report_issue": "Informar de un problema/error", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -154,33 +176,33 @@ "hashtag.column_header.tag_mode.none": "sin {additional}", "hashtag.column_settings.select.no_options_message": "No se encontraron sugerencias", "hashtag.column_settings.select.placeholder": "Introduzca hashtags…", - "hashtag.column_settings.tag_mode.all": "All of these", + "hashtag.column_settings.tag_mode.all": "Cualquiera de estos", "hashtag.column_settings.tag_mode.any": "Cualquiera de estos", "hashtag.column_settings.tag_mode.none": "Ninguno de estos", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", "home.column_settings.basic": "Básico", "home.column_settings.show_reblogs": "Mostrar retoots", "home.column_settings.show_replies": "Mostrar respuestas", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.days": "{number, plural, one {# dÃa} other {# dÃas}}", + "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}", + "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "introduction.federation.action": "Siguiente", "introduction.federation.federated.headline": "Federado", - "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.federated.text": "Los mensajes públicos de otros servidores del fediverso aparecerán en la cronologÃa federada.", "introduction.federation.home.headline": "Inicio", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.home.text": "Los posts de personas que sigues aparecerán en tu cronologÃa. ¡Puedes seguir a cualquiera en cualquier servidor!", "introduction.federation.local.headline": "Local", - "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.federation.local.text": "Los posts públicos de personas en el mismo servidor que aparecerán en la cronologÃa local.", "introduction.interactions.action": "¡Terminar tutorial!", "introduction.interactions.favourite.headline": "Favorito", - "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", - "introduction.interactions.reblog.headline": "Boost", - "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.favourite.text": "Puedes guardar un toot para más tarde, y hacer saber al autor que te gustó, dándole a favorito.", + "introduction.interactions.reblog.headline": "Retootear", + "introduction.interactions.reblog.text": "Puedes compartir los toots de otras personas con tus seguidores retooteando los mismos.", "introduction.interactions.reply.headline": "Responder", - "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.interactions.reply.text": "Puedes responder a tus propios toots y los de otras personas, que se encadenarán juntos en una conversación.", "introduction.welcome.action": "¡Vamos!", "introduction.welcome.headline": "Primeros pasos", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "introduction.welcome.text": "¡Bienvenido al fediverso! En unos momentos, podrás transmitir mensajes y hablar con tus amigos a través de una amplia variedad de servidores. Pero este servidor, {domain}, es especial, alberga tu perfil, asà que recuerda su nombre.", "keyboard_shortcuts.back": "volver atrás", "keyboard_shortcuts.blocked": "abrir una lista de usuarios bloqueados", "keyboard_shortcuts.boost": "retootear", @@ -189,7 +211,7 @@ "keyboard_shortcuts.description": "Descripción", "keyboard_shortcuts.direct": "abrir la columna de mensajes directos", "keyboard_shortcuts.down": "mover hacia abajo en la lista", - "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.enter": "abrir estado", "keyboard_shortcuts.favourite": "añadir a favoritos", "keyboard_shortcuts.favourites": "abrir la lista de favoritos", "keyboard_shortcuts.federated": "abrir el timeline federado", @@ -209,7 +231,7 @@ "keyboard_shortcuts.search": "para poner el foco en la búsqueda", "keyboard_shortcuts.start": "abrir la columna \"comenzar\"", "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar texto tras aviso de contenido (CW)", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar medios", "keyboard_shortcuts.toot": "para comenzar un nuevo toot", "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", @@ -221,11 +243,12 @@ "lists.account.remove": "Quitar de lista", "lists.delete": "Borrar lista", "lists.edit": "Editar lista", - "lists.edit.submit": "Change title", + "lists.edit.submit": "Cambiar tÃtulo", "lists.new.create": "Añadir lista", "lists.new.title_placeholder": "TÃtulo de la nueva lista", "lists.search": "Buscar entre la gente a la que sigues", "lists.subheading": "Tus listas", + "load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}", "loading_indicator.label": "Cargando…", "media_gallery.toggle_visible": "Cambiar visibilidad", "missing_indicator.label": "No encontrado", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favoritos", "navigation_bar.filters": "Palabras silenciadas", "navigation_bar.follow_requests": "Solicitudes para seguirte", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Siguiendo y seguidores", "navigation_bar.info": "Información adicional", "navigation_bar.keyboard_shortcuts": "Atajos", "navigation_bar.lists": "Listas", @@ -251,41 +274,42 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Toots fijados", "navigation_bar.preferences": "Preferencias", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Historia federada", "navigation_bar.security": "Seguridad", "notification.favourite": "{name} marcó tu estado como favorito", "notification.follow": "{name} te empezó a seguir", "notification.mention": "{name} te ha mencionado", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "Una encuesta en la que has votado ha terminado", "notification.reblog": "{name} ha retooteado tu estado", "notifications.clear": "Limpiar notificaciones", "notifications.clear_confirmation": "¿Seguro que quieres limpiar permanentemente todas tus notificaciones?", "notifications.column_settings.alert": "Notificaciones de escritorio", "notifications.column_settings.favourite": "Favoritos:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Mostrar todas las categorÃas", + "notifications.column_settings.filter_bar.category": "Barra de filtrado rápido", + "notifications.column_settings.filter_bar.show": "Mostrar", "notifications.column_settings.follow": "Nuevos seguidores:", "notifications.column_settings.mention": "Menciones:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "Resultados de la votación:", "notifications.column_settings.push": "Notificaciones push", "notifications.column_settings.reblog": "Retoots:", "notifications.column_settings.show": "Mostrar en columna", "notifications.column_settings.sound": "Reproducir sonido", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Boosts", - "notifications.filter.favourites": "Favourites", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", + "notifications.filter.all": "Todos", + "notifications.filter.boosts": "Retoots", + "notifications.filter.favourites": "Favoritos", + "notifications.filter.follows": "Seguidores", + "notifications.filter.mentions": "Menciones", + "notifications.filter.polls": "Resultados de la votación", "notifications.group": "{count} notificaciones", - "poll.closed": "Closed", - "poll.refresh": "Refresh", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "poll.closed": "Cerrada", + "poll.refresh": "Actualizar", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# voto} other {# votos}}", + "poll.vote": "Votar", + "poll.voted": "Has votado a favor de esta respuesta", + "poll_button.add_poll": "Añadir una encuesta", + "poll_button.remove_poll": "Eliminar encuesta", "privacy.change": "Ajustar privacidad", "privacy.direct.long": "Sólo mostrar a los usuarios mencionados", "privacy.direct.short": "Directo", @@ -294,7 +318,8 @@ "privacy.public.long": "Mostrar en la historia federada", "privacy.public.short": "Público", "privacy.unlisted.long": "No mostrar en la historia federada", - "privacy.unlisted.short": "Sin federar", + "privacy.unlisted.short": "No listado", + "refresh": "Actualizar", "regeneration_indicator.label": "Cargando…", "regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!", "relative_time.days": "{number}d", @@ -313,19 +338,20 @@ "search_popout.search_format": "Formato de búsqueda avanzada", "search_popout.tips.full_text": "Búsquedas de texto recuperan posts que has escrito, marcado como favoritos, retooteado o en los que has sido mencionado, asà como usuarios, nombres y hashtags.", "search_popout.tips.hashtag": "etiqueta", - "search_popout.tips.status": "status", + "search_popout.tips.status": "estado", "search_popout.tips.text": "El texto simple devuelve correspondencias de nombre, usuario y hashtag", "search_popout.tips.user": "usuario", "search_results.accounts": "Gente", "search_results.hashtags": "Etiquetas", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Buscar toots por su contenido no está disponible en este servidor de Mastodon.", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this status in the moderation interface", - "status.block": "Block @{name}", + "status.admin_account": "Abrir interfaz de moderación para @{name}", + "status.admin_status": "Abrir este estado en la interfaz de moderación", + "status.block": "Bloquear a @{name}", "status.cancel_reblog_private": "Des-impulsar", "status.cannot_reblog": "Este toot no puede retootearse", - "status.copy": "Copy link to status", + "status.copy": "Copiar enlace al estado", "status.delete": "Borrar", "status.detailed_status": "Vista de conversación detallada", "status.direct": "Mensaje directo a @{name}", @@ -342,7 +368,7 @@ "status.open": "Expandir estado", "status.pin": "Fijar", "status.pinned": "Toot fijado", - "status.read_more": "Read more", + "status.read_more": "Leer más", "status.reblog": "Retootear", "status.reblog_private": "Implusar a la audiencia original", "status.reblogged_by": "Retooteado por {name}", @@ -357,30 +383,39 @@ "status.show_less_all": "Mostrar menos para todo", "status.show_more": "Mostrar más", "status.show_more_all": "Mostrar más para todo", - "status.show_thread": "Show thread", + "status.show_thread": "Ver hilo", + "status.uncached_media_warning": "No disponible", "status.unmute_conversation": "Dejar de silenciar conversación", "status.unpin": "Dejar de fijar", - "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", + "suggestions.dismiss": "Descartar sugerencia", + "suggestions.header": "Es posible que te interese…", "tabs_bar.federated_timeline": "Federado", "tabs_bar.home": "Inicio", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificaciones", "tabs_bar.search": "Buscar", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "time_remaining.days": "{number, plural, one {# dÃa restante} other {# dÃas restantes}}", + "time_remaining.hours": "{number, plural, one {# hora restante} other {# horas restantes}}", + "time_remaining.minutes": "{number, plural, one {# minuto restante} other {# minutos restantes}}", + "time_remaining.moments": "Momentos restantes", + "time_remaining.seconds": "{number, plural, one {# segundo restante} other {# segundos restantes}}", + "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {personas}} hablando", + "trends.trending_now": "Tendencia ahora", "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.", "upload_area.title": "Arrastra y suelta para subir", "upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", + "upload_error.limit": "LÃmite de subida de archivos excedido.", + "upload_error.poll": "Subida de archivos no permitida con encuestas.", "upload_form.description": "Describir para los usuarios con dificultad visual", - "upload_form.focus": "Recortar", + "upload_form.edit": "Editar", "upload_form.undo": "Borrar", + "upload_modal.analyzing_picture": "Analizando imagen…", + "upload_modal.apply": "Aplicar", + "upload_modal.description_placeholder": "Un rápido zorro marrón salta sobre el perro perezoso", + "upload_modal.detect_text": "Detectar texto de la imagen", + "upload_modal.edit_media": "Editar multimedia", + "upload_modal.hint": "Haga clic o arrastre el cÃrculo en la vista previa para elegir el punto focal que siempre estará a la vista en todas las miniaturas.", + "upload_modal.preview_label": "Vista previa ({ratio})", "upload_progress.label": "Subiendo…", "video.close": "Cerrar video", "video.exit_fullscreen": "Salir de pantalla completa", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json new file mode 100644 index 0000000000000000000000000000000000000000..db60dab545276725f6219f1f0d22686c606467ba --- /dev/null +++ b/app/javascript/mastodon/locales/et.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Lisa või Eemalda nimekirjadest", + "account.badges.bot": "Robot", + "account.block": "Blokeeri @{name}", + "account.block_domain": "Peida kõik domeenist {domain}", + "account.blocked": "Blokeeritud", + "account.cancel_follow_request": "Cancel follow request", + "account.direct": "Otsesõnum @{name}", + "account.domain_blocked": "Domeen peidetud", + "account.edit_profile": "Muuda profiili", + "account.endorse": "Too profiilil esile", + "account.follow": "Jälgi", + "account.followers": "Jälgijad", + "account.followers.empty": "Keegi ei jälgi seda kasutajat veel.", + "account.follows": "Jälgib", + "account.follows.empty": "See kasutaja ei jälgi veel kedagi.", + "account.follows_you": "Jälgib sind", + "account.hide_reblogs": "Peida upitused kasutajalt @{name}", + "account.last_status": "Last active", + "account.link_verified_on": "Selle lingi autorsust kontrolliti {date}", + "account.locked_info": "Selle konto privaatsus on lukustatud. Omanik vaatab manuaalselt üle, kes teda jägida saab.", + "account.media": "Meedia", + "account.mention": "Maini @{name}", + "account.moved_to": "{name} on kolinud:", + "account.mute": "Vaigista @{name}", + "account.mute_notifications": "Vaigista teated kasutajalt @{name}", + "account.muted": "Vaigistatud", + "account.never_active": "Never", + "account.posts": "Tuututused", + "account.posts_with_replies": "Tuututused ja vastused", + "account.report": "Raporteeri @{name}", + "account.requested": "Ootab kinnitust. Klõpsa jälgimise soovi tühistamiseks", + "account.share": "Jaga @{name} profiili", + "account.show_reblogs": "Näita kasutaja @{name} upitusi", + "account.unblock": "Eemalda blokeering @{name}", + "account.unblock_domain": "Tee {domain} nähtavaks", + "account.unendorse": "Ära kuva profiilil", + "account.unfollow": "Ära jälgi", + "account.unmute": "Ära vaigista @{name}", + "account.unmute_notifications": "Ära vaigista teateid kasutajalt @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", + "alert.unexpected.message": "Tekkis ootamatu viga.", + "alert.unexpected.title": "Oih!", + "autosuggest_hashtag.per_week": "{count} per week", + "boost_modal.combo": "Saad vajutada {combo}, et see järgmine kord vahele jätta", + "bundle_column_error.body": "Mindagi läks valesti selle komponendi laadimisel.", + "bundle_column_error.retry": "Proovi uuesti", + "bundle_column_error.title": "Võrgu viga", + "bundle_modal_error.close": "Sulge", + "bundle_modal_error.message": "Selle komponendi laadimisel läks midagi viltu.", + "bundle_modal_error.retry": "Proovi uuesti", + "column.blocks": "Blokeeritud kasutajad", + "column.community": "Kohalik ajajoon", + "column.direct": "Otsesõnumid", + "column.directory": "Browse profiles", + "column.domain_blocks": "Peidetud domeenid", + "column.favourites": "Lemmikud", + "column.follow_requests": "Jälgimistaotlused", + "column.home": "Kodu", + "column.lists": "Nimekirjad", + "column.mutes": "Vaigistatud kasutajad", + "column.notifications": "Teated", + "column.pins": "Kinnitatud upitused", + "column.public": "Föderatiivne ajajoon", + "column_back_button.label": "Tagasi", + "column_header.hide_settings": "Peida sätted", + "column_header.moveLeft_settings": "Liiguta tulp vasakule", + "column_header.moveRight_settings": "Liiguta tulp paremale", + "column_header.pin": "Kinnita", + "column_header.show_settings": "Näita sätteid", + "column_header.unpin": "Eemalda kinnitus", + "column_subheading.settings": "Sätted", + "community.column_settings.media_only": "Ainult meedia", + "compose_form.direct_message_warning": "See tuut saadetakse ainult mainitud kasutajatele.", + "compose_form.direct_message_warning_learn_more": "Vaata veel", + "compose_form.hashtag_warning": "Seda tuuti ei kuvata ühegi sildi all, sest see on kirjendamata. Ainult avalikud tuutid on sildi järgi otsitavad.", + "compose_form.lock_disclaimer": "Sinu konto ei ole {locked}. Igaüks saab sind jälgida ja näha su ainult-jälgijatele postitusi.", + "compose_form.lock_disclaimer.lock": "lukus", + "compose_form.placeholder": "Millest mõtled?", + "compose_form.poll.add_option": "Lisa valik", + "compose_form.poll.duration": "Küsitluse kestus", + "compose_form.poll.option_placeholder": "Valik {number}", + "compose_form.poll.remove_option": "Eemalda see valik", + "compose_form.publish": "Tuut", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Märgista meedia tundlikuks", + "compose_form.sensitive.marked": "Meedia on sensitiivseks märgitud", + "compose_form.sensitive.unmarked": "Meedia ei ole sensitiivseks märgitud", + "compose_form.spoiler.marked": "Tekst on hoiatuse taha peidetud", + "compose_form.spoiler.unmarked": "Tekst ei ole peidetud", + "compose_form.spoiler_placeholder": "Kirjuta oma hoiatus siia", + "confirmation_modal.cancel": "Katkesta", + "confirmations.block.block_and_report": "Blokeeri & Teata", + "confirmations.block.confirm": "Blokeeri", + "confirmations.block.message": "Oled kindel, et soovid blokkida {name}?", + "confirmations.delete.confirm": "Kustuta", + "confirmations.delete.message": "Oled kindel, et soovid selle staatuse kustutada?", + "confirmations.delete_list.confirm": "Kustuta", + "confirmations.delete_list.message": "Oled kindel, et soovid selle nimekirja püsivalt kustutada?", + "confirmations.domain_block.confirm": "Peida terve domeen", + "confirmations.domain_block.message": "Oled ikka päris kindel, et soovid blokeerida terve {domain}? Enamikul juhtudel piisab mõnest sihitud blokist või vaigistusest, mis on eelistatav. Sa ei näe selle domeeni sisu üheski avalikus ajajoones või teadetes. Sinu jälgijad sellest domeenist eemaldatakse.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", + "confirmations.mute.confirm": "Vaigista", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "confirmations.mute.message": "Oled kindel, et soovid {name} vaigistada?", + "confirmations.redraft.confirm": "Kustuta & taasalusta", + "confirmations.redraft.message": "Oled kindel, et soovid selle staatuse kustutada ja alustada uuesti? Lemmikud ja upitused lähevad kaotsi ja vastused originaaalpostitusele jäävad orvuks.", + "confirmations.reply.confirm": "Vasta", + "confirmations.reply.message": "Kohene vastamine kirjutab üle sõnumi, mida hetkel koostad. Oled kindel, et soovid jätkata?", + "confirmations.unfollow.confirm": "Ära jälgi", + "confirmations.unfollow.message": "Oled kindel, et ei soovi jälgida {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", + "embed.instructions": "Manusta see staatus oma veebilehele, kopeerides alloleva koodi.", + "embed.preview": "Nii näeb see välja:", + "emoji_button.activity": "Tegevus", + "emoji_button.custom": "Mugandatud", + "emoji_button.flags": "Lipud", + "emoji_button.food": "Toit & Jook", + "emoji_button.label": "Sisesta emoji", + "emoji_button.nature": "Loodus", + "emoji_button.not_found": "Ei ole emojosi!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objektid", + "emoji_button.people": "Inimesed", + "emoji_button.recent": "Tihti kasutatud", + "emoji_button.search": "Otsi...", + "emoji_button.search_results": "Otsitulemused", + "emoji_button.symbols": "Sümbolid", + "emoji_button.travel": "Reisimine & Kohad", + "empty_column.account_timeline": "Siin tuute ei ole!", + "empty_column.account_unavailable": "Profiil pole saadaval", + "empty_column.blocks": "Sa ei ole veel ühtegi kasutajat blokeerinud.", + "empty_column.community": "Kohalik ajajoon on tühi. Kirjuta midagi avalikult, et pall veerema saada!", + "empty_column.direct": "Sul ei veel otsesõnumeid. Kui saadad või võtad mõne vastu, ilmuvad nad siia.", + "empty_column.domain_blocks": "Siin ei ole veel peidetud domeene.", + "empty_column.favourited_statuses": "Sul pole veel lemmikuid tuute. Kui märgid mõne, näed neid siin.", + "empty_column.favourites": "Keegi pole veel seda tuuti lemmikuks märkinud. Kui seegi seda teeb, näed seda siin.", + "empty_column.follow_requests": "Sul pole veel ühtegi jälgimise taotlust. Kui saad mõne, näed seda siin.", + "empty_column.hashtag": "Selle sildiga pole veel midagi.", + "empty_column.home": "Sinu kodu ajajoon on tühi! Külasta {public} või kasuta otsingut alustamaks ja kohtamaks teisi kasutajaid.", + "empty_column.home.public_timeline": "avalik ajajoon", + "empty_column.list": "Siin nimstus pole veel midagi. Kui selle nimistu liikmed postitavad uusi staatusi, näed neid siin.", + "empty_column.lists": "Sul ei ole veel ühtegi nimekirja. Kui lood mõne, näed seda siin.", + "empty_column.mutes": "Sa pole veel ühtegi kasutajat vaigistanud.", + "empty_column.notifications": "Sul ei ole veel teateid. Suhtle teistega alustamaks vestlust.", + "empty_column.public": "Siin pole midagi! Kirjuta midagi avalikut või jälgi ise kasutajaid täitmaks seda ruumi", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", + "follow_request.authorize": "Autoriseeri", + "follow_request.reject": "Hülga", + "getting_started.developers": "Arendajad", + "getting_started.directory": "Profiili kataloog", + "getting_started.documentation": "Dokumentatsioon", + "getting_started.heading": "Alustamine", + "getting_started.invite": "Kutsu inimesi", + "getting_started.open_source_notice": "Mastodon on avatud lähtekoodiga tarkvara. Saad panustada või teatada probleemidest GitHubis {github}.", + "getting_started.security": "Turvalisus", + "getting_started.terms": "Kasutustingimused", + "hashtag.column_header.tag_mode.all": "ja {additional}", + "hashtag.column_header.tag_mode.any": "või {additional}", + "hashtag.column_header.tag_mode.none": "ilma {additional}", + "hashtag.column_settings.select.no_options_message": "Soovitusi ei leitud", + "hashtag.column_settings.select.placeholder": "Sisesta sildid…", + "hashtag.column_settings.tag_mode.all": "Kõik need", + "hashtag.column_settings.tag_mode.any": "Mõni neist", + "hashtag.column_settings.tag_mode.none": "Mitte ükski neist", + "hashtag.column_settings.tag_toggle": "Kaasa lisamärked selle tulba jaoks", + "home.column_settings.basic": "Peamine", + "home.column_settings.show_reblogs": "Näita upitusi", + "home.column_settings.show_replies": "Näita vastuseid", + "intervals.full.days": "{number, plural, one {# päev} other {# päevad}}", + "intervals.full.hours": "{number, plural, one {# tund} other {# tundi}}", + "intervals.full.minutes": "{number, plural, one {# minut} other {# minutit}}", + "introduction.federation.action": "Järgmine", + "introduction.federation.federated.headline": "Föderatiivne", + "introduction.federation.federated.text": "Avalikud postitused teistest föderatsiooni serveritest kuvatakse föderatiivsel ajajoonel.", + "introduction.federation.home.headline": "Kodu", + "introduction.federation.home.text": "Inimest postitused keda jälgid kuvatakse sinu koduajajoonel. Saad jälgida igaüht igas serveris!", + "introduction.federation.local.headline": "Kohalik", + "introduction.federation.local.text": "Samas serveris olevate inimeste postitused kuvatakse kohalikul ajajoonel.", + "introduction.interactions.action": "Välju õpetusest!", + "introduction.interactions.favourite.headline": "Lemmik", + "introduction.interactions.favourite.text": "Saad tuuti salvestada ja anda autorile teada, et meeldis märkides selle lemmikuks.", + "introduction.interactions.reblog.headline": "Upita", + "introduction.interactions.reblog.text": "Saad jagada teiste inimeste tuute oma jälgijatega upitades neid.", + "introduction.interactions.reply.headline": "Vasta", + "introduction.interactions.reply.text": "Saad vastata teiste ja enda tuutidele, mis ühendab nad kokku aruteluks.", + "introduction.welcome.action": "Lähme!", + "introduction.welcome.headline": "Esimesed sammud", + "introduction.welcome.text": "Teretulemast fediversumisse! Mõne aja pärast saad avaldada sõnumeid ja rääkida oma sõpradega läbi laia valiku serverite. Aga see server, {domain}, on eriline—ta majutab sinu profiili. Seega jäta ta nimi meelde.", + "keyboard_shortcuts.back": "tagasiminekuks", + "keyboard_shortcuts.blocked": "avamaks blokeeritud kasutajate nimistut", + "keyboard_shortcuts.boost": "upitamiseks", + "keyboard_shortcuts.column": "fokuseerimaks staatust ühele tulpadest", + "keyboard_shortcuts.compose": "fokuseerimaks tekstikoostamise alale", + "keyboard_shortcuts.description": "Kirjeldus", + "keyboard_shortcuts.direct": "avamaks otsesõnumite tulpa", + "keyboard_shortcuts.down": "liikumaks nimstus alla", + "keyboard_shortcuts.enter": "staatuse avamiseks", + "keyboard_shortcuts.favourite": "lemmikuks märkimiseks", + "keyboard_shortcuts.favourites": "avamaks lemmikute nimistut", + "keyboard_shortcuts.federated": "avamaks föderatsiooni ajajoont", + "keyboard_shortcuts.heading": "Klaviatuuri kiirkäsud", + "keyboard_shortcuts.home": "avamaks kodu ajajoont", + "keyboard_shortcuts.hotkey": "Kiirklahv", + "keyboard_shortcuts.legend": "selle legendi kuvamiseks", + "keyboard_shortcuts.local": "avamaks kohalikku ajajoont", + "keyboard_shortcuts.mention": "autori mainimiseks", + "keyboard_shortcuts.muted": "avamaks vaigistatud kasutajate nimistut", + "keyboard_shortcuts.my_profile": "avamaks profiili", + "keyboard_shortcuts.notifications": "avamaks teadete tulpa", + "keyboard_shortcuts.pinned": "avamaks kinnitatud tuutide nimistut", + "keyboard_shortcuts.profile": "avamaks autori profiili", + "keyboard_shortcuts.reply": "vastamiseks", + "keyboard_shortcuts.requests": "avamaks jälgimistaotluste nimistut", + "keyboard_shortcuts.search": "otsingu fokuseerimiseks", + "keyboard_shortcuts.start": "avamaks \"Alusta\" tulpa", + "keyboard_shortcuts.toggle_hidden": "näitamaks/peitmaks teksti CW taga", + "keyboard_shortcuts.toggle_sensitivity": "et peita/näidata meediat", + "keyboard_shortcuts.toot": "alustamaks täiesti uut tuuti", + "keyboard_shortcuts.unfocus": "tekstiala/otsingu koostamise mittefokuseerimiseks", + "keyboard_shortcuts.up": "liikumaks nimistus üles", + "lightbox.close": "Sulge", + "lightbox.next": "Järgmine", + "lightbox.previous": "Eelmine", + "lightbox.view_context": "Vaata konteksti", + "lists.account.add": "Lisa nimistusse", + "lists.account.remove": "Eemalda nimistust", + "lists.delete": "Kustuta nimistu", + "lists.edit": "Muuda nimistut", + "lists.edit.submit": "Muuda pealkiri", + "lists.new.create": "Lisa nimistu", + "lists.new.title_placeholder": "Uus nimistu pealkiri", + "lists.search": "Otsi sinu poolt jälgitavate inimese hulgast", + "lists.subheading": "Sinu nimistud", + "load_pending": "{count, plural, one {# uus kirje} other {# uut kirjet}}", + "loading_indicator.label": "Laeb..", + "media_gallery.toggle_visible": "Lülita nähtavus", + "missing_indicator.label": "Ei leitud", + "missing_indicator.sublabel": "Seda ressurssi ei leitud", + "mute_modal.hide_notifications": "Kas peita teated sellelt kasutajalt?", + "navigation_bar.apps": "Mobiilrakendused", + "navigation_bar.blocks": "Blokeeritud kasutajad", + "navigation_bar.community_timeline": "Kohalik ajajoon", + "navigation_bar.compose": "Koosta uus tuut", + "navigation_bar.direct": "Otsesõnumid", + "navigation_bar.discover": "Avasta", + "navigation_bar.domain_blocks": "Peidetud domeenid", + "navigation_bar.edit_profile": "Muuda profiili", + "navigation_bar.favourites": "Lemmikud", + "navigation_bar.filters": "Vaigistatud sõnad", + "navigation_bar.follow_requests": "Jälgimistaotlused", + "navigation_bar.follows_and_followers": "Jälgitud ja jälgijad", + "navigation_bar.info": "Selle serveri kohta", + "navigation_bar.keyboard_shortcuts": "Kiirklahvid", + "navigation_bar.lists": "Nimistud", + "navigation_bar.logout": "Logi välja", + "navigation_bar.mutes": "Vaigistatud kasutajad", + "navigation_bar.personal": "Isiklik", + "navigation_bar.pins": "Kinnitatud tuutid", + "navigation_bar.preferences": "Eelistused", + "navigation_bar.public_timeline": "Föderatiivne ajajoon", + "navigation_bar.security": "Turvalisus", + "notification.favourite": "{name} märkis su staatuse lemmikuks", + "notification.follow": "{name} jälgib sind", + "notification.mention": "{name} mainis sind", + "notification.poll": "Küsitlus, milles osalesid, on lõppenud", + "notification.reblog": "{name} upitas su staatust", + "notifications.clear": "Puhasta teated", + "notifications.clear_confirmation": "Oled kindel, et soovid püsivalt kõik oma teated puhastada?", + "notifications.column_settings.alert": "Töölauateated", + "notifications.column_settings.favourite": "Lemmikud:", + "notifications.column_settings.filter_bar.advanced": "Kuva kõik kategooriad", + "notifications.column_settings.filter_bar.category": "Kiirfiltri riba", + "notifications.column_settings.filter_bar.show": "Kuva", + "notifications.column_settings.follow": "Uued jälgijad:", + "notifications.column_settings.mention": "Mainimised:", + "notifications.column_settings.poll": "Küsitluse tulemused:", + "notifications.column_settings.push": "Push teated", + "notifications.column_settings.reblog": "Upitused:", + "notifications.column_settings.show": "Kuva tulbas", + "notifications.column_settings.sound": "Mängi heli", + "notifications.filter.all": "Kõik", + "notifications.filter.boosts": "Upitused", + "notifications.filter.favourites": "Lemmikud", + "notifications.filter.follows": "Jälgib", + "notifications.filter.mentions": "Mainimised", + "notifications.filter.polls": "Küsitluse tulemused", + "notifications.group": "{count} teated", + "poll.closed": "Suletud", + "poll.refresh": "Värskenda", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# hääl} other {# hääli}}", + "poll.vote": "Hääleta", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Lisa küsitlus", + "poll_button.remove_poll": "Eemalda küsitlus", + "privacy.change": "Muuda staatuse privaatsust", + "privacy.direct.long": "Postita ainult mainitud kasutajatele", + "privacy.direct.short": "Otsene", + "privacy.private.long": "Postita ainult jälgijatele", + "privacy.private.short": "Ainult jälgijatele", + "privacy.public.long": "Postita avalikele ajajoontele", + "privacy.public.short": "Avalik", + "privacy.unlisted.long": "Ära postita avalikele ajajoontele", + "privacy.unlisted.short": "Määramata", + "refresh": "Refresh", + "regeneration_indicator.label": "Laeb…", + "regeneration_indicator.sublabel": "Sinu kodu voog on ettevalmistamisel!", + "relative_time.days": "{number}p", + "relative_time.hours": "{number}t", + "relative_time.just_now": "nüüd", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Tühista", + "report.forward": "Edasta kasutajale {target}", + "report.forward_hint": "See kasutaja on teisest serverist. Kas saadan anonümiseeritud koopia sellest teatest sinna ka?", + "report.hint": "See teade saadetakse sinu serveri moderaatoritele. Te saate lisada selgituse selle kohta, miks selle kasutaja kohta teate esitasite, siin:", + "report.placeholder": "Lisaks kommentaarid", + "report.submit": "Saada", + "report.target": "Teatamine {target} kohta", + "search.placeholder": "Otsi", + "search_popout.search_format": "Täiustatud otsiformaat", + "search_popout.tips.full_text": "Lihtne tekst toob esile staatused mida olete kirjutanud, lisanud lemmikuks, upitanud või olete seal mainitud, ning lisaks veel kattuvad kasutajanimed, kuvanimed ja sildid.", + "search_popout.tips.hashtag": "silt", + "search_popout.tips.status": "staatus", + "search_popout.tips.text": "Lihtne tekst toob esile kattuvad kuvanimed, kasutajanimed ning sildid", + "search_popout.tips.user": "kasutaja", + "search_results.accounts": "Inimesed", + "search_results.hashtags": "Sildid", + "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.total": "{count, number} {count, plural, one {tulemus} other {tulemust}}", + "status.admin_account": "Ava moderaatoriliides kasutajale @{name}", + "status.admin_status": "Ava see staatus moderaatoriliites", + "status.block": "Blokeeri @{name}", + "status.cancel_reblog_private": "Äraupita", + "status.cannot_reblog": "Seda postitust ei saa upitada", + "status.copy": "Kopeeri link staatusesse", + "status.delete": "Kustuta", + "status.detailed_status": "Detailne vestluskuva", + "status.direct": "Otsesõnum @{name}", + "status.embed": "Sängita", + "status.favourite": "Lemmik", + "status.filtered": "Filtreeritud", + "status.load_more": "Lae veel", + "status.media_hidden": "Meedia peidetud", + "status.mention": "Mainimine @{name}", + "status.more": "Veel", + "status.mute": "Vaigista @{name}", + "status.mute_conversation": "Vaigista vestlus", + "status.open": "Laienda see staatus", + "status.pin": "Kinnita profiilile", + "status.pinned": "Kinnitatud tuut", + "status.read_more": "Loe veel", + "status.reblog": "Upita", + "status.reblog_private": "Upita algsele publikule", + "status.reblogged_by": "{name} upitatud", + "status.reblogs.empty": "Keegi pole seda tuuti veel upitanud. Kui keegi upitab, näed seda siin.", + "status.redraft": "Kustuta & alga uuesti", + "status.reply": "Vasta", + "status.replyAll": "Vasta lõimele", + "status.report": "Raport @{name}", + "status.sensitive_warning": "Tundlik sisu", + "status.share": "Jaga", + "status.show_less": "Näita vähem", + "status.show_less_all": "Näita vähem kõigile", + "status.show_more": "Näita veel", + "status.show_more_all": "Näita enam kõigile", + "status.show_thread": "Kuva lõim", + "status.uncached_media_warning": "Not available", + "status.unmute_conversation": "Ära vaigista vestlust", + "status.unpin": "Kinnita profiililt lahti", + "suggestions.dismiss": "Eira soovitust", + "suggestions.header": "Sind võib huvitada…", + "tabs_bar.federated_timeline": "Föderatiivne", + "tabs_bar.home": "Kodu", + "tabs_bar.local_timeline": "Kohalik", + "tabs_bar.notifications": "Teated", + "tabs_bar.search": "Otsi", + "time_remaining.days": "{number, plural, one {# päev} other {# päeva}} left", + "time_remaining.hours": "{number, plural, one {# tund} other {# tundi}} left", + "time_remaining.minutes": "{number, plural, one {# minut} other {# minutit}} left", + "time_remaining.moments": "Hetked jäänud", + "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekundit}} left", + "trends.count_by_accounts": "{count} {rawCount, plural, one {inimene} other {inimesed}} talking", + "trends.trending_now": "Trending now", + "ui.beforeunload": "Sinu mustand läheb kaotsi, kui lahkud Mastodonist.", + "upload_area.title": "Lohista & aseta üleslaadimiseks", + "upload_button.label": "Lisa meedia (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_error.limit": "Faili üleslaadimise limiit ületatud.", + "upload_error.poll": "Küsitlustes pole faili üleslaadimine lubatud.", + "upload_form.description": "Kirjelda vaegnägijatele", + "upload_form.edit": "Edit", + "upload_form.undo": "Kustuta", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Laeb üles....", + "video.close": "Sulge video", + "video.exit_fullscreen": "Välju täisekraanist", + "video.expand": "Suurenda video", + "video.fullscreen": "Täisekraan", + "video.hide": "Peida video", + "video.mute": "Vaigista heli", + "video.pause": "Paus", + "video.play": "Mängi", + "video.unmute": "Taasta heli" +} diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 53d614eb91397649488b20a61ae901749b770540..1fb7faf9adf53a8a1ac081d14d1cdac7fb6039ec 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -4,6 +4,7 @@ "account.block": "Blokeatu @{name}", "account.block_domain": "Ezkutatu {domain} domeinuko guztia", "account.blocked": "Blokeatuta", + "account.cancel_follow_request": "Ezeztatu jarraitzeko eskaria", "account.direct": "Mezu zuzena @{name}(r)i", "account.domain_blocked": "Ezkutatutako domeinua", "account.edit_profile": "Aldatu profila", @@ -15,16 +16,18 @@ "account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.", "account.follows_you": "Jarraitzen dizu", "account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak", + "account.last_status": "Azkenekoz aktiboa", "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.media": "Multimedia", "account.mention": "Aipatu @{name}", - "account.moved_to": "{name} hona lekualdatu da:", + "account.moved_to": "{name} hona migratu da:", "account.mute": "Mututu @{name}", "account.mute_notifications": "Mututu @{name}(r)en jakinarazpenak", "account.muted": "Mutututa", - "account.posts": "Toot", - "account.posts_with_replies": "Toot eta erantzunak", + "account.never_active": "Inoiz ez", + "account.posts": "Tootak", + "account.posts_with_replies": "Toot-ak eta erantzunak", "account.report": "Salatu @{name}", "account.requested": "Onarpenaren zain. Klikatu jarraitzeko eskaera ezeztatzeko", "account.share": "@{name}(e)ren profila elkarbanatu", @@ -35,8 +38,11 @@ "account.unfollow": "Utzi jarraitzeari", "account.unmute": "Desmututu @{name}", "account.unmute_notifications": "Desmututu @{name}(r)en jakinarazpenak", + "alert.rate_limited.message": "Saiatu {retry_time, time, medium} barru.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Ustekabeko errore bat gertatu da.", "alert.unexpected.title": "Ene!", + "autosuggest_hashtag.per_week": "{count} asteko", "boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko", "bundle_column_error.body": "Zerbait okerra gertatu da osagai hau kargatzean.", "bundle_column_error.retry": "Saiatu berriro", @@ -47,6 +53,7 @@ "column.blocks": "Blokeatutako erabiltzaileak", "column.community": "Denbora-lerro lokala", "column.direct": "Mezu zuzenak", + "column.directory": "Arakatu profilak", "column.domain_blocks": "Ezkutatutako domeinuak", "column.favourites": "Gogokoak", "column.follow_requests": "Jarraitzeko eskariak", @@ -54,7 +61,7 @@ "column.lists": "Zerrendak", "column.mutes": "Mutututako erabiltzaileak", "column.notifications": "Jakinarazpenak", - "column.pins": "Pinned toot", + "column.pins": "Finkatutako toot-ak", "column.public": "Federatutako denbora-lerroa", "column_back_button.label": "Atzera", "column_header.hide_settings": "Ezkutatu ezarpenak", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Ziur behin betiko ezabatu nahi duzula zerrenda hau?", "confirmations.domain_block.confirm": "Ezkutatu domeinu osoa", "confirmations.domain_block.message": "Ziur, erabat ziur, {domain} domeinu osoa blokeatu nahi duzula? Gehienetan gutxi batzuk blokeatu edo mututzearekin nahikoa da. Ez duzu domeinu horretako edukirik ikusiko denbora lerroetan edo jakinarazpenetan. Domeinu horretako zure jarraitzaileak kenduko dira ere.", + "confirmations.logout.confirm": "Amaitu saioa", + "confirmations.logout.message": "Ziur saioa amaitu nahi duzula?", "confirmations.mute.confirm": "Mututu", + "confirmations.mute.explanation": "Honek horko mezuak eta aipamena egiten dietenak ezkutatuko ditu, baina beraiek zure mezuak ikusi ahal izango dituzte eta zuri jarraitu.", "confirmations.mute.message": "Ziur {name} mututu nahi duzula?", "confirmations.redraft.confirm": "Ezabatu eta berridatzi", "confirmations.redraft.message": "Ziur mezu hau ezabatu eta berridatzi nahi duzula? Gogokoak eta bultzadak galduko dira eta jaso dituen erantzunak umezurtz geratuko dira.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Orain erantzuteak idazten ari zaren mezua gainidatziko du. Ziur jarraitu nahi duzula?", "confirmations.unfollow.confirm": "Utzi jarraitzeari", "confirmations.unfollow.message": "Ziur {name} jarraitzeari utzi nahi diozula?", + "conversation.delete": "Ezabatu elkarrizketa", + "conversation.mark_as_read": "Markatu irakurrita bezala", + "conversation.open": "Ikusi elkarrizketa", + "conversation.with": "Hauekin: {names}", + "directory.federated": "Fedibertso ezagunekoak", + "directory.local": "{domain} domeinukoak soilik", + "directory.new_arrivals": "Iritsi berriak", + "directory.recently_active": "Duela gutxi aktibo", "embed.instructions": "Txertatu mezu hau zure webgunean beheko kodea kopatuz.", "embed.preview": "Hau da izango duen itxura:", "emoji_button.activity": "Jarduera", @@ -134,6 +152,10 @@ "empty_column.mutes": "Ez duzu erabiltzailerik mututu oraindik.", "empty_column.notifications": "Ez duzu jakinarazpenik oraindik. Jarri besteekin harremanetan elkarrizketa abiatzeko.", "empty_column.public": "Ez dago ezer hemen! Idatzi zerbait publikoki edo jarraitu eskuz beste zerbitzari batzuetako erabiltzaileak hau betetzen joateko", + "error.unexpected_crash.explanation": "Gure kodean arazoren bat dela eta, edo nabigatzailearekin bateragarritasun arazoren bat dela eta, orri hau ezin izan da ongi bistaratu.", + "error.unexpected_crash.next_steps": "Saiatu orria berritzen. Horrek ez badu laguntzen, agian Mastodon erabiltzeko aukera duzu oraindik ere beste nabigatzaile bat edo aplikazio natibo bat erabilita.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Eman arazoaren berri", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Zerrenda berriaren izena", "lists.search": "Bilatu jarraitzen dituzun pertsonen artean", "lists.subheading": "Zure zerrendak", + "load_pending": "{count, plural, one {eleentuberri #} other {# elementu berri}}", "loading_indicator.label": "Kargatzen...", "media_gallery.toggle_visible": "Txandakatu ikusgaitasuna", "missing_indicator.label": "Ez aurkitua", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Pertsonala", "navigation_bar.pins": "Finkatutako toot-ak", "navigation_bar.preferences": "Hobespenak", - "navigation_bar.profile_directory": "Profilen direktorioa", "navigation_bar.public_timeline": "Federatutako denbora-lerroa", "navigation_bar.security": "Segurtasuna", "notification.favourite": "{name}(e)k zure mezua gogoko du", @@ -282,8 +304,10 @@ "notifications.group": "{count} jakinarazpen", "poll.closed": "Itxita", "poll.refresh": "Berritu", + "poll.total_people": "{count, plural, one {pertsona #} other {# pertsona}}", "poll.total_votes": "{count, plural, one {boto #} other {# boto}}", "poll.vote": "Bozkatu", + "poll.voted": "Erantzun honi eman diozu botoa", "poll_button.add_poll": "Gehitu inkesta bat", "poll_button.remove_poll": "Kendu inkesta", "privacy.change": "Doitu mezuaren pribatutasuna", @@ -295,6 +319,7 @@ "privacy.public.short": "Publikoa", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Zerrendatu gabea", + "refresh": "Berritu", "regeneration_indicator.label": "Kargatzen…", "regeneration_indicator.sublabel": "Zure hasiera-jarioa prestatzen ari da!", "relative_time.days": "{number}e", @@ -319,6 +344,7 @@ "search_results.accounts": "Jendea", "search_results.hashtags": "Traolak", "search_results.statuses": "Toot-ak", + "search_results.statuses_fts_disabled": "Mastodon zerbitzari honek ez du Toot-en edukiaren bilaketa gaitu.", "search_results.total": "{count, number} {count, plural, one {emaitza} other {emaitzak}}", "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea", "status.admin_status": "Ireki mezu hau moderazio interfazean", @@ -358,6 +384,7 @@ "status.show_more": "Erakutsi gehiago", "status.show_more_all": "Erakutsi denetarik gehiago", "status.show_thread": "Erakutsi haria", + "status.uncached_media_warning": "Ez eskuragarri", "status.unmute_conversation": "Desmututu elkarrizketa", "status.unpin": "Desfinkatu profiletik", "suggestions.dismiss": "Errefusatu proposamena", @@ -373,14 +400,22 @@ "time_remaining.moments": "Amaitzekotan", "time_remaining.seconds": "{number, plural, one {segundo #} other {# segundo}} amaitzeko", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} hitz egiten", + "trends.trending_now": "Joera orain", "ui.beforeunload": "Zure zirriborroa galduko da Mastodon uzten baduzu.", "upload_area.title": "Arrastatu eta jaregin igotzeko", "upload_button.label": "Gehitu multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Fitxategi igoera muga gaindituta.", "upload_error.poll": "Ez da inkestetan fitxategiak igotzea onartzen.", "upload_form.description": "Deskribatu ikusmen arazoak dituztenentzat", - "upload_form.focus": "Aldatu aurrebista", + "upload_form.edit": "Editatu", "upload_form.undo": "Ezabatu", + "upload_modal.analyzing_picture": "Irudia aztertzen…", + "upload_modal.apply": "Aplikatu", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Antzeman irudiko testua", + "upload_modal.edit_media": "Editatu multimedia", + "upload_modal.hint": "Sakatu eta jaregin aurrebistako zirkulua iruditxoetan beti ikusgai egongo den puntu fokala hautatzeko.", + "upload_modal.preview_label": "Aurreikusi({ratio})", "upload_progress.label": "Igotzen...", "video.close": "Itxi bideoa", "video.exit_fullscreen": "Irten pantaila osotik", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index ab408d53d187898e66ed60cca2aa6e54ff8167d9..fa3453f105d3d4dd341c7a763b178884f1917469 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,9 +1,10 @@ { - "account.add_or_remove_from_list": "Ø§ÙØ²ÙˆØ¯Ù† یا ØØ°Ù از Ùهرست‌ها", + "account.add_or_remove_from_list": "Ø§ÙØ²ÙˆØ¯Ù† یا برداشتن از Ùهرست", "account.badges.bot": "ربات", "account.block": "مسدودسازی @{name}", "account.block_domain": "پنهان‌سازی همه چیز از سرور {domain}", - "account.blocked": "مسدودشده", + "account.blocked": "مسدود شده", + "account.cancel_follow_request": "لغو درخواست پیگیری", "account.direct": "پیغام خصوصی به @{name}", "account.domain_blocked": "دامین پنهان‌شده", "account.edit_profile": "ویرایش نمایه", @@ -15,7 +16,8 @@ "account.follows.empty": "این کاربر هنوز هیچ کسی را Ù¾ÛŒ نمی‌گیرد.", "account.follows_you": "پیگیر شماست", "account.hide_reblogs": "پنهان کردن بازبوق‌های @{name}", - "account.link_verified_on": "مالکیت این نشانی در تایخ {date} بررسی شد", + "account.last_status": "آخرین ÙØ¹Ø§Ù„یت", + "account.link_verified_on": "مالکیت این نشانی در تاریخ {date} بررسی شد", "account.locked_info": "این ØØ³Ø§Ø¨ خصوصی است. ØµØ§ØØ¨ این ØØ³Ø§Ø¨ تصمیم می‌گیرد Ú©Ù‡ Ú†Ù‡ کسی می‌تواند پیگیرش باشد.", "account.media": "عکس Ùˆ ویدیو", "account.mention": "نام‌بردن از @{name}", @@ -23,6 +25,7 @@ "account.mute": "بی‌صدا کردن @{name}", "account.mute_notifications": "بی‌صداکردن اعلان‌ها از طر٠@{name}", "account.muted": "بی‌صداشده", + "account.never_active": "هرگز", "account.posts": "نوشته‌ها", "account.posts_with_replies": "نوشته‌ها Ùˆ پاسخ‌ها", "account.report": "گزارش @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "پایان پیگیری", "account.unmute": "باصدا کردن @{name}", "account.unmute_notifications": "باصداکردن اعلان‌ها از طر٠@{name}", + "alert.rate_limited.message": "Ù„Ø·ÙØ§Ù‹ پس از {retry_time, time, medium} دوباره تلاش کنید.", + "alert.rate_limited.title": "Ù…ØØ¯ÙˆØ¯ÛŒØª تعداد", "alert.unexpected.message": "خطای پیش‌بینی‌نشده‌ای رخ داد.", "alert.unexpected.title": "ای وای!", + "autosuggest_hashtag.per_week": "{count} در Ù‡ÙØªÙ‡", "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.", "bundle_column_error.retry": "تلاش دوباره", @@ -47,6 +53,7 @@ "column.blocks": "کاربران مسدودشده", "column.community": "نوشته‌های Ù…ØÙ„ÛŒ", "column.direct": "پیغام‌های خصوصی", + "column.directory": "مرور نمایه‌ها", "column.domain_blocks": "دامین‌های پنهان‌شده", "column.favourites": "پسندیده‌ها", "column.follow_requests": "درخواست‌های پیگیری", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "آیا واقعاً می‌خواهید این Ùهرست را برای همیشه پاک کنید؟", "confirmations.domain_block.confirm": "پنهان‌سازی Ú©Ù„ دامین", "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید Ú©Ù„ دامین {domain} را مسدود کنید؟ بیشتر وقت‌ها مسدودکردن یا بی‌صداکردن چند ØØ³Ø§Ø¨ کاربری خاص کاÙÛŒ است Ùˆ توصیه می‌شود. پس از این کار شما هیچ نوشته‌ای را از این دامین در Ùهرست نوشته‌های عمومی یا اعلان‌هایتان نخواهید دید. پیگیران شما از این دامین هم ØØ°Ù خواهد شد.", + "confirmations.logout.confirm": "خروج", + "confirmations.logout.message": "آیا مطمئنید Ú©Ù‡ می‌خواهید خارج شوید؟", "confirmations.mute.confirm": "بی‌صدا Ú©Ù†", + "confirmations.mute.explanation": "این کار نوشته‌های آن‌ها Ùˆ نوشته‌هایی را Ú©Ù‡ از آن‌ها نام برده پنهان می‌کند، ولی آن‌ها همچنان اجازه دارند نوشته‌های شما را ببینند Ùˆ شما را Ù¾ÛŒ بگیرند.", "confirmations.mute.message": "آیا واقعاً می‌خواهید {name} را بی‌صدا کنید؟", "confirmations.redraft.confirm": "پاک‌کردن Ùˆ بازنویسی", "confirmations.redraft.message": "آیا واقعاً می‌خواهید این نوشته را پاک کنید Ùˆ آن را از نو بنویسید؟ با این کار بازبوق‌ها Ùˆ پسندیده‌شدن‌های آن از دست می‌رود Ùˆ پاسخ‌ها به آن بی‌مرجع می‌شود.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "اگر الان پاسخ دهید، چیزی Ú©Ù‡ در ØØ§Ù„ نوشتنش بودید پاک خواهد شد. آیا همین را می‌خواهید؟", "confirmations.unfollow.confirm": "لغو پیگیری", "confirmations.unfollow.message": "آیا واقعاً می‌خواهید به پیگیری از {name} پایان دهید؟", + "conversation.delete": "ØØ°Ù Ú¯ÙØªÚ¯Ùˆ", + "conversation.mark_as_read": "علامت‌گذاری به عنوان خوانده شده", + "conversation.open": "دیدن Ú¯ÙØªÚ¯Ùˆ", + "conversation.with": "با {names}", + "directory.federated": "از سرورهای همسایه", + "directory.local": "تنها از {domain}", + "directory.new_arrivals": "تازه‌واردان", + "directory.recently_active": "کاربران ÙØ¹Ø§Ù„ اخیر", "embed.instructions": "برای جاگذاری این نوشته در سایت خودتان، کد زیر را Ú©Ù¾ÛŒ کنید.", "embed.preview": "نوشتهٔ جاگذاری‌شده این گونه به نظر خواهد رسید:", "emoji_button.activity": "ÙØ¹Ø§Ù„یت", @@ -126,7 +144,7 @@ "empty_column.favourited_statuses": "شما هنوز هیچ بوقی را نپسندیده‌اید. وقتی بوقی را بپسندید، این‌جا نمایش خواهد ÛŒØ§ÙØª.", "empty_column.favourites": "هنوز هیچ کسی این بوق را نپسندیده است. وقتی کسی آن را بپسندد، نامش این‌جا نمایش خواهد ÛŒØ§ÙØª.", "empty_column.follow_requests": "شما هنوز هیچ درخواست پیگیری‌ای ندارید. وقتی چنین درخواستی بگیرید، این‌جا نمایش خواهد ÛŒØ§ÙØª.", - "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", + "empty_column.hashtag": "هنوز هیچ چیزی با این برچسب (هشتگ) نیست.", "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", "empty_column.home.public_timeline": "Ùهرست نوشته‌های همه‌جا", "empty_column.list": "در این Ùهرست هنوز چیزی نیست. وقتی اعضای این Ùهرست چیزی بنویسند، این‌جا ظاهر خواهد شد.", @@ -134,6 +152,10 @@ "empty_column.mutes": "شما هنوز هیچ کاربری را بی‌صدا نکرده‌اید.", "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا Ú¯ÙØªÚ¯Ùˆ آغاز شود.", "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران سرورهای دیگر را Ù¾ÛŒ بگیرید تا این‌جا پر شود", + "error.unexpected_crash.explanation": "به خاطر اشکالی در کدهای ما یا ناسازگاری با مرورگر شما، این ØµÙØÙ‡ به درستی نمایش Ù†ÛŒØ§ÙØª.", + "error.unexpected_crash.next_steps": "Ù„Ø·ÙØ§Ù‹ ØµÙØÙ‡ را دوباره باز کنید. اگر آن هم Ú©Ù…Ú©ÛŒ نکرد، همچنان شاید بتوانید با ماستدون از راه یکی از اپ‌های آن کار کنید.", + "errors.unexpected_crash.copy_stacktrace": "Ú©Ù¾ÛŒ جزئیات اشکال", + "errors.unexpected_crash.report_issue": "گزارش اشکال", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "نام Ùهرست تازه", "lists.search": "بین کسانی Ú©Ù‡ Ù¾ÛŒ می‌گیرید بگردید", "lists.subheading": "Ùهرست‌های شما", + "load_pending": "{count, plural, one {# مورد تازه} other {# مورد تازه}}", "loading_indicator.label": "بارگیری...", "media_gallery.toggle_visible": "تغییر پیدایی", "missing_indicator.label": "پیدا نشد", @@ -251,7 +274,6 @@ "navigation_bar.personal": "شخصی", "navigation_bar.pins": "نوشته‌های ثابت", "navigation_bar.preferences": "ØªØ±Ø¬ÛŒØØ§Øª", - "navigation_bar.profile_directory": "Ùهرست گزیدهٔ کاربران", "navigation_bar.public_timeline": "نوشته‌های همه‌جا", "navigation_bar.security": "امنیت", "notification.favourite": "‫{name}‬ نوشتهٔ شما را پسندید", @@ -282,8 +304,10 @@ "notifications.group": "{count} اعلان", "poll.closed": "Ù¾Ø§ÛŒØ§Ù†â€ŒÛŒØ§ÙØªÙ‡", "poll.refresh": "به‌روزرسانی", + "poll.total_people": "{count, plural, one {# Ù†ÙØ±} other {# Ù†ÙØ±}}", "poll.total_votes": "{count, plural, one {# رأی} other {# رأی}}", "poll.vote": "رأی", + "poll.voted": "شما به این گزینه رأی دادید", "poll_button.add_poll": "Ø§ÙØ²ÙˆØ¯Ù† نظرسنجی", "poll_button.remove_poll": "ØØ°Ù نظرسنجی", "privacy.change": "تنظیم ØØ±ÛŒÙ… خصوصی نوشته‌ها", @@ -295,6 +319,7 @@ "privacy.public.short": "عمومی", "privacy.unlisted.long": "عمومی، ولی Ùهرست Ù†Ú©Ù†", "privacy.unlisted.short": "Ùهرست‌نشده", + "refresh": "به‌روزرسانی", "regeneration_indicator.label": "در ØØ§Ù„ باز شدن…", "regeneration_indicator.sublabel": "این Ùهرست دارد آماده می‌شود!", "relative_time.days": "{number} روز", @@ -312,13 +337,14 @@ "search.placeholder": "جستجو", "search_popout.search_format": "راهنمای جستجوی Ù¾ÛŒØ´Ø±ÙØªÙ‡", "search_popout.tips.full_text": "جستجوی متنی ساده می‌تواند بوق‌هایی Ú©Ù‡ شما نوشته‌اید، پسندیده‌اید، بازبوقیده‌اید، یا در آن‌ها از شما نام برده شده است را پیدا کند. همچنین نام‌های کاربری، نام Ù†Ù…Ø§ÛŒØ´â€ŒÛŒØ§ÙØªÙ‡ØŒ Ùˆ هشتگ‌ها را هم شامل می‌شود.", - "search_popout.tips.hashtag": "هشتگ", + "search_popout.tips.hashtag": "برچسب", "search_popout.tips.status": "نوشته", - "search_popout.tips.text": "جستجوی متنی ساده برای نام‌ها، نام‌های کاربری، Ùˆ هشتگ‌ها", + "search_popout.tips.text": "جستجوی متنی ساده برای نام‌ها، نام‌های کاربری، Ùˆ برچسب‌ها", "search_popout.tips.user": "کاربر", "search_results.accounts": "Ø§ÙØ±Ø§Ø¯", - "search_results.hashtags": "هشتگ‌ها", + "search_results.hashtags": "برچسب‌ها", "search_results.statuses": "بوق‌ها", + "search_results.statuses_fts_disabled": "جستجوی Ù…ØØªÙˆØ§ÛŒ بوق‌ها در این سرور ماستدون ممکن نیست.", "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}", "status.admin_account": "Ù…ØÛŒØ· مدیریت مربوط به @{name} را باز Ú©Ù†", "status.admin_status": "این نوشته را در Ù…ØÛŒØ· مدیریت باز Ú©Ù†", @@ -358,6 +384,7 @@ "status.show_more": "نمایش", "status.show_more_all": "نمایش بیشتر همه", "status.show_thread": "نمایش Ú¯ÙØªÚ¯Ùˆ", + "status.uncached_media_warning": "ناموجود", "status.unmute_conversation": "باصداکردن Ú¯ÙØªÚ¯Ùˆ", "status.unpin": "برداشتن نوشتهٔ ثابت نمایه", "suggestions.dismiss": "پیشنهاد را نادیده بگیر", @@ -373,14 +400,22 @@ "time_remaining.moments": "زمان باقی‌مانده", "time_remaining.seconds": "{number, plural, one {# ثانیه} other {# ثانیه}} باقی مانده", "trends.count_by_accounts": "{count} {rawCount, plural, one {Ù†ÙØ± نوشته است} other {Ù†ÙØ± نوشته‌اند}}", + "trends.trending_now": "Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø±", "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.", "upload_area.title": "برای بارگذاری به این‌جا بکشید", "upload_button.label": "Ø§ÙØ²ÙˆØ¯Ù† عکس Ùˆ ویدیو (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "از ØØ¯ مجاز باگذاری ÙØ±Ø§ØªØ± Ø±ÙØªÛŒØ¯.", "upload_error.poll": "باگذاری پرونده در نظرسنجی‌ها ممکن نیست.", "upload_form.description": "نوشتهٔ توضیØÛŒ برای کم‌بینایان Ùˆ نابینایان", - "upload_form.focus": "تغییر پیش‌نمایش", + "upload_form.edit": "ویرایش", "upload_form.undo": "ØØ°Ù", + "upload_modal.analyzing_picture": "در ØØ§Ù„ پردازش تصویر…", + "upload_modal.apply": "اجرا", + "upload_modal.description_placeholder": "مردی با بیل مادرزنش را کشت", + "upload_modal.detect_text": "پیدا کردن نوشته از درون تصویر", + "upload_modal.edit_media": "ویرایش تصویر", + "upload_modal.hint": "ØØªÛŒ اگر تصویر بریده یا Ú©ÙˆÚ†Ú© شود، نقطهٔ کانونی آن همیشه دیده خواهد شد. نقطهٔ کانونی را با کلیک یا جابه‌جا کردن آن تنظیم کنید.", + "upload_modal.preview_label": "پیش‌نمایش ({ratio})", "upload_progress.label": "بارگذاری...", "video.close": "بستن ویدیو", "video.exit_fullscreen": "خروج از ØØ§Ù„ت تمام ØµÙØÙ‡", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 4817b16f85618b6c601391842a5d2b18dc7cc9a0..ca96d298abd397120eba70b66d541d13ba470ef2 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -4,17 +4,19 @@ "account.block": "Estä @{name}", "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}", "account.blocked": "Estetty", + "account.cancel_follow_request": "Peruuta seurauspyyntö", "account.direct": "Viesti käyttäjälle @{name}", "account.domain_blocked": "Verkko-osoite piilotettu", "account.edit_profile": "Muokkaa", "account.endorse": "Suosittele profiilissasi", "account.follow": "Seuraa", - "account.followers": "Seuraajia", + "account.followers": "Seuraajaa", "account.followers.empty": "Tällä käyttäjällä ei ole vielä seuraajia.", "account.follows": "Seuraa", "account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.", "account.follows_you": "Seuraa sinua", "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}", + "account.last_status": "Aktiivinen viimeksi", "account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}", "account.locked_info": "Tämän tili on yksityinen. Käyttäjä vahvistaa itse kuka voi seurata häntä.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Mykistä @{name}", "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}", "account.muted": "Mykistetty", + "account.never_active": "Ei koskaan", "account.posts": "Tuuttaukset", "account.posts_with_replies": "Tuuttaukset ja vastaukset", "account.report": "Raportoi @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Lakkaa seuraamasta", "account.unmute": "Poista käyttäjän @{name} mykistys", "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta", + "alert.rate_limited.message": "Yritä uudestaan {retry_time, time, medium} jälkeen.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.title": "Hups!", + "autosuggest_hashtag.per_week": "{count} viikossa", "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}", "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.", "bundle_column_error.retry": "Yritä uudestaan", @@ -47,6 +53,7 @@ "column.blocks": "Estetyt käyttäjät", "column.community": "Paikallinen aikajana", "column.direct": "Viestit", + "column.directory": "Selaa profiileja", "column.domain_blocks": "Piilotetut verkkotunnukset", "column.favourites": "Suosikit", "column.follow_requests": "Seuraamispyynnöt", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Haluatko varmasti poistaa tämän listan kokonaan?", "confirmations.domain_block.confirm": "Piilota koko verkko-osoite", "confirmations.domain_block.message": "Haluatko aivan varmasti estää koko verkko-osoitteen {domain}? Useimmiten jokunen kohdistettu esto ja mykistys riittää, ja se on suositeltavampi tapa toimia.", + "confirmations.logout.confirm": "Kirjaudu ulos", + "confirmations.logout.message": "Oletko varma, että haluat kirjautua ulos?", "confirmations.mute.confirm": "Mykistä", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Haluatko varmasti mykistää käyttäjän {name}?", "confirmations.redraft.confirm": "Poista & palauta muokattavaksi", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Jos vastaat nyt, vastaus korvaa tällä hetkellä työstämäsi viestin. Oletko varma, että haluat jatkaa?", "confirmations.unfollow.confirm": "Lakkaa seuraamasta", "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta käyttäjää {name}?", + "conversation.delete": "Poista keskustelu", + "conversation.mark_as_read": "Merkitse luetuksi", + "conversation.open": "Näytä keskustelu", + "conversation.with": "{names} kanssa", + "directory.federated": "Koko tunnettu fediverse", + "directory.local": "Vain palvelimelta {domain}", + "directory.new_arrivals": "Äskettäin saapuneet", + "directory.recently_active": "Hiljattain aktiiviset", "embed.instructions": "Upota statuspäivitys sivullesi kopioimalla alla oleva koodi.", "embed.preview": "Se tulee näyttämään tältä:", "emoji_button.activity": "Aktiviteetit", @@ -134,6 +152,10 @@ "empty_column.mutes": "Et ole mykistänyt vielä yhtään käyttäjää.", "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Aloita keskustelu juttelemalla muille.", "empty_column.public": "Täällä ei ole mitään! Saat sisältöä, kun kirjoitat jotain julkisesti tai käyt seuraamassa muiden instanssien käyttäjiä", + "error.unexpected_crash.explanation": "Sivua ei voi näyttää oikein, johtuen bugista tai ongelmasta selaimen yhteensopivuudessa.", + "error.unexpected_crash.next_steps": "Kokeile päivittää sivu. Jos tämä ei auta, saatat yhä pystyä käyttämään Mastodonia toisen selaimen tai sovelluksen kautta.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Ilmoita ongelmasta", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -209,14 +231,14 @@ "keyboard_shortcuts.search": "siirry hakukenttään", "keyboard_shortcuts.start": "avaa \"Aloitus\" -sarake", "keyboard_shortcuts.toggle_hidden": "näytä/piilota sisältövaroituksella merkitty teksti", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "näytä/piilota media", "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta", "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä", "keyboard_shortcuts.up": "siirry listassa ylöspäin", "lightbox.close": "Sulje", "lightbox.next": "Seuraava", "lightbox.previous": "Edellinen", - "lightbox.view_context": "View context", + "lightbox.view_context": "Näytä kontekstissa", "lists.account.add": "Lisää listaan", "lists.account.remove": "Poista listasta", "lists.delete": "Poista lista", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Uuden listan nimi", "lists.search": "Etsi seuraamistasi henkilöistä", "lists.subheading": "Omat listat", + "load_pending": "{count, plural, one {# uusi kappale} other {# uutta kappaletta}}", "loading_indicator.label": "Ladataan...", "media_gallery.toggle_visible": "Säädä näkyvyyttä", "missing_indicator.label": "Ei löytynyt", @@ -236,56 +259,57 @@ "navigation_bar.community_timeline": "Paikallinen aikajana", "navigation_bar.compose": "Kirjoita uusi tuuttaus", "navigation_bar.direct": "Viestit", - "navigation_bar.discover": "Discover", + "navigation_bar.discover": "Löydä uutta", "navigation_bar.domain_blocks": "Piilotetut verkkotunnukset", "navigation_bar.edit_profile": "Muokkaa profiilia", "navigation_bar.favourites": "Suosikit", "navigation_bar.filters": "Mykistetyt sanat", "navigation_bar.follow_requests": "Seuraamispyynnöt", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Seurattavat ja seuraajat", "navigation_bar.info": "Tietoa tästä instanssista", "navigation_bar.keyboard_shortcuts": "Näppäinkomennot", "navigation_bar.lists": "Listat", "navigation_bar.logout": "Kirjaudu ulos", "navigation_bar.mutes": "Mykistetyt käyttäjät", - "navigation_bar.personal": "Personal", + "navigation_bar.personal": "Henkilökohtaiset", "navigation_bar.pins": "Kiinnitetyt tuuttaukset", "navigation_bar.preferences": "Asetukset", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Yleinen aikajana", "navigation_bar.security": "Tunnukset", "notification.favourite": "{name} tykkäsi tilastasi", "notification.follow": "{name} seurasi sinua", "notification.mention": "{name} mainitsi sinut", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "Kysely, johon osallistuit, on päättynyt", "notification.reblog": "{name} buustasi tilaasi", "notifications.clear": "Tyhjennä ilmoitukset", "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?", "notifications.column_settings.alert": "Työpöytäilmoitukset", "notifications.column_settings.favourite": "Tykkäykset:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Näytä kaikki kategoriat", + "notifications.column_settings.filter_bar.category": "Pikasuodatuspalkki", + "notifications.column_settings.filter_bar.show": "Näytä", "notifications.column_settings.follow": "Uudet seuraajat:", "notifications.column_settings.mention": "Maininnat:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "Kyselyn tulokset:", "notifications.column_settings.push": "Push-ilmoitukset", "notifications.column_settings.reblog": "Buustit:", "notifications.column_settings.show": "Näytä sarakkeessa", "notifications.column_settings.sound": "Äänimerkki", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Boosts", - "notifications.filter.favourites": "Favourites", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", - "notifications.group": "{count} notifications", - "poll.closed": "Closed", - "poll.refresh": "Refresh", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "notifications.filter.all": "Kaikki", + "notifications.filter.boosts": "Buustit", + "notifications.filter.favourites": "Suosikit", + "notifications.filter.follows": "Seuraa", + "notifications.filter.mentions": "Maininnat", + "notifications.filter.polls": "Kyselyn tulokset", + "notifications.group": "{count} ilmoitusta", + "poll.closed": "Suljettu", + "poll.refresh": "Päivitä", + "poll.total_people": "{count, plural, one {# henkilö} other {# henkilöä}}", + "poll.total_votes": "{count, plural, one {# ääni} other {# ääntä}}", + "poll.vote": "Äänestä", + "poll.voted": "Äänestit tätä vastausta", + "poll_button.add_poll": "Lisää kysely", + "poll_button.remove_poll": "Poista kysely", "privacy.change": "Säädä tuuttauksen näkyvyyttä", "privacy.direct.long": "Julkaise vain mainituille käyttäjille", "privacy.direct.short": "Suora viesti", @@ -295,6 +319,7 @@ "privacy.public.short": "Julkinen", "privacy.unlisted.long": "Älä julkaise julkisilla aikajanoilla", "privacy.unlisted.short": "Listaamaton julkinen", + "refresh": "Päivitä", "regeneration_indicator.label": "Ladataan…", "regeneration_indicator.sublabel": "Kotinäkymääsi valmistellaan!", "relative_time.days": "{number} pv", @@ -312,22 +337,23 @@ "search.placeholder": "Hae", "search_popout.search_format": "Tarkennettu haku", "search_popout.tips.full_text": "Tekstihaku palauttaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja hastagit.", - "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.hashtag": "hashtagit", "search_popout.tips.status": "tila", "search_popout.tips.text": "Tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit", "search_popout.tips.user": "käyttäjä", "search_results.accounts": "Ihmiset", "search_results.hashtags": "Hashtagit", "search_results.statuses": "Tuuttaukset", - "search_results.total": "{count, number} {count, plural, one {result} other {results}}", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this status in the moderation interface", + "search_results.statuses_fts_disabled": "Tuuttausten haku sisällön perusteella ei ole käytössä tällä Mastodon-serverillä.", + "search_results.total": "{count, number} {count, plural, one {tulos} other {tulosta}}", + "status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}", + "status.admin_status": "Avaa tilapäivitys moderaattorinäkymässä", "status.block": "Estä @{name}", "status.cancel_reblog_private": "Peru buustaus", "status.cannot_reblog": "Tätä julkaisua ei voi buustata", - "status.copy": "Copy link to status", + "status.copy": "Kopioi linkki tilapäivitykseen", "status.delete": "Poista", - "status.detailed_status": "Detailed conversation view", + "status.detailed_status": "Yksityiskohtainen keskustelunäkymä", "status.direct": "Viesti käyttäjälle @{name}", "status.embed": "Upota", "status.favourite": "Tykkää", @@ -346,7 +372,7 @@ "status.reblog": "Buustaa", "status.reblog_private": "Buustaa alkuperäiselle yleisölle", "status.reblogged_by": "{name} buustasi", - "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", + "status.reblogs.empty": "Kukaan ei ole vielä buustannut tätä tuuttausta. Kun joku tekee niin, näkyy kyseinen henkilö tässä.", "status.redraft": "Poista & palauta muokattavaksi", "status.reply": "Vastaa", "status.replyAll": "Vastaa ketjuun", @@ -357,30 +383,39 @@ "status.show_less_all": "Näytä vähemmän kaikista", "status.show_more": "Näytä lisää", "status.show_more_all": "Näytä lisää kaikista", - "status.show_thread": "Show thread", + "status.show_thread": "Näytä ketju", + "status.uncached_media_warning": "Ei saatavilla", "status.unmute_conversation": "Poista keskustelun mykistys", "status.unpin": "Irrota profiilista", "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", + "suggestions.header": "Saatat olla kiinnostunut myös…", "tabs_bar.federated_timeline": "Yleinen", "tabs_bar.home": "Koti", "tabs_bar.local_timeline": "Paikallinen", "tabs_bar.notifications": "Ilmoitukset", "tabs_bar.search": "Hae", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä", + "time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä", + "time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä", + "time_remaining.moments": "Hetki jäljellä", + "time_remaining.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} jäljellä", + "trends.count_by_accounts": "{count} {rawCount, plural, one {henkilö} other {henkilöä}} keskustelee", + "trends.trending_now": "Suosittua nyt", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän", "upload_button.label": "Lisää mediaa", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", + "upload_error.limit": "Tiedostolatauksien raja ylitetty.", + "upload_error.poll": "Tiedon lataaminen ei ole sallittua kyselyissä.", "upload_form.description": "Anna kuvaus näkörajoitteisia varten", - "upload_form.focus": "Rajaa", + "upload_form.edit": "Muokkaa", "upload_form.undo": "Peru", + "upload_modal.analyzing_picture": "Analysoidaan kuvaa…", + "upload_modal.apply": "Käytä", + "upload_modal.description_placeholder": "Eräänä jäätävänä ja pimeänä yönä gorilla ratkaisi sudokun kahdessa minuutissa", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Ladataan...", "video.close": "Sulje video", "video.exit_fullscreen": "Poistu koko näytön tilasta", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 507e37df31ad954b9f830eb9b0be6e3f6ee25d82..9568b54f0f9b73a721dcacb8f0b4dd3db0353b67 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -4,10 +4,11 @@ "account.block": "Bloquer @{name}", "account.block_domain": "Tout masquer venant de {domain}", "account.blocked": "Bloqué", + "account.cancel_follow_request": "Annuler la demande de suivi", "account.direct": "Envoyer un message direct à @{name}", "account.domain_blocked": "Domaine caché", "account.edit_profile": "Modifier le profil", - "account.endorse": "Figure sur le profil", + "account.endorse": "Recommander sur le profil", "account.follow": "Suivre", "account.followers": "Abonné⋅eâ‹…s", "account.followers.empty": "Personne ne suit cet utilisateur·rice pour l’instant.", @@ -15,6 +16,7 @@ "account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour l’instant.", "account.follows_you": "Vous suit", "account.hide_reblogs": "Masquer les partages de @{name}", + "account.last_status": "Dernière activité", "account.link_verified_on": "La propriété de ce lien a été vérifiée le {date}", "account.locked_info": "Ce compte est verrouillé. Son propriétaire approuve manuellement qui peut le ou la suivre.", "account.media": "Média", @@ -23,6 +25,7 @@ "account.mute": "Masquer @{name}", "account.mute_notifications": "Ignorer les notifications de @{name}", "account.muted": "Silencé", + "account.never_active": "Jamais", "account.posts": "Pouets", "account.posts_with_replies": "Pouets et réponses", "account.report": "Signaler @{name}", @@ -31,13 +34,16 @@ "account.show_reblogs": "Afficher les partages de @{name}", "account.unblock": "Débloquer @{name}", "account.unblock_domain": "Ne plus masquer {domain}", - "account.unendorse": "Ne figure pas sur le profil", + "account.unendorse": "Ne plus recommander sur le profil", "account.unfollow": "Ne plus suivre", "account.unmute": "Ne plus masquer @{name}", "account.unmute_notifications": "Réactiver les notifications de @{name}", + "alert.rate_limited.message": "Veuillez réessayer après {retry_time, time, medium}.", + "alert.rate_limited.title": "Débit limité", "alert.unexpected.message": "Une erreur inattendue s’est produite.", "alert.unexpected.title": "Oups !", - "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois", + "autosuggest_hashtag.per_week": "{count} par semaine", + "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci, la prochaine fois", "bundle_column_error.body": "Une erreur s’est produite lors du chargement de ce composant.", "bundle_column_error.retry": "Réessayer", "bundle_column_error.title": "Erreur réseau", @@ -46,7 +52,8 @@ "bundle_modal_error.retry": "Réessayer", "column.blocks": "Comptes bloqués", "column.community": "Fil public local", - "column.direct": "Messages directs", + "column.direct": "Messages privés", + "column.directory": "Parcourir les profils", "column.domain_blocks": "Domaines cachés", "column.favourites": "Favoris", "column.follow_requests": "Demandes de suivi", @@ -62,11 +69,11 @@ "column_header.moveRight_settings": "Déplacer la colonne vers la droite", "column_header.pin": "Épingler", "column_header.show_settings": "Afficher les paramètres", - "column_header.unpin": "Retirer", + "column_header.unpin": "Désépingler", "column_subheading.settings": "Paramètres", "community.column_settings.media_only": "Média uniquement", "compose_form.direct_message_warning": "Ce pouet sera uniquement envoyé aux personnes mentionnées. Cependant, l’administration de votre instance et des instances réceptrices pourront inspecter ce message.", - "compose_form.direct_message_warning_learn_more": "En savoir plus", + "compose_form.direct_message_warning_learn_more": "Plus d’informations", "compose_form.hashtag_warning": "Ce pouet ne sera pas listé dans les recherches par hashtag car sa visibilité est réglée sur \"non listé\". Seuls les pouets avec une visibilité \"publique\" peuvent être recherchés par hashtag.", "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.", "compose_form.lock_disclaimer.lock": "verrouillé", @@ -79,7 +86,7 @@ "compose_form.publish_loud": "{publish} !", "compose_form.sensitive.hide": "Marquer le média comme sensible", "compose_form.sensitive.marked": "Média marqué comme sensible", - "compose_form.sensitive.unmarked": "Média non marqué comme sensible", + "compose_form.sensitive.unmarked": "Le média n’est pas marqué comme sensible", "compose_form.spoiler.marked": "Le texte est caché derrière un avertissement", "compose_form.spoiler.unmarked": "Le texte n’est pas caché", "compose_form.spoiler_placeholder": "Écrivez ici votre avertissement", @@ -93,25 +100,36 @@ "confirmations.delete_list.message": "Êtes-vous sûr·e de vouloir supprimer définitivement cette liste ?", "confirmations.domain_block.confirm": "Masquer le domaine entier", "confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûrâ‹…e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables. Vous ne verrez plus de contenu provenant de ce domaine, ni dans fils publics, ni dans vos notifications. Vos abonné·e·s utilisant ce domaine seront retiré·e·s.", + "confirmations.logout.confirm": "Déconnexion", + "confirmations.logout.message": "Êtes-vous sûr de vouloir vous déconnecter ?", "confirmations.mute.confirm": "Masquer", - "confirmations.mute.message": "Confirmez-vous le masquage de {name} ?", + "confirmations.mute.explanation": "Cela masquera leurs messages et les messages les mentionnant, mais cela leur permettra quand même de voir vos messages et vous suivre.", + "confirmations.mute.message": "Êtes-vous sûr·e de vouloir masquer {name} ?", "confirmations.redraft.confirm": "Effacer et ré-écrire", "confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer ce statut pour le ré-écrire ? Ses partages ainsi que ses mises en favori seront perdu·e·s et ses réponses seront orphelines.", "confirmations.reply.confirm": "Répondre", - "confirmations.reply.message": "Répondre maintenant écrasera le message que vous êtes en train de composer. Voulez-vous vraiment continuer ?", + "confirmations.reply.message": "Répondre maintenant écrasera le message que vous composez actuellement. Êtes-vous sûr de vouloir continuer ?", "confirmations.unfollow.confirm": "Ne plus suivre", "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name} ?", + "conversation.delete": "Supprimer la conversation", + "conversation.mark_as_read": "Marquer comme lu", + "conversation.open": "Afficher la conversation", + "conversation.with": "Avec {names}", + "directory.federated": "Du fédiverse connu", + "directory.local": "De {domain} seulement", + "directory.new_arrivals": "Nouveaux arrivants", + "directory.recently_active": "Récemment actif", "embed.instructions": "Intégrez ce statut à votre site en copiant le code ci-dessous.", "embed.preview": "Il apparaîtra comme cela :", "emoji_button.activity": "Activités", - "emoji_button.custom": "Personnalisés", + "emoji_button.custom": "Personnalisé", "emoji_button.flags": "Drapeaux", "emoji_button.food": "Nourriture & Boisson", "emoji_button.label": "Insérer un émoji", "emoji_button.nature": "Nature", "emoji_button.not_found": "Pas d’émoji !! (╯°□°)╯︵ â”»â”â”»", "emoji_button.objects": "Objets", - "emoji_button.people": "Personnages", + "emoji_button.people": "Personnes", "emoji_button.recent": "Fréquemment utilisés", "emoji_button.search": "Recherche…", "emoji_button.search_results": "Résultats de la recherche", @@ -131,8 +149,13 @@ "empty_column.home.public_timeline": "le fil public", "empty_column.list": "Il n’y a rien dans cette liste pour l’instant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.", "empty_column.lists": "Vous n’avez pas encore de liste. Lorsque vous en créerez une, elle apparaîtra ici.", - "empty_column.mutes": "Vous n’avez pas encore mis d'utilisateur·rice·s en silence.", + "empty_column.mutes": "Vous n’avez pas encore mis d’utilisateur·rice·s en silence.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", + "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour le remplir", + "error.unexpected_crash.explanation": "En raison d’un bug dans notre code ou d’un problème de compatibilité avec le navigateur, cette page n’a pas pu être affichée correctement.", + "error.unexpected_crash.next_steps": "Essayez de rafraîchir la page. Si cela n’aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.", + "errors.unexpected_crash.copy_stacktrace": "Copier la trace-pile dans le presse-papier", + "errors.unexpected_crash.report_issue": "Signaler un bogue", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", @@ -153,12 +176,12 @@ "hashtag.column_header.tag_mode.any": "ou {additional}", "hashtag.column_header.tag_mode.none": "sans {additional}", "hashtag.column_settings.select.no_options_message": "Aucune suggestion trouvée", - "hashtag.column_settings.select.placeholder": "Ajouter des hashtags…", + "hashtag.column_settings.select.placeholder": "Ajouter des mots-clés…", "hashtag.column_settings.tag_mode.all": "Tous ces éléments", "hashtag.column_settings.tag_mode.any": "Au moins un de ces éléments", "hashtag.column_settings.tag_mode.none": "Aucun de ces éléments", - "hashtag.column_settings.tag_toggle": "Inclure des tags additionnels dans cette colonne", - "home.column_settings.basic": "Basique", + "hashtag.column_settings.tag_toggle": "Inclure des mots-clés additionnels dans cette colonne", + "home.column_settings.basic": "Base", "home.column_settings.show_reblogs": "Afficher les partages", "home.column_settings.show_replies": "Afficher les réponses", "intervals.full.days": "{number, plural, one {# jour} other {# jours}}", @@ -173,7 +196,7 @@ "introduction.federation.local.text": "Les messages publics de personnes se trouvant sur le même serveur que vous apparaîtront sur le fil public local.", "introduction.interactions.action": "Finir le tutoriel !", "introduction.interactions.favourite.headline": "Favoris", - "introduction.interactions.favourite.text": "Vous pouvez garder un pouet pour plus tard, et faire savoir à son auteur·ice que vous l'avez aimé, en le favorisant.", + "introduction.interactions.favourite.text": "Vous pouvez garder un pouet pour plus tard et faire savoir à son auteur·ice que vous l’avez aimé, en le favorisant.", "introduction.interactions.reblog.headline": "Repartager", "introduction.interactions.reblog.text": "Vous pouvez partager les pouets d'autres personnes avec vos abonné·e·s en les repartageant.", "introduction.interactions.reply.headline": "Répondre", @@ -185,7 +208,7 @@ "keyboard_shortcuts.blocked": "pour ouvrir une liste d’utilisateur·rice·s bloqué·e·s", "keyboard_shortcuts.boost": "pour partager", "keyboard_shortcuts.column": "pour focaliser un statut dans l’une des colonnes", - "keyboard_shortcuts.compose": "pour centrer la zone de rédaction", + "keyboard_shortcuts.compose": "pour focaliser la zone de rédaction", "keyboard_shortcuts.description": "Description", "keyboard_shortcuts.direct": "pour ouvrir une colonne des messages directs", "keyboard_shortcuts.down": "pour descendre dans la liste", @@ -226,6 +249,7 @@ "lists.new.title_placeholder": "Titre de la nouvelle liste", "lists.search": "Rechercher parmi les gens que vous suivez", "lists.subheading": "Vos listes", + "load_pending": "{count, plural, one {# nouvel élément} other {# nouveaux éléments}}", "loading_indicator.label": "Chargement…", "media_gallery.toggle_visible": "Modifier la visibilité", "missing_indicator.label": "Non trouvé", @@ -243,7 +267,7 @@ "navigation_bar.filters": "Mots silenciés", "navigation_bar.follow_requests": "Demandes de suivi", "navigation_bar.follows_and_followers": "Abonnements et abonné⋅e·s", - "navigation_bar.info": "Plus d’informations", + "navigation_bar.info": "À propos de ce serveur", "navigation_bar.keyboard_shortcuts": "Raccourcis clavier", "navigation_bar.lists": "Listes", "navigation_bar.logout": "Déconnexion", @@ -251,7 +275,6 @@ "navigation_bar.personal": "Personnel", "navigation_bar.pins": "Pouets épinglés", "navigation_bar.preferences": "Préférences", - "navigation_bar.profile_directory": "Annuaire des profils", "navigation_bar.public_timeline": "Fil public global", "navigation_bar.security": "Sécurité", "notification.favourite": "{name} a ajouté à ses favoris :", @@ -282,8 +305,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Fermé", "poll.refresh": "Actualiser", + "poll.total_people": "{count, plural, one {# personne} other {# personnes}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Voter", + "poll.voted": "Vous avez voté pour cette réponse", "poll_button.add_poll": "Ajouter un sondage", "poll_button.remove_poll": "Supprimer le sondage", "privacy.change": "Ajuster la confidentialité du message", @@ -295,6 +320,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Ne pas afficher dans les fils publics", "privacy.unlisted.short": "Non listé", + "refresh": "Actualiser", "regeneration_indicator.label": "Chargement…", "regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation !", "relative_time.days": "{number} j", @@ -319,9 +345,10 @@ "search_results.accounts": "Comptes", "search_results.hashtags": "Hashtags", "search_results.statuses": "Pouets", + "search_results.statuses_fts_disabled": "La recherche de pouets par leur contenu n'est pas activée sur ce serveur Mastodon.", "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}", - "status.admin_account": "Ouvrir l'interface de modération pour @{name}", - "status.admin_status": "Ouvrir ce statut dans l'interface de modération", + "status.admin_account": "Ouvrir l’interface de modération pour @{name}", + "status.admin_status": "Ouvrir ce statut dans l’interface de modération", "status.block": "Bloquer @{name}", "status.cancel_reblog_private": "Dé-booster", "status.cannot_reblog": "Cette publication ne peut être boostée", @@ -358,6 +385,7 @@ "status.show_more": "Déplier", "status.show_more_all": "Tout déplier", "status.show_thread": "Lire le fil", + "status.uncached_media_warning": "Indisponible", "status.unmute_conversation": "Ne plus masquer la conversation", "status.unpin": "Retirer du profil", "suggestions.dismiss": "Rejeter la suggestion", @@ -373,14 +401,22 @@ "time_remaining.moments": "Encore quelques instants", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} restantes", "trends.count_by_accounts": "{count} {rawCount, plural, one {personne} other {personnes}} discutent", + "trends.trending_now": "Tendance en ce moment", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "upload_area.title": "Glissez et déposez pour envoyer", - "upload_button.label": "Joindre un média (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Joindre un média ({formats})", "upload_error.limit": "Taille maximale d'envoi de fichier dépassée.", - "upload_error.poll": "L'envoi de fichiers n'est pas autorisé avec les sondages.", + "upload_error.poll": "L’envoi de fichiers n’est pas autorisé avec les sondages.", "upload_form.description": "Décrire pour les malvoyant·e·s", - "upload_form.focus": "Modifier l’aperçu", + "upload_form.edit": "Modifier", "upload_form.undo": "Supprimer", + "upload_modal.analyzing_picture": "Analyse de l’image en cours…", + "upload_modal.apply": "Appliquer", + "upload_modal.description_placeholder": "Buvez de ce whisky que le patron juge fameux", + "upload_modal.detect_text": "Détecter le texte de l’image", + "upload_modal.edit_media": "Modifier le média", + "upload_modal.hint": "Cliquez ou faites glisser le cercle sur l’aperçu pour choisir le point focal qui sera toujours visible sur toutes les miniatures.", + "upload_modal.preview_label": "Aperçu ({ratio})", "upload_progress.label": "Envoi en cours…", "video.close": "Fermer la vidéo", "video.exit_fullscreen": "Quitter le plein écran", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json new file mode 100644 index 0000000000000000000000000000000000000000..2dd0dbbad97700e96e9027190825dd74d3b3e719 --- /dev/null +++ b/app/javascript/mastodon/locales/ga.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Add or Remove from lists", + "account.badges.bot": "Bot", + "account.block": "Block @{name}", + "account.block_domain": "Hide everything from {domain}", + "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", + "account.direct": "Direct message @{name}", + "account.domain_blocked": "Domain hidden", + "account.edit_profile": "Edit profile", + "account.endorse": "Feature on profile", + "account.follow": "Follow", + "account.followers": "Followers", + "account.followers.empty": "No one follows this user yet.", + "account.follows": "Follows", + "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows_you": "Follows you", + "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", + "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.media": "Media", + "account.mention": "Mention @{name}", + "account.moved_to": "{name} has moved to:", + "account.mute": "Mute @{name}", + "account.mute_notifications": "Mute notifications from @{name}", + "account.muted": "Muted", + "account.never_active": "Never", + "account.posts": "Toots", + "account.posts_with_replies": "Toots and replies", + "account.report": "Report @{name}", + "account.requested": "Awaiting approval", + "account.share": "Share @{name}'s profile", + "account.show_reblogs": "Show boosts from @{name}", + "account.unblock": "Unblock @{name}", + "account.unblock_domain": "Unhide {domain}", + "account.unendorse": "Don't feature on profile", + "account.unfollow": "Unfollow", + "account.unmute": "Unmute @{name}", + "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", + "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", + "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", + "column.blocks": "Blocked users", + "column.community": "Local timeline", + "column.direct": "Direct messages", + "column.directory": "Browse profiles", + "column.domain_blocks": "Hidden domains", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Home", + "column.lists": "Lists", + "column.mutes": "Muted users", + "column.notifications": "Notifications", + "column.pins": "Pinned toot", + "column.public": "Federated timeline", + "column_back_button.label": "Back", + "column_header.hide_settings": "Hide settings", + "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveRight_settings": "Move column to the right", + "column_header.pin": "Pin", + "column_header.show_settings": "Show settings", + "column_header.unpin": "Unpin", + "column_subheading.settings": "Settings", + "community.column_settings.media_only": "Media only", + "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.", + "compose_form.direct_message_warning_learn_more": "Learn more", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", + "compose_form.lock_disclaimer.lock": "locked", + "compose_form.placeholder": "What is on your mind?", + "compose_form.poll.add_option": "Add a choice", + "compose_form.poll.duration": "Poll duration", + "compose_form.poll.option_placeholder": "Choice {number}", + "compose_form.poll.remove_option": "Remove this choice", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.marked": "Media is marked as sensitive", + "compose_form.sensitive.unmarked": "Media is not marked as sensitive", + "compose_form.spoiler.marked": "Text is hidden behind warning", + "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler_placeholder": "Write your warning here", + "confirmation_modal.cancel": "Cancel", + "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.confirm": "Block", + "confirmations.block.message": "Are you sure you want to block {name}?", + "confirmations.delete.confirm": "Delete", + "confirmations.delete.message": "Are you sure you want to delete this status?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", + "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.redraft.confirm": "Delete & redraft", + "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", + "confirmations.reply.confirm": "Reply", + "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Here is what it will look like:", + "emoji_button.activity": "Activity", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flags", + "emoji_button.food": "Food & Drink", + "emoji_button.label": "Insert emoji", + "emoji_button.nature": "Nature", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objects", + "emoji_button.people": "People", + "emoji_button.recent": "Frequently used", + "emoji_button.search": "Search...", + "emoji_button.search_results": "Search results", + "emoji_button.symbols": "Symbols", + "emoji_button.travel": "Travel & Places", + "empty_column.account_timeline": "No toots here!", + "empty_column.account_unavailable": "Profile unavailable", + "empty_column.blocks": "You haven't blocked any users yet.", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.domain_blocks": "There are no hidden domains yet.", + "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", + "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", + "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", + "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Reject", + "getting_started.developers": "Developers", + "getting_started.directory": "Profile directory", + "getting_started.documentation": "Documentation", + "getting_started.heading": "Getting started", + "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.security": "Security", + "getting_started.terms": "Terms of service", + "hashtag.column_header.tag_mode.all": "and {additional}", + "hashtag.column_header.tag_mode.any": "or {additional}", + "hashtag.column_header.tag_mode.none": "without {additional}", + "hashtag.column_settings.select.no_options_message": "No suggestions found", + "hashtag.column_settings.select.placeholder": "Enter hashtags…", + "hashtag.column_settings.tag_mode.all": "All of these", + "hashtag.column_settings.tag_mode.any": "Any of these", + "hashtag.column_settings.tag_mode.none": "None of these", + "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "home.column_settings.basic": "Basic", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "intervals.full.days": "{number, plural, one {# day} other {# days}}", + "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", + "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "introduction.federation.action": "Next", + "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.home.headline": "Home", + "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.local.headline": "Local", + "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.interactions.action": "Finish toot-orial!", + "introduction.interactions.favourite.headline": "Favourite", + "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.reblog.headline": "Boost", + "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reply.headline": "Reply", + "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.welcome.action": "Let's go!", + "introduction.welcome.headline": "First steps", + "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.favourites": "to open favourites list", + "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.local": "to open local timeline", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.muted": "to open muted users list", + "keyboard_shortcuts.my_profile": "to open your profile", + "keyboard_shortcuts.notifications": "to open notifications column", + "keyboard_shortcuts.pinned": "to open pinned toots list", + "keyboard_shortcuts.profile": "to open author's profile", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.start": "to open \"get started\" column", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Close", + "lightbox.next": "Next", + "lightbox.previous": "Previous", + "lightbox.view_context": "View context", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.edit.submit": "Change title", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "missing_indicator.sublabel": "This resource could not be found", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.apps": "Mobile apps", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Direct messages", + "navigation_bar.discover": "Discover", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.filters": "Muted words", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.info": "About this server", + "navigation_bar.keyboard_shortcuts": "Hotkeys", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Logout", + "navigation_bar.mutes": "Muted users", + "navigation_bar.personal": "Personal", + "navigation_bar.pins": "Pinned toots", + "navigation_bar.preferences": "Preferences", + "navigation_bar.public_timeline": "Federated timeline", + "navigation_bar.security": "Security", + "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", + "notification.mention": "{name} mentioned you", + "notification.poll": "A poll you have voted in has ended", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Clear notifications", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.filter_bar.advanced": "Display all categories", + "notifications.column_settings.filter_bar.category": "Quick filter bar", + "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.follow": "New followers:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "notifications.filter.all": "All", + "notifications.filter.boosts": "Boosts", + "notifications.filter.favourites": "Favourites", + "notifications.filter.follows": "Follows", + "notifications.filter.mentions": "Mentions", + "notifications.filter.polls": "Poll results", + "notifications.group": "{count} notifications", + "poll.closed": "Closed", + "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.vote": "Vote", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Add a poll", + "poll_button.remove_poll": "Remove poll", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "status.admin_account": "Open moderation interface for @{name}", + "status.admin_status": "Open this status in the moderation interface", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.copy": "Copy link to status", + "status.delete": "Delete", + "status.detailed_status": "Detailed conversation view", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.filtered": "Filtered", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.read_more": "Read more", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", + "status.redraft": "Delete & re-draft", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "suggestions.dismiss": "Dismiss suggestion", + "suggestions.header": "You might be interested in…", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "time_remaining.days": "{number, plural, one {# day} other {# days}} left", + "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", + "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.moments": "Moments remaining", + "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media ({formats})", + "upload_error.limit": "File upload limit exceeded.", + "upload_error.poll": "File upload not allowed with polls.", + "upload_form.description": "Describe for the visually impaired", + "upload_form.edit": "Edit", + "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading...", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 8fbdabd0d116e1b6cf2758301fd30d340e7995e2..bee0384c33f438f428511fbd01c1b8934cd3adeb 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -4,6 +4,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Ocultar calquer contido de {domain}", "account.blocked": "Bloqueada", + "account.cancel_follow_request": "Cancelar petición de seguemento", "account.direct": "Mensaxe directa @{name}", "account.domain_blocked": "Dominio agochado", "account.edit_profile": "Editar perfil", @@ -15,6 +16,7 @@ "account.follows.empty": "Esta usuaria aÃnda non segue a ninguén.", "account.follows_you": "Séguete", "account.hide_reblogs": "Ocultar repeticións de @{name}", + "account.last_status": "Último activo", "account.link_verified_on": "A propiedade de esta ligazón foi comprobada en {date}", "account.locked_info": "O estado da intimidade de esta conta estableceuse en pechado. A persoa dona da conta revisa quen pode seguila.", "account.media": "Medios", @@ -23,6 +25,7 @@ "account.mute": "Acalar @{name}", "account.mute_notifications": "Acalar as notificacións de @{name}", "account.muted": "Acalada", + "account.never_active": "Nunca", "account.posts": "Toots", "account.posts_with_replies": "Toots e respostas", "account.report": "Informar sobre @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Non seguir", "account.unmute": "Non acalar @{name}", "account.unmute_notifications": "Desbloquear as notificacións de @{name}", + "alert.rate_limited.message": "Por favor inténteo tras {retry_time, time, medium}.", + "alert.rate_limited.title": "Taxa limitada", "alert.unexpected.message": "Aconteceu un fallo non agardado.", "alert.unexpected.title": "Vaia!", + "autosuggest_hashtag.per_week": "{count} por semana", "boost_modal.combo": "Pulse {combo} para saltar esto a próxima vez", "bundle_column_error.body": "Houbo un fallo mentras se cargaba este compoñente.", "bundle_column_error.retry": "Inténteo de novo", @@ -47,6 +53,7 @@ "column.blocks": "Usuarias bloqueadas", "column.community": "Liña temporal local", "column.direct": "Mensaxes directas", + "column.directory": "Ver perfiles", "column.domain_blocks": "Dominios agochados", "column.favourites": "Favoritas", "column.follow_requests": "Peticións de seguimento", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Estás seguro de que queres eliminar permanentemente esta lista?", "confirmations.domain_block.confirm": "Agochar un dominio completo", "confirmations.domain_block.message": "Realmente está segura de que quere bloquear por completo o dominio {domain}? Normalmente é suficiente, e preferible, bloquear de xeito selectivo varios elementos. Non verá contidos de ese dominio en ningunha liña temporal ou nas notificacións. As súas seguidoras en ese dominio serán eliminadas.", + "confirmations.logout.confirm": "Desconectar", + "confirmations.logout.message": "Seguro que desexa desconectar?", "confirmations.mute.confirm": "Acalar", + "confirmations.mute.explanation": "Esto ocultará as publicacións delas e as que as mencionen, pero poderán seguir lendo as túas publicacións e seguirte.", "confirmations.mute.message": "Está segura de que quere acalar a {name}?", "confirmations.redraft.confirm": "Eliminar e reescribir", "confirmations.redraft.message": "Está segura de querer eliminar este estado e voltalo a escribir? Perderá réplicas e favoritas, e as respostas ao orixinal quedarán orfas.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Respostando agora sobreescribirá a mensaxe que está a compoñer. Segura de querer proceder?", "confirmations.unfollow.confirm": "Deixar de seguir", "confirmations.unfollow.message": "Quere deixar de seguir a {name}?", + "conversation.delete": "Eliminar conversa", + "conversation.mark_as_read": "Marcar como lido", + "conversation.open": "Ver conversa", + "conversation.with": "Con {names}", + "directory.federated": "Desde o fediverso coñecido", + "directory.local": "Só desde {domain}", + "directory.new_arrivals": "Novas achegas", + "directory.recently_active": "Activo recentemente", "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.", "embed.preview": "Asà será mostrado:", "emoji_button.activity": "Actividade", @@ -134,6 +152,10 @@ "empty_column.mutes": "Non acalou ningunha usuaria polo de agora.", "empty_column.notifications": "AÃnda non ten notificacións. Interactúe con outras para iniciar unha conversa.", "empty_column.public": "Nada por aquÃ! Escriba algo de xeito público, ou siga manualmente usuarias de outros servidores para ir enchéndoa", + "error.unexpected_crash.explanation": "Non se mostra correctamente a páxina debido a un fallo no código ou problema de compatibilidade do navegador.", + "error.unexpected_crash.next_steps": "Intenta actualizar a páxina. Se esto non axuda podes tamén utilizar Mastodon en outro navegador ou app nativa.", + "errors.unexpected_crash.copy_stacktrace": "Copiar trazas ao portaretallos", + "errors.unexpected_crash.report_issue": "Informar de problema", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Novo tÃtulo da lista", "lists.search": "Procurar entre a xente que segues", "lists.subheading": "As túas listas", + "load_pending": "{count, plural, one {# novo elemento} other {# novos elementos}}", "loading_indicator.label": "Cargando...", "media_gallery.toggle_visible": "Ocultar", "missing_indicator.label": "Non atopado", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Persoal", "navigation_bar.pins": "Mensaxes fixadas", "navigation_bar.preferences": "Preferencias", - "navigation_bar.profile_directory": "Directorio de perfil", "navigation_bar.public_timeline": "Liña temporal federada", "navigation_bar.security": "Seguridade", "notification.favourite": "{name} marcou como favorito o seu estado", @@ -282,8 +304,10 @@ "notifications.group": "{count} notificacións", "poll.closed": "Pechado", "poll.refresh": "Actualizar", + "poll.total_people": "{count, plural,one {# persoa}other {# persoas}}", "poll.total_votes": "{count, plural, one {# voto} outros {# votos}}", "poll.vote": "Votar", + "poll.voted": "Votou por esta opción", "poll_button.add_poll": "Engadir sondaxe", "poll_button.remove_poll": "Eliminar sondaxe", "privacy.change": "Axustar a intimidade do estado", @@ -295,6 +319,7 @@ "privacy.public.short": "Pública", "privacy.unlisted.long": "Non publicar en liñas temporais públicas", "privacy.unlisted.short": "Non listada", + "refresh": "Actualizar", "regeneration_indicator.label": "Cargando…", "regeneration_indicator.sublabel": "Estase a preparar a súa liña temporal de inicio!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Xente", "search_results.hashtags": "Etiquetas", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Non está activada neste servidor Mastodon a busca de toots polo seu contido.", "search_results.total": "{count, number} {count,plural,one {result} outros {results}}", "status.admin_account": "Abrir interface de moderación para @{name}", "status.admin_status": "Abrir este estado na interface de moderación", @@ -358,6 +384,7 @@ "status.show_more": "Mostrar máis", "status.show_more_all": "Mostrar máis para todas", "status.show_thread": "Mostrar fÃo", + "status.uncached_media_warning": "Non dispoñible", "status.unmute_conversation": "Non acalar a conversa", "status.unpin": "Despegar do perfil", "suggestions.dismiss": "Rexeitar suxestión", @@ -373,14 +400,22 @@ "time_remaining.moments": "Está rematando", "time_remaining.seconds": "{number, plural, one {# segundo} other {# segundos}} restantes", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} outras {people}} conversando", + "trends.trending_now": "Tendencias actuais", "ui.beforeunload": "O borrador perderase se sae de Mastodon.", "upload_area.title": "Arrastre e solte para subir", - "upload_button.label": "Engadir medios (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Engadir medios ({formats})", "upload_error.limit": "Excedeu o lÃmite de subida de ficheiros.", "upload_error.poll": "Non se poden subir ficheiros nas sondaxes.", "upload_form.description": "Describa para deficientes visuais", - "upload_form.focus": "Cambiar vista previa", + "upload_form.edit": "Editar", "upload_form.undo": "Eliminar", + "upload_modal.analyzing_picture": "Analizando imaxe…", + "upload_modal.apply": "Aplicar", + "upload_modal.description_placeholder": "Un raposo moi feitiño salta sobre o can preguiceiro", + "upload_modal.detect_text": "Detectar texto na imaxe", + "upload_modal.edit_media": "Editar medios", + "upload_modal.hint": "Prema ou arrastre o cÃrculo na vista previa para escolle o punto focal que se verá na vista de todas as miniaturas.", + "upload_modal.preview_label": "Vista previa ({ratio})", "upload_progress.label": "Subindo...", "video.close": "Pechar video", "video.exit_fullscreen": "SaÃr da pantalla completa", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 5b1e17e991f9ab4bb2fee23deebced8a6746f03d..81912477d9a5cd05002c51c86f3fb6e75f8af570 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -1,21 +1,23 @@ { - "account.add_or_remove_from_list": "Add or Remove from lists", - "account.badges.bot": "Bot", + "account.add_or_remove_from_list": "הוסף ×ו הסר מהרשימות", + "account.badges.bot": "בוט", "account.block": "חסימת @{name}", "account.block_domain": "להסתיר הכל מהקהילה {domain}", - "account.blocked": "Blocked", + "account.blocked": "חסו×", + "account.cancel_follow_request": "בטל בקשת מעקב", "account.direct": "Direct Message @{name}", - "account.domain_blocked": "Domain hidden", + "account.domain_blocked": "הדומיין חסוי", "account.edit_profile": "עריכת פרופיל", - "account.endorse": "Feature on profile", + "account.endorse": "הצג בפרופיל", "account.follow": "מעקב", "account.followers": "עוקבי×", - "account.followers.empty": "No one follows this user yet.", + "account.followers.empty": "××£ ×חד ×œ× ×¢×•×§×‘ ×חר המשתמש ×”×–×” עדיין.", "account.follows": "× ×¢×§×‘×™×", - "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows.empty": "משתמש ×–×” ×œ× ×¢×•×§×‘ ×חר ××£ ×חד עדיין.", "account.follows_you": "במעקב ×חריך", "account.hide_reblogs": "להסתיר ×”×™×“×”×•×“×™× ×ž×ת @{name}", - "account.link_verified_on": "Ownership of this link was checked on {date}", + "account.last_status": "פעילות ××—×¨×•× ×”", + "account.link_verified_on": "בעלות על הקישור ×”×–×” × ×‘×“×§×” ל××—×¨×•× ×” ב{date}", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.media": "מדיה", "account.mention": "×זכור של @{name}", @@ -23,6 +25,7 @@ "account.mute": "להשתיק ×ת @{name}", "account.mute_notifications": "להסתיר התר×ות מ×ת @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "הודעות", "account.posts_with_replies": "Toots with replies", "account.report": "לדווח על @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "הפסקת מעקב", "account.unmute": "הפסקת השתקת @{name}", "account.unmute_notifications": "להפסיק הסתרת הודעות ×ž×¢× @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "×ירעה שגי××” בלתי צפויה.", "alert.unexpected.title": "×ופס!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "× ×™×ª×Ÿ להקיש {combo} כדי לדלג ×‘×¤×¢× ×”×‘××”", "bundle_column_error.body": "משהו השתבש בעת הצגת הרכיב ×”×–×”.", "bundle_column_error.retry": "×œ× ×¡×•×ª שוב", @@ -47,6 +53,7 @@ "column.blocks": "חסימות", "column.community": "ציר זמן מקומי", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "חיבובי×", "column.follow_requests": "בקשות מעקב", @@ -64,7 +71,7 @@ "column_header.show_settings": "הצגת העדפות", "column_header.unpin": "שחרור קיבוע", "column_subheading.settings": "×פשרויות", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "confirmations.domain_block.confirm": "הסתר קהילה שלמה", "confirmations.domain_block.message": "ב×מת ב×מת ×œ×—×¡×•× ×ת כל קהילת {domain}? ברב ×”×ž×§×¨×™× ×”×©×ª×§×•×ª × ×‘×—×¨×•×ª של מספר ×ž×©×ª×ž×©×™× ×ž×¡×•×™×™×ž×™× ×¦×¨×™×›×” להספיק.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "להשתיק", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "להשתיק ×ת {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "להפסיק מעקב", "confirmations.unfollow.message": "להפסיק מעקב ×חרי {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "× ×™×ª×Ÿ להטמיע ×ת ההודעה ב×תרך ×¢\"×™ העתקת הקוד שלהלן.", "embed.preview": "×“×•×’×ž× ×›×™×¦×“ ×–×” יר××”:", "emoji_button.activity": "פעילות", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "×ין התר×ות עדיין. ×™×ללה, ×”×’×™×¢ הזמן להתחיל להתערבב.", "empty_column.public": "×ין פה כלו×! כדי ×œ×ž×œ× ×ת הטור ×”×–×” ×פשר לכתוב משהו, ×ו להתחיל לעקוב ×חרי ×× ×©×™× ×ž×§×”×™×œ×•×ª ×חרות", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "טוען...", "media_gallery.toggle_visible": "× ×¨××”\\בלתי × ×¨××”", "missing_indicator.label": "×œ× × ×ž×¦×", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "×—×™×¦×¨×•×¦×™× ×ž×§×•×‘×¢×™×", "navigation_bar.preferences": "העדפות", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "ציר זמן בין-קהילתי", "navigation_bar.security": "Security", "notification.favourite": "חצרוצך חובב על ידי {name}", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "×©×™× ×•×™ פרטיות ההודעה", @@ -295,6 +319,7 @@ "privacy.public.short": "פומבי", "privacy.unlisted.long": "×œ× ×™×•×¤×™×¢ ×‘×¤×™×“×™× ×”×¦×™×‘×•×¨×™×™× ×”×ž×©×•×ª×¤×™×", "privacy.unlisted.short": "×œ× ×œ×¤×™×“ הכללי", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {תוצ××”} other {תוצ×ות}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "הר××” יותר", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "הסרת השתקת שיחה", "status.unpin": "לשחרר מקיבוע ב×ודות", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "×”×˜×™×•×˜× ×ª×בד ×× ×ª×¢×–×‘×• ×ת מסטודון.", "upload_area.title": "× ×™×ª×Ÿ להעלות על ידי Drag & drop", "upload_button.label": "הוספת מדיה", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "תי×ור לכבדי ר××™×”", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "ביטול", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "עולה...", "video.close": "סגירת ויד×ו", "video.exit_fullscreen": "יצי××” ממסך מל×", diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index 7a71d84c9da96de9665a9ff2ff0f16121cd30389..325b64dc4220ae521d8d2b17be81b02733b953b1 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -4,6 +4,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Edit profile", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Follows you", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Mute @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.report": "Report @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You can press {combo} to skip this next time", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blocked users", "column.community": "Local timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Loading...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferences", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federated timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Uploading...", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 8f02973d770e12894e93437e6b6492332312e036..fd32fcd3804190e4f82989dc6ed02ec5cdf03970 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -4,6 +4,7 @@ "account.block": "Blokiraj @{name}", "account.block_domain": "Sakrij sve sa {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Uredi profil", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "te slijedi", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "UtiÅ¡aj @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Postovi", "account.posts_with_replies": "Toots with replies", "account.report": "Prijavi @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Prestani slijediti", "account.unmute": "PoniÅ¡ti utiÅ¡avanje @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "MožeÅ¡ pritisnuti {combo} kako bi ovo preskoÄio sljedeći put", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blokirani korisnici", "column.community": "Lokalni timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriti", "column.follow_requests": "Zahtjevi za slijeÄ‘enje", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Unpin", "column_subheading.settings": "Postavke", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "confirmations.domain_block.confirm": "Sakrij cijelu domenu", "confirmations.domain_block.message": "Jesi li zaista, zaista siguran da želiÅ¡ potpuno blokirati {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "UtiÅ¡aj", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Jesi li siguran da želiÅ¡ utiÅ¡ati {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Aktivnost", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "JoÅ¡ nemaÅ¡ notifikacija. Komuniciraj sa drugima kako bi zapoÄeo razgovor.", "empty_column.public": "Ovdje nema niÅ¡ta! NapiÅ¡i neÅ¡to javno, ili ruÄno slijedi korisnike sa drugih instanci kako bi popunio", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "UÄitavam...", "media_gallery.toggle_visible": "Preklopi vidljivost", "missing_indicator.label": "Nije naÄ‘en", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Postavke", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federalni timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} je lajkao tvoj status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Podesi status privatnosti", @@ -295,6 +319,7 @@ "privacy.public.short": "Javno", "privacy.unlisted.long": "Ne prikazuj u javnim timelineovima", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Pokaži viÅ¡e", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "PoniÅ¡ti utiÅ¡avanje razgovora", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Povuci i spusti kako bi uploadao", "upload_button.label": "Dodaj media", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "PoniÅ¡ti", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Uploadam...", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 3f1f4a6e9f11b46f1dde1c28a808de05323b5700..0eadb290348a5a445b29215a2dbd504ec2aa87d6 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -4,6 +4,7 @@ "account.block": "@{name} letiltása", "account.block_domain": "Minden elrejtése innen: {domain}", "account.blocked": "Letiltva", + "account.cancel_follow_request": "Követési kérelem törlése", "account.direct": "Közvetlen üzenet @{name} számára", "account.domain_blocked": "Rejtett domain", "account.edit_profile": "Profil szerkesztése", @@ -15,6 +16,7 @@ "account.follows.empty": "Ez a felhasználó még senkit sem követ.", "account.follows_you": "Követ téged", "account.hide_reblogs": "@{name} megtolásainak némÃtása", + "account.last_status": "Utoljára aktÃv", "account.link_verified_on": "A linket ellenÅ‘riztük: {date}", "account.locked_info": "Ez a fiók zárt. A tulaj engedélyezi, ki követheti Å‘t.", "account.media": "Média", @@ -23,6 +25,7 @@ "account.mute": "@{name} némÃtása", "account.mute_notifications": "@{name} értesÃtéseinek némÃtása", "account.muted": "NémÃtva", + "account.never_active": "Soha", "account.posts": "Tülkölés", "account.posts_with_replies": "Tülkölés válaszokkal", "account.report": "@{name} jelentése", @@ -35,8 +38,11 @@ "account.unfollow": "Követés vége", "account.unmute": "@{name} némÃtás feloldása", "account.unmute_notifications": "@{name} némÃtott értesÃtéseinek feloldása", + "alert.rate_limited.message": "Kérlek, próbáld újra {retry_time, time, medium}.", + "alert.rate_limited.title": "Forgalomkorlátozás", "alert.unexpected.message": "Váratlan hiba történt.", "alert.unexpected.title": "Hoppá!", + "autosuggest_hashtag.per_week": "{count}/hét", "boost_modal.combo": "Hogy átugord ezt következÅ‘ alkalommal, használd {combo}", "bundle_column_error.body": "Hiba történt a komponens betöltése közben.", "bundle_column_error.retry": "Próbáld újra", @@ -47,6 +53,7 @@ "column.blocks": "Letiltott felhasználók", "column.community": "Helyi idÅ‘vonal", "column.direct": "Közvetlen üzenetek", + "column.directory": "Profilok böngészése", "column.domain_blocks": "Rejtett domainek", "column.favourites": "Kedvencek", "column.follow_requests": "Követési kérelmek", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Biztos, hogy véglegesen törölni szeretnéd ezt a listát?", "confirmations.domain_block.confirm": "Teljes domain elrejtése", "confirmations.domain_block.message": "Egészen biztos, hogy le szeretnéd tiltani a teljes {domain}-t? A legtöbb esetben néhány célzott tiltás vagy némÃtás elegendÅ‘ és kÃvánatosabb megoldás. Semmilyen tartalmat nem fogsz látni ebbÅ‘l a domainbÅ‘l se idÅ‘vonalakon, se értesÃtésekben. Az ebben a domainben lévÅ‘ követÅ‘idet is eltávolÃtjuk.", + "confirmations.logout.confirm": "Kijelentkezés", + "confirmations.logout.message": "Biztosan ki akar jelentkezni?", "confirmations.mute.confirm": "NémÃtás", + "confirmations.mute.explanation": "Ez elrejti a tÅ‘lük érkezÅ‘ posztokat, valamint az Å‘ket megemlÃtÅ‘ket, de Å‘k továbbra is láthatják a te posztjaidat és követhetnek is téged.", "confirmations.mute.message": "Biztos, hogy némÃtani szeretnéd {name}?", "confirmations.redraft.confirm": "Törlés és újraÃrás", "confirmations.redraft.message": "Biztos, hogy ezt a tülköt szeretnéd törölni és újraÃrni? Minden megtolást és kedvencnek jelölést elvesztesz, az eredetire adott válaszok pedig elárvulnak.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Ha most válaszolsz, ez felülÃrja a most szerkesztés alatt álló üzenetet. Mégis ezt szeretnéd?", "confirmations.unfollow.confirm": "Követés visszavonása", "confirmations.unfollow.message": "Biztos, hogy vissza szeretnéd vonni {name} követését?", + "conversation.delete": "Beszélgetés törlése", + "conversation.mark_as_read": "Megjelölés olvasottként", + "conversation.open": "Beszélgetés megtekintése", + "conversation.with": "{names}-el/al", + "directory.federated": "Az ismert fediverzumból", + "directory.local": "Csak {domain}-ból/bÅ‘l", + "directory.new_arrivals": "Új csatlakozók", + "directory.recently_active": "Nemrég aktÃv", "embed.instructions": "Ãgyazd be ezt a tülköt a weboldaladba az alábbi kód kimásolásával.", "embed.preview": "Ãgy fog kinézni:", "emoji_button.activity": "Aktivitás", @@ -134,6 +152,10 @@ "empty_column.mutes": "Még egy felhasználót sem némÃtottál le.", "empty_column.notifications": "Jelenleg nincsenek értesÃtéseid. Lépj kapcsolatba másokkal, hogy elindÃtsd a beszélgetést.", "empty_column.public": "Jelenleg itt nincs semmi! Ãrj valamit nyilvánosan vagy kövess más szervereken levÅ‘ felhasználókat, hogy megtöltsd", + "error.unexpected_crash.explanation": "Egy hiba vagy böngészÅ‘ inkompatibilitás miatt ez az oldal nem jelenÃthetÅ‘ meg rendesen.", + "error.unexpected_crash.next_steps": "Próbáld frissÃteni az oldalt. Ha ez nem segÃt, egy másik böngészÅ‘n vagy appon keresztül még mindig használhatod a Mastodont.", + "errors.unexpected_crash.copy_stacktrace": "Stacktrace vágólapra másolása", + "errors.unexpected_crash.report_issue": "Probléma bejelentése", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Új lista cÃme", "lists.search": "Keresés a követett személyek között", "lists.subheading": "Listáid", + "load_pending": "{count, plural, one {# új elem} other {# új elem}}", "loading_indicator.label": "Betöltés...", "media_gallery.toggle_visible": "Láthatóság állÃtása", "missing_indicator.label": "Nincs találat", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Személyes", "navigation_bar.pins": "Kitűzött tülkök", "navigation_bar.preferences": "BeállÃtások", - "navigation_bar.profile_directory": "Profilok", "navigation_bar.public_timeline": "Föderációs idÅ‘vonal", "navigation_bar.security": "Biztonság", "notification.favourite": "{name} kedvencnek jelölte egy tülködet", @@ -282,8 +304,10 @@ "notifications.group": "{count} értesÃtés", "poll.closed": "Lezárva", "poll.refresh": "FrissÃtés", + "poll.total_people": "{count, plural, one {# személy} other {# személy}}", "poll.total_votes": "{count, plural, one {# szavazat} other {# szavazat}}", "poll.vote": "Szavazás", + "poll.voted": "Erre a válaszra szavaztál", "poll_button.add_poll": "Új szavazás", "poll_button.remove_poll": "Szavazás törlése", "privacy.change": "Tülk láthatóságának módosÃtása", @@ -295,6 +319,7 @@ "privacy.public.short": "Nyilvános", "privacy.unlisted.long": "Ne mutassuk nyilvános idÅ‘vonalon", "privacy.unlisted.short": "Listázatlan", + "refresh": "FrissÃtés", "regeneration_indicator.label": "TöltÅ‘dik…", "regeneration_indicator.sublabel": "A saját idÅ‘vonalad épp készül!", "relative_time.days": "{number}nap", @@ -319,6 +344,7 @@ "search_results.accounts": "Emberek", "search_results.hashtags": "Hashtagek", "search_results.statuses": "Tülkök", + "search_results.statuses_fts_disabled": "Ezen a szerveren nem engedélyezett a tülkök tartalom szerinti keresése.", "search_results.total": "{count, number} {count, plural, one {találat} other {találat}}", "status.admin_account": "Moderáció megnyitása @{name} felhasználóhoz", "status.admin_status": "Tülk megnyitása moderációra", @@ -358,6 +384,7 @@ "status.show_more": "Többet", "status.show_more_all": "Többet mindenhol", "status.show_thread": "Szál mutatása", + "status.uncached_media_warning": "Nem elérhetÅ‘", "status.unmute_conversation": "Beszélgetés némÃtásának kikapcsolása", "status.unpin": "Kitűzés eltávolÃtása a profilodról", "suggestions.dismiss": "Javaslat elvetése", @@ -373,14 +400,22 @@ "time_remaining.moments": "Pillanatok vannak hátra", "time_remaining.seconds": "{number, plural, one {# másodperc} other {# másodperc}} van hátra", "trends.count_by_accounts": "{count} {rawCount, plural, one {résztvevÅ‘} other {résztvevÅ‘}} beszélget", + "trends.trending_now": "Most trendi", "ui.beforeunload": "A piszkozatod el fog veszni, ha elhagyod a Mastodon-t.", "upload_area.title": "Húzd ide a feltöltéshez", "upload_button.label": "Média hozzáadása (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Túllépted a fájl feltöltési limitet.", "upload_error.poll": "Szavazásnál nem lehet fájlt feltölteni.", "upload_form.description": "LeÃrás látáskorlátozottak számára", - "upload_form.focus": "ElÅ‘nézet megváltoztatása", + "upload_form.edit": "Szerkesztés", "upload_form.undo": "Mégsem", + "upload_modal.analyzing_picture": "Kép elemzése…", + "upload_modal.apply": "Alkalmazás", + "upload_modal.description_placeholder": "A gyors, barna róka átugrik a lusta kutya fölött", + "upload_modal.detect_text": "Szöveg felismerése a képrÅ‘l", + "upload_modal.edit_media": "Média szerkesztése", + "upload_modal.hint": "Kattints vagy húzd a kört az elÅ‘nézetben arra a fókuszpontra, mely minden megjelenÃtett bélyegképen látható kell, legyen.", + "upload_modal.preview_label": "ElÅ‘nézet ({ratio})", "upload_progress.label": "Feltöltés...", "video.close": "Videó bezárása", "video.exit_fullscreen": "Kilépés teljes képernyÅ‘bÅ‘l", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 483becbcf8cb2d66fb8152791781be980a03490f..e667e5b4e8c4618b9520b2854991afaeef5d1343 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -1,20 +1,22 @@ { "account.add_or_remove_from_list": "Add or Remove from lists", - "account.badges.bot": "Bot", + "account.badges.bot": "Ô²Õ¸Õ¿", "account.block": "Ô±Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¥Õ¬ @{name}ÖŠÕ«Õ¶", "account.block_domain": "Ô¹Õ¡Ö„ÖÕ¶Õ¥Õ¬ Õ¡Õ´Õ¥Õ¶Õ¨ Õ°Õ¥Õ¿Õ¥Ö‚ÕµÕ¡Õ¬ Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ«ÖÕ {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Ô½Õ´Õ¢Õ¡Õ£Ö€Õ¥Õ¬ Õ¡Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶ Õ§Õ»Õ¨", "account.endorse": "Feature on profile", "account.follow": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬", - "account.followers": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¾Õ¸Õ²Õ¶Õ¥Ö€", + "account.followers": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Õ²Õ¶Õ¥Ö€", "account.followers.empty": "No one follows this user yet.", "account.follows": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Ö‚Õ´ Õ§", "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¸Ö‚Õ´ Õ§ Ö„Õ¥Õ¦", "account.hide_reblogs": "Ô¹Õ¡Ö„ÖÕ¶Õ¥Õ¬ @{name}ÖŠÕ« Õ¿Õ¡Ö€Õ¡Õ®Õ¡Õ®Õ¶Õ¥Ö€Õ¨", + "account.last_status": "Last active", "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.media": "Õ„Õ¥Õ¤Õ«Õ¡", @@ -23,6 +25,7 @@ "account.mute": "Ô¼Õ¼Õ¥ÖÕ¶Õ¥Õ¬ @{name}ÖŠÕ«Õ¶", "account.mute_notifications": "Ô±Õ¶Õ»Õ¡Õ¿Õ¥Õ¬ Õ®Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ @{name}ÖŠÕ«Ö", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Ô³Ö€Õ¡Õ¼Õ¸Ö‚Õ´Õ¶Õ¥Ö€", "account.posts_with_replies": "Toots with replies", "account.report": "Ô²Õ¸Õ²Õ¸Ö„Õ¥Õ¬ @{name}ÖŠÕ«Ö", @@ -35,8 +38,11 @@ "account.unfollow": "Õ‰Õ°Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬", "account.unmute": "Ô±ÕºÕ¡Õ¬Õ¼Õ¥ÖÕ¶Õ¥Õ¬ @{name}ÖŠÕ«Õ¶", "account.unmute_notifications": "Õ„Õ«Õ¡ÖÕ¶Õ¥Õ¬ Õ®Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ @{name}ÖŠÕ«Ö", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.title": "Վա՜յ", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Ô¿Õ¡Ö€Õ¸Õ² Õ¥Õ½ Õ½Õ¥Õ²Õ´Õ¥Õ¬ {combo}Õ Õ½Õ¡ Õ°Õ¡Õ»Õ¸Ö€Õ¤ Õ¡Õ¶Õ£Õ¡Õ´ Õ¢Õ¡Ö Õ©Õ¸Õ²Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€", "bundle_column_error.body": "Ô±ÕµÕ½ Õ¢Õ¡Õ²Õ¡Õ¤Ö€Õ«Õ¹Õ¨ Õ¢Õ¥Õ¼Õ¶Õ¥Õ¬Õ¸Ö‚ Õ¨Õ¶Õ©Õ¡ÖÖ„Õ¸Ö‚Õ´ Õ«Õ¶Õ¹ÖŠÕ¸Ö€ Õ¢Õ¡Õ¶ ÕÕ¡ÖƒÕ¡Õ¶Õ¾Õ¥ÖÖ‰", "bundle_column_error.retry": "Ô¿Ö€Õ¯Õ«Õ¶ ÖƒÕ¸Ö€Õ±Õ¥Õ¬", @@ -47,6 +53,7 @@ "column.blocks": "Ô±Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¾Õ¡Õ® Ö…Õ£Õ¿Õ¡Õ¿Õ¥Ö€Õ¥Ö€", "column.community": "ÕÕ¥Õ²Õ¡Õ¯Õ¡Õ¶ Õ°Õ¸Õ½Ö„", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Õ€Õ¡Õ¾Õ¡Õ¶Õ¡Õ®Õ¶Õ¥Ö€", "column.follow_requests": "Õ€Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡ÕµÖÕ¥Ö€", @@ -64,7 +71,7 @@ "column_header.show_settings": "Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨", "column_header.unpin": "Õ€Õ¡Õ¶Õ¥Õ¬", "column_subheading.settings": "Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "Ô±ÕµÕ½ Õ©Õ¸Ö‚Õ©Õ¨ Õ¹Õ« Õ°Õ¡Õ·Õ¾Õ¡Õ¼Õ¾Õ« Õ¸Ö€Õ¥Ö‚Õ§ ÕºÕ«Õ¿Õ¡Õ¯Õ« Õ¿Õ¡Õ¯, Ö„Õ¡Õ¶Õ¦Õ« Õ¡ÕµÕ¶ Õ®Õ¡Õ®Õ¸Ö‚Õ¯ Õ§Ö‰ Õ„Õ«Õ¡ÕµÕ¶ Õ°Ö€Õ¡ÕºÕ¡Ö€Õ¡Õ¯Õ¡ÕµÕ«Õ¶ Õ©Õ©Õ¥Ö€Õ¨ Õ°Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ§ Õ¸Ö€Õ¸Õ¶Õ¥Õ¬ ÕºÕ«Õ¿Õ¡Õ¯Õ¶Õ¥Ö€Õ¸Õ¾Ö‰", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "ÕŽÕ½Õ¿Õ¡ÕžÕ° Õ¥Õ½, Õ¸Ö€ Õ¸Ö‚Õ¦Õ¸Ö‚Õ´ Õ¥Õ½ Õ´Õ·Õ¿Õ¡ÕºÕ¥Õ½ Õ»Õ¶Õ»Õ¥Õ¬ Õ¡ÕµÕ½ ÖÕ¡Õ¶Õ¯Õ¨Ö‰", "confirmations.domain_block.confirm": "Ô¹Õ¡Ö„ÖÕ¶Õ¥Õ¬ Õ¡Õ´Õ¢Õ¸Õ²Õ» Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¨", "confirmations.domain_block.message": "Õ€Õ¡Õ½Õ¿Õ¡Õ¿ÖŠÕ°Õ¡Õ½Õ¿Õ¡ÕžÕ¿ Õ¾Õ½Õ¿Õ¡Õ° Õ¥Õ½, Õ¸Ö€ Õ¸Ö‚Õ¦Õ¸Ö‚Õ´ Õ¥Õ½ Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¥Õ¬ Õ¡Õ´Õ¢Õ¸Õ²Õ» {domain} Õ¿Õ«Ö€Õ¸Ö‚ÕµÕ©Õ¨Ö‰ ÕÕ¸Õ¾Õ¸Ö€Õ¡Õ¢Õ¡Ö€ Õ´Õ« Õ¥Ö€Õ¯Õ¸Ö‚ Õ©Õ«Ö€Õ¡ÕÕ¡Õ¾Õ¸Ö€Õ¾Õ¡Õ® Õ¡Ö€Õ£Õ¥Õ¬Õ¡ÖƒÕ¡Õ¯Õ¸Ö‚Õ´ Õ¯Õ¡Õ´ Õ¬Õ¼Õ¥ÖÕ¸Ö‚Õ´ Õ¢Õ¡Õ¾Õ¡Õ¯Õ¡Õ¶ Õ§ Õ¸Ö‚ Õ¶Õ¡ÕÕ¨Õ¶Õ¿Ö€Õ¥Õ¬Õ«Ö‰", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Ô¼Õ¼Õ¥ÖÕ¶Õ¥Õ¬", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "ÕŽÕ½Õ¿Õ¡ÕžÕ° Õ¥Õ½, Õ¸Ö€ Õ¸Ö‚Õ¦Õ¸Ö‚Õ´ Õ¥Õ½ {name}ÖŠÕ«Õ¶ Õ¬Õ¼Õ¥ÖÕ¶Õ¥Õ¬Ö‰", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Ô±ÕºÕ¡Õ°Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬", "confirmations.unfollow.message": "ÕŽÕ½Õ¿Õ¡ÕžÕ° Õ¥Õ½, Õ¸Ö€ Õ¸Ö‚Õ¦Õ¸Ö‚Õ´ Õ¥Õ½ Õ¡ÕµÕ¬Õ¥Ö‚Õ½ Õ¹Õ°Õ¥Õ¿Õ¥Ö‚Õ¥Õ¬ {name}ÖŠÕ«Õ¶Ö‰", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Ô±ÕµÕ½ Õ©Õ¸Ö‚Õ©Õ¨ Ö„Õ¸ Õ¯Õ¡ÕµÖ„Õ¸Ö‚Õ´ Õ¶Õ¥Ö€Õ¤Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ¯Õ¡Ö€Õ¸Õ² Õ¥Õ½ ÕºÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬ Õ¶Õ¥Ö€Ö„Õ¸Õ°Õ«Õ·ÕµÕ¡Õ¬ Õ¯Õ¸Õ¤Õ¨Ö‰", "embed.preview": "Ô±Õ°Õ¡, Õ©Õ¥ Õ«Õ¶Õ¹ Õ¿Õ¥Õ½Ö„ Õ¯Õ¸Ö‚Õ¶Õ¥Õ¶Õ¡ Õ¡ÕµÕ¶Õ", "emoji_button.activity": "Ô¶Õ¢Õ¡Õ²Õ´Õ¸Ö‚Õ¶Ö„Õ¶Õ¥Ö€", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "ÕˆÕ¹ Õ´Õ« Õ®Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´ Õ¤Õ¥Õ¼ Õ¹Õ¸Ö‚Õ¶Õ¥Õ½Ö‰ Ô²Õ¦Õ«Ö€ Õ´ÕµÕ¸Ö‚Õ½Õ¶Õ¥Ö€Õ«Õ¶Õ ÕÕ¸Õ½Õ¡Õ¯ÖÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ½Õ¯Õ½Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰", "empty_column.public": "Ô±ÕµÕ½Õ¿Õ¥Õ² Õ¢Õ¡Õ¶ Õ¹Õ¯Õ¡Õ›Ö‰ Õ€Ö€Õ¡ÕºÕ¡Ö€Õ¡Õ¯Õ¡ÕµÕ«Õ¶ Õ´Õ« Õ¢Õ¡Õ¶ Õ£Ö€Õ«Ö€ Õ¯Õ¡Õ´ Õ°Õ¥Õ¿Õ¥Ö‚Õ«Ö€ Õ¡ÕµÕ¬ Õ°Õ¡Õ¶Õ£Õ¸Ö‚ÕµÖÕ¶Õ¥Ö€Õ«Ö Õ§Õ¡Õ¯Õ¶Õ¥Ö€Õ«Õ Õ¡ÕµÕ¶ Õ¬ÖÕ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€Ö‰", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Õ†Õ¸Ö€ ÖÕ¡Õ¶Õ¯Õ« Õ¾Õ¥Ö€Õ¶Õ¡Õ£Õ«Ö€", "lists.search": "Õ“Õ¶Õ¿Ö€Õ¥Õ¬ Ö„Õ¸ Õ°Õ¥Õ¿Õ¥Ö‚Õ¡Õ® Õ´Õ¡Ö€Õ¤Õ¯Õ¡Õ¶Ö Õ´Õ¥Õ»", "lists.subheading": "Õ”Õ¸ ÖÕ¡Õ¶Õ¯Õ¥Ö€Õ¨", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Ô²Õ¥Õ¼Õ¶Õ¾Õ¸Ö‚Õ´ է…", "media_gallery.toggle_visible": "Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬/Õ©Õ¡Ö„ÖÕ¶Õ¥Õ¬", "missing_indicator.label": "Õ‰Õ£Õ¿Õ¶Õ¾Õ¥Ö", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Ô±Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶", "navigation_bar.pins": "Ô±Õ´Ö€Õ¡ÖÕ¾Õ¡Õ® Õ©Õ©Õ¥Ö€", "navigation_bar.preferences": "Õ†Õ¡ÕÕ¡ÕºÕ¡Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Ô´Õ¡Õ·Õ¶Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„", "navigation_bar.security": "Ô±Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶", "notification.favourite": "{name} Õ°Õ¡Õ¾Õ¡Õ¶Õ¥Ö Õ©Õ¸Ö‚Õ©Õ¤", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¥Õ¬ Õ©Õ©Õ« Õ£Õ¡Õ²Õ¿Õ¶Õ«Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨", @@ -295,6 +319,7 @@ "privacy.public.short": "Õ€Ö€Õ¡ÕºÕ¡Ö€Õ¡Õ¯Õ¡ÕµÕ«Õ¶", "privacy.unlisted.long": "Õ‰Õ©Õ©Õ¥Õ¬ Õ°Ö€Õ¡ÕºÕ¡Ö€Õ¡Õ¯Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„Õ¥Ö€Õ¸Ö‚Õ´", "privacy.unlisted.short": "Ô¾Õ¡Õ®Õ¸Ö‚Õ¯", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}Ö…Ö€", @@ -316,9 +341,10 @@ "search_popout.tips.status": "Õ©Õ¸Ö‚Õ©", "search_popout.tips.text": "Õ€Õ¡Õ½Õ¡Ö€Õ¡Õ¯ Õ¿Õ¥Ö„Õ½Õ¿Õ¨ Õ¯Õ¾Õ¥Ö€Õ¡Õ¤Õ¡Ö€Õ±Õ¶Õ« Õ°Õ¡Õ´Õ¨Õ¶Õ¯Õ¶Õ¸Õ² Õ¡Õ¶Õ¸Ö‚Õ¶Õ¶Õ¥Ö€, Ö…Õ£Õ¿Õ¡Õ¶Õ¸Ö‚Õ¶Õ¶Õ¥Ö€ Õ¸Ö‚ ÕºÕ«Õ¿Õ¡Õ¯Õ¶Õ¥Ö€", "search_popout.tips.user": "Ö…Õ£Õ¿Õ¡Õ¿Õ¥Ö€", - "search_results.accounts": "People", + "search_results.accounts": "Õ„Õ¡Ö€Õ¤Õ«Õ¯", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {Õ¡Ö€Õ¤ÕµÕ¸Ö‚Õ¶Ö„} other {Õ¡Ö€Õ¤ÕµÕ¸Ö‚Õ¶Ö„}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Ô±Õ¾Õ¥Õ¬Õ«Õ¶", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Ô±ÕºÕ¡Õ¬Õ¼Õ¥ÖÕ¶Õ¥Õ¬ ÕÕ¸Õ½Õ¡Õ¯ÖÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨", "status.unpin": "Õ€Õ¡Õ¶Õ¥Õ¬ Õ¡Õ¶Õ±Õ¶Õ¡Õ¯Õ¡Õ¶ Õ§Õ»Õ«Ö", "suggestions.dismiss": "Dismiss suggestion", @@ -366,21 +393,29 @@ "tabs_bar.home": "Õ€Õ«Õ´Õ¶Õ¡Õ¯Õ¡Õ¶", "tabs_bar.local_timeline": "ÕÕ¥Õ²Õ¡Õ¯Õ¡Õ¶", "tabs_bar.notifications": "Ô¾Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€", - "tabs_bar.search": "Search", + "tabs_bar.search": "Õ“Õ¶Õ¿Ö€Õ¥Õ¬", "time_remaining.days": "{number, plural, one {# day} other {# days}} left", "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Õ”Õ¸ Õ½Õ¥Ö‚Õ¡Õ£Õ«Ö€Õ¨ Õ¯Õ¯Õ¸Ö€Õ«, Õ¥Õ©Õ¥ Õ¬Ö„Õ¥Õ½ Õ„Õ¡Õ½Õ¿Õ¸Õ¤Õ¸Õ¶Õ¨Ö‰", "upload_area.title": "Õ”Õ¡Õ·Õ«Ö€ Õ¸Ö‚ Õ¶Õ¥Õ¿Õ«Ö€Õ Õ¾Õ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€", "upload_button.label": "Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ´Õ¥Õ¤Õ«Õ¡", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Õ†Õ¯Õ¡Ö€Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¸Ö‚ Õ¿Õ¥Õ½Õ¸Õ²Õ¡Õ¯Õ¡Õ¶ ÕÕ¶Õ¤Õ«Ö€Õ¶Õ¥Ö€ Õ¸Ö‚Õ¶Õ¥ÖÕ¸Õ²Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Õ€Õ¥Õ¿Õ¡Ö€Õ¯Õ¥Õ¬", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "ÕŽÕ¥Ö€Õ¢Õ¥Õ¼Õ¶Õ¾Õ¸Ö‚Õ´ է…", "video.close": "Õ“Õ¡Õ¯Õ¥Õ¬ Õ¿Õ¥Õ½Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨", "video.exit_fullscreen": "Ô±Õ¶Õ»Õ¡Õ¿Õ¥Õ¬ Õ¬Õ«Õ¡Õ§Õ¯Ö€Õ¡Õ¶ Õ¤Õ«Õ¿Õ¸Ö‚Õ´Õ¨", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 1f5c45af6904f5a62d3856af5522f2bb0ecc4527..900f95d422b1b7944d157f2eb235194ebef8d7e5 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -1,29 +1,32 @@ { - "account.add_or_remove_from_list": "Add or Remove from lists", + "account.add_or_remove_from_list": "Tambah atau Hapus dari daftar", "account.badges.bot": "Bot", "account.block": "Blokir @{name}", "account.block_domain": "Sembunyikan segalanya dari {domain}", "account.blocked": "Terblokir", + "account.cancel_follow_request": "Batalkan permintaan ikuti", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain disembunyikan", "account.edit_profile": "Ubah profil", - "account.endorse": "Feature on profile", + "account.endorse": "Tampilkan di profil", "account.follow": "Ikuti", "account.followers": "Pengikut", - "account.followers.empty": "No one follows this user yet.", + "account.followers.empty": "Tidak ada satupun yang mengkuti pengguna ini saat ini.", "account.follows": "Mengikuti", - "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows.empty": "Pengguna ini belum mengikuti siapapun.", "account.follows_you": "Mengikuti anda", "account.hide_reblogs": "Sembunyikan boosts dari @{name}", - "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.last_status": "Terakhir aktif", + "account.link_verified_on": "Kepemilikan tautan ini telah dicek pada {date}", + "account.locked_info": "Status privasi akun ini disetel untuk dikunci. Pemilik secara manual meninjau siapa yang dapat mengikuti mereka.", "account.media": "Media", "account.mention": "Balasan @{name}", "account.moved_to": "{name} telah pindah ke:", "account.mute": "Bisukan @{name}", "account.mute_notifications": "Sembunyikan notifikasi dari @{name}", "account.muted": "Dibisukan", - "account.posts": "Toots", + "account.never_active": "Tak pernah", + "account.posts": "Toot", "account.posts_with_replies": "Postingan dengan balasan", "account.report": "Laporkan @{name}", "account.requested": "Menunggu persetujuan. Klik untuk membatalkan permintaan", @@ -31,23 +34,27 @@ "account.show_reblogs": "Tampilkan boost dari @{name}", "account.unblock": "Hapus blokir @{name}", "account.unblock_domain": "Tampilkan {domain}", - "account.unendorse": "Don't feature on profile", + "account.unendorse": "Jangan tampilkan di profil", "account.unfollow": "Berhenti mengikuti", "account.unmute": "Berhenti membisukan @{name}", "account.unmute_notifications": "Munculkan notifikasi dari @{name}", - "alert.unexpected.message": "An unexpected error occurred.", + "alert.rate_limited.message": "Tolong ulangi setelah {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", + "alert.unexpected.message": "Terjadi kesalahan yang tidak terduga.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per minggu", "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini", "bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.", "bundle_column_error.retry": "Coba lagi", - "bundle_column_error.title": "Network error", + "bundle_column_error.title": "Kesalahan jaringan", "bundle_modal_error.close": "Tutup", "bundle_modal_error.message": "Kesalahan terjadi saat memuat komponen ini.", "bundle_modal_error.retry": "Coba lagi", "column.blocks": "Pengguna diblokir", "column.community": "Linimasa Lokal", - "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.direct": "Pesan langsung", + "column.directory": "Jelajahi profil", + "column.domain_blocks": "Topik tersembunyi", "column.favourites": "Favorit", "column.follow_requests": "Permintaan mengikuti", "column.home": "Beranda", @@ -64,43 +71,54 @@ "column_header.show_settings": "Tampilkan pengaturan", "column_header.unpin": "Lepaskan", "column_subheading.settings": "Pengaturan", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Hanya media", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", - "compose_form.direct_message_warning_learn_more": "Learn more", + "compose_form.direct_message_warning_learn_more": "Pelajari selengkapnya", "compose_form.hashtag_warning": "Toot ini tidak akan ada dalam daftar tagar manapun karena telah di set sebagai tidak terdaftar. Hanya postingan publik yang bisa dicari dengan tagar.", "compose_form.lock_disclaimer": "Akun anda tidak {locked}. Semua orang dapat mengikuti anda untuk melihat postingan khusus untuk pengikut anda.", "compose_form.lock_disclaimer.lock": "terkunci", "compose_form.placeholder": "Apa yang ada di pikiran anda?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", + "compose_form.poll.add_option": "Tambahkan pilihan", + "compose_form.poll.duration": "Durasi jajak pendapat", + "compose_form.poll.option_placeholder": "Pilihan {number}", + "compose_form.poll.remove_option": "Hapus opsi ini", "compose_form.publish": "Toot", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Tandai sebagai media sensitif", "compose_form.sensitive.marked": "Sumber ini telah ditandai sebagai sumber sensitif.", "compose_form.sensitive.unmarked": "Sumber ini tidak ditandai sebagai sumber sensitif", "compose_form.spoiler.marked": "Teks disembunyikan dibalik peringatan", "compose_form.spoiler.unmarked": "Teks tidak tersembunyi", "compose_form.spoiler_placeholder": "Peringatan konten", "confirmation_modal.cancel": "Batal", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "Blokir & Laporkan", "confirmations.block.confirm": "Blokir", "confirmations.block.message": "Apa anda yakin ingin memblokir {name}?", "confirmations.delete.confirm": "Hapus", "confirmations.delete.message": "Apa anda yakin untuk menghapus status ini?", - "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.confirm": "Hapus", "confirmations.delete_list.message": "Apakah anda yakin untuk menghapus daftar ini secara permanen?", "confirmations.domain_block.confirm": "Sembunyikan keseluruhan domain", "confirmations.domain_block.message": "Apakah anda benar benar yakin untuk memblokir keseluruhan {domain}? Dalam kasus tertentu beberapa pemblokiran atau penyembunyian lebih baik.", + "confirmations.logout.confirm": "Keluar", + "confirmations.logout.message": "Apakah anda yakin ingin keluar?", "confirmations.mute.confirm": "Bisukan", + "confirmations.mute.explanation": "Ini akan menyembunyikan pos dari mereka dan pos yang menyebut mereka, tapi ini tetap mengizinkan mereka melihat posmu dan mengikutimu.", "confirmations.mute.message": "Apa anda yakin ingin membisukan {name}?", - "confirmations.redraft.confirm": "Delete & redraft", + "confirmations.redraft.confirm": "Hapus dan konsep ulang", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", - "confirmations.reply.confirm": "Reply", - "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.reply.confirm": "Balas", + "confirmations.reply.message": "Membalas sekarang akan menimpa pesan yang sedang Anda buat. Anda yakin ingin melanjutkan?", "confirmations.unfollow.confirm": "Berhenti mengikuti", "confirmations.unfollow.message": "Apakah anda ingin berhenti mengikuti {name}?", + "conversation.delete": "Hapus percakapan", + "conversation.mark_as_read": "Tandai sudah dibaca", + "conversation.open": "Lihat percakapan", + "conversation.with": "Dengan {names}", + "directory.federated": "Dari fediverse yang dikenal", + "directory.local": "Dari {domain} saja", + "directory.new_arrivals": "Yang baru datang", + "directory.recently_active": "Baru-baru ini aktif", "embed.instructions": "Sematkan status ini di website anda dengan menyalin kode di bawah ini.", "embed.preview": "Seperti ini nantinya:", "emoji_button.activity": "Aktivitas", @@ -117,23 +135,27 @@ "emoji_button.search_results": "Hasil pencarian", "emoji_button.symbols": "Simbol", "emoji_button.travel": "Tempat Wisata", - "empty_column.account_timeline": "No toots here!", - "empty_column.account_unavailable": "Profile unavailable", - "empty_column.blocks": "You haven't blocked any users yet.", + "empty_column.account_timeline": "Tidak ada toot di sini!", + "empty_column.account_unavailable": "Profil tidak tersedia", + "empty_column.blocks": "Anda belum memblokir siapapun.", "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", - "empty_column.domain_blocks": "There are no hidden domains yet.", - "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", - "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", - "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "empty_column.direct": "Anda belum memiliki pesan langsung. Ketika Anda mengirim atau menerimanya, maka akan muncul di sini.", + "empty_column.domain_blocks": "Tidak ada topik tersembunyi.", + "empty_column.favourited_statuses": "Anda belum memiliki toot favorit. Ketika Anda mengirim atau menerimanya, maka akan muncul di sini.", + "empty_column.favourites": "Tidak ada seorangpun yang memfavoritkan toot ini. Ketika seseorang melakukannya, maka akan muncul disini.", + "empty_column.follow_requests": "Anda belum memiliki permintaan mengikuti. Ketika Anda menerimanya, maka akan muncul disini.", "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.", "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.", "empty_column.home.public_timeline": "linimasa publik", "empty_column.list": "Tidak ada postingan di list ini. Ketika anggota dari list ini memposting status baru, status tersebut akan tampil disini.", - "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", - "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.lists": "Anda belum memiliki daftar. Ketika Anda membuatnya, maka akan muncul disini.", + "empty_column.mutes": "Anda belum membisukan siapapun.", "empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.", "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisi ini", + "error.unexpected_crash.explanation": "Karena kutu pada kode kami atau isu kompatibilitas peramban, halaman tak dapat ditampilkan dengan benar.", + "error.unexpected_crash.next_steps": "Coba segarkan halaman. Jika tak membantu, Anda masih bisa memakai Mastodon dengan peramban berbeda atau aplikasi native.", + "errors.unexpected_crash.copy_stacktrace": "Salin stacktrace ke papan klip", + "errors.unexpected_crash.report_issue": "Laporkan masalah", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -141,149 +163,151 @@ "federation.local_only.short": "Local-only", "follow_request.authorize": "Izinkan", "follow_request.reject": "Tolak", - "getting_started.developers": "Developers", - "getting_started.directory": "Profile directory", - "getting_started.documentation": "Documentation", + "getting_started.developers": "Pengembang", + "getting_started.directory": "Direktori profil", + "getting_started.documentation": "Dokumentasi", "getting_started.heading": "Mulai", - "getting_started.invite": "Invite people", + "getting_started.invite": "Undang orang", "getting_started.open_source_notice": "Mastodon adalah perangkat lunak yang bersifat terbuka. Anda dapat berkontribusi atau melaporkan permasalahan/bug di Github {github}.", - "getting_started.security": "Security", - "getting_started.terms": "Terms of service", - "hashtag.column_header.tag_mode.all": "and {additional}", - "hashtag.column_header.tag_mode.any": "or {additional}", - "hashtag.column_header.tag_mode.none": "without {additional}", - "hashtag.column_settings.select.no_options_message": "No suggestions found", - "hashtag.column_settings.select.placeholder": "Enter hashtags…", - "hashtag.column_settings.tag_mode.all": "All of these", - "hashtag.column_settings.tag_mode.any": "Any of these", - "hashtag.column_settings.tag_mode.none": "None of these", + "getting_started.security": "Keamanan", + "getting_started.terms": "Ketentuan layanan", + "hashtag.column_header.tag_mode.all": "dan {additional}", + "hashtag.column_header.tag_mode.any": "atau {additional}", + "hashtag.column_header.tag_mode.none": "tanpa {additional}", + "hashtag.column_settings.select.no_options_message": "Tidak ada saran yang ditemukan", + "hashtag.column_settings.select.placeholder": "Masukkan tagar…", + "hashtag.column_settings.tag_mode.all": "Semua ini", + "hashtag.column_settings.tag_mode.any": "Semua ini", + "hashtag.column_settings.tag_mode.none": "Tak satu pun", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", "home.column_settings.basic": "Dasar", "home.column_settings.show_reblogs": "Tampilkan boost", "home.column_settings.show_replies": "Tampilkan balasan", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", - "introduction.federation.action": "Next", - "introduction.federation.federated.headline": "Federated", - "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.federation.local.headline": "Local", - "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "intervals.full.days": "{number, plural, other {# hari}}", + "intervals.full.hours": "{number, plural, other {# jam}}", + "intervals.full.minutes": "{number, plural, other {# menit}}", + "introduction.federation.action": "Selanjutnya", + "introduction.federation.federated.headline": "Gabungan", + "introduction.federation.federated.text": "Pos publik dari server fediverse lain akan muncul di linimasa gabungan.", + "introduction.federation.home.headline": "Beranda", + "introduction.federation.home.text": "Pos dari orang yang Anda ikuti akan muncul di beranda. Anda dapat mengikuti siapa pun dari server mana pun!", + "introduction.federation.local.headline": "Lokal", + "introduction.federation.local.text": "Pos publik dari orang yang ada di server sama denganmu akan muncul di linimasa lokal.", "introduction.interactions.action": "Finish toot-orial!", - "introduction.interactions.favourite.headline": "Favourite", - "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.favourite.headline": "Favorit", + "introduction.interactions.favourite.text": "Anda dapat menyimpan toot untuk dibaca nanti, biarkan penulis tahu Anda menyukainya, dengan memfavoritkannya.", "introduction.interactions.reblog.headline": "Boost", - "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "introduction.interactions.reblog.text": "Anda dapat membagikan toot orang lain kepada pengikut Anda dengan mem-boost-nya.", + "introduction.interactions.reply.headline": "Balas", + "introduction.interactions.reply.text": "Anda dapat membalas toot Anda dan orang lain, yang akan menjalin dalam satu percakapan.", + "introduction.welcome.action": "Ayo!", + "introduction.welcome.headline": "Langkah pertama", + "introduction.welcome.text": "Selamat datang di fediverse! Beberapa saat lagi, Anda dapat menyiarkan pesan dan berbincang dengan teman lintas server. Namun server ini, {domain}, spesial--ia menyimpan profil Anda, jadi ingatlah namanya.", "keyboard_shortcuts.back": "untuk kembali", - "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.blocked": "buka daftar pengguna terblokir", "keyboard_shortcuts.boost": "untuk menyebarkan", "keyboard_shortcuts.column": "untuk fokus kepada sebuah status di sebuah kolom", "keyboard_shortcuts.compose": "untuk fokus ke area penulisan", "keyboard_shortcuts.description": "Deskripsi", - "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.direct": "buka kolom pesan langsung", "keyboard_shortcuts.down": "untuk pindah ke bawah dalam sebuah daftar", "keyboard_shortcuts.enter": "untuk membuka status", "keyboard_shortcuts.favourite": "untuk memfavoritkan", - "keyboard_shortcuts.favourites": "to open favourites list", - "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.favourites": "buka daftar favorit", + "keyboard_shortcuts.federated": "buka linimasa gabungan", "keyboard_shortcuts.heading": "Pintasan keyboard", - "keyboard_shortcuts.home": "to open home timeline", - "keyboard_shortcuts.hotkey": "Hotkey", - "keyboard_shortcuts.legend": "to display this legend", - "keyboard_shortcuts.local": "to open local timeline", - "keyboard_shortcuts.mention": "to mention author", - "keyboard_shortcuts.muted": "to open muted users list", - "keyboard_shortcuts.my_profile": "to open your profile", - "keyboard_shortcuts.notifications": "to open notifications column", - "keyboard_shortcuts.pinned": "to open pinned toots list", - "keyboard_shortcuts.profile": "to open author's profile", - "keyboard_shortcuts.reply": "to reply", - "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.home": "buka linimasa beranda", + "keyboard_shortcuts.hotkey": "Pintasan", + "keyboard_shortcuts.legend": "tampilkan legenda ini", + "keyboard_shortcuts.local": "buka linimasa lokal", + "keyboard_shortcuts.mention": "sebut pencipta", + "keyboard_shortcuts.muted": "buka daftar pengguna terbisukan", + "keyboard_shortcuts.my_profile": "buka profil Anda", + "keyboard_shortcuts.notifications": "buka kolom notifikasi", + "keyboard_shortcuts.pinned": "buka daftar toot tersemat", + "keyboard_shortcuts.profile": "buka profil pencipta", + "keyboard_shortcuts.reply": "balas", + "keyboard_shortcuts.requests": "buka daftar permintaan ikuti", "keyboard_shortcuts.search": "untuk fokus mencari", - "keyboard_shortcuts.start": "to open \"get started\" column", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", - "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.start": "buka kolom \"memulai\"", + "keyboard_shortcuts.toggle_hidden": "tampilkan/sembunyikan teks di belakang CW", + "keyboard_shortcuts.toggle_sensitivity": "tampilkan/sembunyikan media", + "keyboard_shortcuts.toot": "mulai toot baru", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "Tutup", - "lightbox.next": "Next", - "lightbox.previous": "Previous", - "lightbox.view_context": "View context", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", - "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.edit.submit": "Change title", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lightbox.next": "Selanjutnya", + "lightbox.previous": "Sebelumnya", + "lightbox.view_context": "Lihat konteks", + "lists.account.add": "Tambah ke daftar", + "lists.account.remove": "Hapus dari daftar", + "lists.delete": "Hapus daftar", + "lists.edit": "Sunting daftar", + "lists.edit.submit": "Ubah judul", + "lists.new.create": "Tambah daftar", + "lists.new.title_placeholder": "Judul daftar baru", + "lists.search": "Cari di antara orang yang Anda ikuti", + "lists.subheading": "Daftar Anda", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Tunggu sebentar...", "media_gallery.toggle_visible": "Tampil/Sembunyikan", "missing_indicator.label": "Tidak ditemukan", - "missing_indicator.sublabel": "This resource could not be found", - "mute_modal.hide_notifications": "Hide notifications from this user?", - "navigation_bar.apps": "Mobile apps", + "missing_indicator.sublabel": "Sumber daya tak bisa ditemukan", + "mute_modal.hide_notifications": "Sembunyikan notifikasi dari pengguna ini?", + "navigation_bar.apps": "Aplikasi mobile", "navigation_bar.blocks": "Pengguna diblokir", "navigation_bar.community_timeline": "Linimasa lokal", - "navigation_bar.compose": "Compose new toot", - "navigation_bar.direct": "Direct messages", - "navigation_bar.discover": "Discover", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.compose": "Tulis toot baru", + "navigation_bar.direct": "Pesan langsung", + "navigation_bar.discover": "Temukan", + "navigation_bar.domain_blocks": "Domain tersembunyi", "navigation_bar.edit_profile": "Ubah profil", "navigation_bar.favourites": "Favorit", - "navigation_bar.filters": "Muted words", + "navigation_bar.filters": "Kata yang dibisukan", "navigation_bar.follow_requests": "Permintaan mengikuti", "navigation_bar.follows_and_followers": "Follows and followers", "navigation_bar.info": "Informasi selengkapnya", "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", - "navigation_bar.lists": "Lists", + "navigation_bar.lists": "Daftar", "navigation_bar.logout": "Keluar", "navigation_bar.mutes": "Pengguna dibisukan", "navigation_bar.personal": "Personal", - "navigation_bar.pins": "Pinned toots", + "navigation_bar.pins": "Toot tersemat", "navigation_bar.preferences": "Pengaturan", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Linimasa gabungan", - "navigation_bar.security": "Security", + "navigation_bar.security": "Keamanan", "notification.favourite": "{name} menyukai status anda", "notification.follow": "{name} mengikuti anda", - "notification.mention": "{name} mentioned you", - "notification.poll": "A poll you have voted in has ended", + "notification.mention": "{name} menyebut Anda", + "notification.poll": "Japat yang Anda ikuti telah berakhir", "notification.reblog": "{name} mem-boost status anda", "notifications.clear": "Hapus notifikasi", "notifications.clear_confirmation": "Apa anda yakin hendak menghapus semua notifikasi anda?", "notifications.column_settings.alert": "Notifikasi desktop", "notifications.column_settings.favourite": "Favorit:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Tampilkan semua kategori", + "notifications.column_settings.filter_bar.category": "Bilah penyaring cepat", + "notifications.column_settings.filter_bar.show": "Tampilkan", "notifications.column_settings.follow": "Pengikut baru:", "notifications.column_settings.mention": "Balasan:", - "notifications.column_settings.poll": "Poll results:", - "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.poll": "Hasil japat:", + "notifications.column_settings.push": "Notifikasi dorong", "notifications.column_settings.reblog": "Boost:", "notifications.column_settings.show": "Tampilkan dalam kolom", "notifications.column_settings.sound": "Mainkan suara", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Boosts", - "notifications.filter.favourites": "Favourites", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", - "notifications.group": "{count} notifications", - "poll.closed": "Closed", - "poll.refresh": "Refresh", + "notifications.filter.all": "Semua", + "notifications.filter.boosts": "Boost", + "notifications.filter.favourites": "Favorit", + "notifications.filter.follows": "Diikuti", + "notifications.filter.mentions": "Sebutan", + "notifications.filter.polls": "Hasil japat", + "notifications.group": "{count} notifikasi", + "poll.closed": "Ditutup", + "poll.refresh": "Segarkan", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Tentukan privasi status", @@ -295,6 +319,7 @@ "privacy.public.short": "Publik", "privacy.unlisted.long": "Tidak ditampilkan di linimasa publik", "privacy.unlisted.short": "Tak Terdaftar", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Linimasa anda sedang disiapkan!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Tampilkan semua", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.", "upload_area.title": "Seret & lepaskan untuk mengunggah", "upload_button.label": "Tambahkan media", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Deskripsikan untuk mereka yang tidak bisa melihat dengan jelas", - "upload_form.focus": "Potong", + "upload_form.edit": "Edit", "upload_form.undo": "Undo", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Mengunggah...", "video.close": "Close video", "video.exit_fullscreen": "Keluar dari layar penuh", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 2d73f9226bd45531aac217f9d5311213a7c83896..06db699cb248a6720df8b6c84ba6da0232e949ca 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -4,6 +4,7 @@ "account.block": "Blokusar @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Modifikar profilo", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Sequas tu", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Celar @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Mesaji", "account.posts_with_replies": "Toots with replies", "account.report": "Denuncar @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Ne plus sequar", "account.unmute": "Ne plus celar @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blokusita uzeri", "column.community": "Lokala tempolineo", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorati", "column.follow_requests": "Demandi di sequado", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Unpin", "column_subheading.settings": "Settings", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.", "empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Kargante...", "media_gallery.toggle_visible": "Chanjar videbleso", "missing_indicator.label": "Ne trovita", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferi", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federata tempolineo", "navigation_bar.security": "Security", "notification.favourite": "{name} favorizis tua mesajo", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Aranjar privateso di mesaji", @@ -295,6 +319,7 @@ "privacy.public.short": "Publike", "privacy.unlisted.long": "Ne montrar en publika tempolinei", "privacy.unlisted.short": "Ne enlistigota", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Montrar plue", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Tranar faligar por kargar", "upload_button.label": "Adjuntar kontenajo", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Desfacar", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Kargante...", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 9f803e3553b422d8becb4688cb414096bc41c858..c5b1d724b7ee220a944d5ba67ef7ad23c0a062b8 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -4,7 +4,8 @@ "account.block": "Blocca @{name}", "account.block_domain": "Nascondi tutto da {domain}", "account.blocked": "Bloccato", - "account.direct": "Invia messaggio diretto a @{name}", + "account.cancel_follow_request": "Annulla richiesta di seguito", + "account.direct": "Invia messaggio privato a @{name}", "account.domain_blocked": "Dominio nascosto", "account.edit_profile": "Modifica profilo", "account.endorse": "Metti in evidenza sul profilo", @@ -15,6 +16,7 @@ "account.follows.empty": "Questo utente non segue ancora nessuno.", "account.follows_you": "Ti segue", "account.hide_reblogs": "Nascondi condivisioni da @{name}", + "account.last_status": "Ultima attività ", "account.link_verified_on": "La proprietà di questo link è stata controllata il {date}", "account.locked_info": "Il livello di privacy di questo account è impostato a \"bloccato\". Il proprietario esamina manualmente le richieste di seguirlo.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Silenzia @{name}", "account.mute_notifications": "Silenzia notifiche da @{name}", "account.muted": "Silenziato", + "account.never_active": "Mai", "account.posts": "Toot", "account.posts_with_replies": "Toot e risposte", "account.report": "Segnala @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Non seguire", "account.unmute": "Non silenziare @{name}", "account.unmute_notifications": "Non silenziare più le notifiche da @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Si è verificato un errore inatteso.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per settimana", "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta", "bundle_column_error.body": "E' avvenuto un errore durante il caricamento di questo componente.", "bundle_column_error.retry": "Riprova", @@ -47,6 +53,7 @@ "column.blocks": "Utenti bloccati", "column.community": "Timeline locale", "column.direct": "Messaggi diretti", + "column.directory": "Sfoglia profili", "column.domain_blocks": "Domini nascosti", "column.favourites": "Apprezzati", "column.follow_requests": "Richieste di amicizia", @@ -66,7 +73,7 @@ "column_subheading.settings": "Impostazioni", "community.column_settings.media_only": "Solo media", "compose_form.direct_message_warning": "Questo toot sarà mandato solo a tutti gli utenti menzionati.", - "compose_form.direct_message_warning_learn_more": "Per saperne di piu'", + "compose_form.direct_message_warning_learn_more": "Per saperne di più", "compose_form.hashtag_warning": "Questo toot non è listato, quindi non sarà trovato nelle ricerche per hashtag. Solo i toot pubblici possono essere cercati per hashtag.", "compose_form.lock_disclaimer": "Il tuo account non è {bloccato}. Chiunque può decidere di seguirti per vedere i tuoi post per soli seguaci.", "compose_form.lock_disclaimer.lock": "bloccato", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Sei sicuro di voler cancellare definitivamente questa lista?", "confirmations.domain_block.confirm": "Nascondi intero dominio", "confirmations.domain_block.message": "Sei davvero sicuro che vuoi bloccare l'intero {domain}? Nella maggior parte dei casi, pochi blocchi o silenziamenti mirati sono sufficienti e preferibili. Non vedrai nessun contenuto di quel dominio né nelle timeline pubbliche né nelle notifiche. I tuoi seguaci di quel dominio saranno eliminati.", + "confirmations.logout.confirm": "Esci", + "confirmations.logout.message": "Sei sicuro di voler uscire?", "confirmations.mute.confirm": "Silenzia", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Sei sicuro di voler silenziare {name}?", "confirmations.redraft.confirm": "Cancella e riscrivi", "confirmations.redraft.message": "Sei sicuro di voler cancellare questo stato e riscriverlo? Perderai tutte le risposte, condivisioni e preferiti.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Se rispondi ora, il messaggio che stai componendo sarà sovrascritto. Sei sicuro di voler continuare?", "confirmations.unfollow.confirm": "Smetti di seguire", "confirmations.unfollow.message": "Sei sicuro che non vuoi più seguire {name}?", + "conversation.delete": "Elimina conversazione", + "conversation.mark_as_read": "Segna come letto", + "conversation.open": "Visualizza conversazione", + "conversation.with": "Con {names}", + "directory.federated": "Da un fediverso noto", + "directory.local": "Solo da {domain}", + "directory.new_arrivals": "Nuovi arrivi", + "directory.recently_active": "Attivo di recente", "embed.instructions": "Inserisci questo status nel tuo sito copiando il codice qui sotto.", "embed.preview": "Ecco come apparirà :", "emoji_button.activity": "Attività ", @@ -121,7 +139,7 @@ "empty_column.account_unavailable": "Profilo non disponibile", "empty_column.blocks": "Non hai ancora bloccato nessun utente.", "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!", - "empty_column.direct": "Non hai ancora nessun messaggio diretto. Quando ne manderai o riceverai qualcuno, apparirà qui.", + "empty_column.direct": "Non hai ancora nessun messaggio privato. Quando ne manderai o riceverai qualcuno, apparirà qui.", "empty_column.domain_blocks": "Non vi sono domini nascosti.", "empty_column.favourited_statuses": "Non hai ancora segnato nessun toot come apprezzato. Quando lo farai, comparirà qui.", "empty_column.favourites": "Nessuno ha ancora segnato questo toot come apprezzato. Quando qualcuno lo farà , apparirà qui.", @@ -134,6 +152,10 @@ "empty_column.mutes": "Non hai ancora silenziato nessun utente.", "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.", "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,7 +164,7 @@ "follow_request.authorize": "Autorizza", "follow_request.reject": "Rifiuta", "getting_started.developers": "Sviluppatori", - "getting_started.directory": "Directory del profilo", + "getting_started.directory": "Directory dei profili", "getting_started.documentation": "Documentazione", "getting_started.heading": "Come iniziare", "getting_started.invite": "Invita qualcuno", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Titolo della nuova lista", "lists.search": "Cerca tra le persone che segui", "lists.subheading": "Le tue liste", + "load_pending": "{count, plural, one {# nuovo oggetto} other {# nuovi oggetti}}", "loading_indicator.label": "Caricamento...", "media_gallery.toggle_visible": "Imposta visibilità ", "missing_indicator.label": "Non trovato", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Apprezzati", "navigation_bar.filters": "Parole silenziate", "navigation_bar.follow_requests": "Richieste di amicizia", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Seguiti e seguaci", "navigation_bar.info": "Informazioni su questo server", "navigation_bar.keyboard_shortcuts": "Tasti di scelta rapida", "navigation_bar.lists": "Liste", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personale", "navigation_bar.pins": "Toot fissati in cima", "navigation_bar.preferences": "Impostazioni", - "navigation_bar.profile_directory": "Directory dei profili", "navigation_bar.public_timeline": "Timeline federata", "navigation_bar.security": "Sicurezza", "notification.favourite": "{name} ha apprezzato il tuo post", @@ -282,19 +304,22 @@ "notifications.group": "{count} notifiche", "poll.closed": "Chiuso", "poll.refresh": "Aggiorna", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# voto} other {# voti}}", "poll.vote": "Vota", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Aggiungi un sondaggio", "poll_button.remove_poll": "Rimuovi sondaggio", "privacy.change": "Modifica privacy del post", "privacy.direct.long": "Invia solo a utenti menzionati", - "privacy.direct.short": "Diretto", + "privacy.direct.short": "Diretto in privato", "privacy.private.long": "Invia solo ai seguaci", "privacy.private.short": "Privato", "privacy.public.long": "Invia alla timeline pubblica", "privacy.public.short": "Pubblico", "privacy.unlisted.long": "Non mostrare sulla timeline pubblica", "privacy.unlisted.short": "Non elencato", + "refresh": "Refresh", "regeneration_indicator.label": "Caricamento in corso…", "regeneration_indicator.sublabel": "Stiamo preparando il tuo home feed!", "relative_time.days": "{number}g", @@ -319,6 +344,7 @@ "search_results.accounts": "Gente", "search_results.hashtags": "Hashtag", "search_results.statuses": "Toot", + "search_results.statuses_fts_disabled": "La ricerca di toot per il loro contenuto non è abilitata su questo server Mastodon.", "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}", "status.admin_account": "Apri interfaccia di moderazione per @{name}", "status.admin_status": "Apri questo status nell'interfaccia di moderazione", @@ -328,7 +354,7 @@ "status.copy": "Copia link allo status", "status.delete": "Elimina", "status.detailed_status": "Vista conversazione dettagliata", - "status.direct": "Messaggio diretto @{name}", + "status.direct": "Messaggio privato @{name}", "status.embed": "Incorpora", "status.favourite": "Apprezzato", "status.filtered": "Filtrato", @@ -358,6 +384,7 @@ "status.show_more": "Mostra di più", "status.show_more_all": "Mostra di più per tutti", "status.show_thread": "Mostra thread", + "status.uncached_media_warning": "Non disponibile", "status.unmute_conversation": "Annulla silenzia conversazione", "status.unpin": "Non fissare in cima al profilo", "suggestions.dismiss": "Elimina suggerimento", @@ -373,14 +400,22 @@ "time_remaining.moments": "Restano pochi istanti", "time_remaining.seconds": "{number, plural, one {# secondo} other {# secondi}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {persona ne sta} other {persone ne stanno}} parlando", + "trends.trending_now": "Di tendenza ora", "ui.beforeunload": "La bozza andrà persa se esci da Mastodon.", "upload_area.title": "Trascina per caricare", "upload_button.label": "Aggiungi file multimediale", "upload_error.limit": "Limite al caricamento di file superato.", "upload_error.poll": "Caricamento file non consentito nei sondaggi.", "upload_form.description": "Descrizione per utenti con disabilità visive", - "upload_form.focus": "Modifica anteprima", + "upload_form.edit": "Modifica", "upload_form.undo": "Cancella", + "upload_modal.analyzing_picture": "Analisi immagine…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "Ma la volpe col suo balzo ha raggiunto il quieto Fido", + "upload_modal.detect_text": "Rileva testo dall'immagine", + "upload_modal.edit_media": "Modifica media", + "upload_modal.hint": "Clicca o trascina il cerchio sull'anteprima per scegliere il punto focale che sarà sempre visualizzato su tutte le miniature.", + "upload_modal.preview_label": "Anteprima ({ratio})", "upload_progress.label": "Sto caricando...", "video.close": "Chiudi video", "video.exit_fullscreen": "Esci da modalità a schermo intero", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index c9dd98e7613fa746ade0663dff94b93e2f8885dc..f6026824c12d4f031489a3e64f3a6ecc9dab4ba3 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -4,6 +4,7 @@ "account.block": "@{name}ã•んをブãƒãƒƒã‚¯", "account.block_domain": "{domain}全体をéžè¡¨ç¤º", "account.blocked": "ブãƒãƒƒã‚¯æ¸ˆã¿", + "account.cancel_follow_request": "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’å–り消ã™", "account.direct": "@{name}ã•ã‚“ã«ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸", "account.domain_blocked": "ドメインéžè¡¨ç¤ºä¸", "account.edit_profile": "プãƒãƒ•ィール編集", @@ -15,6 +16,7 @@ "account.follows.empty": "ã¾ã 誰もフォãƒãƒ¼ã—ã¦ã„ã¾ã›ã‚“。", "account.follows_you": "フォãƒãƒ¼ã•れã¦ã„ã¾ã™", "account.hide_reblogs": "@{name}ã•ã‚“ã‹ã‚‰ã®ãƒ–ーストをéžè¡¨ç¤º", + "account.last_status": "æœ€å¾Œã®æ´»å‹•", "account.link_verified_on": "ã“ã®ãƒªãƒ³ã‚¯ã®æ‰€æœ‰æ¨©ã¯{date}ã«ç¢ºèªã•れã¾ã—ãŸ", "account.locked_info": "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯æ‰¿èªåˆ¶ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã™ã€‚ç›¸æ‰‹ãŒæ‰¿èªã™ã‚‹ã¾ã§ãƒ•ã‚©ãƒãƒ¼ã¯å®Œäº†ã—ã¾ã›ã‚“。", "account.media": "メディア", @@ -23,6 +25,7 @@ "account.mute": "@{name}ã•んをミュート", "account.mute_notifications": "@{name}ã•ã‚“ã‹ã‚‰ã®é€šçŸ¥ã‚’å—ã‘å–らãªã„", "account.muted": "ミュート済ã¿", + "account.never_active": "活動ãªã—", "account.posts": "投稿", "account.posts_with_replies": "投稿ã¨è¿”ä¿¡", "account.report": "@{name}ã•ã‚“ã‚’é€šå ±", @@ -35,8 +38,11 @@ "account.unfollow": "フォãƒãƒ¼è§£é™¤", "account.unmute": "@{name}ã•ã‚“ã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除", "account.unmute_notifications": "@{name}ã•ã‚“ã‹ã‚‰ã®é€šçŸ¥ã‚’å—ã‘å–るよã†ã«ã™ã‚‹", + "alert.rate_limited.message": "{retry_time, time, medium} 以é™ã«å†åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。", + "alert.rate_limited.title": "制é™ã«é”ã—ã¾ã—ãŸ", "alert.unexpected.message": "䏿˜Žãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚", "alert.unexpected.title": "エラーï¼", + "autosuggest_hashtag.per_week": "{count} 回 / 週", "boost_modal.combo": "次ã‹ã‚‰ã¯{combo}を押ã›ã°ã‚¹ã‚ップã§ãã¾ã™", "bundle_column_error.body": "コンãƒãƒ¼ãƒãƒ³ãƒˆã®èªã¿è¾¼ã¿ä¸ã«å•題ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚", "bundle_column_error.retry": "å†è©¦è¡Œ", @@ -47,6 +53,7 @@ "column.blocks": "ブãƒãƒƒã‚¯ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼", "column.community": "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ライン", "column.direct": "ダイレクトメッセージ", + "column.directory": "ディレクトリ", "column.domain_blocks": "éžè¡¨ç¤ºã«ã—ãŸãƒ‰ãƒ¡ã‚¤ãƒ³", "column.favourites": "ãŠæ°—ã«å…¥ã‚Š", "column.follow_requests": "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆ", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "本当ã«ã“ã®ãƒªã‚¹ãƒˆã‚’完全ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ", "confirmations.domain_block.confirm": "ドメイン全体をéžè¡¨ç¤º", "confirmations.domain_block.message": "本当ã«{domain}全体をéžè¡¨ç¤ºã«ã—ã¾ã™ã‹ï¼Ÿ 多ãã®å ´åˆã¯å€‹åˆ¥ã«ãƒ–ãƒãƒƒã‚¯ã‚„ミュートã™ã‚‹ã ã‘ã§å……分ã§ã‚りã€ã¾ãŸå¥½ã¾ã—ã„ã§ã™ã€‚公開タイムラインã«ãã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒè¡¨ç¤ºã•れãªããªã‚Šã€é€šçŸ¥ã‚‚届ã‹ãªããªã‚Šã¾ã™ã€‚ãã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯ã‚¢ãƒ³ãƒ•ã‚©ãƒãƒ¼ã•れã¾ã™ã€‚", + "confirmations.logout.confirm": "ãƒã‚°ã‚¢ã‚¦ãƒˆ", + "confirmations.logout.message": "本当ã«ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã™ã‹ï¼Ÿ", "confirmations.mute.confirm": "ミュート", + "confirmations.mute.explanation": "ã“れã«ã‚ˆã‚Šç›¸æ‰‹ã®ãƒˆã‚¥ãƒ¼ãƒˆã¨è¿”ä¿¡ã¯è¦‹ãˆãªããªã‚Šã¾ã™ãŒã€å¼•ãç¶šãã‚ãªãŸã‚’フォãƒãƒ¼ã—トゥートを見るã“ã¨ã¯ã§ãã¾ã™ã€‚", "confirmations.mute.message": "本当ã«{name}ã•んをミュートã—ã¾ã™ã‹ï¼Ÿ", "confirmations.redraft.confirm": "削除ã—ã¦ä¸‹æ›¸ãã«æˆ»ã™", "confirmations.redraft.message": "本当ã«ã“ã®ãƒˆã‚¥ãƒ¼ãƒˆã‚’削除ã—ã¦ä¸‹æ›¸ãã«æˆ»ã—ã¾ã™ã‹ï¼Ÿ ã“ã®ãƒˆã‚¥ãƒ¼ãƒˆã¸ã®ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã‚„ブーストã¯å¤±ã‚れã€è¿”ä¿¡ã¯å¤ç«‹ã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚", @@ -101,6 +111,14 @@ "confirmations.reply.message": "今返信ã™ã‚‹ã¨ç¾åœ¨ä½œæˆä¸ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒä¸Šæ›¸ãã•れã¾ã™ã€‚本当ã«å®Ÿè¡Œã—ã¾ã™ã‹ï¼Ÿ", "confirmations.unfollow.confirm": "フォãƒãƒ¼è§£é™¤", "confirmations.unfollow.message": "本当ã«{name}ã•ã‚“ã®ãƒ•ã‚©ãƒãƒ¼ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ", + "conversation.delete": "会話を削除", + "conversation.mark_as_read": "æ—¢èªã«ã™ã‚‹", + "conversation.open": "会話を表示", + "conversation.with": "{names}", + "directory.federated": "既知ã®é€£åˆã‚ˆã‚Š", + "directory.local": "{domain} ã®ã¿", + "directory.new_arrivals": "æ–°ç€é †", + "directory.recently_active": "æœ€è¿‘ã®æ´»å‹•é †", "embed.instructions": "下記ã®ã‚³ãƒ¼ãƒ‰ã‚’コピーã—ã¦ã‚¦ã‚§ãƒ–サイトã«åŸ‹ã‚è¾¼ã¿ã¾ã™ã€‚", "embed.preview": "表示例:", "emoji_button.activity": "活動", @@ -134,6 +152,10 @@ "empty_column.mutes": "ã¾ã 誰もミュートã—ã¦ã„ã¾ã›ã‚“。", "empty_column.notifications": "ã¾ã 通知ãŒã‚りã¾ã›ã‚“。他ã®äººã¨ãµã‚Œåˆã£ã¦ä¼šè©±ã‚’å§‹ã‚ã¾ã—ょã†ã€‚", "empty_column.public": "ã“ã“ã«ã¯ã¾ã 何もã‚りã¾ã›ã‚“ï¼ å…¬é–‹ã§ä½•ã‹ã‚’投稿ã—ãŸã‚Šã€ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フォãƒãƒ¼ã—ãŸã‚Šã—ã¦ã„ã£ã±ã„ã«ã—ã¾ã—ょã†", + "error.unexpected_crash.explanation": "ä¸å…·åˆã‹ãƒ–ラウザã®äº’æ›æ€§å•題ã®ãŸã‚ã€ã“ã®ãƒšãƒ¼ã‚¸ã‚’æ£ã—ã表示ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚", + "error.unexpected_crash.next_steps": "ページã®å†èªã¿è¾¼ã¿ã‚’ãŠè©¦ã—ãã ã•ã„。ãれã§ã‚‚解決ã—ãªã„å ´åˆã€åˆ¥ã®ãƒ–ラウザã‹ã‚¢ãƒ—リを使ãˆã°ä½¿ç”¨ã§ãã‚‹ã“ã¨ãŒã‚りã¾ã™ã€‚", + "errors.unexpected_crash.copy_stacktrace": "スタックトレースをクリップボードã«ã‚³ãƒ”ー", + "errors.unexpected_crash.report_issue": "å•é¡Œã‚’å ±å‘Š", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -147,7 +169,7 @@ "getting_started.heading": "スタート", "getting_started.invite": "招待", "getting_started.open_source_notice": "Mastodonã¯ã‚ªãƒ¼ãƒ—ンソースソフトウェアã§ã™ã€‚誰ã§ã‚‚GitHub ( {github} ) ã‹ã‚‰é–‹ç™ºã«å‚åŠ ã—ãŸã‚Šã€å•é¡Œã‚’å ±å‘Šã—ãŸã‚Šã§ãã¾ã™ã€‚", - "getting_started.security": "ã‚»ã‚ュリティ", + "getting_started.security": "アカウントè¨å®š", "getting_started.terms": "プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼", "hashtag.column_header.tag_mode.all": "㨠{additional}", "hashtag.column_header.tag_mode.any": "ã‹ {additional}", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "æ–°è¦ãƒªã‚¹ãƒˆå", "lists.search": "フォãƒãƒ¼ã—ã¦ã„る人ã®ä¸ã‹ã‚‰æ¤œç´¢", "lists.subheading": "ã‚ãªãŸã®ãƒªã‚¹ãƒˆ", + "load_pending": "{count} ä»¶ã®æ–°ç€", "loading_indicator.label": "èªã¿è¾¼ã¿ä¸...", "media_gallery.toggle_visible": "表示切り替ãˆ", "missing_indicator.label": "見ã¤ã‹ã‚Šã¾ã›ã‚“", @@ -251,7 +274,6 @@ "navigation_bar.personal": "個人用", "navigation_bar.pins": "固定ã—ãŸãƒˆã‚¥ãƒ¼ãƒˆ", "navigation_bar.preferences": "ユーザーè¨å®š", - "navigation_bar.profile_directory": "ディレクトリ", "navigation_bar.public_timeline": "連åˆã‚¿ã‚¤ãƒ ライン", "navigation_bar.security": "ã‚»ã‚ュリティ", "notification.favourite": "{name}ã•ã‚“ãŒã‚ãªãŸã®ãƒˆã‚¥ãƒ¼ãƒˆã‚’ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—ã¾ã—ãŸ", @@ -282,8 +304,10 @@ "notifications.group": "{count} ä»¶ã®é€šçŸ¥", "poll.closed": "終了", "poll.refresh": "æ›´æ–°", + "poll.total_people": "{count}人", "poll.total_votes": "{count}票", "poll.vote": "投票", + "poll.voted": "ã“ã®é …ç›®ã«æŠ•ç¥¨ã—ã¾ã—ãŸ", "poll_button.add_poll": "ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã‚’è¿½åŠ ", "poll_button.remove_poll": "アンケートを削除", "privacy.change": "公開範囲を変更", @@ -295,6 +319,7 @@ "privacy.public.short": "公開", "privacy.unlisted.long": "公開TLã§è¡¨ç¤ºã—ãªã„", "privacy.unlisted.short": "未åŽè¼‰", + "refresh": "æ›´æ–°", "regeneration_indicator.label": "èªã¿è¾¼ã¿ä¸â€¦", "regeneration_indicator.sublabel": "ãƒ›ãƒ¼ãƒ ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã¯æº–å‚™ä¸ã§ã™ï¼", "relative_time.days": "{number}æ—¥å‰", @@ -319,6 +344,7 @@ "search_results.accounts": "人々", "search_results.hashtags": "ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°", "search_results.statuses": "トゥート", + "search_results.statuses_fts_disabled": "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ã¯ãƒˆã‚¥ãƒ¼ãƒˆæœ¬æ–‡ã®æ¤œç´¢ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。", "search_results.total": "{count, number}ä»¶ã®çµæžœ", "status.admin_account": "@{name} ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ç”»é¢ã‚’é–‹ã", "status.admin_status": "ã“ã®ãƒˆã‚¥ãƒ¼ãƒˆã‚’モデレーション画é¢ã§é–‹ã", @@ -358,6 +384,7 @@ "status.show_more": "ã‚‚ã£ã¨è¦‹ã‚‹", "status.show_more_all": "å…¨ã¦è¦‹ã‚‹", "status.show_thread": "スレッドを表示", + "status.uncached_media_warning": "利用ã§ãã¾ã›ã‚“", "status.unmute_conversation": "会話ã®ãƒŸãƒ¥ãƒ¼ãƒˆã‚’解除", "status.unpin": "プãƒãƒ•ィールã¸ã®å›ºå®šã‚’解除", "suggestions.dismiss": "éš ã™", @@ -373,14 +400,22 @@ "time_remaining.moments": "ã¾ã‚‚ãªã終了", "time_remaining.seconds": "残り{number}ç§’", "trends.count_by_accounts": "{count}人ãŒãƒˆã‚¥ãƒ¼ãƒˆ", + "trends.trending_now": "トレンドタグ", "ui.beforeunload": "Mastodonã‹ã‚‰é›¢ã‚Œã‚‹ã¨é€ä¿¡å‰ã®æŠ•稿ã¯å¤±ã‚れã¾ã™ã€‚", "upload_area.title": "ドラッグ&ドãƒãƒƒãƒ—ã§ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰", "upload_button.label": "ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’è¿½åŠ ({formats})", "upload_error.limit": "アップãƒãƒ¼ãƒ‰ã§ãる上é™ã‚’è¶…ãˆã¦ã„ã¾ã™ã€‚", "upload_error.poll": "アンケートã§ã¯ãƒ•ァイルをアップãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“。", "upload_form.description": "視覚障害者ã®ãŸã‚ã®èª¬æ˜Ž", - "upload_form.focus": "プレビューを変更", + "upload_form.edit": "編集", "upload_form.undo": "削除", + "upload_modal.analyzing_picture": "ç”»åƒã‚’è§£æžä¸â€¦", + "upload_modal.apply": "é©ç”¨", + "upload_modal.description_placeholder": "ã‚ã®ã‚¤ãƒ¼ãƒãƒˆãƒ¼ãƒ´ã‚©ã®ã™ãã¨ãŠã£ãŸé¢¨", + "upload_modal.detect_text": "ç”»åƒã‹ã‚‰ãƒ†ã‚ストを検出", + "upload_modal.edit_media": "メディアを編集", + "upload_modal.hint": "サムãƒã‚¤ãƒ«ã®ç„¦ç‚¹ã«ã—ãŸã„å ´æ‰€ã‚’ã‚¯ãƒªãƒƒã‚¯ã™ã‚‹ã‹å††å½¢ã®æž ã‚’ãã®å ´æ‰€ã«ãƒ‰ãƒ©ãƒƒã‚°ã—ã¦ãã ã•ã„。", + "upload_modal.preview_label": "プレビュー ({ratio})", "upload_progress.label": "アップãƒãƒ¼ãƒ‰ä¸...", "video.close": "動画を閉ã˜ã‚‹", "video.exit_fullscreen": "全画é¢ã‚’終了ã™ã‚‹", diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json index b18fa7210d7ba7bf29e14cb3b71fa9cc2d7b9978..c378bc9c88916e28c398a8c659d611f095eed645 100644 --- a/app/javascript/mastodon/locales/ka.json +++ b/app/javascript/mastodon/locales/ka.json @@ -4,6 +4,7 @@ "account.block": "დáƒáƒ‘ლáƒáƒ™áƒ” @{name}", "account.block_domain": "დáƒáƒ˜áƒ›áƒáƒšáƒáƒ¡ ყველáƒáƒ¤áƒ”რი დáƒáƒ›áƒ”ნიდáƒáƒœ {domain}", "account.blocked": "დáƒáƒ˜áƒ‘ლáƒáƒ™áƒ", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "პირდáƒáƒžáƒ˜áƒ ი წერილი @{name}-ს", "account.domain_blocked": "დáƒáƒ›áƒ”ნი დáƒáƒ›áƒáƒšáƒ£áƒšáƒ˜áƒ", "account.edit_profile": "პრáƒáƒ¤áƒ˜áƒšáƒ˜áƒ¡ ცვლილებáƒ", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "მáƒáƒ’ყვებáƒáƒ—", "account.hide_reblogs": "დáƒáƒ˜áƒ›áƒáƒšáƒáƒ¡ ბუსტები @{name}-სგáƒáƒœ", + "account.last_status": "Last active", "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.media": "მედიáƒ", @@ -23,6 +25,7 @@ "account.mute": "გáƒáƒáƒ©áƒ£áƒ›áƒ” @{name}", "account.mute_notifications": "გáƒáƒáƒ©áƒ£áƒ›áƒ” შეტყáƒáƒ‘ინებები @{name}-სგáƒáƒœ", "account.muted": "გáƒáƒ©áƒ£áƒ›áƒ”ბული", + "account.never_active": "Never", "account.posts": "ტუტები", "account.posts_with_replies": "ტუტები დრპáƒáƒ¡áƒ£áƒ®áƒ”ბი", "account.report": "დáƒáƒáƒ ეპáƒáƒ ტე @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "ნუღáƒáƒ მიჰყვები", "account.unmute": "ნუღáƒáƒ áƒáƒ©áƒ£áƒ›áƒ”ბ @{name}-ს", "account.unmute_notifications": "ნუღáƒáƒ áƒáƒ©áƒ£áƒ›áƒ”ბ შეტყáƒáƒ‘ინებებს @{name}-სგáƒáƒœ", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "წáƒáƒ მáƒáƒ˜áƒ¨áƒ•რმáƒáƒ£áƒšáƒáƒ“ნელი შეცდáƒáƒ›áƒ.", "alert.unexpected.title": "უპს!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "შეგიძლიáƒáƒ— დáƒáƒáƒáƒ˜áƒ áƒáƒ— {combo}-ს რáƒáƒ—რშემდეგ ჯერზე გáƒáƒ›áƒáƒ¢áƒáƒ•áƒáƒ— ეს", "bundle_column_error.body": "áƒáƒ› კáƒáƒ›áƒžáƒáƒœáƒ”ნტის ჩáƒáƒ¢áƒ•ირთვისáƒáƒ¡ რáƒáƒ¦áƒáƒª áƒáƒ˜áƒ იáƒ.", "bundle_column_error.retry": "სცáƒáƒ“ეთ კიდევ ერთხელ", @@ -47,6 +53,7 @@ "column.blocks": "დáƒáƒ‘ლáƒáƒ™áƒ˜áƒšáƒ˜ მáƒáƒ›áƒ®áƒ›áƒáƒ ებლები", "column.community": "ლáƒáƒ™áƒáƒšáƒ£áƒ ი თáƒáƒ˜áƒ›áƒšáƒáƒ˜áƒœáƒ˜", "column.direct": "პირდáƒáƒžáƒ˜áƒ ი წერილები", + "column.directory": "Browse profiles", "column.domain_blocks": "დáƒáƒ›áƒáƒšáƒ£áƒšáƒ˜ დáƒáƒ›áƒ”ნები", "column.favourites": "ფáƒáƒ•áƒáƒ იტები", "column.follow_requests": "დáƒáƒ“ევნების მáƒáƒ—ხáƒáƒ•ნები", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "დáƒáƒ წმუნებული ხáƒáƒ თ, გსურთ სáƒáƒ›áƒ£áƒ“áƒáƒ›áƒáƒ“ გáƒáƒáƒ£áƒ¥áƒ›áƒáƒ— ეს სიáƒ?", "confirmations.domain_block.confirm": "მთელი დáƒáƒ›áƒ”ნის დáƒáƒ›áƒáƒšáƒ•áƒ", "confirmations.domain_block.message": "ნáƒáƒ¦áƒ“áƒáƒ“, ნáƒáƒ¦áƒ“áƒáƒ“, დáƒáƒ წმუნებული ხáƒáƒ თ, გსურთ დáƒáƒ‘ლáƒáƒ™áƒáƒ— მთელი {domain}? უმეტეს შემთხვევáƒáƒ¨áƒ˜ რáƒáƒ›áƒ“ენიმე გáƒáƒ›áƒ˜áƒ–ნული ბლáƒáƒ™áƒ˜ áƒáƒœ გáƒáƒ©áƒ£áƒ›áƒ”ბრსáƒáƒ™áƒ›áƒáƒ ისი დრუკეთესიáƒ. კáƒáƒœáƒ¢áƒ”ნტს áƒáƒ› დáƒáƒ›áƒ”ნიდáƒáƒœ ვერიხილáƒáƒ•თ ვერც ერთ ღირთáƒáƒ˜áƒ›áƒšáƒáƒ˜áƒœáƒ–ე áƒáƒœ თქვენს შეტყáƒáƒ‘ინებებში. áƒáƒ› დáƒáƒ›áƒ”ნიდáƒáƒœ áƒáƒ სებული მიმდევრები áƒáƒ›áƒáƒ˜áƒ¨áƒšáƒ”ბáƒ.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "გáƒáƒ©áƒ£áƒ›áƒ”ბáƒ", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "დáƒáƒ წმუნებული ხáƒáƒ თ, გსურთ გáƒáƒáƒ©áƒ£áƒ›áƒáƒ— {name}?", "confirmations.redraft.confirm": "გáƒáƒ£áƒ¥áƒ›áƒ”ბრდრგáƒáƒ“áƒáƒœáƒáƒ¬áƒ˜áƒšáƒ”ბáƒ", "confirmations.redraft.message": "დáƒáƒ წმუნებული ხáƒáƒ თ, გსურთ გáƒáƒáƒ£áƒ¥áƒ›áƒáƒ— ეს სტáƒáƒ¢áƒ£áƒ¡áƒ˜ დრგáƒáƒ“áƒáƒáƒœáƒáƒ¬áƒ˜áƒšáƒáƒ—? დáƒáƒ™áƒáƒ გáƒáƒ•თ ყველრპáƒáƒ¡áƒ£áƒ®áƒ¡, ბუსტს დრმáƒáƒ¡áƒ–ედ áƒáƒ სებულ ფáƒáƒ•áƒáƒ იტს.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "ნუღáƒáƒ მიჰყვები", "confirmations.unfollow.message": "დáƒáƒ წმუნებული ხáƒáƒ თ, áƒáƒ¦áƒáƒ გსურთ მიჰყვებáƒáƒ“ეთ {name}-ს?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "ეს სტáƒáƒ¢áƒ£áƒ¡áƒ˜ ჩáƒáƒ¡áƒ•ით თქვენს ვებ-სáƒáƒ˜áƒ¢áƒ–ე შემდეგი კáƒáƒ“ის კáƒáƒžáƒ˜áƒ ებით.", "embed.preview": "ესáƒáƒ თუ რáƒáƒ’áƒáƒ ც გáƒáƒ›áƒáƒ©áƒœáƒ“ებáƒ:", "emoji_button.activity": "áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘áƒ", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "ჯერშეტყáƒáƒ‘ინებები áƒáƒ გáƒáƒ¥áƒ•თ. სáƒáƒ£áƒ‘რის დáƒáƒ¡áƒáƒ¬áƒ§áƒ”ბáƒáƒ“ იურთიერთქმედეთ სხვებთáƒáƒœ.", "empty_column.public": "áƒáƒ¥ áƒáƒ áƒáƒ¤áƒ”რიáƒ! შესáƒáƒ•სებáƒáƒ“, დáƒáƒ¬áƒ”რეთ რáƒáƒ˜áƒ›áƒ” ღიáƒáƒ“ áƒáƒœ ხელით გáƒáƒ°áƒ§áƒ”ვით მáƒáƒ›áƒ®áƒ›áƒáƒ ებლებს სხვრინსტáƒáƒœáƒªáƒ˜áƒ”ბისგáƒáƒœ", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "áƒáƒ®áƒáƒšáƒ˜ სიის სáƒáƒ—áƒáƒ£áƒ ი", "lists.search": "ძებნრáƒáƒ“áƒáƒ›áƒ˜áƒáƒœáƒ”ბს შáƒáƒ ის რáƒáƒ›áƒ”ლთáƒáƒª მიჰყვებით", "lists.subheading": "თქვენი სიები", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "იტვირთებáƒ...", "media_gallery.toggle_visible": "ხილვáƒáƒ“áƒáƒ‘ის ჩáƒáƒ თვáƒ", "missing_indicator.label": "áƒáƒ áƒáƒ ნáƒáƒžáƒáƒ•ნი", @@ -251,7 +274,6 @@ "navigation_bar.personal": "პირáƒáƒ“ი", "navigation_bar.pins": "áƒáƒžáƒ˜áƒœáƒ£áƒšáƒ˜ ტუტები", "navigation_bar.preferences": "პრეფერენსიები", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "ფედერáƒáƒšáƒ£áƒ ი თáƒáƒ˜áƒ›áƒšáƒáƒ˜áƒœáƒ˜", "navigation_bar.security": "უსáƒáƒ¤áƒ თხáƒáƒ”ბáƒ", "notification.favourite": "{name}-მრთქვენი სტáƒáƒ¢áƒ£áƒ¡áƒ˜ áƒáƒ¥áƒªáƒ˜áƒ ფáƒáƒ•áƒáƒ იტáƒáƒ“", @@ -282,8 +304,10 @@ "notifications.group": "{count} შეტყáƒáƒ‘ინებáƒ", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "სტáƒáƒ¢áƒ£áƒ¡áƒ˜áƒ¡ კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ‘ის მითითებáƒ", @@ -295,6 +319,7 @@ "privacy.public.short": "სáƒáƒ¯áƒáƒ áƒ", "privacy.unlisted.long": "áƒáƒ დáƒáƒ˜áƒžáƒáƒ¡áƒ¢áƒáƒ¡ სáƒáƒ¯áƒáƒ რთáƒáƒ˜áƒ›áƒšáƒáƒ˜áƒœáƒ”ბზე", "privacy.unlisted.short": "ჩáƒáƒ›áƒáƒ£áƒ—ვლელი", + "refresh": "Refresh", "regeneration_indicator.label": "იტვირთებáƒâ€¦", "regeneration_indicator.sublabel": "თქვენი სáƒáƒ®áƒšáƒ˜áƒ¡ ლენტრმზáƒáƒ“დებáƒ!", "relative_time.days": "{number}დღ", @@ -319,6 +344,7 @@ "search_results.accounts": "ხáƒáƒšáƒ®áƒ˜", "search_results.hashtags": "ჰეშტეგები", "search_results.statuses": "ტუტები", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "áƒáƒ©áƒ•ენე მეტი", "status.show_more_all": "áƒáƒ©áƒ•ენე მეტი ყველáƒáƒ–ე", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "სáƒáƒ£áƒ‘áƒáƒ ზე გáƒáƒ©áƒ£áƒ›áƒ”ბის მáƒáƒ¨áƒáƒ ებáƒ", "status.unpin": "პრáƒáƒ¤áƒ˜áƒšáƒ˜áƒ“áƒáƒœ პინის მáƒáƒ¨áƒáƒ ებáƒ", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} სáƒáƒ£áƒ‘რáƒáƒ‘ს", + "trends.trending_now": "Trending now", "ui.beforeunload": "თქვენი დრáƒáƒ¤áƒ¢áƒ˜ გáƒáƒ£áƒ¥áƒ›áƒ“ებრთუ დáƒáƒ¢áƒáƒ•ებთ მáƒáƒ¡áƒ¢áƒáƒ“áƒáƒœáƒ¡.", "upload_area.title": "გáƒáƒ“მáƒáƒ¬áƒ˜áƒ”თ დრჩáƒáƒáƒ’დეთ áƒáƒ¡áƒáƒ¢áƒ•ირთáƒáƒ—", "upload_button.label": "მედიის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "áƒáƒ¦áƒ¬áƒ”რილáƒáƒ‘რვიზუáƒáƒšáƒ£áƒ áƒáƒ“ უფáƒáƒ¡áƒ£áƒ ისთვის", - "upload_form.focus": "კრáƒáƒžáƒ˜", + "upload_form.edit": "Edit", "upload_form.undo": "გáƒáƒ£áƒ¥áƒ›áƒ”ბáƒ", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "იტვირთებáƒ...", "video.close": "ვიდეáƒáƒ¡ დáƒáƒ®áƒ£áƒ ვáƒ", "video.exit_fullscreen": "სრულ ეკრáƒáƒœáƒ–ე ჩვენების გáƒáƒ—იშვáƒ", diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json index f551783a7de4af7022b3dfcf076b01720c40fc3f..be0a5050397af653ffc7d9a3d66f29bae882d38b 100644 --- a/app/javascript/mastodon/locales/kk.json +++ b/app/javascript/mastodon/locales/kk.json @@ -4,6 +4,7 @@ "account.block": "Бұғаттау @{name}", "account.block_domain": "Домендегі барлығын бұғатта {domain}", "account.blocked": "Бұғатталды", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Жеке хат @{name}", "account.domain_blocked": "Домен жабық", "account.edit_profile": "Профильді өңдеу", @@ -15,6 +16,7 @@ "account.follows.empty": "Ешкімге жазылмапты.", "account.follows_you": "Сізге жазылыпты", "account.hide_reblogs": "@{name} атты қолданушының әрекеттерін жаÑыру", + "account.last_status": "Last active", "account.link_verified_on": "Сілтеме меншігі раÑталған күн {date}", "account.locked_info": "Бұл қолданушы өзі туралы мәліметтерді жаÑырған. Тек жазылғандар ғана көре алады.", "account.media": "Медиа", @@ -23,6 +25,7 @@ "account.mute": "ҮнÑіз қылу @{name}", "account.mute_notifications": "@{name} туралы еÑкертпелерді жаÑыру", "account.muted": "ҮнÑіз", + "account.never_active": "Never", "account.posts": "Жазбалар", "account.posts_with_replies": "Жазбалар мен жауаптар", "account.report": "Шағымдану @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Оқымау", "account.unmute": "@{name} еÑкертпелерін қоÑу", "account.unmute_notifications": "@{name} еÑкертпелерін көрÑету", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Бір нәрÑе Ð´Ò±Ñ€Ñ‹Ñ Ð±Ð¾Ð»Ð¼Ð°Ð´Ñ‹.", "alert.unexpected.title": "Өй!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "КелеÑіде өткізіп жіберу үшін баÑыңыз {combo}", "bundle_column_error.body": "Бұл компонентті жүктеген кезде бір қате пайда болды.", "bundle_column_error.retry": "Қайтадан көріңіз", @@ -47,6 +53,7 @@ "column.blocks": "Бұғатталғандар", "column.community": "Жергілікті желі", "column.direct": "Жеке хаттар", + "column.directory": "Browse profiles", "column.domain_blocks": "ЖаÑырылған домендер", "column.favourites": "Таңдаулылар", "column.follow_requests": "Жазылу Ñұранымдары", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Бұл тізімді жоÑÑыз ба шынымен?", "confirmations.domain_block.confirm": "Бұл доменді бұғатта", "confirmations.domain_block.message": "Бұл домендегі {domain} жазбаларды шынымен бұғаттайÑыз ба? Кейде үнÑіз қылып таÑтау да жеткілікті.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "ҮнÑіз қылу", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "{name} атты қолданушы үнÑіз болÑын ба?", "confirmations.redraft.confirm": "Өшіруді құптау", "confirmations.redraft.message": "Бұл жазбаны өшіріп, нобайларға жібереміз бе? Барлық жауаптар мен лайктарды жоғалтаÑыз.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Жауабыңыз жазып жатқан жазбаңыздың Ò¯Ñтіне кетеді. ЖалғаÑтырамыз ба?", "confirmations.unfollow.confirm": "Оқымау", "confirmations.unfollow.message": "\"{name} атты қолданушыға енді жазылғыңыз келмей ме?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Төмендегі кодты көшіріп алу арқылы жазбаны баÑқа Ñайттарға да орналаÑтыра алаÑыз.", "embed.preview": "Былай көрінетін болады:", "emoji_button.activity": "БелÑенділік", @@ -134,6 +152,10 @@ "empty_column.mutes": "Әзірше ешқандай үнÑізге қойылған қолданушы жоқ.", "empty_column.notifications": "Әзірше ешқандай еÑкертпе жоқ. БаÑқалармен аралаÑуды баÑтаңыз және пікірталаÑтарға қатыÑыңыз.", "empty_column.public": "Ештеңе жоқ бұл жерде! Өзіңіз баÑтап жазып көріңіз немеÑе баÑқаларға жазылыңыз", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Жаңа тізім аты", "lists.search": "Сіз іздеген адамдар араÑында іздеу", "lists.subheading": "Тізімдеріңіз", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Жүктеу...", "media_gallery.toggle_visible": "Көрінуді қоÑу", "missing_indicator.label": "Табылмады", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Жеке", "navigation_bar.pins": "ЖабыÑтырылғандар", "navigation_bar.preferences": "БаÑымдықтар", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Жаһандық желі", "navigation_bar.security": "ҚауіпÑіздік", "notification.favourite": "{name} жазбаңызды таңдаулыға қоÑты", @@ -282,8 +304,10 @@ "notifications.group": "{count} еÑкертпе", "poll.closed": "Жабық", "poll.refresh": "Жаңарту", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# дауыÑ} other {# дауыÑ}}", "poll.vote": "Ð”Ð°ÑƒÑ‹Ñ Ð±ÐµÑ€Ñƒ", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Сауалнама қоÑу", "poll_button.remove_poll": "Сауалнаманы өшіру", "privacy.change": "ҚұпиÑлылықты реттеу", @@ -295,6 +319,7 @@ "privacy.public.short": "Ðшық", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "ТізімÑіз", + "refresh": "Refresh", "regeneration_indicator.label": "Жүктеу…", "regeneration_indicator.sublabel": "Жергілікті желі құрылуда!", "relative_time.days": "{number}күн", @@ -319,6 +344,7 @@ "search_results.accounts": "Ðдамдар", "search_results.hashtags": "Ð¥Ñштегтер", "search_results.statuses": "Жазбалар", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "@{name} үшін Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñін аш", "status.admin_status": "Бұл жазбаны Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñінде аш", @@ -358,6 +384,7 @@ "status.show_more": "Толығырақ", "status.show_more_all": "Бәрін толығымен", "status.show_thread": "Желіні көрÑет", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "ПікірталаÑты үнÑіз қылмау", "status.unpin": "Профильден алып таÑтау", "suggestions.dismiss": "Өткізіп жіберу", @@ -373,14 +400,22 @@ "time_remaining.moments": "Қалған уақыт", "time_remaining.seconds": "{number, plural, one {# Ñекунд} other {# Ñекунд}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} жазған екен", + "trends.trending_now": "Trending now", "ui.beforeunload": "Mastodon желіÑінен шықÑаңыз, нобайыңыз Ñақталмайды.", "upload_area.title": "Жүктеу үшін Ñүйреп әкеліңіз", "upload_button.label": "Медиа қоÑу (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Файл жүктеу лимитінен аÑып кеттіңіз.", "upload_error.poll": "Сауалнамамен бірге файл жүктеуге болмайды.", "upload_form.description": "Көру қабілеті нашар адамдар үшін Ñипаттаңыз", - "upload_form.focus": "Превьюді өзгерту", + "upload_form.edit": "Edit", "upload_form.undo": "Өшіру", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Жүктеп жатыр...", "video.close": "Видеоны жабу", "video.exit_fullscreen": "Толық Ñкраннан шық", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index f5977d0c669164524db5cb23b38b883418ae61a9..2640b43b415dbdf8323b65feae11678735ac8032 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -4,6 +4,7 @@ "account.block": "@{name}ì„ ì°¨ë‹¨", "account.block_domain": "{domain} ì „ì²´ë¥¼ 숨김", "account.blocked": "차단 ë¨", + "account.cancel_follow_request": "팔로우 ìš”ì² ì·¨ì†Œ", "account.direct": "@{name}ìœ¼ë¡œë¶€í„°ì˜ ë‹¤ì´ë ‰íЏ 메시지", "account.domain_blocked": "ë„ë©”ì¸ ìˆ¨ê²¨ì§", "account.edit_profile": "프로필 편집", @@ -15,6 +16,7 @@ "account.follows.empty": "ì´ ìœ ì €ëŠ” ì•„ì§ ì•„ë¬´ë„ íŒ”ë¡œìš° í•˜ê³ ìžˆì§€ 않습니다.", "account.follows_you": "ë‚ íŒ”ë¡œìš°í•©ë‹ˆë‹¤", "account.hide_reblogs": "@{name}ì˜ ë¶€ìŠ¤íŠ¸ë¥¼ 숨기기", + "account.last_status": "마지막 활ë™", "account.link_verified_on": "{date}ì— ì´ ë§í¬ì˜ ì†Œìœ ê¶Œì´ í™•ì¸ ë¨", "account.locked_info": "ì´ ê³„ì •ì˜ í”„ë¼ì´ë²„시 ì„¤ì •ì€ ìž ê¸ˆìœ¼ë¡œ ì„¤ì •ë˜ì–´ 있습니다. ê³„ì • ì†Œìœ ìžê°€ 수ë™ìœ¼ë¡œ 팔로어를 승ì¸í•©ë‹ˆë‹¤.", "account.media": "미디어", @@ -23,6 +25,7 @@ "account.mute": "@{name} 뮤트", "account.mute_notifications": "@{name}ì˜ ì•Œë¦¼ì„ ë®¤íŠ¸", "account.muted": "뮤트 ë¨", + "account.never_active": "ì—†ìŒ", "account.posts": "툿", "account.posts_with_replies": "툿과 답장", "account.report": "@{name} ì‹ ê³ ", @@ -35,8 +38,11 @@ "account.unfollow": "팔로우 í•´ì œ", "account.unmute": "뮤트 í•´ì œ", "account.unmute_notifications": "@{name}ì˜ ì•Œë¦¼ 뮤트 í•´ì œ", + "alert.rate_limited.message": "{retry_time, time, medium}ì— ë‹¤ì‹œ 시ë„í•´ 주세요.", + "alert.rate_limited.title": "ë¹ˆë„ ì œí•œ", "alert.unexpected.message": "예측하지 못한 ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.", "alert.unexpected.title": "ì•—!", + "autosuggest_hashtag.per_week": "주간 {count}회", "boost_modal.combo": "{combo}를 누르면 다ìŒë¶€í„° ì´ ê³¼ì •ì„ ê±´ë„ˆë›¸ 수 있습니다", "bundle_column_error.body": "ì»´í¬ë„ŒíŠ¸ë¥¼ 불러오는 ê³¼ì •ì—서 ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤.", "bundle_column_error.retry": "다시 시ë„", @@ -47,6 +53,7 @@ "column.blocks": "차단 ì¤‘ì¸ ì‚¬ìš©ìž", "column.community": "로컬 타임ë¼ì¸", "column.direct": "다ì´ë ‰íЏ 메시지", + "column.directory": "프로필 둘러보기", "column.domain_blocks": "숨겨진 ë„ë©”ì¸", "column.favourites": "ì¦ê²¨ì°¾ê¸°", "column.follow_requests": "팔로우 ìš”ì²", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "ì •ë§ë¡œ ì´ ë¦¬ìŠ¤íŠ¸ë¥¼ ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?", "confirmations.domain_block.confirm": "ë„ë©”ì¸ ì „ì²´ë¥¼ 숨김", "confirmations.domain_block.message": "ì •ë§ë¡œ {domain} ì „ì²´ë¥¼ ìˆ¨ê¸°ì‹œê² ìŠµë‹ˆê¹Œ? ëŒ€ë¶€ë¶„ì˜ ê²½ìš° 개별 차단ì´ë‚˜ 뮤트로 충분합니다. ëª¨ë“ ê³µê°œ 타임ë¼ì¸ê³¼ 알림ì—서 해당 ë„ë©”ì¸ì—서 ìž‘ì„±ëœ ì»¨í…ì¸ ë¥¼ ë³´ì§€ 못합니다. 해당 ë„ë©”ì¸ íŒ”ë¡œì›Œì™€ì˜ ê´€ê³„ê°€ 사ë¼ì§‘니다.", + "confirmations.logout.confirm": "로그아웃", + "confirmations.logout.message": "ì •ë§ë¡œ 로그아웃 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?", "confirmations.mute.confirm": "뮤트", + "confirmations.mute.explanation": "ì´ ë™ìž‘ì€ ê·¸ì˜ ê²Œì‹œë¬¼, 그를 멘션하는 ê²Œì‹œë¬¼ì„ ìˆ¨ê¹ë‹ˆë‹¤, 하지만 ì—¬ì „ížˆ 그가 ë‹¹ì‹ ì˜ ê²Œì‹œë¬¼ì„ ë³´ê³ íŒ”ë¡œìš° í• ìˆ˜ 있습니다.", "confirmations.mute.message": "ì •ë§ë¡œ {name}를 ë®¤íŠ¸í•˜ì‹œê² ìŠµë‹ˆê¹Œ?", "confirmations.redraft.confirm": "ì‚ì œí•˜ê³ ë‹¤ì‹œ 쓰기", "confirmations.redraft.message": "ì •ë§ë¡œ ì´ í¬ìŠ¤íŠ¸ë¥¼ ì‚ì œí•˜ê³ ë‹¤ì‹œ ì“°ì‹œê² ìŠµë‹ˆê¹Œ? 해당 í¬ìŠ¤íŠ¸ì— ëŒ€í•œ 부스트와 ì¦ê²¨ì°¾ê¸°ë¥¼ 잃게 ë˜ê³ ì›ë³¸ì— 대한 ë‹µìž¥ì€ ì—°ê²° ë˜ì§€ 않습니다.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "ë‹µê¸€ì„ ë‹¬ê¸° 위해 현재 작성 ì¤‘ì¸ ë©”ì‹œì§€ê°€ ë®ì–´ 씌워집니다. ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?", "confirmations.unfollow.confirm": "언팔로우", "confirmations.unfollow.message": "ì •ë§ë¡œ {name}를 ì–¸íŒ”ë¡œìš°í•˜ì‹œê² ìŠµë‹ˆê¹Œ?", + "conversation.delete": "대화 ì‚ì œ", + "conversation.mark_as_read": "ì½ì€ ìƒíƒœë¡œ 표시", + "conversation.open": "대화 보기", + "conversation.with": "{names} 님과", + "directory.federated": "ì•Œë ¤ì§„ 연합우주로부터", + "directory.local": "{domain}ì—서만", + "directory.new_arrivals": "새로운 사람들", + "directory.recently_active": "최근 활ë™", "embed.instructions": "ì•„ëž˜ì˜ ì½”ë“œë¥¼ 복사하여 대화를 ì›í•˜ëŠ” 곳으로 ê³µìœ í•˜ì„¸ìš”.", "embed.preview": "다ìŒê³¼ ê°™ì´ í‘œì‹œë©ë‹ˆë‹¤:", "emoji_button.activity": "활ë™", @@ -134,6 +152,10 @@ "empty_column.mutes": "ì•„ì§ ì•„ë¬´ë„ ë®¤íŠ¸í•˜ì§€ 않았습니다.", "empty_column.notifications": "ì•„ì§ ì•Œë¦¼ì´ ì—†ìŠµë‹ˆë‹¤. 다른 사람과 대화를 시작해 보세요.", "empty_column.public": "여기엔 ì•„ì§ ì•„ë¬´ ê²ƒë„ ì—†ìŠµë‹ˆë‹¤! 공개ì 으로 무언가 í¬ìŠ¤íŒ…í•˜ê±°ë‚˜, 다른 ì„œë²„ì˜ ìœ ì €ë¥¼ 팔로우 해서 채워보세요", + "error.unexpected_crash.explanation": "버그 í˜¹ì€ ë¸Œë¼ìš°ì € 호환성 ë¬¸ì œë¡œ ì´ íŽ˜ì´ì§€ë¥¼ 올바르게 í‘œì‹œí• ìˆ˜ 없습니다.", + "error.unexpected_crash.next_steps": "페ì´ì§€ë¥¼ ìƒˆë¡œê³ ì¹¨ 해보세요. ê·¸ëž˜ë„ í•´ê²°ë˜ì§€ 않는 경우, 다른 브ë¼ìš°ì €ë‚˜ 네ì´í‹°ë¸Œ ì•±ìœ¼ë¡œë„ ë§ˆìŠ¤í† ëˆì„ ì´ìš©í•˜ì‹¤ 수 있습니다.", + "errors.unexpected_crash.copy_stacktrace": "ì—러 ë‚´ìš©ì„ í´ë¦½ë³´ë“œì— 복사", + "errors.unexpected_crash.report_issue": "ë¬¸ì œ ì‹ ê³ ", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "새 ë¦¬ìŠ¤íŠ¸ì˜ ì´ë¦„", "lists.search": "팔로우 ì¤‘ì¸ ì‚¬ëžŒë“¤ 중ì—서 찾기", "lists.subheading": "ë‹¹ì‹ ì˜ ë¦¬ìŠ¤íŠ¸", + "load_pending": "{count}ê°œì˜ ìƒˆ í•목", "loading_indicator.label": "불러오는 중...", "media_gallery.toggle_visible": "표시 ì „í™˜", "missing_indicator.label": "ì°¾ì„ ìˆ˜ 없습니다", @@ -251,7 +274,6 @@ "navigation_bar.personal": "ê°œì¸ìš©", "navigation_bar.pins": "ê³ ì •ëœ íˆ¿", "navigation_bar.preferences": "ì‚¬ìš©ìž ì„¤ì •", - "navigation_bar.profile_directory": "프로필 ë””ë ‰í† ë¦¬", "navigation_bar.public_timeline": "ì—°í•© 타임ë¼ì¸", "navigation_bar.security": "보안", "notification.favourite": "{name}ë‹˜ì´ ì¦ê²¨ì°¾ê¸° 했습니다", @@ -282,8 +304,10 @@ "notifications.group": "{count} ê°œì˜ ì•Œë¦¼", "poll.closed": "마ê°ë¨", "poll.refresh": "ìƒˆë¡œê³ ì¹¨", - "poll.total_votes": "{count} 명 참여", + "poll.total_people": "{count}명", + "poll.total_votes": "{count} 표", "poll.vote": "투표", + "poll.voted": "ì´ ë‹µë³€ì— íˆ¬í‘œí–ˆìŠµë‹ˆë‹¤", "poll_button.add_poll": "투표 추가", "poll_button.remove_poll": "투표 ì‚ì œ", "privacy.change": "í¬ìŠ¤íŠ¸ì˜ í”„ë¼ì´ë²„시 ì„¤ì •ì„ ë³€ê²½", @@ -295,6 +319,7 @@ "privacy.public.short": "공개", "privacy.unlisted.long": "공개 타임ë¼ì¸ì— 표시하지 않ìŒ", "privacy.unlisted.short": "타임ë¼ì¸ì— 비표시", + "refresh": "ìƒˆë¡œê³ ì¹¨", "regeneration_indicator.label": "불러오는 중…", "regeneration_indicator.sublabel": "ë‹¹ì‹ ì˜ í™ˆ 피드가 준비ë˜ëŠ” 중입니다!", "relative_time.days": "{number}ì¼ ì „", @@ -319,6 +344,7 @@ "search_results.accounts": "사람", "search_results.hashtags": "해시태그", "search_results.statuses": "툿", + "search_results.statuses_fts_disabled": "ì´ ë§ˆìŠ¤í† ëˆ ì„œë²„ì—ì„ íˆ¿ì˜ ë‚´ìš©ì„ í†µí•œ ê²€ìƒ‰ì´ í™œì„±í™” ë˜ì–´ 있지 않습니다.", "search_results.total": "{count, number}ê±´ì˜ ê²°ê³¼", "status.admin_account": "@{name}ì— ëŒ€í•œ 모ë”ë ˆì´ì…˜ ì¸í„°íŽ˜ì´ìФ 열기", "status.admin_status": "모ë”ë ˆì´ì…˜ ì¸í„°íŽ˜ì´ìФì—서 ì´ ê²Œì‹œë¬¼ 열기", @@ -331,7 +357,7 @@ "status.direct": "@{name}ì—게 다ì´ë ‰íЏ 메시지", "status.embed": "ê³µìœ í•˜ê¸°", "status.favourite": "ì¦ê²¨ì°¾ê¸°", - "status.filtered": "í•„í„°ë§ ë¨", + "status.filtered": "필터로 걸러ì§", "status.load_more": "ë” ë³´ê¸°", "status.local_only": "This post is only visible by other users of your instance", "status.media_hidden": "미디어 숨겨ì§", @@ -358,6 +384,7 @@ "status.show_more": "ë” ë³´ê¸°", "status.show_more_all": "ëª¨ë‘ íŽ¼ì¹˜ê¸°", "status.show_thread": "글타래 보기", + "status.uncached_media_warning": "ì‚¬ìš©í• ìˆ˜ ì—†ìŒ", "status.unmute_conversation": "ì´ ëŒ€í™”ì˜ ë®¤íŠ¸ í•´ì œí•˜ê¸°", "status.unpin": "ê³ ì • í•´ì œ", "suggestions.dismiss": "추천 지우기", @@ -373,14 +400,22 @@ "time_remaining.moments": "ë‚¨ì€ ì‹œê°„", "time_remaining.seconds": "{number} ì´ˆ 남ìŒ", "trends.count_by_accounts": "{count} ëª…ì˜ ì‚¬ëžŒë“¤ì´ ë§í•˜ê³ 있습니다", + "trends.trending_now": "지금 ìœ í–‰ì¤‘", "ui.beforeunload": "지금 나가면 ì €ìž¥ë˜ì§€ ì•Šì€ í•ëª©ì„ ìžƒê²Œ ë©ë‹ˆë‹¤.", "upload_area.title": "드래그 & 드ë¡ìœ¼ë¡œ 업로드", "upload_button.label": "미디어 추가 (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "íŒŒì¼ ì—…ë¡œë“œ ì œí•œì— ë„달했습니다.", "upload_error.poll": "íŒŒì¼ ì—…ë¡œë“œëŠ” 투표와 함께 ì²¨ë¶€í• ìˆ˜ 없습니다.", "upload_form.description": "시ê°ìž¥ì• ì¸ì„ 위한 설명", - "upload_form.focus": "미리보기 변경", + "upload_form.edit": "편집", "upload_form.undo": "ì‚ì œ", + "upload_modal.analyzing_picture": "ì´ë¯¸ì§€ ë¶„ì„ ì¤‘â€¦", + "upload_modal.apply": "ì ìš©", + "upload_modal.description_placeholder": "ë‹¤ëžŒì¥ í—Œ 쳇바퀴 íƒ€ê³ íŒŒ", + "upload_modal.detect_text": "ì´ë¯¸ì§€ì—서 í…스트 추출", + "upload_modal.edit_media": "미디어 편집", + "upload_modal.hint": "미리보기를 í´ë¦í•˜ê±°ë‚˜ 드래그 해서 í¬ì»¬ í¬ì¸íŠ¸ë¥¼ 맞추세요. ì´ ì ì€ ì¸ë„¤ì¼ì— í•ìƒ ë³´ì—¬ì§ˆ ë¶€ë¶„ì„ ë‚˜íƒ€ëƒ…ë‹ˆë‹¤.", + "upload_modal.preview_label": "미리보기 ({ratio})", "upload_progress.label": "업로드 중...", "video.close": "ë™ì˜ìƒ 닫기", "video.exit_fullscreen": "ì „ì²´í™”ë©´ 나가기", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 7a71d84c9da96de9665a9ff2ff0f16121cd30389..611a5eb53cf3b0288fd322003f0e907bbc289665 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -4,6 +4,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Edit profile", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Follows you", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,10 +25,11 @@ "account.mute": "Mute @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.report": "Report @{name}", - "account.requested": "Awaiting approval. Click to cancel follow request", + "account.requested": "Awaiting approval", "account.share": "Share @{name}'s profile", "account.show_reblogs": "Show boosts from @{name}", "account.unblock": "Unblock @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You can press {combo} to skip this next time", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blocked users", "column.community": "Local timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Unpin", "column_subheading.settings": "Settings", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Loading...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferences", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federated timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,15 +400,23 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", - "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Add media ({formats})", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", - "upload_progress.label": "Uploading...", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", "video.expand": "Expand video", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index ccc5d4e57917f781d65c137574f383747fbd0d69..fc76339c54f534877d939069a95c88fa4c697cfc 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -4,6 +4,7 @@ "account.block": "BloÄ·Ä“t @{name}", "account.block_domain": "SlÄ“pt visu no {domain}", "account.blocked": "BloÄ·Ä“ts", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "PrivÄtÄ ziņa @{name}", "account.domain_blocked": "DomÄ“ns ir paslÄ“pts", "account.edit_profile": "Labot profilu", @@ -15,6 +16,7 @@ "account.follows.empty": "Å is lietotÄjs pagaidÄm nevienam neseko.", "account.follows_you": "Seko tev", "account.hide_reblogs": "PaslÄ“pt paceltos ierakstus no lietotÄja @{name}", + "account.last_status": "Last active", "account.link_verified_on": "Å Ä«s saites piederÄ«ba ir pÄrbaudÄ«ta {date}", "account.locked_info": "Å Ä« konta privÄtuma status ir iestatÄ«ts slÄ“gts. ĪpaÅ¡nieks izskatÄ«s un izvÄ“lÄ“sies kas viņam drÄ«kst sekot.", "account.media": "MÄ“diji", @@ -23,6 +25,7 @@ "account.mute": "ApklusinÄt @{name}", "account.mute_notifications": "NerÄdÄ«t paziņojumus no @{name}", "account.muted": "ApklusinÄts", + "account.never_active": "Never", "account.posts": "Ieraksti", "account.posts_with_replies": "Ieraksti un atbildes", "account.report": "Ziņot par lietotÄju @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Nesekot", "account.unmute": "Noņemt apklusinÄjumu no lietotÄja @{name}", "account.unmute_notifications": "RÄdÄ«t paziņojumus no lietotÄja @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "NegaidÄ«ta kļūda.", "alert.unexpected.title": "Ups!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Nospied {combo} lai izlaistu Å¡o nÄkamreiz", "bundle_column_error.body": "Kaut kas nogÄja greizi ielÄdÄ“jot Å¡o komponenti.", "bundle_column_error.retry": "Mēģini vÄ“lreiz", @@ -47,6 +53,7 @@ "column.blocks": "BloÄ·Ä“tie lietotÄji", "column.community": "LokÄlÄ laika lÄ«nija", "column.direct": "PrivÄtÄs ziņas", + "column.directory": "Browse profiles", "column.domain_blocks": "PaslÄ“ptie domÄ“ni", "column.favourites": "FavorÄ«ti", "column.follow_requests": "SekotÄju pieprasÄ«jumi", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Vai tieÅ¡am vÄ“lies neatgriezeniski dzÄ“st Å¡o sarakstu?", "confirmations.domain_block.confirm": "PaslÄ“pt visu domÄ“nu", "confirmations.domain_block.message": "Vai tu tieÅ¡Äm, tieÅ¡am vÄ“lies bloÄ·Ä“t visu domÄ“nu {domain}? LielÄkajÄ daÄ¼Ä gadÄ«jumu pietiek ja nobloÄ·Ä“ vai apklusini kÄdu. Tu neredzÄ“si saturu vai paziņojumus no šī domÄ“na nevienÄ laika lÄ«nijÄ. Tavi sekotÄji no šī domÄ“na tiks noņemti.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "ApklusinÄt", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Vai Tu tieÅ¡Äm velies apklusinÄt {name}?", "confirmations.redraft.confirm": "DzÄ“st un pÄrrakstÄ«t", "confirmations.redraft.message": "Vai tieÅ¡Äm vÄ“lies dzÄ“st un pÄrrakstÄ«t Å¡o ierakstu? FavorÄ«ti un paceltie ieraksti tiks dzÄ“sti, kÄ arÄ« atbildes tiks atsaistÄ«tas no šī ieraksta.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Atbildot tagad tava ziņa ko Å¡obrÄ«d raksti tiks pÄrrakstÄ«ta. Vai tieÅ¡Äm vÄ“lies turpinÄt?", "confirmations.unfollow.confirm": "Nesekot", "confirmations.unfollow.message": "Vai tieÅ¡am vairs nevÄ“lies sekot lietotÄjam {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Iegul Å¡o ziņojumu savÄ mÄjaslapÄ kopÄ“jot kodu zemÄk.", "embed.preview": "Tas izskatÄ«sies Å¡Ädi:", "emoji_button.activity": "AktivitÄte", @@ -134,6 +152,10 @@ "empty_column.mutes": "Tu neesi nevienu apklusinÄjis.", "empty_column.notifications": "Tev nav paziņojumu. Iesaisties sarunÄs ar citiem.", "empty_column.public": "Å eit nekÄ nav, tukÅ¡ums! Ieraksti kaut ko publiski, vai uzmeklÄ“ un seko kÄdam no citas instances", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Loading...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferences", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federated timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,15 +400,23 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", - "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Add media ({formats})", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", - "upload_progress.label": "Uploading...", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", "video.expand": "Expand video", diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json new file mode 100644 index 0000000000000000000000000000000000000000..9a4a417af4165e335790bfa7f9172e2bfcbc6f60 --- /dev/null +++ b/app/javascript/mastodon/locales/mk.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Додади или одÑтрани од лиÑта", + "account.badges.bot": "Бот", + "account.block": "Блокирај @{name}", + "account.block_domain": "Сокријај Ñе од {domain}", + "account.blocked": "Блокиран", + "account.cancel_follow_request": "Одкажи барање за Ñледење", + "account.direct": "Директна порана @{name}", + "account.domain_blocked": "Скриен домен", + "account.edit_profile": "Измени профил", + "account.endorse": "КарактериÑтики на профилот", + "account.follow": "Следи", + "account.followers": "Следбеници", + "account.followers.empty": "Ðикој не го Ñледи овој кориÑник Ñеуште.", + "account.follows": "Следи", + "account.follows.empty": "КориÑникот не Ñледи никој Ñеуште.", + "account.follows_you": "Те Ñледи тебе", + "account.hide_reblogs": "Сокриј буÑÑ‚ од @{name}", + "account.last_status": "ПоÑледно активен", + "account.link_verified_on": "СопÑтевноÑта на овај линк беше проверен на {date}", + "account.locked_info": "СтатуÑот на приватноÑÑ‚ на овај кориÑник е Ñетиран како заклучен. КориÑникот одлучува кој можи да го Ñледи него.", + "account.media": "Медија", + "account.mention": "Спомни @{name}", + "account.moved_to": "{name} Ñе преÑели во:", + "account.mute": "Зачути го @{name}", + "account.mute_notifications": "ИÑклучи извеÑтувања од @{name}", + "account.muted": "Зачутено", + "account.never_active": "Ðикогаш", + "account.posts": "Тутови", + "account.posts_with_replies": "Тутови и реплики", + "account.report": "Пријави @{name}", + "account.requested": "Се чека одобрување. Кликни за да одкажиш барање за Ñледење", + "account.share": "Сподели @{name} профил", + "account.show_reblogs": "Прикажи буÑтови од @{name}", + "account.unblock": "Одблокирај @{name}", + "account.unblock_domain": "Прикажи {domain}", + "account.unendorse": "Don't feature on profile", + "account.unfollow": "ОдÑледи", + "account.unmute": "Зачути го @{name}", + "account.unmute_notifications": "ИÑклучи извеÑтувања од @{name}", + "alert.rate_limited.message": "Обидете Ñе повторно поÑле {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", + "alert.unexpected.message": "Ðеочекувана грешка.", + "alert.unexpected.title": "УпÑ!", + "autosuggest_hashtag.per_week": "{count} неделно", + "boost_modal.combo": "Кликни {combo} за да го преÑкокниш ова нареден пат", + "bundle_column_error.body": "Се Ñлучи проблем при вчитувањето.", + "bundle_column_error.retry": "Обидете Ñе повторно", + "bundle_column_error.title": "Мрежна грешка", + "bundle_modal_error.close": "Затвори", + "bundle_modal_error.message": "ÐаÑтана грешка при прикажувањето на оваа веб-Ñтраница.", + "bundle_modal_error.retry": "Обидете Ñе повторно", + "column.blocks": "Блокирани кориÑници", + "column.community": "Local timeline", + "column.direct": "Директна порака", + "column.directory": "Browse profiles", + "column.domain_blocks": "Hidden domains", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Дома", + "column.lists": "ЛиÑта", + "column.mutes": "Muted users", + "column.notifications": "ИзвеÑтувања", + "column.pins": "Pinned toot", + "column.public": "Federated timeline", + "column_back_button.label": "Ðазад", + "column_header.hide_settings": "Hide settings", + "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveRight_settings": "Move column to the right", + "column_header.pin": "Pin", + "column_header.show_settings": "Show settings", + "column_header.unpin": "Unpin", + "column_subheading.settings": "Settings", + "community.column_settings.media_only": "Media only", + "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.", + "compose_form.direct_message_warning_learn_more": "Learn more", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", + "compose_form.lock_disclaimer.lock": "locked", + "compose_form.placeholder": "What is on your mind?", + "compose_form.poll.add_option": "Add a choice", + "compose_form.poll.duration": "Poll duration", + "compose_form.poll.option_placeholder": "Choice {number}", + "compose_form.poll.remove_option": "Remove this choice", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.marked": "Media is marked as sensitive", + "compose_form.sensitive.unmarked": "Media is not marked as sensitive", + "compose_form.spoiler.marked": "Text is hidden behind warning", + "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler_placeholder": "Write your warning here", + "confirmation_modal.cancel": "Cancel", + "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.confirm": "Блокирај", + "confirmations.block.message": "Are you sure you want to block {name}?", + "confirmations.delete.confirm": "Delete", + "confirmations.delete.message": "Are you sure you want to delete this status?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", + "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.redraft.confirm": "Delete & redraft", + "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", + "confirmations.reply.confirm": "Reply", + "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Here is what it will look like:", + "emoji_button.activity": "Activity", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flags", + "emoji_button.food": "Food & Drink", + "emoji_button.label": "Insert emoji", + "emoji_button.nature": "Nature", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objects", + "emoji_button.people": "People", + "emoji_button.recent": "Frequently used", + "emoji_button.search": "Search...", + "emoji_button.search_results": "Search results", + "emoji_button.symbols": "Symbols", + "emoji_button.travel": "Travel & Places", + "empty_column.account_timeline": "No toots here!", + "empty_column.account_unavailable": "Profile unavailable", + "empty_column.blocks": "You haven't blocked any users yet.", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.domain_blocks": "There are no hidden domains yet.", + "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", + "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", + "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", + "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Reject", + "getting_started.developers": "Developers", + "getting_started.directory": "Profile directory", + "getting_started.documentation": "Documentation", + "getting_started.heading": "Getting started", + "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.security": "Security", + "getting_started.terms": "Terms of service", + "hashtag.column_header.tag_mode.all": "and {additional}", + "hashtag.column_header.tag_mode.any": "or {additional}", + "hashtag.column_header.tag_mode.none": "without {additional}", + "hashtag.column_settings.select.no_options_message": "No suggestions found", + "hashtag.column_settings.select.placeholder": "Enter hashtags…", + "hashtag.column_settings.tag_mode.all": "All of these", + "hashtag.column_settings.tag_mode.any": "Any of these", + "hashtag.column_settings.tag_mode.none": "None of these", + "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "home.column_settings.basic": "Basic", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "intervals.full.days": "{number, plural, one {# day} other {# days}}", + "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", + "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "introduction.federation.action": "Next", + "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.home.headline": "Home", + "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.local.headline": "Local", + "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.interactions.action": "Finish toot-orial!", + "introduction.interactions.favourite.headline": "Favourite", + "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.reblog.headline": "Boost", + "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reply.headline": "Reply", + "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.welcome.action": "Let's go!", + "introduction.welcome.headline": "First steps", + "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.favourites": "to open favourites list", + "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.local": "to open local timeline", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.muted": "to open muted users list", + "keyboard_shortcuts.my_profile": "to open your profile", + "keyboard_shortcuts.notifications": "to open notifications column", + "keyboard_shortcuts.pinned": "to open pinned toots list", + "keyboard_shortcuts.profile": "to open author's profile", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.start": "to open \"get started\" column", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Close", + "lightbox.next": "Next", + "lightbox.previous": "Previous", + "lightbox.view_context": "View context", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.edit.submit": "Change title", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "missing_indicator.sublabel": "This resource could not be found", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.apps": "Mobile apps", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Direct messages", + "navigation_bar.discover": "Discover", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.filters": "Muted words", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.info": "About this server", + "navigation_bar.keyboard_shortcuts": "Hotkeys", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Logout", + "navigation_bar.mutes": "Muted users", + "navigation_bar.personal": "Personal", + "navigation_bar.pins": "Pinned toots", + "navigation_bar.preferences": "Preferences", + "navigation_bar.public_timeline": "Federated timeline", + "navigation_bar.security": "Security", + "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", + "notification.mention": "{name} mentioned you", + "notification.poll": "A poll you have voted in has ended", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Clear notifications", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.filter_bar.advanced": "Display all categories", + "notifications.column_settings.filter_bar.category": "Quick filter bar", + "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.follow": "New followers:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "notifications.filter.all": "All", + "notifications.filter.boosts": "Boosts", + "notifications.filter.favourites": "Favourites", + "notifications.filter.follows": "Follows", + "notifications.filter.mentions": "Mentions", + "notifications.filter.polls": "Poll results", + "notifications.group": "{count} notifications", + "poll.closed": "Closed", + "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.vote": "Vote", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Add a poll", + "poll_button.remove_poll": "Remove poll", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "status.admin_account": "Open moderation interface for @{name}", + "status.admin_status": "Open this status in the moderation interface", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.copy": "Copy link to status", + "status.delete": "Delete", + "status.detailed_status": "Detailed conversation view", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.filtered": "Filtered", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.read_more": "Read more", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", + "status.redraft": "Delete & re-draft", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "suggestions.dismiss": "Dismiss suggestion", + "suggestions.header": "You might be interested in…", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "time_remaining.days": "{number, plural, one {# day} other {# days}} left", + "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", + "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.moments": "Moments remaining", + "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media ({formats})", + "upload_error.limit": "File upload limit exceeded.", + "upload_error.poll": "File upload not allowed with polls.", + "upload_form.description": "Describe for the visually impaired", + "upload_form.edit": "Edit", + "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json index 098d6cca93b6c8bae6c8825e190004282c3aa9d2..6efd42955cfe20fae93a6c4a0f18b2bbbd374bbe 100644 --- a/app/javascript/mastodon/locales/ms.json +++ b/app/javascript/mastodon/locales/ms.json @@ -4,6 +4,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Edit profile", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Follows you", "account.hide_reblogs": "Hide boosts from @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,10 +25,11 @@ "account.mute": "Mute @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", "account.report": "Report @{name}", - "account.requested": "Awaiting approval. Click to cancel follow request", + "account.requested": "Awaiting approval", "account.share": "Share @{name}'s profile", "account.show_reblogs": "Show boosts from @{name}", "account.unblock": "Unblock @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You can press {combo} to skip this next time", "bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.retry": "Try again", @@ -47,6 +53,7 @@ "column.blocks": "Blocked users", "column.community": "Local timeline", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -64,7 +71,7 @@ "column_header.show_settings": "Show settings", "column_header.unpin": "Unpin", "column_subheading.settings": "Settings", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", "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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Mute", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Here is what it will look like:", "emoji_button.activity": "Activity", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "New list title", "lists.search": "Search among people you follow", "lists.subheading": "Your lists", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Loading...", "media_gallery.toggle_visible": "Toggle visibility", "missing_indicator.label": "Not found", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Pinned toots", "navigation_bar.preferences": "Preferences", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federated timeline", "navigation_bar.security": "Security", "notification.favourite": "{name} favourited your status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Adjust status privacy", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Show more", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Unmute conversation", "status.unpin": "Unpin from profile", "suggestions.dismiss": "Dismiss suggestion", @@ -373,15 +400,23 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", - "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", + "upload_button.label": "Add media ({formats})", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", - "upload_progress.label": "Uploading...", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", "video.expand": "Expand video", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index ee7d9cf638e7b76f0c93e5b4c370a2f334d66469..ba649dde09fd6489233a0b3fb5f7375f021dbed0 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -4,6 +4,7 @@ "account.block": "Blokkeer @{name}", "account.block_domain": "Verberg alles van {domain}", "account.blocked": "Geblokkeerd", + "account.cancel_follow_request": "Volgverzoek annuleren", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domein verborgen", "account.edit_profile": "Profiel bewerken", @@ -15,6 +16,7 @@ "account.follows.empty": "Deze gebruiker volgt nog niemand.", "account.follows_you": "Volgt jou", "account.hide_reblogs": "Verberg boosts van @{name}", + "account.last_status": "Laatst actief", "account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}", "account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Negeer @{name}", "account.mute_notifications": "Negeer meldingen van @{name}", "account.muted": "Genegeerd", + "account.never_active": "Nooit", "account.posts": "Toots", "account.posts_with_replies": "Toots en reacties", "account.report": "Rapporteer @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Ontvolgen", "account.unmute": "@{name} niet langer negeren", "account.unmute_notifications": "@{name} meldingen niet langer negeren", + "alert.rate_limited.message": "Probeer het nog een keer na {retry_time, time, medium}.", + "alert.rate_limited.title": "Beperkt te gebruiken", "alert.unexpected.message": "Er deed zich een onverwachte fout voor", "alert.unexpected.title": "Oeps!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan", "bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.", "bundle_column_error.retry": "Opnieuw proberen", @@ -47,6 +53,7 @@ "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", "column.direct": "Directe berichten", + "column.directory": "Gebruikersgids", "column.domain_blocks": "Genegeerde servers", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?", "confirmations.domain_block.confirm": "Verberg alles van deze server", "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen toots van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.", + "confirmations.logout.confirm": "Uitloggen", + "confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?", "confirmations.mute.confirm": "Negeren", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?", "confirmations.redraft.confirm": "Verwijderen en herschrijven", "confirmations.redraft.message": "Weet je zeker dat je deze toot wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en reacties op de originele toot zitten niet meer aan de nieuwe toot vast.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Door nu te reageren overschrijf je de toot die je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?", "confirmations.unfollow.confirm": "Ontvolgen", "confirmations.unfollow.message": "Weet je het zeker dat je {name} wilt ontvolgen?", + "conversation.delete": "Gesprek verwijderen", + "conversation.mark_as_read": "Als gelezen markeren", + "conversation.open": "Gesprek tonen", + "conversation.with": "Met {names}", + "directory.federated": "Fediverse (wat bekend is)", + "directory.local": "Alleen {domain}", + "directory.new_arrivals": "Nieuwe accounts", + "directory.recently_active": "Onlangs actief", "embed.instructions": "Embed deze toot op jouw website, door de onderstaande code te kopiëren.", "embed.preview": "Zo komt het eruit te zien:", "emoji_button.activity": "Activiteiten", @@ -134,6 +152,10 @@ "empty_column.mutes": "Jij hebt nog geen gebruikers genegeerd.", "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.", "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere servers om het te vullen", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Stacktrace naar klembord kopiëren", + "errors.unexpected_crash.report_issue": "Technisch probleem melden", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Naam nieuwe lijst", "lists.search": "Zoek naar mensen die je volgt", "lists.subheading": "Jouw lijsten", + "load_pending": "{count, plural, one {# nieuw item} other {# nieuwe items}}", "loading_indicator.label": "Laden…", "media_gallery.toggle_visible": "Media wel/niet tonen", "missing_indicator.label": "Niet gevonden", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Persoonlijk", "navigation_bar.pins": "Vastgezette toots", "navigation_bar.preferences": "Instellingen", - "navigation_bar.profile_directory": "Gebruikersgids", "navigation_bar.public_timeline": "Globale tijdlijn", "navigation_bar.security": "Beveiliging", "notification.favourite": "{name} voegde jouw toot als favoriet toe", @@ -282,8 +304,10 @@ "notifications.group": "{count} meldingen", "poll.closed": "Gesloten", "poll.refresh": "Vernieuwen", + "poll.total_people": "{count, plural, one {# persoon} other {# mensen}}", "poll.total_votes": "{count, plural, one {# stem} other {# stemmen}}", "poll.vote": "Stemmen", + "poll.voted": "Je hebt hier op gestemd", "poll_button.add_poll": "Poll toevoegen", "poll_button.remove_poll": "Poll verwijderen", "privacy.change": "Zichtbaarheid toot aanpassen", @@ -295,6 +319,7 @@ "privacy.public.short": "Openbaar", "privacy.unlisted.long": "Niet op openbare tijdlijnen tonen", "privacy.unlisted.short": "Minder openbaar", + "refresh": "Vernieuwen", "regeneration_indicator.label": "Aan het laden…", "regeneration_indicator.sublabel": "Jouw tijdlijn wordt aangemaakt!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Gebruikers", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Het zoeken in toots is op deze Mastodonserver niet ingeschakeld.", "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", "status.admin_account": "Moderatie-omgeving van @{name} openen", "status.admin_status": "Deze toot in de moderatie-omgeving openen", @@ -358,6 +384,7 @@ "status.show_more": "Meer tonen", "status.show_more_all": "Alles meer tonen", "status.show_thread": "Gesprek tonen", + "status.uncached_media_warning": "Niet beschikbaar", "status.unmute_conversation": "Gesprek niet langer negeren", "status.unpin": "Van profielpagina losmaken", "suggestions.dismiss": "Voorstel verwerpen", @@ -373,14 +400,22 @@ "time_remaining.moments": "Nog enkele ogenblikken resterend", "time_remaining.seconds": "{number, plural, one {# seconde} other {# seconden}} te gaan", "trends.count_by_accounts": "{count} {rawCount, plural, one {persoon praat} other {mensen praten}} hierover", + "trends.trending_now": "Trends", "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.", "upload_area.title": "Hiernaar toe slepen om te uploaden", "upload_button.label": "Media toevoegen ({formats})", "upload_error.limit": "Uploadlimiet van bestand overschreden.", "upload_error.poll": "Het uploaden van bestanden is in polls niet toegestaan.", "upload_form.description": "Omschrijf dit voor mensen met een visuele beperking", - "upload_form.focus": "Voorvertoning aanpassen", + "upload_form.edit": "Bewerken", "upload_form.undo": "Verwijderen", + "upload_modal.analyzing_picture": "Afbeelding analyseren…", + "upload_modal.apply": "Toepassen", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Tekst in een afbeelding detecteren", + "upload_modal.edit_media": "Media bewerken", + "upload_modal.hint": "Klik of sleep de cirkel in de voorvertoning naar een centraal punt dat op elke thumbnail zichtbaar moet blijven.", + "upload_modal.preview_label": "Voorvertoning ({ratio})", "upload_progress.label": "Uploaden...", "video.close": "Video sluiten", "video.exit_fullscreen": "Volledig scherm sluiten", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json new file mode 100644 index 0000000000000000000000000000000000000000..73a319179b6985d543b0904a45a3144db87a2c5c --- /dev/null +++ b/app/javascript/mastodon/locales/nn.json @@ -0,0 +1,423 @@ +{ + "account.add_or_remove_from_list": "Legg til eller ta vekk fra liste", + "account.badges.bot": "Robot", + "account.block": "Blokkér @{name}", + "account.block_domain": "Gøyme alt innhald for domenet {domain}", + "account.blocked": "Blokkert", + "account.cancel_follow_request": "Avslutt føljar-førespurnad", + "account.direct": "Direkte meld @{name}", + "account.domain_blocked": "Domenet er gøymt", + "account.edit_profile": "Rediger profil", + "account.endorse": "Framhev pÃ¥ profilen din", + "account.follow": "Følj", + "account.followers": "Føljare", + "account.followers.empty": "Er ikkje nokon som føljar denne brukaren ennÃ¥.", + "account.follows": "Føljingar", + "account.follows.empty": "Denne brukaren foljer ikkje nokon ennÃ¥.", + "account.follows_you": "Føljar deg", + "account.hide_reblogs": "Gøym fremhevingar for @{name}", + "account.last_status": "Sist aktiv", + "account.link_verified_on": "Eigerskap for denne linken er sist sjekket den {date}", + "account.locked_info": "Brukarens privat-status er satt til lukka. Eigaren mÃ¥ manuelt døme kvem som kan følje honom.", + "account.media": "Media", + "account.mention": "Nemne @{name}", + "account.moved_to": "{name} har flytta til:", + "account.mute": "MÃ¥lbind @{name}", + "account.mute_notifications": "MÃ¥lbind varslingar ifrÃ¥ @{name}", + "account.muted": "MÃ¥lbindt", + "account.never_active": "Aldri", + "account.posts": "Tutar", + "account.posts_with_replies": "Tutar og svar", + "account.report": "Rapporter @{name}", + "account.requested": "Venter pÃ¥ samtykke. Klikk for Ã¥ avbryte føljar-førespurnad", + "account.share": "Del @{name} sin profil", + "account.show_reblogs": "SjÃ¥ framhevingar ifrÃ¥ @{name}", + "account.unblock": "Avblokker @{name}", + "account.unblock_domain": "Vis {domain}", + "account.unendorse": "Ikkje framhev pÃ¥ profil", + "account.unfollow": "Avfølja", + "account.unmute": "Av-demp @{name}", + "account.unmute_notifications": "Av-demp notifikasjoner ifrÃ¥ @{name}", + "alert.rate_limited.message": "Ver vennlig og prøv igjen {retry_time, time, medium}.", + "alert.rate_limited.title": "Bregrensa rate", + "alert.unexpected.message": "Eit uforventa problem har hendt.", + "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per veke", + "boost_modal.combo": "Du kan trykke {combo} for Ã¥ hoppe over dette neste gong", + "bundle_column_error.body": "Noko gikk gale mens komponent ble nedlasta.", + "bundle_column_error.retry": "Prøv igjen", + "bundle_column_error.title": "Tenarmaskin feil", + "bundle_modal_error.close": "Lukk", + "bundle_modal_error.message": "Noko gikk gale mens komponent var i ferd med Ã¥ bli nedlasta.", + "bundle_modal_error.retry": "Prøv igjen", + "column.blocks": "Blokka brukare", + "column.community": "Lokal samtid", + "column.direct": "Direkte meldingar", + "column.directory": "SjÃ¥ gjennom profiler", + "column.domain_blocks": "Gøymte domener", + "column.favourites": "Favorittar", + "column.follow_requests": "Føljarførespurnad", + "column.home": "Heim", + "column.lists": "Lister", + "column.mutes": "MÃ¥lbindte brukare", + "column.notifications": "Varslingar", + "column.pins": "Festa tuter", + "column.public": "Federert samtid", + "column_back_button.label": "Tilbake", + "column_header.hide_settings": "Skjul innstillingar", + "column_header.moveLeft_settings": "Flytt feltet til venstre", + "column_header.moveRight_settings": "Flytt feltet til høgre", + "column_header.pin": "Fest", + "column_header.show_settings": "Vis innstillingar", + "column_header.unpin": "Løys", + "column_subheading.settings": "Innstillingar", + "community.column_settings.media_only": "Kun medie", + "compose_form.direct_message_warning": "Denne tuten vil kun verte synleg for nemnde brukarar.", + "compose_form.direct_message_warning_learn_more": "Lær meir", + "compose_form.hashtag_warning": "Denne tuten vill ikkje bli lista under nokon knagg ettersom den ikkje er opplista. Berre offentlege tutar kan ble søkt pÃ¥ ved emneknagg.", + "compose_form.lock_disclaimer": "Din brukar er ikkje {locked}. Alle kan følje deg for Ã¥ sjÃ¥ føljar-modus poster.", + "compose_form.lock_disclaimer.lock": "lÃ¥st", + "compose_form.placeholder": "Kva har du pÃ¥ hjartet?", + "compose_form.poll.add_option": "Legg til eit punkt", + "compose_form.poll.duration": "Varigheit for spørring", + "compose_form.poll.option_placeholder": "Val {number}", + "compose_form.poll.remove_option": "Ta burt dette valet", + "compose_form.publish": "Tut", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Merk media som sensitivt", + "compose_form.sensitive.marked": "Media er markert som sensitivt", + "compose_form.sensitive.unmarked": "Media er ikkje merka som sensitivt", + "compose_form.spoiler.marked": "Tekst er gøymt bak ein advarsel", + "compose_form.spoiler.unmarked": "Tekst er ikkje gøymt", + "compose_form.spoiler_placeholder": "Skriv varselen din her", + "confirmation_modal.cancel": "Avbrot", + "confirmations.block.block_and_report": "Blokk & rapportér", + "confirmations.block.confirm": "Blokkér", + "confirmations.block.message": "Er du sikker pÃ¥ at du vill blokke {name}?", + "confirmations.delete.confirm": "Slett", + "confirmations.delete.message": "Er du sikker pÃ¥ at du vill slette denne statusen?", + "confirmations.delete_list.confirm": "Slett", + "confirmations.delete_list.message": "Er du sikker pÃ¥ at du vill slette denne listen for alltid?", + "confirmations.domain_block.confirm": "Gøym heile domenet", + "confirmations.domain_block.message": "Er du ordentleg, ordentleg sikker pÃ¥ at du vill blokkere heile {domain}? I dei tilfeller er det bedre med ein mÃ¥lretta blokkering eller demping av individuelle brukare.", + "confirmations.logout.confirm": "Logg ut", + "confirmations.logout.message": "Er du sikker pÃ¥ at du vill logge ut?", + "confirmations.mute.confirm": "MÃ¥lbind", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", + "confirmations.mute.message": "Er du sikker pÃ¥ at d vill mÃ¥lbinde {name}?", + "confirmations.redraft.confirm": "Slett & gjennopprett", + "confirmations.redraft.message": "Er du sikker pÃ¥ at du vill slette statusen og gjennoprette den? Favoritter og framhevinger vill bli borte, og svar til den originale posten vill bli einstøing.", + "confirmations.reply.confirm": "Svar", + "confirmations.reply.message": "Ã… svare nÃ¥ vill overskrive meldingen du er i ferd med Ã¥ skrive. Er du sikker pÃ¥ at du vill gÃ¥ fram?", + "confirmations.unfollow.confirm": "Avfølj", + "confirmations.unfollow.message": "Er du sikker pÃ¥ at du vill avfølje {name}?", + "conversation.delete": "Slett samtale", + "conversation.mark_as_read": "Merk som lest", + "conversation.open": "SjÃ¥ samtale", + "conversation.with": "Med {names}", + "directory.federated": "Fra kjent fedivers", + "directory.local": "Fra berre {domain} domenet", + "directory.new_arrivals": "Nyankommne", + "directory.recently_active": "Nylig aktiv", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Dette er korleis den vil sjÃ¥ ut:", + "emoji_button.activity": "Aktivitet", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flagg", + "emoji_button.food": "Mat & drikke", + "emoji_button.label": "Legg til smilefjes", + "emoji_button.nature": "Natur", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.objects": "Objektar", + "emoji_button.people": "Folk", + "emoji_button.recent": "Ofte brukt", + "emoji_button.search": "Søk...", + "emoji_button.search_results": "Søke resultater", + "emoji_button.symbols": "Symbolar", + "emoji_button.travel": "Reise & steder", + "empty_column.account_timeline": "Ikkje nokon tutar her!", + "empty_column.account_unavailable": "Profil ikkje tilgjengelig", + "empty_column.blocks": "Du har ikkje blokkért nokon brukarar ennÃ¥.", + "empty_column.community": "Den lokale samtiden er tom. Skriv noko offentleg Ã¥ fÃ¥ ballen til Ã¥ rulle!", + "empty_column.direct": "Du har ikkje nokon direkte meldingar ennÃ¥. NÃ¥r du sendar eller fÃ¥r ein, sÃ¥ vill den ende opp her.", + "empty_column.domain_blocks": "Der er ikkje nokon gøymte domener enno.", + "empty_column.favourited_statuses": "Du har ikkje favorisert nokon tutar enno. NÃ¥r du favoriserer noko, sÃ¥ vill det ende opp her.", + "empty_column.favourites": "Ikkje nokon har favorisert denne tuten enno. NÃ¥r nokon gjer det, sÃ¥ vill den ende opp her.", + "empty_column.follow_requests": "Du har ikkje nokon føljar førespurnad enno. NÃ¥r du fÃ¥r ein, sÃ¥ vill den sjÃ¥ast her.", + "empty_column.hashtag": "Det er ikkje noko i denne emneknaggen her enno.", + "empty_column.home": "Din heime-tidslinja er tom! Dra til {public} eller søk for Ã¥ starte Ã¥ møte andre brukare.", + "empty_column.home.public_timeline": "Den offentlege tidslinja", + "empty_column.list": "Det er ikkje noko i denne lista enno. NÃ¥r medlemmar av denne lista poster statuser, sÃ¥ vill dei sjÃ¥ast her.", + "empty_column.lists": "Du har ikkje nokon liste enno. NÃ¥r du lagar ein, sÃ¥ vill den ende up her.", + "empty_column.mutes": "Du har ikkje dempet nokon brukare enno.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Rapportér problem", + "follow_request.authorize": "Autoriser", + "follow_request.reject": "Reject", + "getting_started.developers": "Utviklare", + "getting_started.directory": "Profile directory", + "getting_started.documentation": "Dokumentasjon", + "getting_started.heading": "Komme i gong", + "getting_started.invite": "Inviter folk", + "getting_started.open_source_notice": "Mastodon har Ã¥pen kilde kode. Du kan hjelpe til med problemar pÃ¥ GitHub gjennom {github}.", + "getting_started.security": "Brukar instillingar", + "getting_started.terms": "BrukarvillkÃ¥r", + "hashtag.column_header.tag_mode.all": "og {additional}", + "hashtag.column_header.tag_mode.any": "eller {additional}", + "hashtag.column_header.tag_mode.none": "uten {additional}", + "hashtag.column_settings.select.no_options_message": "No suggestions found", + "hashtag.column_settings.select.placeholder": "Enter hashtags…", + "hashtag.column_settings.tag_mode.all": "Alle disse", + "hashtag.column_settings.tag_mode.any": "Any of these", + "hashtag.column_settings.tag_mode.none": "None of these", + "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "home.column_settings.basic": "Basic", + "home.column_settings.show_reblogs": "Vis fremhevingar", + "home.column_settings.show_replies": "Vis svar", + "intervals.full.days": "{number, plural, one {# day} other {# days}}", + "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", + "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "introduction.federation.action": "Neste", + "introduction.federation.federated.headline": "Federert", + "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.home.headline": "Heim", + "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.local.headline": "Lokal", + "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.interactions.action": "Fullfør omvisning!", + "introduction.interactions.favourite.headline": "Lik", + "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.reblog.headline": "Fremhev", + "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reply.headline": "Svar", + "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.welcome.action": "Let's go!", + "introduction.welcome.headline": "Første steg", + "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.favourites": "to open favourites list", + "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.local": "to open local timeline", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.muted": "to open muted users list", + "keyboard_shortcuts.my_profile": "to open your profile", + "keyboard_shortcuts.notifications": "to open notifications column", + "keyboard_shortcuts.pinned": "to open pinned toots list", + "keyboard_shortcuts.profile": "to open author's profile", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.start": "to open \"get started\" column", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toot": "Start ein heilt ny tut", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Lukk", + "lightbox.next": "Neste", + "lightbox.previous": "Forrige", + "lightbox.view_context": "SjÃ¥ kontekst", + "lists.account.add": "Legg til i liste", + "lists.account.remove": "Ta burt fra liste", + "lists.delete": "Slett liste", + "lists.edit": "Rediger liste", + "lists.edit.submit": "Bytt tittel", + "lists.new.create": "Legg til liste", + "lists.new.title_placeholder": "Ny liste tittel", + "lists.search": "Søk gjennom folk du føljar", + "lists.subheading": "Dine lister", + "load_pending": "{count, plural, one {# new item} other {# new items}}", + "loading_indicator.label": "Laster...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Ikkje funne", + "missing_indicator.sublabel": "Denne ressursen ble ikkje funne", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.apps": "Mobile apps", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Direct messages", + "navigation_bar.discover": "Discover", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.filters": "Muted words", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.info": "About this server", + "navigation_bar.keyboard_shortcuts": "Hotkeys", + "navigation_bar.lists": "Lister", + "navigation_bar.logout": "Logg ut", + "navigation_bar.mutes": "Dempa brukare", + "navigation_bar.personal": "Personlig", + "navigation_bar.pins": "Festa tuter", + "navigation_bar.preferences": "Preferanser", + "navigation_bar.public_timeline": "Federert tidslinje", + "navigation_bar.security": "Sikkerheit", + "notification.favourite": "{name} likte din status", + "notification.follow": "{name} fulgte deg", + "notification.mention": "{name} nevnte deg", + "notification.poll": "A poll you have voted in has ended", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Klarer notifikasjoner", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.filter_bar.advanced": "Display all categories", + "notifications.column_settings.filter_bar.category": "Quick filter bar", + "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.follow": "Nye føljare:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.reblog": "Framhevinger:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "notifications.filter.all": "All", + "notifications.filter.boosts": "Framhevinger", + "notifications.filter.favourites": "Favoritter", + "notifications.filter.follows": "Føljare", + "notifications.filter.mentions": "Mentions", + "notifications.filter.polls": "Poll results", + "notifications.group": "{count} notifications", + "poll.closed": "Closed", + "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.vote": "Vote", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Add a poll", + "poll_button.remove_poll": "Remove poll", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "refresh": "Refresh", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "status.admin_account": "Open moderation interface for @{name}", + "status.admin_status": "Open this status in the moderation interface", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.copy": "Copy link to status", + "status.delete": "Delete", + "status.detailed_status": "Detailed conversation view", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.filtered": "Filtered", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.read_more": "Read more", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", + "status.redraft": "Delete & re-draft", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "suggestions.dismiss": "Dismiss suggestion", + "suggestions.header": "You might be interested in…", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "time_remaining.days": "{number, plural, one {# day} other {# days}} left", + "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", + "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.moments": "Moments remaining", + "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media ({formats})", + "upload_error.limit": "File upload limit exceeded.", + "upload_error.poll": "File upload not allowed with polls.", + "upload_form.description": "Describe for the visually impaired", + "upload_form.edit": "Edit", + "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", + "upload_progress.label": "Uploading…", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index b9495f6b6985bd94ad2a719f42cdf3dc3a478b67..28f89863323581c959eafc17c18c5419134e5dd2 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -4,6 +4,7 @@ "account.block": "Blokkér @{name}", "account.block_domain": "Skjul alt fra {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Rediger profil", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Følger deg", "account.hide_reblogs": "Skjul fremhevinger fra @{name}", + "account.last_status": "Last active", "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.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Demp @{name}", "account.mute_notifications": "Ignorer varsler fra @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Innlegg", "account.posts_with_replies": "Toots with replies", "account.report": "Rapportér @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Avfølg", "account.unmute": "Avdemp @{name}", "account.unmute_notifications": "Vis varsler fra @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "You kan trykke {combo} for Ã¥ hoppe over dette neste gang", "bundle_column_error.body": "Noe gikk galt mens denne komponenten lastet.", "bundle_column_error.retry": "Prøv igjen", @@ -47,6 +53,7 @@ "column.blocks": "Blokkerte brukere", "column.community": "Lokal tidslinje", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Likt", "column.follow_requests": "Følgeforespørsler", @@ -64,7 +71,7 @@ "column_header.show_settings": "Vis innstillinger", "column_header.unpin": "Løsne", "column_subheading.settings": "Innstillinger", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "Denne tuten blir ikke listet under noen emneknagger da den er ulistet. Kun offentlige tuter kan søktes etter med emneknagg.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Er du sikker pÃ¥ at du vil slette denne listen permanent?", "confirmations.domain_block.confirm": "Skjul alt fra domenet", "confirmations.domain_block.message": "Er du sikker pÃ¥ at du vil skjule hele domenet {domain}? I de fleste tilfeller er det bedre med mÃ¥lrettet blokkering eller demping.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Demp", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Er du sikker pÃ¥ at du vil dempe {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Slutt Ã¥ følge", "confirmations.unfollow.message": "Er du sikker pÃ¥ at du vil slutte Ã¥ følge {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Kopier koden under for Ã¥ bygge inn denne statusen pÃ¥ hjemmesiden din.", "embed.preview": "Slik kommer det til Ã¥ se ut:", "emoji_button.activity": "Aktivitet", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "Du har ingen varsler ennÃ¥. Kommuniser med andre for Ã¥ begynne samtalen.", "empty_column.public": "Det er ingenting her! Skriv noe offentlig, eller følg brukere manuelt fra andre instanser for Ã¥ fylle den opp", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Ny listetittel", "lists.search": "Søk blant personer du følger", "lists.subheading": "Dine lister", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Laster...", "media_gallery.toggle_visible": "Veksle synlighet", "missing_indicator.label": "Ikke funnet", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Festa tuter", "navigation_bar.preferences": "Preferanser", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Felles tidslinje", "navigation_bar.security": "Security", "notification.favourite": "{name} likte din status", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Justér synlighet", @@ -295,6 +319,7 @@ "privacy.public.short": "Offentlig", "privacy.unlisted.long": "Ikke vis i offentlige tidslinjer", "privacy.unlisted.short": "Uoppført", + "refresh": "Refresh", "regeneration_indicator.label": "Laster…", "regeneration_indicator.sublabel": "Dine startside forberedes!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Vis mer", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Ikke demp samtale", "status.unpin": "Angre festing pÃ¥ profilen", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.", "upload_area.title": "Dra og slipp for Ã¥ laste opp", "upload_button.label": "Legg til media", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Beskriv for synshemmede", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Angre", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Laster opp...", "video.close": "Lukk video", "video.exit_fullscreen": "Lukk fullskjerm", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index f5c150d376282298caf49439073ff6dfafcbd2bf..1a2479ecb2d9f4371db33c2d2ed986ac573780bd 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -4,6 +4,7 @@ "account.block": "Blocar @{name}", "account.block_domain": "Tot amagar del domeni {domain}", "account.blocked": "Blocat", + "account.cancel_follow_request": "Anullar la demanda de seguiment", "account.direct": "Escriure un MP a @{name}", "account.domain_blocked": "Domeni amagat", "account.edit_profile": "Modificar lo perfil", @@ -15,6 +16,7 @@ "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.", "account.follows_you": "Vos sèc", "account.hide_reblogs": "Rescondre los partatges de @{name}", + "account.last_status": "Darrièra activitat", "account.link_verified_on": "La proprietat d’aqueste ligam foguèt verificada lo {date}", "account.locked_info": "L’estatut de privacitat del compte es configurat sus clavat. Lo proprietari causÃs qual pòt sègre son compte.", "account.media": "Mèdias", @@ -23,6 +25,7 @@ "account.mute": "Rescondre @{name}", "account.mute_notifications": "Rescondre las notificacions de @{name}", "account.muted": "Mes en silenci", + "account.never_active": "Jamai", "account.posts": "Tuts", "account.posts_with_replies": "Tuts e responsas", "account.report": "Senhalar @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Quitar de sègre", "account.unmute": "Quitar de rescondre @{name}", "account.unmute_notifications": "Mostrar las notificacions de @{name}", + "alert.rate_limited.message": "Mercés de tornar ensajar aprèp {retry_time, time, medium}.", + "alert.rate_limited.title": "Taus limitat", "alert.unexpected.message": "Una error s’es producha.", "alert.unexpected.title": "Ops !", + "autosuggest_hashtag.per_week": "{count} per setmana", "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven", "bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament d’aqueste compausant.", "bundle_column_error.retry": "Tornar ensajar", @@ -47,6 +53,7 @@ "column.blocks": "Personas blocadas", "column.community": "Flux public local", "column.direct": "Messatges dirèctes", + "column.directory": "Percórrer los perfils", "column.domain_blocks": "Domenis resconduts", "column.favourites": "Favorits", "column.follow_requests": "Demandas d’abonament", @@ -77,7 +84,7 @@ "compose_form.poll.remove_option": "Levar aquesta opcion", "compose_form.publish": "Tut", "compose_form.publish_loud": "{publish} !", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Marcar coma sensible", "compose_form.sensitive.marked": "Lo mèdia es marcat coma sensible", "compose_form.sensitive.unmarked": "Lo mèdia es pas marcat coma sensible", "compose_form.spoiler.marked": "Lo tèxte es rescondut jos l’avertiment", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Volètz vertadièrament suprimir aquesta lista per totjorn ?", "confirmations.domain_block.confirm": "Amagar tot lo domeni", "confirmations.domain_block.message": "Volètz vertadièrament blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.\nVeiretz pas cap de contengut d’aquel domeni dins cap de flux public o dins vòstras notificacions. Vòstres seguidors d’aquel domeni serà n levats.", + "confirmations.logout.confirm": "Desconnexion", + "confirmations.logout.message": "Volètz vertadièrament vos desconnectar ?", "confirmations.mute.confirm": "Rescondre", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Volètz vertadièrament rescondre {name} ?", "confirmations.redraft.confirm": "Escafar & tornar formular", "confirmations.redraft.message": "Volètz vertadièrament escafar aqueste estatut e lo reformular ? Totes sos partiments e favorits serà n perduts, e sas responsas serà n orfanèlas.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Respondre remplaçarà lo messatge que sètz a escriure. Volètz vertadièrament contunhar ?", "confirmations.unfollow.confirm": "Quitar de sègre", "confirmations.unfollow.message": "Volètz vertadièrament quitar de sègre {name} ?", + "conversation.delete": "Suprimir la conversacion", + "conversation.mark_as_read": "Marcar coma legida", + "conversation.open": "Veire la conversacion", + "conversation.with": "Amb {names}", + "directory.federated": "Del fediverse conegut", + "directory.local": "Solament de {domain}", + "directory.new_arrivals": "Nòus-venguts", + "directory.recently_active": "Actius fa res", "embed.instructions": "Embarcar aqueste estatut per lo far veire sus un site Internet en copiar lo còdi çai-jos.", "embed.preview": "Semblarà aquò :", "emoji_button.activity": "Activitats", @@ -134,6 +152,10 @@ "empty_column.mutes": "Encara avètz pas mes en silenci degun.", "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.", "empty_column.public": "I a pas res aquÃ ! Escrivètz quicòm de public, o seguètz de personas d’autres servidors per garnir lo flux public", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Senhalar un problèma", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -209,14 +231,14 @@ "keyboard_shortcuts.search": "anar a la recèrca", "keyboard_shortcuts.start": "dobrir la colomna « Per començar »", "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "per mostrar/rescondre los mèdias", "keyboard_shortcuts.toot": "començar un estatut tot novèl", "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca", "keyboard_shortcuts.up": "far montar dins la lista", "lightbox.close": "Tampar", "lightbox.next": "Seguent", "lightbox.previous": "Precedent", - "lightbox.view_context": "View context", + "lightbox.view_context": "Veire lo contèxt", "lists.account.add": "Ajustar a la lista", "lists.account.remove": "Levar de la lista", "lists.delete": "Suprimir la lista", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "TÃtol de la nòva lista", "lists.search": "Cercar demest lo monde que seguètz", "lists.subheading": "Vòstras listas", + "load_pending": "{count, plural, one {# nòu element} other {# nòu elements}}", "loading_indicator.label": "Cargament…", "media_gallery.toggle_visible": "Modificar la visibilitat", "missing_indicator.label": "Pas trobat", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favorits", "navigation_bar.filters": "Mots ignorats", "navigation_bar.follow_requests": "Demandas d’abonament", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Abonament e seguidors", "navigation_bar.info": "Tocant aqueste servidor", "navigation_bar.keyboard_shortcuts": "Acorchis clavièr", "navigation_bar.lists": "Listas", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Tuts penjats", "navigation_bar.preferences": "Preferéncias", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Flux public global", "navigation_bar.security": "Seguretat", "notification.favourite": "{name} a ajustat a sos favorits", @@ -282,8 +304,10 @@ "notifications.group": "{count} notificacions", "poll.closed": "Tampat", "poll.refresh": "Actualizar", + "poll.total_people": "{count, plural, one {# persona} other {# personas}}", "poll.total_votes": "{count, plural, one {# vòte} other {# vòtes}}", "poll.vote": "Votar", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Ajustar un sondatge", "poll_button.remove_poll": "Levar lo sondatge", "privacy.change": "Ajustar la confidencialitat del messatge", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Mostrar pas dins los fluxes publics", "privacy.unlisted.short": "Pas-listat", + "refresh": "Actualizar", "regeneration_indicator.label": "Cargament…", "regeneration_indicator.sublabel": "Sèm a preparar vòstre flux d’acuèlh !", "relative_time.days": "fa {number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Gents", "search_results.hashtags": "Etiquetas", "search_results.statuses": "Tuts", + "search_results.statuses_fts_disabled": "La recèrca de tuts per lor contengut es pas activada sus aqueste servidor Mastodon.", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "status.admin_account": "Dobrir l’interfà cia de moderacion per @{name}", "status.admin_status": "Dobrir aqueste estatut dins l’interfà cia de moderacion", @@ -358,6 +384,7 @@ "status.show_more": "Desplegar", "status.show_more_all": "Los desplegar totes", "status.show_thread": "Mostrar lo fil", + "status.uncached_media_warning": "Pas disponible", "status.unmute_conversation": "Tornar mostrar la conversacion", "status.unpin": "Tirar del perfil", "suggestions.dismiss": "Regetar la suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments restants", "time_remaining.seconds": "demòra{number, plural, one { # segonda} other {n # segondas}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} ne charra other {people}} ne charran", + "trends.trending_now": "Tendéncia del moment", "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.", "upload_area.title": "Lisatz e depausatz per mandar", "upload_button.label": "Ajustar un mèdia (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Talha maximum pels mandadÃs subrepassada.", "upload_error.poll": "Lo mandadÃs de fichièr es pas autorizat pels sondatges.", "upload_form.description": "Descripcion pels mal vesents", - "upload_form.focus": "Modificar l’apercebut", + "upload_form.edit": "Modificar", "upload_form.undo": "Suprimir", + "upload_modal.analyzing_picture": "Analisi de l’imatge…", + "upload_modal.apply": "Aplicar", + "upload_modal.description_placeholder": "Lo dròlle bilingüe manja un yaourt de ròcs exagonals e kiwis verds farà un an mai", + "upload_modal.detect_text": "Detectar lo tèxt de l’imatge", + "upload_modal.edit_media": "Modificar lo mèdia", + "upload_modal.hint": "Clicatz o lissatz lo cercle de l’apercebut per causir lo ponch que serà totjorn visible dins las vinhetas.", + "upload_modal.preview_label": "Apercebut ({ratio})", "upload_progress.label": "MandadÃs…", "video.close": "Tampar la vidèo", "video.exit_fullscreen": "Sortir plen ecran", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 3e170d1a171fcc5065bb3d307422b150415e263e..cf0c0188edc7c963737be4e1f5e16d1e85e83fe0 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -4,6 +4,7 @@ "account.block": "Blokuj @{name}", "account.block_domain": "Blokuj wszystko z {domain}", "account.blocked": "Zablokowany(-a)", + "account.cancel_follow_request": "Zrezygnuj z proÅ›by o możliwość Å›ledzenia", "account.direct": "WyÅ›lij wiadomość bezpoÅ›redniÄ… do @{name}", "account.domain_blocked": "Ukryto domenÄ™", "account.edit_profile": "Edytuj profil", @@ -15,6 +16,7 @@ "account.follows.empty": "Ten użytkownik nie Å›ledzi jeszcze nikogo.", "account.follows_you": "Åšledzi CiÄ™", "account.hide_reblogs": "Ukryj podbicia od @{name}", + "account.last_status": "Ostatnia aktywność", "account.link_verified_on": "WÅ‚asność tego odnoÅ›nika zostaÅ‚a potwierdzona {date}", "account.locked_info": "To konto jest prywatne. WÅ‚aÅ›ciciel rÄ™cznie wybiera kto może go Å›ledzić.", "account.media": "Zawartość multimedialna", @@ -23,6 +25,7 @@ "account.mute": "Wycisz @{name}", "account.mute_notifications": "Wycisz powiadomienia o @{name}", "account.muted": "Wyciszony", + "account.never_active": "Nigdy", "account.posts": "Wpisy", "account.posts_with_replies": "Wpisy i odpowiedzi", "account.report": "ZgÅ‚oÅ› @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "PrzestaÅ„ Å›ledzić", "account.unmute": "Cofnij wyciszenie @{name}", "account.unmute_notifications": "Cofnij wyciszenie powiadomieÅ„ od @{name}", + "alert.rate_limited.message": "Spróbuj ponownie po {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "WystÄ…piÅ‚ nieoczekiwany błąd.", "alert.unexpected.title": "O nie!", + "autosuggest_hashtag.per_week": "{count} co tydzieÅ„", "boost_modal.combo": "NaciÅ›nij {combo}, aby pominąć to nastÄ™pnym razem", "bundle_column_error.body": "CoÅ› poszÅ‚o nie tak podczas Å‚adowania tego skÅ‚adnika.", "bundle_column_error.retry": "Spróbuj ponownie", @@ -47,6 +53,7 @@ "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oÅ› czasu", "column.direct": "WiadomoÅ›ci bezpoÅ›rednie", + "column.directory": "PrzeglÄ…daj profile", "column.domain_blocks": "Ukryte domeny", "column.favourites": "Ulubione", "column.follow_requests": "ProÅ›by o Å›ledzenie", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Czy na pewno chcesz bezpowrotnie usunąć tÄ… listÄ™?", "confirmations.domain_block.confirm": "Ukryj wszysyko z domeny", "confirmations.domain_block.message": "Czy na pewno chcesz zablokować całą domenÄ™ {domain}? Zwykle lepszym rozwiÄ…zaniem jest blokada lub wyciszenie kilku użytkowników.", + "confirmations.logout.confirm": "Wyloguj", + "confirmations.logout.message": "Czy na pewno chcesz siÄ™ wylogować?", "confirmations.mute.confirm": "Wycisz", + "confirmations.mute.explanation": "To schowa ich i wspominajÄ…ce ich posty, ale wciąż pozwoli im widzieć twoje posty i Å›ledzić ciÄ™.", "confirmations.mute.message": "Czy na pewno chcesz wyciszyć {name}?", "confirmations.redraft.confirm": "UsuÅ„ i przeredaguj", "confirmations.redraft.message": "Czy na pewno chcesz usunąć i przeredagować ten wpis? Polubienia i podbicia zostanÄ… utracone, a odpowiedzi do oryginalnego wpisu zostanÄ… osierocone.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "W ten sposób utracisz wpis który obecnie tworzysz. Czy na pewno chcesz to zrobić?", "confirmations.unfollow.confirm": "PrzestaÅ„ Å›ledzić", "confirmations.unfollow.message": "Czy na pewno zamierzasz przestać Å›ledzić {name}?", + "conversation.delete": "UsuÅ„ rozmowÄ™", + "conversation.mark_as_read": "Oznacz jako przeczytane", + "conversation.open": "Zobacz rozmowÄ™", + "conversation.with": "Z {names}", + "directory.federated": "Ze znanego fediwersum", + "directory.local": "Tylko z {domain}", + "directory.new_arrivals": "NowoÅ›ci", + "directory.recently_active": "Ostatnio aktywne", "embed.instructions": "Osadź ten wpis na swojej stronie wklejajÄ…c poniższy kod.", "embed.preview": "Tak bÄ™dzie to wyglÄ…dać:", "emoji_button.activity": "Aktywność", @@ -134,6 +152,10 @@ "empty_column.mutes": "Nie wyciszyÅ‚eÅ›(-aÅ›) jeszcze żadnego użytkownika.", "empty_column.notifications": "Nie masz żadnych powiadomieÅ„. Rozpocznij interakcje z innymi użytkownikami.", "empty_column.public": "Tu nic nie ma! Napisz coÅ› publicznie, lub dodaj ludzi z innych serwerów, aby to wyÅ›wietlić", + "error.unexpected_crash.explanation": "W zwiÄ…zku z błędem w naszym kodzie lub braku kompatybilnoÅ›ci przeglÄ…darki, ta strona nie może być poprawnie wyÅ›wietlona.", + "error.unexpected_crash.next_steps": "Spróbuj odÅ›wieżyć stronÄ™. JeÅ›li to nie pomoże, wciąż jesteÅ› w stanie używać Mastodona przez innÄ… przeglÄ…darkÄ™ lub natywnÄ… aplikacjÄ™.", + "errors.unexpected_crash.copy_stacktrace": "Skopiuj Å›lad stosu do schowka", + "errors.unexpected_crash.report_issue": "ZgÅ‚oÅ› problem", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Wprowadź tytuÅ‚ listy", "lists.search": "Szukaj wÅ›ród osób które Å›ledzisz", "lists.subheading": "Twoje listy", + "load_pending": "{count, plural, one {# nowy przedmiot} other {nowe przedmioty}}", "loading_indicator.label": "Åadowanie…", "media_gallery.toggle_visible": "Przełącz widoczność", "missing_indicator.label": "Nie znaleziono", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Osobiste", "navigation_bar.pins": "PrzypiÄ™te wpisy", "navigation_bar.preferences": "Preferencje", - "navigation_bar.profile_directory": "Katalog profilów", "navigation_bar.public_timeline": "Globalna oÅ› czasu", "navigation_bar.security": "BezpieczeÅ„stwo", "notification.favourite": "{name} dodaÅ‚(a) Twój wpis do ulubionych", @@ -282,8 +304,10 @@ "notifications.group": "{count, number} {count, plural, one {powiadomienie} few {powiadomienia} many {powiadomieÅ„} more {powiadomieÅ„}}", "poll.closed": "ZamkniÄ™te", "poll.refresh": "OdÅ›wież", + "poll.total_people": "{count, plural, one {# osoba} few {# osoby} many {# osób} other {# osób}}", "poll.total_votes": "{count, plural, one {# gÅ‚os} few {# gÅ‚osy} many {# gÅ‚osów} other {# gÅ‚osów}}", "poll.vote": "ZagÅ‚osuj", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Dodaj gÅ‚osowanie", "poll_button.remove_poll": "UsuÅ„ gÅ‚osowanie", "privacy.change": "Dostosuj widoczność wpisów", @@ -295,6 +319,7 @@ "privacy.public.short": "Publiczny", "privacy.unlisted.long": "Niewidoczny na publicznych osiach czasu", "privacy.unlisted.short": "Niewidoczny", + "refresh": "OdÅ›wież", "regeneration_indicator.label": "Åaduję…", "regeneration_indicator.sublabel": "Twoja oÅ› czasu jest przygotowywana!", "relative_time.days": "{number} dni", @@ -319,6 +344,7 @@ "search_results.accounts": "Ludzie", "search_results.hashtags": "Hashtagi", "search_results.statuses": "Wpisy", + "search_results.statuses_fts_disabled": "Szukanie wpisów przy pomocy ich zawartoÅ›ci nie jest włączone na tym serwerze Mastodona.", "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}", "status.admin_account": "Otwórz interfejs moderacyjny dla @{name}", "status.admin_status": "Otwórz ten wpis w interfejsie moderacyjnym", @@ -358,6 +384,7 @@ "status.show_more": "RozwiÅ„", "status.show_more_all": "RozwiÅ„ wszystkie", "status.show_thread": "Pokaż wÄ…tek", + "status.uncached_media_warning": "NiedostÄ™pne", "status.unmute_conversation": "Cofnij wyciszenie konwersacji", "status.unpin": "Odepnij z profilu", "suggestions.dismiss": "Odrzuć sugestiÄ™", @@ -373,14 +400,22 @@ "time_remaining.moments": "PozostaÅ‚a chwila", "time_remaining.seconds": "{number, plural, one {PozostaÅ‚a # sekunda} few {PozostaÅ‚y # sekundy} many {PozostaÅ‚o # sekund} other {PozostaÅ‚o # sekund}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {osoba rozmawia} few {osoby rozmawiajÄ…} other {osób rozmawia}} o tym", + "trends.trending_now": "Popularne teraz", "ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuÅ›cisz Mastodona.", "upload_area.title": "PrzeciÄ…gnij i upuść aby wysÅ‚ać", "upload_button.label": "Dodaj zawartość multimedialnÄ… (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Przekroczono limit plików do wysÅ‚ania.", "upload_error.poll": "Dołączanie plików nie dozwolone z gÅ‚osowaniami.", "upload_form.description": "Wprowadź opis dla niewidomych i niedowidzÄ…cych", - "upload_form.focus": "Dopasuj podglÄ…d", + "upload_form.edit": "Edytuj", "upload_form.undo": "UsuÅ„", + "upload_modal.analyzing_picture": "Analizowanie obrazu…", + "upload_modal.apply": "Zastosuj", + "upload_modal.description_placeholder": "Pchnąć w tÄ™ łódź jeża lub oÅ›m skrzyÅ„ fig", + "upload_modal.detect_text": "Wykryj tekst ze obrazu", + "upload_modal.edit_media": "Edytuj multimedia", + "upload_modal.hint": "Kliknij lub przeciÄ…gnij kółko na podglÄ…dzie by wybrać centralny punkt, który zawsze bÄ™dzie na widoku na miniaturce.", + "upload_modal.preview_label": "PodglÄ…d ({ratio})", "upload_progress.label": "WysyÅ‚anie…", "video.close": "Zamknij film", "video.exit_fullscreen": "Opuść tryb peÅ‚noekranowy", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index a221584171e79d526e654c81d54c133bf14a547a..86322e1e12c94971a99f28939c268ed075b17153 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -4,6 +4,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo de {domain}", "account.blocked": "Bloqueado", + "account.cancel_follow_request": "Cancelar solicitação para seguir", "account.direct": "Direct Message @{name}", "account.domain_blocked": "DomÃnio escondido", "account.edit_profile": "Editar perfil", @@ -15,6 +16,7 @@ "account.follows.empty": "Esse usuário não segue ninguém no momento.", "account.follows_you": "Segue você", "account.hide_reblogs": "Esconder compartilhamentos de @{name}", + "account.last_status": "Última atividade", "account.link_verified_on": "A posse desse link foi verificada em {date}", "account.locked_info": "Essa conta está trancada. Se você a seguir sua solicitação será revisada manualmente.", "account.media": "MÃdia", @@ -23,6 +25,7 @@ "account.mute": "Silenciar @{name}", "account.mute_notifications": "Silenciar notificações de @{name}", "account.muted": "Silenciado", + "account.never_active": "Nunca", "account.posts": "Toots", "account.posts_with_replies": "Toots e respostas", "account.report": "Denunciar @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Deixar de seguir", "account.unmute": "Não silenciar @{name}", "account.unmute_notifications": "Retirar silêncio das notificações vindas de @{name}", + "alert.rate_limited.message": "Por favor tente novamente após {retry_time, time, medium}.", + "alert.rate_limited.title": "Limite de tentativas", "alert.unexpected.message": "Um erro inesperado ocorreu.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.title": "Eita!", + "autosuggest_hashtag.per_week": "{count} por semana", "boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez", "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.", "bundle_column_error.retry": "Tente novamente", @@ -47,6 +53,7 @@ "column.blocks": "Usuários bloqueados", "column.community": "Local", "column.direct": "Mensagens diretas", + "column.directory": "Explorar perfis", "column.domain_blocks": "DomÃnios escondidos", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores pendentes", @@ -77,7 +84,7 @@ "compose_form.poll.remove_option": "Remover essa opção", "compose_form.publish": "Publicar", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Marcar mÃdia como sensÃvel", "compose_form.sensitive.marked": "MÃdia está marcada como sensÃvel", "compose_form.sensitive.unmarked": "MÃdia não está marcada como sensÃvel", "compose_form.spoiler.marked": "O texto está escondido por um aviso de conteúdo", @@ -89,11 +96,14 @@ "confirmations.block.message": "Você tem certeza de que quer bloquear {name}?", "confirmations.delete.confirm": "Excluir", "confirmations.delete.message": "Você tem certeza de que quer excluir esta postagem?", - "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.confirm": "Excluir", "confirmations.delete_list.message": "Você tem certeza que quer deletar permanentemente a lista?", "confirmations.domain_block.confirm": "Esconder o domÃnio inteiro", "confirmations.domain_block.message": "Você quer mesmo bloquear {domain} inteiro? Na maioria dos casos, silenciar ou bloquear alguns usuários é o suficiente e o recomendado. Você não vai ver conteúdo desse domÃnio em nenhuma das timelines públicas ou nas suas notificações. Seus seguidores desse domÃnio serão removidos.", + "confirmations.logout.confirm": "Sair", + "confirmations.logout.message": "Tem certeza que deseja encerrar a sessão?", "confirmations.mute.confirm": "Silenciar", + "confirmations.mute.explanation": "Isto irá esconder postagens e postagens que mencionam, mas ainda vai permitir que eles vejam suas publicações e sigam você.", "confirmations.mute.message": "Você tem certeza de que quer silenciar {name}?", "confirmations.redraft.confirm": "Apagar & usar como rascunho", "confirmations.redraft.message": "Você tem certeza que deseja apagar esse status e usá-lo como rascunho? Seus compartilhamentos e favoritos serão perdidos e as respostas ao toot original ficarão desconectadas.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Responder agora vai sobrescrever a mensagem que você está compondo. Você tem certeza que quer continuar?", "confirmations.unfollow.confirm": "Deixar de seguir", "confirmations.unfollow.message": "Você tem certeza de que quer deixar de seguir {name}?", + "conversation.delete": "Excluir conversa", + "conversation.mark_as_read": "Marcar como lida", + "conversation.open": "Ver conversa", + "conversation.with": "Com {names}", + "directory.federated": "De fediverso conhecido", + "directory.local": "De {domain} apenas", + "directory.new_arrivals": "Acabaram de chegar", + "directory.recently_active": "Reverta esta propriedade para seu valor padrão", "embed.instructions": "Incorpore esta postagem em seu site copiando o código abaixo.", "embed.preview": "Aqui está uma previsão de como ficará:", "emoji_button.activity": "Atividades", @@ -134,6 +152,10 @@ "empty_column.mutes": "Você ainda não silenciou nenhum usuário.", "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.", "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias", + "error.unexpected_crash.explanation": "Devido a um bug em nosso código ou a um problema de compatibilidade do navegador, esta página não pode ser exibida corretamente.", + "error.unexpected_crash.next_steps": "Tente atualizar a página. Se isso não ajudar, você ainda pode usar Mastodon através de um navegador diferente ou aplicativo nativo.", + "errors.unexpected_crash.copy_stacktrace": "Copiar stacktrace para a área de transferência", + "errors.unexpected_crash.report_issue": "Reportar problema", "federation.change": "Ajustar federação do toot", "federation.federated.long": "Permitir que o toot chegue a outras instâncias", "federation.federated.short": "Federado", @@ -163,11 +185,11 @@ "home.column_settings.show_replies": "Mostrar as respostas", "intervals.full.days": "{number, plural, one {# dia} other {# dias}}", "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "introduction.federation.action": "Próximo", - "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.headline": "Global", "introduction.federation.federated.text": "Posts públicos de outros servidores do fediverso vão aparecer na timeline global.", - "introduction.federation.home.headline": "Home", + "introduction.federation.home.headline": "InÃcio", "introduction.federation.home.text": "Posts de pessoas que você segue vão aparecer na sua página inicial. Você pode seguir pessoas de qualquer servidor!", "introduction.federation.local.headline": "Local", "introduction.federation.local.text": "Posts públicos de pessoas no mesmo servidor que você vão aparecer na timeline local.", @@ -209,23 +231,24 @@ "keyboard_shortcuts.search": "para focar a pesquisa", "keyboard_shortcuts.start": "para abrir a coluna \"primeiros passos\"", "keyboard_shortcuts.toggle_hidden": "mostrar/esconder o texto com aviso de conteúdo", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "mostrar/esconder mÃdia", "keyboard_shortcuts.toot": "para compor um novo toot", "keyboard_shortcuts.unfocus": "para remover o foco da área de composição/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", "lightbox.close": "Fechar", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", - "lightbox.view_context": "View context", + "lightbox.view_context": "Ver contexto", "lists.account.add": "Adicionar a listas", "lists.account.remove": "Remover da lista", - "lists.delete": "Delete list", + "lists.delete": "Excluir lista", "lists.edit": "Editar lista", "lists.edit.submit": "Mudar o tÃtulo", "lists.new.create": "Adicionar lista", "lists.new.title_placeholder": "Novo tÃtulo da lista", "lists.search": "Procurar entre as pessoas que você segue", "lists.subheading": "Suas listas", + "load_pending": "{count, plural, one {# novo item} other {# novos items}}", "loading_indicator.label": "Carregando...", "media_gallery.toggle_visible": "Esconder/Mostrar", "missing_indicator.label": "Não encontrado", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favoritos", "navigation_bar.filters": "Palavras silenciadas", "navigation_bar.follow_requests": "Seguidores pendentes", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Seguindo e seguidores", "navigation_bar.info": "Mais informações", "navigation_bar.keyboard_shortcuts": "Atalhos de teclado", "navigation_bar.lists": "Listas", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Pessoal", "navigation_bar.pins": "Postagens fixadas", "navigation_bar.preferences": "Preferências", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Global", "navigation_bar.security": "Segurança", "notification.favourite": "{name} adicionou a sua postagem aos favoritos", @@ -282,8 +304,10 @@ "notifications.group": "{count} notificações", "poll.closed": "Fechada", "poll.refresh": "Atualizar", + "poll.total_people": "{count, plural, one {# pessoa} other {# pessoas}}", "poll.total_votes": "{count, plural, one {# voto} other {# votos}}", "poll.vote": "Votar", + "poll.voted": "Você votou nesta resposta", "poll_button.add_poll": "Adicionar uma enquete", "poll_button.remove_poll": "Remover enquete", "privacy.change": "Ajustar a privacidade da mensagem", @@ -295,6 +319,7 @@ "privacy.public.short": "Pública", "privacy.unlisted.long": "Não publicar em feeds públicos", "privacy.unlisted.short": "Não listada", + "refresh": "Atualizar", "regeneration_indicator.label": "Carregando…", "regeneration_indicator.sublabel": "Sua página inicial está sendo preparada!", "relative_time.days": "{number}d", @@ -319,10 +344,11 @@ "search_results.accounts": "Pessoas", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Pesquisar toots por seu conteúdo não está habilitado neste servidor Mastodon.", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "status.admin_account": "Abrir interface de moderação para @{name}", "status.admin_status": "Abrir esse status na interface de moderação", - "status.block": "Block @{name}", + "status.block": "Bloquear @{name}", "status.cancel_reblog_private": "Desfazer compartilhamento", "status.cannot_reblog": "Esta postagem não pode ser compartilhada", "status.copy": "Copiar o link para o status", @@ -358,6 +384,7 @@ "status.show_more": "Mostrar mais", "status.show_more_all": "Mostrar mais para todas as mensagens", "status.show_thread": "Mostrar sequência", + "status.uncached_media_warning": "Não disponÃvel", "status.unmute_conversation": "Desativar silêncio desta conversa", "status.unpin": "Desafixar do perfil", "suggestions.dismiss": "Ignorar a sugestão", @@ -373,14 +400,22 @@ "time_remaining.moments": "Momentos restantes", "time_remaining.seconds": "{number, plural, one {# segundo restante} other {# segundos restantes}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {pessoa} other {pessoas}} falando sobre", + "trends.trending_now": "Em alta no momento", "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.", "upload_area.title": "Arraste e solte para enviar", "upload_button.label": "Adicionar mÃdia (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Limite de envio de arquivos excedido.", "upload_error.poll": "Envio de arquivos não é permitido com enquetes.", "upload_form.description": "Descreva a imagem para deficientes visuais", - "upload_form.focus": "Ajustar foco", + "upload_form.edit": "Editar", "upload_form.undo": "Remover", + "upload_modal.analyzing_picture": "Analisando imagem…", + "upload_modal.apply": "Aplicar", + "upload_modal.description_placeholder": "Grave e cabisbaixo, o filho justo zelava pela querida mãe doente", + "upload_modal.detect_text": "Detectar texto da imagem", + "upload_modal.edit_media": "Editar mÃdia", + "upload_modal.hint": "Clique ou arraste o cÃrculo na visualização para escolher o ponto focal que sempre será visto em todas as miniaturas.", + "upload_modal.preview_label": "Prévia ({ratio})", "upload_progress.label": "Salvando...", "video.close": "Fechar vÃdeo", "video.exit_fullscreen": "Sair da tela cheia", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt-PT.json similarity index 69% rename from app/javascript/mastodon/locales/pt.json rename to app/javascript/mastodon/locales/pt-PT.json index ef7434196c9a8b1650f798444280585277c123a9..c3b1ea17136bda3623b3a57d21d586e404fd2725 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -4,6 +4,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo do domÃnio {domain}", "account.blocked": "Bloqueado", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Mensagem directa @{name}", "account.domain_blocked": "DomÃnio escondido", "account.edit_profile": "Editar perfil", @@ -15,14 +16,16 @@ "account.follows.empty": "Este utilizador ainda não segue alguém.", "account.follows_you": "É teu seguidor", "account.hide_reblogs": "Esconder partilhas de @{name}", + "account.last_status": "Last active", "account.link_verified_on": "A posse deste link foi verificada em {date}", "account.locked_info": "O estatuto de privacidade desta conta é fechado. O dono revê manualmente que a pode seguir.", - "account.media": "Media", + "account.media": "Média", "account.mention": "Mencionar @{name}", "account.moved_to": "{name} mudou a sua conta para:", "account.mute": "Silenciar @{name}", "account.mute_notifications": "Silenciar notificações de @{name}", "account.muted": "Silenciada", + "account.never_active": "Never", "account.posts": "Publicações", "account.posts_with_replies": "Publicações e respostas", "account.report": "Denunciar @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Deixar de seguir", "account.unmute": "Não silenciar @{name}", "account.unmute_notifications": "Deixar de silenciar @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Ocorreu um erro inesperado.", "alert.unexpected.title": "Bolas!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Pode clicar {combo} para não voltar a ver", "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.", "bundle_column_error.retry": "Tente de novo", @@ -47,53 +53,57 @@ "column.blocks": "Utilizadores Bloqueados", "column.community": "Cronologia local", "column.direct": "Mensagens directas", + "column.directory": "Browse profiles", "column.domain_blocks": "DomÃnios escondidos", "column.favourites": "Favoritos", - "column.follow_requests": "Seguidores Pendentes", + "column.follow_requests": "Seguidores pendentes", "column.home": "InÃcio", "column.lists": "Listas", "column.mutes": "Utilizadores silenciados", "column.notifications": "Notificações", "column.pins": "Publicações fixas", - "column.public": "Cronologia federativa", + "column.public": "Cronologia federada", "column_back_button.label": "Voltar", - "column_header.hide_settings": "Esconder preferências", + "column_header.hide_settings": "Esconder configurações", "column_header.moveLeft_settings": "Mover coluna para a esquerda", "column_header.moveRight_settings": "Mover coluna para a direita", "column_header.pin": "Fixar", - "column_header.show_settings": "Mostrar preferências", + "column_header.show_settings": "Mostrar configurações", "column_header.unpin": "Desafixar", - "column_subheading.settings": "Preferências", - "community.column_settings.media_only": "Somente media", - "compose_form.direct_message_warning": "Esta publicação só será enviada para os utilizadores mencionados.", - "compose_form.direct_message_warning_learn_more": "Aprender mais", - "compose_form.hashtag_warning": "Esta pulbicacção não será listada em nenhuma hashtag por ser não listada. Somente publicações públicas podem ser pesquisadas por hashtag.", + "column_subheading.settings": "Configurações", + "community.column_settings.media_only": "Somente multimédia", + "compose_form.direct_message_warning": "Esta publicação será enviada apenas para os utilizadores mencionados.", + "compose_form.direct_message_warning_learn_more": "Conhecer mais", + "compose_form.hashtag_warning": "Este toot não será listado em nenhuma hashtag por ser não listado. Apenas toots públics podem ser pesquisados por hashtag.", "compose_form.lock_disclaimer": "A tua conta não está {locked}. Qualquer pessoa pode seguir-te e ver as publicações direcionadas apenas a seguidores.", - "compose_form.lock_disclaimer.lock": "fechada", + "compose_form.lock_disclaimer.lock": "bloqueado", "compose_form.placeholder": "Em que estás a pensar?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", - "compose_form.publish": "Publicar", - "compose_form.publish_loud": "{publicar}!", - "compose_form.sensitive.hide": "Mark media as sensitive", - "compose_form.sensitive.marked": "Media marcado como sensÃvel", - "compose_form.sensitive.unmarked": "Media não está marcado como sensÃvel", + "compose_form.poll.add_option": "Adicionar uma opção", + "compose_form.poll.duration": "Duração da votação", + "compose_form.poll.option_placeholder": "Opção {number}", + "compose_form.poll.remove_option": "Eliminar esta opção", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.hide": "Marcar multimédia como sensÃvel", + "compose_form.sensitive.marked": "Média marcada como sensÃvel", + "compose_form.sensitive.unmarked": "Média não está marcada como sensÃvel", "compose_form.spoiler.marked": "Texto escondido atrás de aviso", "compose_form.spoiler.unmarked": "O texto não está escondido", "compose_form.spoiler_placeholder": "Escreve o teu aviso aqui", "confirmation_modal.cancel": "Cancelar", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "Bloquear e denunciar", "confirmations.block.confirm": "Bloquear", "confirmations.block.message": "De certeza que queres bloquear {name}?", "confirmations.delete.confirm": "Eliminar", "confirmations.delete.message": "De certeza que queres eliminar esta publicação?", - "confirmations.delete_list.confirm": "Apagar", - "confirmations.delete_list.message": "Tens a certeza de que desejas apagar permanentemente esta lista?", + "confirmations.delete_list.confirm": "Eliminar", + "confirmations.delete_list.message": "Tens a certeza de que desejas eliminar permanentemente esta lista?", "confirmations.domain_block.confirm": "Esconder tudo deste domÃnio", - "confirmations.domain_block.message": "De certeza que queres bloquear completamente o domÃnio {domain}? Na maioria dos casos, silenciar ou bloquear alguns utilizadores é o suficiente e o recomendado. Não irás ver conteúdo daquele domÃnio em cronologia alguma, nem nas tuas notificações. Os teus seguidores daquele domÃnio serão removidos.", + "confirmations.domain_block.message": "De certeza que queres bloquear completamente o domÃnio {domain}? Na maioria dos casos, silenciar ou bloquear alguns utilizadores é suficiente e é o recomendado. Não irás ver conteúdo daquele domÃnio em cronologia alguma nem nas tuas notificações. Os teus seguidores daquele domÃnio serão removidos.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Silenciar", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "De certeza que queres silenciar {name}?", "confirmations.redraft.confirm": "Apagar & redigir", "confirmations.redraft.message": "Tens a certeza que queres apagar e redigir esta publicação? Os favoritos e as partilhas perder-se-ão e as respostas à publicação original ficarão órfãs.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Responder agora irá reescrever a mensagem que estás a compor actualmente. Tens a certeza que queres continuar?", "confirmations.unfollow.confirm": "Deixar de seguir", "confirmations.unfollow.message": "De certeza que queres deixar de seguir {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Publica esta publicação no teu site copiando o código abaixo.", "embed.preview": "Podes ver aqui como irá ficar:", "emoji_button.activity": "Actividade", @@ -109,23 +127,23 @@ "emoji_button.food": "Comida & Bebida", "emoji_button.label": "Inserir Emoji", "emoji_button.nature": "Natureza", - "emoji_button.not_found": "Não tem emojos!! (╯°□°)╯︵ â”»â”â”»", + "emoji_button.not_found": "Não tem emojis!! (╯°□°)╯︵ â”»â”â”»", "emoji_button.objects": "Objectos", "emoji_button.people": "Pessoas", - "emoji_button.recent": "Regularmente utilizados", - "emoji_button.search": "Procurar...", + "emoji_button.recent": "Utilizados regularmente", + "emoji_button.search": "Pesquisar...", "emoji_button.search_results": "Resultados da pesquisa", "emoji_button.symbols": "SÃmbolos", "emoji_button.travel": "Viagens & Lugares", - "empty_column.account_timeline": "Sem publicações!", - "empty_column.account_unavailable": "Profile unavailable", + "empty_column.account_timeline": "Sem toots por aqui!", + "empty_column.account_unavailable": "Perfil indisponÃvel", "empty_column.blocks": "Ainda não bloqueaste qualquer utilizador.", - "empty_column.community": "Ainda não existe conteúdo local para mostrar!", + "empty_column.community": "A timeline local está vazia. Escreve algo publicamente para começar!", "empty_column.direct": "Ainda não tens qualquer mensagem directa. Quando enviares ou receberes alguma, ela irá aparecer aqui.", "empty_column.domain_blocks": "Ainda não há qualquer domÃnio escondido.", - "empty_column.favourited_statuses": "Ainda não tens quaisquer publicações favoritas. Quando tiveres alguma, ela irá aparecer aqui.", - "empty_column.favourites": "Ainda ninguém favorizou esta publicação. Quando alguém o fizer, ela irá aparecer aqui.", - "empty_column.follow_requests": "Ainda não tens pedido de seguimento algum. Quando receberes algum, ele irá aparecer aqui.", + "empty_column.favourited_statuses": "Ainda não tens quaisquer toots favoritos. Quando tiveres algum, ele irá aparecer aqui.", + "empty_column.favourites": "Ainda ninguém marcou este toot como favorito. Quando alguém o fizer, ele irá aparecer aqui.", + "empty_column.follow_requests": "Ainda não tens nenhum pedido de seguimento. Quando receberes algum, ele irá aparecer aqui.", "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", "empty_column.home.public_timeline": "Cronologia pública", @@ -134,6 +152,10 @@ "empty_column.mutes": "Ainda não silenciaste qualquer utilizador.", "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para veres aqui os conteúdos públicos", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Ajustar federação do toot", "federation.federated.long": "Permitir que o toot chegue a outras instâncias", "federation.federated.short": "Federado", @@ -143,10 +165,10 @@ "follow_request.reject": "Rejeitar", "getting_started.developers": "Responsáveis pelo desenvolvimento", "getting_started.directory": "Directório de perfil", - "getting_started.documentation": "Documentation", + "getting_started.documentation": "Documentação", "getting_started.heading": "Primeiros passos", "getting_started.invite": "Convidar pessoas", - "getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}.", + "getting_started.open_source_notice": "Mastodon é software de código aberto (open source). Podes contribuir ou reportar problemas no GitHub do projecto: {github}.", "getting_started.security": "Segurança", "getting_started.terms": "Termos de serviço", "hashtag.column_header.tag_mode.all": "e {additional}", @@ -159,28 +181,28 @@ "hashtag.column_settings.tag_mode.none": "Nenhum destes", "hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionais para esta coluna", "home.column_settings.basic": "Básico", - "home.column_settings.show_reblogs": "Mostrar as partilhas", - "home.column_settings.show_replies": "Mostrar as respostas", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "home.column_settings.show_reblogs": "Mostrar boosts", + "home.column_settings.show_replies": "Mostrar respostas", + "intervals.full.days": "{number, plural, one {# dia} other {# dias}}", + "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}", + "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "introduction.federation.action": "Seguinte", - "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.headline": "Federada", "introduction.federation.federated.text": "Publicações públicas de outros servidores do fediverse aparecerão na cronologia federativa.", - "introduction.federation.home.headline": "Home", + "introduction.federation.home.headline": "InÃcio", "introduction.federation.home.text": "As publicações das pessoas que tu segues aparecerão na tua coluna inicial. Tu podes seguir qualquer pessoa em qualquer servidor!", "introduction.federation.local.headline": "Local", "introduction.federation.local.text": "Publicações públicas de pessoas que tu segues no teu servidor aparecerão na coluna local.", "introduction.interactions.action": "Terminar o tutorial!", "introduction.interactions.favourite.headline": "Favorito", - "introduction.interactions.favourite.text": "Tu podes guardar um toot para depois e deixar o autor saber que gostaste dele, favoritando-o.", - "introduction.interactions.reblog.headline": "Partilhar", + "introduction.interactions.favourite.text": "Podes guardar um toot para depois e deixar o autor saber que gostaste dele, marcando-o como favorito.", + "introduction.interactions.reblog.headline": "Boost", "introduction.interactions.reblog.text": "Podes partilhar os toots de outras pessoas com os teus seguidores partilhando-os.", "introduction.interactions.reply.headline": "Responder", "introduction.interactions.reply.text": "Tu podes responder a toots de outras pessoas e aos teus, o que os irá juntar numa conversa.", "introduction.welcome.action": "Vamos!", "introduction.welcome.headline": "Primeiros passos", - "introduction.welcome.text": "Bem-vindo ao fediverse! Em pouco tempo poderás enviar mensagens e falar com os teus amigos numa grande variedade de servidores. Mas este servidor, {domain}, é especial—ele alberga o teu perfil. Por isso, lembra-te do seu nome.", + "introduction.welcome.text": "Bem-vindo ao fediverso! Em pouco tempo poderás enviar mensagens e falar com os teus amigos numa grande variedade de servidores. Mas este servidor, {domain}, é especial—ele alberga o teu perfil. Por isso, lembra-te do seu nome.", "keyboard_shortcuts.back": "para voltar", "keyboard_shortcuts.blocked": "para abrir a lista de utilizadores bloqueados", "keyboard_shortcuts.boost": "para partilhar", @@ -189,10 +211,10 @@ "keyboard_shortcuts.description": "Descrição", "keyboard_shortcuts.direct": "para abrir a coluna das mensagens directas", "keyboard_shortcuts.down": "para mover para baixo na lista", - "keyboard_shortcuts.enter": "para expandir uma publicação", + "keyboard_shortcuts.enter": "para expandir um estado", "keyboard_shortcuts.favourite": "para adicionar aos favoritos", "keyboard_shortcuts.favourites": "para abrir a lista dos favoritos", - "keyboard_shortcuts.federated": "para abrir a cronologia federativa", + "keyboard_shortcuts.federated": "para abrir a cronologia federada", "keyboard_shortcuts.heading": "Atalhos do teclado", "keyboard_shortcuts.home": "para abrir a cronologia inicial", "keyboard_shortcuts.hotkey": "Atalho", @@ -209,31 +231,32 @@ "keyboard_shortcuts.search": "para focar na pesquisa", "keyboard_shortcuts.start": "para abrir a coluna dos \"primeiros passos\"", "keyboard_shortcuts.toggle_hidden": "para mostrar/esconder texto atrás de CW", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", - "keyboard_shortcuts.toot": "para compor um novo post", - "keyboard_shortcuts.unfocus": "para remover o foco da área de publicação/pesquisa", + "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar média", + "keyboard_shortcuts.toot": "para compor um novo toot", + "keyboard_shortcuts.unfocus": "para remover o foco da área de texto/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", "lightbox.close": "Fechar", "lightbox.next": "Próximo", "lightbox.previous": "Anterior", - "lightbox.view_context": "View context", + "lightbox.view_context": "Ver contexto", "lists.account.add": "Adicionar à lista", "lists.account.remove": "Remover da lista", - "lists.delete": "Delete list", + "lists.delete": "Remover lista", "lists.edit": "Editar lista", "lists.edit.submit": "Mudar o tÃtulo", "lists.new.create": "Adicionar lista", - "lists.new.title_placeholder": "Novo tÃtulo da lista", + "lists.new.title_placeholder": "TÃtulo da nova lista", "lists.search": "Pesquisa entre as pessoas que segues", "lists.subheading": "As tuas listas", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "A carregar...", - "media_gallery.toggle_visible": "Esconder/Mostrar", + "media_gallery.toggle_visible": "Mostrar/ocultar", "missing_indicator.label": "Não encontrado", "missing_indicator.sublabel": "Este recurso não foi encontrado", "mute_modal.hide_notifications": "Esconder notificações deste utilizador?", "navigation_bar.apps": "Aplicações móveis", "navigation_bar.blocks": "Utilizadores bloqueados", - "navigation_bar.community_timeline": "Local", + "navigation_bar.community_timeline": "Cronologia local", "navigation_bar.compose": "Escrever novo toot", "navigation_bar.direct": "Mensagens directas", "navigation_bar.discover": "Descobrir", @@ -242,23 +265,22 @@ "navigation_bar.favourites": "Favoritos", "navigation_bar.filters": "Palavras silenciadas", "navigation_bar.follow_requests": "Seguidores pendentes", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Seguindo e seguidores", "navigation_bar.info": "Sobre este servidor", "navigation_bar.keyboard_shortcuts": "Atalhos de teclado", "navigation_bar.lists": "Listas", "navigation_bar.logout": "Sair", "navigation_bar.mutes": "Utilizadores silenciados", - "navigation_bar.personal": "Personal", - "navigation_bar.pins": "Posts fixos", + "navigation_bar.personal": "Pessoal", + "navigation_bar.pins": "Toots afixados", "navigation_bar.preferences": "Preferências", - "navigation_bar.profile_directory": "Profile directory", - "navigation_bar.public_timeline": "Global", + "navigation_bar.public_timeline": "Cronologia federada", "navigation_bar.security": "Segurança", - "notification.favourite": "{name} adicionou o teu post aos favoritos", - "notification.follow": "{name} seguiu-te", + "notification.favourite": "{name} adicionou o teu estado aos favoritos", + "notification.follow": "{name} começou a seguir-te", "notification.mention": "{name} mencionou-te", - "notification.poll": "A poll you have voted in has ended", - "notification.reblog": "{name} partilhou o teu post", + "notification.poll": "Uma votação em participaste chegou ao fim", + "notification.reblog": "{name} fez boost ao teu o teu estado", "notifications.clear": "Limpar notificações", "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?", "notifications.column_settings.alert": "Notificações no computador", @@ -268,24 +290,26 @@ "notifications.column_settings.filter_bar.show": "Mostrar", "notifications.column_settings.follow": "Novos seguidores:", "notifications.column_settings.mention": "Menções:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "Resultados da votação:", "notifications.column_settings.push": "Notificações Push", - "notifications.column_settings.reblog": "Partilhas:", - "notifications.column_settings.show": "Mostrar nas colunas", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Mostrar na coluna", "notifications.column_settings.sound": "Reproduzir som", "notifications.filter.all": "Todas", - "notifications.filter.boosts": "Partilhas", - "notifications.filter.favourites": "Favoritas", + "notifications.filter.boosts": "Boosts", + "notifications.filter.favourites": "Favoritos", "notifications.filter.follows": "Seguimento", "notifications.filter.mentions": "Referências", - "notifications.filter.polls": "Poll results", + "notifications.filter.polls": "Resultados da votação", "notifications.group": "{count} notificações", "poll.closed": "Fechado", "poll.refresh": "Recarregar", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{contar, plural, um {# vote} outro {# votes}}", "poll.vote": "Votar", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Adicionar votação", + "poll_button.remove_poll": "Remover votação", "privacy.change": "Ajustar a privacidade da mensagem", "privacy.direct.long": "Apenas para utilizadores mencionados", "privacy.direct.short": "Directo", @@ -295,6 +319,7 @@ "privacy.public.short": "Público", "privacy.unlisted.long": "Não publicar nos feeds públicos", "privacy.unlisted.short": "Não listar", + "refresh": "Refresh", "regeneration_indicator.label": "A carregar…", "regeneration_indicator.sublabel": "A tua home está a ser preparada!", "relative_time.days": "{number}d", @@ -305,26 +330,27 @@ "reply_indicator.cancel": "Cancelar", "report.forward": "Reenviar para {target}", "report.forward_hint": "A conta é de outro servidor. Enviar uma cópia anónima do relatório para lá também?", - "report.hint": "O relatório será enviado para os moderadores do teu servidor. Podes fornecer, em baixo, uma explicação do motivo pelo qual estás a relatar esta conta:", + "report.hint": "O relatório será enviado para os moderadores do teu servidor. Podes fornecer, em baixo, uma explicação do motivo pelo qual estás a denunciar esta conta:", "report.placeholder": "Comentários adicionais", "report.submit": "Enviar", "report.target": "Denunciar", "search.placeholder": "Pesquisar", "search_popout.search_format": "Formato avançado de pesquisa", - "search_popout.tips.full_text": "Texto simples devolve publicações que tu escreveste, favoritaste, partilhaste ou em que foste mencionado, tal como nomes de utilizador correspondentes, alcunhas e hashtags.", + "search_popout.tips.full_text": "Texto simples devolve publicações que tu escreveste, marcaste como favorita, partilhaste ou em que foste mencionado, tal como nomes de utilizador correspondentes, alcunhas e hashtags.", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "estado", "search_popout.tips.text": "O texto simples retorna a correspondência de nomes, utilizadores e hashtags", "search_popout.tips.user": "utilizador", "search_results.accounts": "Pessoas", "search_results.hashtags": "Hashtags", - "search_results.statuses": "Publicações", + "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "status.admin_account": "Abrir a interface de moderação para @{name}", "status.admin_status": "Abrir esta publicação na interface de moderação", - "status.block": "Block @{name}", - "status.cancel_reblog_private": "Não partilhar", - "status.cannot_reblog": "Este post não pode ser partilhado", + "status.block": "Bloquear @{name}", + "status.cancel_reblog_private": "Remover boost", + "status.cannot_reblog": "Não é possÃvel fazer boost a esta publicação", "status.copy": "Copiar o link para a publicação", "status.delete": "Eliminar", "status.detailed_status": "Vista de conversação detalhada", @@ -334,7 +360,7 @@ "status.filtered": "Filtrada", "status.load_more": "Carregar mais", "status.local_only": "Esse post só é visÃvel para outros usuários da sua instância", - "status.media_hidden": "Media escondida", + "status.media_hidden": "Média escondida", "status.mention": "Mencionar @{name}", "status.more": "Mais", "status.mute": "Silenciar @{name}", @@ -344,48 +370,57 @@ "status.pinned": "Publicação fixa", "status.read_more": "Ler mais", "status.reblog": "Partilhar", - "status.reblog_private": "Partilhar com a audiência original", - "status.reblogged_by": "{name} partilhou", - "status.reblogs.empty": "Ainda ninguém partilhou esta publicação. Quando alguém o fizer, ela irá aparecer aqui.", + "status.reblog_private": "Fazer boost com a audiência original", + "status.reblogged_by": "{name} fez boost", + "status.reblogs.empty": "Ainda ninguém fez boost a este toot. Quando alguém o fizer, ele irá aparecer aqui.", "status.redraft": "Apagar & reescrever", "status.reply": "Responder", "status.replyAll": "Responder à conversa", "status.report": "Denunciar @{name}", "status.sensitive_warning": "Conteúdo sensÃvel", - "status.share": "Compartilhar", + "status.share": "Partilhar", "status.show_less": "Mostrar menos", "status.show_less_all": "Mostrar menos para todas", "status.show_more": "Mostrar mais", "status.show_more_all": "Mostrar mais para todas", "status.show_thread": "Mostrar conversa", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Deixar de silenciar esta conversa", "status.unpin": "Não fixar no perfil", "suggestions.dismiss": "Dispensar a sugestão", "suggestions.header": "Tu podes estar interessado em…", - "tabs_bar.federated_timeline": "Global", - "tabs_bar.home": "Home", + "tabs_bar.federated_timeline": "Federada", + "tabs_bar.home": "InÃcio", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificações", "tabs_bar.search": "Pesquisar", "time_remaining.days": "{número, plural, um {# day} outro {# days}} faltam", "time_remaining.hours": "{número, plural, um {# hour} outro {# hours}} faltam", "time_remaining.minutes": "{número, plural, um {# minute} outro {# minutes}} faltam", - "time_remaining.moments": "Momentos em falta", + "time_remaining.moments": "Momentos restantes", "time_remaining.seconds": "{número, plural, um {# second} outro {# seconds}} faltam", "trends.count_by_accounts": "{count} {rawCount, plural, uma {person} outra {people}} a falar", - "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.", + "trends.trending_now": "Trending now", + "ui.beforeunload": "O teu rascunho será perdido se abandonares o Mastodon.", "upload_area.title": "Arraste e solte para enviar", "upload_button.label": "Adicionar media", "upload_error.limit": "Limite máximo do ficheiro a carregar excedido.", - "upload_error.poll": "File upload not allowed with polls.", + "upload_error.poll": "Carregamento de ficheiros não é permitido em votações.", "upload_form.description": "Descrição da imagem para pessoas com dificuldades visuais", - "upload_form.focus": "Alterar previsualização", + "upload_form.edit": "Edit", "upload_form.undo": "Apagar", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "A enviar...", "video.close": "Fechar vÃdeo", "video.exit_fullscreen": "Sair de full screen", "video.expand": "Expandir vÃdeo", - "video.fullscreen": "Full screen", + "video.fullscreen": "Ecrã completo", "video.hide": "Esconder vÃdeo", "video.mute": "Silenciar", "video.pause": "Pausar", diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json index 963ad4dec7b14d0e59794d4838ef933a00f72fcf..b52ec721b8d1743e0caa6b1c354f57e8541eeba1 100644 --- a/app/javascript/mastodon/locales/ro.json +++ b/app/javascript/mastodon/locales/ro.json @@ -4,6 +4,7 @@ "account.block": "Blochează @{name}", "account.block_domain": "Ascunde tot de la {domain}", "account.blocked": "Blocat", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Mesaj direct @{name}", "account.domain_blocked": "Domeniu ascuns", "account.edit_profile": "Editează profilul", @@ -15,6 +16,7 @@ "account.follows.empty": "Acest utilizator nu urmăreÈ™te pe nimeni incă.", "account.follows_you": "Te urmăreÈ™te", "account.hide_reblogs": "Ascunde redistribuirile de la @{name}", + "account.last_status": "Last active", "account.link_verified_on": "DeÈ›inerea acestui link a fost verificată la {date}", "account.locked_info": "Acest profil este privat. Această persoană gestioneaz manual cine o urmăreÈ™te.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "OpreÈ™te @{name}", "account.mute_notifications": "OpreÈ™te notificările de la @{name}", "account.muted": "Oprit", + "account.never_active": "Never", "account.posts": "Postări", "account.posts_with_replies": "Postări È™i replici", "account.report": "Raportează @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Nu mai urmări", "account.unmute": "Activează notificările de la @{name}", "account.unmute_notifications": "Activează notificările de la @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "A apărut o eroare neaÈ™teptată.", "alert.unexpected.title": "Hopa!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "PoÈ›i apăsa {combo} pentru a omite asta data viitoare", "bundle_column_error.body": "Ceva nu a funcÈ›ionat la încărcarea acestui component.", "bundle_column_error.retry": "ÃŽncearcă din nou", @@ -47,6 +53,7 @@ "column.blocks": "Utilizatori blocaÈ›i", "column.community": "Fluxul Local", "column.direct": "Mesaje directe", + "column.directory": "Browse profiles", "column.domain_blocks": "Domenii ascunse", "column.favourites": "Favorite", "column.follow_requests": "Cereri de urmărire", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "EÈ™ti sigur că vrei să È™tergi permanent această listă?", "confirmations.domain_block.confirm": "Ascunde tot domeniul", "confirmations.domain_block.message": "EÈ™ti absolut sigur că vrei să blochezi complet {domain}? ÃŽn cele mai multe cazuri raportarea sau oprirea anumitor lucruri este suficientă È™i de preferat. Nu vei mai vedea nici un conÈ›inut de la acest domeniu in nici un flux public sau în notificările tale. Urmăritorii tăi de la acele domenii vor fi eliminaÈ›i.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "OpreÈ™te", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "EÈ™ti sigur că vrei să opreÈ™ti {name}?", "confirmations.redraft.confirm": "Șterge È™i salvează ca ciornă", "confirmations.redraft.message": "EÈ™ti sigur că vrei să faci asta? Tot ce È›ine de această postare, inclusiv răspunsurile vor fi deconectate.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Răspunzând la asta acum, mesajul pe care îl compui în prezent se va È™terge. EÈ™ti sigur că vrei să continui?", "confirmations.unfollow.confirm": "Nu mai urmări", "confirmations.unfollow.message": "EÈ™ti sigur că nu mai vrei să îl urmăreÈ™ti pe {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Inserează această postare pe site-ul tău adăugând codul de mai jos.", "embed.preview": "Cam aÈ™a va arăta:", "emoji_button.activity": "Activitate", @@ -134,6 +152,10 @@ "empty_column.mutes": "Nu ai oprit nici un utilizator incă.", "empty_column.notifications": "Nu ai nici o notificare încă. InteracÈ›ionează cu alÈ›ii pentru a începe o conversaÈ›ie.", "empty_column.public": "Nu este nimci aici încă! Scrie ceva public, sau urmăreÈ™te alÈ›i utilizatori din alte instanÈ›e pentru a porni fluxul", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Titlu pentru noua listă", "lists.search": "Caută printre persoanale pe care le urmăreÈ™ti", "lists.subheading": "Listele tale", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "ÃŽncărcare...", "media_gallery.toggle_visible": "ComutaÈ›i vizibilitatea", "missing_indicator.label": "Nu a fost găsit", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Postări fixate", "navigation_bar.preferences": "PreferinÈ›e", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Flux global", "navigation_bar.security": "Securitate", "notification.favourite": "{name} a adăugat statusul tău la favorite", @@ -282,8 +304,10 @@ "notifications.group": "{count} notificări", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Cine vede asta", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Nu afisa in fluxul public", "privacy.unlisted.short": "Nelistat", + "refresh": "Refresh", "regeneration_indicator.label": "ÃŽncărcare…", "regeneration_indicator.sublabel": "Fluxul tău este în preparare!", "relative_time.days": "{number}z", @@ -319,6 +344,7 @@ "search_results.accounts": "Oameni", "search_results.hashtags": "Hashtaguri", "search_results.statuses": "Postări", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Arată mai mult", "status.show_more_all": "Arată mai mult pentru toÈ›i", "status.show_thread": "Arată topicul", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "ReporneÈ™te conversaÈ›ia", "status.unpin": "Eliberează din profil", "suggestions.dismiss": "Omite sugestia", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} vorbesc", + "trends.trending_now": "Trending now", "ui.beforeunload": "Postarea se va pierde dacă părăseÈ™ti pagina.", "upload_area.title": "Trage È™i eliberează pentru a încărca", "upload_button.label": "Adaugă media (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Adaugă o descriere pentru persoanele cu deficienÈ›e de vedere", - "upload_form.focus": "Schimbă previzualizarea", + "upload_form.edit": "Edit", "upload_form.undo": "Șterge", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Se ÃŽncarcă...", "video.close": "ÃŽnchide video", "video.exit_fullscreen": "ÃŽnchide", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 609c0ab9ce38686ba53df9804745d8249675e97d..9a36c3b575ccbd7ff569196f9e5839715ce59d1f 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -4,6 +4,7 @@ "account.block": "Блокировать", "account.block_domain": "Блокировать вÑе Ñ {domain}", "account.blocked": "Заблокирован(а)", + "account.cancel_follow_request": "Отменить запроÑ", "account.direct": "ÐапиÑать @{name}", "account.domain_blocked": "Домен Ñкрыт", "account.edit_profile": "Изменить профиль", @@ -13,20 +14,22 @@ "account.followers.empty": "Ðикто не подпиÑан на Ñтого пользователÑ.", "account.follows": "ПодпиÑки", "account.follows.empty": "Ðтот пользователь ни на кого не подпиÑан.", - "account.follows_you": "ПодпиÑан(а) на ВаÑ", + "account.follows_you": "ПодпиÑан(а) на ваÑ", "account.hide_reblogs": "Скрыть реблоги от @{name}", + "account.last_status": "ПоÑледнÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñть", "account.link_verified_on": "Владение Ñтой ÑÑылкой было проверено {date}", "account.locked_info": "Ðто закрытый аккаунт. Его владелец вручную одобрÑет подпиÑчиков.", "account.media": "Медиа", "account.mention": "УпомÑнуть", "account.moved_to": "Ищите {name} здеÑÑŒ:", - "account.mute": "Заглушить", + "account.mute": "Скрыть @{name}", "account.mute_notifications": "Скрыть ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ @{name}", - "account.muted": "Приглушён", + "account.muted": "Скрыт", + "account.never_active": "Ðикогда", "account.posts": "ПоÑты", - "account.posts_with_replies": "ПоÑты и ответы", + "account.posts_with_replies": "ПоÑты Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð°Ð¼Ð¸", "account.report": "ПожаловатьÑÑ", - "account.requested": "Ожидает подтверждениÑ", + "account.requested": "Ожидает подтверждениÑ. Ðажмите Ð´Ð»Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ñ‹", "account.share": "ПоделитьÑÑ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»ÐµÐ¼ @{name}", "account.show_reblogs": "Показывать Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ‚ @{name}", "account.unblock": "Разблокировать", @@ -35,8 +38,11 @@ "account.unfollow": "ОтпиÑатьÑÑ", "account.unmute": "СнÑть глушение", "account.unmute_notifications": "Показывать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ñ‚ @{name}", + "alert.rate_limited.message": "ПожалуйÑта, повторите через {retry_time, time, medium}.", + "alert.rate_limited.title": "СкороÑть ограничена", "alert.unexpected.message": "Что-то пошло не так.", "alert.unexpected.title": "Ой!", + "autosuggest_hashtag.per_week": "{count} / неделю", "boost_modal.combo": "Ðажмите {combo}, чтобы пропуÑтить Ñто в Ñледующий раз", "bundle_column_error.body": "Что-то пошло не так при загрузке Ñтого компонента.", "bundle_column_error.retry": "Попробовать Ñнова", @@ -47,12 +53,13 @@ "column.blocks": "СпиÑок блокировки", "column.community": "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", "column.direct": "Личные ÑообщениÑ", + "column.directory": "ПроÑмотр профилей", "column.domain_blocks": "Скрытые домены", "column.favourites": "ПонравившееÑÑ", "column.follow_requests": "ЗапроÑÑ‹ на подпиÑку", "column.home": "ГлавнаÑ", "column.lists": "СпиÑки", - "column.mutes": "СпиÑок глушениÑ", + "column.mutes": "СпиÑок Ñкрытых пользователей", "column.notifications": "УведомлениÑ", "column.pins": "Закреплённый поÑÑ‚", "column.public": "Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", @@ -70,12 +77,12 @@ "compose_form.hashtag_warning": "Ðтот поÑÑ‚ не будет показываетÑÑ Ð² поиÑке по Ñ…Ñштегу, Ñ‚.к. он непубличный. Только публичные поÑты можно найти в поиÑке по Ñ…Ñштегу.", "compose_form.lock_disclaimer": "Ваш аккаунт не {locked}. Любой человек может подпиÑатьÑÑ Ð½Ð° Ð’Ð°Ñ Ð¸ проÑматривать поÑты Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков.", "compose_form.lock_disclaimer.lock": "закрыт", - "compose_form.placeholder": "О чем Ð’Ñ‹ думаете?", + "compose_form.placeholder": "О чем вы думаете?", "compose_form.poll.add_option": "Добавить", "compose_form.poll.duration": "ДлительноÑть опроÑа", "compose_form.poll.option_placeholder": "Вариант {number}", "compose_form.poll.remove_option": "Удалить Ñтот вариант", - "compose_form.publish": "Трубить", + "compose_form.publish": "ЗапоÑтить", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive.hide": "Пометить медиафайл как чувÑтвительный", "compose_form.sensitive.marked": "Медиафайлы не отмечены как чувÑтвительные", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Ð’Ñ‹ дейÑтвительно хотите навÑегда удалить Ñтот ÑпиÑок?", "confirmations.domain_block.confirm": "Блокировать веÑÑŒ домен", "confirmations.domain_block.message": "Ð’Ñ‹ на Ñамом деле уверены, что хотите блокировать веÑÑŒ {domain}? Ð’ большинÑтве Ñлучаев неÑкольких отдельных блокировок или глушений доÑтаточно.", + "confirmations.logout.confirm": "Выйти", + "confirmations.logout.message": "Ð’Ñ‹ уверены, что хотите выйти?", "confirmations.mute.confirm": "Заглушить", + "confirmations.mute.explanation": "Будут Ñкрыты их поÑты и те, где они упоминаютÑÑ, но они при Ñтом Ñмогут видеть ваши поÑты и подпиÑыватьÑÑ Ð½Ð° ваÑ.", "confirmations.mute.message": "Ð’Ñ‹ уверены, что хотите заглушить {name}?", "confirmations.redraft.confirm": "Удалить и иÑправить", "confirmations.redraft.message": "Ð’Ñ‹ уверены, что хотите удалить Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¸ превратить в черновик? Ð’Ñ‹ потерÑете вÑе ответы, Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¸ отметки 'нравитÑÑ' к нему.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "При ответе текÑÑ‚ набираемого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ перезапиÑан. Продолжить?", "confirmations.unfollow.confirm": "ОтпиÑатьÑÑ", "confirmations.unfollow.message": "Ð’Ñ‹ уверены, что хотите отпиÑатьÑÑ Ð¾Ñ‚ {name}?", + "conversation.delete": "Удалить беÑеду", + "conversation.mark_as_read": "Пометить прочитанным", + "conversation.open": "ПроÑмотр беÑеды", + "conversation.with": "С {names}", + "directory.federated": "Со вÑей федерации", + "directory.local": "Только из {domain}", + "directory.new_arrivals": "Ðовички", + "directory.recently_active": "Ðедавно активные", "embed.instructions": "Ð’Ñтройте Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° Вашем Ñайте, Ñкопировав код внизу.", "embed.preview": "Так Ñто будет выглÑдеть:", "emoji_button.activity": "ЗанÑтиÑ", @@ -117,23 +135,27 @@ "emoji_button.search_results": "Результаты поиÑка", "emoji_button.symbols": "Символы", "emoji_button.travel": "ПутешеÑтвиÑ", - "empty_column.account_timeline": "СтатуÑов нет!", + "empty_column.account_timeline": "ЗдеÑÑŒ нет поÑтов!", "empty_column.account_unavailable": "Профиль недоÑтупен", "empty_column.blocks": "Ð’Ñ‹ ещё никого не заблокировали.", "empty_column.community": "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð° пуÑта. Ðапишите что-нибудь, чтобы разогреть народ!", - "empty_column.direct": "У Ð’Ð°Ñ Ð¿Ð¾ÐºÐ° нет личных Ñообщений. Когда Ð’Ñ‹ начнёте их отправлÑть или получать, они поÑвÑÑ‚ÑÑ Ð·Ð´ÐµÑÑŒ.", + "empty_column.direct": "У Ð²Ð°Ñ Ð¿Ð¾ÐºÐ° нет личных Ñообщений. Как только вы отправите или получите одно, оно поÑвитÑÑ Ð·Ð´ÐµÑÑŒ.", "empty_column.domain_blocks": "Скрытых доменов пока нет.", - "empty_column.favourited_statuses": "Ð’Ñ‹ не добавили ни одного ÑтатуÑа в 'Избранное'. Как только Ð’Ñ‹ Ñто Ñделаете, они поÑвÑÑ‚ÑÑ Ð·Ð´ÐµÑÑŒ.", - "empty_column.favourites": "Ðикто ещё не добавил Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² 'Избранное'. Как только кто-то Ñто Ñделает, они поÑвÑÑ‚ÑÑ Ð·Ð´ÐµÑÑŒ.", + "empty_column.favourited_statuses": "Ð’Ñ‹ не добавили ни один поÑÑ‚ в «Избранное». Как только вы Ñто Ñделаете, он поÑвитÑÑ Ð·Ð´ÐµÑÑŒ.", + "empty_column.favourites": "Ðикто ещё не добавил Ñтот поÑÑ‚ в «Избранное». Как только кто-то Ñто Ñделает, Ñто отобразитÑÑ Ð·Ð´ÐµÑÑŒ.", "empty_column.follow_requests": "Вам ещё не приходили запроÑÑ‹ на подпиÑку. Ð’Ñе новые запроÑÑ‹ будут показаны здеÑÑŒ.", "empty_column.hashtag": "СтатуÑов Ñ Ñ‚Ð°ÐºÐ¸Ð¼ Ñ…Ñштегом еще не ÑущеÑтвует.", - "empty_column.home": "Пока Ð’Ñ‹ ни на кого не подпиÑаны. ПолиÑтайте {public} или иÑпользуйте поиÑк, чтобы оÑвоитьÑÑ Ð¸ завеÑти новые знакомÑтва.", + "empty_column.home": "Пока вы ни на кого не подпиÑаны. ПолиÑтайте {public} или иÑпользуйте поиÑк, чтобы оÑвоитьÑÑ Ð¸ завеÑти новые знакомÑтва.", "empty_column.home.public_timeline": "публичные ленты", "empty_column.list": "Ð’ Ñтом ÑпиÑке пока ничего нет.", - "empty_column.lists": "У Ð’Ð°Ñ ÐµÑ‰Ñ‘ нет ÑпиÑков. Ð’Ñе Ñозданные Вами ÑпиÑки будут показаны здеÑÑŒ.", - "empty_column.mutes": "Ð’Ñ‹ ещё никого не заглушили.", - "empty_column.notifications": "У Ð’Ð°Ñ ÐµÑ‰Ðµ нет уведомлений. Заведите знакомÑтво Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ пользователÑми, чтобы начать разговор.", + "empty_column.lists": "У Ð²Ð°Ñ ÐµÑ‰Ñ‘ нет ÑпиÑков. Созданные вами ÑпиÑки будут показаны здеÑÑŒ.", + "empty_column.mutes": "Ð’Ñ‹ ещё никого не Ñкрывали.", + "empty_column.notifications": "У Ð²Ð°Ñ Ð¿Ð¾ÐºÐ° нет уведомлений. ВзаимодейÑтвуйте Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸, чтобы завеÑти разговор.", "empty_column.public": "ЗдеÑÑŒ ничего нет! Опубликуйте что-нибудь или подпишитеÑÑŒ на пользователей Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… узлов, чтобы заполнить ленту.", + "error.unexpected_crash.explanation": "Из-за неÑовмеÑтимого браузера или ошибки в нашем коде, Ñта Ñтраница не может быть корректно отображена.", + "error.unexpected_crash.next_steps": "Попробуйте обновить Ñтраницу. ЕÑли проблема не иÑчезает, иÑпользуйте Mastodon из-под другого браузера или приложениÑ.", + "errors.unexpected_crash.copy_stacktrace": "Копировать ÑÑ‚ÐµÐºÑ‚Ñ€ÐµÐ¹Ñ Ð² буфер обмена", + "errors.unexpected_crash.report_issue": "Сообщить о проблеме", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -141,12 +163,12 @@ "federation.local_only.short": "Local-only", "follow_request.authorize": "Ðвторизовать", "follow_request.reject": "Отказать", - "getting_started.developers": "Ð”Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð²", + "getting_started.developers": "Разработчикам", "getting_started.directory": "Каталог профилей", "getting_started.documentation": "ДокументациÑ", "getting_started.heading": "Добро пожаловать", "getting_started.invite": "ПриглаÑить людей", - "getting_started.open_source_notice": "Mastodon - ÑÐµÑ€Ð²Ð¸Ñ Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом. Ð’Ñ‹ можете помочь проекту или Ñообщить о проблемах на GitHub по адреÑу {github}.", + "getting_started.open_source_notice": "Mastodon — ÑÐµÑ€Ð²Ð¸Ñ Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом. Ð’Ñ‹ можете внеÑти вклад или Ñообщить о проблемах на GitHub: {github}.", "getting_started.security": "БезопаÑноÑть", "getting_started.terms": "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑпользованиÑ", "hashtag.column_header.tag_mode.all": "и {additional}", @@ -161,9 +183,9 @@ "home.column_settings.basic": "ОÑновные", "home.column_settings.show_reblogs": "Показывать продвижениÑ", "home.column_settings.show_replies": "Показывать ответы", - "intervals.full.days": "{number, plural, one {# день} few {# днÑ} many {# дней} other {# дней}}", - "intervals.full.hours": "{number, plural, one {# чаÑ} few {# чаÑа} many {# чаÑов} other {# чаÑов}}", - "intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} many {# минут} other {# минут}}", + "intervals.full.days": "{number, plural, one {# день} few {# днÑ} other {# дней}}", + "intervals.full.hours": "{number, plural, one {# чаÑ} few {# чаÑа} other {# чаÑов}}", + "intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} other {# минут}}", "introduction.federation.action": "Далее", "introduction.federation.federated.headline": "Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", "introduction.federation.federated.text": "Публичные ÑтатуÑÑ‹ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ñерверов федеративной Ñети раÑположатÑÑ Ð² глобальной ленте.", @@ -172,7 +194,7 @@ "introduction.federation.local.headline": "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", "introduction.federation.local.text": "Публичные ÑтатуÑÑ‹ от людей Ñ Ñ‚Ð¾Ð³Ð¾ же Ñервера, что и вы, будут отображены в локальной ленте.", "introduction.interactions.action": "Завершить обучение", - "introduction.interactions.favourite.headline": "Отметки \"нравитÑÑ\"", + "introduction.interactions.favourite.headline": "Отметки «нравитÑÑ»", "introduction.interactions.favourite.text": "Ð’Ñ‹ можете отметить ÑтатуÑ, чтобы вернутьÑÑ Ðº нему позже и дать знать автору, что запиÑÑŒ вам понравилаÑÑŒ, поÑтавив отметку \"нравитÑÑ\".", "introduction.interactions.reblog.headline": "ПродвижениÑ", "introduction.interactions.reblog.text": "Ð’Ñ‹ можете делитьÑÑ ÑтатуÑами других людей, Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð³Ð°Ñ Ð¸Ñ… в Ñвоём аккаунте.", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Заголовок ÑпиÑка", "lists.search": "ИÑкать из ваших подпиÑок", "lists.subheading": "Ваши ÑпиÑки", + "load_pending": "{count, plural, one {# новый Ñлемент} few {# новых Ñлемента} other {# новых Ñлементов}}", "loading_indicator.label": "Загрузка...", "media_gallery.toggle_visible": "Показать/Ñкрыть", "missing_indicator.label": "Ðе найдено", @@ -247,16 +270,15 @@ "navigation_bar.keyboard_shortcuts": "Ð¡Ð¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ", "navigation_bar.lists": "СпиÑки", "navigation_bar.logout": "Выйти", - "navigation_bar.mutes": "СпиÑок глушениÑ", + "navigation_bar.mutes": "СпиÑок Ñкрытых пользователей", "navigation_bar.personal": "Личное", "navigation_bar.pins": "Закреплённые поÑты", - "navigation_bar.preferences": "Опции", - "navigation_bar.profile_directory": "Каталог профилей", + "navigation_bar.preferences": "ÐаÑтройки", "navigation_bar.public_timeline": "Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ð»ÐµÐ½Ñ‚Ð°", "navigation_bar.security": "БезопаÑноÑть", "notification.favourite": "{name} понравилÑÑ Ð’Ð°Ñˆ ÑтатуÑ", - "notification.follow": "{name} подпиÑалÑÑ(-лаÑÑŒ) на ВаÑ", - "notification.mention": "{name} упомÑнул(а) ВаÑ", + "notification.follow": "{name} подпиÑалÑÑ (-лаÑÑŒ) на ваÑ", + "notification.mention": "{name} упомÑнул(а) ваÑ", "notification.poll": "ОпроÑ, в котором вы принÑли учаÑтие, завершилÑÑ", "notification.reblog": "{name} продвинул(а) Ваш ÑтатуÑ", "notifications.clear": "ОчиÑтить уведомлениÑ", @@ -282,8 +304,10 @@ "notifications.group": "{count} уведомл.", "poll.closed": "Завершён", "poll.refresh": "Обновить", + "poll.total_people": "{count, plural, one {# человек} few {# человека} many {# человек} other {# человек}}", "poll.total_votes": "{count, plural, one {# голоÑ} few {# голоÑа} many {# голоÑов} other {# голоÑов}}", "poll.vote": "ГолоÑовать", + "poll.voted": "Ð’Ñ‹ проголоÑовали за Ñтот вариант", "poll_button.add_poll": "Добавить опроÑ", "poll_button.remove_poll": "Удалить опроÑ", "privacy.change": "Изменить видимоÑть ÑтатуÑа", @@ -295,6 +319,7 @@ "privacy.public.short": "Публичный", "privacy.unlisted.long": "Ðе показывать в лентах", "privacy.unlisted.short": "Скрытый", + "refresh": "Обновить", "regeneration_indicator.label": "Загрузка…", "regeneration_indicator.sublabel": "Ваша домашнÑÑ Ð»ÐµÐ½Ñ‚Ð° готовитÑÑ!", "relative_time.days": "{number}д", @@ -319,6 +344,7 @@ "search_results.accounts": "Люди", "search_results.hashtags": "Ð¥Ñштеги", "search_results.statuses": "ПоÑты", + "search_results.statuses_fts_disabled": "ПоиÑк поÑтов по их контенту не поддерживаетÑÑ Ð½Ð° Ñтом Ñервере Mastodon.", "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}", "status.admin_account": "Открыть Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ‚Ð¾Ñ€Ð° Ð´Ð»Ñ @{name}", "status.admin_status": "Открыть Ñтот ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² интерфейÑе модератора", @@ -358,6 +384,7 @@ "status.show_more": "Развернуть", "status.show_more_all": "Развернуть Ð´Ð»Ñ Ð²Ñех", "status.show_thread": "Показать обÑуждение", + "status.uncached_media_warning": "ÐедоÑтупно", "status.unmute_conversation": "СнÑть глушение Ñ Ð¾Ð±ÑуждениÑ", "status.unpin": "Открепить от профилÑ", "suggestions.dismiss": "Удалить предложение", @@ -372,15 +399,23 @@ "time_remaining.minutes": "{number, plural, one {оÑталаÑÑŒ # минута} few {оÑталоÑÑŒ # минуты} many {оÑталоÑÑŒ # минут} other {оÑталоÑÑŒ # минут}}", "time_remaining.moments": "оÑталиÑÑŒ Ñчитанные мгновениÑ", "time_remaining.seconds": "{number, plural, one {оÑталаÑÑŒ # Ñекунду} few {оÑталоÑÑŒ # Ñекунды} many {оÑталоÑÑŒ # Ñекунд} other {оÑталоÑÑŒ # Ñекунд}}", - "trends.count_by_accounts": "ПопулÑрно у {count} {rawCount, plural, one {человека} few {человек} many {человек} other {человек}}", + "trends.count_by_accounts": "{count} {rawCount, plural, one {человек говорит} few {человека говорÑÑ‚} other {человек говорÑÑ‚}} про Ñто", + "trends.trending_now": "Самое актуальное", "ui.beforeunload": "Ваш черновик будет утерÑн, еÑли вы покинете Mastodon.", "upload_area.title": "Перетащите Ñюда, чтобы загрузить", "upload_button.label": "Добавить медиаконтент", "upload_error.limit": "ДоÑтигнут лимит загруженных файлов.", "upload_error.poll": "К опроÑам Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¸ÐºÑ€ÐµÐ¿Ð»Ñть файлы.", - "upload_form.description": "ОпиÑать Ð´Ð»Ñ Ð»ÑŽÐ´ÐµÐ¹ Ñ Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñми зрениÑ", - "upload_form.focus": "Обрезать", + "upload_form.description": "Добавьте опиÑание Ð´Ð»Ñ Ð»ÑŽÐ´ÐµÐ¹ Ñ Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸Ñми зрениÑ:", + "upload_form.edit": "Изменить", "upload_form.undo": "Отменить", + "upload_modal.analyzing_picture": "Обработка изображениÑ…", + "upload_modal.apply": "Применить", + "upload_modal.description_placeholder": "Ðа дворе трава, на траве дрова", + "upload_modal.detect_text": "Ðайти текÑÑ‚ на картинке", + "upload_modal.edit_media": "Изменение медиа", + "upload_modal.hint": "Ðажмите и перетащите круг в предпроÑмотре в точку фокуÑа, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ñегда будет видна на ÑÑкизах.", + "upload_modal.preview_label": "ПредпроÑмотр ({ratio})", "upload_progress.label": "Загрузка...", "video.close": "Закрыть видео", "video.exit_fullscreen": "Покинуть полноÑкранный режим", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 7bda37548489c087741fda13b7a34dc3b0107097..01fd3d42c979dfd89e483932411724886bb187bd 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -4,6 +4,7 @@ "account.block": "Blokuj @{name}", "account.block_domain": "Ukry vÅ¡etko z {domain}", "account.blocked": "Blokovaný/á", + "account.cancel_follow_request": "ZruÅ¡ požiadanie o sledovanie", "account.direct": "Súkromná správa pre @{name}", "account.domain_blocked": "Doména ukrytá", "account.edit_profile": "Uprav profil", @@ -15,15 +16,17 @@ "account.follows.empty": "Tento užÃvateľ eÅ¡te nikoho nenásleduje.", "account.follows_you": "Následuje Å¥a", "account.hide_reblogs": "Skry vyzdvihnutia od @{name}", + "account.last_status": "Naposledy aktÃvny", "account.link_verified_on": "VlastnÃctvo tohto odkazu bolo skontrolované {date}", "account.locked_info": "Stav súkromia pre tento úÄet je nastavený na zamknutý. Jeho vlastnÃk sám prehodnocuje, kto ho môže sledovaÅ¥.", "account.media": "Médiá", "account.mention": "Spomeň @{name}", "account.moved_to": "{name} sa presunul/a na:", - "account.mute": "IgnorovaÅ¥ @{name}", + "account.mute": "NevÅ¡Ãmaj si @{name}", "account.mute_notifications": "Stĺm oboznámenia od @{name}", "account.muted": "UtÃÅ¡ený/á", - "account.posts": "PrÃspevky", + "account.never_active": "Nikdy", + "account.posts": "PrÃspevkov", "account.posts_with_replies": "PrÃspevky aj s odpoveÄami", "account.report": "Nahlás @{name}", "account.requested": "ÄŒaká na schválenie. Klikni pre zruÅ¡enie žiadosti", @@ -35,8 +38,11 @@ "account.unfollow": "Prestaň následovaÅ¥", "account.unmute": "Prestaň ignorovaÅ¥ @{name}", "account.unmute_notifications": "ZruÅ¡ stĺmenie oboznámenà od @{name}", + "alert.rate_limited.message": "ProsÃm, skús to znova za {retry_time, time, medium}.", + "alert.rate_limited.title": "Tempo obmedzené", "alert.unexpected.message": "Vyskytla sa neÄakaná chyba.", "alert.unexpected.title": "Ups!", + "autosuggest_hashtag.per_week": "{count} týždenne", "boost_modal.combo": "Nabudúce môžeÅ¡ kliknúť {combo} pre preskoÄenie", "bundle_column_error.body": "Pri naÄÃtanà tohto prvku nastala nejaká chyba.", "bundle_column_error.retry": "Skús to znova", @@ -47,6 +53,7 @@ "column.blocks": "Blokovanà užÃvatelia", "column.community": "Miestna Äasová os", "column.direct": "Súkromné správy", + "column.directory": "Prehľadávaj profily", "column.domain_blocks": "Skryté domény", "column.favourites": "Obľúbené", "column.follow_requests": "Žiadosti o sledovanie", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Si si istý/á, že chceÅ¡ natrvalo vymazaÅ¥ tento zoznam?", "confirmations.domain_block.confirm": "Skry celú doménu", "confirmations.domain_block.message": "Si si naozaj istý/á, že chceÅ¡ blokovaÅ¥ celú doménu {domain}? Vo väÄÅ¡ine prÃpadov staÄà blokovaÅ¥ alebo ignorovaÅ¥ pár konkrétnych užÃvateľov, Äo sa doporuÄuje. NeuvidÃÅ¡ obsah z tejto domény v žiadnej verejnej Äasovej osi, ani v oznámeniach. Tvoji následovnÃci pochádzajúci z tejto domény budú odstránenÃ.", + "confirmations.logout.confirm": "Odhlás sa", + "confirmations.logout.message": "Si si istý/á, že sa chceÅ¡ odhlásiÅ¥?", "confirmations.mute.confirm": "Ignoruj", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Naozaj chceÅ¡ ignorovaÅ¥ {name}?", "confirmations.redraft.confirm": "VyÄisti a prepÃÅ¡", "confirmations.redraft.message": "Si si istý/á, že chceÅ¡ premazaÅ¥ a prepÃsaÅ¥ tento prÃspevok? Jeho nadobudnuté vyzdvihnutia a obľúbenia, ale i odpovede na pôvodný prÃspevok budú odlúÄené.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "OdpovedanÃm akurát teraz prepÃÅ¡eÅ¡ správu, ktorú máš práve rozpÃsanú. Si si istý/á, že chceÅ¡ pokraÄovaÅ¥?", "confirmations.unfollow.confirm": "Nesleduj", "confirmations.unfollow.message": "Naozaj chceÅ¡ prestaÅ¥ sledovaÅ¥ {name}?", + "conversation.delete": "Vymaž konverzáciu", + "conversation.mark_as_read": "OznaÄ za preÄÃtané", + "conversation.open": "Ukáž konverzáciu", + "conversation.with": "S {names}", + "directory.federated": "Zo známého fedivesmÃru", + "directory.local": "Iba z {domain}", + "directory.new_arrivals": "Nové prÃchody", + "directory.recently_active": "Nedávno aktÃvne", "embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.", "embed.preview": "Tu je ako to bude vyzeraÅ¥:", "emoji_button.activity": "Aktivita", @@ -134,6 +152,10 @@ "empty_column.mutes": "EÅ¡te si nestĺmil žiadných užÃvateľov.", "empty_column.notifications": "EÅ¡te nemáš žiadne oznámenia. ZaÄni komunikovaÅ¥ s ostatnými, aby diskusia mohla zaÄaÅ¥.", "empty_column.public": "EÅ¡te tu niÄ nie je. NapÃÅ¡ nieÄo verejne, alebo zaÄni sledovaÅ¥ užÃvateľov z iných serverov, aby tu nieÄo pribudlo", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "SkopÃruj stacktrace do schránky", + "errors.unexpected_crash.report_issue": "Nahlás problém", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,10 +164,10 @@ "follow_request.authorize": "Povoľ prÃstup", "follow_request.reject": "Odmietni", "getting_started.developers": "Vývojári", - "getting_started.directory": "Databáza profilov", + "getting_started.directory": "Zoznam profilov", "getting_started.documentation": "Dokumentácia", "getting_started.heading": "ZaÄni tu", - "getting_started.invite": "PozvaÅ¥ ľudÃ", + "getting_started.invite": "Pozvi ľudÃ", "getting_started.open_source_notice": "Mastodon je softvér s otvoreným kódom. NahlásiÅ¥ chyby, alebo prispievaÅ¥ môžeÅ¡ na GitHube v {github}.", "getting_started.security": "ZabezpeÄenie", "getting_started.terms": "Podmienky prevozu", @@ -161,9 +183,9 @@ "home.column_settings.basic": "Základné", "home.column_settings.show_reblogs": "ZobraziÅ¥ povýšené", "home.column_settings.show_replies": "UkázaÅ¥ odpovede", - "intervals.full.days": "{number, plural, one {# deň} few {# dnÃ} many {# dnÃ} other {# dni}}", - "intervals.full.hours": "{number, plural, one {# hodina} few {# hodÃn} many {# hodÃn} other {# hodiny}}", - "intervals.full.minutes": "{number, plural, one {# minúta} few {# minút} many {# minút} other {# minúty}}", + "intervals.full.days": "{number, plural, one {# deň} few {# dnÃ} many {# dnÃ} other {# dnÃ}}", + "intervals.full.hours": "{number, plural, one {# hodina} few {# hodÃn} many {# hodÃn} other {# hodÃn}}", + "intervals.full.minutes": "{number, plural, one {# minúta} few {# minút} many {# minút} other {# minút}}", "introduction.federation.action": "ÄŽalej", "introduction.federation.federated.headline": "Federovaná", "introduction.federation.federated.text": "Verejné prÃspevky z ostatných serverov vo fediverse budú zobrazené vo federovanej Äasovej osi.", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Názov nového zoznamu", "lists.search": "Vyhľadávaj medzi užÃvateľmi, ktorých sledujeÅ¡", "lists.subheading": "Tvoje zoznamy", + "load_pending": "{count, plural, one {# nová položka} other {# nových položiek}}", "loading_indicator.label": "NaÄÃtam...", "media_gallery.toggle_visible": "Zapni/Vypni viditeľnosÅ¥", "missing_indicator.label": "Nenájdené", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Osobné", "navigation_bar.pins": "Pripnuté prÃspevky", "navigation_bar.preferences": "Voľby", - "navigation_bar.profile_directory": "Katalóg profilov", "navigation_bar.public_timeline": "Federovaná Äasová os", "navigation_bar.security": "ZabezbeÄenie", "notification.favourite": "{name} si obľúbil/a tvoj prÃspevok", @@ -259,7 +281,7 @@ "notification.mention": "{name} Å¥a spomenul/a", "notification.poll": "Anketa v ktorej si hlasoval/a sa skonÄila", "notification.reblog": "{name} zdieľal/a tvoj prÃspevok", - "notifications.clear": "VyÄistiÅ¥ zoznam oboznámenÃ", + "notifications.clear": "VyÄisti oboznámenia", "notifications.clear_confirmation": "Naozaj chceÅ¡ nenávratne preÄistiÅ¥ vÅ¡etky tvoje oboznámenia?", "notifications.column_settings.alert": "Oboznámenia na ploche", "notifications.column_settings.favourite": "Obľúbené:", @@ -281,9 +303,11 @@ "notifications.filter.polls": "Výsledky ankiet", "notifications.group": "{count} oboznámenÃ", "poll.closed": "Uzatvorená", - "poll.refresh": "Aktualizuj", - "poll.total_votes": "{count, plural, one {# hlas} few {# hlasov} many {# hlasov} other {# hlasy}}", + "poll.refresh": "ObÄerstvi", + "poll.total_people": "{count, plural, one {# Älovek} few {# ľudia} other {# ľudÃ}}", + "poll.total_votes": "{count, plural, one {# hlas} few {# hlasov} many {# hlasov} other {# hlasov}}", "poll.vote": "Hlasuj", + "poll.voted": "Hlasoval/a si za túto voľbu", "poll_button.add_poll": "Pridaj anketu", "poll_button.remove_poll": "Odstráň anketu", "privacy.change": "Uprav súkromie prÃspevku", @@ -295,6 +319,7 @@ "privacy.public.short": "Verejné", "privacy.unlisted.long": "Neposielaj do verejných Äasových osÃ", "privacy.unlisted.short": "Verejne, ale nezobraziÅ¥ v osi", + "refresh": "ObÄerstvi", "regeneration_indicator.label": "NaÄÃtava sa…", "regeneration_indicator.sublabel": "VaÅ¡a domovská nástenka sa pripravuje!", "relative_time.days": "{number}dnÃ", @@ -319,6 +344,7 @@ "search_results.accounts": "Ľudia", "search_results.hashtags": "HaÅ¡tagy", "search_results.statuses": "PrÃspevky", + "search_results.statuses_fts_disabled": "Vyhľadávanie v obsahu prÃspevkov nieje na tomto Mastodon serveri povolené.", "search_results.total": "{count, number} {count, plural, one {výsledok} many {výsledkov} other {výsledky}}", "status.admin_account": "Otvor moderovacie rozhranie užÃvateľa @{name}", "status.admin_status": "Otvor tento prÃspevok v moderovacom rozhranÃ", @@ -358,6 +384,7 @@ "status.show_more": "Ukáž viac", "status.show_more_all": "VÅ¡etkým ukáž viac", "status.show_thread": "Ukáž diskusné vlákno", + "status.uncached_media_warning": "Nedostupný/é", "status.unmute_conversation": "Prestaň ignorovaÅ¥ konverzáciu", "status.unpin": "Odopni z profilu", "suggestions.dismiss": "Zavrhni návrh", @@ -367,20 +394,28 @@ "tabs_bar.local_timeline": "Miestna", "tabs_bar.notifications": "Oboznámenia", "tabs_bar.search": "Hľadaj", - "time_remaining.days": "Ostáva {number, plural, one {# deň} few {# dnÃ} many {# dnÃ} other {# dni}}", + "time_remaining.days": "Ostáva {number, plural, one {# deň} few {# dnÃ} many {# dnÃ} other {# dnÃ}}", "time_remaining.hours": "Ostáva {number, plural, one {# hodina} few {# hodÃn} many {# hodÃn} other {# hodiny}}", "time_remaining.minutes": "Ostáva {number, plural, one {# minúta} few {# minút} many {# minút} other {# minúty}}", "time_remaining.moments": "Ostáva už iba chviľka", - "time_remaining.seconds": "Ostáva {number, plural, one {# sekunda} few {# sekúnd} many {# sekúnd} other {# sekundy}}", + "time_remaining.seconds": "Ostáva {number, plural, one {# sekunda} few {# sekúnd} many {# sekúnd} other {# sekúnd}}", "trends.count_by_accounts": "{count} {rawCount, plural, one {Älovek vravÃ} other {ľudia vravia}}", + "trends.trending_now": "Teraz populárne", "ui.beforeunload": "ÄŒo máš rozpÃsané sa stratÃ, ak opustÃÅ¡ Mastodon.", "upload_area.title": "Pretiahni a pusÅ¥ pre nahratie", "upload_button.label": "Pridaj médiálny súbor (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "Limit pre nahrávanie súborov bol prekroÄený.", "upload_error.poll": "Nahrávanie súborov pri anketách nieje možné.", "upload_form.description": "Opis pre slabo vidiacich", - "upload_form.focus": "Pozmeň náhľad", + "upload_form.edit": "Uprav", "upload_form.undo": "Vymaž", + "upload_modal.analyzing_picture": "Analyzujem obrázok…", + "upload_modal.apply": "Použi", + "upload_modal.description_placeholder": "Rýchla hnedá lÃÅ¡ka skáÄe ponad lenivého psa", + "upload_modal.detect_text": "Rozpoznaj text z obrázka", + "upload_modal.edit_media": "Uprav médiá", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Náhľad ({ratio})", "upload_progress.label": "Nahráva sa...", "video.close": "Zavri video", "video.exit_fullscreen": "Vypni zobrazenie na celú obrazovku", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index a0e150ec45382ba248b31687968fd855f9e7c507..29d7bf473ec6007201326431d1edfd6658ec045f 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -4,6 +4,7 @@ "account.block": "Blokiraj @{name}", "account.block_domain": "Skrij vse iz {domain}", "account.blocked": "Blokirano", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Neposredno sporoÄilo @{name}", "account.domain_blocked": "Skrita domena", "account.edit_profile": "Uredi profil", @@ -15,6 +16,7 @@ "account.follows.empty": "Ta uporabnik Å¡e ne sledi nikomur.", "account.follows_you": "Sledi tebi", "account.hide_reblogs": "Skrij spodbude od @{name}", + "account.last_status": "Last active", "account.link_verified_on": "LastniÅ¡tvo te povezave je bilo preverjeno {date}", "account.locked_info": "Stanje zasebnosti raÄuna je nastavljeno na zaklenjeno. Lastnik roÄno pregleda, kdo ga lahko spremlja.", "account.media": "Mediji", @@ -23,6 +25,7 @@ "account.mute": "UtiÅ¡aj @{name}", "account.mute_notifications": "UtiÅ¡aj obvestila od @{name}", "account.muted": "UtiÅ¡an", + "account.never_active": "Never", "account.posts": "Tuti", "account.posts_with_replies": "Tuti in odgovori", "account.report": "Prijavi @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Prenehaj slediti", "account.unmute": "OdtiÅ¡aj @{name}", "account.unmute_notifications": "Vklopi obvestila od @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Zgodila se je nepriÄakovana napaka.", "alert.unexpected.title": "Uups!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "ÄŒe želite preskoÄiti to, lahko pritisnete {combo}", "bundle_column_error.body": "Med nalaganjem te komponente je priÅ¡lo do napake.", "bundle_column_error.retry": "Poskusi ponovno", @@ -47,6 +53,7 @@ "column.blocks": "Blokirani uporabniki", "column.community": "Lokalna Äasovnica", "column.direct": "Neposredna sporoÄila", + "column.directory": "Browse profiles", "column.domain_blocks": "Skrite domene", "column.favourites": "Priljubljene", "column.follow_requests": "Sledi proÅ¡njam", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Ali ste prepriÄani, da želite trajno izbrisati ta seznam?", "confirmations.domain_block.confirm": "Skrij celotno domeno", "confirmations.domain_block.message": "Ali ste res, res prepriÄani, da želite blokirati celotno {domain}? V veÄini primerov je nekaj ciljnih blokiranj ali utiÅ¡anj dovolj in boljÅ¡e. Vsebino iz te domene ne boste videli v javnih Äasovnicah ali obvestilih. VaÅ¡i sledilci iz te domene bodo odstranjeni.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "UtiÅ¡anje", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Ali ste prepriÄani, da želite utiÅ¡ati {name}?", "confirmations.redraft.confirm": "IzbriÅ¡i in preoblikuj", "confirmations.redraft.message": "Ali ste prepriÄani, da želite izbrisati ta status in ga preoblikovati? Vzljubi in spodbude bodo izgubljeni, odgovori na izvirno objavo pa bodo osiroteli.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Odgovarjanje bo prepisalo sporoÄilo, ki ga trenutno sestavljate. Ali ste prepriÄani, da želite nadaljevati?", "confirmations.unfollow.confirm": "Prenehaj slediti", "confirmations.unfollow.message": "Ali ste prepriÄani, da ne želite veÄ slediti {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Vstavi ta status na svojo spletno stran tako, da kopirate spodnjo kodo.", "embed.preview": "Tako bo izgledalo:", "emoji_button.activity": "Dejavnost", @@ -132,8 +150,12 @@ "empty_column.list": "Na tem seznamu ni niÄesar. Ko bodo Älani tega seznama objavili nove statuse, se bodo pojavili tukaj.", "empty_column.lists": "Nimate seznamov. Ko ga boste ustvarili, se bo prikazal tukaj.", "empty_column.mutes": "Niste utiÅ¡ali Å¡e nobenega uporabnika.", - "empty_column.notifications": "Nimate Å¡e nobenih obvestil. Poveži se z drugimi, da zaÄnete pogovor.", + "empty_column.notifications": "Nimate Å¡e nobenih obvestil. Povežite se z drugimi, da zaÄnete pogovor.", "empty_column.public": "Tukaj ni niÄesar! Da ga napolnite, napiÅ¡ite nekaj javnega ali pa roÄno sledite uporabnikom iz drugih strežnikov", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -172,7 +194,7 @@ "introduction.federation.local.headline": "Lokalno", "introduction.federation.local.text": "Javne objave ljudi na istem strežniku, se bodo prikazale na lokalni Äasovnici.", "introduction.interactions.action": "ZakljuÄi vadnico!", - "introduction.interactions.favourite.headline": "Priljubljeni", + "introduction.interactions.favourite.headline": "Vzljubi", "introduction.interactions.favourite.text": "Tut lahko shranite za pozneje in ga vzljubite ter s tem pokažete avtorju, da vam je ta tut priljubljen.", "introduction.interactions.reblog.headline": "Spodbudi", "introduction.interactions.reblog.text": "Tute drugih ljudi lahko delite z vaÅ¡imi sledilci, tako da spodbudite tute.", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Nov naslov seznama", "lists.search": "IÅ¡Äi med ljudmi, katerim sledite", "lists.subheading": "VaÅ¡i seznami", + "load_pending": "{count, plural, one {# nov element} other {# novih elementov}}", "loading_indicator.label": "Nalaganje...", "media_gallery.toggle_visible": "Preklopi vidljivost", "missing_indicator.label": "Ni najdeno", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Osebno", "navigation_bar.pins": "Pripeti tuti", "navigation_bar.preferences": "Nastavitve", - "navigation_bar.profile_directory": "Imenik profilov", "navigation_bar.public_timeline": "Združena Äasovnica", "navigation_bar.security": "Varnost", "notification.favourite": "{name} je vzljubil/a vaÅ¡ status", @@ -282,8 +304,10 @@ "notifications.group": "{count} obvestil", "poll.closed": "Zaprto", "poll.refresh": "Osveži", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural,one {# glas} other {# glasov}}", "poll.vote": "Glasuj", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Dodaj anketo", "poll_button.remove_poll": "Odstrani anketo", "privacy.change": "Prilagodi zasebnost statusa", @@ -295,6 +319,7 @@ "privacy.public.short": "Javno", "privacy.unlisted.long": "Ne objavi na javne Äasovnice", "privacy.unlisted.short": "Ni prikazano", + "refresh": "Refresh", "regeneration_indicator.label": "Nalaganje…", "regeneration_indicator.sublabel": "VaÅ¡ domaÄi vir se pripravlja!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Ljudje", "search_results.hashtags": "KljuÄniki", "search_results.statuses": "Tuti", + "search_results.statuses_fts_disabled": "Iskanje tutov po njihovi vsebini ni omogoÄeno na tem strežniku Mastodon.", "search_results.total": "{count, number} {count, plural, one {rezultat} other {rezultatov}}", "status.admin_account": "Odpri vmesnik za moderiranje za @{name}", "status.admin_status": "Odpri status v vmesniku za moderiranje", @@ -357,30 +383,39 @@ "status.show_less_all": "Prikaži manj za vse", "status.show_more": "Prikaži veÄ", "status.show_more_all": "Prikaži veÄ za vse", - "status.show_thread": "Show thread", + "status.show_thread": "Prikaži objavo", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "OdtiÅ¡aj pogovor", "status.unpin": "Odpni iz profila", - "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", + "suggestions.dismiss": "Zavrni predlog", + "suggestions.header": "Morda bi vas zanimalo…", "tabs_bar.federated_timeline": "Združeno", "tabs_bar.home": "Domov", "tabs_bar.local_timeline": "Lokalno", "tabs_bar.notifications": "Obvestila", - "tabs_bar.search": "PoiÅ¡Äi", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "tabs_bar.search": "Iskanje", + "time_remaining.days": "{number, plural, one {# dan} other {# dni}} je ostalo", + "time_remaining.hours": "{number, plural, one {# ura} other {# ur}} je ostalo", + "time_remaining.minutes": "{number, plural, one {# minuta} other {# minut}} je ostalo", + "time_remaining.moments": "Preostali trenutki", + "time_remaining.seconds": "{number, plural, one {# sekunda} other {# sekund}} je ostalo", + "trends.count_by_accounts": "{count} {rawCount, plural, one {oseba} other {ljudi}} govori", + "trends.trending_now": "Trending now", "ui.beforeunload": "VaÅ¡ osnutek bo izgubljen, Äe zapustite Mastodona.", - "upload_area.title": "Povlecite in spustite za poÅ¡iljanje", - "upload_button.label": "Dodaj medij", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", + "upload_area.title": "Za poÅ¡iljanje povlecite in spustite", + "upload_button.label": "Dodaj medije ({formats})", + "upload_error.limit": "Omejitev prenosa datoteke je presežena.", + "upload_error.poll": "Prenos datoteke z anketami ni dovoljen.", "upload_form.description": "OpiÅ¡ite za slabovidne", - "upload_form.focus": "Obreži", + "upload_form.edit": "Edit", "upload_form.undo": "IzbriÅ¡i", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "PoÅ¡iljanje...", "video.close": "Zapri video", "video.exit_fullscreen": "Izhod iz celozaslonskega naÄina", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 3962e35f0c55e8383e3f38b3354007ef0e85e28b..1f6de613d636ed674a3b018ef3544e4f88e291f2 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -4,6 +4,7 @@ "account.block": "Blloko @{name}", "account.block_domain": "Fshih gjithçka prej {domain}", "account.blocked": "E bllokuar", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Mesazh i drejtpërdrejt për @{name}", "account.domain_blocked": "Përkatësi e fshehur", "account.edit_profile": "Përpunoni profilin", @@ -15,6 +16,7 @@ "account.follows.empty": "Ky përdorues ende s’ndjek njeri.", "account.follows_you": "Ju ndjek", "account.hide_reblogs": "Fshih përforcime nga @{name}", + "account.last_status": "Last active", "account.link_verified_on": "Pronësia e kësaj lidhjeje qe kontrolluar më {date}", "account.locked_info": "Gjendja e privatësisë së kësaj llogarie është caktuar si e kyçur. I zoti merr dorazi në shqyrtim cilët mund ta ndjekin.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "Heshtoni @{name}", "account.mute_notifications": "Heshtoji njoftimet prej @{name}", "account.muted": "Heshtuar", + "account.never_active": "Never", "account.posts": "Mesazhe", "account.posts_with_replies": "Mesazhe dhe përgjigje", "account.report": "Raportojeni @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Resht së ndjekuri", "account.unmute": "Ktheji zërin @{name}", "account.unmute_notifications": "Hiqua ndalimin e shfaqjes njoftimeve nga @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Ndodhi një gabim të papritur.", "alert.unexpected.title": "Hëm!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Mund të shtypni {combo}, që të anashkalohet kjo herës tjetër", "bundle_column_error.body": "Diç shkoi ters teksa ngarkohej ky përbërës.", "bundle_column_error.retry": "Riprovoni", @@ -47,6 +53,7 @@ "column.blocks": "Përdorues të bllokuar", "column.community": "Rrjedhë kohore vendore", "column.direct": "Mesazhe të drejtpërdrejta", + "column.directory": "Browse profiles", "column.domain_blocks": "Përkatësi të fshehura", "column.favourites": "Të parapëlqyer", "column.follow_requests": "Kërkesa për ndjekje", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Jeni i sigurt që doni të fshihet përgjithmonë kjo listë?", "confirmations.domain_block.confirm": "Fshih krejt përkatësinë", "confirmations.domain_block.message": "Jeni i sigurt, shumë i sigurt se doni të bllokohet krejt {domain}? Në shumicën e rasteve, ndoca bllokime ose heshtime me synim të caktuar janë të mjaftueshme dhe të parapëlqyera. S’keni për të parë lëndë nga kjo përkatësi në ndonjë rrjedhë kohore publike, apo te njoftimet tuaja. Ndjekësit tuaj prej asaj përkatësie do të hiqen.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Heshtoje", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Jeni i sigurt se doni të heshtohet {name}?", "confirmations.redraft.confirm": "Fshijeni & rihartojeni", "confirmations.redraft.message": "Jeni i sigurt se doni të fshihet kjo gjendje dhe të rihartohet? Parapëlqimet dhe boosts do të humbin, ndërsa përgjigjet te postimi origjinal do të bëhen jetime.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Përgjigja tani do të shkaktojë mbishkrimin e mesazhit që po hartoni. Jeni i sigurt se doni të vazhdohet më tej?", "confirmations.unfollow.confirm": "Resht së ndjekuri", "confirmations.unfollow.message": "Jeni i sigurt se doni të mos ndiqet më {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Trupëzojeni këtë gjendje në sajtin tuaj duke kopjuar kodin më poshtë.", "embed.preview": "Ja si do të duket:", "emoji_button.activity": "Veprimtari", @@ -134,6 +152,10 @@ "empty_column.mutes": "S’keni heshtuar ende ndonjë përdorues.", "empty_column.notifications": "Ende s’keni ndonjë njoftim. Ndërveproni me të tjerët që të nisë biseda.", "empty_column.public": "S’ka gjë këtu! Shkruani diçka publikisht, ose ndiqni dorazi përdorues prej instancash të tjera, që ta mbushni këtë zonë", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Titull liste të re", "lists.search": "Kërkoni mes personash që ndiqni", "lists.subheading": "Listat tuaja", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Po ngarkohet…", "media_gallery.toggle_visible": "Ndërroni dukshmërinë", "missing_indicator.label": "S’u gjet", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personale", "navigation_bar.pins": "Mesazhe të fiksuar", "navigation_bar.preferences": "Parapëlqime", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Rrjedhë kohore të federuarish", "navigation_bar.security": "Siguri", "notification.favourite": "{name} parapëlqeu gjendjen tuaj", @@ -282,8 +304,10 @@ "notifications.group": "{count}s njoftime", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Rregulloni privatësi gjendje", @@ -295,6 +319,7 @@ "privacy.public.short": "Publike", "privacy.unlisted.long": "Mos e postoni në rrjedha publike kohore", "privacy.unlisted.short": "Jo në lista", + "refresh": "Refresh", "regeneration_indicator.label": "Po ngarkohet…", "regeneration_indicator.sublabel": "Prurja juaj vetjake po përgatiteet!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Persona", "search_results.hashtags": "Hashtagë", "search_results.statuses": "Mesazhe", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, një {result} {results} të tjera}", "status.admin_account": "Hap ndërfaqe moderimi për @{name}", "status.admin_status": "Hape këtë gjendje te ndërfaqja e moderimit", @@ -358,6 +384,7 @@ "status.show_more": "Shfaq më tepër", "status.show_more_all": "Shfaq më tepër për të tërë", "status.show_thread": "Shfaq rrjedhën", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Ktheji zërin bisedës", "status.unpin": "Shfiksoje nga profili", "suggestions.dismiss": "Mos e merr parasysh sugjerimin", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, një {person} {people} të tjerë} po flasin", + "trends.trending_now": "Trending now", "ui.beforeunload": "Skica juaj do të humbë nëse dilni nga Mastodon-i.", "upload_area.title": "Merreni & vëreni që të ngarkohet", "upload_button.label": "Shtoni media (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "U tejkalua kufi ngarkimi kartelash.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Përshkruajeni për persona me probleme shikimi", - "upload_form.focus": "Ndryshoni parapamjen", + "upload_form.edit": "Edit", "upload_form.undo": "Fshije", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Po ngarkohet…", "video.close": "Mbylle videon", "video.exit_fullscreen": "Dil nga mënyra Sa Krejt Ekrani", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index b432dc70847f53d7ac94bb430bc174eb466f832c..e396087dff74b93a47bf97e6fb7b8fe8618d19ab 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -4,6 +4,7 @@ "account.block": "Blokiraj korisnika @{name}", "account.block_domain": "Sakrij sve sa domena {domain}", "account.blocked": "Blocked", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Direct Message @{name}", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Izmeni profil", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Prati Vas", "account.hide_reblogs": "Sakrij podrÅ¡ke koje daje korisnika @{name}", + "account.last_status": "Last active", "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.media": "Mediji", @@ -23,6 +25,7 @@ "account.mute": "Ućutkaj korisnika @{name}", "account.mute_notifications": "IskljuÄi obaveÅ¡tenja od korisnika @{name}", "account.muted": "Muted", + "account.never_active": "Never", "account.posts": "Statusa", "account.posts_with_replies": "Toots with replies", "account.report": "Prijavi @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Otprati", "account.unmute": "Ukloni ućutkavanje korisniku @{name}", "account.unmute_notifications": "UkljuÄi nazad obaveÅ¡tenja od korisnika @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Možete pritisnuti {combo} da preskoÄite ovo sledeći put", "bundle_column_error.body": "NeÅ¡to je poÅ¡lo po zlu prilikom uÄitavanja ove komponente.", "bundle_column_error.retry": "PokuÅ¡ajte ponovo", @@ -47,6 +53,7 @@ "column.blocks": "Blokirani korisnici", "column.community": "Lokalna lajna", "column.direct": "Direct messages", + "column.directory": "Browse profiles", "column.domain_blocks": "Hidden domains", "column.favourites": "Omiljeni", "column.follow_requests": "Zahtevi za praćenje", @@ -64,7 +71,7 @@ "column_header.show_settings": "Prikaži postavke", "column_header.unpin": "OtkaÄi", "column_subheading.settings": "Postavke", - "community.column_settings.media_only": "Media Only", + "community.column_settings.media_only": "Media only", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obriÅ¡ete ovu listu?", "confirmations.domain_block.confirm": "Sakrij ceo domen", "confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini sluÄajeva, par dobrih blokiranja ili ućutkavanja su dovoljna i preporuÄljiva.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Ućutkaj", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Da li stvarno želite da ućutkate korisnika {name}?", "confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Otprati", "confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Ugradi ovaj status na VaÅ¡ veb sajt kopiranjem koda ispod.", "embed.preview": "Ovako će da izgleda:", "emoji_button.activity": "Aktivnost", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "Trenutno nemate obaveÅ¡tenja. Družite se malo da zapoÄnete razgovore.", "empty_column.public": "Ovde nema niÄega! NapiÅ¡ite neÅ¡to javno, ili naÄ‘ite korisnike sa drugih instanci koje ćete zapratiti da popunite ovu prazninu", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Naslov nove liste", "lists.search": "Pretraži meÄ‘u ljudima koje pratite", "lists.subheading": "VaÅ¡e liste", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "UÄitavam...", "media_gallery.toggle_visible": "UkljuÄi/iskljuÄi vidljivost", "missing_indicator.label": "Nije pronaÄ‘eno", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "PrikaÄeni tutovi", "navigation_bar.preferences": "PodeÅ¡avanja", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federisana lajna", "navigation_bar.security": "Security", "notification.favourite": "{name} je stavio VaÅ¡ status kao omiljeni", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "Podesi status privatnosti", @@ -295,6 +319,7 @@ "privacy.public.short": "Javno", "privacy.unlisted.long": "Ne objavljuj na javnim lajnama", "privacy.unlisted.short": "Neizlistano", + "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "Hashtags", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Prikaži viÅ¡e", "status.show_more_all": "Show more for all", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "UkljuÄi prepisku", "status.unpin": "OtkaÄi sa profila", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "trends.trending_now": "Trending now", "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.", "upload_area.title": "Prevucite ovde da otpremite", "upload_button.label": "Dodaj multimediju", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "OpiÅ¡i za slabovide osobe", - "upload_form.focus": "Crop", + "upload_form.edit": "Edit", "upload_form.undo": "Opozovi", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Otpremam...", "video.close": "Zatvori video", "video.exit_fullscreen": "Napusti ceo ekran", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 22c468076a391549d91bf4cb5cadab71bd178d52..40d14599a41b15a2f0abec6c01626e480d08c851 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -4,6 +4,7 @@ "account.block": "Блокирај @{name}", "account.block_domain": "Сакриј Ñве Ñа домена {domain}", "account.blocked": "Блокиран", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "Директна порука @{name}", "account.domain_blocked": "Домен Ñакривен", "account.edit_profile": "Измени профил", @@ -15,6 +16,7 @@ "account.follows.empty": "КориÑник тренутно не прати никога.", "account.follows_you": "Прати ВаÑ", "account.hide_reblogs": "Сакриј подршке које даје кориÑника @{name}", + "account.last_status": "Last active", "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.media": "Медији", @@ -23,6 +25,7 @@ "account.mute": "Ућуткај кориÑника @{name}", "account.mute_notifications": "ИÑкључи обавештења од кориÑника @{name}", "account.muted": "Ућуткан", + "account.never_active": "Never", "account.posts": "Трубе", "account.posts_with_replies": "Трубе и одговори", "account.report": "Пријави @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "Отпрати", "account.unmute": "Уклони ућуткавање кориÑнику @{name}", "account.unmute_notifications": "Укључи назад обавештења од кориÑника @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "Појавила Ñе неочекивана грешка.", "alert.unexpected.title": "УпÑ!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "Можете притиÑнути {combo} да преÑкочите ово Ñледећи пут", "bundle_column_error.body": "Ðешто је пошло по злу приликом учитавања ове компоненте.", "bundle_column_error.retry": "Покушајте поново", @@ -47,6 +53,7 @@ "column.blocks": "Блокирани кориÑници", "column.community": "Локална временÑка линија", "column.direct": "Директне поруке", + "column.directory": "Browse profiles", "column.domain_blocks": "Скривени домени", "column.favourites": "Омиљене", "column.follow_requests": "Захтеви за праћење", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Да ли Ñте Ñигурни да желите да беÑповратно обришете ову лиÑту?", "confirmations.domain_block.confirm": "Сакриј цео домен", "confirmations.domain_block.message": "Да ли Ñте заиÑта Ñигурни да желите да блокирате цео домен {domain}? У већини Ñлучајева, неколико добро промишљених блокирања или ућуткавања Ñу довољна и препоручљива.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "Ућуткај", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Да ли Ñтварно желите да ућуткате кориÑника {name}?", "confirmations.redraft.confirm": "Избриши и преправи", "confirmations.redraft.message": "Да ли Ñте Ñигурни да желите да избришете овај ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¸ да га преправите? Сва Ñтављања у омиљене трубе, као и подршке ће бити изгубљене, а одговори на оригинални поÑÑ‚ ће бити поништени.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "Отпрати", "confirmations.unfollow.message": "Да ли Ñте Ñигурни да желите да отпратите кориÑника {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "Угради овај ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° Ваш веб Ñајт копирањем кода иÑпод.", "embed.preview": "Овако ће да изгледа:", "emoji_button.activity": "ÐктивноÑÑ‚", @@ -134,6 +152,10 @@ "empty_column.mutes": "Још увек немате ућутканих кориÑника.", "empty_column.notifications": "Тренутно немате обавештења. Дружите Ñе мало да започнете разговор.", "empty_column.public": "Овде нема ничега! Ðапишите нешто јавно, или нађите кориÑнике Ñа других инÑтанци које ћете запратити да попуните ову празнину", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "ÐаÑлов нове лиÑте", "lists.search": "Претражи међу људима које пратите", "lists.subheading": "Ваше лиÑте", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "Учитавам...", "media_gallery.toggle_visible": "Укључи/иÑкључи видљивоÑÑ‚", "missing_indicator.label": "Ðије пронађено", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "Прикачене трубе", "navigation_bar.preferences": "Подешавања", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Здружена временÑка линија", "navigation_bar.security": "БезбедноÑÑ‚", "notification.favourite": "{name} је Ñтавио/ла Ваш ÑÑ‚Ð°Ñ‚ÑƒÑ ÐºÐ°Ð¾ омиљени", @@ -282,8 +304,10 @@ "notifications.group": "{count} обавештења", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "ПодеÑи ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¿Ñ€Ð¸Ð²Ð°Ñ‚Ð½Ð¾Ñти", @@ -295,6 +319,7 @@ "privacy.public.short": "Јавно", "privacy.unlisted.long": "Ðе објављуј на јавним временÑким линијама", "privacy.unlisted.short": "ÐеизлиÑтано", + "refresh": "Refresh", "regeneration_indicator.label": "Учитавање…", "regeneration_indicator.sublabel": "Ваша почетна Ñтраница Ñе припрема!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "Људи", "search_results.hashtags": "Тарабе", "search_results.statuses": "Трубе", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "Прикажи више", "status.show_more_all": "Прикажи више за Ñве", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "Укључи препиÑку", "status.unpin": "Откачи Ñа профила", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {човек} other {људи}} прича", + "trends.trending_now": "Trending now", "ui.beforeunload": "Ðко напуÑтите МаÑтодонт, изгубићете напиÑани нацрт.", "upload_area.title": "Превуците овде да отпремите", "upload_button.label": "Додај мултимедију (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Опишите за оÑобе Ñа оштећеним видом", - "upload_form.focus": "ПодеÑите", + "upload_form.edit": "Edit", "upload_form.undo": "Обриши", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Отпремам...", "video.close": "Затвори видео", "video.exit_fullscreen": "ÐапуÑти цео екран", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 56e2c1a5d58bab615299eefa2bc4e6bc80529e30..2bbb534c889df84c730304f1768a08d066b78caf 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -4,52 +4,59 @@ "account.block": "Blockera @{name}", "account.block_domain": "Dölj allt frÃ¥n {domain}", "account.blocked": "Blockerad", - "account.direct": "Direktmeddelande @{name}", + "account.cancel_follow_request": "Avbryt förfrÃ¥gan att följa", + "account.direct": "Skicka ett direktmeddelande till @{name}", "account.domain_blocked": "Domän dold", "account.edit_profile": "Redigera profil", - "account.endorse": "Feature on profile", + "account.endorse": "Visa upp pÃ¥ profil", "account.follow": "Följ", "account.followers": "Följare", "account.followers.empty": "Ingen följer denna användaren än.", "account.follows": "Följer", - "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows.empty": "Den här användaren följer inte nÃ¥gon ännu.", "account.follows_you": "Följer dig", "account.hide_reblogs": "Dölj knuffar frÃ¥n @{name}", - "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.last_status": "Senast aktiv", + "account.link_verified_on": "Ägarskapet för det här kontot kontrollerades den {date}", + "account.locked_info": "Detta konto har en lÃ¥st integritetsstatus. Ägaren väljer manuellt vem som kan följa.", "account.media": "Media", "account.mention": "Nämna @{name}", "account.moved_to": "{name} har flyttat till:", "account.mute": "Tysta @{name}", "account.mute_notifications": "Stäng av notifieringar frÃ¥n @{name}", - "account.muted": "Nertystad", + "account.muted": "Tystad", + "account.never_active": "Aldrig", "account.posts": "Inlägg", "account.posts_with_replies": "Toots och svar", "account.report": "Rapportera @{name}", "account.requested": "Inväntar godkännande. Klicka för att avbryta följförfrÃ¥gan", - "account.share": "Dela @{name}'s profil", + "account.share": "Dela @{name}s profil", "account.show_reblogs": "Visa knuffar frÃ¥n @{name}", "account.unblock": "Avblockera @{name}", - "account.unblock_domain": "Ta fram {domain}", - "account.unendorse": "Don't feature on profile", + "account.unblock_domain": "Sluta dölja {domain}", + "account.unendorse": "Visa inte upp pÃ¥ profil", "account.unfollow": "Sluta följa", - "account.unmute": "Ta bort tystad @{name}", + "account.unmute": "Sluta tysta @{name}", "account.unmute_notifications": "Ã…teraktivera notifikationer frÃ¥n @{name}", + "alert.rate_limited.message": "Vänligen försök igen efter {retry_time, time, medium}.", + "alert.rate_limited.title": "Begränsad mängd", "alert.unexpected.message": "Ett oväntat fel uppstod.", - "alert.unexpected.title": "Whups!", + "alert.unexpected.title": "Hoppsan!", + "autosuggest_hashtag.per_week": "{count} per vecka", "boost_modal.combo": "Du kan trycka {combo} för att slippa denna nästa gÃ¥ng", "bundle_column_error.body": "NÃ¥got gick fel när du laddade denna komponent.", "bundle_column_error.retry": "Försök igen", "bundle_column_error.title": "Nätverksfel", "bundle_modal_error.close": "Stäng", - "bundle_modal_error.message": "NÃ¥got gick fel när du laddade denna komponent.", + "bundle_modal_error.message": "NÃ¥got gick fel när denna komponent laddades.", "bundle_modal_error.retry": "Försök igen", "column.blocks": "Blockerade användare", "column.community": "Lokal tidslinje", - "column.direct": "Direktmeddelande", + "column.direct": "Direktmeddelanden", + "column.directory": "Bläddra bland profiler", "column.domain_blocks": "Dolda domäner", "column.favourites": "Favoriter", - "column.follow_requests": "Följ förfrÃ¥gningar", + "column.follow_requests": "FöljförfrÃ¥gningar", "column.home": "Hem", "column.lists": "Listor", "column.mutes": "Tystade användare", @@ -65,75 +72,90 @@ "column_header.unpin": "Ã…ngra fäst", "column_subheading.settings": "Inställningar", "community.column_settings.media_only": "Enbart media", - "compose_form.direct_message_warning": "Denna toot kommer endast att skickas nämnda nämnda användare.", + "compose_form.direct_message_warning": "Denna toot kommer endast att skickas till nämnda användare.", "compose_form.direct_message_warning_learn_more": "Visa mer", "compose_form.hashtag_warning": "Denna toot kommer inte att listas under nÃ¥gon hashtag eftersom den är onoterad. Endast offentliga toots kan sökas med hashtag.", - "compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vemsomhelst kan följa dig och även se dina inlägg skrivna för endast dina följare.", + "compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vem som helst kan följa dig och även se dina inlägg som bara är för följare.", "compose_form.lock_disclaimer.lock": "lÃ¥st", "compose_form.placeholder": "Vad funderar du pÃ¥?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", - "compose_form.publish": "Toot", + "compose_form.poll.add_option": "Nytt alternativ", + "compose_form.poll.duration": "Varaktighet för omröstning", + "compose_form.poll.option_placeholder": "Alternativ {number}", + "compose_form.poll.remove_option": "Ta bort alternativ", + "compose_form.publish": "Tut", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Markera media som känsligt", "compose_form.sensitive.marked": "Media har markerats som känsligt", "compose_form.sensitive.unmarked": "Media har inte markerats som känsligt", "compose_form.spoiler.marked": "Texten har dolts bakom en varning", "compose_form.spoiler.unmarked": "Texten är inte dold", "compose_form.spoiler_placeholder": "Skriv din varning här", "confirmation_modal.cancel": "Ã…ngra", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "Blockera & rapportera", "confirmations.block.confirm": "Blockera", "confirmations.block.message": "Är du säker att du vill blockera {name}?", "confirmations.delete.confirm": "Ta bort", "confirmations.delete.message": "Är du säker att du vill ta bort denna status?", - "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.confirm": "Ta bort", "confirmations.delete_list.message": "Är du säker pÃ¥ att du vill radera denna lista permanent?", - "confirmations.domain_block.confirm": "Blockera hela domänen", + "confirmations.domain_block.confirm": "Dölj hela domänen", "confirmations.domain_block.message": "Är du verkligen säker pÃ¥ att du vill blockera hela {domain}? I de flesta fall är nÃ¥gra riktade blockeringar eller nedtystade konton tillräckligt och att föredra. Du kommer sluta se innehÃ¥ll frÃ¥n {domain}-domänen i den allmänna tidslinjen och i dina egna notifieringar. Du kommer även sluta följa alla eventuella följare du har frÃ¥n {domain}.", + "confirmations.logout.confirm": "Logga ut", + "confirmations.logout.message": "Är du säker pÃ¥ att du vill logga ut?", "confirmations.mute.confirm": "Tysta", + "confirmations.mute.explanation": "Detta kommer dölja postningar frÃ¥n dem och postningar som nämner dem, men fortfarande tillÃ¥ta dem att se dina postningar och följa dig.", "confirmations.mute.message": "Är du säker du vill tysta ner {name}?", "confirmations.redraft.confirm": "Radera och gör om", "confirmations.redraft.message": "Är du säker pÃ¥ att du vill radera meddelandet och göra om det? Du kommer förlora alla svar, knuffar och favoriter som hänvisar till meddelandet.", - "confirmations.reply.confirm": "Reply", - "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.reply.confirm": "Svara", + "confirmations.reply.message": "Om du svarar nu kommer det att ersätta meddelandet du hÃ¥ller pÃ¥ att skriva. Är du säker pÃ¥ att du vill fortsätta?", "confirmations.unfollow.confirm": "Sluta följa", "confirmations.unfollow.message": "Är du säker pÃ¥ att du vill sluta följa {name}?", + "conversation.delete": "Radera konversation", + "conversation.mark_as_read": "Markera som läst", + "conversation.open": "Se konversation", + "conversation.with": "Med {names}", + "directory.federated": "FrÃ¥n känt fediverse", + "directory.local": "Endast frÃ¥n {domain}", + "directory.new_arrivals": "Nyanlända", + "directory.recently_active": "Nyligen aktiva", "embed.instructions": "Bädda in den här statusen pÃ¥ din webbplats genom att kopiera koden nedan.", - "embed.preview": "Här ser du hur det kommer att se ut:", + "embed.preview": "SÃ¥ här kommer det att se ut:", "emoji_button.activity": "Aktivitet", - "emoji_button.custom": "Specialgjord", + "emoji_button.custom": "Anpassad", "emoji_button.flags": "Flaggor", - "emoji_button.food": "Mat & Dryck", + "emoji_button.food": "Mat & dryck", "emoji_button.label": "Lägg till emoji", "emoji_button.nature": "Natur", "emoji_button.not_found": "Inga emojos!! (╯°□°)╯︵ â”»â”â”»", "emoji_button.objects": "Objekt", - "emoji_button.people": "Människor", + "emoji_button.people": "Personer", "emoji_button.recent": "Ofta använda", "emoji_button.search": "Sök...", "emoji_button.search_results": "Sökresultat", "emoji_button.symbols": "Symboler", - "emoji_button.travel": "Resor & Platser", - "empty_column.account_timeline": "No toots here!", - "empty_column.account_unavailable": "Profile unavailable", - "empty_column.blocks": "You haven't blocked any users yet.", - "empty_column.community": "Den lokala tidslinjen är tom. Skriv nÃ¥got offentligt för att fÃ¥ bollen att rulla!", - "empty_column.direct": "Du har inga direktmeddelanden än. När du skickar eller tar emot kommer den att dyka upp här.", - "empty_column.domain_blocks": "There are no hidden domains yet.", - "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", - "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", - "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "emoji_button.travel": "Resor & platser", + "empty_column.account_timeline": "Inga inlägg här!", + "empty_column.account_unavailable": "Profilen är inte tillgänglig", + "empty_column.blocks": "Du har ännu inte blockerat nÃ¥gra användare.", + "empty_column.community": "Den lokala tidslinjen är tom. Skriv nÃ¥got offentligt för att sätta bollen i rullning!", + "empty_column.direct": "Du har inga direktmeddelanden än. När du skickar eller tar emot ett kommer det att dyka upp här.", + "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.", + "empty_column.favourited_statuses": "Du har inga favoritmarkerade toots än. När du favoritmarkerar en kommer den synas här.", + "empty_column.favourites": "Ingen har favoritmarkerat den här tooten än. När nÃ¥gon gör det kommer de synas här.", + "empty_column.follow_requests": "Du har inga följförfrÃ¥gningar än. När du fÃ¥r en kommer den synas här.", "empty_column.hashtag": "Det finns inget i denna hashtag ännu.", "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igÃ¥ng och träffa andra användare.", "empty_column.home.public_timeline": "den publika tidslinjen", "empty_column.list": "Det finns inget i denna lista än. När medlemmar i denna lista lägger till nya statusar kommer de att visas här.", - "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", - "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.lists": "Du har inga listor än. När skapar en kommer den dyka upp här.", + "empty_column.mutes": "Du har ännu inte tystat nÃ¥gra användare.", "empty_column.notifications": "Du har inga meddelanden än. Interagera med andra för att starta konversationen.", "empty_column.public": "Det finns inget här! Skriv nÃ¥got offentligt, eller följ manuellt användarna frÃ¥n andra instanser för att fylla pÃ¥ det", + "error.unexpected_crash.explanation": "PÃ¥ grund av en bugg i vÃ¥r kod eller kompatiblitetsproblem i webbläsaren kan den här sidan inte visas korrekt.", + "error.unexpected_crash.next_steps": "Prova att ladda om sidan. Om det inte hjälper kan du försöka använda Mastodon med en annan webbläsare eller app.", + "errors.unexpected_crash.copy_stacktrace": "Kopiera stacktrace till urklipp", + "errors.unexpected_crash.report_issue": "Rapportera problem", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,81 +164,81 @@ "follow_request.authorize": "Godkänn", "follow_request.reject": "Avvisa", "getting_started.developers": "Utvecklare", - "getting_started.directory": "Profile directory", - "getting_started.documentation": "Documentation", + "getting_started.directory": "Profilkatalog", + "getting_started.documentation": "Dokumentation", "getting_started.heading": "Kom igÃ¥ng", "getting_started.invite": "Skicka inbjudningar", "getting_started.open_source_notice": "Mastodon är programvara med öppen källkod. Du kan bidra eller rapportera problem via GitHub pÃ¥ {github}.", "getting_started.security": "Säkerhet", "getting_started.terms": "Användarvillkor", - "hashtag.column_header.tag_mode.all": "and {additional}", - "hashtag.column_header.tag_mode.any": "or {additional}", - "hashtag.column_header.tag_mode.none": "without {additional}", - "hashtag.column_settings.select.no_options_message": "No suggestions found", - "hashtag.column_settings.select.placeholder": "Enter hashtags…", - "hashtag.column_settings.tag_mode.all": "All of these", - "hashtag.column_settings.tag_mode.any": "Any of these", + "hashtag.column_header.tag_mode.all": "och {additional}", + "hashtag.column_header.tag_mode.any": "eller {additional}", + "hashtag.column_header.tag_mode.none": "utan {additional}", + "hashtag.column_settings.select.no_options_message": "Inga förslag hittades", + "hashtag.column_settings.select.placeholder": "Ange hashtags …", + "hashtag.column_settings.tag_mode.all": "Alla dessa", + "hashtag.column_settings.tag_mode.any": "NÃ¥gon av dessa", "hashtag.column_settings.tag_mode.none": "Ingen av dessa", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", "home.column_settings.basic": "Grundläggande", "home.column_settings.show_reblogs": "Visa knuffar", "home.column_settings.show_replies": "Visa svar", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.days": "{number, plural, one {# dag} other {# dagar}}", + "intervals.full.hours": "{hours, plural, one {# timme} other {# timmar}}", + "intervals.full.minutes": "{minutes, plural, one {1 minut} other {# minuter}}", "introduction.federation.action": "Nästa", - "introduction.federation.federated.headline": "Federated", - "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.federation.local.headline": "Local", - "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", - "introduction.interactions.action": "Finish toot-orial!", - "introduction.interactions.favourite.headline": "Favourite", - "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", - "introduction.interactions.reblog.headline": "Boost", - "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", - "keyboard_shortcuts.back": "att navigera tillbaka", - "keyboard_shortcuts.blocked": "to open blocked users list", - "keyboard_shortcuts.boost": "att knuffa", - "keyboard_shortcuts.column": "att fokusera en status i en av kolumnerna", - "keyboard_shortcuts.compose": "att fokusera komponera text fältet", - "keyboard_shortcuts.description": "Description", - "keyboard_shortcuts.direct": "to open direct messages column", - "keyboard_shortcuts.down": "att flytta ner i listan", - "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "att favorisera", - "keyboard_shortcuts.favourites": "to open favourites list", - "keyboard_shortcuts.federated": "to open federated timeline", - "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.home": "to open home timeline", - "keyboard_shortcuts.hotkey": "Snabbvalstangent", - "keyboard_shortcuts.legend": "att visa denna översikt", - "keyboard_shortcuts.local": "to open local timeline", - "keyboard_shortcuts.mention": "att nämna författaren", - "keyboard_shortcuts.muted": "to open muted users list", - "keyboard_shortcuts.my_profile": "to open your profile", - "keyboard_shortcuts.notifications": "to open notifications column", - "keyboard_shortcuts.pinned": "to open pinned toots list", - "keyboard_shortcuts.profile": "to open author's profile", - "keyboard_shortcuts.reply": "att svara", - "keyboard_shortcuts.requests": "to open follow requests list", - "keyboard_shortcuts.search": "att fokusera sökfältet", - "keyboard_shortcuts.start": "to open \"get started\" column", - "keyboard_shortcuts.toggle_hidden": "att visa/gömma text bakom CW", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", - "keyboard_shortcuts.toot": "att börja en helt ny toot", - "keyboard_shortcuts.unfocus": "att avfokusera komponera text fält / sökfält", - "keyboard_shortcuts.up": "att flytta upp i listan", + "introduction.federation.federated.headline": "Federerad", + "introduction.federation.federated.text": "Publika inlägg frÃ¥n andra servrar i servernätverket visas i den förenade tidslinjen.", + "introduction.federation.home.headline": "Hem", + "introduction.federation.home.text": "Inlägg frÃ¥n personer du följer kommer att visas i din hemtidlinje. Du kan följa vem du vill pÃ¥ vald server!", + "introduction.federation.local.headline": "Lokal", + "introduction.federation.local.text": "Publika inlägg frÃ¥n personer pÃ¥ samma server som du kommer att visas pÃ¥ den lokala tidslinjen.", + "introduction.interactions.action": "Slutför introduktionsguide!", + "introduction.interactions.favourite.headline": "Favorit", + "introduction.interactions.favourite.text": "Du kan spara en tut till senare, och visa författaren att du gillade den genom att göra den till favorit.", + "introduction.interactions.reblog.headline": "Knuffa", + "introduction.interactions.reblog.text": "Du kan dela andra personers tutar med dina följare genom att knuffa dem.", + "introduction.interactions.reply.headline": "Svara", + "introduction.interactions.reply.text": "Du kan besvara andra personers och dina egna tutar, vilket kommer att koppla ihop dem i en konversation.", + "introduction.welcome.action": "Sätt igÃ¥ng!", + "introduction.welcome.headline": "Första stegen", + "introduction.welcome.text": "Välkommen till fediverse! Om nÃ¥gra ögonblick kommer du kunna sända ut meddelanden och prata med dina vänner över en mängd servrar. Men den här servern, {domain}, är speciell — den är hem Ã¥t din profil, sÃ¥ kom ihÃ¥g vad den heter.", + "keyboard_shortcuts.back": "för att gÃ¥ bakÃ¥t", + "keyboard_shortcuts.blocked": "för att öppna listan över blockerade användare", + "keyboard_shortcuts.boost": "för att knuffa", + "keyboard_shortcuts.column": "för att fokusera en status i en av kolumnerna", + "keyboard_shortcuts.compose": "för att fokusera skrivfältet", + "keyboard_shortcuts.description": "Beskrivning", + "keyboard_shortcuts.direct": "för att öppna Direktmeddelanden", + "keyboard_shortcuts.down": "för att flytta nedÃ¥t i listan", + "keyboard_shortcuts.enter": "för att öppna en status", + "keyboard_shortcuts.favourite": "för att sätta som favorit", + "keyboard_shortcuts.favourites": "för att öppna Favoriter", + "keyboard_shortcuts.federated": "för att öppna Förenad tidslinje", + "keyboard_shortcuts.heading": "Tangentbordsgenvägar", + "keyboard_shortcuts.home": "för att öppna Hem-tidslinjen", + "keyboard_shortcuts.hotkey": "Kommando", + "keyboard_shortcuts.legend": "för att visa denna översikt", + "keyboard_shortcuts.local": "för att öppna Lokal tidslinje", + "keyboard_shortcuts.mention": "för att nämna skaparen", + "keyboard_shortcuts.muted": "för att öppna listan över tystade användare", + "keyboard_shortcuts.my_profile": "för att öppna din profil", + "keyboard_shortcuts.notifications": "för att öppna Meddelanden", + "keyboard_shortcuts.pinned": "för att öppna NÃ¥lade toots", + "keyboard_shortcuts.profile": "för att öppna skaparens profil", + "keyboard_shortcuts.reply": "för att svara", + "keyboard_shortcuts.requests": "för att öppna FöljförfrÃ¥gningar", + "keyboard_shortcuts.search": "för att fokusera sökfältet", + "keyboard_shortcuts.start": "för att öppna \"Kom igÃ¥ng\"-kolumnen", + "keyboard_shortcuts.toggle_hidden": "för att visa/gömma text bakom CW", + "keyboard_shortcuts.toggle_sensitivity": "för att visa/gömma media", + "keyboard_shortcuts.toot": "för att pÃ¥börja en helt ny toot", + "keyboard_shortcuts.unfocus": "för att avfokusera skrivfält/sökfält", + "keyboard_shortcuts.up": "för att flytta uppÃ¥t i listan", "lightbox.close": "Stäng", "lightbox.next": "Nästa", "lightbox.previous": "Tidigare", - "lightbox.view_context": "View context", + "lightbox.view_context": "Visa kontext", "lists.account.add": "Lägg till i lista", "lists.account.remove": "Ta bort frÃ¥n lista", "lists.delete": "Radera lista", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Ny listrubrik", "lists.search": "Sök bland personer du följer", "lists.subheading": "Dina listor", + "load_pending": "{count, plural, other {# objekt}}", "loading_indicator.label": "Laddar...", "media_gallery.toggle_visible": "Växla synlighet", "missing_indicator.label": "Hittades inte", @@ -234,58 +257,59 @@ "navigation_bar.apps": "Mobilappar", "navigation_bar.blocks": "Blockerade användare", "navigation_bar.community_timeline": "Lokal tidslinje", - "navigation_bar.compose": "Compose new toot", + "navigation_bar.compose": "Författa ny toot", "navigation_bar.direct": "Direktmeddelanden", "navigation_bar.discover": "Upptäck", "navigation_bar.domain_blocks": "Dolda domäner", "navigation_bar.edit_profile": "Redigera profil", "navigation_bar.favourites": "Favoriter", - "navigation_bar.filters": "Muted words", + "navigation_bar.filters": "Tystade ord", "navigation_bar.follow_requests": "FöljförfrÃ¥gningar", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Följer och följare", "navigation_bar.info": "Om denna instans", - "navigation_bar.keyboard_shortcuts": "Tangentbordsgenvägar", + "navigation_bar.keyboard_shortcuts": "Kortkommandon", "navigation_bar.lists": "Listor", "navigation_bar.logout": "Logga ut", "navigation_bar.mutes": "Tystade användare", - "navigation_bar.personal": "Personal", + "navigation_bar.personal": "Personligt", "navigation_bar.pins": "NÃ¥lade inlägg (toots)", "navigation_bar.preferences": "Inställningar", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Förenad tidslinje", "navigation_bar.security": "Säkerhet", "notification.favourite": "{name} favoriserade din status", "notification.follow": "{name} följer dig", "notification.mention": "{name} nämnde dig", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "En omröstning du röstat i har avslutats", "notification.reblog": "{name} knuffade din status", "notifications.clear": "Rensa meddelanden", "notifications.clear_confirmation": "Är du säker pÃ¥ att du vill radera alla dina meddelanden permanent?", "notifications.column_settings.alert": "Skrivbordsmeddelanden", "notifications.column_settings.favourite": "Favoriter:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Visa alla kategorier", + "notifications.column_settings.filter_bar.category": "Snabbfilter", + "notifications.column_settings.filter_bar.show": "Visa", "notifications.column_settings.follow": "Nya följare:", "notifications.column_settings.mention": "Omnämningar:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "Omröstningsresultat:", "notifications.column_settings.push": "Push meddelanden", "notifications.column_settings.reblog": "Knuffar:", "notifications.column_settings.show": "Visa i kolumnen", "notifications.column_settings.sound": "Spela upp ljud", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Boosts", + "notifications.filter.all": "Alla", + "notifications.filter.boosts": "Knuffar", "notifications.filter.favourites": "Favoriter", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", + "notifications.filter.follows": "Följer", + "notifications.filter.mentions": "Omnämningar", + "notifications.filter.polls": "Omröstningsresultat", "notifications.group": "{count} aviseringar", - "poll.closed": "Closed", + "poll.closed": "Stängd", "poll.refresh": "Ladda om", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.total_people": "{persons, plural, one {# person} other {# personer}}", + "poll.total_votes": "{count, plural, one {1 röst} other {# röster}}", "poll.vote": "Rösta", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "poll.voted": "Du röstade för detta svar", + "poll_button.add_poll": "Lägg till en omröstning", + "poll_button.remove_poll": "Ta bort omröstning", "privacy.change": "Justera sekretess", "privacy.direct.long": "Skicka endast till nämnda användare", "privacy.direct.short": "Direkt", @@ -295,13 +319,14 @@ "privacy.public.short": "Publik", "privacy.unlisted.long": "Skicka inte till publik tidslinje", "privacy.unlisted.short": "Olistad", + "refresh": "Läs om", "regeneration_indicator.label": "Laddar…", "regeneration_indicator.sublabel": "Ditt hemmaflöde förbereds!", "relative_time.days": "{number}d", - "relative_time.hours": "{number}h", + "relative_time.hours": "{number}tim", "relative_time.just_now": "nu", - "relative_time.minutes": "{number}m", - "relative_time.seconds": "{number}s", + "relative_time.minutes": "{number}min", + "relative_time.seconds": "{number}sek", "reply_indicator.cancel": "Ã…ngra", "report.forward": "Vidarebefordra till {target}", "report.forward_hint": "Kontot är frÃ¥n en annan server. Skicka även en anonymiserad kopia av anmälan dit?", @@ -317,21 +342,22 @@ "search_popout.tips.text": "Enkel text returnerar matchande visningsnamn, användarnamn och hashtags", "search_popout.tips.user": "användare", "search_results.accounts": "Människor", - "search_results.hashtags": "Hashtags", + "search_results.hashtags": "Hashtaggar", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Att söka toots med deras innehÃ¥ll är inte möjligt pÃ¥ denna Mastodon-server.", "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this status in the moderation interface", - "status.block": "Block @{name}", + "status.admin_account": "Öppet modereringsgränssnitt för @{name}", + "status.admin_status": "Öppna denna status i modereringsgränssnittet", + "status.block": "Blockera @{name}", "status.cancel_reblog_private": "Ta bort knuff", "status.cannot_reblog": "Detta inlägg kan inte knuffas", - "status.copy": "Copy link to status", + "status.copy": "Kopiera länk till status", "status.delete": "Ta bort", - "status.detailed_status": "Detailed conversation view", + "status.detailed_status": "Detaljerad samtalsvy", "status.direct": "Direktmeddela @{name}", "status.embed": "Bädda in", "status.favourite": "Favorit", - "status.filtered": "Filtered", + "status.filtered": "Filtrerat", "status.load_more": "Ladda fler", "status.local_only": "This post is only visible by other users of your instance", "status.media_hidden": "Media dold", @@ -343,7 +369,7 @@ "status.pin": "Fäst i profil", "status.pinned": "Fäst toot", "status.read_more": "Läs mer", - "status.reblog": "Knuff", + "status.reblog": "Knuffa", "status.reblog_private": "Knuffa till de ursprungliga Ã¥hörarna", "status.reblogged_by": "{name} knuffade", "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", @@ -358,6 +384,7 @@ "status.show_more": "Visa mer", "status.show_more_all": "Visa mer för alla", "status.show_thread": "Visa trÃ¥d", + "status.uncached_media_warning": "Ej tillgängligt", "status.unmute_conversation": "Öppna konversation", "status.unpin": "Ã…ngra fäst i profil", "suggestions.dismiss": "Dismiss suggestion", @@ -367,20 +394,28 @@ "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Meddelanden", "tabs_bar.search": "Sök", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.days": "{number, plural, one {# dag} other {# dagar}} kvar", + "time_remaining.hours": "{hours, plural, one {# timme} other {# timmar}} kvar", + "time_remaining.minutes": "{minutes, plural, one {1 minut} other {# minuter}} kvar", "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", + "time_remaining.seconds": "{hours, plural, one {# sekund} other {# sekunder}} kvar", "trends.count_by_accounts": "{count} {rawCount, plural, en {person} andra {people}} pratar", + "trends.trending_now": "Trending now", "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.", "upload_area.title": "Dra & släpp för att ladda upp", "upload_button.label": "Lägg till media", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "Beskriv för synskadade", - "upload_form.focus": "Beskär", + "upload_form.edit": "Redigera", "upload_form.undo": "Ta bort", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Verkställ", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "Laddar upp...", "video.close": "Stäng video", "video.exit_fullscreen": "Stäng helskärm", diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json index f0e6ff2b9574ef951ed65901ebbb4771ec56978a..957cf024c53fe3aa588ac186e88df6a5f01b9913 100644 --- a/app/javascript/mastodon/locales/ta.json +++ b/app/javascript/mastodon/locales/ta.json @@ -4,6 +4,7 @@ "account.block": "Block @{name}", "account.block_domain": "எலà¯à®²à®¾à®µà®±à¯à®±à¯ˆà®¯à¯à®®à¯ மறைகà¯à®• {domain}", "account.blocked": "தடைமà¯à®Ÿà¯à®Ÿà¯à®•ளà¯", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "நேரடி செயà¯à®¤à®¿ @{name}", "account.domain_blocked": "டொமைன௠மறைகà¯à®•பà¯à®ªà®Ÿà¯à®Ÿà®¤à¯", "account.edit_profile": "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à®µà¯à®®à¯", @@ -15,6 +16,7 @@ "account.follows.empty": "இநà¯à®¤ பயனர௠இதà¯à®µà®°à¯ˆ யாரையà¯à®®à¯ பினà¯à®¤à¯Šà®Ÿà®°à®µà®¿à®²à¯à®²à¯ˆ.", "account.follows_you": "நீ பின௠தொடரà¯à®•ிறாயà¯", "account.hide_reblogs": "இரà¯à®¨à¯à®¤à¯ ஊகà¯à®•ியாக மறை @{name}", + "account.last_status": "Last active", "account.link_verified_on": "இநà¯à®¤ இணைபà¯à®ªà¯ˆ உரிமையாளர௠சரிபாரà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ {date}", "account.locked_info": "இநà¯à®¤à®•௠கணகà¯à®•௠தனியà¯à®°à®¿à®®à¯ˆ நிலை பூடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯. அவரà¯à®•ளைப௠பினà¯à®¤à¯Šà®Ÿà®°à¯à®ªà®µà®°à¯ யார௠எனà¯à®ªà®¤à¯ˆ உரிமையாளர௠கைமà¯à®±à¯ˆà®¯à®¾à®• மதிபà¯à®ªà®¾à®¯à¯à®µà¯ செயà¯à®•ிறாரà¯.", "account.media": "Media", @@ -23,6 +25,7 @@ "account.mute": "ஊமையான @{name}", "account.mute_notifications": "அறிவிபà¯à®ªà¯à®•ளை à®®à¯à®Ÿà®•à¯à®•௠@{name}", "account.muted": "à®®à¯à®Ÿà®•à¯à®•ியதà¯", + "account.never_active": "Never", "account.posts": "Toots", "account.posts_with_replies": "Toots மறà¯à®±à¯à®®à¯ பதிலà¯à®•ளà¯", "account.report": "Report @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "பினà¯à®¤à¯†à®¾à®Ÿà®°à®¾à®Ÿà¯", "account.unmute": "தடà¯à®ªà¯à®ªà¯à®¨à¯€à®•à¯à®•௠@{name}", "account.unmute_notifications": "அறிவிபà¯à®ªà¯à®•ளை அகறà¯à®±à®µà¯à®®à¯ @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "எதிர௠பாராத பிழை à®à®±à¯à®ªà®Ÿà¯à®Ÿà¯ விடà¯à®Ÿà®¤à¯.", "alert.unexpected.title": "அசà¯à®šà®šà¯à®šà¯‡à®¾!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "நீஙà¯à®•ள௠அழà¯à®¤à¯à®¤à®µà¯à®®à¯ {combo} அடà¯à®¤à¯à®¤ à®®à¯à®±à¯ˆ தவிரà¯à®•à¯à®•வà¯à®®à¯", "bundle_column_error.body": "இநà¯à®¤ கூறà¯à®•ளை à®à®±à¯à®±à¯à®®à¯à®ªà¯‹à®¤à¯ à®à®¤à¯‹ தவற௠à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¤à¯.", "bundle_column_error.retry": "மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿ செயà¯", @@ -47,6 +53,7 @@ "column.blocks": "தடà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®Ÿ பயனரà¯à®•ளà¯", "column.community": "உளà¯à®³à¯‚ர௠காலகà¯à®•ெடà¯", "column.direct": "நேரடி செயà¯à®¤à®¿à®•ளà¯", + "column.directory": "Browse profiles", "column.domain_blocks": "மறைநà¯à®¤ களஙà¯à®•ளà¯", "column.favourites": "விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯à®•à¯à®•à¯à®•நà¯à®¤", "column.follow_requests": "கோரிகà¯à®•ைகளை பினà¯à®ªà®±à¯à®±à®µà¯à®®à¯", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "இநà¯à®¤ படà¯à®Ÿà®¿à®¯à®²à®¿à®²à¯ நிரநà¯à®¤à®°à®®à®¾à®• நீகà¯à®• விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா?", "confirmations.domain_block.confirm": "à®®à¯à®´à¯ டொமைனை மறை", "confirmations.domain_block.message": "நீஙà¯à®•ள௠உணà¯à®®à¯ˆà®¯à®¿à®²à¯, நிசà¯à®šà®¯à®®à®¾à®• நீஙà¯à®•ள௠மà¯à®´à¯ தடà¯à®•à¯à®• வேணà¯à®Ÿà¯à®®à¯ நிசà¯à®šà®¯à®®à®¾à®• {domain}? பெரà¯à®®à¯à®ªà®¾à®²à®¾à®© சநà¯à®¤à®°à¯à®ªà¯à®ªà®™à¯à®•ளில௠ஒர௠சில இலகà¯à®•à¯à®•ள௠அலà¯à®²à®¤à¯ மியூடà¯à®•ள௠போதà¯à®®à®¾à®©à®µà¯ˆ மறà¯à®±à¯à®®à¯ சிறநà¯à®¤à®µà¯ˆ. எநà¯à®¤ பொத௠நேரதà¯à®¤à®¿à®²à¯à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•ள௠அறிவிபà¯à®ªà¯à®•ளிலà¯à®®à¯ அநà¯à®¤à®•௠களதà¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ உளà¯à®³à®Ÿà®•à¯à®•தà¯à®¤à¯ˆ நீஙà¯à®•ள௠பாரà¯à®•à¯à®• மாடà¯à®Ÿà¯€à®°à¯à®•ளà¯. அநà¯à®¤ களதà¯à®¤à®¿à®²à¯ இரà¯à®¨à¯à®¤à¯ உஙà¯à®•ள௠ஆதரவாளரà¯à®•ள௠அகறà¯à®±à®ªà¯à®ªà®Ÿà¯à®µà®¾à®°à¯à®•ளà¯.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "ஊமையான", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "நிசà¯à®šà®¯à®®à®¾à®• நீஙà¯à®•ள௠மà¯à®Ÿà®•à¯à®• விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா {name}?", "confirmations.redraft.confirm": "நீகà¯à®•௠& redraft", "confirmations.redraft.message": "நிசà¯à®šà®¯à®®à®¾à®• இநà¯à®¤ நிலையை நீகà¯à®•ி, அதை மறà¯à®ªà®Ÿà®¿à®¯à¯à®®à¯ உரà¯à®µà®¾à®•à¯à®• வேணà¯à®Ÿà¯à®®à®¾? பிடிதà¯à®¤à®µà¯ˆ மறà¯à®±à¯à®®à¯ ஊகà¯à®•à®™à¯à®•ள௠இழகà¯à®•பà¯à®ªà®Ÿà¯à®®à¯, மறà¯à®±à¯à®®à¯ அசல௠இடà¯à®•ையில௠பதிலà¯à®•ள௠அனாதையான இரà¯à®•à¯à®•à¯à®®à¯.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "இபà¯à®ªà¯‹à®¤à¯ பதிலà¯, தறà¯à®ªà¯‹à®¤à¯ நீஙà¯à®•ள௠உரà¯à®µà®¾à®•à¯à®•à¯à®®à¯ செயà¯à®¤à®¿ மேலெழà¯à®¤à®ªà¯à®ªà®Ÿà¯à®®à¯. நீஙà¯à®•ள௠தொடர விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா?", "confirmations.unfollow.confirm": "பினà¯à®¤à¯†à®¾à®Ÿà®°à®¾à®Ÿà¯", "confirmations.unfollow.message": "நிசà¯à®šà®¯à®®à®¾à®• நீஙà¯à®•ள௠பினà¯à®¤à¯Šà®Ÿà®° விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா {name}?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "கீழே உளà¯à®³ கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà¯ˆ நகலெடà¯à®ªà¯à®ªà®¤à®©à¯ மூலம௠உஙà¯à®•ள௠இணையதளதà¯à®¤à®¿à®²à¯ இநà¯à®¤ நிலையை உடà¯à®ªà¯Šà®¤à®¿à®•à¯à®•வà¯à®®à¯.", "embed.preview": "இத௠போனà¯à®± தோறà¯à®±à®¤à¯à®¤à¯ˆ இஙà¯à®•௠காணலாமà¯:", "emoji_button.activity": "நடவடிகà¯à®•ை", @@ -134,6 +152,10 @@ "empty_column.mutes": "நீஙà¯à®•ள௠இதà¯à®µà®°à¯ˆ எநà¯à®¤ பயனரà¯à®•ளையà¯à®®à¯ à®®à¯à®Ÿà®•à¯à®•ியிரà¯à®•à¯à®•விலà¯à®²à¯ˆ.", "empty_column.notifications": "உஙà¯à®•ளிடம௠எநà¯à®¤ அறிவிபà¯à®ªà¯à®•ளà¯à®®à¯ இலà¯à®²à¯ˆ. உரையாடலைத௠தொடஙà¯à®• பிறரà¯à®Ÿà®©à¯ தொடரà¯à®ªà¯à®•ொளà¯à®³à®µà¯à®®à¯.", "empty_column.public": "இஙà¯à®•ே எதà¯à®µà¯à®®à¯ இலà¯à®²à¯ˆ! பகிரஙà¯à®•மாக ஒனà¯à®±à¯ˆ எழà¯à®¤à®µà¯à®®à¯ அலà¯à®²à®¤à¯ மறà¯à®± நிகழà¯à®µà¯à®•ளிலிரà¯à®¨à¯à®¤à¯ பயனரà¯à®•ளை அதை நிரபà¯à®ªà¯à®µà®¤à®±à¯à®•௠கைமà¯à®±à¯ˆà®¯à®¾à®• பினà¯à®ªà®±à¯à®±à®µà¯à®®à¯", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "பà¯à®¤à®¿à®¯ படà¯à®Ÿà®¿à®¯à®²à¯ தலைபà¯à®ªà¯", "lists.search": "நீஙà¯à®•ள௠பினà¯à®¤à¯†à®¾à®Ÿà®°à¯à®®à¯ நபரà¯à®•ள௠மதà¯à®¤à®¿à®¯à®¿à®²à¯ தேடà¯à®¤à®²à¯", "lists.subheading": "உஙà¯à®•ள௠படà¯à®Ÿà®¿à®¯à®²à¯à®•ளà¯", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "à®à®±à¯à®±à¯à®¤à®²à¯...", "media_gallery.toggle_visible": "நிலைமாறà¯à®±à¯ தெரியà¯à®®à¯", "missing_indicator.label": "கிடைகà¯à®•விலà¯à®²à¯ˆ", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "பொரà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà®© toots", "navigation_bar.preferences": "விரà¯à®ªà¯à®ªà®™à¯à®•ளà¯", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "கூடà¯à®Ÿà®¾à®Ÿà¯à®šà®¿ காலகà¯à®•ெடà¯", "navigation_bar.security": "பதà¯à®¤à®¿à®°à®®à¯", "notification.favourite": "{name} ஆரà¯à®µà®®à¯ கொணà¯à®Ÿà®µà®°à¯, உஙà¯à®•ள௠நிலை", @@ -282,8 +304,10 @@ "notifications.group": "{count} notifications", "poll.closed": "மூடிய", "poll.refresh": "பதà¯à®¤à¯à®¯à®¿à®°à¯à®ªà¯à®ª?டà¯à®Ÿà¯", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} மறà¯à®± {# votes}}", "poll.vote": "வாகà¯à®•ளி", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "வாகà¯à®•ெடà¯à®ªà¯à®ªà¯ˆà®šà¯ சேரà¯à®•à¯à®•வà¯à®®à¯", "poll_button.remove_poll": "வாகà¯à®•ெடà¯à®ªà¯à®ªà¯ˆ அகறà¯à®±à¯", "privacy.change": "நிலை தனியà¯à®°à®¿à®®à¯ˆ", @@ -295,6 +319,7 @@ "privacy.public.short": "Public", "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "படà¯à®Ÿà®¿à®¯à®²à®¿à®Ÿà®ªà¯à®ªà®Ÿà®¾à®¤", + "refresh": "Refresh", "regeneration_indicator.label": "சà¯à®®à¯ˆà®¯à¯‡à®±à¯à®±à®®à¯â€¦", "regeneration_indicator.sublabel": "உஙà¯à®•ள௠வீடà¯à®Ÿà¯ ஊடà¯à®Ÿà®®à¯ தயார௠செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®•ிறதà¯!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "People", "search_results.hashtags": "ஹாஷà¯à®Ÿà¯‡à®•à¯à®•à¯à®•ளைசà¯", "search_results.statuses": "Toots", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} மறà¯à®± {results}}", "status.admin_account": "மிதமான இடைமà¯à®•தà¯à®¤à¯ˆ திறகà¯à®• @{name}", "status.admin_status": "மிதமான இடைமà¯à®•தà¯à®¤à®¿à®²à¯ இநà¯à®¤ நிலையை திறகà¯à®•வà¯à®®à¯", @@ -358,6 +384,7 @@ "status.show_more": "மேலà¯à®®à¯ காடà¯à®Ÿ", "status.show_more_all": "அனைவரà¯à®•à¯à®•à¯à®®à¯ மேலà¯à®®à¯ காடà¯à®Ÿà¯", "status.show_thread": "நூல௠காடà¯à®Ÿà¯", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "ஊமையாக உரையாடல௠இலà¯à®²à¯ˆ", "status.unpin": "சà¯à®¯à®µà®¿à®µà®°à®¤à¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ நீகà¯à®•வà¯à®®à¯", "suggestions.dismiss": "பரிநà¯à®¤à¯à®°à¯ˆ விலகà¯à®•", @@ -373,14 +400,22 @@ "time_remaining.moments": "தரà¯à®£à®™à¯à®•ள௠மீதமà¯à®³à¯à®³à®©", "time_remaining.seconds": "{number, plural, one {# second} மறà¯à®± {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} மறà¯à®± {people}} உரையாடà¯", + "trends.trending_now": "Trending now", "ui.beforeunload": "நீஙà¯à®•ள௠வெளியே செனà¯à®±à®¾à®²à¯ உஙà¯à®•ள௠வரைவ௠இழகà¯à®•பà¯à®ªà®Ÿà¯à®®à¯ மஸà¯à®¤à¯‹à®Ÿà¯‹à®©à¯.", "upload_area.title": "பதிவேறà¯à®± & இழà¯à®•à¯à®•வà¯à®®à¯", "upload_button.label": "மீடியாவைச௠சேரà¯à®•à¯à®•வà¯à®®à¯ (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "கோபà¯à®ªà¯ பதிவேறà¯à®± வரமà¯à®ªà¯ மீறபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.", "upload_error.poll": "கோபà¯à®ªà¯ பதிவேறà¯à®±à®®à¯ அனà¯à®®à®¤à®¿à®•à¯à®•பà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.", "upload_form.description": "பாரà¯à®µà¯ˆà®¯à®±à¯à®± விவரிகà¯à®•வà¯à®®à¯", - "upload_form.focus": "மாறà¯à®±à®®à¯ à®®à¯à®©à¯à®©à¯‹à®Ÿà¯à®Ÿà®®à¯", + "upload_form.edit": "Edit", "upload_form.undo": "Delete", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "à®à®±à¯à®±à¯à®•ிறத௠...", "video.close": "வீடியோவை மூடà¯", "video.exit_fullscreen": "à®®à¯à®´à¯ திரையில௠இரà¯à®¨à¯à®¤à¯ வெளியேறவà¯à®®à¯", diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json index 9fb454fbae58862fd7f6eb81405be0afd3d0ae3c..6e2a3acfd49d9b6aa15f339e1ce8bee1bb94f1dc 100644 --- a/app/javascript/mastodon/locales/te.json +++ b/app/javascript/mastodon/locales/te.json @@ -4,6 +4,7 @@ "account.block": "@{name} నౠబà±à°²à°¾à°•ౠచేయి", "account.block_domain": "{domain} à°¨à±à°‚à°šà°¿ à°…à°¨à±à°¨à±€ దాచిపెటà±à°Ÿà±", "account.blocked": "à°¬à±à°²à°¾à°•ౠఅయినవి", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "@{name}కౠనేరà±à°—à°¾ సందేశం పంపà±", "account.domain_blocked": "డొమైనౠదాచిపెటà±à°Ÿà°¬à°¡à°¿à°¨à°¦à°¿", "account.edit_profile": "à°ªà±à°°à±Šà°«à±ˆà°²à±à°¨à°¿ సవరించండి", @@ -15,6 +16,7 @@ "account.follows.empty": "à°ˆ వినియోగదారి ఇంకా ఎవరినీ à°…à°¨à±à°¸à°°à°¿à°‚చడంలేదà±.", "account.follows_you": "మిమà±à°®à°²à±à°¨à°¿ à°…à°¨à±à°¸à°°à°¿à°¸à±à°¤à±à°¨à±à°¨à°¾à°°à±", "account.hide_reblogs": "@{name} à°¨à±à°‚à°šà°¿ బూసà±à°Ÿà± లనౠదాచిపెటà±à°Ÿà±", + "account.last_status": "Last active", "account.link_verified_on": "à°ˆ లంకె యొకà±à°• యాజమానà±à°¯à°‚ {date}à°¨ పరీకà±à°·à°¿à°‚చబడింది", "account.locked_info": "à°ˆ ఖాతా యొకà±à°• గోపà±à°¯à°¤ à°¸à±à°¥à°¿à°¤à°¿ లాకౠచేయబడి à°µà±à°‚ది. à°ˆ ఖాతానౠఎవరౠఅనà±à°¸à°°à°¿à°‚చవచà±à°šà±‹ యజమానే నిరà±à°£à°¯à°‚ తీసà±à°•à±à°‚టారà±.", "account.media": "మీడియా", @@ -23,6 +25,7 @@ "account.mute": "@{name}నౠమà±à°¯à±‚టౠచెయà±à°¯à°¿", "account.mute_notifications": "@{name}à°¨à±à°‚à°šà°¿ à°ªà±à°°à°•టనలనౠమà±à°¯à±‚టౠచెయà±à°¯à°¿", "account.muted": "à°®à±à°¯à±‚టౠఅయినవి", + "account.never_active": "Never", "account.posts": "టూటà±à°²à±", "account.posts_with_replies": "టూటà±à°²à± మరియౠపà±à°°à°¤à±à°¯à±à°¤à±à°¤à°°à°®à±à°²à±", "account.report": "@{name}పై à°«à°¿à°°à±à°¯à°¾à°¦à±à°šà±‡à°¯à±", @@ -35,8 +38,11 @@ "account.unfollow": "à°…à°¨à±à°¸à°°à°¿à°‚చవదà±à°¦à±", "account.unmute": "@{name}పై à°®à±à°¯à±‚టౠని తొలగించà±", "account.unmute_notifications": "@{name} à°¨à±à°‚à°šà°¿ à°ªà±à°°à°•టనలపై à°®à±à°¯à±‚టౠని తొలగించà±", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "à°…à°¨à±à°•ోని తపà±à°ªà± జరిగినది.", "alert.unexpected.title": "à°…à°¯à±à°¯à±‹!", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "మీరౠతదà±à°ªà°°à°¿à°¸à°¾à°°à°¿ దీనిని దాటవేయడానికి {combo} నొకà±à°•వచà±à°šà±", "bundle_column_error.body": "à°ˆ à°à°¾à°—à°‚ లోడౠఅవà±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± à°à°¦à±‹ తపà±à°ªà± జరిగింది.", "bundle_column_error.retry": "మళà±à°³à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿", @@ -47,6 +53,7 @@ "column.blocks": "à°¬à±à°²à°¾à°•ౠచేయబడిన వినియోగదారà±à°²à±", "column.community": "à°¸à±à°¥à°¾à°¨à°¿à°• కాలకà±à°°à°®à°‚", "column.direct": "à°ªà±à°°à°¤à±à°¯à°•à±à°· సందేశాలà±", + "column.directory": "Browse profiles", "column.domain_blocks": "దాచిన డొమైనà±à°²à±", "column.favourites": "ఇషà±à°Ÿà°ªà°¡à°¿à°¨à°µà°¿", "column.follow_requests": "à°…à°¨à±à°¸à°°à°¿à°‚చడానికి à°…à°à±à°¯à°°à±à°§à°¨à°²à±", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "మీరౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ à°ˆ జాబితానౠశాశà±à°µà°¤à°‚à°—à°¾ తొలగించాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?", "confirmations.domain_block.confirm": "మొతà±à°¤à°‚ డొమైనà±à°¨à± దాచà±", "confirmations.domain_block.message": "మీరౠనిజంగా నిజంగా మొతà±à°¤à°‚ {domain} ని à°¬à±à°²à°¾à°•ౠచేయాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾? చాలా సందరà±à°à°¾à°²à°²à±‹ కొనà±à°¨à°¿ లకà±à°·à±à°¯à°‚à°—à°¾ ఉనà±à°¨ à°¬à±à°²à°¾à°•à±à°¸à± లేదా à°®à±à°¯à±‚à°Ÿà±à°¸à± సరిపోతాయి మరియౠఉతà±à°¤à°®à°®à±ˆà°¨à°µà°¿. మీరౠఆ డొమైనౠనà±à°‚à°¡à°¿ కంటెంటà±à°¨à± à° à°ªà±à°°à°œà°¾ కాలకà±à°°à°®à°¾à°²à°²à±‹ లేదా మీ నోటిఫికేషనà±à°²à°²à±‹ చూడలేరà±. à°† డొమైనౠనà±à°‚à°¡à°¿ మీ à°…à°¨à±à°šà°°à±à°²à± తీసివేయబడతారà±.", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "à°®à±à°¯à±‚టౠచేయి", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "{name}నౠమీరౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ à°®à±à°¯à±‚టౠచేయాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?", "confirmations.redraft.confirm": "తొలగించౠ& తిరగరాయà±", "confirmations.redraft.message": "మీరౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ à°ˆ à°¸à±à°Ÿà±‡à°Ÿà°¸à± ని తొలగించి తిరగరాయాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾? à°ˆ à°¸à±à°Ÿà±‡à°Ÿà°¸à± యొకà±à°• బూసà±à°Ÿà± లౠమరియౠఇషà±à°Ÿà°¾à°²à± పోతాయి,మరియౠపà±à°°à°¤à±à°¯à±à°¤à±à°¤à°°à°¾à°²à± అనాధలౠఅయిపోతాయి.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "ఇపà±à°ªà±à°¡à±‡ à°ªà±à°°à°¤à±à°¯à±à°¤à±à°¤à°°à°‚ ఇసà±à°¤à±‡ మీరౠపà±à°°à°¸à±à°¤à±à°¤à°‚ à°µà±à°°à°¾à°¸à±à°¤à±à°¨à±à°¨ సందేశం తిరగరాయబడà±à°¤à±à°‚ది. మీరౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ కొనసాగించాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?", "confirmations.unfollow.confirm": "à°…à°¨à±à°¸à°°à°¿à°‚చవదà±à°¦à±", "confirmations.unfollow.message": "{name}నౠమీరౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ à°…à°¨à±à°¸à°°à°¿à°‚చవదà±à°¦à°¨à±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "దిగà±à°µ కోడà±à°¨à± కాపీ చేయడం à°¦à±à°µà°¾à°°à°¾ మీ వెబà±à°¸à±ˆà°Ÿà±à°²à±‹ à°ˆ à°¸à±à°Ÿà±‡à°Ÿà°¸à± ని పొందà±à°ªà°°à°šà°‚à°¡à°¿.", "embed.preview": "అది à°ˆ à°•à±à°°à°¿à°‚ది విధంగా కనిపిసà±à°¤à±à°‚ది:", "emoji_button.activity": "కారà±à°¯à°•లాపాలà±", @@ -134,6 +152,10 @@ "empty_column.mutes": "మీరౠఇంకా ఠవినియోగదారà±à°²à°¨à±‚ à°®à±à°¯à±‚టౠచేయలేదà±.", "empty_column.notifications": "మీకౠఇంకా ఠనోటిఫికేషనà±à°²à± లేవà±. సంà°à°¾à°·à°£à°¨à± à°ªà±à°°à°¾à°°à°‚à°à°¿à°‚చడానికి ఇతరà±à°²à°¤à±‹ à°ªà±à°°à°¤à°¿à°¸à±à°ªà°‚దించండి.", "empty_column.public": "ఇకà±à°•à°¡ à°à°®à±€ లేదà±! దీనà±à°¨à°¿ నింపడానికి బహిరంగంగా à°à°¦à±ˆà°¨à°¾ à°µà±à°°à°¾à°¯à°‚à°¡à°¿, లేదా ఇతర సేవికల à°¨à±à°‚à°¡à°¿ వినియోగదారà±à°²à°¨à± à°…à°¨à±à°¸à°°à°¿à°‚à°šà°‚à°¡à°¿", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "కొతà±à°¤ జాబితా శీరà±à°·à°¿à°•", "lists.search": "మీరౠఅనà±à°¸à°°à°¿à°‚చే à°µà±à°¯à°•à±à°¤à±à°²à°²à±‹ శోధించండి", "lists.subheading": "మీ జాబితాలà±", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "లోడౠఅవà±à°¤à±‹à°‚ది...", "media_gallery.toggle_visible": "దృశà±à°¯à°®à°¾à°¨à°¤à°¨à± టోగà±à°²à± చేయండి", "missing_indicator.label": "దొరకలేదà±", @@ -251,7 +274,6 @@ "navigation_bar.personal": "à°µà±à°¯à°•à±à°¤à°¿à°—తం", "navigation_bar.pins": "అతికించిన టూటà±à°²à±", "navigation_bar.preferences": "à°ªà±à°°à°¾à°§à°¾à°¨à±à°¯à°¤à°²à±", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "సమాఖà±à°¯ కాలకà±à°°à°®à°‚", "navigation_bar.security": "à°à°¦à±à°°à°¤", "notification.favourite": "{name} మీ à°¸à±à°Ÿà±‡à°Ÿà°¸à± నౠఇషà±à°Ÿà°ªà°¡à±à°¡à°¾à°°à±", @@ -282,8 +304,10 @@ "notifications.group": "{count} à°ªà±à°°à°•టనలà±", "poll.closed": "మూసివేయబడినవి", "poll.refresh": "నవీకరించà±", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "à°Žà°¨à±à°¨à±à°•ోండి", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "à°’à°• à°Žà°¨à±à°¨à°¿à°•నౠచేరà±à°šà±", "poll_button.remove_poll": "à°Žà°¨à±à°¨à°¿à°•నౠతొలగించà±", "privacy.change": "à°¸à±à°Ÿà±‡à°Ÿà°¸à± గోపà±à°¯à°¤à°¨à± సరà±à°¦à±à°¬à°¾à°Ÿà± చేయండి", @@ -295,6 +319,7 @@ "privacy.public.short": "à°ªà±à°°à°œà°¾", "privacy.unlisted.long": "à°ªà±à°°à°œà°¾ కాలకà±à°°à°®à°¾à°²à°²à±‹ చూపించవదà±à°¦à±", "privacy.unlisted.short": "జాబితా చేయబడనిది", + "refresh": "Refresh", "regeneration_indicator.label": "లోడౠఅవà±à°¤à±‹à°‚ది…", "regeneration_indicator.sublabel": "మీ హోమౠఫీడౠసిదà±à°§à°®à°µà±à°¤à±‹à°‚ది!", "relative_time.days": "{number}d", @@ -319,6 +344,7 @@ "search_results.accounts": "à°µà±à°¯à°•à±à°¤à±à°²à±", "search_results.hashtags": "హాషౠటà±à°¯à°¾à°—à±à°²à±", "search_results.statuses": "టూటà±à°²à±", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.admin_account": "@{name} కొరకౠసమనà±à°µà°¯ వినిమయసీమనౠతెరà±à°µà±", "status.admin_status": "సమనà±à°µà°¯ వినిమయసీమలో à°ˆ à°¸à±à°Ÿà±‡à°Ÿà°¸à± నౠతెరవండి", @@ -358,6 +384,7 @@ "status.show_more": "ఇంకా చూపించà±", "status.show_more_all": "à°…à°¨à±à°¨à°¿à°Ÿà°¿à°•à±€ ఇంకా చూపించà±", "status.show_thread": "గొలà±à°¸à±à°¨à± చూపించà±", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "సంà°à°¾à°·à°£à°¨à± à°…à°¨à±à°®à±à°¯à±‚టౠచేయి", "status.unpin": "à°ªà±à°°à±Šà°«à±ˆà°²à± à°¨à±à°‚à°¡à°¿ పీకివేయà±", "suggestions.dismiss": "సూచననౠరదà±à°¦à± చేయి", @@ -373,14 +400,22 @@ "time_remaining.moments": "కొనà±à°¨à°¿ à°•à±à°·à°£à°¾à°²à± మాతà±à°°à°®à±‡ మిగిలి ఉనà±à°¨à°¾à°¯à°¿", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} మాటà±à°²à°¾à°¡à±à°¤à±à°¨à±à°¨à°¾à°°à±", + "trends.trending_now": "Trending now", "ui.beforeunload": "మీరౠమాసà±à°Ÿà±Šà°¡à±Šà°¨à±à°¨à± వదిలివేసà±à°¤à±‡ మీ à°¡à±à°°à°¾à°«à±à°Ÿà±à°²à± పోతాయి.", "upload_area.title": "à°…à°ªà±à°²à±‹à°¡à± చేయడానికి à°¡à±à°°à°¾à°—à± & à°¡à±à°°à°¾à°ªà± చేయండి", "upload_button.label": "మీడియానౠజోడించండి (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "దృషà±à°Ÿà°¿ లోపమà±à°¨à±à°¨ వారి కోసం వివరించండి", - "upload_form.focus": "à°ªà±à°°à°¿à°µà±à°¯à±‚నౠమారà±à°šà±", + "upload_form.edit": "Edit", "upload_form.undo": "తొలగించà±", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "à°…à°ªà±à°²à±‹à°¡à± à°…à°µà±à°¤à±‹à°‚ది...", "video.close": "వీడియోని మూసివేయి", "video.exit_fullscreen": "పూరà±à°¤à°¿ à°¸à±à°•à±à°°à±€à°¨à± à°¨à±à°‚à°¡à°¿ నిషà±à°•à±à°°à°®à°¿à°‚à°šà±", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index a854eecab1e2b43989f2cdb57bb4aff1c53df14d..352e92092290a44100291ad59d6252cdb0ca088d 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -4,6 +4,7 @@ "account.block": "ปิดà¸à¸±à¹‰à¸™ @{name}", "account.block_domain": "ซ่à¸à¸™à¸—ุà¸à¸à¸¢à¹ˆà¸²à¸‡à¸ˆà¸²à¸ {domain}", "account.blocked": "ปิดà¸à¸±à¹‰à¸™à¸à¸¢à¸¹à¹ˆ", + "account.cancel_follow_request": "ยà¸à¹€à¸¥à¸´à¸à¸„ำขà¸à¸•ิดตาม", "account.direct": "ส่งข้à¸à¸„วามโดยตรงถึง @{name}", "account.domain_blocked": "ซ่à¸à¸™à¹‚ดเมนà¸à¸¢à¸¹à¹ˆ", "account.edit_profile": "à¹à¸à¹‰à¹„ขโปรไฟล์", @@ -15,6 +16,7 @@ "account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร", "account.follows_you": "ติดตามคุณ", "account.hide_reblogs": "ซ่à¸à¸™à¸à¸²à¸£à¸”ันจาภ@{name}", + "account.last_status": "ใช้งานล่าสุด", "account.link_verified_on": "ตรวจสà¸à¸šà¸„วามเป็นเจ้าขà¸à¸‡à¸‚à¸à¸‡à¸¥à¸´à¸‡à¸à¹Œà¸™à¸µà¹‰à¹€à¸¡à¸·à¹ˆà¸ {date}", "account.locked_info": "มีà¸à¸²à¸£à¸•ั้งสถานะความเป็นส่วนตัวขà¸à¸‡à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸¥à¹‡à¸à¸„à¸à¸¢à¸¹à¹ˆ เจ้าขà¸à¸‡à¸•รวจทานผู้ที่สามารถติดตามเขาด้วยตนเà¸à¸‡", "account.media": "สื่à¸", @@ -23,6 +25,7 @@ "account.mute": "ปิดเสียง @{name}", "account.mute_notifications": "ปิดเสียงà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸ @{name}", "account.muted": "ปิดเสียงà¸à¸¢à¸¹à¹ˆ", + "account.never_active": "ไม่เลย", "account.posts": "โพสต์", "account.posts_with_replies": "โพสต์à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š", "account.report": "รายงาน @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "เลิà¸à¸•ิดตาม", "account.unmute": "เลิà¸à¸›à¸´à¸”เสียง @{name}", "account.unmute_notifications": "เลิà¸à¸›à¸´à¸”เสียงà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸ @{name}", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "เà¸à¸´à¸”ข้à¸à¸œà¸´à¸”พลาดที่ไม่คาดคิด", "alert.unexpected.title": "à¸à¸¸à¸›à¸ªà¹Œ!", + "autosuggest_hashtag.per_week": "{count} ต่à¸à¸ªà¸±à¸›à¸”าห์", "boost_modal.combo": "คุณสามารถà¸à¸” {combo} เพื่à¸à¸‚้ามสิ่งนี้ในครั้งถัดไป", "bundle_column_error.body": "มีบางà¸à¸¢à¹ˆà¸²à¸‡à¸œà¸´à¸”พลาดขณะโหลดส่วนประà¸à¸à¸šà¸™à¸µà¹‰", "bundle_column_error.retry": "ลà¸à¸‡à¸à¸µà¸à¸„รั้ง", @@ -47,6 +53,7 @@ "column.blocks": "ผู้ใช้ที่ปิดà¸à¸±à¹‰à¸™à¸à¸¢à¸¹à¹ˆ", "column.community": "เส้นเวลาในเว็บ", "column.direct": "ข้à¸à¸„วามโดยตรง", + "column.directory": "เรียà¸à¸”ูโปรไฟล์", "column.domain_blocks": "โดเมนที่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆ", "column.favourites": "รายà¸à¸²à¸£à¹‚ปรด", "column.follow_requests": "คำขà¸à¸•ิดตาม", @@ -72,7 +79,7 @@ "compose_form.lock_disclaimer.lock": "ล็à¸à¸„à¸à¸¢à¸¹à¹ˆ", "compose_form.placeholder": "คุณà¸à¸³à¸¥à¸±à¸‡à¸„ิดà¸à¸°à¹„รà¸à¸¢à¸¹à¹ˆ?", "compose_form.poll.add_option": "เพิ่มทางเลืà¸à¸", - "compose_form.poll.duration": "Poll duration", + "compose_form.poll.duration": "ระยะเวลาโพล", "compose_form.poll.option_placeholder": "ทางเลืà¸à¸ {number}", "compose_form.poll.remove_option": "เà¸à¸²à¸—างเลืà¸à¸à¸™à¸µà¹‰à¸à¸à¸", "compose_form.publish": "โพสต์", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸£à¸²à¸¢à¸à¸²à¸£à¸™à¸µà¹‰à¸à¸¢à¹ˆà¸²à¸‡à¸–าวร?", "confirmations.domain_block.confirm": "ซ่à¸à¸™à¸—ั้งโดเมน", "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.message": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸à¸à¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š?", "confirmations.mute.confirm": "ปิดเสียง", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸›à¸´à¸”เสียง {name}?", "confirmations.redraft.confirm": "ลบà¹à¸¥à¹‰à¸§à¸£à¹ˆà¸²à¸‡à¹ƒà¸«à¸¡à¹ˆ", "confirmations.redraft.message": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸¥à¸šà¸ªà¸–านะนี้à¹à¸¥à¹‰à¸§à¸£à¹ˆà¸²à¸‡à¹ƒà¸«à¸¡à¹ˆ? รายà¸à¸²à¸£à¹‚ปรดà¹à¸¥à¸°à¸à¸²à¸£à¸”ันจะหายไป à¹à¸¥à¸°à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¹‚พสต์ดั้งเดิมจะไม่มีความเà¸à¸µà¹ˆà¸¢à¸§à¸žà¸±à¸™", @@ -101,6 +111,14 @@ "confirmations.reply.message": "à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¸•à¸à¸™à¸™à¸µà¹‰à¸ˆà¸°à¹€à¸‚ียนทับข้à¸à¸„วามที่คุณà¸à¸³à¸¥à¸±à¸‡à¹€à¸‚ียน คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸•่à¸?", "confirmations.unfollow.confirm": "เลิà¸à¸•ิดตาม", "confirmations.unfollow.message": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¹€à¸¥à¸´à¸à¸•ิดตาม {name}?", + "conversation.delete": "ลบà¸à¸²à¸£à¸ªà¸™à¸—นา", + "conversation.mark_as_read": "ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸à¹ˆà¸²à¸™à¹à¸¥à¹‰à¸§", + "conversation.open": "ดูà¸à¸²à¸£à¸ªà¸™à¸—นา", + "conversation.with": "à¸à¸±à¸š {names}", + "directory.federated": "จาà¸à¹€à¸Ÿà¸”ิเวิร์สที่รู้จัà¸", + "directory.local": "จาภ{domain} เท่านั้น", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "à¸à¸±à¸‡à¸ªà¸–านะนี้ในเว็บไซต์ขà¸à¸‡à¸„ุณโดยคัดลà¸à¸à¹‚ค้ดด้านล่าง", "embed.preview": "นี่คืà¸à¸¥à¸±à¸à¸©à¸“ะที่จะปราà¸à¸:", "emoji_button.activity": "à¸à¸´à¸ˆà¸à¸£à¸£à¸¡", @@ -134,6 +152,10 @@ "empty_column.mutes": "คุณยังไม่ได้ปิดเสียงผู้ใช้ใด ๆ", "empty_column.notifications": "คุณยังไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹ƒà¸” ๆ โต้ตà¸à¸šà¸à¸±à¸šà¸œà¸¹à¹‰à¸à¸·à¹ˆà¸™à¹€à¸žà¸·à¹ˆà¸à¹€à¸£à¸´à¹ˆà¸¡à¸à¸²à¸£à¸ªà¸™à¸—นา", "empty_column.public": "ไม่มีสิ่งใดที่นี่! เขียนบางà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸›à¹‡à¸™à¸ªà¸²à¸˜à¸²à¸£à¸“ะ หรืà¸à¸•ิดตามผู้ใช้จาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸à¸·à¹ˆà¸™ ๆ ด้วยตนเà¸à¸‡à¹€à¸žà¸·à¹ˆà¸à¹€à¸•ิมให้เต็ม", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "รายงานปัà¸à¸«à¸²", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -146,7 +168,7 @@ "getting_started.documentation": "เà¸à¸à¸ªà¸²à¸£à¸›à¸£à¸°à¸à¸à¸š", "getting_started.heading": "เริ่มต้นใช้งาน", "getting_started.invite": "เชิà¸à¸œà¸¹à¹‰à¸„น", - "getting_started.open_source_notice": "Mastodon เป็นซà¸à¸Ÿà¸•์à¹à¸§à¸£à¹Œà¹€à¸›à¸´à¸”ต้นฉบับ คุณสามารถมีส่วนร่วมหรืà¸à¸£à¸²à¸¢à¸‡à¸²à¸™à¸›à¸±à¸à¸«à¸²à¹ƒà¸™ GitHub ที่ {github}", + "getting_started.open_source_notice": "Mastodon เป็นซà¸à¸Ÿà¸•์à¹à¸§à¸£à¹Œà¹€à¸›à¸´à¸”ต้นฉบับ คุณสามารถมีส่วนร่วมหรืà¸à¸£à¸²à¸¢à¸‡à¸²à¸™à¸›à¸±à¸à¸«à¸²à¸—ี่ GitHub ที่ {github}", "getting_started.security": "ความปลà¸à¸”ภัย", "getting_started.terms": "เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£", "hashtag.column_header.tag_mode.all": "à¹à¸¥à¸° {additional}", @@ -161,12 +183,12 @@ "home.column_settings.basic": "พื้นà¸à¸²à¸™", "home.column_settings.show_reblogs": "à¹à¸ªà¸”งà¸à¸²à¸£à¸”ัน", "home.column_settings.show_replies": "à¹à¸ªà¸”งà¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸š", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.days": "{number, plural, other {# วัน}}", + "intervals.full.hours": "{number, plural, other {# ชั่วโมง}}", + "intervals.full.minutes": "{number, plural, other {# นาที}}", "introduction.federation.action": "ถัดไป", "introduction.federation.federated.headline": "ที่ติดต่à¸à¸à¸±à¸šà¸ ายนà¸à¸", - "introduction.federation.federated.text": "โพสต์สาธารณะจาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸à¸·à¹ˆà¸™ ๆ ขà¸à¸‡ Fediverse จะปราà¸à¸à¹ƒà¸™à¹€à¸ªà¹‰à¸™à¹€à¸§à¸¥à¸²à¸—ี่ติดต่à¸à¸à¸±à¸šà¸ ายนà¸à¸", + "introduction.federation.federated.text": "โพสต์สาธารณะจาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸à¸·à¹ˆà¸™ ๆ ขà¸à¸‡à¹€à¸Ÿà¸”ิเวิร์สจะปราà¸à¸à¹ƒà¸™à¹€à¸ªà¹‰à¸™à¹€à¸§à¸¥à¸²à¸—ี่ติดต่à¸à¸à¸±à¸šà¸ ายนà¸à¸", "introduction.federation.home.headline": "หน้าà¹à¸£à¸", "introduction.federation.home.text": "โพสต์จาà¸à¸œà¸¹à¹‰à¸„นที่คุณติดตามจะปราà¸à¸à¹ƒà¸™à¸Ÿà¸µà¸”หน้าà¹à¸£à¸à¸‚à¸à¸‡à¸„ุณ คุณสามารถติดตามใครà¸à¹‡à¸•ามในเซิร์ฟเวà¸à¸£à¹Œà¹ƒà¸”à¸à¹‡à¸•าม!", "introduction.federation.local.headline": "ในเว็บ", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "ชื่à¸à¹€à¸£à¸·à¹ˆà¸à¸‡à¸£à¸²à¸¢à¸à¸²à¸£à¹ƒà¸«à¸¡à¹ˆ", "lists.search": "ค้นหาในหมู่ผู้คนที่คุณติดตาม", "lists.subheading": "รายà¸à¸²à¸£à¸‚à¸à¸‡à¸„ุณ", + "load_pending": "{count, plural, other {# รายà¸à¸²à¸£à¹ƒà¸«à¸¡à¹ˆ}}", "loading_indicator.label": "à¸à¸³à¸¥à¸±à¸‡à¹‚หลด...", "media_gallery.toggle_visible": "เปิด/ปิดà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™", "missing_indicator.label": "ไม่พบ", @@ -251,13 +274,12 @@ "navigation_bar.personal": "ส่วนบุคคล", "navigation_bar.pins": "โพสต์ที่ปัà¸à¸«à¸¡à¸¸à¸”", "navigation_bar.preferences": "à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ลัà¸à¸©à¸“ะ", - "navigation_bar.profile_directory": "ไดเรà¸à¸—à¸à¸£à¸µà¹‚ปรไฟล์", "navigation_bar.public_timeline": "เส้นเวลาที่ติดต่à¸à¸à¸±à¸šà¸ ายนà¸à¸", "navigation_bar.security": "ความปลà¸à¸”ภัย", "notification.favourite": "{name} ได้ชื่นชà¸à¸šà¸ªà¸–านะขà¸à¸‡à¸„ุณ", "notification.follow": "{name} ได้ติดตามคุณ", "notification.mention": "{name} ได้à¸à¸¥à¹ˆà¸²à¸§à¸–ึงคุณ", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "โพลที่คุณได้ลงคะà¹à¸™à¸™à¹„ด้สิ้นสุดà¹à¸¥à¹‰à¸§", "notification.reblog": "{name} ได้ดันสถานะขà¸à¸‡à¸„ุณ", "notifications.clear": "ล้างà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™", "notifications.clear_confirmation": "คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸à¹„ม่ว่าต้à¸à¸‡à¸à¸²à¸£à¸¥à¹‰à¸²à¸‡à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ั้งหมดขà¸à¸‡à¸„ุณà¸à¸¢à¹ˆà¸²à¸‡à¸–าวร?", @@ -268,7 +290,7 @@ "notifications.column_settings.filter_bar.show": "à¹à¸ªà¸”ง", "notifications.column_settings.follow": "ผู้ติดตามใหม่:", "notifications.column_settings.mention": "à¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึง:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "ผลลัพธ์โพล:", "notifications.column_settings.push": "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸šà¸šà¸œà¸¥à¸±à¸", "notifications.column_settings.reblog": "à¸à¸²à¸£à¸”ัน:", "notifications.column_settings.show": "à¹à¸ªà¸”งในคà¸à¸¥à¸±à¸¡à¸™à¹Œ", @@ -278,14 +300,16 @@ "notifications.filter.favourites": "รายà¸à¸²à¸£à¹‚ปรด", "notifications.filter.follows": "à¸à¸²à¸£à¸•ิดตาม", "notifications.filter.mentions": "à¸à¸²à¸£à¸à¸¥à¹ˆà¸²à¸§à¸–ึง", - "notifications.filter.polls": "Poll results", + "notifications.filter.polls": "ผลลัพธ์โพล", "notifications.group": "{count} à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™", "poll.closed": "ปิดà¹à¸¥à¹‰à¸§", "poll.refresh": "รีเฟรช", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, other {# à¸à¸²à¸£à¸¥à¸‡à¸„ะà¹à¸™à¸™}}", + "poll.vote": "ลงคะà¹à¸™à¸™", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "เพิ่มโพล", + "poll_button.remove_poll": "เà¸à¸²à¹‚พลà¸à¸à¸", "privacy.change": "ปรับเปลี่ยนความเป็นส่วนตัวขà¸à¸‡à¸ªà¸–านะ", "privacy.direct.long": "โพสต์ไปยังผู้ใช้ที่à¸à¸¥à¹ˆà¸²à¸§à¸–ึงเท่านั้น", "privacy.direct.short": "โดยตรง", @@ -295,6 +319,7 @@ "privacy.public.short": "สาธารณะ", "privacy.unlisted.long": "ไม่โพสต์ไปยังเส้นเวลาสาธารณะ", "privacy.unlisted.short": "ไม่à¸à¸¢à¸¹à¹ˆà¹ƒà¸™à¸£à¸²à¸¢à¸à¸²à¸£", + "refresh": "รีเฟรช", "regeneration_indicator.label": "à¸à¸³à¸¥à¸±à¸‡à¹‚หลด…", "regeneration_indicator.sublabel": "à¸à¸³à¸¥à¸±à¸‡à¹€à¸•รียมฟีดหน้าà¹à¸£à¸à¸‚à¸à¸‡à¸„ุณ!", "relative_time.days": "{number} วัน", @@ -319,7 +344,8 @@ "search_results.accounts": "ผู้คน", "search_results.hashtags": "à¹à¸®à¸Šà¹à¸—็à¸", "search_results.statuses": "โพสต์", - "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", + "search_results.total": "{count, number} {count, plural, other {ผลลัพธ์}}", "status.admin_account": "เปิดส่วนติดต่à¸à¸à¸²à¸£à¸„วบคุมสำหรับ @{name}", "status.admin_status": "เปิดสถานะนี้ในส่วนติดต่à¸à¸à¸²à¸£à¸„วบคุม", "status.block": "ปิดà¸à¸±à¹‰à¸™ @{name}", @@ -358,6 +384,7 @@ "status.show_more": "à¹à¸ªà¸”งเพิ่มเติม", "status.show_more_all": "à¹à¸ªà¸”งเพิ่มเติมทั้งหมด", "status.show_thread": "à¹à¸ªà¸”งà¸à¸£à¸°à¸—ู้", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "เลิà¸à¸›à¸´à¸”เสียงà¸à¸²à¸£à¸ªà¸™à¸—นา", "status.unpin": "ถà¸à¸™à¸«à¸¡à¸¸à¸”จาà¸à¹‚ปรไฟล์", "suggestions.dismiss": "ยà¸à¹€à¸¥à¸´à¸à¸‚้à¸à¹€à¸ªà¸™à¸à¹à¸™à¸°", @@ -367,20 +394,28 @@ "tabs_bar.local_timeline": "ในเว็บ", "tabs_bar.notifications": "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™", "tabs_bar.search": "ค้นหา", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.days": "เหลืà¸à¸à¸µà¸ {number, plural, other {# วัน}}", + "time_remaining.hours": "เหลืà¸à¸à¸µà¸ {number, plural, other {# ชั่วโมง}}", + "time_remaining.minutes": "เหลืà¸à¸à¸µà¸ {number, plural, other {# นาที}}", "time_remaining.moments": "ช่วงเวลาที่เหลืà¸", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "time_remaining.seconds": "เหลืà¸à¸à¸µà¸ {number, plural, other {# วินาที}}", + "trends.count_by_accounts": "{count} {rawCount, plural, other {คน}}à¸à¸³à¸¥à¸±à¸‡à¸„ุย", + "trends.trending_now": "à¸à¸³à¸¥à¸±à¸‡à¸™à¸´à¸¢à¸¡", "ui.beforeunload": "à¹à¸šà¸šà¸£à¹ˆà¸²à¸‡à¸‚à¸à¸‡à¸„ุณจะหายไปหาà¸à¸„ุณà¸à¸à¸à¸ˆà¸²à¸ Mastodon", "upload_area.title": "ลาà¸à¹à¸¥à¹‰à¸§à¸›à¸¥à¹ˆà¸à¸¢à¹€à¸žà¸·à¹ˆà¸à¸à¸±à¸›à¹‚หลด", "upload_button.label": "เพิ่มสื่ภ(JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "เà¸à¸´à¸™à¸‚ีดจำà¸à¸±à¸”à¸à¸²à¸£à¸à¸±à¸›à¹‚หลดไฟล์", "upload_error.poll": "ไม่à¸à¸™à¸¸à¸à¸²à¸•ให้à¸à¸±à¸›à¹‚หลดไฟล์à¸à¸±à¸šà¸à¸²à¸£à¸¥à¸‡à¸„ะà¹à¸™à¸™", "upload_form.description": "à¸à¸˜à¸´à¸šà¸²à¸¢à¸ªà¸³à¸«à¸£à¸±à¸šà¸œà¸¹à¹‰à¸šà¸à¸žà¸£à¹ˆà¸à¸‡à¸—างà¸à¸²à¸£à¸¡à¸à¸‡à¹€à¸«à¹‡à¸™", - "upload_form.focus": "ตัวà¸à¸¢à¹ˆà¸²à¸‡à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡", + "upload_form.edit": "à¹à¸à¹‰à¹„ข", "upload_form.undo": "ลบ", + "upload_modal.analyzing_picture": "à¸à¸³à¸¥à¸±à¸‡à¸§à¸´à¹€à¸„ราะห์รูปภาพ…", + "upload_modal.apply": "นำไปใช้", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "à¹à¸à¹‰à¹„ขสื่à¸", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "ตัวà¸à¸¢à¹ˆà¸²à¸‡ ({ratio})", "upload_progress.label": "à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸›à¹‚หลด...", "video.close": "ปิดวิดีโà¸", "video.exit_fullscreen": "à¸à¸à¸à¸ˆà¸²à¸à¹€à¸•็มหน้าจà¸", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 471ce5d2d5af680c6dbf63a45defdd3080703fb7..4be3eaa15e36c983462ad27d6876b33ada627646 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -1,43 +1,49 @@ { - "account.add_or_remove_from_list": "Listelere ekle veya kaldır", + "account.add_or_remove_from_list": "Listeye ekle veya kaldır", "account.badges.bot": "Bot", - "account.block": "Engelle @{name}", + "account.block": "@{name} adlı kiÅŸiyi engelle", "account.block_domain": "{domain} alanından her ÅŸeyi gizle", "account.blocked": "EngellenmiÅŸ", + "account.cancel_follow_request": "Takip isteÄŸini iptal et", "account.direct": "Mesaj gönder : @{name}", "account.domain_blocked": "Alan adı gizlendi", "account.edit_profile": "Profili düzenle", "account.endorse": "Profildeki özellik", "account.follow": "Takip et", - "account.followers": "Takipçiler", + "account.followers": "Takipçi", "account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.", "account.follows": "Takip ettikleri", "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.", "account.follows_you": "Seni takip ediyor", - "account.hide_reblogs": "@{name} kiÅŸisinden boost'ları gizle", + "account.hide_reblogs": "@{name} kiÅŸisinin yinelemelerini gizle", + "account.last_status": "Son aktivite", "account.link_verified_on": "Bu baÄŸlantının mülkiyeti {date} tarihinde kontrol edildi", - "account.locked_info": "Bu hesabın gizlilik durumu kilitli olarak ayarlanmış. Sahibi, onu kimin takip edebileceÄŸini elle inceler.", + "account.locked_info": "Bu hesabın gizlilik durumu kilitli olarak ayarlanmış. Sahibi, onu kimin takip edebileceÄŸini elle inceliyor.", "account.media": "Medya", "account.mention": "@{name} kullanıcısından bahset", "account.moved_to": "{name} ÅŸuraya taşındı:", - "account.mute": "@{name} kullanıcısını sessize al", - "account.mute_notifications": "@{name} kullanıcısının bildirimlerini kapat", - "account.muted": "Sesi kısık", - "account.posts": "Gönderiler", + "account.mute": "@{name} adlı kiÅŸiyi sessize al", + "account.mute_notifications": "@{name} adlı kiÅŸinin bildirimlerini kapat", + "account.muted": "Susturuldu", + "account.never_active": "Asla", + "account.posts": "Gönderi", "account.posts_with_replies": "Gönderiler ve yanıtlar", - "account.report": "@{name} kullanıcısını bildir", - "account.requested": "Onay bekliyor. Takip isteÄŸini iptal etmek için tıklayın", + "account.report": "@{name} adlı kiÅŸiyi bildir", + "account.requested": "Onay Bekleniyor. Takip isteÄŸini iptal etmek için tıklayın", "account.share": "@{name} kullanıcısının profilini paylaÅŸ", - "account.show_reblogs": "@{name} kullanıcısından boostları göster", - "account.unblock": "Engeli kaldır @{name}", + "account.show_reblogs": "@{name} kullanıcısının yinelemelerini göster", + "account.unblock": "@{name} adlı kiÅŸinin engelini kaldır", "account.unblock_domain": "{domain} göster", "account.unendorse": "Profilde özellik yok", - "account.unfollow": "Takipten vazgeç", - "account.unmute": "Sesi aç : @{name}", - "account.unmute_notifications": "@{name} kullanıcısından bildirimleri aç", + "account.unfollow": "Takipi bırak", + "account.unmute": "@{name} adlı kiÅŸinin sesini aç", + "account.unmute_notifications": "@{name} adlı kiÅŸinin bildirimlerini aç", + "alert.rate_limited.message": "Lütfen sonra tekrar deneyin {retry_time, time, medium}.", + "alert.rate_limited.title": "Oran sınırlıdır", "alert.unexpected.message": "Beklenmedik bir hata oluÅŸtu.", "alert.unexpected.title": "Hay aksi!", - "boost_modal.combo": "Bir dahaki sefere {combo} tuÅŸuna basabilirsiniz", + "autosuggest_hashtag.per_week": "Haftada {count}", + "boost_modal.combo": "Bir daha ki sefere {combo} tuÅŸuna basabilirsiniz", "bundle_column_error.body": "Bu bileÅŸen yüklenirken bir ÅŸeyler ters gitti.", "bundle_column_error.retry": "Tekrar deneyin", "bundle_column_error.title": "AÄŸ hatası", @@ -47,6 +53,7 @@ "column.blocks": "Engellenen kullanıcılar", "column.community": "Yerel zaman tüneli", "column.direct": "DoÄŸrudan mesajlar", + "column.directory": "Profillere göz at", "column.domain_blocks": "Gizli alan adları", "column.favourites": "Favoriler", "column.follow_requests": "Takip istekleri", @@ -77,7 +84,7 @@ "compose_form.poll.remove_option": "Bu seçimi kaldır", "compose_form.publish": "Gönder", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "Medyayı hassas olarak iÅŸaretle", "compose_form.sensitive.marked": "Medya hassas olarak iÅŸaretlendi", "compose_form.sensitive.unmarked": "Medya hassas olarak iÅŸaretlenmemiÅŸ", "compose_form.spoiler.marked": "Metin uyarının arkasına gizlenir", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "Bu listeyi kalıcı olarak silmek istediÄŸinize emin misiniz?", "confirmations.domain_block.confirm": "Alan adının tamamını gizle", "confirmations.domain_block.message": "tüm {domain} alan adını engellemek istediÄŸinizden emin misiniz? Genellikle birkaç hedefli engel ve susturma iÅŸi görür ve tercih edilir.", + "confirmations.logout.confirm": "Çıkış Yap", + "confirmations.logout.message": "Çıkış yapmak istediÄŸinize emin misiniz?", "confirmations.mute.confirm": "Sessize al", + "confirmations.mute.explanation": "Bu onlardan gelen ve onlardan bahseden gönderileri gizleyecek, fakat yine de onların gönderilerinizi görmelerine ve sizi takip etmelerine izin verecektir.", "confirmations.mute.message": "{name} kullanıcısını sessize almak istiyor musunuz?", "confirmations.redraft.confirm": "Sil ve yeniden tasarla", "confirmations.redraft.message": "Bu durumu silip tekrar taslaklaÅŸtırmak istediÄŸinizden emin misiniz? Tüm cevapları, boostları ve favorileri kaybedeceksiniz.", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Åžimdi yanıtlarken o an oluÅŸturduÄŸunuz mesajın üzerine yazılır. Devam etmek istediÄŸinize emin misiniz?", "confirmations.unfollow.confirm": "Takibi kaldır", "confirmations.unfollow.message": "{name}'yi takipten çıkarmak istediÄŸinizden emin misiniz?", + "conversation.delete": "KonuÅŸmayı sil", + "conversation.mark_as_read": "OkunmuÅŸ olarak iÅŸaretle", + "conversation.open": "KonuÅŸmayı görüntüle", + "conversation.with": "{names} ile", + "directory.federated": "Bilinen fediverse'lerden", + "directory.local": "Yalnızca {domain} adresinden", + "directory.new_arrivals": "Yeni gelenler", + "directory.recently_active": "Son zamanlarda aktif", "embed.instructions": "AÅŸağıdaki kodu kopyalayarak bu durumu sitenize gömün.", "embed.preview": "İşte nasıl görüneceÄŸi:", "emoji_button.activity": "Aktivite", @@ -118,7 +136,7 @@ "emoji_button.symbols": "Semboller", "emoji_button.travel": "Seyahat ve Yerler", "empty_column.account_timeline": "Burada hiç gönderi yok!", - "empty_column.account_unavailable": "Profile unavailable", + "empty_column.account_unavailable": "Profil kullanılamıyor", "empty_column.blocks": "Henüz bir kullanıcıyı engellemediniz.", "empty_column.community": "Yerel zaman çizelgesi boÅŸ. Daha fazla eÄŸlence için herkese açık bir gönderi paylaşın!", "empty_column.direct": "Henüz doÄŸrudan mesajınız yok. Bir tane gönderdiÄŸinizde veya aldığınızda burada görünecektir.", @@ -134,6 +152,10 @@ "empty_column.mutes": "Henüz hiçbir kullanıcıyı sessize almadınız.", "empty_column.notifications": "Henüz hiçbir bildiriminiz yok. DiÄŸer insanlarla sobhet edebilmek için etkileÅŸime geçebilirsiniz.", "empty_column.public": "Burada hiçbir ÅŸey yok! Herkese açık bir ÅŸeyler yazın veya burayı doldurmak için diÄŸer sunuculardaki kullanıcıları takip edin", + "error.unexpected_crash.explanation": "Bizim kodumuzdaki bir hatadan ya da tarayıcı uyumluluk sorunundan dolayı, bu sayfa düzgün görüntülenemedi.", + "error.unexpected_crash.next_steps": "Sayfayı yenilemeyi deneyin. EÄŸer bu yardımcı olmazsa, Mastodon'u farklı bir tarayıcı ya da yerel uygulama üzerinden kullanabilirsiniz.", + "errors.unexpected_crash.copy_stacktrace": "Yığın izlemeyi (stacktrace) panoya kopyala", + "errors.unexpected_crash.report_issue": "Sorun bildir", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -161,20 +183,20 @@ "home.column_settings.basic": "Temel", "home.column_settings.show_reblogs": "Boost edilenleri göster", "home.column_settings.show_replies": "Cevapları göster", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", + "intervals.full.days": "{number, plural, one {# gün} other {# gün}}", + "intervals.full.hours": "{number, plural, one {# saat} other {# saat}}", + "intervals.full.minutes": "{number, plural, one {# dakika} other {# dakika}}", "introduction.federation.action": "İleri", "introduction.federation.federated.headline": "BirleÅŸik", "introduction.federation.federated.text": "DiÄŸer dosya sunucularından gelen genel gönderiler, birleÅŸik zaman çizelgesinde görünecektir.", "introduction.federation.home.headline": "Ana sayfa", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.home.text": "Takip ettiÄŸiniz kiÅŸilerin yayınları ana sayfada gösterilecek. Herhangi bir sunucudaki herkesi takip edebilirsiniz!", "introduction.federation.local.headline": "Yerel", "introduction.federation.local.text": "Aynı sunucudaki kiÅŸilerin gönderileri yerel zaman tünelinde gözükecektir.", "introduction.interactions.action": "Öğreticiyi bitirin!", "introduction.interactions.favourite.headline": "Favori", "introduction.interactions.favourite.text": "Bir gönderiyi favorilerinize alarak sonrası için saklayabilirsiniz ve yazara gönderiyi beÄŸendiÄŸinizi söyleyebilirsiniz.", - "introduction.interactions.reblog.headline": "Boost", + "introduction.interactions.reblog.headline": "Yinele", "introduction.interactions.reblog.text": "BaÅŸkalarının gönderilerini boostlayarak kendi takipçilerinizle paylaÅŸabillirsiniz.", "introduction.interactions.reply.headline": "Yanıt", "introduction.interactions.reply.text": "BaÅŸkalarının gönderilerini ve kendi gönderilerinizi yanıtlayabilirsiniz. Bir konuÅŸmada zincirli bir ÅŸekilde olacaklardır.", @@ -209,14 +231,14 @@ "keyboard_shortcuts.search": "aramaya odaklanmak için", "keyboard_shortcuts.start": "\"baÅŸlayın\" sütununu açmak için", "keyboard_shortcuts.toggle_hidden": "CW'den önceki yazıyı göstermek/gizlemek için", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "medyayı göstermek/gizlemek için", "keyboard_shortcuts.toot": "yeni bir gönderiye baÅŸlamak için", "keyboard_shortcuts.unfocus": "aramada bir gönderiye odaklanmamak için", "keyboard_shortcuts.up": "listede yukarıya çıkmak için", "lightbox.close": "Kapat", "lightbox.next": "Sonraki", "lightbox.previous": "Önceli", - "lightbox.view_context": "View context", + "lightbox.view_context": "İçeriÄŸi göster", "lists.account.add": "Listeye ekle", "lists.account.remove": "Listeden kaldır", "lists.delete": "Listeyi sil", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "Yeni liste baÅŸlığı", "lists.search": "Takip ettiÄŸiniz kiÅŸiler arasından arayın", "lists.subheading": "Listeleriniz", + "load_pending": "{count, plural, one {# yeni öğe} other {# yeni öğe}}", "loading_indicator.label": "Yükleniyor...", "media_gallery.toggle_visible": "Görünürlüğü deÄŸiÅŸtir", "missing_indicator.label": "Bulunamadı", @@ -242,7 +265,7 @@ "navigation_bar.favourites": "Favoriler", "navigation_bar.filters": "SusturulmuÅŸ kelimeler", "navigation_bar.follow_requests": "Takip istekleri", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "Takip edilenler ve takipçiler", "navigation_bar.info": "GeniÅŸletilmiÅŸ bilgi", "navigation_bar.keyboard_shortcuts": "Klavye kısayolları", "navigation_bar.lists": "Listeler", @@ -251,12 +274,11 @@ "navigation_bar.personal": "KiÅŸisel", "navigation_bar.pins": "SabitlenmiÅŸ gönderiler", "navigation_bar.preferences": "Tercihler", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Federe zaman tüneli", "navigation_bar.security": "Güvenlik", "notification.favourite": "{name} senin durumunu favorilere ekledi", "notification.follow": "{name} seni takip ediyor", - "notification.mention": "{name} mentioned you", + "notification.mention": "{name} senden bahsetti", "notification.poll": "Oy verdiÄŸiniz bir anket bitti", "notification.reblog": "{name} senin durumunu boost etti", "notifications.clear": "Bildirimleri temizle", @@ -282,8 +304,10 @@ "notifications.group": "{count} bildirim", "poll.closed": "Kapandı", "poll.refresh": "Yenile", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", + "poll.total_people": "{count, plural, one {# kiÅŸi} other {# kiÅŸi}}", + "poll.total_votes": "{count, plural, one {# oy} other {# oy}}", "poll.vote": "Oy ver", + "poll.voted": "Bu cevap için oy kullandınız", "poll_button.add_poll": "Bir anket ekleyin", "poll_button.remove_poll": "Anket kaldır", "privacy.change": "Gönderi gizliliÄŸini ayarla", @@ -295,6 +319,7 @@ "privacy.public.short": "Herkese açık", "privacy.unlisted.long": "Herkese açık zaman tüneline gönderme", "privacy.unlisted.short": "ListelenmemiÅŸ", + "refresh": "Yenile", "regeneration_indicator.label": "Yükleniyor…", "regeneration_indicator.sublabel": "Ev akışınız hazırlanıyor!", "relative_time.days": "{number}g", @@ -311,14 +336,15 @@ "report.target": "Raporlama", "search.placeholder": "Ara", "search_popout.search_format": "GeliÅŸmiÅŸ arama formatı", - "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", - "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.full_text": "Basit metin yazdığınız, tercih ettiÄŸiniz, yinelediÄŸiniz veya bunlardan bahsettiÄŸiniz durumların yanı sıra kullanıcı adlarını, görünen adları ve hashtag'leri eÅŸleÅŸtiren durumları döndürür.", + "search_popout.tips.hashtag": "etiketler", "search_popout.tips.status": "durum", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.text": "Basit metin, eÅŸleÅŸen görünen adları, kullanıcı adlarını ve hashtag'leri döndürür", "search_popout.tips.user": "kullanıcı", "search_results.accounts": "İnsanlar", "search_results.hashtags": "Hashtagler", "search_results.statuses": "Gönderiler", + "search_results.statuses_fts_disabled": "Bu Mastodon sunucusunda gönderi içeriÄŸine göre arama etkin deÄŸil.", "search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}", "status.admin_account": "@{name} için denetim arayüzünü açın", "status.admin_status": "Denetim arayüzünde bu durumu açın", @@ -344,7 +370,7 @@ "status.pinned": "SabitlenmiÅŸ gönderi", "status.read_more": "Daha dazla oku", "status.reblog": "Boostla", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Orjinal kitleye yinele", "status.reblogged_by": "{name} boost etti", "status.reblogs.empty": "Kimse bu gönderiyi boostlamadı. Biri yaptığında burada gözükecek.", "status.redraft": "Sil & tekrar taslakla", @@ -358,7 +384,8 @@ "status.show_more": "Daha fazla göster", "status.show_more_all": "Hepsi için daha fazla göster", "status.show_thread": "BaÅŸlığı göster", - "status.unmute_conversation": "Unmute conversation", + "status.uncached_media_warning": "Mevcut deÄŸil", + "status.unmute_conversation": "Sohbeti aç", "status.unpin": "Profilden sabitlemeyi kaldır", "suggestions.dismiss": "Öneriyi görmezden gel", "suggestions.header": "Åžuna ilgi duyuyor olabilirsiniz…", @@ -367,20 +394,28 @@ "tabs_bar.local_timeline": "Yerel", "tabs_bar.notifications": "Bildirimler", "tabs_bar.search": "Ara", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", - "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "time_remaining.days": "{number, plural, one {# gün} other {# gün}} kaldı", + "time_remaining.hours": "{number, plural, one {# saat} other {# saat}} kaldı", + "time_remaining.minutes": "{number, plural, one {# dakika} other {# dakika}} kaldı", + "time_remaining.moments": "Sadece birkaç dakika kaldı", + "time_remaining.seconds": "{number, plural, one {# saniye} other {# saniye}} kaldı", + "trends.count_by_accounts": "{count} {rawCount, plural, one {kiÅŸi} other {kiÅŸi}} konuÅŸuyor", + "trends.trending_now": "Åžu an popüler", "ui.beforeunload": "Mastodon'dan ayrılırsanız taslağınız kaybolacak.", "upload_area.title": "Karşıya yükleme için sürükle bırak yapınız", "upload_button.label": "Görsel ekle", "upload_error.limit": "Dosya yükleme sınırı aşıldı.", "upload_error.poll": "Anketlerde dosya yüklemesine izin verilmez.", - "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Kırp", + "upload_form.description": "Görme engelliler için açıklama", + "upload_form.edit": "Düzenle", "upload_form.undo": "Geri al", + "upload_modal.analyzing_picture": "Resmi analiz ediyor…", + "upload_modal.apply": "Uygula", + "upload_modal.description_placeholder": "Pijamalı hasta yağız ÅŸoföre çabucak güvendi", + "upload_modal.detect_text": "Resimdeki metni algıla", + "upload_modal.edit_media": "Medyayı düzenle", + "upload_modal.hint": "Her zaman tüm küçük resimlerde görüntülenecek odak noktasını seçmek için ön izlemedeki daireyi tıklayın veya sürükleyin.", + "upload_modal.preview_label": "Ön izleme ({ratio})", "upload_progress.label": "Yükleniyor...", "video.close": "Videoyu kapat", "video.exit_fullscreen": "Tam ekrandan çık", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 63c73563e2ce10033f443e05e15325d1d4fd214e..b8be74b008b2ba4b7d2d69f5186723300a5ae77e 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -1,52 +1,59 @@ { - "account.add_or_remove_from_list": "Add or Remove from lists", + "account.add_or_remove_from_list": "Додати або видалити зі ÑпиÑків", "account.badges.bot": "Бот", "account.block": "Заблокувати @{name}", "account.block_domain": "Заглушити {domain}", "account.blocked": "Заблоковані", + "account.cancel_follow_request": "СкаÑувати запит на підпиÑку", "account.direct": "ПрÑме Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ @{name}", "account.domain_blocked": "Домен приховано", "account.edit_profile": "Редагувати профіль", "account.endorse": "Feature on profile", "account.follow": "ПідпиÑатиÑÑ", "account.followers": "ПідпиÑники", - "account.followers.empty": "No one follows this user yet.", + "account.followers.empty": "Ðіхто ще не підпиÑавÑÑ Ð½Ð° цього кориÑтувача.", "account.follows": "ПідпиÑки", - "account.follows.empty": "This user doesn't follow anyone yet.", + "account.follows.empty": "Цей кориÑтувач ще ні на кого не підпиÑавÑÑ.", "account.follows_you": "ПідпиÑаний(-а) на ВаÑ", "account.hide_reblogs": "Сховати передмухи від @{name}", - "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.last_status": "ÐšÑ€Ð°Ð¹Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть", + "account.link_verified_on": "Права влаÑноÑті на це поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¸ перевірені {date}", + "account.locked_info": "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÐºÐ¾Ð½Ñ„Ñ–Ð´ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾Ñті цього облікового запиÑу вÑтановлено у заблокований. ВлаÑник вручну переглÑдає, хто може за ним Ñтежити.", "account.media": "Медіа", "account.mention": "Згадати @{name}", "account.moved_to": "{name} переїхав на:", "account.mute": "Заглушити @{name}", "account.mute_notifications": "Ðе показувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ @{name}", "account.muted": "Заглушений", + "account.never_active": "Ðіколи", "account.posts": "Дмухи", "account.posts_with_replies": "Дмухи й відповіді", "account.report": "ПоÑкаржитиÑÑ Ð½Ð° @{name}", "account.requested": "Очікує підтвердженнÑ. ÐатиÑніть щоб відмінити запит", "account.share": "Поширити профіль @{name}", "account.show_reblogs": "Показати передмухи від @{name}", - "account.unblock": "Розблокувати", + "account.unblock": "Розблокувати @{name}", "account.unblock_domain": "Розблокувати {domain}", "account.unendorse": "Don't feature on profile", "account.unfollow": "ВідпиÑатиÑÑ", - "account.unmute": "ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ @{name}", + "account.unmute": "ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ Ð· @{name}", "account.unmute_notifications": "Показувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ @{name}", + "alert.rate_limited.message": "Спробуйте ще раз через {retry_time, time, medium}.", + "alert.rate_limited.title": "ШвидкіÑть обмежена", "alert.unexpected.message": "ТрапилаÑÑŒ неочікувана помилка.", "alert.unexpected.title": "Ой!", + "autosuggest_hashtag.per_week": "{count} в тиждень", "boost_modal.combo": "Ви можете натиÑнути {combo}, щоб пропуÑтити це наÑтупного разу", - "bundle_column_error.body": "ЩоÑÑŒ пішло не так при завантаженні компоненту.", - "bundle_column_error.retry": "Спробуйте ще", + "bundle_column_error.body": "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚Ñƒ.", + "bundle_column_error.retry": "Спробуйте ще раз", "bundle_column_error.title": "Помилка мережі", "bundle_modal_error.close": "Закрити", - "bundle_modal_error.message": "ЩоÑÑŒ пішло не так при завантаженні компоненту.", - "bundle_modal_error.retry": "Спробувати ще", + "bundle_modal_error.message": "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚Ñƒ.", + "bundle_modal_error.retry": "Спробувати ще раз", "column.blocks": "Заблоковані кориÑтувачі", "column.community": "Локальна Ñтрічка", "column.direct": "ПрÑмі повідомленнÑ", + "column.directory": "ПереглÑнути профілі", "column.domain_blocks": "Приховані домени", "column.favourites": "Вподобане", "column.follow_requests": "Запити на підпиÑку", @@ -58,7 +65,7 @@ "column.public": "Глобальна Ñтрічка", "column_back_button.label": "Ðазад", "column_header.hide_settings": "Приховати налаштуваннÑ", - "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveLeft_settings": "ЗміÑтити колонку вліво", "column_header.moveRight_settings": "ЗміÑтити колонку вправо", "column_header.pin": "Закріпити", "column_header.show_settings": "Показати налаштуваннÑ", @@ -66,25 +73,25 @@ "column_subheading.settings": "ÐалаштуваннÑ", "community.column_settings.media_only": "Тільки медіа", "compose_form.direct_message_warning": "Цей дмух буде видимий тільки згаданим кориÑтувачам.", - "compose_form.direct_message_warning_learn_more": "ДізнатиÑÑŒ більше", - "compose_form.hashtag_warning": "Цей дмух не буде відображений у жодній Ñтрічці хештеґу, так Ñк він прихований. Тільки публічні дмухи можуть бути знайдені за хештеґом.", + "compose_form.direct_message_warning_learn_more": "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ", + "compose_form.hashtag_warning": "Цей дмух не буде відображений у жодній Ñтрічці хештеґу, оÑкільки він прихований. Тільки публічні дмухи можуть бути знайдені за хештеґом.", "compose_form.lock_disclaimer": "Ваш акаунт не {locked}. Кожен може підпиÑатиÑÑ Ð½Ð° Ð’Ð°Ñ Ñ‚Ð° бачити Ваші приватні поÑти.", "compose_form.lock_disclaimer.lock": "приватний", "compose_form.placeholder": "Що у Ð’Ð°Ñ Ð½Ð° думці?", - "compose_form.poll.add_option": "Add a choice", - "compose_form.poll.duration": "Poll duration", - "compose_form.poll.option_placeholder": "Choice {number}", - "compose_form.poll.remove_option": "Remove this choice", + "compose_form.poll.add_option": "Додати варіант", + "compose_form.poll.duration": "ТриваліÑть опитуваннÑ", + "compose_form.poll.option_placeholder": "Варіант {number}", + "compose_form.poll.remove_option": "Видалити цей варіант", "compose_form.publish": "Дмухнути", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.hide": "Mark media as sensitive", - "compose_form.sensitive.marked": "Медіа відмічене <b>неÑприйнÑтливим</b>", - "compose_form.sensitive.unmarked": "Медіа відмічене ÑприйнÑтливим", - "compose_form.spoiler.marked": "ТекÑÑ‚ приховано за попередженнÑм", + "compose_form.sensitive.hide": "Позначити медіа Ñк дражливе", + "compose_form.sensitive.marked": "Медіа відмічене Ñк дражливе", + "compose_form.sensitive.unmarked": "Медіа не відмічене Ñк дражливе", + "compose_form.spoiler.marked": "ТекÑÑ‚ приховано під попередженнÑм", "compose_form.spoiler.unmarked": "ТекÑÑ‚ видимий", - "compose_form.spoiler_placeholder": "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ñ‰Ð¾Ð´Ð¾ прихованого текÑту", + "compose_form.spoiler_placeholder": "Ðапишіть Ñвоє Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ñ‚ÑƒÑ‚", "confirmation_modal.cancel": "Відмінити", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "Заблокувати та поÑкаржитиÑÑ", "confirmations.block.confirm": "Заблокувати", "confirmations.block.message": "Ви впевнені, що хочете заблокувати {name}?", "confirmations.delete.confirm": "Видалити", @@ -93,15 +100,26 @@ "confirmations.delete_list.message": "Ви впевнені, що хочете видалити цей ÑпиÑок назавжди?", "confirmations.domain_block.confirm": "Сховати веÑÑŒ домен", "confirmations.domain_block.message": "Ви точно, точно впевнені, що хочете заблокувати веÑÑŒ домен {domain}? У більшоÑті випадків Ð´Ð»Ñ Ð½Ð¾Ñ€Ð¼Ð°Ð»ÑŒÐ½Ð¾Ñ— роботи краще заблокувати/заглушити лише деÑких кориÑтувачів. Ви не зможете бачити контент з цього домену у будь-Ñких Ñтрічках або ваших ÑповіщеннÑÑ…. Ваші підпиÑники з цього домену будуть відпиÑані від ваÑ.", + "confirmations.logout.confirm": "Вийти", + "confirmations.logout.message": "Ви впевнені, що хочете вийти?", "confirmations.mute.confirm": "Заглушити", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "Ви впевнені, що хочете заглушити {name}?", - "confirmations.redraft.confirm": "Видалити Ñ– переÑтворити", + "confirmations.redraft.confirm": "Видалити та переÑтворити", "confirmations.redraft.message": "Ви впевнені, що хочете видалити Ð´Ð¾Ð¿Ð¸Ñ Ñ– переÑтворити його? Ви втратите вÑÑ– відповіді, передмухи та вподобайки допиÑу.", - "confirmations.reply.confirm": "Reply", - "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", + "confirmations.reply.confirm": "ВідповіÑти", + "confirmations.reply.message": "Поточна відповідь перезапише повідомленнÑ, Ñке ви зараз пишете. Ви впевнені, що хочете продовжити?", "confirmations.unfollow.confirm": "ВідпиÑатиÑÑ", "confirmations.unfollow.message": "Ви впевнені, що хочете відпиÑатиÑÑ Ð²Ñ–Ð´ {name}?", - "embed.instructions": "Інтегруйте цей ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° вашому вебÑайті, Ñкопіювавши код нижче.", + "conversation.delete": "Видалити цю беÑіду", + "conversation.mark_as_read": "Позначити Ñк прочитане", + "conversation.open": "ПереглÑнути беÑіду", + "conversation.with": "With {names}", + "directory.federated": "З відомого федеÑвіту", + "directory.local": "Тільки з домену {domain}", + "directory.new_arrivals": "Ðові надходженнÑ", + "directory.recently_active": "Ðктивні нещодавно", + "embed.instructions": "Вбудуйте цей ÑÑ‚Ð°Ñ‚ÑƒÑ Ð´Ð¾ вашого вебÑайту, Ñкопіювавши код нижче.", "embed.preview": "ОÑÑŒ Ñк він виглÑдатиме:", "emoji_button.activity": "ЗанÑттÑ", "emoji_button.custom": "ОÑобливі", @@ -113,27 +131,31 @@ "emoji_button.objects": "Предмети", "emoji_button.people": "Люди", "emoji_button.recent": "ЧаÑто викориÑтовувані", - "emoji_button.search": "Знайти...", + "emoji_button.search": "Шукати...", "emoji_button.search_results": "Результати пошуку", "emoji_button.symbols": "Символи", "emoji_button.travel": "Подорожі", - "empty_column.account_timeline": "No toots here!", - "empty_column.account_unavailable": "Profile unavailable", - "empty_column.blocks": "You haven't blocked any users yet.", + "empty_column.account_timeline": "Тут дмухалок немає!", + "empty_column.account_unavailable": "Профіль недоÑтупний", + "empty_column.blocks": "Ви ще не заблокували жодного кориÑтувача.", "empty_column.community": "Локальна Ñтрічка пуÑта. Ðапишіть щоÑÑŒ, щоб розігріти народ!", "empty_column.direct": "У Ð²Ð°Ñ Ñ‰Ðµ немає прÑмих повідомлень. Коли ви відправите чи отримаєте ÑкеÑÑŒ, воно з'ÑвитьÑÑ Ñ‚ÑƒÑ‚.", - "empty_column.domain_blocks": "There are no hidden domains yet.", - "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.", - "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.", - "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", + "empty_column.domain_blocks": "Тут поки немає прихованих доменів.", + "empty_column.favourited_statuses": "У Ð²Ð°Ñ Ñ‰Ðµ немає вподобаних дмухів. Коли ви щоÑÑŒ вподобаєте, воно з'ÑвитьÑÑ Ñ‚ÑƒÑ‚.", + "empty_column.favourites": "Ðіхто ще не вподобав цього дмуху. Коли хтоÑÑŒ це зробить, вони з'ÑвлÑтьÑÑ Ñ‚ÑƒÑ‚.", + "empty_column.follow_requests": "У Ð²Ð°Ñ Ñ‰Ðµ немає запитів на підпиÑку. Коли ви Ñ—Ñ… отримаєте, вони з'ÑвлÑтьÑÑ Ñ‚ÑƒÑ‚.", "empty_column.hashtag": "ДопиÑів з цим хештегом поки не Ñ–Ñнує.", "empty_column.home": "Ви поки ні на кого не підпиÑані. Погортайте {public}, або ÑкориÑтуйтеÑÑŒ пошуком, щоб оÑвоїтиÑÑ Ñ‚Ð° познайомитиÑÑ Ð· іншими кориÑтувачами.", "empty_column.home.public_timeline": "публічні Ñтрічки", "empty_column.list": "Ðемає нічого в цьому ÑпиÑку. Коли його учаÑники дмухнуть нові ÑтатуÑи, вони з'ÑвлÑтьÑÑ Ñ‚ÑƒÑ‚.", - "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.", - "empty_column.mutes": "You haven't muted any users yet.", + "empty_column.lists": "У Ð²Ð°Ñ Ñ‰Ðµ немає ÑпиÑків. Коли ви Ñ—Ñ… Ñтворите, вони з'ÑвлÑтьÑÑ Ñ‚ÑƒÑ‚.", + "empty_column.mutes": "Ви ще не заглушили жодного кориÑтувача.", "empty_column.notifications": "У Ð²Ð°Ñ Ñ‰Ðµ немає Ñповіщень. ПерепиÑуйтеÑÑŒ з іншими кориÑтувачами, щоб почати розмову.", "empty_column.public": "Тут поки нічого немає! Опублікуйте щоÑÑŒ, або вручну підпишітьÑÑ Ð½Ð° кориÑтувачів інших інÑтанцій, щоб заповнити Ñтрічку", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,99 +164,100 @@ "follow_request.authorize": "Ðвторизувати", "follow_request.reject": "Відмовити", "getting_started.developers": "Розробникам", - "getting_started.directory": "Profile directory", + "getting_started.directory": "Каталог профілів", "getting_started.documentation": "ДокументаціÑ", "getting_started.heading": "ЛаÑкаво проÑимо", "getting_started.invite": "ЗапроÑіть людей", - "getting_started.open_source_notice": "Mastodon - програма з відкритим вихідним кодом. Ви можете допомогти проекту, або повідомити про проблеми на GitHub за адреÑою {github}.", + "getting_started.open_source_notice": "Mastodon — програма з відкритим Ñирцевим кодом. Ви можете допомогти проекту, або повідомити про проблеми на GitHub за адреÑою {github}.", "getting_started.security": "Безпека", "getting_started.terms": "Умови викориÑтаннÑ", - "hashtag.column_header.tag_mode.all": "and {additional}", - "hashtag.column_header.tag_mode.any": "or {additional}", - "hashtag.column_header.tag_mode.none": "without {additional}", - "hashtag.column_settings.select.no_options_message": "No suggestions found", - "hashtag.column_settings.select.placeholder": "Enter hashtags…", - "hashtag.column_settings.tag_mode.all": "All of these", - "hashtag.column_settings.tag_mode.any": "Any of these", - "hashtag.column_settings.tag_mode.none": "None of these", - "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "hashtag.column_header.tag_mode.all": "та {additional}", + "hashtag.column_header.tag_mode.any": "або {additional}", + "hashtag.column_header.tag_mode.none": "без {additional}", + "hashtag.column_settings.select.no_options_message": "Ðе знайдено пропозицій", + "hashtag.column_settings.select.placeholder": "Введіть хештеґи…", + "hashtag.column_settings.tag_mode.all": "УÑÑ– ці", + "hashtag.column_settings.tag_mode.any": "Який-небудь з цих", + "hashtag.column_settings.tag_mode.none": "Жоден з цих", + "hashtag.column_settings.tag_toggle": "Додайте додаткові теґи до цього Ñтовпчика", "home.column_settings.basic": "ОÑновні", "home.column_settings.show_reblogs": "Показувати передмухи", "home.column_settings.show_replies": "Показувати відповіді", - "intervals.full.days": "{number, plural, one {# day} other {# days}}", - "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}", - "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}", - "introduction.federation.action": "Next", - "introduction.federation.federated.headline": "Federated", - "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", - "introduction.federation.home.headline": "Home", - "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", - "introduction.federation.local.headline": "Local", - "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", - "introduction.interactions.action": "Finish toot-orial!", - "introduction.interactions.favourite.headline": "Favourite", - "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", - "introduction.interactions.reblog.headline": "Boost", - "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", - "introduction.interactions.reply.headline": "Reply", - "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", - "introduction.welcome.action": "Let's go!", - "introduction.welcome.headline": "First steps", - "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "intervals.full.days": "{number, plural, one {# день} few {# дні} other {# днів}}", + "intervals.full.hours": "{number, plural, one {# година} few {# години} other {# годин}}", + "intervals.full.minutes": "{number, plural, one {# хвилина} few {# хвилини} other {# хвилин}}", + "introduction.federation.action": "Далі", + "introduction.federation.federated.headline": "Глобальна", + "introduction.federation.federated.text": "Публічні поÑти з інших Ñерверів федіверÑу будуть з'ÑвлÑтиÑÑ Ñƒ глобальній Ñтрічці.", + "introduction.federation.home.headline": "Головна", + "introduction.federation.home.text": "ПоÑти від людей, за Ñкими ви Ñлідкуєте, з'ÑвлÑтьÑÑ Ñƒ Вашій домашній Ñтрічці. Ви можете Ñлідкувати за кожним на будь-Ñкому Ñервері!", + "introduction.federation.local.headline": "Локальна", + "introduction.federation.local.text": "Публічні поÑти від людей на Ñервері, на Ñкому Ви знаходитеÑÑŒ, будуть з'ÑвлÑтиÑÑ Ñƒ локальній Ñтрічці.", + "introduction.interactions.action": "Завершити вÑтуп!", + "introduction.interactions.favourite.headline": "Улюблене", + "introduction.interactions.favourite.text": "Ви можете зберегти дмух на потім Ñ– повідомити автора, що він вам ÑподобавÑÑ, додавши його в улюблене.", + "introduction.interactions.reblog.headline": "Передмухнути", + "introduction.interactions.reblog.text": "Ви можете ділитиÑÑ Ð´Ð¼ÑƒÑ…Ð°Ð¼Ð¸ інших людей зі Ñвоїми підпиÑниками, передмухуючи Ñ—Ñ….", + "introduction.interactions.reply.headline": "ВідповіÑти", + "introduction.interactions.reply.text": "Ви можете відповідати на дмухи інших людей та влаÑні, Ñтворюючи ланцюжки розмов.", + "introduction.welcome.action": "Поїхали!", + "introduction.welcome.headline": "Перші кроки", + "introduction.welcome.text": "Вітаємо у федіверÑÑ–! Ðевдовзі ви зможете поширювати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° ÑпілкуватиÑÑ Ð·Ñ– Ñвоїми друзÑми на розмаїтті Ñерверів. Ðле цей Ñервер, {domain}, Ñ” оÑобливим — на ньому розміщений ваш профіль, тож запам'Ñтайте його назву.", "keyboard_shortcuts.back": "переходити назад", - "keyboard_shortcuts.blocked": "to open blocked users list", + "keyboard_shortcuts.blocked": "відкрити ÑпиÑок заблокованих кориÑтувачів", "keyboard_shortcuts.boost": "передмухувати", "keyboard_shortcuts.column": "фокуÑуватиÑÑ Ð½Ð° одній з колонок", "keyboard_shortcuts.compose": "фокуÑуватиÑÑ Ð½Ð° полі введеннÑ", "keyboard_shortcuts.description": "ОпиÑ", - "keyboard_shortcuts.direct": "to open direct messages column", + "keyboard_shortcuts.direct": "відкрити колонку прÑмих повідомлень", "keyboard_shortcuts.down": "рухатиÑÑ Ð²Ð½Ð¸Ð· Ñтрічкою", "keyboard_shortcuts.enter": "відкрити ÑтатуÑ", "keyboard_shortcuts.favourite": "вподобати", - "keyboard_shortcuts.favourites": "to open favourites list", - "keyboard_shortcuts.federated": "to open federated timeline", + "keyboard_shortcuts.favourites": "відкрити ÑпиÑок улюбленого", + "keyboard_shortcuts.federated": "відкрити глобальну Ñтрічку", "keyboard_shortcuts.heading": "ГарÑчі клавіші", - "keyboard_shortcuts.home": "to open home timeline", + "keyboard_shortcuts.home": "відкрити домашню Ñтрічку", "keyboard_shortcuts.hotkey": "ГарÑча клавіша", "keyboard_shortcuts.legend": "показати підказку", - "keyboard_shortcuts.local": "to open local timeline", + "keyboard_shortcuts.local": "відкрити локальну Ñтрічку", "keyboard_shortcuts.mention": "згадати автора", - "keyboard_shortcuts.muted": "to open muted users list", - "keyboard_shortcuts.my_profile": "to open your profile", - "keyboard_shortcuts.notifications": "to open notifications column", - "keyboard_shortcuts.pinned": "to open pinned toots list", + "keyboard_shortcuts.muted": "відкрити ÑпиÑок заглушених кориÑтувачів", + "keyboard_shortcuts.my_profile": "відкрити ваш профіль", + "keyboard_shortcuts.notifications": "відкрити колонку Ñповіщень", + "keyboard_shortcuts.pinned": "відкрити ÑпиÑок закріплених дмухів", "keyboard_shortcuts.profile": "відкрити профіль автора", "keyboard_shortcuts.reply": "відповіÑти", - "keyboard_shortcuts.requests": "to open follow requests list", + "keyboard_shortcuts.requests": "відкрити ÑпиÑок бажаючих підпиÑатиÑÑ", "keyboard_shortcuts.search": "ÑфокуÑуватиÑÑ Ð½Ð° пошуку", - "keyboard_shortcuts.start": "to open \"get started\" column", - "keyboard_shortcuts.toggle_hidden": "показати/приховати прихований текÑÑ‚", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.start": "відкрити колонку \"Початок\"", + "keyboard_shortcuts.toggle_hidden": "показати/приховати текÑÑ‚ під попередженнÑм", + "keyboard_shortcuts.toggle_sensitivity": "показати/приховати медіа", "keyboard_shortcuts.toot": "почати пиÑати новий дмух", "keyboard_shortcuts.unfocus": "розфокуÑуватиÑÑ Ð· нового допиÑу чи пошуку", "keyboard_shortcuts.up": "рухатиÑÑ Ð²Ð²ÐµÑ€Ñ… ÑпиÑком", "lightbox.close": "Закрити", "lightbox.next": "Далі", "lightbox.previous": "Ðазад", - "lightbox.view_context": "View context", + "lightbox.view_context": "ПереглÑнути контекÑÑ‚", "lists.account.add": "Додати до ÑпиÑку", "lists.account.remove": "Видалити зі ÑпиÑку", "lists.delete": "Видалити ÑпиÑок", "lists.edit": "Редагувати ÑпиÑок", - "lists.edit.submit": "Change title", + "lists.edit.submit": "Змінити назву", "lists.new.create": "Додати ÑпиÑок", "lists.new.title_placeholder": "Ðова назва ÑпиÑку", "lists.search": "Шукати Ñеред людей, на Ñких ви підпиÑані", "lists.subheading": "Ваші ÑпиÑки", + "load_pending": "{count, plural, one {# новий елемент} other {# нових елементів}}", "loading_indicator.label": "ЗавантаженнÑ...", "media_gallery.toggle_visible": "Показати/приховати", "missing_indicator.label": "Ðе знайдено", "missing_indicator.sublabel": "РеÑÑƒÑ€Ñ Ð½Ðµ знайдений", "mute_modal.hide_notifications": "Приховати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ кориÑтувача?", - "navigation_bar.apps": "Mobile apps", + "navigation_bar.apps": "Мобільні додатки", "navigation_bar.blocks": "Заблоковані кориÑтувачі", "navigation_bar.community_timeline": "Локальна Ñтрічка", - "navigation_bar.compose": "Compose new toot", + "navigation_bar.compose": "ÐапиÑати новий дмух", "navigation_bar.direct": "ПрÑмі повідомленнÑ", "navigation_bar.discover": "Знайти", "navigation_bar.domain_blocks": "Приховані домени", @@ -242,61 +265,63 @@ "navigation_bar.favourites": "Вподобане", "navigation_bar.filters": "Приховані Ñлова", "navigation_bar.follow_requests": "Запити на підпиÑку", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "ПідпиÑки Ñ– підпиÑники", "navigation_bar.info": "Про Ñайт", - "navigation_bar.keyboard_shortcuts": "ГарÑчі клавіши", + "navigation_bar.keyboard_shortcuts": "ГарÑчі клавіші", "navigation_bar.lists": "СпиÑки", "navigation_bar.logout": "Вийти", "navigation_bar.mutes": "Заглушені кориÑтувачі", "navigation_bar.personal": "ОÑобиÑте", "navigation_bar.pins": "Закріплені дмухи", "navigation_bar.preferences": "ÐалаштуваннÑ", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "Глобальна Ñтрічка", "navigation_bar.security": "Безпека", "notification.favourite": "{name} вподобав(-ла) ваш допиÑ", "notification.follow": "{name} підпиÑавÑÑ(-лаÑÑŒ) на ВаÑ", "notification.mention": "{name} згадав(-ла) ВаÑ", - "notification.poll": "A poll you have voted in has ended", + "notification.poll": "ОпитуваннÑ, у Ñкому ви голоÑували, закінчилоÑÑ", "notification.reblog": "{name} передмухнув(-ла) Ваш допиÑ", "notifications.clear": "ОчиÑтити ÑповіщеннÑ", "notifications.clear_confirmation": "Ви впевнені, що хочете назавжди видалити вÑÑ– ÑповіщенÑ?", "notifications.column_settings.alert": "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð½Ð° комп'ютері", "notifications.column_settings.favourite": "Вподобане:", - "notifications.column_settings.filter_bar.advanced": "Display all categories", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", + "notifications.column_settings.filter_bar.advanced": "Показати вÑÑ– категорії", + "notifications.column_settings.filter_bar.category": "Панель швидкого фільтру", + "notifications.column_settings.filter_bar.show": "Показати", "notifications.column_settings.follow": "Ðові підпиÑники:", "notifications.column_settings.mention": "Згадки:", - "notifications.column_settings.poll": "Poll results:", + "notifications.column_settings.poll": "Результати опитуваннÑ:", "notifications.column_settings.push": "Push-ÑповіщеннÑ", "notifications.column_settings.reblog": "Передмухи:", "notifications.column_settings.show": "Показати в колонці", "notifications.column_settings.sound": "Відтворювати звуки", - "notifications.filter.all": "All", - "notifications.filter.boosts": "Boosts", - "notifications.filter.favourites": "Favourites", - "notifications.filter.follows": "Follows", - "notifications.filter.mentions": "Mentions", - "notifications.filter.polls": "Poll results", + "notifications.filter.all": "УÑÑ–", + "notifications.filter.boosts": "Передмухи", + "notifications.filter.favourites": "Улюблені", + "notifications.filter.follows": "ПідпиÑки", + "notifications.filter.mentions": "Згадки", + "notifications.filter.polls": "Результати опитуваннÑ", "notifications.group": "{count} Ñповіщень", - "poll.closed": "Closed", - "poll.refresh": "Refresh", - "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", - "poll.vote": "Vote", - "poll_button.add_poll": "Add a poll", - "poll_button.remove_poll": "Remove poll", + "poll.closed": "Закрито", + "poll.refresh": "Оновити", + "poll.total_people": "{count, plural, one {# person} other {# people}}", + "poll.total_votes": "{count, plural, one {# голоÑ} few {# голоÑи} many {# голоÑів} other {# голоÑів}}", + "poll.vote": "ПроголоÑувати", + "poll.voted": "You voted for this answer", + "poll_button.add_poll": "Додати опитуваннÑ", + "poll_button.remove_poll": "Видалити опитуваннÑ", "privacy.change": "Змінити видиміÑть допиÑу", "privacy.direct.long": "Показати тільки згаданим кориÑтувачам", - "privacy.direct.short": "Ðаправлений", + "privacy.direct.short": "ОÑобиÑто", "privacy.private.long": "Показати тільки підпиÑникам", "privacy.private.short": "Тільки Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників", "privacy.public.long": "Показувати у публічних Ñтрічках", - "privacy.public.short": "Публічний", + "privacy.public.short": "Публічно", "privacy.unlisted.long": "Ðе показувати у публічних Ñтрічках", "privacy.unlisted.short": "Прихований", + "refresh": "Оновити", "regeneration_indicator.label": "ЗавантаженнÑ…", - "regeneration_indicator.sublabel": "Ваша Ð´Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñтрічка готова!", + "regeneration_indicator.sublabel": "Ваша Ð´Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñтрічка готуєтьÑÑ!", "relative_time.days": "{number}д", "relative_time.hours": "{number}г", "relative_time.just_now": "щойно", @@ -308,82 +333,92 @@ "report.hint": "Скаргу буде відправлено модераторам Вашого Ñайту. Ви можете надати їм поÑÑненнÑ, чому ви ÑкаржитеÑÑŒ на аккаунт нижче:", "report.placeholder": "Додаткові коментарі", "report.submit": "Відправити", - "report.target": "СкаржимоÑÑ Ð½Ð°", + "report.target": "СкаржимоÑÑ Ð½Ð° {target}", "search.placeholder": "Пошук", - "search_popout.search_format": "Advanced search format", - "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", - "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "status", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", - "search_popout.tips.user": "user", - "search_results.accounts": "People", - "search_results.hashtags": "Hashtags", - "search_results.statuses": "Toots", + "search_popout.search_format": "Розширений формат пошуку", + "search_popout.tips.full_text": "Пошук за текÑтом знаходить ÑтатуÑи, Ñкі ви напиÑали, вподобали, передмухнули, або в Ñких Ð²Ð°Ñ Ð·Ð³Ð°Ð´ÑƒÐ²Ð°Ð»Ð¸. Також він знаходить імена кориÑтувачів, реальні імена та хештеґи.", + "search_popout.tips.hashtag": "хештеґ", + "search_popout.tips.status": "ÑтатуÑ", + "search_popout.tips.text": "Пошук за текÑтом знаходить імена кориÑтувачів, реальні імена та хештеґи", + "search_popout.tips.user": "кориÑтувач", + "search_results.accounts": "Люди", + "search_results.hashtags": "Хештеґи", + "search_results.statuses": "Дмухів", + "search_results.statuses_fts_disabled": "Пошук дмухів за вміÑтом недоÑтупний на цьому Ñервері Mastodon.", "search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}", - "status.admin_account": "Open moderation interface for @{name}", - "status.admin_status": "Open this status in the moderation interface", - "status.block": "Block @{name}", - "status.cancel_reblog_private": "Unboost", + "status.admin_account": "Відкрити Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¼Ð¾Ð´ÐµÑ€Ð°Ñ†Ñ–Ñ— Ð´Ð»Ñ @{name}", + "status.admin_status": "Відкрити цей ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² інтерфейÑÑ– модерації", + "status.block": "Заблокувати @{name}", + "status.cancel_reblog_private": "Відмінити передмуханнÑ", "status.cannot_reblog": "Цей Ð´Ð¾Ð¿Ð¸Ñ Ð½Ðµ може бути передмухнутий", - "status.copy": "Copy link to status", + "status.copy": "Копіювати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð¾ ÑтатуÑу", "status.delete": "Видалити", - "status.detailed_status": "Detailed conversation view", - "status.direct": "Direct message @{name}", - "status.embed": "Embed", + "status.detailed_status": "Детальний виглÑд беÑіди", + "status.direct": "ПрÑме Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð¾ @{name}", + "status.embed": "Вбудувати", "status.favourite": "ПодобаєтьÑÑ", - "status.filtered": "Filtered", + "status.filtered": "Відфільтровано", "status.load_more": "Завантажити більше", "status.local_only": "This post is only visible by other users of your instance", "status.media_hidden": "Медіаконтент приховано", - "status.mention": "Згадати", - "status.more": "More", - "status.mute": "Mute @{name}", + "status.mention": "Згадати @{name}", + "status.more": "Більше", + "status.mute": "Заглушити @{name}", "status.mute_conversation": "Заглушити діалог", "status.open": "Розгорнути допиÑ", - "status.pin": "Pin on profile", - "status.pinned": "Pinned toot", - "status.read_more": "Read more", + "status.pin": "Закріпити у профілі", + "status.pinned": "Закріплений дмух", + "status.read_more": "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ", "status.reblog": "Передмухнути", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Передмухнути Ð´Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— аудиторії", "status.reblogged_by": "{name} передмухнув(-ла)", - "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.", - "status.redraft": "Delete & re-draft", + "status.reblogs.empty": "Ðіхто ще не передмухнув цього дмуху. Коли ÑкіÑÑŒ кориÑтувачі це зроблÑть, вони будуть відображені тут.", + "status.redraft": "Видалити та переÑтворити", "status.reply": "ВідповіÑти", - "status.replyAll": "ВідповіÑти на тред", - "status.report": "ПоÑкаржитиÑÑ", - "status.sensitive_warning": "ÐеприÑтойний зміÑÑ‚", - "status.share": "Share", + "status.replyAll": "ВідповіÑти на ланцюжок", + "status.report": "ПоÑкаржитиÑÑ Ð½Ð° @{name}", + "status.sensitive_warning": "Дражливий зміÑÑ‚", + "status.share": "ПоділитиÑÑ", "status.show_less": "Згорнути", "status.show_less_all": "Show less for all", "status.show_more": "Розгорнути", "status.show_more_all": "Show more for all", - "status.show_thread": "Show thread", + "status.show_thread": "Показати ланцюжок", + "status.uncached_media_warning": "ÐедоÑтупно", "status.unmute_conversation": "ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ Ð· діалогу", - "status.unpin": "Unpin from profile", - "suggestions.dismiss": "Dismiss suggestion", - "suggestions.header": "You might be interested in…", + "status.unpin": "Відкріпити від профілю", + "suggestions.dismiss": "Відхилити пропозицію", + "suggestions.header": "Ð’Ð°Ñ Ð¼Ð¾Ð¶Ðµ зацікавити…", "tabs_bar.federated_timeline": "Глобальна", "tabs_bar.home": "Головна", "tabs_bar.local_timeline": "Локальна", "tabs_bar.notifications": "СповіщеннÑ", "tabs_bar.search": "Пошук", - "time_remaining.days": "{number, plural, one {# day} other {# days}} left", - "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", - "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", + "time_remaining.days": "{number, plural, one {# день} few {# дні} other {# днів}}", + "time_remaining.hours": "{number, plural, one {# година} few {# години} other {# годин}}", + "time_remaining.minutes": "{number, plural, one {# хвилина} few {# хвилини} other {# хвилин}}", "time_remaining.moments": "Moments remaining", - "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", - "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking", + "time_remaining.seconds": "{number, plural, one {# Ñекунда} few {# Ñекунди} other {# Ñекунд}}", + "trends.count_by_accounts": "{count} {rawCount, plural, one {людина} few {людини} many {людей} other {людей}} обговорюють це", + "trends.trending_now": "Ðктуальні", "ui.beforeunload": "Вашу чернетку буде втрачено, Ñкщо ви покинете Mastodon.", "upload_area.title": "ПеретÑгніть Ñюди, щоб завантажити", - "upload_button.label": "Додати медіаконтент", - "upload_error.limit": "File upload limit exceeded.", - "upload_error.poll": "File upload not allowed with polls.", + "upload_button.label": "Додати медіаконтент ({formats})", + "upload_error.limit": "Ліміт Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² перевищено.", + "upload_error.poll": "Ðе можна завантажувати файли до опитувань.", "upload_form.description": "Опишіть Ð´Ð»Ñ Ð»ÑŽÐ´ÐµÐ¹ з вадами зору", - "upload_form.focus": "Обрізати", + "upload_form.edit": "Змінити", "upload_form.undo": "Видалити", + "upload_modal.analyzing_picture": "Ðналізуємо малюнок…", + "upload_modal.apply": "ЗаÑтоÑувати", + "upload_modal.description_placeholder": "ЩурÑчий бугай із їжаком-харцизом в'ючиÑÑŒ підпиÑали ґешефт у єнах", + "upload_modal.detect_text": "ВиÑвити текÑÑ‚ на малюнку", + "upload_modal.edit_media": "Редагувати медіа", + "upload_modal.hint": "Клацніть або перетÑгніть коло на превью, щоб обрати точку, Ñку буде завжди видно на мініатюрах.", + "upload_modal.preview_label": "ПереглÑнути ({ratio})", "upload_progress.label": "ЗавантаженнÑ...", "video.close": "Закрити відео", - "video.exit_fullscreen": "Вийти з повного екрану", + "video.exit_fullscreen": "Вийти з повноекранного режиму", "video.expand": "Розширити відео", "video.fullscreen": "Ðа веÑÑŒ екран", "video.hide": "Приховати відео", diff --git a/app/javascript/mastodon/locales/whitelist_pt.json b/app/javascript/mastodon/locales/whitelist_br.json similarity index 100% rename from app/javascript/mastodon/locales/whitelist_pt.json rename to app/javascript/mastodon/locales/whitelist_br.json diff --git a/app/javascript/mastodon/locales/whitelist_es-AR.json b/app/javascript/mastodon/locales/whitelist_es-AR.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_es-AR.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_et.json b/app/javascript/mastodon/locales/whitelist_et.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_et.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_ga.json b/app/javascript/mastodon/locales/whitelist_ga.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_ga.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_mk.json b/app/javascript/mastodon/locales/whitelist_mk.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_mk.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_nn.json b/app/javascript/mastodon/locales/whitelist_nn.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_nn.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_pt-PT.json b/app/javascript/mastodon/locales/whitelist_pt-PT.json new file mode 100644 index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_pt-PT.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 6b9f9a0f8091948846a7827a2c853d681d413634..0e53f8ffad9930c2521bf6db635498d941973ebd 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -4,6 +4,7 @@ "account.block": "å±è”½ @{name}", "account.block_domain": "éšè—æ¥è‡ª {domain} 的内容", "account.blocked": "å·²å±è”½", + "account.cancel_follow_request": "å–æ¶ˆå…³æ³¨è¯·æ±‚", "account.direct": "å‘é€ç§ä¿¡ç»™ @{name}", "account.domain_blocked": "网站已å±è”½", "account.edit_profile": "修改个人资料", @@ -12,9 +13,10 @@ "account.followers": "关注者", "account.followers.empty": "ç›®å‰æ— 人关注æ¤ç”¨æˆ·ã€‚", "account.follows": "æ£åœ¨å…³æ³¨", - "account.follows.empty": "æ¤ç”¨æˆ·ç›®å‰æ²¡æœ‰å…³æ³¨ä»»ä½•人。", + "account.follows.empty": "æ¤ç”¨æˆ·ç›®å‰å°šæœªå…³æ³¨ä»»ä½•人。", "account.follows_you": "å…³æ³¨äº†ä½ ", "account.hide_reblogs": "éšè—æ¥è‡ª @{name} 的转嘟", + "account.last_status": "Last active", "account.link_verified_on": "æ¤é“¾æŽ¥çš„æ‰€æœ‰æƒå·²åœ¨ {date} 检查", "account.locked_info": "æ¤è´¦æˆ·å·²é”å˜Ÿã€‚è´¦æˆ·çš„ä¸»äººä¼šæ‰‹åŠ¨å®¡æ ¸å…³æ³¨è€…ã€‚", "account.media": "媒体", @@ -23,6 +25,7 @@ "account.mute": "éšè— @{name}", "account.mute_notifications": "éšè—æ¥è‡ª @{name} 的通知", "account.muted": "å·²éšè—", + "account.never_active": "Never", "account.posts": "嘟文", "account.posts_with_replies": "嘟文和回å¤", "account.report": "举报 @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "å–æ¶ˆå…³æ³¨", "account.unmute": "ä¸å†éšè— @{name}", "account.unmute_notifications": "ä¸å†éšè—æ¥è‡ª @{name} 的通知", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "å‘生了æ„外错误。", "alert.unexpected.title": "哎呀ï¼", + "autosuggest_hashtag.per_week": "æ¯æ˜ŸæœŸ {count} æ¡", "boost_modal.combo": "ä¸‹æ¬¡æŒ‰ä½ {combo} å³å¯è·³è¿‡æ¤æç¤º", "bundle_column_error.body": "载入这个组件时å‘生了错误。", "bundle_column_error.retry": "é‡è¯•", @@ -47,8 +53,9 @@ "column.blocks": "å·²å±è”½çš„用户", "column.community": "本站时间轴", "column.direct": "ç§ä¿¡", + "column.directory": "Browse profiles", "column.domain_blocks": "å·²å±è”½çš„网站", - "column.favourites": "æ”¶è—过的嘟文", + "column.favourites": "æ”¶è—", "column.follow_requests": "关注请求", "column.home": "主页", "column.lists": "列表", @@ -92,15 +99,26 @@ "confirmations.delete_list.confirm": "åˆ é™¤", "confirmations.delete_list.message": "ä½ ç¡®å®šè¦æ°¸ä¹…åˆ é™¤è¿™ä¸ªåˆ—è¡¨å—?", "confirmations.domain_block.confirm": "éšè—整个网站的内容", - "confirmations.domain_block.message": "ä½ çœŸçš„ç¡®å®šè¦éšè—所有æ¥è‡ª {domain} 的内容å—?多数情况下,å±è”½æˆ–éšè—å‡ ä¸ªç‰¹å®šçš„ç”¨æˆ·åº”è¯¥å°±èƒ½æ»¡è¶³ä½ çš„éœ€è¦äº†ã€‚æ¥è‡ªè¯¥ç½‘站的内容将ä¸å†å‡ºçŽ°åœ¨ä½ çš„å…¬å…±æ—¶é—´è½´æˆ–é€šçŸ¥åˆ—è¡¨é‡Œã€‚æ¥è‡ªè¯¥ç½‘站的关注者将会被移除。", + "confirmations.domain_block.message": "ä½ çœŸçš„ç¡®å®šè¦éšè—所有æ¥è‡ª {domain} 的内容å—?多数情况下,å±è”½æˆ–éšè—å‡ ä¸ªç‰¹å®šçš„ç”¨æˆ·å°±å·²ç»è¶³å¤Ÿäº†ã€‚æ¥è‡ªè¯¥ç½‘站的内容将ä¸å†å‡ºçŽ°åœ¨ä½ çš„ä»»ä½•å…¬å…±æ—¶é—´è½´æˆ–é€šçŸ¥åˆ—è¡¨é‡Œã€‚æ¥è‡ªè¯¥ç½‘站的关注者将会被移除。", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "éšè—", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "ä½ ç¡®å®šè¦éšè— {name} å—?", "confirmations.redraft.confirm": "åˆ é™¤å¹¶é‡æ–°ç¼–辑", - "confirmations.redraft.message": "ä½ ç¡®å®šè¦åˆ 除这æ¡å˜Ÿæ–‡å¹¶é‡æ–°ç¼–辑它å—?所有相关的转嘟和收è—都会被清除,回å¤å°†ä¼šè¢«å¤ç«‹ã€‚", + "confirmations.redraft.message": "ä½ ç¡®å®šè¦åˆ 除这æ¡å˜Ÿæ–‡å¹¶é‡æ–°ç¼–辑它å—?所有相关的转嘟和收è—都会被清除,回å¤å°†ä¼šå¤±å޻关è”。", "confirmations.reply.confirm": "回å¤", "confirmations.reply.message": "å›žå¤æ¤æ¶ˆæ¯å°†ä¼šè¦†ç›–当剿£åœ¨ç¼–辑的信æ¯ã€‚确定继ç»å—?", "confirmations.unfollow.confirm": "å–æ¶ˆå…³æ³¨", "confirmations.unfollow.message": "ä½ ç¡®å®šè¦å–消关注 {name} å—?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "è¦åœ¨ä½ 的网站上嵌入这æ¡å˜Ÿæ–‡ï¼Œè¯·å¤åˆ¶ä»¥ä¸‹ä»£ç 。", "embed.preview": "它会åƒè¿™æ ·æ˜¾ç¤ºå‡ºæ¥ï¼š", "emoji_button.activity": "活动", @@ -120,20 +138,24 @@ "empty_column.account_timeline": "这里没有嘟文ï¼", "empty_column.account_unavailable": "个人资料ä¸å¯ç”¨", "empty_column.blocks": "ä½ ç›®å‰æ²¡æœ‰å±è”½ä»»ä½•用户。", - "empty_column.community": "æœ¬ç«™æ—¶é—´è½´æš‚æ—¶æ²¡æœ‰å†…å®¹ï¼Œå¿«å˜Ÿå‡ ä¸ªæ¥æŠ¢å¤´é¦™å•Šï¼", + "empty_column.community": "本站时间轴暂时没有内容,快写点什么让它动起æ¥å§ï¼", "empty_column.direct": "ä½ è¿˜æ²¡æœ‰ä½¿ç”¨è¿‡ç§ä¿¡ã€‚å½“ä½ å‘出或者收到ç§ä¿¡æ—¶ï¼Œå®ƒä¼šåœ¨è¿™é‡Œæ˜¾ç¤ºã€‚", "empty_column.domain_blocks": "ç›®å‰æ²¡æœ‰è¢«éšè—的站点。", "empty_column.favourited_statuses": "ä½ è¿˜æ²¡æœ‰æ”¶è—过任何嘟文。收è—过的嘟文会显示在这里。", - "empty_column.favourites": "没人收è—过这æ¡å˜Ÿæ–‡ã€‚å‡å¦‚有人收è—了,就会显示在这里。", + "empty_column.favourites": "没有人收è—过这æ¡å˜Ÿæ–‡ã€‚如果有人收è—了,就会显示在这里。", "empty_column.follow_requests": "ä½ æ²¡æœ‰æ”¶åˆ°æ–°çš„å…³æ³¨è¯·æ±‚ã€‚æ”¶åˆ°äº†ä¹‹åŽå°±ä¼šæ˜¾ç¤ºåœ¨è¿™é‡Œã€‚", "empty_column.hashtag": "这个è¯é¢˜æ ‡ç¾ä¸‹æš‚时没有内容。", - "empty_column.home": "ä½ è¿˜æ²¡æœ‰å…³æ³¨ä»»ä½•ç”¨æˆ·ã€‚å¿«çœ‹çœ‹{public},å‘其他用户æè®ªå§ã€‚", + "empty_column.home": "ä½ è¿˜æ²¡æœ‰å…³æ³¨ä»»ä½•ç”¨æˆ·ã€‚å¿«çœ‹çœ‹{public},å‘其他人问个好å§ã€‚", "empty_column.home.public_timeline": "公共时间轴", "empty_column.list": "è¿™ä¸ªåˆ—è¡¨ä¸æš‚æ—¶æ²¡æœ‰å†…容。列表ä¸ç”¨æˆ·æ‰€å‘é€çš„的新嘟文将会在这里显示。", - "empty_column.lists": "ä½ æ²¡æœ‰åˆ›å»ºè¿‡åˆ—è¡¨ã€‚ä½ åˆ›å»ºçš„åˆ—è¡¨ä¼šåœ¨è¿™é‡Œæ˜¾ç¤ºã€‚", + "empty_column.lists": "ä½ è¿˜æ²¡æœ‰åˆ›å»ºè¿‡åˆ—è¡¨ã€‚ä½ åˆ›å»ºçš„åˆ—è¡¨ä¼šåœ¨è¿™é‡Œæ˜¾ç¤ºã€‚", "empty_column.mutes": "ä½ æ²¡æœ‰éšè—任何用户。", - "empty_column.notifications": "ä½ è¿˜æ²¡æœ‰æ”¶åˆ°è¿‡ä»»ä½•é€šçŸ¥ï¼Œå¿«å‘其他用户æè®ªå§ã€‚", + "empty_column.notifications": "ä½ è¿˜æ²¡æœ‰æ”¶åˆ°è¿‡ä»»ä½•é€šçŸ¥ï¼Œå¿«å’Œå…¶ä»–ç”¨æˆ·äº’åŠ¨å§ã€‚", "empty_column.public": "这里什么都没有ï¼å†™ä¸€äº›å…¬å¼€çš„嘟文,或者关注其他æœåŠ¡å™¨çš„ç”¨æˆ·åŽï¼Œè¿™é‡Œå°±ä¼šæœ‰å˜Ÿæ–‡å‡ºçŽ°äº†", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -142,11 +164,11 @@ "follow_request.authorize": "åŒæ„", "follow_request.reject": "æ‹’ç»", "getting_started.developers": "å¼€å‘", - "getting_started.directory": "用户资料目录", + "getting_started.directory": "用户目录", "getting_started.documentation": "文档", "getting_started.heading": "开始使用", "getting_started.invite": "邀请用户", - "getting_started.open_source_notice": "Mastodon 是一个开æºè½¯ä»¶ã€‚欢迎å‰å¾€ GitHub({github}ï¼‰è´¡çŒ®ä»£ç æˆ–å馈问题。", + "getting_started.open_source_notice": "Mastodon 是开æºè½¯ä»¶ã€‚欢迎å‰å¾€ GitHub({github}ï¼‰è´¡çŒ®ä»£ç æˆ–å馈问题。", "getting_started.security": "叿ˆ·å®‰å…¨", "getting_started.terms": "ä½¿ç”¨æ¡æ¬¾", "hashtag.column_header.tag_mode.all": "ä»¥åŠ {additional}", @@ -166,9 +188,9 @@ "intervals.full.minutes": "{number} 分钟", "introduction.federation.action": "下一æ¥", "introduction.federation.federated.headline": "跨站", - "introduction.federation.federated.text": "其他跨站æœåŠ¡å™¨çš„å…¬å…±åŠ¨æ€ä¼šæ˜¾ç¤ºåœ¨è·¨ç«™æ—¶é—´çº¿ä¸ã€‚", + "introduction.federation.federated.text": "è”邦宇宙ä¸å…¶ä»–æœåŠ¡å™¨çš„å…¬å¼€å˜Ÿæ–‡ä¼šæ˜¾ç¤ºåœ¨è·¨ç«™æ—¶é—´è½´ä¸ã€‚", "introduction.federation.home.headline": "主页", - "introduction.federation.home.text": "ä½ æ‰€å…³æ³¨çš„ç”¨æˆ·çš„åŠ¨æ€ä¼šæ˜¾ç¤ºåœ¨ä¸»é¡µé‡Œã€‚ä½ å¯ä»¥å…³æ³¨ä»»ä½•æœåŠ¡å™¨ä¸Šçš„ä»»ä½•äºº!", + "introduction.federation.home.text": "ä½ æ‰€å…³æ³¨çš„ç”¨æˆ·çš„åŠ¨æ€ä¼šæ˜¾ç¤ºåœ¨ä¸»é¡µé‡Œã€‚ä½ å¯ä»¥å…³æ³¨ä»»ä½•æœåŠ¡å™¨ä¸Šçš„ä»»ä½•äººï¼", "introduction.federation.local.headline": "本站", "introduction.federation.local.text": "ä½ æ‰€å…³æ³¨çš„ç”¨æˆ·çš„åŠ¨æ€ä¼šæ˜¾ç¤ºåœ¨ä¸»é¡µé‡Œï¼Œä½ å¯ä»¥å…³æ³¨ä»»ä½•æœåŠ¡å™¨ä¸Šçš„ä»»ä½•äººã€‚", "introduction.interactions.action": "教程结æŸï¼", @@ -177,10 +199,10 @@ "introduction.interactions.reblog.headline": "转嘟", "introduction.interactions.reblog.text": "é€šè¿‡è½¬å˜Ÿï¼Œä½ å¯ä»¥å‘ä½ çš„å…³æ³¨è€…åˆ†äº«å…¶ä»–äººçš„å˜Ÿæ–‡ã€‚", "introduction.interactions.reply.headline": "回å¤", - "introduction.interactions.reply.text": "ä½ å¯ä»¥å‘其他人回å¤ï¼Œè¿™äº›å›žå¤ä¼šåƒå¯¹è¯ä¸€æ ·ä¸²åœ¨ä¸€èµ·ã€‚", + "introduction.interactions.reply.text": "ä½ å¯ä»¥å›žå¤å…¶ä»–嘟文,这些回å¤ä¼šåƒå¯¹è¯ä¸€æ ·å…³è”在一起。", "introduction.welcome.action": "让我们开始å§ï¼", "introduction.welcome.headline": "首先", - "introduction.welcome.text": "欢迎æ¥åˆ°è”邦ï¼ç¨åŽï¼Œæ‚¨å°†å¯ä»¥å¹¿æ’消æ¯å¹¶å’Œæ‚¨çš„æœ‹å‹äº¤æµï¼Œè¿™äº›æ¶ˆæ¯å°†ç©¿è¶ŠäºŽè”邦ä¸çš„å„弿œåŠ¡å™¨ã€‚ä½†æ˜¯è¿™å°æœåŠ¡å™¨ï¼Œ{domain},是特殊的——它ä¿å˜äº†ä½ 的个人资料,所以请记ä½å®ƒçš„åå—。", + "introduction.welcome.text": "欢迎æ¥åˆ°è”邦宇宙ï¼å¾ˆå¿«ï¼Œæ‚¨å°±å¯ä»¥å‘布信æ¯å¹¶å’Œæ‚¨çš„æœ‹å‹äº¤æµï¼Œè¿™äº›æ¶ˆæ¯å°†å‘é€åˆ°è”邦ä¸çš„å„个æœåС噍ä¸ã€‚ä½†æ˜¯è¿™å°æœåŠ¡å™¨ï¼Œ{domain},是特殊的——它ä¿å˜äº†ä½ 的个人资料,所以请记ä½å®ƒçš„åå—。", "keyboard_shortcuts.back": "返回上一页", "keyboard_shortcuts.blocked": "打开被å±è”½ç”¨æˆ·åˆ—表", "keyboard_shortcuts.boost": "转嘟", @@ -199,9 +221,9 @@ "keyboard_shortcuts.legend": "显示æ¤åˆ—表", "keyboard_shortcuts.local": "打开本站时间轴", "keyboard_shortcuts.mention": "æåŠå˜Ÿæ–‡ä½œè€…", - "keyboard_shortcuts.muted": "打开å±è”½ç”¨æˆ·åˆ—表", + "keyboard_shortcuts.muted": "打开éšè—用户列表", "keyboard_shortcuts.my_profile": "æ‰“å¼€ä½ çš„ä¸ªäººèµ„æ–™", - "keyboard_shortcuts.notifications": "打å¡é€šçŸ¥æ ", + "keyboard_shortcuts.notifications": "打开通知æ ", "keyboard_shortcuts.pinned": "打开置顶嘟文列表", "keyboard_shortcuts.profile": "打开作者的个人资料", "keyboard_shortcuts.reply": "回å¤å˜Ÿæ–‡", @@ -218,7 +240,7 @@ "lightbox.previous": "上一个", "lightbox.view_context": "查看上下文", "lists.account.add": "æ·»åŠ åˆ°åˆ—è¡¨", - "lists.account.remove": "从列表ä¸åˆ 除", + "lists.account.remove": "从列表ä¸ç§»é™¤", "lists.delete": "åˆ é™¤åˆ—è¡¨", "lists.edit": "编辑列表", "lists.edit.submit": "æ›´æ”¹æ ‡é¢˜", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "æ–°åˆ—è¡¨çš„æ ‡é¢˜", "lists.search": "æœç´¢ä½ 关注的人", "lists.subheading": "ä½ çš„åˆ—è¡¨", + "load_pending": "{count} 项", "loading_indicator.label": "åŠ è½½ä¸â€¦â€¦", "media_gallery.toggle_visible": "åˆ‡æ¢æ˜¾ç¤º/éšè—", "missing_indicator.label": "找ä¸åˆ°å†…容", @@ -240,29 +263,28 @@ "navigation_bar.domain_blocks": "å·²å±è”½çš„网站", "navigation_bar.edit_profile": "修改个人资料", "navigation_bar.favourites": "æ”¶è—的内容", - "navigation_bar.filters": "被éšè—çš„è¯", + "navigation_bar.filters": "å±è”½å…³é”®è¯", "navigation_bar.follow_requests": "关注请求", - "navigation_bar.follows_and_followers": "æ£åœ¨å…³æ³¨ä»¥åŠå…³æ³¨è€…", + "navigation_bar.follows_and_followers": "关注管ç†", "navigation_bar.info": "关于本站", "navigation_bar.keyboard_shortcuts": "å¿«æ·é”®åˆ—表", "navigation_bar.lists": "列表", - "navigation_bar.logout": "注销", + "navigation_bar.logout": "登出", "navigation_bar.mutes": "å·²éšè—的用户", "navigation_bar.personal": "个人", "navigation_bar.pins": "置顶嘟文", "navigation_bar.preferences": "首选项", - "navigation_bar.profile_directory": "用户资料目录", "navigation_bar.public_timeline": "跨站公共时间轴", "navigation_bar.security": "安全", "notification.favourite": "{name} æ”¶è—äº†ä½ çš„å˜Ÿæ–‡", "notification.follow": "{name} å¼€å§‹å…³æ³¨ä½ ", - "notification.mention": "{name} æåŠä½ ", + "notification.mention": "{name} æåŠäº†ä½ ", "notification.poll": "ä½ å‚与的一个投票已ç»ç»“æŸ", - "notification.reblog": "{name} è½¬äº†ä½ çš„å˜Ÿæ–‡", + "notification.reblog": "{name} è½¬å˜Ÿäº†ä½ çš„å˜Ÿæ–‡", "notifications.clear": "清空通知列表", "notifications.clear_confirmation": "ä½ ç¡®å®šè¦æ°¸ä¹…清空通知列表å—?", "notifications.column_settings.alert": "桌é¢é€šçŸ¥", - "notifications.column_settings.favourite": "ä½ çš„å˜Ÿæ–‡è¢«æ”¶è—æ—¶ï¼š", + "notifications.column_settings.favourite": "å½“ä½ çš„å˜Ÿæ–‡è¢«æ”¶è—æ—¶ï¼š", "notifications.column_settings.filter_bar.advanced": "显示所有类别", "notifications.column_settings.filter_bar.category": "快速过滤æ ", "notifications.column_settings.filter_bar.show": "显示", @@ -282,8 +304,10 @@ "notifications.group": "{count} æ¡é€šçŸ¥", "poll.closed": "已关é—", "poll.refresh": "刷新", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count} 票", "poll.vote": "投票", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "å‘起投票", "poll_button.remove_poll": "移除投票", "privacy.change": "设置嘟文å¯è§èŒƒå›´", @@ -295,6 +319,7 @@ "privacy.public.short": "公开", "privacy.unlisted.long": "所有人å¯è§ï¼Œä½†ä¸ä¼šå‡ºçŽ°åœ¨å…¬å…±æ—¶é—´è½´ä¸Š", "privacy.unlisted.short": "ä¸å…¬å¼€", + "refresh": "Refresh", "regeneration_indicator.label": "åŠ è½½ä¸â€¦â€¦", "regeneration_indicator.sublabel": "ä½ çš„ä¸»é¡µæ—¶é—´è½´æ£åœ¨å‡†å¤‡ä¸ï¼", "relative_time.days": "{number}天", @@ -306,25 +331,26 @@ "report.forward": "å‘é€ä¸¾æŠ¥è‡³ {target}", "report.forward_hint": "è¿™å用户æ¥è‡ªå¦ä¸€ä¸ªæœåŠ¡å™¨ã€‚æ˜¯å¦è¦å‘那个æœåС噍å‘é€ä¸€æ¡åŒ¿å的举报?", "report.hint": "举报将会å‘é€ç»™ä½ 所在æœåŠ¡å™¨çš„ç›‘å¯Ÿå‘˜ã€‚ä½ å¯ä»¥åœ¨ä¸‹é¢å¡«å†™ä¸¾æŠ¥è¯¥ç”¨æˆ·çš„ç†ç”±ï¼š", - "report.placeholder": "附言", + "report.placeholder": "备注", "report.submit": "æäº¤", "report.target": "举报 {target}", "search.placeholder": "æœç´¢", "search_popout.search_format": "高级æœç´¢æ ¼å¼", - "search_popout.tips.full_text": "è¾“å…¥å…¶ä»–å†…å®¹å°†ä¼šè¿”å›žæ‰€æœ‰ä½ æ’°å†™ã€æ”¶è—ã€è½¬å˜Ÿè¿‡æˆ–æåŠåˆ°ä½ çš„å˜Ÿæ–‡ï¼ŒåŒæ—¶ä¹Ÿä¼šåœ¨ç”¨æˆ·åã€æ˜µç§°å’Œè¯é¢˜æ ‡ç¾ä¸è¿›è¡Œæœç´¢ã€‚", + "search_popout.tips.full_text": "è¾“å…¥å…³é”®è¯æ£€ç´¢æ‰€æœ‰ä½ å‘é€ã€æ”¶è—ã€è½¬å˜Ÿè¿‡æˆ–æåŠåˆ°ä½ 的嘟文,以åŠå…¶ä»–用户公开的用户åã€æ˜µç§°å’Œè¯é¢˜æ ‡ç¾ã€‚", "search_popout.tips.hashtag": "è¯é¢˜æ ‡ç¾", "search_popout.tips.status": "嘟文", - "search_popout.tips.text": "输入其他内容将会返回昵称ã€ç”¨æˆ·åå’Œè¯é¢˜æ ‡ç¾", + "search_popout.tips.text": "è¾“å…¥å…³é”®è¯æ£€ç´¢æ˜µç§°ã€ç”¨æˆ·åå’Œè¯é¢˜æ ‡ç¾", "search_popout.tips.user": "用户", "search_results.accounts": "用户", "search_results.hashtags": "è¯é¢˜æ ‡ç¾", "search_results.statuses": "嘟文", + "search_results.statuses_fts_disabled": "æ¤MastodonæœåŠ¡å™¨æœªå¯ç”¨å˜Ÿæ–‡å†…容æœç´¢ã€‚", "search_results.total": "å…± {count, number} 个结果", "status.admin_account": "打开 @{name} 的管ç†ç•Œé¢", "status.admin_status": "打开这æ¡å˜Ÿæ–‡çš„管ç†ç•Œé¢", "status.block": "å±è”½ @{name}", "status.cancel_reblog_private": "å–æ¶ˆè½¬å˜Ÿ", - "status.cannot_reblog": "æ— æ³•è½¬å˜Ÿè¿™æ¡å˜Ÿæ–‡", + "status.cannot_reblog": "è¿™æ¡å˜Ÿæ–‡ä¸å…许被转嘟", "status.copy": "å¤åˆ¶å˜Ÿæ–‡é“¾æŽ¥", "status.delete": "åˆ é™¤", "status.detailed_status": "对è¯è¯¦æƒ…", @@ -344,9 +370,9 @@ "status.pinned": "置顶嘟文", "status.read_more": "阅读全文", "status.reblog": "转嘟", - "status.reblog_private": "转嘟给原有关注者", + "status.reblog_private": "转嘟(å¯è§è€…ä¸å˜ï¼‰", "status.reblogged_by": "{name} 转嘟了", - "status.reblogs.empty": "æ— äººè½¬å˜Ÿæ¤æ¡ã€‚如果有人转嘟了,就会显示在这里。", + "status.reblogs.empty": "æ²¡æœ‰äººè½¬å˜Ÿè¿‡æ¤æ¡å˜Ÿæ–‡ã€‚如果有人转嘟了,就会显示在这里。", "status.redraft": "åˆ é™¤å¹¶é‡æ–°ç¼–辑", "status.reply": "回å¤", "status.replyAll": "å›žå¤æ‰€æœ‰äºº", @@ -358,6 +384,7 @@ "status.show_more": "显示内容", "status.show_more_all": "显示所有内容", "status.show_thread": "显示全部对è¯", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "ä¸å†éšè—æ¤å¯¹è¯", "status.unpin": "在个人资料页é¢å–消置顶", "suggestions.dismiss": "å…³é—建议", @@ -373,15 +400,23 @@ "time_remaining.moments": "å³å°†ç»“æŸ", "time_remaining.seconds": "剩余 {number, plural, one {# ç§’} other {# ç§’}}", "trends.count_by_accounts": "{count} 人æ£åœ¨è®¨è®º", - "ui.beforeunload": "å¦‚æžœä½ çŽ°åœ¨ç¦»å¼€ Mastodonï¼Œä½ çš„è‰ç¨¿å†…容将会被丢弃。", + "trends.trending_now": "现在æµè¡Œ", + "ui.beforeunload": "å¦‚æžœä½ çŽ°åœ¨ç¦»å¼€ Mastodonï¼Œä½ çš„è‰ç¨¿å†…容将会丢失。", "upload_area.title": "将文件拖放到æ¤å¤„å¼€å§‹ä¸Šä¼ ", "upload_button.label": "ä¸Šä¼ åª’ä½“æ–‡ä»¶ (JPEG, PNG, GIF, WebM, MP4, MOV)", - "upload_error.limit": "è¶…è¿‡æ–‡ä»¶ä¸Šä¼ é™åˆ¶ã€‚", + "upload_error.limit": "文件大å°è¶…过é™åˆ¶ã€‚", "upload_error.poll": "投票ä¸ä¸å…è®¸ä¸Šä¼ æ–‡ä»¶ã€‚", "upload_form.description": "为视觉障ç¢äººå£«æ·»åŠ æ–‡å—说明", - "upload_form.focus": "剪è£", + "upload_form.edit": "编辑", "upload_form.undo": "åˆ é™¤", - "upload_progress.label": "ä¸Šä¼ ä¸â€¦", + "upload_modal.analyzing_picture": "分æžå›¾ç‰‡â€¦", + "upload_modal.apply": "应用", + "upload_modal.description_placeholder": "天地玄黄 å®‡å®™æ´ªè’ æ—¥æœˆç›ˆä»„ è¾°å®¿åˆ—å¼ ", + "upload_modal.detect_text": "ä»Žå›¾ç‰‡ä¸æ£€æµ‹æ–‡æœ¬", + "upload_modal.edit_media": "编辑媒体", + "upload_modal.hint": "在预览图上点击或拖动圆圈,以选择缩略图的焦点。", + "upload_modal.preview_label": "预览 ({ratio})", + "upload_progress.label": "ä¸Šä¼ ä¸â€¦â€¦", "video.close": "å…³é—视频", "video.exit_fullscreen": "退出全å±", "video.expand": "展开视频", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index cd1753ac8bf83e5c7ee7f06be7159840cb358129..49993cb52053fa5b68a9c53172a0c06bb78cddac 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -4,6 +4,7 @@ "account.block": "å°éŽ– @{name}", "account.block_domain": "éš±è—來自 {domain} çš„ä¸€åˆ‡æ–‡ç« ", "account.blocked": "å°éŽ–", + "account.cancel_follow_request": "Cancel follow request", "account.direct": "ç§è¨Š @{name}", "account.domain_blocked": "æœå‹™ç«™è¢«éš±è—", "account.edit_profile": "修改個人資料", @@ -15,6 +16,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "é—œæ³¨ä½ ", "account.hide_reblogs": "éš±è— @{name} 的轉推", + "account.last_status": "Last active", "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.media": "媒體", @@ -23,6 +25,7 @@ "account.mute": "å°‡ @{name} éœéŸ³", "account.mute_notifications": "將來自 @{name} 的通知éœéŸ³", "account.muted": "éœéŸ³", + "account.never_active": "Never", "account.posts": "æ–‡ç« ", "account.posts_with_replies": "包å«å›žè¦†çš„æ–‡ç« ", "account.report": "èˆ‰å ± @{name}", @@ -35,8 +38,11 @@ "account.unfollow": "å–æ¶ˆé—œæ³¨", "account.unmute": "å–æ¶ˆ @{name} çš„éœéŸ³", "account.unmute_notifications": "å–æ¶ˆä¾†è‡ª @{name} 通知的éœéŸ³", + "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.", + "alert.rate_limited.title": "Rate limited", "alert.unexpected.message": "發生ä¸å¯é 期的錯誤。", "alert.unexpected.title": "噢ï¼", + "autosuggest_hashtag.per_week": "{count} per week", "boost_modal.combo": "å¦‚ä½ æƒ³åœ¨ä¸‹æ¬¡è·¯éŽé€™é¡¯ç¤ºï¼Œè«‹æŒ‰{combo},", "bundle_column_error.body": "åŠ è¼‰æœ¬çµ„ä»¶å‡ºéŒ¯ã€‚", "bundle_column_error.retry": "é‡è©¦", @@ -47,6 +53,7 @@ "column.blocks": "å°éŽ–ç”¨æˆ¶", "column.community": "本站時間軸", "column.direct": "個人訊æ¯", + "column.directory": "Browse profiles", "column.domain_blocks": "éš±è—çš„æœå‹™ç«™", "column.favourites": "æœ€æ„›çš„æ–‡ç« ", "column.follow_requests": "關注請求", @@ -93,7 +100,10 @@ "confirmations.delete_list.message": "ä½ ç¢ºå®šè¦æ°¸ä¹…刪除這列表嗎?", "confirmations.domain_block.confirm": "éš±è—æ•´å€‹ç¶²ç«™", "confirmations.domain_block.message": "ä½ çœŸçš„çœŸçš„ç¢ºå®šè¦éš±è—整個 {domain} ?多數情æ³ä¸‹ï¼Œæ¯”較推薦å°éŽ–æˆ–éœéŸ³å¹¾å€‹ç‰¹å®šç›®æ¨™å°±å¥½ã€‚ä½ å¾žæ¤å°‡ä¸æœƒå†çœ‹åˆ°è©²ç«™çš„內容和通知。來自該站的關注者亦會被移除。", + "confirmations.logout.confirm": "Log out", + "confirmations.logout.message": "Are you sure you want to log out?", "confirmations.mute.confirm": "éœéŸ³", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "ä½ ç¢ºå®šè¦å°‡{name}éœéŸ³å—Žï¼Ÿ", "confirmations.redraft.confirm": "刪除並編輯", "confirmations.redraft.message": "ä½ ç¢ºå®šè¦åˆªé™¤ä¸¦é‡æ–°ç·¨è¼¯å—Žï¼Ÿæ‰€æœ‰ç›¸é—œçš„回覆ã€è½‰æŽ¨èˆ‡æœ€æ„›éƒ½æœƒè¢«åˆªé™¤ã€‚", @@ -101,6 +111,14 @@ "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.unfollow.confirm": "å–æ¶ˆé—œæ³¨", "confirmations.unfollow.message": "真的ä¸è¦ç¹¼çºŒé—œæ³¨ {name} 了嗎?", + "conversation.delete": "Delete conversation", + "conversation.mark_as_read": "Mark as read", + "conversation.open": "View conversation", + "conversation.with": "With {names}", + "directory.federated": "From known fediverse", + "directory.local": "From {domain} only", + "directory.new_arrivals": "New arrivals", + "directory.recently_active": "Recently active", "embed.instructions": "è¦å…§åµŒæ¤æ–‡ç« ï¼Œè«‹å°‡ä»¥ä¸‹ä»£ç¢¼è²¼é€²ä½ çš„ç¶²ç«™ã€‚", "embed.preview": "看上去會是這樣:", "emoji_button.activity": "活動", @@ -134,6 +152,10 @@ "empty_column.mutes": "You haven't muted any users yet.", "empty_column.notifications": "ä½ æ²’æœ‰ä»»ä½•é€šçŸ¥ç´€éŒ„ï¼Œå¿«å‘其他用戶æè¨•å§ã€‚", "empty_column.public": "跨站時間軸暫時沒有內容ï¼å¿«å¯«ä¸€äº›å…¬å…±çš„æ–‡ç« ,或者關注å¦ä¸€äº›æœå‹™ç«™çš„用戶å§ï¼ä½ 和本站ã€å‹ç«™çš„交æµï¼Œå°‡æ±ºå®šé€™è£å‡ºç¾çš„內容。", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -226,6 +248,7 @@ "lists.new.title_placeholder": "新列表標題", "lists.search": "å¾žä½ é—œæ³¨çš„ç”¨æˆ¶ä¸æœç´¢", "lists.subheading": "列表", + "load_pending": "{count, plural, one {# new item} other {# new items}}", "loading_indicator.label": "載入ä¸...", "media_gallery.toggle_visible": "打開或關上", "missing_indicator.label": "找ä¸åˆ°å…§å®¹", @@ -251,7 +274,6 @@ "navigation_bar.personal": "Personal", "navigation_bar.pins": "ç½®é ‚æ–‡ç« ", "navigation_bar.preferences": "å好è¨å®š", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "跨站時間軸", "navigation_bar.security": "安全", "notification.favourite": "{name} æ”¶è—äº†ä½ çš„æ–‡ç« ", @@ -282,8 +304,10 @@ "notifications.group": "{count} æ¢é€šçŸ¥", "poll.closed": "Closed", "poll.refresh": "Refresh", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# vote} other {# votes}}", "poll.vote": "Vote", + "poll.voted": "You voted for this answer", "poll_button.add_poll": "Add a poll", "poll_button.remove_poll": "Remove poll", "privacy.change": "調整ç§éš±è¨å®š", @@ -295,6 +319,7 @@ "privacy.public.short": "公共", "privacy.unlisted.long": "公開,但ä¸åœ¨å…¬å…±æ™‚間軸顯示", "privacy.unlisted.short": "公開", + "refresh": "Refresh", "regeneration_indicator.label": "載入ä¸â€¦â€¦", "regeneration_indicator.sublabel": "ä½ çš„ä¸»é æ™‚間軸æ£åœ¨æº–å‚™ä¸ï¼", "relative_time.days": "{number}æ—¥", @@ -319,6 +344,7 @@ "search_results.accounts": "使用者", "search_results.hashtags": "標籤", "search_results.statuses": "æ–‡ç« ", + "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.", "search_results.total": "{count, number} é …çµæžœ", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this status in the moderation interface", @@ -358,6 +384,7 @@ "status.show_more": "顯示更多", "status.show_more_all": "é¡¯ç¤ºæ›´å¤šé€™é¡žæ–‡ç« ", "status.show_thread": "Show thread", + "status.uncached_media_warning": "Not available", "status.unmute_conversation": "è§£ç¦å°è©±", "status.unpin": "è§£é™¤ç½®é ‚", "suggestions.dismiss": "Dismiss suggestion", @@ -373,14 +400,22 @@ "time_remaining.moments": "Moments remaining", "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left", "trends.count_by_accounts": "{count} ä½ç”¨æˆ¶åœ¨è¨Žè«–", + "trends.trending_now": "Trending now", "ui.beforeunload": "å¦‚æžœä½ ç¾åœ¨é›¢é–‹ Mastodonï¼Œä½ çš„è‰ç¨¿å…§å®¹å°‡æœƒè¢«ä¸Ÿæ£„。", "upload_area.title": "將檔案拖放至æ¤ä¸Šè¼‰", "upload_button.label": "上載媒體檔案", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", "upload_form.description": "ç‚ºè¦–è¦ºéšœç¤™äººå£«æ·»åŠ æ–‡å—說明", - "upload_form.focus": "è£åˆ‡", + "upload_form.edit": "Edit", "upload_form.undo": "刪除", + "upload_modal.analyzing_picture": "Analyzing picture…", + "upload_modal.apply": "Apply", + "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog", + "upload_modal.detect_text": "Detect text from picture", + "upload_modal.edit_media": "Edit media", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "Preview ({ratio})", "upload_progress.label": "上載ä¸â€¦â€¦", "video.close": "關閉影片", "video.exit_fullscreen": "退出全熒幕", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 05a8c4dac72650c5e217b6c41b3abf4def9241cf..e6080011ee8ef9dc758ade4c3a3a329ec57ac506 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -2,27 +2,30 @@ "account.add_or_remove_from_list": "從å單䏿–°å¢žæˆ–移除", "account.badges.bot": "機器人", "account.block": "å°éŽ– @{name}", - "account.block_domain": "éš±è—來自 {domain} 的所有嘟文", + "account.block_domain": "éš±è—來自 {domain} 的所有內容", "account.blocked": "å·²å°éŽ–", + "account.cancel_follow_request": "å–æ¶ˆé—œæ³¨è«‹æ±‚", "account.direct": "傳ç§è¨Šçµ¦ @{name}", "account.domain_blocked": "已隱è—網域", "account.edit_profile": "編輯個人資料", "account.endorse": "åœ¨å€‹äººè³‡æ–™æŽ¨è–¦å°æ–¹", "account.follow": "關注", "account.followers": "關注者", - "account.followers.empty": "還沒有人關注這ä½ä½¿ç”¨è€…。", + "account.followers.empty": "尚沒有人關注這ä½ä½¿ç”¨è€…。", "account.follows": "æ£åœ¨é—œæ³¨", - "account.follows.empty": "這個使用者尚未關注任何使用者。", + "account.follows.empty": "這ä½ä½¿ç”¨è€…尚未關注任何使用者。", "account.follows_you": "é—œæ³¨äº†ä½ ", "account.hide_reblogs": "éš±è—來自 @{name} 的轉推", - "account.link_verified_on": "æ¤é€£çµçš„æ‰€æœ‰æ¬Šå·²åœ¨ {date} 檢查", - "account.locked_info": "æ¤å¸³è™Ÿçš„éš±ç§ç‹€æ…‹è¢«è¨ç‚ºéŽ–å®šï¼Œæ“æœ‰è€…å°‡æ‰‹å‹•å¯©æ ¸å¯é—œæ³¨æ¤å¸³è™Ÿçš„人。", + "account.last_status": "上次活èº", + "account.link_verified_on": "已在 {date} 檢查æ¤é€£çµçš„æ“æœ‰è€…æ¬Šé™", + "account.locked_info": "這隻帳戶的隱ç§ç‹€æ…‹è¢«è¨æˆéŽ–å®šã€‚è©²æ“æœ‰è€…æœƒæ‰‹å‹•å¯©æ ¸èƒ½é—œæ³¨é€™éš»å¸³è™Ÿçš„äººã€‚", "account.media": "媒體", "account.mention": "æåŠ @{name}", "account.moved_to": "{name} å·²é·ç§»è‡³ï¼š", "account.mute": "éœéŸ³ @{name}", "account.mute_notifications": "éœéŸ³ä¾†è‡ª @{name} 的通知", "account.muted": "å·²éœéŸ³", + "account.never_active": "æ°¸ä¸", "account.posts": "嘟文", "account.posts_with_replies": "嘟文與回覆", "account.report": "檢舉 @{name}", @@ -33,22 +36,26 @@ "account.unblock_domain": "å–æ¶ˆéš±è— {domain}", "account.unendorse": "ä¸å†æ–¼å€‹äººè³‡æ–™é é¢æŽ¨è–¦å°æ–¹", "account.unfollow": "å–æ¶ˆé—œæ³¨", - "account.unmute": "ä¸å†éœéŸ³ @{name}", - "account.unmute_notifications": "ä¸å†éœéŸ³ä¾†è‡ª @{name} 的通知", + "account.unmute": "å–æ¶ˆéœéŸ³ @{name}", + "account.unmute_notifications": "釿–°æŽ¥æ”¶ä¾†è‡ª @{name} 的通知", + "alert.rate_limited.message": "請在 {retry_time, time, medium} éŽå¾Œé‡è©¦", + "alert.rate_limited.title": "å·²é™é€Ÿ", "alert.unexpected.message": "發生了éžé 期的錯誤。", "alert.unexpected.title": "哎呀ï¼", + "autosuggest_hashtag.per_week": "{count} / 週", "boost_modal.combo": "下次您å¯ä»¥æŒ‰ {combo} è·³éŽ", - "bundle_column_error.body": "載入æ¤çµ„件時發生錯誤。", + "bundle_column_error.body": "載入æ¤å…ƒä»¶æ™‚發生錯誤。", "bundle_column_error.retry": "é‡è©¦", "bundle_column_error.title": "網路錯誤", "bundle_modal_error.close": "關閉", - "bundle_modal_error.message": "載入æ¤çµ„件時發生錯誤。", + "bundle_modal_error.message": "載入æ¤å…ƒä»¶æ™‚發生錯誤。", "bundle_modal_error.retry": "é‡è©¦", "column.blocks": "å°éŽ–çš„ä½¿ç”¨è€…", - "column.community": "本地時間軸", + "column.community": "本機時間軸", "column.direct": "ç§è¨Š", + "column.directory": "ç€è¦½å€‹äººè³‡æ–™", "column.domain_blocks": "éš±è—的網域", - "column.favourites": "最愛", + "column.favourites": "æ”¶è—", "column.follow_requests": "關注請求", "column.home": "主é ", "column.lists": "åå–®", @@ -64,44 +71,55 @@ "column_header.show_settings": "顯示è¨å®š", "column_header.unpin": "å–æ¶ˆé‡˜é¸", "column_subheading.settings": "è¨å®š", - "community.column_settings.media_only": "僅媒體", - "compose_form.direct_message_warning": "這æ¢å˜Ÿæ–‡åªæœ‰è¢«æåŠçš„使用者æ‰èƒ½çœ‹åˆ°ã€‚", + "community.column_settings.media_only": "åªæœ‰åª’é«”", + "compose_form.direct_message_warning": "這æ¢å˜Ÿæ–‡åªæœ‰è¢«æåŠçš„使用者æ‰çœ‹å¾—到。", "compose_form.direct_message_warning_learn_more": "了解更多", - "compose_form.hashtag_warning": "å› é€™å‰‡å˜Ÿæ–‡è¨æˆã€Œä¸å…¬é–‹ã€ï¼Œå› æ¤å®ƒä¸æœƒåˆ—在任何「#ã€æ¨™ç±¤ä¸‹ã€‚åªæœ‰å…¬é–‹å˜Ÿæ–‡æ‰èƒ½ç”¨ã€Œ#ã€æ¨™ç±¤æ‰¾åˆ°ã€‚", - "compose_form.lock_disclaimer": "您的帳戶尚未{locked}。任何人都能關注您並看到您è¨å®šæˆåƒ…關注者能看的嘟文。", + "compose_form.hashtag_warning": "由於這則嘟文被è¨å®šæˆã€Œä¸å…¬é–‹ã€ï¼Œæ‰€ä»¥å®ƒå°‡ä¸æœƒè¢«åˆ—åœ¨ä»»ä½•ä¸»é¡Œæ¨™ç±¤ä¸‹ã€‚åªæœ‰å…¬é–‹çš„嘟文æ‰èƒ½è—‰ä¸»é¡Œæ¨™ç±¤æ‰¾åˆ°ã€‚", + "compose_form.lock_disclaimer": "您的帳戶尚未{locked}。任何人都能關注您並看到您è¨å®šæˆåªæœ‰é—œæ³¨è€…能看的嘟文。", "compose_form.lock_disclaimer.lock": "上鎖", "compose_form.placeholder": "您æ£åœ¨æƒ³äº›ä»€éº¼ï¼Ÿ", "compose_form.poll.add_option": "æ–°å¢žé¸æ“‡", "compose_form.poll.duration": "投票期é™", "compose_form.poll.option_placeholder": "第 {number} 個鏿“‡", "compose_form.poll.remove_option": "移除æ¤é¸æ“‡", - "compose_form.publish": "嘟掉", + "compose_form.publish": "嘟出去", "compose_form.publish_loud": "{publish}ï¼", - "compose_form.sensitive.hide": "Mark media as sensitive", + "compose_form.sensitive.hide": "æ¨™è¨˜åª’é«”ç‚ºæ•æ„Ÿå…§å®¹", "compose_form.sensitive.marked": "æ¤åª’é«”è¢«æ¨™è¨˜ç‚ºæ•æ„Ÿå…§å®¹", - "compose_form.sensitive.unmarked": "æ¤åª’é«”æœªè¢«æ¨™è¨˜ç‚ºæ•æ„Ÿå…§å®¹", - "compose_form.spoiler.marked": "æ£æ–‡å·²éš±è—在è¦å‘Šä¹‹å¾Œ", + "compose_form.sensitive.unmarked": "æ¤åª’é«”æœªæ¨™è¨˜ç‚ºæ•æ„Ÿå…§å®¹", + "compose_form.spoiler.marked": "æ£æ–‡å·²éš±è—到è¦å‘Šä¹‹å¾Œ", "compose_form.spoiler.unmarked": "æ£æ–‡æœªè¢«éš±è—", "compose_form.spoiler_placeholder": "請在æ¤è™•寫入è¦å‘Šè¨Šæ¯", "confirmation_modal.cancel": "å–æ¶ˆ", - "confirmations.block.block_and_report": "Block & Report", + "confirmations.block.block_and_report": "å°éŽ–ä¸¦æª¢èˆ‰", "confirmations.block.confirm": "å°éŽ–", - "confirmations.block.message": "ä½ ç¢ºå®šè¦å°éŽ– {name} ?", + "confirmations.block.message": "確定å°éŽ– {name} ?", "confirmations.delete.confirm": "刪除", "confirmations.delete.message": "ä½ ç¢ºå®šè¦åˆªé™¤é€™æ¢å˜Ÿæ–‡ï¼Ÿ", "confirmations.delete_list.confirm": "刪除", - "confirmations.delete_list.message": "ç¢ºå®šè¦æ°¸ä¹…刪除æ¤å單?", + "confirmations.delete_list.message": "確定永久刪除æ¤å單?", "confirmations.domain_block.confirm": "éš±è—æ•´å€‹ç¶²åŸŸ", - "confirmations.domain_block.message": "確定å°éŽ–æ•´å€‹ {domain} 嗎?多數情æ³ä¸‹ï¼Œå°éŽ–æˆ–éœéŸ³å¹¾å€‹ç‰¹å®šä½¿ç”¨è€…æ‡‰è©²å°±èƒ½æ»¿è¶³ä½ çš„éœ€æ±‚äº†ã€‚æ‚¨å°‡ä¸èƒ½åœ¨ä»»ä½•公開時間軸或通知ä¸çœ‹åˆ°ä¾†è‡ªè©²ç¶²åŸŸçš„內容。來自該網域的關注者將被移除。", + "confirmations.domain_block.message": "真的éžå¸¸ç¢ºå®šå°éŽ–æ•´å€‹ {domain} 嗎?大部分情æ³ä¸‹ï¼Œä½ åªéœ€è¦å°éŽ–æˆ–éœéŸ³å°‘æ•¸ç‰¹å®šçš„äººå°±èƒ½æ»¿è¶³éœ€æ±‚äº†ã€‚ä½ å°‡ä¸èƒ½åœ¨ä»»ä½•公開的時間軸åŠé€šçŸ¥ä¸çœ‹åˆ°é‚£å€‹ç¶²åŸŸçš„å…§å®¹ã€‚ä½ ä¾†è‡ªè©²ç¶²åŸŸçš„é—œæ³¨è€…ä¹Ÿæœƒè¢«ç§»é™¤ã€‚", + "confirmations.logout.confirm": "登出", + "confirmations.logout.message": "確定è¦ç™»å‡ºå—Žï¼Ÿ", "confirmations.mute.confirm": "éœéŸ³", + "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.message": "確定éœéŸ³ {name} ?", "confirmations.redraft.confirm": "åˆªé™¤ä¸¦é‡æ–°ç·¨è¼¯", - "confirmations.redraft.message": "ä½ ç¢ºå®šè¦åˆªé™¤é€™æ¢å˜Ÿæ–‡ä¸¦é‡æ–°ç·¨è¼¯å®ƒå—Žï¼Ÿé€™éº¼åšå°‡å¤±åŽ»è½‰å˜Ÿå’Œæœ€æ„›ï¼Œè€Œå°åŽŸå§‹å˜Ÿæ–‡çš„å›žè¦†å°‡è¢«å¤ç«‹ã€‚", + "confirmations.redraft.message": "ç¢ºå®šåˆªæŽ‰é€™å‰‡å˜Ÿæ–‡ä¸¦é‡æ–°ç·¨è¼¯å—Žï¼Ÿå°‡æœƒå¤±åŽ»é€™å‰‡å˜Ÿæ–‡çš„è½‰å˜ŸåŠæ”¶è—,且回覆這則的嘟文將會變æˆç¨ç«‹çš„嘟文。", "confirmations.reply.confirm": "回覆", "confirmations.reply.message": "ç¾åœ¨å›žè¦†å°‡è“‹æŽ‰æ‚¨ç›®å‰æ£åœ¨æ’°å¯«çš„訊æ¯ã€‚是å¦ä»è¦å›žè¦†ï¼Ÿ", "confirmations.unfollow.confirm": "å–æ¶ˆé—œæ³¨", "confirmations.unfollow.message": "真的è¦å–消關注 {name} 嗎?", - "embed.instructions": "è¦åµŒå…¥æ¤å˜Ÿæ–‡ï¼Œè«‹å°‡ä»¥ä¸‹ä»£ç¢¼è²¼é€²ä½ 的網站。", + "conversation.delete": "刪除å°è©±", + "conversation.mark_as_read": "標為已讀", + "conversation.open": "檢視å°è©±", + "conversation.with": "與 {names}", + "directory.federated": "來自已知è¯é‚¦å®‡å®™", + "directory.local": "僅來自 {domain}", + "directory.new_arrivals": "新貨", + "directory.recently_active": "最近活èº", + "embed.instructions": "è¦åµŒå…¥æ¤å˜Ÿæ–‡ï¼Œè«‹å°‡ä»¥ä¸‹ç¨‹å¼ç¢¼è²¼é€²ä½ 的網站。", "embed.preview": "他會顯示æˆé€™æ¨£ï¼š", "emoji_button.activity": "活動", "emoji_button.custom": "自訂", @@ -109,7 +127,7 @@ "emoji_button.food": "飲食", "emoji_button.label": "æ’入表情符號", "emoji_button.nature": "大自然", - "emoji_button.not_found": "就沒這表情符號å¼ï¼ï¼ (╯°□°)╯︵ â”»â”â”»", + "emoji_button.not_found": "啊就沒這表情符號å¼ï¼ï¼ (╯°□°)╯︵ â”»â”â”»", "emoji_button.objects": "物件", "emoji_button.people": "使用者", "emoji_button.recent": "最常使用", @@ -118,22 +136,26 @@ "emoji_button.symbols": "符號", "emoji_button.travel": "æ—…éŠèˆ‡åœ°é»ž", "empty_column.account_timeline": "這裡還沒有嘟文ï¼", - "empty_column.account_unavailable": "Profile unavailable", + "empty_column.account_unavailable": "無法å–得個人資料", "empty_column.blocks": "ä½ é‚„æ²’æœ‰å°éŽ–ä»»ä½•ä½¿ç”¨è€…ã€‚", "empty_column.community": "本地時間軸是空的。快公開嘟些文æ¶é 香啊ï¼", "empty_column.direct": "您還沒有任何ç§è¨Šã€‚當您ç§è¨Šåˆ¥äººæˆ–收到ç§è¨Šæ™‚,它將於æ¤é¡¯ç¤ºã€‚", "empty_column.domain_blocks": "尚未隱è—任何網域。", - "empty_column.favourited_statuses": "ä½ é‚„æ²’æœ‰å°‡ä»»ä½•å˜Ÿæ–‡æ¨™ç‚ºæœ€æ„›ã€‚æœ€æ„›çš„å˜Ÿæ–‡å°‡é¡¯ç¤ºæ–¼æ¤ã€‚", - "empty_column.favourites": "還沒有人將æ¤å˜Ÿæ–‡æ¨™ç‚ºæœ€æ„›ã€‚å¦‚æžœæœ‰äººæ¨™æˆæœ€æ„›ï¼Œå‰‡æœƒé¡¯ç¤ºåœ¨é€™è£¡ã€‚", - "empty_column.follow_requests": "您尚未收到任何關注請求。收到時會顯示於æ¤ã€‚", - "empty_column.hashtag": "這個「#ã€æ¨™ç±¤ä¸‹ä»€éº¼éƒ½æ²’有。", + "empty_column.favourited_statuses": "ä½ é‚„æ²’æ”¶è—ä»»ä½•å˜Ÿæ–‡ã€‚é€™è£¡å°‡æœƒé¡¯ç¤ºä½ æ”¶è—的嘟文。", + "empty_column.favourites": "還沒有人收è—這則嘟文。這裡將會顯示被收è—的嘟文。", + "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。", + "empty_column.hashtag": "這個主題標籤下什麼也沒有。", "empty_column.home": "æ‚¨çš„é¦–é æ™‚間軸是空的ï¼å‰å¾€ {public} 或使用æœå°‹åŠŸèƒ½ä¾†èªè˜å…¶ä»–人。", "empty_column.home.public_timeline": "公開時間軸", - "empty_column.list": "æ¤ä»½å單還沒有æ±è¥¿ã€‚ç•¶æ¤å單的æˆå“¡å˜Ÿå‡ºäº†æ–°çš„嘟文時,它們就會出ç¾åœ¨é€™è£¡ã€‚", - "empty_column.lists": "ä½ é‚„æ²’æœ‰å»ºç«‹ä»»ä½•åå–®ã€‚ä½ å»ºç«‹çš„å單將會顯示在這裡。", - "empty_column.mutes": "ä½ é‚„æ²’æœ‰éœéŸ³ä»»ä½•使用者。", + "empty_column.list": "這份å單還沒有æ±è¥¿ã€‚ç•¶æ¤å單的æˆå“¡å˜Ÿå‡ºäº†æ–°çš„嘟文時,它們就會顯示於æ¤ã€‚", + "empty_column.lists": "ä½ é‚„æ²’æœ‰å»ºç«‹ä»»ä½•åå–®ã€‚é€™è£¡å°‡æœƒé¡¯ç¤ºä½ æ‰€å»ºç«‹çš„å單。", + "empty_column.mutes": "ä½ å°šæœªéœéŸ³ä»»ä½•使用者。", "empty_column.notifications": "您尚未收到任何通知,和別人互動開啟å°è©±å§ã€‚", "empty_column.public": "這裡什麼都沒有ï¼å˜—試寫些公開的嘟文,或著自己關注其他伺æœå™¨çš„使用者後就會有嘟文出ç¾äº†", + "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", + "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", + "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard", + "errors.unexpected_crash.report_issue": "Report issue", "federation.change": "Adjust status federation", "federation.federated.long": "Allow toot to reach other instances", "federation.federated.short": "Federated", @@ -151,72 +173,72 @@ "getting_started.terms": "æœå‹™æ¢æ¬¾", "hashtag.column_header.tag_mode.all": "以åŠ{additional}", "hashtag.column_header.tag_mode.any": "或是{additional}", - "hashtag.column_header.tag_mode.none": "而ä¸ç”¨{additional}", + "hashtag.column_header.tag_mode.none": "而無需{additional}", "hashtag.column_settings.select.no_options_message": "找ä¸åˆ°å»ºè°", - "hashtag.column_settings.select.placeholder": "輸入「#ã€æ¨™ç±¤â€¦", + "hashtag.column_settings.select.placeholder": "輸入主題標籤…", "hashtag.column_settings.tag_mode.all": "全部", "hashtag.column_settings.tag_mode.any": "任一", - "hashtag.column_settings.tag_mode.none": "全都ä¸è¦", - "hashtag.column_settings.tag_toggle": "å°æ¤æ¬„ä½åŠ å…¥é¡å¤–標籤", + "hashtag.column_settings.tag_mode.none": "å…¨ä¸", + "hashtag.column_settings.tag_toggle": "å°‡é¡å¤–æ¨™ç±¤åŠ å…¥åˆ°é€™å€‹æ¬„ä½", "home.column_settings.basic": "基本", - "home.column_settings.show_reblogs": "顯示轉推", + "home.column_settings.show_reblogs": "顯示轉嘟", "home.column_settings.show_replies": "顯示回覆", "intervals.full.days": "{number, plural, one {# 天} other {# 天}}", "intervals.full.hours": "{number, plural, one {# å°æ™‚} other {# å°æ™‚}}", "intervals.full.minutes": "{number, plural, one {# 分é˜} other {# 分é˜}}", "introduction.federation.action": "下一æ¥", - "introduction.federation.federated.headline": "è¯é‚¦", - "introduction.federation.federated.text": "來自è¯é‚¦ç¶²è·¯ä¸å…¶ä»–伺æœå™¨çš„公開嘟文將會在è¯é‚¦ç¶²è·¯æ™‚間軸ä¸é¡¯ç¤ºã€‚", + "introduction.federation.federated.headline": "ç«™å°è¯ç›Ÿ", + "introduction.federation.federated.text": "來自è¯ç›Ÿå®‡å®™ä¸å…¶ä»–ç«™å°çš„公開嘟文將會在站點è¯ç›Ÿæ™‚間軸ä¸é¡¯ç¤ºã€‚", "introduction.federation.home.headline": "首é ", - "introduction.federation.home.text": "您所關注使用者所發的嘟文將顯示在首é 的訊æ¯ä¾†æºã€‚您能關注任何伺æœå™¨ä¸Šçš„任何人ï¼", - "introduction.federation.local.headline": "本地", - "introduction.federation.local.text": "跟您åŒä¼ºæœå™¨ä¹‹ä½¿ç”¨è€…所發的公開嘟文將會顯示在本地時間軸ä¸ã€‚", + "introduction.federation.home.text": "ä½ é—œæ³¨ä½¿ç”¨è€…çš„å˜Ÿæ–‡å°‡æœƒåœ¨é¦–é å‹•æ…‹ä¸é¡¯ç¤ºã€‚ä½ å¯ä»¥é—œæ³¨ä»»ä½•伺æœå™¨ä¸Šçš„任何人ï¼", + "introduction.federation.local.headline": "本機", + "introduction.federation.local.text": "跟您åŒä¼ºæœå™¨ä¹‹ä½¿ç”¨è€…所發的公開嘟文將會顯示在本機時間軸ä¸ã€‚", "introduction.interactions.action": "å®Œæˆæ•™å¸ï¼", - "introduction.interactions.favourite.headline": "最愛", - "introduction.interactions.favourite.text": "您能ç¨å€™å„²å˜å˜Ÿæ–‡ï¼Œæˆ–è€…å°‡å˜Ÿæ–‡åŠ åˆ°æœ€æ„›ï¼Œè®“ä½œè€…çŸ¥é“æ‚¨å–œæ¡é€™å˜Ÿæ–‡ã€‚", + "introduction.interactions.favourite.headline": "關注", + "introduction.interactions.favourite.text": "您能儲å˜å˜Ÿæ–‡ä¾›ç¨å€™è§€çœ‹ï¼Œæˆ–者收è—å˜Ÿæ–‡ï¼Œè®“ä½œè€…çŸ¥é“æ‚¨å–œæ¡é€™å‰‡å˜Ÿæ–‡ã€‚", "introduction.interactions.reblog.headline": "轉嘟", - "introduction.interactions.reblog.text": "您能é€éŽè½‰å˜Ÿä»–人嘟文來分享給您的關注者。", + "introduction.interactions.reblog.text": "您能藉由轉嘟他人嘟文來分享給您的關注者。", "introduction.interactions.reply.headline": "回覆", - "introduction.interactions.reply.text": "您能回覆其他人或自己的嘟文。將會把這些回覆串æˆä¸€ä¸²å°è©±ã€‚", - "introduction.welcome.action": "é–‹å§‹ï¼", + "introduction.interactions.reply.text": "æ‚¨èƒ½å›žè¦†å…¶ä»–äººæˆ–è‡ªå·±çš„å˜Ÿæ–‡ï¼Œé€™éº¼åšæœƒæŠŠé€™äº›å›žè¦†ä¸²æˆä¸€ä¸²å°è©±ã€‚", + "introduction.welcome.action": "開始旅程å§ï¼", "introduction.welcome.headline": "第一æ¥", - "introduction.welcome.text": "æ¡è¿Žä¾†åˆ°è¯é‚¦ï¼ç¨å€™æ‚¨å°‡å¯ä»¥å»£æ’訊æ¯ä¸¦è·¨å„種å„å¼å„樣的伺æœå™¨èˆ‡æœ‹å‹èŠå¤©ã€‚但這å°ä¼ºæœå™¨ï¼Œ{domain},å分特殊 -- 它寄管了您的個人資料,所以請記ä½é€™å°ä¼ºæœå™¨çš„å稱。", + "introduction.welcome.text": "æ¡è¿Žä¾†åˆ°è¯ç›Ÿå®‡å®™ï¼ç‰ç‰ä½ å°±å¯ä»¥å»£æ’訊æ¯åŠè·¨è¶Šå„種å„å¼å„樣的伺æœå™¨èˆ‡æœ‹å‹èŠå¤©ã€‚但這å°ä¼ºæœå™¨ï¼Œ{domain},éžå¸¸ç‰¹åˆ¥ - å®ƒå¯„ç®¡äº†ä½ çš„å€‹äººè³‡æ–™ï¼Œæ‰€ä»¥è«‹è¨˜ä½å®ƒçš„åå—。", "keyboard_shortcuts.back": "返回上一é ", - "keyboard_shortcuts.blocked": "開啟「å°éŽ–çš„ä½¿ç”¨è€…ã€åå–®", + "keyboard_shortcuts.blocked": "開啟「å°éŽ–ä½¿ç”¨è€…ã€åå–®", "keyboard_shortcuts.boost": "轉嘟", "keyboard_shortcuts.column": "將焦點放在其ä¸ä¸€æ¬„的嘟文", "keyboard_shortcuts.compose": "將焦點移至撰寫文å—å€å¡Š", "keyboard_shortcuts.description": "æè¿°", "keyboard_shortcuts.direct": "開啟ç§è¨Šæ¬„", - "keyboard_shortcuts.down": "在åå–®ä¸å¾€ä¸‹ç§»å‹•", + "keyboard_shortcuts.down": "往下移動åå–®é …ç›®", "keyboard_shortcuts.enter": "檢視嘟文", - "keyboard_shortcuts.favourite": "åŠ å…¥æœ€æ„›", - "keyboard_shortcuts.favourites": "開啟最愛åå–®", - "keyboard_shortcuts.federated": "開啟è¯é‚¦æ™‚間軸", + "keyboard_shortcuts.favourite": "æ”¶è—", + "keyboard_shortcuts.favourites": "開啟收è—åå–®", + "keyboard_shortcuts.federated": "開啟站點è¯ç›Ÿæ™‚間軸", "keyboard_shortcuts.heading": "éµç›¤å¿«é€Ÿéµ", "keyboard_shortcuts.home": "é–‹å•Ÿé¦–é æ™‚間軸", "keyboard_shortcuts.hotkey": "快速éµ", - "keyboard_shortcuts.legend": "顯示æ¤èªªæ˜Ž", - "keyboard_shortcuts.local": "開啟本地時間軸", + "keyboard_shortcuts.legend": "顯示æ¤åˆ—表", + "keyboard_shortcuts.local": "開啟本機時間軸", "keyboard_shortcuts.mention": "æåŠä½œè€…", "keyboard_shortcuts.muted": "開啟éœéŸ³ä½¿ç”¨è€…åå–®", "keyboard_shortcuts.my_profile": "開啟個人資料é é¢", "keyboard_shortcuts.notifications": "開啟通知欄", "keyboard_shortcuts.pinned": "開啟釘é¸çš„嘟文åå–®", - "keyboard_shortcuts.profile": "開啟作者的個人資料é ", - "keyboard_shortcuts.reply": "回應嘟文", + "keyboard_shortcuts.profile": "開啟作者的個人資料é é¢", + "keyboard_shortcuts.reply": "回覆", "keyboard_shortcuts.requests": "開啟關注請求åå–®", "keyboard_shortcuts.search": "將焦點移至æœå°‹æ¡†", "keyboard_shortcuts.start": "é–‹å•Ÿã€Œé–‹å§‹ä½¿ç”¨ã€æ¬„ä½", "keyboard_shortcuts.toggle_hidden": "顯示/éš±è—在內容è¦å‘Šä¹‹å¾Œçš„æ£æ–‡", - "keyboard_shortcuts.toggle_sensitivity": "to show/hide media", + "keyboard_shortcuts.toggle_sensitivity": "顯示 / éš±è—媒體", "keyboard_shortcuts.toot": "開始發出新嘟文", "keyboard_shortcuts.unfocus": "å–æ¶ˆè¼¸å…¥æ–‡å—å€å¡Š / æœå°‹çš„焦點", - "keyboard_shortcuts.up": "在åå–®ä¸å¾€ä¸Šç§»å‹•", + "keyboard_shortcuts.up": "往上移動åå–®é …ç›®", "lightbox.close": "關閉", "lightbox.next": "下一æ¥", "lightbox.previous": "上一æ¥", - "lightbox.view_context": "View context", + "lightbox.view_context": "檢視內文", "lists.account.add": "新增至åå–®", "lists.account.remove": "從åå–®ä¸ç§»é™¤", "lists.delete": "刪除åå–®", @@ -226,23 +248,24 @@ "lists.new.title_placeholder": "æ–°å單標題", "lists.search": "æœå°‹æ‚¨é—œæ³¨çš„使用者", "lists.subheading": "您的åå–®", + "load_pending": "{count, plural, other {# å€‹æ–°é …ç›®}}", "loading_indicator.label": "讀å–ä¸...", "media_gallery.toggle_visible": "切æ›å¯è¦‹æ€§", "missing_indicator.label": "找ä¸åˆ°", "missing_indicator.sublabel": "找ä¸åˆ°æ¤è³‡æº", - "mute_modal.hide_notifications": "éš±è—來自這個使用者的通知?", - "navigation_bar.apps": "行動應用程å¼", - "navigation_bar.blocks": "å°éŽ–çš„ä½¿ç”¨è€…", - "navigation_bar.community_timeline": "本地時間軸", + "mute_modal.hide_notifications": "éš±è—來自這ä½ä½¿ç”¨è€…的通知?", + "navigation_bar.apps": "å°éŽ–çš„ä½¿ç”¨è€…", + "navigation_bar.blocks": "å°éŽ–ä½¿ç”¨è€…", + "navigation_bar.community_timeline": "本機時間軸", "navigation_bar.compose": "撰寫新嘟文", "navigation_bar.direct": "ç§è¨Š", "navigation_bar.discover": "探索", "navigation_bar.domain_blocks": "éš±è—的網域", "navigation_bar.edit_profile": "編輯個人資料", - "navigation_bar.favourites": "最愛內容", + "navigation_bar.favourites": "æ”¶è—", "navigation_bar.filters": "éœéŸ³è©žå½™", "navigation_bar.follow_requests": "關注請求", - "navigation_bar.follows_and_followers": "Follows and followers", + "navigation_bar.follows_and_followers": "關注åŠé—œæ³¨è€…", "navigation_bar.info": "關於æ¤ä¼ºæœå™¨", "navigation_bar.keyboard_shortcuts": "快速éµ", "navigation_bar.lists": "åå–®", @@ -251,7 +274,6 @@ "navigation_bar.personal": "個人", "navigation_bar.pins": "釘é¸çš„嘟文", "navigation_bar.preferences": "å好è¨å®š", - "navigation_bar.profile_directory": "Profile directory", "navigation_bar.public_timeline": "è¯é‚¦æ™‚間軸", "navigation_bar.security": "安全性", "notification.favourite": "{name} æŠŠä½ çš„å˜Ÿæ–‡åŠ å…¥äº†æœ€æ„›", @@ -282,8 +304,10 @@ "notifications.group": "{count} æ¢é€šçŸ¥", "poll.closed": "已關閉", "poll.refresh": "釿–°æ•´ç†", + "poll.total_people": "{count, plural, one {# person} other {# people}}", "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}", "poll.vote": "投票", + "poll.voted": "ä½ å·²å°æ¤å•題投票", "poll_button.add_poll": "建立投票", "poll_button.remove_poll": "移除投票", "privacy.change": "調整隱ç§ç‹€æ…‹", @@ -295,6 +319,7 @@ "privacy.public.short": "公開", "privacy.unlisted.long": "å…¬é–‹ï¼Œä½†ä¸æœƒé¡¯ç¤ºåœ¨å…¬é–‹æ™‚間軸", "privacy.unlisted.short": "ä¸å…¬é–‹", + "refresh": "Refresh", "regeneration_indicator.label": "載入ä¸â€¦", "regeneration_indicator.sublabel": "ä½ çš„ä¸»é æ™‚間軸æ£åœ¨æº–å‚™ä¸!", "relative_time.days": "{number} 天", @@ -319,6 +344,7 @@ "search_results.accounts": "使用者", "search_results.hashtags": "主題標籤", "search_results.statuses": "嘟文", + "search_results.statuses_fts_disabled": "「ä¾å…§å®¹æœå°‹å˜Ÿæ–‡ã€æœªåœ¨æ¤ Mastodon 伺æœå™¨å•Ÿç”¨ã€‚", "search_results.total": "{count, number} é …çµæžœ", "status.admin_account": "開啟 @{name} 的管ç†ä»‹é¢", "status.admin_status": "在管ç†ä»‹é¢é–‹å•Ÿæ¤å˜Ÿæ–‡", @@ -358,6 +384,7 @@ "status.show_more": "顯示更多", "status.show_more_all": "顯示更多這類嘟文", "status.show_thread": "顯示討論串", + "status.uncached_media_warning": "無法使用", "status.unmute_conversation": "解除æ¤å°è©±çš„éœéŸ³", "status.unpin": "è§£é™¤ç½®é ‚", "suggestions.dismiss": "關閉建è°", @@ -373,14 +400,22 @@ "time_remaining.moments": "剩餘時間", "time_remaining.seconds": "剩餘 {number, plural, one {# ç§’} other {# ç§’}}", "trends.count_by_accounts": "{count} ä½ä½¿ç”¨è€…在討論", + "trends.trending_now": "ç›®å‰è¶¨å‹¢", "ui.beforeunload": "如果離開 Mastodonï¼Œä½ çš„è‰ç¨¿å°‡æœƒä¸è¦‹ã€‚", "upload_area.title": "拖放來上傳", "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_error.limit": "å·²é”到檔案上傳é™åˆ¶ã€‚", "upload_error.poll": "ä¸å…許在投票上傳檔案。", "upload_form.description": "ç‚ºè¦–éšœäººå£«å¢žåŠ æ–‡å—說明", - "upload_form.focus": "變更é 覽", + "upload_form.edit": "編輯", "upload_form.undo": "刪除", + "upload_modal.analyzing_picture": "æ£åœ¨åˆ†æžåœ–片…", + "upload_modal.apply": "套用", + "upload_modal.description_placeholder": "A quick brown fox è·³éŽé‚£é𻿇¶ç‹—", + "upload_modal.detect_text": "å¾žåœ–ç‰‡åµæ¸¬æ–‡å—", + "upload_modal.edit_media": "編輯媒體", + "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.", + "upload_modal.preview_label": "é 覽 ({ratio})", "upload_progress.label": "上傳ä¸...", "video.close": "關閉影片", "video.exit_fullscreen": "退出全螢幕", diff --git a/app/javascript/mastodon/reducers/alerts.js b/app/javascript/mastodon/reducers/alerts.js index 089d920c3e4eeaf786195df9da8bbcfa0e69afe5..c62ab0dfdb9c801fbf38efcea560d2a97c4f2001 100644 --- a/app/javascript/mastodon/reducers/alerts.js +++ b/app/javascript/mastodon/reducers/alerts.js @@ -14,6 +14,7 @@ export default function alerts(state = initialState, action) { key: state.size > 0 ? state.last().get('key') + 1 : 0, title: action.title, message: action.message, + message_values: action.message_values, })); case ALERT_DISMISS: return state.filterNot(item => item.get('key') === action.alert.key); diff --git a/app/javascript/mastodon/reducers/blocks.js b/app/javascript/mastodon/reducers/blocks.js new file mode 100644 index 0000000000000000000000000000000000000000..1b65071634fb16f24b897680ebe6106dba242282 --- /dev/null +++ b/app/javascript/mastodon/reducers/blocks.js @@ -0,0 +1,22 @@ +import Immutable from 'immutable'; + +import { + BLOCKS_INIT_MODAL, +} from '../actions/blocks'; + +const initialState = Immutable.Map({ + new: Immutable.Map({ + account_id: null, + }), +}); + +export default function mutes(state = initialState, action) { + switch (action.type) { + case BLOCKS_INIT_MODAL: + return state.withMutations((state) => { + state.setIn(['new', 'account_id'], action.account.get('id')); + }); + default: + return state; + } +} diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 77353528281f8e01af9dc8e2c43a8abf177137ac..8692cb25467a2e45c3039d12ac7e73ab90381847 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -107,10 +107,13 @@ function clearAll(state) { }); }; -function appendMedia(state, media) { +function appendMedia(state, media, file) { const prevSize = state.get('media_attachments').size; return state.withMutations(map => { + if (media.get('type') === 'image') { + media = media.set('file', file); + } map.update('media_attachments', list => list.push(media)); map.set('is_uploading', false); map.set('resetFileKey', Math.floor((Math.random() * 0x10000))); @@ -148,15 +151,20 @@ const insertSuggestion = (state, position, token, completion, path) => { }); }; -const updateSuggestionTags = (state, token) => { - const prefix = token.slice(1); +const sortHashtagsByUse = (state, tags) => { + const personalHistory = state.get('tagHistory'); - return state.merge({ - suggestions: state.get('tagHistory') - .filter(tag => tag.toLowerCase().startsWith(prefix.toLowerCase())) - .slice(0, 4) - .map(tag => '#' + tag), - suggestion_token: token, + return tags.sort((a, b) => { + const usedA = personalHistory.includes(a.name); + const usedB = personalHistory.includes(b.name); + + if (usedA === usedB) { + return 0; + } else if (usedA && !usedB) { + return -1; + } else { + return 1; + } }); }; @@ -205,6 +213,36 @@ const expiresInFromExpiresAt = expires_at => { return [300, 1800, 3600, 21600, 86400, 259200, 604800].find(expires_in => expires_in >= delta) || 24 * 3600; }; +const mergeLocalHashtagResults = (suggestions, prefix, tagHistory) => { + prefix = prefix.toLowerCase(); + if (suggestions.length < 4) { + const localTags = tagHistory.filter(tag => tag.toLowerCase().startsWith(prefix) && !suggestions.some(suggestion => suggestion.type === 'hashtag' && suggestion.name.toLowerCase() === tag.toLowerCase())); + return suggestions.concat(localTags.slice(0, 4 - suggestions.length).toJS().map(tag => ({ type: 'hashtag', name: tag }))); + } else { + return suggestions; + } +}; + +const normalizeSuggestions = (state, { accounts, emojis, tags, token }) => { + if (accounts) { + return accounts.map(item => ({ id: item.id, type: 'account' })); + } else if (emojis) { + return emojis.map(item => ({ ...item, type: 'emoji' })); + } else { + return mergeLocalHashtagResults(sortHashtagsByUse(state, tags.map(item => ({ ...item, type: 'hashtag' }))), token.slice(1), state.get('tagHistory')); + } +}; + +const updateSuggestionTags = (state, token) => { + const prefix = token.slice(1); + + const suggestions = state.get('suggestions').toJS(); + return state.merge({ + suggestions: ImmutableList(mergeLocalHashtagResults(suggestions, prefix, state.get('tagHistory'))), + suggestion_token: token, + }); +}; + export default function compose(state = initialState, action) { switch(action.type) { case STORE_HYDRATE: @@ -296,7 +334,7 @@ export default function compose(state = initialState, action) { case COMPOSE_UPLOAD_REQUEST: return state.set('is_uploading', true); case COMPOSE_UPLOAD_SUCCESS: - return appendMedia(state, fromJS(action.media)); + return appendMedia(state, fromJS(action.media), action.file); case COMPOSE_UPLOAD_FAIL: return state.set('is_uploading', false); case COMPOSE_UPLOAD_UNDO: @@ -321,7 +359,7 @@ export default function compose(state = initialState, action) { case COMPOSE_SUGGESTIONS_CLEAR: return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null); case COMPOSE_SUGGESTIONS_READY: - return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token); + return state.set('suggestions', ImmutableList(normalizeSuggestions(state, action))).set('suggestion_token', action.token); case COMPOSE_SUGGESTION_SELECT: return insertSuggestion(state, action.position, action.token, action.completion, action.path); case COMPOSE_SUGGESTION_TAGS_UPDATE: diff --git a/app/javascript/mastodon/reducers/conversations.js b/app/javascript/mastodon/reducers/conversations.js index 39065823928ae2d8e1f0fe0984ce8cc73cc04d7f..975418eda2b026f29f51dc41264fb64894a93a8c 100644 --- a/app/javascript/mastodon/reducers/conversations.js +++ b/app/javascript/mastodon/reducers/conversations.js @@ -7,6 +7,7 @@ import { CONVERSATIONS_FETCH_FAIL, CONVERSATIONS_UPDATE, CONVERSATIONS_READ, + CONVERSATIONS_DELETE_SUCCESS, } from '../actions/conversations'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'mastodon/actions/accounts'; import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks'; @@ -107,6 +108,8 @@ export default function conversations(state = initialState, action) { return filterConversations(state, [action.relationship.id]); case DOMAIN_BLOCK_SUCCESS: return filterConversations(state, action.accounts); + case CONVERSATIONS_DELETE_SUCCESS: + return state.update('items', list => list.filterNot(item => item.get('id') === action.id)); default: return state; } diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index 981ad8e64cfd682bfe94eba5c5504464a34fb639..b8d60888815471295d95a174bca4212bb2c5e73c 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -15,6 +15,7 @@ import settings from './settings'; import push_notifications from './push_notifications'; import status_lists from './status_lists'; import mutes from './mutes'; +import blocks from './blocks'; import reports from './reports'; import contexts from './contexts'; import compose from './compose'; @@ -31,6 +32,8 @@ import conversations from './conversations'; import suggestions from './suggestions'; import polls from './polls'; import identity_proofs from './identity_proofs'; +import trends from './trends'; +import missed_updates from './missed_updates'; const reducers = { dropdown_menu, @@ -49,6 +52,7 @@ const reducers = { settings, push_notifications, mutes, + blocks, reports, contexts, compose, @@ -65,6 +69,8 @@ const reducers = { conversations, suggestions, polls, + trends, + missed_updates, }; export default combineReducers(reducers); diff --git a/app/javascript/mastodon/reducers/missed_updates.js b/app/javascript/mastodon/reducers/missed_updates.js new file mode 100644 index 0000000000000000000000000000000000000000..b71d62d8287a897f8768ab523a3b18ec92f9ce17 --- /dev/null +++ b/app/javascript/mastodon/reducers/missed_updates.js @@ -0,0 +1,21 @@ +import { Map as ImmutableMap } from 'immutable'; +import { NOTIFICATIONS_UPDATE } from 'mastodon/actions/notifications'; +import { APP_FOCUS, APP_UNFOCUS } from 'mastodon/actions/app'; + +const initialState = ImmutableMap({ + focused: true, + unread: 0, +}); + +export default function missed_updates(state = initialState, action) { + switch(action.type) { + case APP_FOCUS: + return state.set('focused', true).set('unread', 0); + case APP_UNFOCUS: + return state.set('focused', false); + case NOTIFICATIONS_UPDATE: + return state.get('focused') ? state : state.update('unread', x => x + 1); + default: + return state; + } +}; diff --git a/app/javascript/mastodon/reducers/mutes.js b/app/javascript/mastodon/reducers/mutes.js index a96232dbd2340f06df37bc4417beced856778015..4672e50974eaa2a1394b5df9a6c51bc15548e0fa 100644 --- a/app/javascript/mastodon/reducers/mutes.js +++ b/app/javascript/mastodon/reducers/mutes.js @@ -7,7 +7,6 @@ import { const initialState = Immutable.Map({ new: Immutable.Map({ - isSubmitting: false, account: null, notifications: true, }), @@ -17,7 +16,6 @@ export default function mutes(state = initialState, action) { switch (action.type) { case MUTES_INIT_MODAL: return state.withMutations((state) => { - state.setIn(['new', 'isSubmitting'], false); state.setIn(['new', 'account'], action.account); state.setIn(['new', 'notifications'], true); }); diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 33fe86e002e2e92a51ce148df9e4336dc577a3bd..6ba80bd6a5741118a2528cfbe1c07ffd60c2750d 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -6,6 +6,9 @@ import { NOTIFICATIONS_FILTER_SET, NOTIFICATIONS_CLEAR, NOTIFICATIONS_SCROLL_TOP, + NOTIFICATIONS_LOAD_PENDING, + NOTIFICATIONS_MOUNT, + NOTIFICATIONS_UNMOUNT, } from '../actions/notifications'; import { ACCOUNT_BLOCK_SUCCESS, @@ -17,9 +20,11 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import compareId from '../compare_id'; const initialState = ImmutableMap({ + pendingItems: ImmutableList(), items: ImmutableList(), hasMore: true, top: false, + mounted: false, unread: 0, isLoading: false, }); @@ -32,9 +37,13 @@ const notificationToMap = notification => ImmutableMap({ status: notification.status ? notification.status.id : null, }); -const normalizeNotification = (state, notification) => { +const normalizeNotification = (state, notification, usePendingItems) => { const top = state.get('top'); + if (usePendingItems || !state.get('pendingItems').isEmpty()) { + return state.update('pendingItems', list => list.unshift(notificationToMap(notification))).update('unread', unread => unread + 1); + } + if (!top) { state = state.update('unread', unread => unread + 1); } @@ -48,7 +57,7 @@ const normalizeNotification = (state, notification) => { }); }; -const expandNormalizedNotifications = (state, notifications, next) => { +const expandNormalizedNotifications = (state, notifications, next, isLoadingRecent, usePendingItems) => { let items = ImmutableList(); notifications.forEach((n, i) => { @@ -57,7 +66,9 @@ const expandNormalizedNotifications = (state, notifications, next) => { return state.withMutations(mutable => { if (!items.isEmpty()) { - mutable.update('items', list => { + usePendingItems = isLoadingRecent && (usePendingItems || !mutable.get('pendingItems').isEmpty()); + + mutable.update(usePendingItems ? 'pendingItems' : 'items', list => { const lastIndex = 1 + list.findLastIndex( item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id')) ); @@ -79,35 +90,39 @@ const expandNormalizedNotifications = (state, notifications, next) => { }; const filterNotifications = (state, accountIds) => { - return state.update('items', list => list.filterNot(item => item !== null && accountIds.includes(item.get('account')))); + const helper = list => list.filterNot(item => item !== null && accountIds.includes(item.get('account'))); + return state.update('items', helper).update('pendingItems', helper); }; const updateTop = (state, top) => { if (top) { - state = state.set('unread', 0); + state = state.set('unread', state.get('pendingItems').size); } return state.set('top', top); }; const deleteByStatus = (state, statusId) => { - return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId)); + const helper = list => list.filterNot(item => item !== null && item.get('status') === statusId); + return state.update('items', helper).update('pendingItems', helper); }; export default function notifications(state = initialState, action) { switch(action.type) { + case NOTIFICATIONS_LOAD_PENDING: + return state.update('items', list => state.get('pendingItems').concat(list.take(40))).set('pendingItems', ImmutableList()).set('unread', 0); case NOTIFICATIONS_EXPAND_REQUEST: return state.set('isLoading', true); case NOTIFICATIONS_EXPAND_FAIL: return state.set('isLoading', false); case NOTIFICATIONS_FILTER_SET: - return state.set('items', ImmutableList()).set('hasMore', true); + return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', true); case NOTIFICATIONS_SCROLL_TOP: return updateTop(state, action.top); case NOTIFICATIONS_UPDATE: - return normalizeNotification(state, action.notification); + return normalizeNotification(state, action.notification, action.usePendingItems); case NOTIFICATIONS_EXPAND_SUCCESS: - return expandNormalizedNotifications(state, action.notifications, action.next); + return expandNormalizedNotifications(state, action.notifications, action.next, action.isLoadingRecent, action.usePendingItems); case ACCOUNT_BLOCK_SUCCESS: return filterNotifications(state, [action.relationship.id]); case ACCOUNT_MUTE_SUCCESS: @@ -115,13 +130,17 @@ export default function notifications(state = initialState, action) { case DOMAIN_BLOCK_SUCCESS: return filterNotifications(state, action.accounts); case NOTIFICATIONS_CLEAR: - return state.set('items', ImmutableList()).set('hasMore', false); + return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false); case TIMELINE_DELETE: return deleteByStatus(state, action.id); case TIMELINE_DISCONNECT: return action.timeline === 'home' ? - state.update('items', items => items.first() ? items.unshift(null) : items) : + state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) : state; + case NOTIFICATIONS_MOUNT: + return state.set('mounted', true); + case NOTIFICATIONS_UNMOUNT: + return state.set('mounted', false); default: return state; } diff --git a/app/javascript/mastodon/reducers/search.js b/app/javascript/mastodon/reducers/search.js index 77b7f588c5864099516ff41cd8c1c6402a692b25..875b2d92b39d4ba6cd6ad2d9ca091c55729ac411 100644 --- a/app/javascript/mastodon/reducers/search.js +++ b/app/javascript/mastodon/reducers/search.js @@ -3,6 +3,7 @@ import { SEARCH_CLEAR, SEARCH_FETCH_SUCCESS, SEARCH_SHOW, + SEARCH_EXPAND_SUCCESS, } from '../actions/search'; import { COMPOSE_MENTION, @@ -42,6 +43,9 @@ export default function search(state = initialState, action) { statuses: ImmutableList(action.results.statuses.map(item => item.id)), hashtags: fromJS(action.results.hashtags), })).set('submitted', true).set('searchTerm', action.searchTerm); + case SEARCH_EXPAND_SUCCESS: + const results = action.searchType === 'hashtags' ? fromJS(action.results.hashtags) : action.results[action.searchType].map(item => item.id); + return state.updateIn(['results', action.searchType], list => list.concat(results)); default: return state; } diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index a0eea137f11057afdbe0c1019d01a0bbb36cae04..793a99f8f56f6beb069e2c2aad0f4766fe994b9d 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -10,10 +10,12 @@ import uuid from '../uuid'; const initialState = ImmutableMap({ saved: true, - onboarded: false, - skinTone: 1, + trends: ImmutableMap({ + show: true, + }), + home: ImmutableMap({ shows: ImmutableMap({ reblog: true, @@ -74,10 +76,6 @@ const initialState = ImmutableMap({ body: '', }), }), - - trends: ImmutableMap({ - show: true, - }), }); const defaultColumns = fromJS([ diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js index 309a95a19b837eeb7dcf85cf0eea406a25833855..0d7222e10a884267056367733ecf78c62babe48d 100644 --- a/app/javascript/mastodon/reducers/timelines.js +++ b/app/javascript/mastodon/reducers/timelines.js @@ -8,6 +8,7 @@ import { TIMELINE_SCROLL_TOP, TIMELINE_CONNECT, TIMELINE_DISCONNECT, + TIMELINE_LOAD_PENDING, } from '../actions/timelines'; import { ACCOUNT_BLOCK_SUCCESS, @@ -25,10 +26,11 @@ const initialTimeline = ImmutableMap({ top: true, isLoading: false, hasMore: true, + pendingItems: ImmutableList(), items: ImmutableList(), }); -const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => { +const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent, usePendingItems) => { return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { mMap.set('isLoading', false); mMap.set('isPartial', isPartial); @@ -38,7 +40,9 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is if (timeline.endsWith(':pinned')) { mMap.set('items', statuses.map(status => status.get('id'))); } else if (!statuses.isEmpty()) { - mMap.update('items', ImmutableList(), oldIds => { + usePendingItems = isLoadingRecent && (usePendingItems || !mMap.get('pendingItems').isEmpty()); + + mMap.update(usePendingItems ? 'pendingItems' : 'items', ImmutableList(), oldIds => { const newIds = statuses.map(status => status.get('id')); const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1; @@ -57,8 +61,17 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is })); }; -const updateTimeline = (state, timeline, status) => { - const top = state.getIn([timeline, 'top']); +const updateTimeline = (state, timeline, status, usePendingItems) => { + const top = state.getIn([timeline, 'top']); + + if (usePendingItems || !state.getIn([timeline, 'pendingItems']).isEmpty()) { + if (state.getIn([timeline, 'pendingItems'], ImmutableList()).includes(status.get('id')) || state.getIn([timeline, 'items'], ImmutableList()).includes(status.get('id'))) { + return state; + } + + return state.update(timeline, initialTimeline, map => map.update('pendingItems', list => list.unshift(status.get('id'))).update('unread', unread => unread + 1)); + } + const ids = state.getIn([timeline, 'items'], ImmutableList()); const includesId = ids.includes(status.get('id')); const unread = state.getIn([timeline, 'unread'], 0); @@ -78,8 +91,10 @@ const updateTimeline = (state, timeline, status) => { const deleteStatus = (state, id, accountId, references, exclude_account = null) => { state.keySeq().forEach(timeline => { - if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) - state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id)); + if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) { + const helper = list => list.filterNot(item => item === id); + state = state.updateIn([timeline, 'items'], helper).updateIn([timeline, 'pendingItems'], helper); + } }); // Remove reblogs of deleted status @@ -109,29 +124,31 @@ const filterTimelines = (state, relationship, statuses) => { return state; }; -const filterTimeline = (timeline, state, relationship, statuses) => - state.updateIn([timeline, 'items'], ImmutableList(), list => - list.filterNot(statusId => - statuses.getIn([statusId, 'account']) === relationship.id - )); +const filterTimeline = (timeline, state, relationship, statuses) => { + const helper = list => list.filterNot(statusId => statuses.getIn([statusId, 'account']) === relationship.id); + return state.updateIn([timeline, 'items'], ImmutableList(), helper).updateIn([timeline, 'pendingItems'], ImmutableList(), helper); +}; const updateTop = (state, timeline, top) => { return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { - if (top) mMap.set('unread', 0); + if (top) mMap.set('unread', mMap.get('pendingItems').size); mMap.set('top', top); })); }; export default function timelines(state = initialState, action) { switch(action.type) { + case TIMELINE_LOAD_PENDING: + return state.update(action.timeline, initialTimeline, map => + map.update('items', list => map.get('pendingItems').concat(list.take(40))).set('pendingItems', ImmutableList()).set('unread', 0)); case TIMELINE_EXPAND_REQUEST: return state.update(action.timeline, initialTimeline, map => map.set('isLoading', true)); case TIMELINE_EXPAND_FAIL: return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); case TIMELINE_EXPAND_SUCCESS: - return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent); + return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent, action.usePendingItems); case TIMELINE_UPDATE: - return updateTimeline(state, action.timeline, fromJS(action.status)); + return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); case TIMELINE_CLEAR: @@ -149,7 +166,7 @@ export default function timelines(state = initialState, action) { return state.update( action.timeline, initialTimeline, - map => map.set('online', false).update('items', items => items.first() ? items.unshift(null) : items) + map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) ); default: return state; diff --git a/app/javascript/mastodon/reducers/trends.js b/app/javascript/mastodon/reducers/trends.js new file mode 100644 index 0000000000000000000000000000000000000000..5cecc8fcab5f777423edcb9b3d0f9920ac792301 --- /dev/null +++ b/app/javascript/mastodon/reducers/trends.js @@ -0,0 +1,23 @@ +import { TRENDS_FETCH_REQUEST, TRENDS_FETCH_SUCCESS, TRENDS_FETCH_FAIL } from '../actions/trends'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; + +const initialState = ImmutableMap({ + items: ImmutableList(), + isLoading: false, +}); + +export default function trendsReducer(state = initialState, action) { + switch(action.type) { + case TRENDS_FETCH_REQUEST: + return state.set('isLoading', true); + case TRENDS_FETCH_SUCCESS: + return state.withMutations(map => { + map.set('items', fromJS(action.trends)); + map.set('isLoading', false); + }); + case TRENDS_FETCH_FAIL: + return state.set('isLoading', false); + default: + return state; + } +}; diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index 8db18c5dc66eac1b1ab73cfe88c7f7ef89cfff3a..08e94022f1ce63ffe7de9f750539d97de61da590 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -20,6 +20,14 @@ import { MUTES_FETCH_SUCCESS, MUTES_EXPAND_SUCCESS, } from '../actions/mutes'; +import { + DIRECTORY_FETCH_REQUEST, + DIRECTORY_FETCH_SUCCESS, + DIRECTORY_FETCH_FAIL, + DIRECTORY_EXPAND_REQUEST, + DIRECTORY_EXPAND_SUCCESS, + DIRECTORY_EXPAND_FAIL, +} from 'mastodon/actions/directory'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; const initialState = ImmutableMap({ @@ -74,6 +82,16 @@ export default function userLists(state = initialState, action) { return state.setIn(['mutes', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); case MUTES_EXPAND_SUCCESS: return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); + case DIRECTORY_FETCH_SUCCESS: + return state.setIn(['directory', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['directory', 'isLoading'], false); + case DIRECTORY_EXPAND_SUCCESS: + return state.updateIn(['directory', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['directory', 'isLoading'], false); + case DIRECTORY_FETCH_REQUEST: + case DIRECTORY_EXPAND_REQUEST: + return state.setIn(['directory', 'isLoading'], true); + case DIRECTORY_FETCH_FAIL: + case DIRECTORY_EXPAND_FAIL: + return state.setIn(['directory', 'isLoading'], false); default: return state; } diff --git a/app/javascript/mastodon/rtl.js b/app/javascript/mastodon/rtl.js index 00870a15d677b48498a70bd12f22fc6d745494b3..89bed6de88811a5be77b99fbee174c7276898f67 100644 --- a/app/javascript/mastodon/rtl.js +++ b/app/javascript/mastodon/rtl.js @@ -20,6 +20,7 @@ export function isRtl(text) { text = text.replace(/(?:^|[^\/\w])@([a-z0-9_]+(@[a-z0-9\.\-]+)?)/ig, ''); text = text.replace(/(?:^|[^\/\w])#([\S]+)/ig, ''); text = text.replace(/\s+/g, ''); + text = text.replace(/(\w\S+\.\w{2,}\S*)/g, ''); const matches = text.match(rtlChars); diff --git a/app/javascript/mastodon/scroll.js b/app/javascript/mastodon/scroll.js index 2af07e0fb1f04c727084e0b342d52a3d89b1a14a..84fe582699e9ddfd0547e4403dc30ad045a90262 100644 --- a/app/javascript/mastodon/scroll.js +++ b/app/javascript/mastodon/scroll.js @@ -26,5 +26,7 @@ const scroll = (node, key, target) => { }; }; -export const scrollRight = (node, position) => scroll(node, 'scrollLeft', position); -export const scrollTop = (node) => scroll(node, 'scrollTop', 0); +const isScrollBehaviorSupported = 'scrollBehavior' in document.documentElement.style; + +export const scrollRight = (node, position) => isScrollBehaviorSupported ? node.scrollTo({ left: position, behavior: 'smooth' }) : scroll(node, 'scrollLeft', position); +export const scrollTop = (node) => isScrollBehaviorSupported ? node.scrollTo({ top: 0, behavior: 'smooth' }) : scroll(node, 'scrollTop', 0); diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index c87654547d47feac1cb3c17a4fbec89514cd0b67..6f1ce9602a4986beee09fa21bbc5c5ad60f8771f 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -128,6 +128,7 @@ export const getAlerts = createSelector([getAlertsBase], (base) => { base.forEach(item => { arr.push({ message: item.get('message'), + message_values: item.get('message_values'), title: item.get('title'), key: item.get('key'), dismissAfter: 5000, diff --git a/app/javascript/mastodon/utils/log_out.js b/app/javascript/mastodon/utils/log_out.js new file mode 100644 index 0000000000000000000000000000000000000000..b43417f4b421263c27ca2cd25c9253702acc758a --- /dev/null +++ b/app/javascript/mastodon/utils/log_out.js @@ -0,0 +1,33 @@ +import Rails from 'rails-ujs'; + +export const logOut = () => { + const form = document.createElement('form'); + + const methodInput = document.createElement('input'); + methodInput.setAttribute('name', '_method'); + methodInput.setAttribute('value', 'delete'); + methodInput.setAttribute('type', 'hidden'); + form.appendChild(methodInput); + + const csrfToken = Rails.csrfToken(); + const csrfParam = Rails.csrfParam(); + + if (csrfParam && csrfToken) { + const csrfInput = document.createElement('input'); + csrfInput.setAttribute('name', csrfParam); + csrfInput.setAttribute('value', csrfToken); + csrfInput.setAttribute('type', 'hidden'); + form.appendChild(csrfInput); + } + + const submitButton = document.createElement('input'); + submitButton.setAttribute('type', 'submit'); + form.appendChild(submitButton); + + form.method = 'post'; + form.action = '/auth/sign_out'; + form.style.display = 'none'; + + document.body.appendChild(form); + submitButton.click(); +}; diff --git a/app/javascript/mastodon/utils/numbers.js b/app/javascript/mastodon/utils/numbers.js index fdd8269ae27870509808d51ecd054a1196e5cfd0..f7e4ceb9354e50ca340f3a20ca7750de1acb76aa 100644 --- a/app/javascript/mastodon/utils/numbers.js +++ b/app/javascript/mastodon/utils/numbers.js @@ -4,7 +4,9 @@ import { FormattedNumber } from 'react-intl'; export const shortNumberFormat = number => { if (number < 1000) { return <FormattedNumber value={number} />; - } else { + } else if (number < 1000000) { return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</Fragment>; + } else { + return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={1} />M</Fragment>; } }; diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js index a8ec5f3fa767286042c9da1084dca64e960584af..7196dc96b1e9b26ab3db09072188f294529e4249 100644 --- a/app/javascript/mastodon/utils/resize_image.js +++ b/app/javascript/mastodon/utils/resize_image.js @@ -31,7 +31,7 @@ const loadImage = inputFile => new Promise((resolve, reject) => { }); const getOrientation = (img, type = 'image/png') => new Promise(resolve => { - if (!['image/jpeg', 'image/webp'].includes(type)) { + if (type !== 'image/jpeg') { resolve(1); return; } @@ -71,7 +71,7 @@ const processImage = (img, { width, height, orientation, type = 'image/png' }) = // and return an all-white image instead. Assume reading failed if the resized // image is perfectly white. const imageData = context.getImageData(0, 0, width, height); - if (imageData.every(value => value === 255)) { + if (imageData.data.every(value => value === 255)) { throw 'Failed to read from canvas'; } diff --git a/app/javascript/mastodon/utils/scrollbar.js b/app/javascript/mastodon/utils/scrollbar.js new file mode 100644 index 0000000000000000000000000000000000000000..6f0ee010b5d02f9b8657e0009dcc88e90e9d4c35 --- /dev/null +++ b/app/javascript/mastodon/utils/scrollbar.js @@ -0,0 +1,36 @@ +import { isMobile } from '../is_mobile'; + +/** @type {number | null} */ +let cachedScrollbarWidth = null; + +/** + * @return {number} + */ +const getActualScrollbarWidth = () => { + const outer = document.createElement('div'); + outer.style.visibility = 'hidden'; + outer.style.overflow = 'scroll'; + document.body.appendChild(outer); + + const inner = document.createElement('div'); + outer.appendChild(inner); + + const scrollbarWidth = outer.offsetWidth - inner.offsetWidth; + outer.parentNode.removeChild(outer); + + return scrollbarWidth; +}; + +/** + * @return {number} + */ +export const getScrollbarWidth = () => { + if (cachedScrollbarWidth !== null) { + return cachedScrollbarWidth; + } + + const scrollbarWidth = isMobile(window.innerWidth) ? 0 : getActualScrollbarWidth(); + cachedScrollbarWidth = scrollbarWidth; + + return scrollbarWidth; +}; diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js index f0c0ee0b74eb10ccf67a8ed65c703ba89ae3893b..42c747d2efc50e379edee125c2717fdf5dbfb5d8 100644 --- a/app/javascript/packs/admin.js +++ b/app/javascript/packs/admin.js @@ -1,4 +1,5 @@ import { delegate } from 'rails-ujs'; +import ready from '../mastodon/ready'; const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]'; @@ -29,7 +30,7 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => { }); }); -delegate(document, '#domain_block_severity', 'change', ({ target }) => { +const onDomainBlockSeverityChange = (target) => { const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media'); const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports'); @@ -40,4 +41,11 @@ delegate(document, '#domain_block_severity', 'change', ({ target }) => { if (rejectReportsDiv) { rejectReportsDiv.style.display = (target.value === 'suspend') ? 'none' : 'block'; } +}; + +delegate(document, '#domain_block_severity', 'change', ({ target }) => onDomainBlockSeverityChange(target)); + +ready(() => { + const input = document.getElementById('domain_block_severity'); + if (input) onDomainBlockSeverityChange(input); }); diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index 132a9766ec7870013c326cf668ff0191beb6aa7f..ed713f335ae7ac6d685ae9a66e5799aca4f0f223 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -31,10 +31,10 @@ function main() { const React = require('react'); const ReactDOM = require('react-dom'); const Rellax = require('rellax'); - const createHistory = require('history').createBrowserHistory; + const { createBrowserHistory } = require('history'); const scrollToDetailedStatus = () => { - const history = createHistory(); + const history = createBrowserHistory(); const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status'); const location = history.location; @@ -115,14 +115,6 @@ function main() { new Rellax('.parallax', { speed: -1 }); } - if (document.body.classList.contains('with-modals')) { - const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; - const scrollbarWidthStyle = document.createElement('style'); - scrollbarWidthStyle.id = 'scrollbar-width'; - document.head.appendChild(scrollbarWidthStyle); - scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0); - } - delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); }); @@ -149,6 +141,15 @@ function main() { return false; }); + delegate(document, '.blocks-table button.icon-button', 'click', function(e) { + e.preventDefault(); + + const classList = this.firstElementChild.classList; + classList.toggle('fa-chevron-down'); + classList.toggle('fa-chevron-up'); + this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden'); + }); + delegate(document, '.modal-button', 'click', e => { e.preventDefault(); @@ -246,6 +247,16 @@ function main() { input.readonly = oldReadOnly; }); + + delegate(document, '.sidebar__toggle__icon', 'click', () => { + const target = document.querySelector('.sidebar ul'); + + if (target.style.display === 'block') { + target.style.display = 'none'; + } else { + target.style.display = 'block'; + } + }); } loadPolyfills().then(main).catch(error => { diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index 6db3bc3dc9b0968420045b16e6bed3586ec84dc3..8ebc45b62d1ae84b3ba34d9323e718160352f52b 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -13,7 +13,7 @@ @import 'mastodon/widgets'; @import 'mastodon/forms'; @import 'mastodon/accounts'; -@import 'mastodon/stream_entries'; +@import 'mastodon/statuses'; @import 'mastodon/boost'; @import 'mastodon/components'; @import 'mastodon/polls'; diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/mailer.scss index b4fb1d709c02dff57bc31f6dc4eca83f44409f1b..e25a80c043287ba3aaae16fdbbd134bdb0a3cdc8 100644 --- a/app/javascript/styles/mailer.scss +++ b/app/javascript/styles/mailer.scss @@ -457,6 +457,13 @@ h5 { .status { padding-bottom: 32px; + &--highlighted { + border: 1px solid lighten($ui-base-color, 8%); + border-radius: 4px; + padding-bottom: 16px; + margin-bottom: 16px; + } + .status-header { td { font-size: 14px; diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss index ee8a7d265a10afe85de1568e708191e00161650c..05e52966b03faae93ca712e555e43a00854c3cff 100644 --- a/app/javascript/styles/mastodon-light/diff.scss +++ b/app/javascript/styles/mastodon-light/diff.scss @@ -14,15 +14,49 @@ html { } } +.status-card__actions button, +.status-card__actions a { + color: rgba($white, 0.8); + + &:hover, + &:active, + &:focus { + color: $white; + } +} + // Change default background colors of columns .column > .scrollable, .getting-started, -.column-inline-form { +.column-inline-form, +.error-column, +.regeneration-indicator { background: $white; border: 1px solid lighten($ui-base-color, 8%); border-top: 0; } +.directory__card__img { + background: lighten($ui-base-color, 12%); +} + +.filter-form, +.directory__card__bar { + background: $white; + border-bottom: 1px solid lighten($ui-base-color, 8%); +} + +.scrollable .directory__list { + width: calc(100% + 2px); + margin-left: -1px; + margin-right: -1px; +} + +.directory__card, +.table-of-contents { + border: 1px solid lighten($ui-base-color, 8%); +} + .column-back-button, .column-header { background: $white; @@ -33,16 +67,16 @@ html { } &--slim-button { - border: 0; - top: -49px; - right: 1px; + top: -50px; + right: 0; } } .column-header__back-button, .column-header__button, .column-header__button.active, -.account__header__bar { +.account__header__bar, +.directory__card__extra { background: $white; } @@ -66,6 +100,19 @@ html { text-decoration: underline; } +.confirmation-modal__secondary-button, +.confirmation-modal__cancel-button, +.mute-modal__cancel-button, +.block-modal__cancel-button { + color: lighten($ui-base-color, 26%); + + &:hover, + &:focus, + &:active { + color: $primary-text-color; + } +} + .column-subheading { background: darken($ui-base-color, 4%); border-bottom: 1px solid lighten($ui-base-color, 8%); @@ -104,7 +151,8 @@ html { .box-widget input[type="email"], .box-widget input[type="password"], .box-widget textarea, -.statuses-grid .detailed-status { +.statuses-grid .detailed-status, +.audio-player { border: 1px solid lighten($ui-base-color, 8%); } @@ -309,11 +357,23 @@ html { .boost-modal, .confirmation-modal, .mute-modal, +.block-modal, .report-modal, .embed-modal, .error-modal, -.onboarding-modal { - background: $ui-base-color; +.onboarding-modal, +.report-modal__comment .setting-text__wrapper, +.report-modal__comment .setting-text { + background: $white; + border: 1px solid lighten($ui-base-color, 8%); +} + +.report-modal__comment { + border-right-color: lighten($ui-base-color, 8%); +} + +.report-modal__container { + border-top-color: lighten($ui-base-color, 8%); } .column-header__collapsible-inner { @@ -322,9 +382,14 @@ html { border-top: 0; } +.focal-point__preview strong { + color: $white; +} + .boost-modal__action-bar, .confirmation-modal__action-bar, .mute-modal__action-bar, +.block-modal__action-bar, .onboarding-modal__paginator, .error-modal__footer { background: darken($ui-base-color, 6%); @@ -345,9 +410,11 @@ html { .embed-modal .embed-modal__container .embed-modal__html { background: $white; + border: 1px solid lighten($ui-base-color, 8%); &:focus { - background: darken($ui-base-color, 6%); + border-color: lighten($ui-base-color, 12%); + background: $white; } } @@ -700,3 +767,10 @@ html { .compose-form .compose-form__warning { box-shadow: none; } + +.audio-player .video-player__controls button, +.audio-player .video-player__time-sep, +.audio-player .video-player__time-current, +.audio-player .video-player__time-total { + color: $primary-text-color; +} diff --git a/app/javascript/styles/mastodon-light/variables.scss b/app/javascript/styles/mastodon-light/variables.scss index 01748148f91c54d637958b4df13b4a10f8d50612..c689445286eae9eff3d2c958b1c6ab644a0448b4 100644 --- a/app/javascript/styles/mastodon-light/variables.scss +++ b/app/javascript/styles/mastodon-light/variables.scss @@ -8,7 +8,7 @@ $classic-secondary-color: #d9e1e8; $classic-highlight-color: #2b90d9; // Differences -$success-green: #3c754d; +$success-green: lighten(#3c754d, 8%); $base-overlay-background: $white !default; $valid-value-color: $success-green !default; diff --git a/app/javascript/styles/mastodon/_mixins.scss b/app/javascript/styles/mastodon/_mixins.scss index faaffb30f47f37afc2cb108e8d730d18a7b26e31..68cad0fde31787b36485cb645bc1b0c29d87e547 100644 --- a/app/javascript/styles/mastodon/_mixins.scss +++ b/app/javascript/styles/mastodon/_mixins.scss @@ -22,24 +22,6 @@ color: $darker-text-color; font-size: 14px; margin: 0; - - &::-moz-focus-inner { - border: 0; - } - - &::-moz-focus-inner, - &:focus, - &:active { - outline: 0 !important; - } - - &:focus { - background: lighten($ui-base-color, 4%); - } - - @media screen and (max-width: 600px) { - font-size: 16px; - } } @mixin search-popout { diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 61637ce96721214253df8bc0e2835fe8d81e8e1f..cf16b54ac179acac973d9d4e654f0f8f6476e03e 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -17,109 +17,102 @@ $small-breakpoint: 960px; .rich-formatting { font-family: $font-sans-serif, sans-serif; - font-size: 16px; + font-size: 14px; font-weight: 400; - font-size: 16px; - line-height: 30px; + line-height: 1.7; + word-wrap: break-word; color: $darker-text-color; - padding-right: 10px; a { color: $highlight-text-color; text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } } p, li { - font-family: $font-sans-serif, sans-serif; - font-size: 16px; - font-weight: 400; - font-size: 16px; - line-height: 30px; - margin-bottom: 12px; color: $darker-text-color; + } - a { - color: $highlight-text-color; - text-decoration: underline; - } + p { + margin-top: 0; + margin-bottom: .85em; &:last-child { margin-bottom: 0; } } - strong, - em { + strong { font-weight: 700; - color: lighten($darker-text-color, 10%); + color: $secondary-text-color; } - h1 { - font-family: $font-display, sans-serif; - font-size: 26px; - line-height: 30px; - font-weight: 500; - margin-bottom: 20px; + em { + font-style: italic; color: $secondary-text-color; + } - small { - font-family: $font-sans-serif, sans-serif; - display: block; - font-size: 18px; - font-weight: 400; - color: lighten($darker-text-color, 10%); - } + code { + font-size: 0.85em; + background: darken($ui-base-color, 8%); + border-radius: 4px; + padding: 0.2em 0.3em; } - h2 { + h1, + h2, + h3, + h4, + h5, + h6 { font-family: $font-display, sans-serif; - font-size: 22px; - line-height: 26px; + margin-top: 1.275em; + margin-bottom: .85em; font-weight: 500; - margin-bottom: 20px; color: $secondary-text-color; } + h1 { + font-size: 2em; + } + + h2 { + font-size: 1.75em; + } + h3 { - font-family: $font-display, sans-serif; - font-size: 18px; - line-height: 24px; - font-weight: 500; - margin-bottom: 20px; - color: $secondary-text-color; + font-size: 1.5em; } h4 { - font-family: $font-display, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 500; - margin-bottom: 20px; - color: $secondary-text-color; + font-size: 1.25em; } - h5 { - font-family: $font-display, sans-serif; - font-size: 14px; - line-height: 24px; - font-weight: 500; - margin-bottom: 20px; - color: $secondary-text-color; + h5, + h6 { + font-size: 1em; } - h6 { - font-family: $font-display, sans-serif; - font-size: 12px; - line-height: 24px; - font-weight: 500; - margin-bottom: 20px; - color: $secondary-text-color; + ul { + list-style: disc; + } + + ol { + list-style: decimal; } ul, ol { - margin-left: 20px; + margin: 0; + padding: 0; + padding-left: 2em; + margin-bottom: 0.85em; &[type='a'] { list-style-type: lower-alpha; @@ -130,31 +123,74 @@ $small-breakpoint: 960px; } } - ul { - list-style: disc; - } - - ol { - list-style: decimal; - } - - li > ol, - li > ul { - margin-top: 6px; - } - hr { width: 100%; height: 0; border: 0; - border-bottom: 1px solid rgba($ui-base-lighter-color, .6); - margin: 20px 0; + border-bottom: 1px solid lighten($ui-base-color, 4%); + margin: 1.7em 0; &.spacer { height: 1px; border: 0; } } + + table { + width: 100%; + border-collapse: collapse; + break-inside: auto; + margin-top: 24px; + margin-bottom: 32px; + + thead tr, + tbody tr { + border-bottom: 1px solid lighten($ui-base-color, 4%); + font-size: 1em; + line-height: 1.625; + font-weight: 400; + text-align: left; + color: $darker-text-color; + } + + thead tr { + border-bottom-width: 2px; + line-height: 1.5; + font-weight: 500; + color: $dark-text-color; + } + + th, + td { + padding: 8px; + align-self: start; + align-items: start; + word-break: break-all; + + &.nowrap { + width: 25%; + position: relative; + + &::before { + content: ' '; + visibility: hidden; + } + + span { + position: absolute; + left: 8px; + right: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + } + + & > :first-child { + margin-top: 0; + } } .information-board { @@ -416,7 +452,7 @@ $small-breakpoint: 960px; } &__call-to-action { - background: darken($ui-base-color, 4%); + background: $ui-base-color; border-radius: 4px; padding: 25px 40px; overflow: hidden; diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index f95313a259911cbde597d3def8c893a5e5f49625..5dc067f0e8f6805d5f2c8a6eed3d45a6669c3b06 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -224,6 +224,7 @@ } .account__header__fields { + max-width: 100vw; padding: 0; margin: 15px -15px -15px; border: 0 none; diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 373a1026035e2c0f7b8c780afa009a1ef646b142..dde1d69ba0a54cf44b8a6c84aa7feb72b55dc26f 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -5,21 +5,66 @@ $content-width: 840px; .admin-wrapper { display: flex; justify-content: center; - height: 100%; + width: 100%; + min-height: 100vh; .sidebar-wrapper { - flex: 1 1 $sidebar-width; - height: 100%; - background: $ui-base-color; - display: flex; - justify-content: flex-end; + min-height: 100vh; + overflow: hidden; + pointer-events: none; + flex: 1 1 auto; + + &__inner { + display: flex; + justify-content: flex-end; + background: $ui-base-color; + height: 100%; + } } .sidebar { width: $sidebar-width; - height: 100%; padding: 0; - overflow-y: auto; + pointer-events: auto; + + &__toggle { + display: none; + background: lighten($ui-base-color, 8%); + height: 48px; + + &__logo { + flex: 1 1 auto; + + a { + display: inline-block; + padding: 15px; + } + + svg { + fill: $primary-text-color; + height: 20px; + position: relative; + bottom: -2px; + } + } + + &__icon { + display: block; + color: $darker-text-color; + text-decoration: none; + flex: 0 0 auto; + font-size: 20px; + padding: 15px; + } + + a { + &:hover, + &:focus, + &:active { + background: lighten($ui-base-color, 12%); + } + } + } .logo { display: block; @@ -52,6 +97,9 @@ $content-width: 840px; transition: all 200ms linear; transition-property: color, background-color; border-radius: 4px 0 0 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; i.fa { margin-right: 5px; @@ -99,12 +147,30 @@ $content-width: 840px; } .content-wrapper { - flex: 2 1 $content-width; - overflow: auto; + box-sizing: border-box; + width: 100%; + max-width: $content-width; + flex: 1 1 auto; + } + + @media screen and (max-width: $content-width + $sidebar-width) { + .sidebar-wrapper--empty { + display: none; + } + + .sidebar-wrapper { + width: $sidebar-width; + flex: 0 0 auto; + } + } + + @media screen and (max-width: $no-columns-breakpoint) { + .sidebar-wrapper { + width: 100%; + } } .content { - max-width: $content-width; padding: 20px 15px; padding-top: 60px; padding-left: 25px; @@ -123,6 +189,12 @@ $content-width: 840px; padding-bottom: 40px; border-bottom: 1px solid lighten($ui-base-color, 8%); margin-bottom: 40px; + + @media screen and (max-width: $no-columns-breakpoint) { + border-bottom: 0; + padding-bottom: 0; + font-weight: 700; + } } h3 { @@ -147,7 +219,7 @@ $content-width: 840px; font-size: 16px; color: $secondary-text-color; line-height: 28px; - font-weight: 400; + font-weight: 500; } .fields-group h6 { @@ -176,7 +248,7 @@ $content-width: 840px; & > p { font-size: 14px; - line-height: 18px; + line-height: 21px; color: $secondary-text-color; margin-bottom: 20px; @@ -204,61 +276,98 @@ $content-width: 840px; border: 0; } } - - .muted-hint { - color: $darker-text-color; - - a { - color: $highlight-text-color; - } - } - - .positive-hint { - color: $valid-value-color; - font-weight: 500; - } - - .negative-hint { - color: $error-value-color; - font-weight: 500; - } - - .neutral-hint { - color: $dark-text-color; - font-weight: 500; - } } @media screen and (max-width: $no-columns-breakpoint) { display: block; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - .sidebar-wrapper, - .content-wrapper { - flex: 0 0 auto; - height: auto; - overflow: initial; + .sidebar-wrapper { + min-height: 0; } .sidebar { width: 100%; padding: 0; height: auto; + + &__toggle { + display: flex; + } + + & > ul { + display: none; + } + + ul a, + ul ul a { + border-radius: 0; + border-bottom: 1px solid lighten($ui-base-color, 4%); + transition: none; + + &:hover { + transition: none; + } + } + + ul ul { + border-radius: 0; + } + + ul .simple-navigation-active-leaf a { + border-bottom-color: $ui-highlight-color; + } } } } +hr.spacer { + width: 100%; + border: 0; + margin: 20px 0; + height: 1px; +} + +body, +.admin-wrapper .content { + .muted-hint { + color: $darker-text-color; + + a { + color: $highlight-text-color; + } + } + + .positive-hint { + color: $valid-value-color; + font-weight: 500; + } + + .negative-hint { + color: $error-value-color; + font-weight: 500; + } + + .neutral-hint { + color: $dark-text-color; + font-weight: 500; + } + + .warning-hint { + color: $gold-star; + font-weight: 500; + } +} + .filters { display: flex; flex-wrap: wrap; .filter-subset { flex: 0 0 auto; - margin: 0 40px 10px 0; + margin: 0 40px 20px 0; &:last-child { - margin-bottom: 20px; + margin-bottom: 30px; } ul { @@ -704,3 +813,47 @@ a.name-tag, text-overflow: ellipsis; vertical-align: middle; } + +.admin-account-bio { + display: flex; + flex-wrap: wrap; + margin: 0 -5px; + margin-top: 20px; + + > div { + box-sizing: border-box; + padding: 0 5px; + margin-bottom: 10px; + flex: 1 0 50%; + } + + .account__header__fields, + .account__header__content { + background: lighten($ui-base-color, 8%); + border-radius: 4px; + height: 100%; + } + + .account__header__fields { + margin: 0; + border: 0; + + a { + color: lighten($ui-highlight-color, 8%); + } + + dl:first-child .verified { + border-radius: 0 4px 0 0; + } + + .verified a { + color: $valid-value-color; + } + } + + .account__header__content { + box-sizing: border-box; + padding: 20px; + color: $primary-text-color; + } +} diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index b5a77ce94feb2e5e1cd47acc3eec47cb36a83900..2b10b5ad3c3ab9e526903875ef01e5b14b38863f 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -8,7 +8,7 @@ body { font-family: $font-sans-serif, sans-serif; - background: darken($ui-base-color, 8%); + background: darken($ui-base-color, 7%); font-size: 13px; line-height: 18px; font-weight: 400; @@ -35,11 +35,19 @@ body { } &.app-body { - position: absolute; - width: 100%; - height: 100%; padding: 0; - background: $ui-base-color; + + &.layout-single-column { + height: auto; + min-height: 100vh; + overflow-y: scroll; + } + + &.layout-multiple-columns { + position: absolute; + width: 100%; + height: 100%; + } &.with-modals--active { overflow-y: hidden; @@ -56,7 +64,6 @@ body { &--active { overflow-y: hidden; - margin-right: 13px; } } @@ -79,9 +86,6 @@ body { &.admin { background: darken($ui-base-color, 4%); - position: fixed; - width: 100%; - height: 100%; padding: 0; } @@ -131,12 +135,97 @@ button { .app-holder { &, - & > div { + & > div, + & > noscript { display: flex; width: 100%; - height: 100%; align-items: center; justify-content: center; outline: 0 !important; } + + & > noscript { + height: 100vh; + } +} + +.layout-single-column .app-holder { + &, + & > div { + min-height: 100vh; + } +} + +.layout-multiple-columns .app-holder { + &, + & > div { + height: 100%; + } +} + +.error-boundary, +.app-holder noscript { + flex-direction: column; + font-size: 16px; + font-weight: 400; + line-height: 1.7; + color: lighten($error-red, 4%); + text-align: center; + + & > div { + max-width: 500px; + } + + p { + margin-bottom: .85em; + + &:last-child { + margin-bottom: 0; + } + } + + a { + color: $highlight-text-color; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + + &__footer { + color: $dark-text-color; + font-size: 13px; + + a { + color: $dark-text-color; + } + } + + button { + display: inline; + border: 0; + background: transparent; + color: $dark-text-color; + font: inherit; + padding: 0; + margin: 0; + line-height: inherit; + cursor: pointer; + outline: 0; + transition: color 300ms linear; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + + &.copied { + color: $valid-value-color; + transition: none; + } + } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e413b00131b157767ea400be0d3430968541573f..86425c47c4ac42f841a77fb3da2fb3b741bd146f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3,6 +3,27 @@ -ms-overflow-style: -ms-autohiding-scrollbar; } +.link-button { + display: block; + font-size: 15px; + line-height: 20px; + color: $ui-highlight-color; + border: 0; + background: transparent; + padding: 0; + cursor: pointer; + + &:hover, + &:active { + text-decoration: underline; + } + + &:disabled { + color: $ui-primary-color; + cursor: default; + } +} + .button { background-color: $ui-highlight-color; border: 10px none; @@ -129,19 +150,28 @@ padding: 0; color: $action-button-color; border: 0; + border-radius: 4px; background: transparent; cursor: pointer; - transition: color 100ms ease-in; + transition: all 100ms ease-in; + transition-property: background-color, color; &:hover, &:active, &:focus { color: lighten($action-button-color, 7%); - transition: color 200ms ease-out; + background-color: rgba($action-button-color, 0.15); + transition: all 200ms ease-out; + transition-property: background-color, color; + } + + &:focus { + background-color: rgba($action-button-color, 0.3); } &.disabled { color: darken($action-button-color, 13%); + background-color: transparent; cursor: default; } @@ -166,10 +196,16 @@ &:active, &:focus { color: darken($lighter-text-color, 7%); + background-color: rgba($lighter-text-color, 0.15); + } + + &:focus { + background-color: rgba($lighter-text-color, 0.3); } &.disabled { color: lighten($lighter-text-color, 7%); + background-color: transparent; } &.active { @@ -197,6 +233,7 @@ .text-icon-button { color: $lighter-text-color; border: 0; + border-radius: 4px; background: transparent; cursor: pointer; font-weight: 600; @@ -204,17 +241,25 @@ padding: 0 3px; line-height: 27px; outline: 0; - transition: color 100ms ease-in; + transition: all 100ms ease-in; + transition-property: background-color, color; &:hover, &:active, &:focus { color: darken($lighter-text-color, 7%); - transition: color 200ms ease-out; + background-color: rgba($lighter-text-color, 0.15); + transition: all 200ms ease-out; + transition-property: background-color, color; + } + + &:focus { + background-color: rgba($lighter-text-color, 0.3); } &.disabled { color: lighten($lighter-text-color, 20%); + background-color: transparent; cursor: default; } @@ -376,6 +421,10 @@ border: 0; outline: 0; + &::placeholder { + color: $dark-text-color; + } + &:focus { outline: 0; } @@ -445,7 +494,8 @@ } .autosuggest-account, - .autosuggest-emoji { + .autosuggest-emoji, + .autosuggest-hashtag { display: flex; flex-direction: row; align-items: center; @@ -454,6 +504,29 @@ font-size: 14px; } + .autosuggest-hashtag { + justify-content: space-between; + + &__name { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + strong { + font-weight: 500; + } + + &__uses { + flex: 0 0 auto; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + .autosuggest-account-icon, .autosuggest-emoji img { display: block; @@ -595,7 +668,8 @@ } } - .icon-button { + .icon-button, + .text-icon-button { box-sizing: content-box; padding: 0 3px; } @@ -603,18 +677,6 @@ .character-counter__wrapper { align-self: center; margin-right: 4px; - - .character-counter { - cursor: default; - font-family: $font-sans-serif, sans-serif; - font-size: 14px; - font-weight: 600; - color: $lighter-text-color; - - &.character-counter--over { - color: $warning-red; - } - } } } @@ -631,6 +693,18 @@ } } +.character-counter { + cursor: default; + font-family: $font-sans-serif, sans-serif; + font-size: 14px; + font-weight: 600; + color: $lighter-text-color; + + &.character-counter--over { + color: $warning-red; + } +} + .no-reduce-motion .spoiler-input { transition: height 0.4s ease, opacity 0.4s ease; } @@ -722,7 +796,7 @@ white-space: pre-wrap; &:last-child { - margin-bottom: 2px; + margin-bottom: 0; } } @@ -753,6 +827,10 @@ } } + a.unhandled-link { + color: lighten($ui-highlight-color, 8%); + } + .status__content__spoiler-link { background: $action-button-color; @@ -877,7 +955,8 @@ opacity: 1; animation: fade 150ms linear; - .video-player { + .video-player, + .audio-player { margin-top: 8px; } @@ -972,7 +1051,8 @@ white-space: normal; } - .video-player { + .video-player, + .audio-player { margin-top: 8px; max-width: 250px; } @@ -1083,7 +1163,8 @@ } } - .video-player { + .video-player, + .audio-player { margin-top: 8px; } } @@ -1195,14 +1276,28 @@ &-composite { @include avatar-radius; + border-radius: 50%; overflow: hidden; + position: relative; + cursor: default; & > div { - @include avatar-radius; float: left; position: relative; box-sizing: border-box; } + + &__label { + display: block; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: $primary-text-color; + text-shadow: 1px 1px 2px $base-shadow-color; + font-weight: 700; + font-size: 15px; + } } } @@ -1391,6 +1486,10 @@ a.account__display-name { color: inherit; } +.detailed-status .button.logo-button { + margin-bottom: 15px; +} + .detailed-status__display-name { color: $secondary-text-color; display: block; @@ -1434,6 +1533,7 @@ a.account__display-name { } .muted { + .status__content, .status__content p, .status__content a { color: $dark-text-color; @@ -1804,6 +1904,7 @@ a.account__display-name { justify-content: center; width: 100%; height: 100%; + min-height: 100vh; &__pane { height: 100%; @@ -1811,12 +1912,14 @@ a.account__display-name { pointer-events: none; display: flex; justify-content: flex-end; + min-width: 285px; &--start { justify-content: flex-start; } &__inner { + position: fixed; width: 285px; pointer-events: auto; height: 100%; @@ -1827,6 +1930,7 @@ a.account__display-name { box-sizing: border-box; width: 100%; max-width: 600px; + flex: 0 0 auto; display: flex; flex-direction: column; @@ -1837,6 +1941,26 @@ a.account__display-name { } } +.tabs-bar__wrapper { + background: darken($ui-base-color, 8%); + position: sticky; + top: 0; + z-index: 2; + padding-top: 0; + + @media screen and (min-width: $no-gap-breakpoint) { + padding-top: 10px; + } + + .tabs-bar { + margin-bottom: 0; + + @media screen and (min-width: $no-gap-breakpoint) { + margin-bottom: 10px; + } + } +} + .react-swipeable-view-container { &, .columns-area, @@ -1871,7 +1995,6 @@ a.account__display-name { flex-direction: column; width: 100%; height: 100%; - background: darken($ui-base-color, 7%); } .drawer { @@ -1997,6 +2120,24 @@ a.account__display-name { padding: 0; } + .directory__list { + display: grid; + grid-gap: 10px; + grid-template-columns: minmax(0, 50%) minmax(0, 50%); + + @media screen and (max-width: $no-gap-breakpoint) { + display: block; + } + } + + .directory__card { + margin-bottom: 0; + } + + .filter-form { + display: flex; + } + .autosuggest-textarea__textarea { font-size: 16px; } @@ -2012,8 +2153,17 @@ a.account__display-name { top: 15px; } + .scrollable { + overflow: visible; + + @supports(display: grid) { + contain: content; + } + } + @media screen and (min-width: $no-gap-breakpoint) { padding: 10px 0; + padding-top: 0; } @media screen and (min-width: 630px) { @@ -2021,7 +2171,8 @@ a.account__display-name { padding: 15px; .media-gallery, - .video-player { + .video-player, + .audio-player { margin-top: 15px; } } @@ -2063,7 +2214,8 @@ a.account__display-name { .media-gallery, &__action-bar, - .video-player { + .video-player, + .audio-player { margin-top: 10px; } } @@ -2128,17 +2280,14 @@ a.account__display-name { @media screen and (min-width: $no-gap-breakpoint) { .tabs-bar { - margin: 10px auto; - margin-bottom: 0; width: 100%; } .react-swipeable-view-container .columns-area--mobile { - height: calc(100% - 20px) !important; + height: calc(100% - 10px) !important; } .getting-started__wrapper, - .getting-started__trends, .search { margin-bottom: 10px; } @@ -2245,13 +2394,24 @@ a.account__display-name { margin-bottom: 10px; height: calc(100% - 20px); overflow-y: auto; + display: flex; + flex-direction: column; + + & > a { + flex: 0 0 auto; + } hr { + flex: 0 0 auto; border: 0; background: transparent; border-top: 1px solid lighten($ui-base-color, 4%); margin: 10px 0; } + + .flex-spacer { + background: transparent; + } } .drawer__pager { @@ -2362,6 +2522,8 @@ a.account__display-name { } .column-back-button { + box-sizing: border-box; + width: 100%; background: lighten($ui-base-color, 4%); color: $highlight-text-color; cursor: pointer; @@ -2641,8 +2803,19 @@ a.account__display-name { } &__trends { - background: $ui-base-color; flex: 0 1 auto; + opacity: 1; + animation: fade 150ms linear; + margin-top: 10px; + + h4 { + font-size: 12px; + text-transform: uppercase; + color: $darker-text-color; + padding: 10px; + font-weight: 500; + border-bottom: 1px solid lighten($ui-base-color, 8%); + } @media screen and (max-height: 810px) { .trends__item:nth-child(3) { @@ -2659,11 +2832,15 @@ a.account__display-name { @media screen and (max-height: 670px) { display: none; } - } - &__scrollable { - max-height: 100%; - overflow-y: auto; + .trends__item { + border-bottom: 0; + padding: 10px; + + &__current { + color: $darker-text-color; + } + } } } @@ -2950,37 +3127,27 @@ a.status-card.compact:hover { cursor: default; display: flex; flex: 1 1 auto; + flex-direction: column; align-items: center; justify-content: center; padding: 20px; - & > div { - width: 100%; - background: transparent; - padding-top: 0; - } - &__figure { - background: url('../images/elephant_ui_working.svg') no-repeat center 0; - width: 100%; - height: 160px; - background-size: contain; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + &, + img { + display: block; + width: auto; + height: 160px; + margin: 0; + } } - &.missing-indicator { + &--without-header { padding-top: 20px + 48px; - - .regeneration-indicator__figure { - background-image: url('../images/elephant_ui_disappointed.svg'); - } } &__label { - margin-top: 200px; + margin-top: 30px; strong { display: block; @@ -3300,6 +3467,10 @@ a.status-card.compact:hover { height: auto; } + &--click-thru { + pointer-events: none; + } + &--hidden { display: none; } @@ -3328,6 +3499,12 @@ a.status-card.compact:hover { background: rgba($base-overlay-background, 0.8); } } + + &:disabled { + .spoiler-button__overlay__label { + background: rgba($base-overlay-background, 0.5); + } + } } } @@ -3371,6 +3548,28 @@ a.status-card.compact:hover { .column-select { &__control { @include search-input; + + &::placeholder { + color: lighten($darker-text-color, 4%); + } + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + + &:focus { + background: lighten($ui-base-color, 4%); + } + + @media screen and (max-width: 600px) { + font-size: 16px; + } } &__placeholder { @@ -3884,6 +4083,28 @@ a.status-card.compact:hover { padding-right: 30px; line-height: 18px; font-size: 16px; + + &::placeholder { + color: lighten($darker-text-color, 4%); + } + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + + &:focus { + background: lighten($ui-base-color, 4%); + } + + @media screen and (max-width: 600px) { + font-size: 16px; + } } .search__icon { @@ -3997,8 +4218,9 @@ a.status-card.compact:hover { } .search-results__info { - padding: 10px; - color: $secondary-text-color; + padding: 20px; + color: $darker-text-color; + text-align: center; } .modal-root { @@ -4039,10 +4261,13 @@ a.status-card.compact:hover { z-index: 9999; } -.video-modal { +.video-modal__container { max-width: 100vw; max-height: 100vh; - position: relative; +} + +.audio-modal__container { + width: 50vw; } .media-modal { @@ -4142,6 +4367,7 @@ a.status-card.compact:hover { } a { + pointer-events: auto; text-decoration: none; font-weight: 500; color: $ui-secondary-color; @@ -4305,7 +4531,8 @@ a.status-card.compact:hover { .confirmation-modal, .report-modal, .actions-modal, -.mute-modal { +.mute-modal, +.block-modal { background: lighten($ui-secondary-color, 8%); color: $inverted-text-color; border-radius: 8px; @@ -4359,7 +4586,8 @@ a.status-card.compact:hover { .boost-modal__action-bar, .confirmation-modal__action-bar, -.mute-modal__action-bar { +.mute-modal__action-bar, +.block-modal__action-bar { display: flex; justify-content: space-between; background: $ui-secondary-color; @@ -4387,11 +4615,13 @@ a.status-card.compact:hover { font-size: 14px; } -.mute-modal { +.mute-modal, +.block-modal { line-height: 24px; } -.mute-modal .react-toggle { +.mute-modal .react-toggle, +.block-modal .react-toggle { vertical-align: middle; } @@ -4420,7 +4650,8 @@ a.status-card.compact:hover { } } -.report-modal__statuses { +.report-modal__statuses, +.focal-point-modal__content { flex: 1 1 auto; min-height: 20vh; max-height: 80vh; @@ -4441,6 +4672,12 @@ a.status-card.compact:hover { } } +.focal-point-modal__content { + @media screen and (max-width: 480px) { + max-height: 40vh; + } +} + .report-modal__comment { padding: 20px; border-right: 1px solid $ui-secondary-color; @@ -4462,32 +4699,72 @@ a.status-card.compact:hover { padding: 10px; font-family: inherit; font-size: 14px; - resize: vertical; + resize: none; border: 0; outline: 0; border-radius: 4px; border: 1px solid $ui-secondary-color; - margin-bottom: 20px; + min-height: 100px; + max-height: 50vh; + margin-bottom: 10px; &:focus { border: 1px solid darken($ui-secondary-color, 8%); } - } - .setting-toggle { - margin-top: 20px; - margin-bottom: 24px; - - &__label { - color: $inverted-text-color; - font-size: 14px; - } - } + &__wrapper { + background: $white; + border: 1px solid $ui-secondary-color; + margin-bottom: 10px; + border-radius: 4px; - @media screen and (max-width: 480px) { - padding: 10px; - max-width: 100%; - order: 2; + .setting-text { + border: 0; + margin-bottom: 0; + border-radius: 0; + + &:focus { + border: 0; + } + } + + &__modifiers { + color: $inverted-text-color; + font-family: inherit; + font-size: 14px; + background: $white; + } + } + + &__toolbar { + display: flex; + justify-content: space-between; + margin-bottom: 20px; + } + } + + .setting-text-label { + display: block; + color: $inverted-text-color; + font-size: 14px; + font-weight: 500; + margin-bottom: 10px; + } + + .setting-toggle { + margin-top: 20px; + margin-bottom: 24px; + + &__label { + color: $inverted-text-color; + font-size: 14px; + } + } + + @media screen and (max-width: 480px) { + padding: 10px; + max-width: 100%; + order: 2; .setting-toggle { margin-bottom: 4px; @@ -4555,33 +4832,36 @@ a.status-card.compact:hover { } .confirmation-modal__action-bar, -.mute-modal__action-bar { - .confirmation-modal__secondary-button, - .confirmation-modal__cancel-button, - .mute-modal__cancel-button { - background-color: transparent; - color: $lighter-text-color; - font-size: 14px; - font-weight: 500; - - &:hover, - &:focus, - &:active { - color: darken($lighter-text-color, 4%); - } - } - +.mute-modal__action-bar, +.block-modal__action-bar { .confirmation-modal__secondary-button { flex-shrink: 1; } } +.confirmation-modal__secondary-button, +.confirmation-modal__cancel-button, +.mute-modal__cancel-button, +.block-modal__cancel-button { + background-color: transparent; + color: $lighter-text-color; + font-size: 14px; + font-weight: 500; + + &:hover, + &:focus, + &:active { + color: darken($lighter-text-color, 4%); + background-color: transparent; + } +} + .confirmation-modal__container, .mute-modal__container, +.block-modal__container, .report-modal__target { padding: 30px; font-size: 16px; - text-align: center; strong { font-weight: 500; @@ -4594,11 +4874,36 @@ a.status-card.compact:hover { } } +.confirmation-modal__container, .report-modal__target { - padding: 20px; + text-align: center; +} + +.block-modal, +.mute-modal { + &__explanation { + margin-top: 20px; + } + + .setting-toggle { + margin-top: 20px; + margin-bottom: 24px; + display: flex; + align-items: center; + + &__label { + color: $inverted-text-color; + margin: 0; + margin-left: 8px; + } + } +} + +.report-modal__target { + padding: 15px; .media-modal__close { - top: 19px; + top: 14px; right: 15px; } } @@ -4609,6 +4914,7 @@ a.status-card.compact:hover { position: absolute; top: 0; left: 0; + z-index: 9999; } .media-gallery__gifv__label { @@ -4794,70 +5100,64 @@ a.status-card.compact:hover { } /* End Media Gallery */ -/* Status Video Player */ -.status__video-player { - background: $base-overlay-background; - box-sizing: border-box; - cursor: default; /* May not be needed */ - margin-top: 8px; - overflow: hidden; - position: relative; -} +.detailed, +.fullscreen { + .video-player__volume__current, + .video-player__volume::before { + bottom: 27px; + } -.status__video-player-video { - height: 100%; - object-fit: cover; - position: relative; - top: 50%; - transform: translateY(-50%); - width: 100%; - z-index: 1; -} + .video-player__volume__handle { + bottom: 23px; + } -.status__video-player-expand, -.status__video-player-mute { - color: $primary-text-color; - opacity: 0.8; - position: absolute; - right: 4px; - text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color; } -.status__video-player-spoiler { - display: none; - color: $primary-text-color; - left: 4px; - position: absolute; - text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color; - top: 4px; - z-index: 100; +.audio-player { + box-sizing: border-box; + position: relative; + background: darken($ui-base-color, 8%); + border-radius: 4px; + padding-bottom: 44px; + direction: ltr; - &.status__video-player-spoiler--visible { - display: block; + &.editable { + border-radius: 0; + height: 100%; } -} -.status__video-player-expand { - bottom: 4px; - z-index: 100; -} + &__waveform { + padding: 15px 0; + position: relative; + overflow: hidden; -.status__video-player-mute { - top: 4px; - z-index: 5; -} + &::before { + content: ""; + display: block; + position: absolute; + border-top: 1px solid lighten($ui-base-color, 4%); + width: 100%; + height: 0; + left: 0; + top: calc(50% + 1px); + } + } -.detailed, -.fullscreen { - .video-player__volume__current, - .video-player__volume::before { - bottom: 27px; + &__progress-placeholder { + background-color: rgba(lighten($ui-highlight-color, 8%), 0.5); } - .video-player__volume__handle { - bottom: 23px; + &__wave-placeholder { + background-color: lighten($ui-base-color, 16%); } + .video-player__controls { + padding: 0 15px; + padding-top: 10px; + background: darken($ui-base-color, 8%); + border-top: 1px solid lighten($ui-base-color, 4%); + border-radius: 0 0 4px 4px; + } } .video-player { @@ -4866,6 +5166,13 @@ a.status-card.compact:hover { background: $base-shadow-color; max-width: 100%; border-radius: 4px; + box-sizing: border-box; + direction: ltr; + + &.editable { + border-radius: 0; + height: 100% !important; + } &:focus { outline: 0; @@ -5147,28 +5454,137 @@ a.status-card.compact:hover { } } -.media-spoiler-video { - background-size: cover; - background-repeat: no-repeat; - background-position: center; - cursor: pointer; - margin-top: 8px; - position: relative; - border: 0; - display: block; -} +.directory { + &__list { + width: 100%; + margin: 10px 0; + transition: opacity 100ms ease-in; -.media-spoiler-video-play-icon { - border-radius: 100px; - color: rgba($primary-text-color, 0.8); - font-size: 36px; - left: 50%; - padding: 5px; - position: absolute; - top: 50%; - transform: translate(-50%, -50%); + &.loading { + opacity: 0.7; + } + + @media screen and (max-width: $no-gap-breakpoint) { + margin: 0; + } + } + + &__card { + box-sizing: border-box; + margin-bottom: 10px; + + &__img { + height: 125px; + position: relative; + background: darken($ui-base-color, 12%); + overflow: hidden; + + img { + display: block; + width: 100%; + height: 100%; + margin: 0; + object-fit: cover; + } + } + + &__bar { + display: flex; + align-items: center; + background: lighten($ui-base-color, 4%); + padding: 10px; + + &__name { + flex: 1 1 auto; + display: flex; + align-items: center; + text-decoration: none; + overflow: hidden; + } + + &__relationship { + width: 23px; + min-height: 1px; + flex: 0 0 auto; + } + + .avatar { + flex: 0 0 auto; + width: 48px; + height: 48px; + padding-top: 2px; + + img { + width: 100%; + height: 100%; + display: block; + margin: 0; + border-radius: 4px; + background: darken($ui-base-color, 8%); + object-fit: cover; + } + } + + .display-name { + margin-left: 15px; + text-align: left; + + strong { + font-size: 15px; + color: $primary-text-color; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + } + + span { + display: block; + font-size: 14px; + color: $darker-text-color; + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + &__extra { + background: $ui-base-color; + display: flex; + align-items: center; + justify-content: center; + + .accounts-table__count { + width: 33.33%; + flex: 0 0 auto; + padding: 15px 0; + } + + .account__header__content { + box-sizing: border-box; + padding: 15px 10px; + border-bottom: 1px solid lighten($ui-base-color, 8%); + width: 100%; + min-height: 18px + 30px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + p { + display: none; + + &:first-child { + display: inline; + } + } + + br { + display: none; + } + } + } + } } -/* End Video Player */ .account-gallery__container { display: flex; @@ -5244,6 +5660,73 @@ a.status-card.compact:hover { } } } + + &.directory__section-headline { + background: darken($ui-base-color, 2%); + border-bottom-color: transparent; + + a, + button { + &.active { + &::before { + display: none; + } + + &::after { + border-color: transparent transparent darken($ui-base-color, 7%); + } + } + } + } +} + +.filter-form { + background: $ui-base-color; + + &__column { + padding: 10px 15px; + } + + .radio-button { + display: block; + } +} + +.radio-button { + font-size: 14px; + position: relative; + display: inline-block; + padding: 6px 0; + line-height: 18px; + cursor: default; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + + input[type=radio], + input[type=checkbox] { + display: none; + } + + &__input { + display: inline-block; + position: relative; + border: 1px solid $ui-primary-color; + box-sizing: border-box; + width: 18px; + height: 18px; + flex: 0 0 auto; + margin-right: 10px; + top: -1px; + border-radius: 50%; + vertical-align: middle; + + &.checked { + border-color: lighten($ui-highlight-color, 8%); + background: lighten($ui-highlight-color, 8%); + } + } } ::-webkit-scrollbar-thumb { @@ -5368,6 +5851,7 @@ noscript { } .embed-modal { + width: auto; max-width: 80vw; max-height: 80vh; @@ -5398,6 +5882,7 @@ noscript { font-size: 14px; margin: 0; margin-bottom: 15px; + border-radius: 4px; &::-moz-focus-inner { border: 0; @@ -5423,6 +5908,7 @@ noscript { max-width: 100%; overflow: hidden; border: 0; + border-radius: 4px; } } } @@ -5595,27 +6081,26 @@ noscript { } } -.focal-point-modal { - max-width: 80vw; - max-height: 80vh; - position: relative; -} - .focal-point { position: relative; - cursor: pointer; + cursor: move; overflow: hidden; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background: $base-shadow-color; - &.dragging { - cursor: move; - } - - img { - max-width: 80vw; + img, + video, + canvas { + display: block; max-height: 80vh; - width: auto; + width: 100%; height: auto; - margin: auto; + margin: 0; + object-fit: contain; + background: $base-shadow-color; } &__reticle { @@ -5635,6 +6120,43 @@ noscript { top: 0; left: 0; } + + &__preview { + position: absolute; + bottom: 10px; + right: 10px; + z-index: 2; + cursor: move; + transition: opacity 0.1s ease; + + &:hover { + opacity: 0.5; + } + + strong { + color: $primary-text-color; + font-size: 14px; + font-weight: 500; + display: block; + margin-bottom: 5px; + } + + div { + border-radius: 4px; + box-shadow: 0 0 14px rgba($base-shadow-color, 0.2); + } + } + + @media screen and (max-width: 480px) { + img, + video { + max-height: 100%; + } + + &__preview { + display: none; + } + } } .account__header__content { @@ -5887,11 +6409,12 @@ noscript { &__current { flex: 0 0 auto; - width: 100px; font-size: 24px; line-height: 36px; font-weight: 500; - text-align: center; + text-align: right; + padding-right: 15px; + margin-left: 5px; color: $secondary-text-color; } @@ -5899,55 +6422,78 @@ noscript { flex: 0 0 auto; width: 50px; - path { + path:first-child { + fill: rgba($highlight-text-color, 0.25) !important; + fill-opacity: 1 !important; + } + + path:last-child { stroke: lighten($highlight-text-color, 6%) !important; } } } } -.layout-toggle { +.conversation { display: flex; + border-bottom: 1px solid lighten($ui-base-color, 8%); padding: 5px; + padding-bottom: 0; - button { - box-sizing: border-box; - flex: 0 0 50%; - background: transparent; - padding: 5px; - border: 0; - position: relative; + &:focus { + background: lighten($ui-base-color, 2%); + outline: 0; + } - &:hover, - &:focus, - &:active { - svg path:first-child { - fill: lighten($ui-base-color, 16%); - } - } + &__avatar { + flex: 0 0 auto; + padding: 10px; + padding-top: 12px; } - svg { - width: 100%; - height: auto; + &__content { + flex: 1 1 auto; + padding: 10px 5px; + padding-right: 15px; + overflow: hidden; - path:first-child { - fill: lighten($ui-base-color, 12%); + &__info { + overflow: hidden; + display: flex; + flex-direction: row-reverse; + justify-content: space-between; } - path:last-child { - fill: darken($ui-base-color, 14%); + &__relative-time { + font-size: 15px; + color: $darker-text-color; + padding-left: 15px; } - } - &__active { - color: $ui-highlight-color; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: lighten($ui-base-color, 12%); - border-radius: 50%; - padding: 0.35rem; + &__names { + color: $darker-text-color; + font-size: 15px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 4px; + flex-basis: 90px; + flex-grow: 1; + + a { + color: $primary-text-color; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + } + + a { + word-break: break-word; + } } } diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 3564bf07b4ddc96e1c6168b6e163cbf5736bd635..319f8c94dad549384f284442d8b3bf38975777fc 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -141,10 +141,71 @@ grid-row: 3; } + @media screen and (max-width: $no-gap-breakpoint) { + grid-gap: 0; + grid-template-columns: minmax(0, 100%); + + .column-0 { + grid-column: 1; + } + + .column-1 { + grid-column: 1; + grid-row: 3; + } + + .column-2 { + grid-column: 1; + grid-row: 2; + } + + .column-3 { + grid-column: 1; + grid-row: 4; + } + } +} + +.grid-4 { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-auto-columns: 25%; + grid-auto-rows: max-content; + + .column-0 { + grid-column: 1 / 5; + grid-row: 1; + } + + .column-1 { + grid-column: 1 / 4; + grid-row: 2; + } + + .column-2 { + grid-column: 4; + grid-row: 2; + } + + .column-3 { + grid-column: 2 / 5; + grid-row: 3; + } + + .column-4 { + grid-column: 1; + grid-row: 3; + } + .landing-page__call-to-action { min-height: 100%; } + .flash-message { + margin-bottom: 10px; + } + @media screen and (max-width: 738px) { grid-template-columns: minmax(0, 50%) minmax(0, 50%); @@ -185,6 +246,11 @@ } .column-3 { + grid-column: 1; + grid-row: 5; + } + + .column-4 { grid-column: 1; grid-row: 4; } @@ -346,6 +412,20 @@ } } + .directory__card { + border-radius: 4px; + + @media screen and (max-width: $no-gap-breakpoint) { + border-radius: 0; + } + } + + .page-header { + @media screen and (max-width: $no-gap-breakpoint) { + border-bottom: 0; + } + } + .public-account-header { overflow: hidden; margin-bottom: 10px; @@ -749,14 +829,22 @@ } } - .static-icon-button { - color: $action-button-color; - font-size: 18px; + .directory__list { + display: grid; + grid-gap: 10px; + grid-template-columns: minmax(0, 50%) minmax(0, 50%); - & > span { - font-size: 14px; - font-weight: 500; + @media screen and (max-width: $no-gap-breakpoint) { + display: block; } + + .icon-button { + font-size: 18px; + } + } + + .directory__card { + margin-bottom: 0; } .card-grid { diff --git a/app/javascript/styles/mastodon/dashboard.scss b/app/javascript/styles/mastodon/dashboard.scss index e4564f062cd7bbb97028507eab9083c49c012cbc..c0944d417dd150c0c22e7f08a9c63196307d892f 100644 --- a/app/javascript/styles/mastodon/dashboard.scss +++ b/app/javascript/styles/mastodon/dashboard.scss @@ -15,6 +15,8 @@ padding: 20px; background: lighten($ui-base-color, 4%); border-radius: 4px; + box-sizing: border-box; + height: 100%; } & > a { diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss index f74c004e995994b30c89db4e2c1d2807eb1d04b7..00d2908832fbca0a8cfe18715c3ab4b6381c586c 100644 --- a/app/javascript/styles/mastodon/footer.scss +++ b/app/javascript/styles/mastodon/footer.scss @@ -128,7 +128,7 @@ &:hover, &:focus, &:active { - svg path { + svg { fill: lighten($ui-base-color, 38%); } } diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 456ee4e0d3d024ea068a75b7266df042bbff64c7..b729d912e539394bc47452d1464515818d6b4092 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -112,6 +112,15 @@ code { padding: 0.2em 0.4em; background: darken($ui-base-color, 12%); } + + li { + list-style: disc; + margin-left: 18px; + } + } + + ul.hint { + margin-bottom: 15px; } span.hint { @@ -245,6 +254,10 @@ code { &-6 { max-width: 50%; } + + .actions { + margin-top: 27px; + } } .fields-group:last-child, @@ -300,6 +313,13 @@ code { } } + .input.static .label_input__wrapper { + font-size: 16px; + padding: 10px; + border: 1px solid $dark-text-color; + border-radius: 4px; + } + input[type=text], input[type=number], input[type=email], @@ -318,6 +338,10 @@ code { border-radius: 4px; padding: 10px; + &::placeholder { + color: lighten($darker-text-color, 4%); + } + &:invalid { box-shadow: none; } diff --git a/app/javascript/styles/mastodon/introduction.scss b/app/javascript/styles/mastodon/introduction.scss index 222d8f60e863b167b5b76326a317b1ad0792f5b4..b44ae730649a847f433b76e2fd4ec7179e416179 100644 --- a/app/javascript/styles/mastodon/introduction.scss +++ b/app/javascript/styles/mastodon/introduction.scss @@ -3,9 +3,10 @@ flex-direction: column; justify-content: center; align-items: center; + height: 100vh; + background: $ui-base-color; @media screen and (max-width: 920px) { - background: darken($ui-base-color, 8%); display: block !important; } diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index 12f57b7a9185c8a161b86dd3f04d4622e101be73..f59a9d693a07ab5bd35f455644c03f41b7b1dbb4 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -5,7 +5,6 @@ li { margin-bottom: 10px; position: relative; - height: 18px + 12px; } &__chart { @@ -24,13 +23,11 @@ &__text { position: relative; - display: inline-block; + display: flex; padding: 6px 0; line-height: 18px; cursor: default; - white-space: nowrap; overflow: hidden; - text-overflow: ellipsis; input[type=radio], input[type=checkbox] { @@ -82,6 +79,9 @@ top: -1px; border-radius: 50%; vertical-align: middle; + margin-top: auto; + margin-bottom: auto; + flex: 0 0 18px; &.checkbox { border-radius: 4px; @@ -95,10 +95,19 @@ &__number { display: inline-block; - width: 36px; + width: 52px; font-weight: 700; padding: 0 10px; + padding-left: 8px; text-align: right; + margin-top: auto; + margin-bottom: auto; + flex: 0 0 52px; + } + + &__vote__mark { + float: left; + line-height: 18px; } &__footer { diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss index 58bc53b141fe716e55195854544ef84b072dcaa8..ecd16625357df482c8ef13556ba9896ff9ae2d05 100644 --- a/app/javascript/styles/mastodon/rtl.scss +++ b/app/javascript/styles/mastodon/rtl.scss @@ -7,6 +7,34 @@ body.rtl { padding-right: 15px; } + .radio-button__input { + margin-right: 0; + margin-left: 10px; + } + + .directory__card__bar .display-name { + margin-left: 0; + margin-right: 15px; + } + + .display-name { + text-align: right; + } + + .notification__message { + margin-left: 0; + margin-right: 68px; + } + + .drawer__inner__mastodon > img { + transform: scaleX(-1); + } + + .notification__favourite-icon-wrapper { + left: auto; + right: -26px; + } + .landing-page__logo { margin-right: 0; margin-left: 20px; @@ -135,7 +163,6 @@ body.rtl { } .status__action-bar { - &__counter { margin-right: 0; margin-left: 11px; @@ -328,6 +355,12 @@ body.rtl { } } + .columns-area--mobile .column, + .columns-area--mobile .drawer { + padding-left: 0; + padding-right: 0; + } + .public-layout { .header { .nav-button { diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/statuses.scss similarity index 100% rename from app/javascript/styles/mastodon/stream_entries.scss rename to app/javascript/styles/mastodon/statuses.scss diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 11ac6dfeb1595ca2b325cfc0dd8a92224fc3c5d6..5a6e10aa4908cdb95b207cb230aa1ac762859543 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -180,6 +180,18 @@ a.table-action-link { } } + &__form { + padding: 16px; + border: 1px solid darken($ui-base-color, 8%); + border-top: 0; + background: $ui-base-color; + + .fields-row { + padding-top: 0; + margin-bottom: 0; + } + } + &__row { border: 1px solid darken($ui-base-color, 8%); border-top: 0; @@ -210,6 +222,45 @@ a.table-action-link { &--unpadded { padding: 0; } + + &--with-image { + display: flex; + align-items: center; + } + + &__image { + flex: 0 0 auto; + display: flex; + justify-content: center; + align-items: center; + margin-right: 10px; + + .emojione { + width: 32px; + height: 32px; + } + } + + &__text { + flex: 1 1 auto; + } + + &__extra { + flex: 0 0 auto; + text-align: right; + color: $darker-text-color; + font-weight: 500; + } + } + + .directory__tag { + margin: 0; + width: 100%; + + a { + background: transparent; + border-radius: 0; + } } } diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss index acaf5b02404a7bdd0086e6560a24c337e97bcaf2..ca050a8d9931d4ba2de070689a364e0810f5945d 100644 --- a/app/javascript/styles/mastodon/widgets.scss +++ b/app/javascript/styles/mastodon/widgets.scss @@ -100,6 +100,16 @@ background-size: 44px 44px; } } + + .trends__item { + padding: 10px; + } +} + +.trends-widget { + h4 { + color: $darker-text-color; + } } .box-widget { @@ -109,41 +119,52 @@ box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); } -.contact-widget, -.landing-page__information.contact-widget { - box-sizing: border-box; - padding: 20px; - min-height: 100%; +.placeholder-widget { + padding: 16px; border-radius: 4px; - background: $ui-base-color; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); + border: 2px dashed $dark-text-color; + text-align: center; + color: $darker-text-color; + margin-bottom: 10px; } .contact-widget { + min-height: 100%; font-size: 15px; color: $darker-text-color; line-height: 20px; word-wrap: break-word; font-weight: 400; + padding: 0; - strong { - font-weight: 500; + h4 { + padding: 10px; + text-transform: uppercase; + font-weight: 700; + font-size: 13px; + color: $darker-text-color; } - p { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } + .account { + border-bottom: 0; + padding: 10px 0; + padding-top: 5px; } - &__mail { - margin-top: 10px; + & > a { + display: inline-block; + padding: 10px; + padding-top: 0; + color: $darker-text-color; + text-decoration: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; - a { - color: $primary-text-color; - text-decoration: none; + &:hover, + &:focus, + &:active { + text-decoration: underline; } } } @@ -324,7 +345,8 @@ &.active h4 { &, .fa, - small { + small, + .trends__item__current { color: $primary-text-color; } } @@ -337,6 +359,10 @@ &.active .avatar-stack .account__avatar { border-color: $ui-highlight-color; } + + .trends__item__current { + padding-right: 0; + } } } @@ -521,6 +547,12 @@ $fluid-breakpoint: $maximum-width + 20px; a { font-size: 14px; line-height: 20px; + } +} + +.notice-widget, +.placeholder-widget { + a { text-decoration: none; font-weight: 500; color: $ui-highlight-color; @@ -532,3 +564,38 @@ $fluid-breakpoint: $maximum-width + 20px; } } } + +.table-of-contents { + background: darken($ui-base-color, 4%); + min-height: 100%; + font-size: 14px; + border-radius: 4px; + + li a { + display: block; + font-weight: 500; + padding: 15px; + overflow: hidden; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-decoration: none; + color: $primary-text-color; + border-bottom: 1px solid lighten($ui-base-color, 4%); + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + + li:last-child a { + border-bottom: 0; + } + + li ul { + padding-left: 20px; + border-bottom: 1px solid lighten($ui-base-color, 4%); + } +} diff --git a/app/lib/activity_tracker.rb b/app/lib/activity_tracker.rb index ae3c11b6af5c99e6f852f2ac8d284218839184ed..81303b71584b3a5b43fcfb439435f6e22f47ca2d 100644 --- a/app/lib/activity_tracker.rb +++ b/app/lib/activity_tracker.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ActivityTracker - EXPIRE_AFTER = 90.days.seconds + EXPIRE_AFTER = 6.months.seconds class << self include Redisable diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index 66b5763a9cf5df8af0bc908cc36d1e80291ee144..a4a9baaee40e568c239b752835b80b0f720cbcb1 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -5,7 +5,7 @@ class ActivityPub::Activity include Redisable SUPPORTED_TYPES = %w(Note Question).freeze - CONVERTED_TYPES = %w(Image Video Article Page).freeze + CONVERTED_TYPES = %w(Image Audio Video Article Page).freeze def initialize(json, account, **options) @json = json diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index 1aa6ee9ec2dd72679488bc92994cefc0e5ac8d9f..34c646668b15ade966f24dbf28f094758e97e6ab 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -40,7 +40,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity end def announceable?(status) - status.account_id == @account.id || status.public_visibility? || status.unlisted_visibility? + status.account_id == @account.id || status.distributable? end def related_to_local_activity? diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 00f0dd42d7ba3817b788052b8820d1a98279561b..76bf9b2e55b244ffe687a017c23f3556d414e555 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -41,8 +41,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity resolve_thread(@status) fetch_replies(@status) + check_for_spam distribute(@status) - forward_for_reply if @status.public_visibility? || @status.unlisted_visibility? + forward_for_reply if @status.distributable? end def find_existing_status @@ -147,12 +148,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity def process_hashtag(tag) return if tag['name'].blank? - hashtag = tag['name'].gsub(/\A#/, '').mb_chars.downcase - hashtag = Tag.where(name: hashtag).first_or_create!(name: hashtag) - - return if @tags.include?(hashtag) - - @tags << hashtag + Tag.find_or_create_by_names(tag['name']) do |hashtag| + @tags << hashtag unless @tags.include?(hashtag) + end rescue ActiveRecord::RecordInvalid nil end @@ -191,22 +189,25 @@ class ActivityPub::Activity::Create < ActivityPub::Activity media_attachments = [] as_array(@object['attachment']).each do |attachment| - next if attachment['url'].blank? + next if attachment['url'].blank? || media_attachments.size >= 4 - href = Addressable::URI.parse(attachment['url']).normalize.to_s - media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence, focus: attachment['focalPoint'], blurhash: supported_blurhash?(attachment['blurhash']) ? attachment['blurhash'] : nil) - media_attachments << media_attachment + begin + href = Addressable::URI.parse(attachment['url']).normalize.to_s + media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence, focus: attachment['focalPoint'], blurhash: supported_blurhash?(attachment['blurhash']) ? attachment['blurhash'] : nil) + media_attachments << media_attachment - next if unsupported_media_type?(attachment['mediaType']) || skip_download? + next if unsupported_media_type?(attachment['mediaType']) || skip_download? - media_attachment.file_remote_url = href - media_attachment.save + media_attachment.file_remote_url = href + media_attachment.save + rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError + RedownloadMediaWorker.perform_in(rand(30..600).seconds, media_attachment.id) + end end media_attachments rescue Addressable::URI::InvalidURIError => e - Rails.logger.debug e - + Rails.logger.debug "Invalid URL in attachment: #{e}" media_attachments end @@ -231,25 +232,40 @@ class ActivityPub::Activity::Create < ActivityPub::Activity items = @object['oneOf'] end + voters_count = @object['votersCount'] + @account.polls.new( multiple: multiple, expires_at: expires_at, options: items.map { |item| item['name'].presence || item['content'] }.compact, - cached_tallies: items.map { |item| item.dig('replies', 'totalItems') || 0 } + cached_tallies: items.map { |item| item.dig('replies', 'totalItems') || 0 }, + voters_count: voters_count ) end def poll_vote? return false if replied_to_status.nil? || replied_to_status.preloadable_poll.nil? || !replied_to_status.local? || !replied_to_status.preloadable_poll.options.include?(@object['name']) - unless replied_to_status.preloadable_poll.expired? - replied_to_status.preloadable_poll.votes.create!(account: @account, choice: replied_to_status.preloadable_poll.options.index(@object['name']), uri: @object['id']) - ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, replied_to_status.id) unless replied_to_status.preloadable_poll.hide_totals? - end + poll_vote! unless replied_to_status.preloadable_poll.expired? true end + def poll_vote! + poll = replied_to_status.preloadable_poll + already_voted = true + RedisLock.acquire(poll_lock_options) do |lock| + if lock.acquired? + already_voted = poll.votes.where(account: @account).exists? + poll.votes.create!(account: @account, choice: poll.options.index(@object['name']), uri: @object['id']) + else + raise Mastodon::RaceConditionError + end + end + increment_voters_count! unless already_voted + ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, replied_to_status.id) unless replied_to_status.preloadable_poll.hide_totals? + end + def resolve_thread(status) return unless status.reply? && status.thread.nil? && Request.valid_url?(in_reply_to_uri) ThreadResolveWorker.perform_async(status.id, in_reply_to_uri) @@ -406,12 +422,31 @@ class ActivityPub::Activity::Create < ActivityPub::Activity Account.local.where(username: local_usernames).exists? end + def check_for_spam + SpamCheck.perform(@status) + end + def forward_for_reply return unless @json['signature'].present? && reply_to_local? ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id, [@account.preferred_inbox_url]) end + def increment_voters_count! + poll = replied_to_status.preloadable_poll + unless poll.voters_count.nil? + poll.voters_count = poll.voters_count + 1 + poll.save + end + rescue ActiveRecord::StaleObjectError + poll.reload + retry + end + def lock_options { redis: Redis.current, key: "create:#{@object['id']}" } end + + def poll_lock_options + { redis: Redis.current, key: "vote:#{replied_to_status.poll_id}:#{@account.id}" } + end end diff --git a/app/lib/activitypub/activity/delete.rb b/app/lib/activitypub/activity/delete.rb index 0eb14b89cef4383dc59e9f0c8884388d39e1a84a..dc9ff580c1ae8b4343d41f38f74a040ff0b99475 100644 --- a/app/lib/activitypub/activity/delete.rb +++ b/app/lib/activitypub/activity/delete.rb @@ -13,8 +13,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity def delete_person lock_or_return("delete_in_progress:#{@account.id}") do - SuspendAccountService.new.call(@account) - @account.destroy! + SuspendAccountService.new.call(@account, reserve_username: false) end end @@ -31,7 +30,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity return if @status.nil? - if @status.public_visibility? || @status.unlisted_visibility? + if @status.distributable? forward_for_reply forward_for_reblogs end @@ -70,7 +69,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity end def delete_now! - RemoveStatusService.new.call(@status) + RemoveStatusService.new.call(@status, redraft: false) end def payload diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 3eb88339aebaa6cf4abbd0c4e857a00a06ebe52a..ec92f4255f6b0295acc9bb457f90fcc22ce23dd6 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -8,7 +8,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity return if target_account.nil? || !target_account.local? || delete_arrived_first?(@json['id']) || @account.requested?(target_account) - if target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved? + if target_account.blocking?(@account) || target_account.domain_blocking?(@account.domain) || target_account.moved? || target_account.instance_actor? reject_follow_request!(target_account) return end @@ -21,7 +21,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) - if target_account.locked? + if target_account.locked? || @account.silenced? NotifyService.new.call(target_account, follow_request) else AuthorizeFollowService.new.call(@account, target_account) diff --git a/app/lib/activitypub/activity/move.rb b/app/lib/activitypub/activity/move.rb index d7a5f595cc1c6707ce247fb6f5dafa1a924f6dd6..12bb82d259a9dac0bafa154330557984c3aebeab 100644 --- a/app/lib/activitypub/activity/move.rb +++ b/app/lib/activitypub/activity/move.rb @@ -10,17 +10,16 @@ class ActivityPub::Activity::Move < ActivityPub::Activity target_account = ActivityPub::FetchRemoteAccountService.new.call(target_uri) - return if target_account.nil? || !target_account.also_known_as.include?(origin_account.uri) + if target_account.nil? || target_account.suspended? || !target_account.also_known_as.include?(origin_account.uri) + unmark_as_processing! + return + end # In case for some reason we didn't have a redirect for the profile already, set it - origin_account.update(moved_to_account: target_account) if origin_account.moved_to_account_id.nil? + origin_account.update(moved_to_account: target_account) # Initiate a re-follow for each follower - origin_account.followers.local.select(:id).find_in_batches do |follower_accounts| - UnfollowFollowWorker.push_bulk(follower_accounts.map(&:id)) do |follower_account_id| - [follower_account_id, origin_account.id, target_account.id] - end - end + MoveWorker.perform_async(origin_account.id, target_account.id) end private @@ -40,4 +39,8 @@ class ActivityPub::Activity::Move < ActivityPub::Activity def mark_as_processing! redis.setex("move_in_progress:#{@account.id}", PROCESSING_COOLDOWN, true) end + + def unmark_as_processing! + redis.del("move_in_progress:#{@account.id}") + end end diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index c259c96f410fb87c531656c3c6a586f4e985867d..2a8f723334652590055949839a6e17654a77b437 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -20,6 +20,8 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } }, identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' }, blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, + discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, + voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, }.freeze def self.default_key_transform @@ -31,21 +33,23 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base end def serializable_hash(options = nil) + named_contexts = {} + context_extensions = {} options = serialization_options(options) - serialized_hash = serializer.serializable_hash(options) + serialized_hash = serializer.serializable_hash(options.merge(named_contexts: named_contexts, context_extensions: context_extensions)) + serialized_hash = serialized_hash.select { |k, _| options[:fields].include?(k) } if options[:fields] serialized_hash = self.class.transform_key_casing!(serialized_hash, instance_options) - { '@context' => serialized_context }.merge(serialized_hash) + { '@context' => serialized_context(named_contexts, context_extensions) }.merge(serialized_hash) end private - def serialized_context + def serialized_context(named_contexts_map, context_extensions_map) context_array = [] - serializer_options = serializer.send(:instance_options) || {} - named_contexts = [:activitystreams] + serializer._named_contexts.keys + serializer_options.fetch(:named_contexts, {}).keys - context_extensions = serializer._context_extensions.keys + serializer_options.fetch(:context_extensions, {}).keys + named_contexts = [:activitystreams] + named_contexts_map.keys + context_extensions = context_extensions_map.keys named_contexts.each do |key| context_array << NAMED_CONTEXT_MAP[key] diff --git a/app/lib/activitypub/serializer.rb b/app/lib/activitypub/serializer.rb index 07bd8c494612e2117d3d1dd55fc34cd9953ced9b..1fdc7931041bcfe6ad51af744d606749be4da324 100644 --- a/app/lib/activitypub/serializer.rb +++ b/app/lib/activitypub/serializer.rb @@ -27,4 +27,12 @@ class ActivityPub::Serializer < ActiveModel::Serializer _context_extensions[extension_name] = true end end + + def serializable_hash(adapter_options = nil, options = {}, adapter_instance = self.class.serialization_adapter_instance) + unless adapter_options&.fetch(:named_contexts, nil).nil? + adapter_options[:named_contexts].merge!(_named_contexts) + adapter_options[:context_extensions].merge!(_context_extensions) + end + super(adapter_options, options, adapter_instance) + end end diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 595291342a361ea583adc5cfc9e4b19450c42c42..512272dbebe3bcc7b4647c2396ac07b789494c11 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -17,7 +17,7 @@ class ActivityPub::TagManager case target.object_type when :person - short_account_url(target) + target.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(target) when :note, :comment, :activity return activity_account_status_url(target.account, target) if target.reblog? short_account_status_url(target.account, target) @@ -29,7 +29,7 @@ class ActivityPub::TagManager case target.object_type when :person - account_url(target) + target.instance_actor? ? instance_actor_url : account_url(target) when :note, :comment, :activity return activity_account_status_url(target.account, target) if target.reblog? account_status_url(target.account, target) @@ -51,7 +51,7 @@ class ActivityPub::TagManager def replies_uri_for(target, page_params = nil) raise ArgumentError, 'target must be a local activity' unless %i(note comment activity).include?(target.object_type) && target.local? - replies_account_status_url(target.account, target, page_params) + account_status_replies_url(target.account, target, page_params) end # Primary audience of a status @@ -119,6 +119,7 @@ class ActivityPub::TagManager def uri_to_local_id(uri, param = :id) path_params = Rails.application.routes.recognize_path(uri) + path_params[:username] = Rails.configuration.x.local_domain if path_params[:controller] == 'instance_actors' path_params[param] end diff --git a/app/lib/connection_pool/shared_connection_pool.rb b/app/lib/connection_pool/shared_connection_pool.rb new file mode 100644 index 0000000000000000000000000000000000000000..2865a4108d4ff34f7f9fa3f8301a81ef67beb5d4 --- /dev/null +++ b/app/lib/connection_pool/shared_connection_pool.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'connection_pool' +require_relative './shared_timed_stack' + +class ConnectionPool::SharedConnectionPool < ConnectionPool + def initialize(options = {}, &block) + super(options, &block) + + @available = ConnectionPool::SharedTimedStack.new(@size, &block) + end + + delegate :size, :flush, to: :@available + + def with(preferred_tag, options = {}) + Thread.handle_interrupt(Exception => :never) do + conn = checkout(preferred_tag, options) + + begin + Thread.handle_interrupt(Exception => :immediate) do + yield conn + end + ensure + checkin(preferred_tag) + end + end + end + + def checkout(preferred_tag, options = {}) + if ::Thread.current[key(preferred_tag)] + ::Thread.current[key_count(preferred_tag)] += 1 + ::Thread.current[key(preferred_tag)] + else + ::Thread.current[key_count(preferred_tag)] = 1 + ::Thread.current[key(preferred_tag)] = @available.pop(preferred_tag, options[:timeout] || @timeout) + end + end + + def checkin(preferred_tag) + if ::Thread.current[key(preferred_tag)] + if ::Thread.current[key_count(preferred_tag)] == 1 + @available.push(::Thread.current[key(preferred_tag)]) + ::Thread.current[key(preferred_tag)] = nil + else + ::Thread.current[key_count(preferred_tag)] -= 1 + end + else + raise ConnectionPool::Error, 'no connections are checked out' + end + + nil + end + + private + + def key(tag) + :"#{@key}-#{tag}" + end + + def key_count(tag) + :"#{@key_count}-#{tag}" + end +end diff --git a/app/lib/connection_pool/shared_timed_stack.rb b/app/lib/connection_pool/shared_timed_stack.rb new file mode 100644 index 0000000000000000000000000000000000000000..14a5285c45753280f0380ad9cbc79efb303e48b9 --- /dev/null +++ b/app/lib/connection_pool/shared_timed_stack.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +class ConnectionPool::SharedTimedStack + def initialize(max = 0, &block) + @create_block = block + @max = max + @created = 0 + @queue = [] + @tagged_queue = Hash.new { |hash, key| hash[key] = [] } + @mutex = Mutex.new + @resource = ConditionVariable.new + end + + def push(connection) + @mutex.synchronize do + store_connection(connection) + @resource.broadcast + end + end + + alias << push + + def pop(preferred_tag, timeout = 5.0) + deadline = current_time + timeout + + @mutex.synchronize do + loop do + return fetch_preferred_connection(preferred_tag) unless @tagged_queue[preferred_tag].empty? + + connection = try_create(preferred_tag) + return connection if connection + + to_wait = deadline - current_time + raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0 + + @resource.wait(@mutex, to_wait) + end + end + end + + def empty? + size.zero? + end + + def size + @mutex.synchronize do + @queue.size + end + end + + def flush + @mutex.synchronize do + @queue.delete_if do |connection| + delete = !connection.in_use && (connection.dead || connection.seconds_idle >= RequestPool::MAX_IDLE_TIME) + + if delete + @tagged_queue[connection.site].delete(connection) + connection.close + @created -= 1 + end + + delete + end + end + end + + private + + def try_create(preferred_tag) + if @created == @max && !@queue.empty? + throw_away_connection = @queue.pop + @tagged_queue[throw_away_connection.site].delete(throw_away_connection) + @create_block.call(preferred_tag) + elsif @created != @max + connection = @create_block.call(preferred_tag) + @created += 1 + connection + end + end + + def fetch_preferred_connection(preferred_tag) + connection = @tagged_queue[preferred_tag].pop + @queue.delete(connection) + connection + end + + def current_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + + def store_connection(connection) + @tagged_queue[connection.site].push(connection) + @queue.push(connection) + end +end diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index ca3d890a813f7a503a38762ace96313f966131eb..d8b486b6094ef770ee458332625d5d3e809dad60 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -19,7 +19,7 @@ class FeedManager def filter?(timeline_type, status, receiver_id) if timeline_type == :home - filter_from_home?(status, receiver_id) + filter_from_home?(status, receiver_id, build_crutches(receiver_id, [status])) elsif timeline_type == :mentions filter_from_mentions?(status, receiver_id) else @@ -29,6 +29,7 @@ class FeedManager def push_to_home(account, status) return false unless add_to_feed(:home, account.id, status, account.user&.aggregates_reblogs?) + trim(:home, account.id) PushUpdateWorker.perform_async(account.id, status.id, "timeline:#{account.id}") if push_update_required?("timeline:#{account.id}") true @@ -36,6 +37,7 @@ class FeedManager def unpush_from_home(account, status) return false unless remove_from_feed(:home, account.id, status, account.user&.aggregates_reblogs?) + redis.publish("timeline:#{account.id}", Oj.dump(event: :delete, payload: status.id.to_s)) true end @@ -46,7 +48,9 @@ class FeedManager should_filter &&= !ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists? return false if should_filter end + return false unless add_to_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?) + trim(:list, list.id) PushUpdateWorker.perform_async(list.account_id, status.id, "timeline:list:#{list.id}") if push_update_required?("timeline:list:#{list.id}") true @@ -54,6 +58,7 @@ class FeedManager def unpush_from_list(list, status) return false unless remove_from_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?) + redis.publish("timeline:list:#{list.id}", Oj.dump(event: :delete, payload: status.id.to_s)) true end @@ -63,7 +68,7 @@ class FeedManager reblog_key = key(type, account_id, 'reblogs') # Remove any items past the MAX_ITEMS'th entry in our feed - redis.zremrangebyrank(timeline_key, '0', (-(FeedManager::MAX_ITEMS + 1)).to_s) + redis.zremrangebyrank(timeline_key, 0, -(FeedManager::MAX_ITEMS + 1)) # Get the score of the REBLOG_FALLOFF'th item in our feed, and stop # tracking anything after it for deduplication purposes. @@ -85,16 +90,21 @@ class FeedManager def merge_into_timeline(from_account, into_account) timeline_key = key(:home, into_account.id) - query = from_account.statuses.limit(FeedManager::MAX_ITEMS / 4) + aggregate = into_account.user&.aggregates_reblogs? + query = from_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4) if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4 - oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0 + oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i query = query.where('id > ?', oldest_home_score) end - query.each do |status| - next if status.direct_visibility? || status.limited_visibility? || filter?(:home, status, into_account) - add_to_feed(:home, into_account.id, status, into_account.user&.aggregates_reblogs?) + statuses = query.to_a + crutches = build_crutches(into_account.id, statuses) + + statuses.each do |status| + next if filter_from_home?(status, into_account, crutches) + + add_to_feed(:home, into_account.id, status, aggregate) end trim(:home, into_account.id) @@ -120,24 +130,35 @@ class FeedManager end def populate_feed(account) - added = 0 - limit = FeedManager::MAX_ITEMS / 2 - max_id = nil + limit = FeedManager::MAX_ITEMS / 2 + aggregate = account.user&.aggregates_reblogs? + timeline_key = key(:home, account.id) - loop do - statuses = Status.as_home_timeline(account) - .paginate_by_max_id(limit, max_id) + account.statuses.where.not(visibility: :direct).limit(limit).each do |status| + add_to_feed(:home, account.id, status, aggregate) + end - break if statuses.empty? + account.following.includes(:account_stat).find_each do |target_account| + if redis.zcard(timeline_key) >= limit + oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i + last_status_score = Mastodon::Snowflake.id_at(account.last_status_at) - statuses.each do |status| - next if filter_from_home?(status, account) - added += 1 if add_to_feed(:home, account.id, status, account.user&.aggregates_reblogs?) + # If the feed is full and this account has not posted more recently + # than the last item on the feed, then we can skip the whole account + # because none of its statuses would stay on the feed anyway + next if last_status_score < oldest_home_score end - break unless added.zero? + statuses = target_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(limit) + crutches = build_crutches(account.id, statuses) + + statuses.each do |status| + next if filter_from_home?(status, account, crutches) + + add_to_feed(:home, account.id, status, aggregate) + end - max_id = statuses.last.id + trim(:home, account.id) end end @@ -152,31 +173,33 @@ class FeedManager (context == :home ? Mute.where(account_id: receiver_id, target_account_id: account_ids).any? : Mute.where(account_id: receiver_id, target_account_id: account_ids, hide_notifications: true).any?) end - def filter_from_home?(status, receiver_id) + def filter_from_home?(status, receiver_id, crutches) return false if receiver_id == status.account_id return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) return true if phrase_filtered?(status, receiver_id, :home) - check_for_blocks = status.active_mentions.pluck(:account_id) + check_for_blocks = crutches[:active_mentions][status.id] || [] check_for_blocks.concat([status.account_id]) if status.reblog? check_for_blocks.concat([status.reblog.account_id]) - check_for_blocks.concat(status.reblog.active_mentions.pluck(:account_id)) + check_for_blocks.concat(crutches[:active_mentions][status.reblog_of_id] || []) end - return true if blocks_or_mutes?(receiver_id, check_for_blocks, :home) + return true if check_for_blocks.any? { |target_account_id| crutches[:blocking][target_account_id] || crutches[:muting][target_account_id] } if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply - should_filter = !Follow.where(account_id: receiver_id, target_account_id: status.in_reply_to_account_id).exists? # and I'm not following the person it's a reply to + should_filter = !crutches[:following][status.in_reply_to_account_id] # and I'm not following the person it's a reply to should_filter &&= receiver_id != status.in_reply_to_account_id # and it's not a reply to me should_filter &&= status.account_id != status.in_reply_to_account_id # and it's not a self-reply - return should_filter + + return !!should_filter elsif status.reblog? # Filter out a reblog - should_filter = Follow.where(account_id: receiver_id, target_account_id: status.account_id, show_reblogs: false).exists? # if the reblogger's reblogs are suppressed - should_filter ||= Block.where(account_id: status.reblog.account_id, target_account_id: receiver_id).exists? # or if the author of the reblogged status is blocking me - should_filter ||= AccountDomainBlock.where(account_id: receiver_id, domain: status.reblog.account.domain).exists? # or the author's domain is blocked - return should_filter + should_filter = crutches[:hiding_reblogs][status.account_id] # if the reblogger's reblogs are suppressed + should_filter ||= crutches[:blocked_by][status.reblog.account_id] # or if the author of the reblogged status is blocking me + should_filter ||= crutches[:domain_blocking][status.reblog.account.domain] # or the author's domain is blocked + + return !!should_filter end false @@ -308,4 +331,31 @@ class FeedManager redis.zrem(timeline_key, status.id) end + + def build_crutches(receiver_id, statuses) + crutches = {} + + crutches[:active_mentions] = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact).pluck(:status_id, :account_id).each_with_object({}) { |(id, account_id), mapping| (mapping[id] ||= []).push(account_id) } + + check_for_blocks = statuses.flat_map do |s| + arr = crutches[:active_mentions][s.id] || [] + arr.concat([s.account_id]) + + if s.reblog? + arr.concat([s.reblog.account_id]) + arr.concat(crutches[:active_mentions][s.reblog_of_id] || []) + end + + arr + end + + crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true } + crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true } + crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true } + crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true } + crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).each_with_object({}) { |domain, mapping| mapping[domain] = true } + crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).each_with_object({}) { |id, mapping| mapping[id] = true } + + crutches + end end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 6b5a8563ad829dcd883b01454d90fdb467af3300..990b9f63ed809b31ee43090a6c238a662d1f9ca8 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -84,8 +84,7 @@ class Formatter end def format_field(account, str, **options) - return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety - html = encode_and_link_urls(str, me: true) + html = account.local? ? encode_and_link_urls(str, me: true) : reformat(str) html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify] html.html_safe # rubocop:disable Rails/OutputSafety end @@ -294,10 +293,10 @@ class Formatter end def hashtag_html(tag) - "<a href=\"#{encode(tag_url(tag.downcase))}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{encode(tag)}</span></a>" + "<a href=\"#{encode(tag_url(tag))}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{encode(tag)}</span></a>" end def mention_html(account) - "<span class=\"h-card\"><a href=\"#{encode(TagManager.instance.url_for(account))}\" class=\"u-url mention\">@<span>#{encode(account.username)}</span></a></span>" + "<span class=\"h-card\"><a href=\"#{encode(ActivityPub::TagManager.instance.url_for(account))}\" class=\"u-url mention\">@<span>#{encode(account.username)}</span></a></span>" end end diff --git a/app/lib/nodeinfo/adapter.rb b/app/lib/nodeinfo/adapter.rb new file mode 100644 index 0000000000000000000000000000000000000000..1b48dcb98fadf2a0b96b8ac64e98e2adb1b901a4 --- /dev/null +++ b/app/lib/nodeinfo/adapter.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class NodeInfo::Adapter < ActiveModelSerializers::Adapter::Attributes + def self.default_key_transform + :camel_lower + end +end diff --git a/app/lib/ostatus/activity/base.rb b/app/lib/ostatus/activity/base.rb deleted file mode 100644 index db70f19980cd584afac66dda7d9d4707ebac4df1..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/base.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Base - include Redisable - - def initialize(xml, account = nil, **options) - @xml = xml - @account = account - @options = options - end - - def status? - [:activity, :note, :comment].include?(type) - end - - def verb - raw = @xml.at_xpath('./activity:verb', activity: OStatus::TagManager::AS_XMLNS).content - OStatus::TagManager::VERBS.key(raw) - rescue - :post - end - - def type - raw = @xml.at_xpath('./activity:object-type', activity: OStatus::TagManager::AS_XMLNS).content - OStatus::TagManager::TYPES.key(raw) - rescue - :activity - end - - def id - @xml.at_xpath('./xmlns:id', xmlns: OStatus::TagManager::XMLNS).content - end - - def url - link = @xml.xpath('./xmlns:link[@rel="alternate"]', xmlns: OStatus::TagManager::XMLNS).find { |link_candidate| link_candidate['type'] == 'text/html' } - link.nil? ? nil : link['href'] - end - - def activitypub_uri - link = @xml.xpath('./xmlns:link[@rel="alternate"]', xmlns: OStatus::TagManager::XMLNS).find { |link_candidate| ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(link_candidate['type']) } - link.nil? ? nil : link['href'] - end - - def activitypub_uri? - activitypub_uri.present? - end - - private - - def find_status(uri) - if OStatus::TagManager.instance.local_id?(uri) - local_id = OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Status') - return Status.find_by(id: local_id) - elsif ActivityPub::TagManager.instance.local_uri?(uri) - local_id = ActivityPub::TagManager.instance.uri_to_local_id(uri) - return Status.find_by(id: local_id) - end - - Status.find_by(uri: uri) - end - - def find_activitypub_status(uri, href) - tag_matches = /tag:([^,:]+)[^:]*:objectId=([\d]+)/.match(uri) - href_matches = %r{/users/([^/]+)}.match(href) - - unless tag_matches.nil? || href_matches.nil? - uri = "https://#{tag_matches[1]}/users/#{href_matches[1]}/statuses/#{tag_matches[2]}" - Status.find_by(uri: uri) - end - end -end diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb deleted file mode 100644 index 60de712db8adec04c6f63be0a7ce11e243e5b3df..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/creation.rb +++ /dev/null @@ -1,219 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Creation < OStatus::Activity::Base - def perform - if redis.exists("delete_upon_arrival:#{@account.id}:#{id}") - Rails.logger.debug "Delete for status #{id} was queued, ignoring" - return [nil, false] - end - - return [nil, false] if @account.suspended? || invalid_origin? - - RedisLock.acquire(lock_options) do |lock| - if lock.acquired? - # Return early if status already exists in db - @status = find_status(id) - return [@status, false] unless @status.nil? - @status = process_status - else - raise Mastodon::RaceConditionError - end - end - - [@status, true] - end - - def process_status - Rails.logger.debug "Creating remote status #{id}" - cached_reblog = reblog - status = nil - - # Skip if the reblogged status is not public - return if cached_reblog && !(cached_reblog.public_visibility? || cached_reblog.unlisted_visibility?) - - media_attachments = save_media.take(4) - - ApplicationRecord.transaction do - status = Status.create!( - uri: id, - url: url, - account: @account, - reblog: cached_reblog, - text: content, - spoiler_text: content_warning, - created_at: published, - override_timestamps: @options[:override_timestamps], - reply: thread?, - language: content_language, - visibility: visibility_scope, - conversation: find_or_create_conversation, - thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil, - media_attachment_ids: media_attachments.map(&:id), - sensitive: sensitive? - ) - - save_mentions(status) - save_hashtags(status) - save_emojis(status) - end - - if thread? && status.thread.nil? && Request.valid_url?(thread.second) - Rails.logger.debug "Trying to attach #{status.id} (#{id}) to #{thread.first}" - ThreadResolveWorker.perform_async(status.id, thread.second) - end - - Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution" - - LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text? - - # Only continue if the status is supposed to have arrived in real-time. - # Note that if @options[:override_timestamps] isn't set, the status - # may have a lower snowflake id than other existing statuses, potentially - # "hiding" it from paginated API calls - return status unless @options[:override_timestamps] || status.within_realtime_window? - - DistributionWorker.perform_async(status.id) - - status - end - - def content - @xml.at_xpath('./xmlns:content', xmlns: OStatus::TagManager::XMLNS).content - end - - def content_language - @xml.at_xpath('./xmlns:content', xmlns: OStatus::TagManager::XMLNS)['xml:lang']&.presence || 'en' - end - - def content_warning - @xml.at_xpath('./xmlns:summary', xmlns: OStatus::TagManager::XMLNS)&.content || '' - end - - def visibility_scope - @xml.at_xpath('./mastodon:scope', mastodon: OStatus::TagManager::MTDN_XMLNS)&.content&.to_sym || :public - end - - def published - @xml.at_xpath('./xmlns:published', xmlns: OStatus::TagManager::XMLNS).content - end - - def thread? - !@xml.at_xpath('./thr:in-reply-to', thr: OStatus::TagManager::THR_XMLNS).nil? - end - - def thread - thr = @xml.at_xpath('./thr:in-reply-to', thr: OStatus::TagManager::THR_XMLNS) - [thr['ref'], thr['href']] - end - - private - - def sensitive? - # OStatus-specific convention (not standard) - @xml.xpath('./xmlns:category', xmlns: OStatus::TagManager::XMLNS).any? { |category| category['term'] == 'nsfw' } - end - - def find_or_create_conversation - uri = @xml.at_xpath('./ostatus:conversation', ostatus: OStatus::TagManager::OS_XMLNS)&.attribute('ref')&.content - return if uri.nil? - - if OStatus::TagManager.instance.local_id?(uri) - local_id = OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Conversation') - return Conversation.find_by(id: local_id) - end - - Conversation.find_by(uri: uri) || Conversation.create!(uri: uri) - end - - def save_mentions(parent) - processed_account_ids = [] - - @xml.xpath('./xmlns:link[@rel="mentioned"]', xmlns: OStatus::TagManager::XMLNS).each do |link| - next if [OStatus::TagManager::TYPES[:group], OStatus::TagManager::TYPES[:collection]].include? link['ostatus:object-type'] - - mentioned_account = account_from_href(link['href']) - - next if mentioned_account.nil? || processed_account_ids.include?(mentioned_account.id) - - mentioned_account.mentions.where(status: parent).first_or_create(status: parent) - - # So we can skip duplicate mentions - processed_account_ids << mentioned_account.id - end - end - - def save_hashtags(parent) - tags = @xml.xpath('./xmlns:category', xmlns: OStatus::TagManager::XMLNS).map { |category| category['term'] }.select(&:present?) - ProcessHashtagsService.new.call(parent, tags) - end - - def save_media - do_not_download = DomainBlock.reject_media?(@account.domain) - media_attachments = [] - - @xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: OStatus::TagManager::XMLNS).each do |link| - next unless link['href'] - - media = MediaAttachment.where(status: nil, remote_url: link['href']).first_or_initialize(account: @account, status: nil, remote_url: link['href']) - parsed_url = Addressable::URI.parse(link['href']).normalize - - next if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? - - media.save - media_attachments << media - - next if do_not_download - - begin - media.file_remote_url = link['href'] - media.save! - rescue ActiveRecord::RecordInvalid - next - end - end - - media_attachments - end - - def save_emojis(parent) - do_not_download = DomainBlock.reject_media?(parent.account.domain) - - return if do_not_download - - @xml.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS).each do |link| - next unless link['href'] && link['name'] - - shortcode = link['name'].delete(':') - emoji = CustomEmoji.find_by(shortcode: shortcode, domain: parent.account.domain) - - next unless emoji.nil? - - emoji = CustomEmoji.new(shortcode: shortcode, domain: parent.account.domain) - emoji.image_remote_url = link['href'] - emoji.save - end - end - - def account_from_href(href) - url = Addressable::URI.parse(href).normalize - - if TagManager.instance.web_domain?(url.host) - Account.find_local(url.path.gsub('/users/', '')) - else - Account.where(uri: href).or(Account.where(url: href)).first || FetchRemoteAccountService.new.call(href) - end - end - - def invalid_origin? - return false unless id.start_with?('http') # Legacy IDs cannot be checked - - needle = Addressable::URI.parse(id).normalized_host - - !(needle.casecmp(@account.domain).zero? || - needle.casecmp(Addressable::URI.parse(@account.remote_url.presence || @account.uri).normalized_host).zero?) - end - - def lock_options - { redis: Redis.current, key: "create:#{id}" } - end -end diff --git a/app/lib/ostatus/activity/deletion.rb b/app/lib/ostatus/activity/deletion.rb deleted file mode 100644 index c98f5ee0ad9b063a55a5c8bda1a3b7eeb9198be2..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/deletion.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Deletion < OStatus::Activity::Base - def perform - Rails.logger.debug "Deleting remote status #{id}" - - status = Status.find_by(uri: id, account: @account) - status ||= Status.find_by(uri: activitypub_uri, account: @account) if activitypub_uri? - - if status.nil? - redis.setex("delete_upon_arrival:#{@account.id}:#{id}", 6 * 3_600, id) - else - RemoveStatusService.new.call(status) - end - end -end diff --git a/app/lib/ostatus/activity/general.rb b/app/lib/ostatus/activity/general.rb deleted file mode 100644 index 8a6aabc3372edb36b0d68bf12f47cd0c2a53b7b9..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/general.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::General < OStatus::Activity::Base - def specialize - special_class&.new(@xml, @account, @options) - end - - private - - def special_class - case verb - when :post - OStatus::Activity::Post - when :share - OStatus::Activity::Share - when :delete - OStatus::Activity::Deletion - end - end -end diff --git a/app/lib/ostatus/activity/post.rb b/app/lib/ostatus/activity/post.rb deleted file mode 100644 index 755ed865639a5663da4b5a5737d57ef44b02f75b..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/post.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Post < OStatus::Activity::Creation - def perform - status, just_created = super - - if just_created - status.mentions.includes(:account).each do |mention| - mentioned_account = mention.account - next unless mentioned_account.local? - NotifyService.new.call(mentioned_account, mention) - end - end - - status - end - - private - - def reblog - nil - end -end diff --git a/app/lib/ostatus/activity/remote.rb b/app/lib/ostatus/activity/remote.rb deleted file mode 100644 index 5b204b6d8f0f022278d4f18cea7a36b546be3d94..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/remote.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Remote < OStatus::Activity::Base - def perform - if activitypub_uri? - find_status(activitypub_uri) || FetchRemoteStatusService.new.call(url) - else - find_status(id) || FetchRemoteStatusService.new.call(url) - end - end -end diff --git a/app/lib/ostatus/activity/share.rb b/app/lib/ostatus/activity/share.rb deleted file mode 100644 index 5ca6014154ba7a564a8d53a196fa2b13c30dfa80..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/activity/share.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -class OStatus::Activity::Share < OStatus::Activity::Creation - def perform - return if reblog.nil? - - status, just_created = super - NotifyService.new.call(reblog.account, status) if reblog.account.local? && just_created - status - end - - def object - @xml.at_xpath('.//activity:object', activity: OStatus::TagManager::AS_XMLNS) - end - - private - - def reblog - return @reblog if defined? @reblog - - original_status = OStatus::Activity::Remote.new(object).perform - return if original_status.nil? - - @reblog = original_status.reblog? ? original_status.reblog : original_status - end -end diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb deleted file mode 100644 index 9a05d96cf9cf721fc4402f227db4c7be050e66ed..0000000000000000000000000000000000000000 --- a/app/lib/ostatus/atom_serializer.rb +++ /dev/null @@ -1,378 +0,0 @@ -# frozen_string_literal: true - -class OStatus::AtomSerializer - include RoutingHelper - include ActionView::Helpers::SanitizeHelper - - class << self - def render(element) - document = Ox::Document.new(version: '1.0') - document << element - ('<?xml version="1.0"?>' + Ox.dump(element, effort: :tolerant)).force_encoding('UTF-8') - end - end - - def author(account) - author = Ox::Element.new('author') - - uri = OStatus::TagManager.instance.uri_for(account) - - append_element(author, 'id', uri) - append_element(author, 'activity:object-type', OStatus::TagManager::TYPES[:person]) - append_element(author, 'uri', uri) - append_element(author, 'name', account.username) - append_element(author, 'email', account.local? ? account.local_username_and_domain : account.acct) - append_element(author, 'summary', Formatter.instance.simplified_format(account).to_str, type: :html) if account.note? - append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account)) - append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original))) if account.avatar? - append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original))) if account.header? - account.emojis.each do |emoji| - append_element(author, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode) - end - append_element(author, 'poco:preferredUsername', account.username) - append_element(author, 'poco:displayName', account.display_name) if account.display_name? - append_element(author, 'poco:note', account.local? ? account.note : strip_tags(account.note)) if account.note? - append_element(author, 'mastodon:scope', account.locked? ? :private : :public) - - author - end - - def feed(account, stream_entries) - feed = Ox::Element.new('feed') - - add_namespaces(feed) - - append_element(feed, 'id', account_url(account, format: 'atom')) - append_element(feed, 'title', account.display_name.presence || account.username) - append_element(feed, 'subtitle', account.note) - append_element(feed, 'updated', account.updated_at.iso8601) - append_element(feed, 'logo', full_asset_url(account.avatar.url(:original))) - - feed << author(account) - - append_element(feed, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account)) - append_element(feed, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_url(account, format: 'atom')) - append_element(feed, 'link', nil, rel: :next, type: 'application/atom+xml', href: account_url(account, format: 'atom', max_id: stream_entries.last.id)) if stream_entries.size == 20 - append_element(feed, 'link', nil, rel: :hub, href: api_push_url) - append_element(feed, 'link', nil, rel: :salmon, href: api_salmon_url(account.id)) - - stream_entries.each do |stream_entry| - feed << entry(stream_entry) - end - - feed - end - - def entry(stream_entry, root = false) - entry = Ox::Element.new('entry') - - add_namespaces(entry) if root - - append_element(entry, 'id', OStatus::TagManager.instance.uri_for(stream_entry.status)) - append_element(entry, 'published', stream_entry.created_at.iso8601) - append_element(entry, 'updated', stream_entry.updated_at.iso8601) - append_element(entry, 'title', stream_entry&.status&.title || "#{stream_entry.account.acct} deleted status") - - entry << author(stream_entry.account) if root - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[stream_entry.object_type]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[stream_entry.verb]) - - entry << object(stream_entry.target) if stream_entry.targeted? - - if stream_entry.status.nil? - append_element(entry, 'content', 'Deleted status') - elsif stream_entry.status.destroyed? - append_element(entry, 'content', 'Deleted status') - append_element(entry, 'link', nil, rel: :alternate, type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(stream_entry.status)) if stream_entry.account.local? - else - serialize_status_attributes(entry, stream_entry.status) - end - - append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(stream_entry.status)) - append_element(entry, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom')) - append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(stream_entry.thread), href: ::TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded? - append_element(entry, 'ostatus:conversation', nil, ref: conversation_uri(stream_entry.status.conversation)) unless stream_entry&.status&.conversation_id.nil? - - entry - end - - def object(status) - object = Ox::Element.new('activity:object') - - append_element(object, 'id', OStatus::TagManager.instance.uri_for(status)) - append_element(object, 'published', status.created_at.iso8601) - append_element(object, 'updated', status.updated_at.iso8601) - append_element(object, 'title', status.title) - - object << author(status.account) - - append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[status.object_type]) - append_element(object, 'activity:verb', OStatus::TagManager::VERBS[status.verb]) - - serialize_status_attributes(object, status) - - append_element(object, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(status)) - append_element(object, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(status.thread), href: ::TagManager.instance.url_for(status.thread)) unless status.thread.nil? - append_element(object, 'ostatus:conversation', nil, ref: conversation_uri(status.conversation)) unless status.conversation_id.nil? - - object - end - - def follow_salmon(follow) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{follow.account.acct} started following #{follow.target_account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(follow.created_at, follow.id, 'Follow')) - append_element(entry, 'title', description) - append_element(entry, 'content', description, type: :html) - - entry << author(follow.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:follow]) - - object = author(follow.target_account) - object.value = 'activity:object' - - entry << object - entry - end - - def follow_request_salmon(follow_request) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(follow_request.created_at, follow_request.id, 'FollowRequest')) - append_element(entry, 'title', "#{follow_request.account.acct} requested to follow #{follow_request.target_account.acct}") - - entry << author(follow_request.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) - - object = author(follow_request.target_account) - object.value = 'activity:object' - - entry << object - entry - end - - def authorize_follow_request_salmon(follow_request) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest')) - append_element(entry, 'title', "#{follow_request.target_account.acct} authorizes follow request by #{follow_request.account.acct}") - - entry << author(follow_request.target_account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:authorize]) - - object = Ox::Element.new('activity:object') - object << author(follow_request.account) - - append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(object, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) - - inner_object = author(follow_request.target_account) - inner_object.value = 'activity:object' - - object << inner_object - entry << object - entry - end - - def reject_follow_request_salmon(follow_request) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow_request.id, 'FollowRequest')) - append_element(entry, 'title', "#{follow_request.target_account.acct} rejects follow request by #{follow_request.account.acct}") - - entry << author(follow_request.target_account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:reject]) - - object = Ox::Element.new('activity:object') - object << author(follow_request.account) - - append_element(object, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(object, 'activity:verb', OStatus::TagManager::VERBS[:request_friend]) - - inner_object = author(follow_request.target_account) - inner_object.value = 'activity:object' - - object << inner_object - entry << object - entry - end - - def unfollow_salmon(follow) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{follow.account.acct} is no longer following #{follow.target_account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, follow.id, 'Follow')) - append_element(entry, 'title', description) - append_element(entry, 'content', description, type: :html) - - entry << author(follow.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unfollow]) - - object = author(follow.target_account) - object.value = 'activity:object' - - entry << object - entry - end - - def block_salmon(block) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{block.account.acct} no longer wishes to interact with #{block.target_account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block')) - append_element(entry, 'title', description) - - entry << author(block.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:block]) - - object = author(block.target_account) - object.value = 'activity:object' - - entry << object - entry - end - - def unblock_salmon(block) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{block.account.acct} no longer blocks #{block.target_account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, block.id, 'Block')) - append_element(entry, 'title', description) - - entry << author(block.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unblock]) - - object = author(block.target_account) - object.value = 'activity:object' - - entry << object - entry - end - - def favourite_salmon(favourite) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{favourite.account.acct} favourited a status by #{favourite.status.account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(favourite.created_at, favourite.id, 'Favourite')) - append_element(entry, 'title', description) - append_element(entry, 'content', description, type: :html) - - entry << author(favourite.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:favorite]) - - entry << object(favourite.status) - - append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status)) - - entry - end - - def unfavourite_salmon(favourite) - entry = Ox::Element.new('entry') - add_namespaces(entry) - - description = "#{favourite.account.acct} no longer favourites a status by #{favourite.status.account.acct}" - - append_element(entry, 'id', OStatus::TagManager.instance.unique_tag(Time.now.utc, favourite.id, 'Favourite')) - append_element(entry, 'title', description) - append_element(entry, 'content', description, type: :html) - - entry << author(favourite.account) - - append_element(entry, 'activity:object-type', OStatus::TagManager::TYPES[:activity]) - append_element(entry, 'activity:verb', OStatus::TagManager::VERBS[:unfavorite]) - - entry << object(favourite.status) - - append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status)) - - entry - end - - private - - def append_element(parent, name, content = nil, **attributes) - element = Ox::Element.new(name) - attributes.each { |k, v| element[k] = sanitize_str(v) } - element << sanitize_str(content) unless content.nil? - parent << element - end - - def sanitize_str(raw_str) - raw_str.to_s - end - - def conversation_uri(conversation) - return conversation.uri if conversation.uri? - OStatus::TagManager.instance.unique_tag(conversation.created_at, conversation.id, 'Conversation') - end - - def add_namespaces(parent) - parent['xmlns'] = OStatus::TagManager::XMLNS - parent['xmlns:thr'] = OStatus::TagManager::THR_XMLNS - parent['xmlns:activity'] = OStatus::TagManager::AS_XMLNS - parent['xmlns:poco'] = OStatus::TagManager::POCO_XMLNS - parent['xmlns:media'] = OStatus::TagManager::MEDIA_XMLNS - parent['xmlns:ostatus'] = OStatus::TagManager::OS_XMLNS - parent['xmlns:mastodon'] = OStatus::TagManager::MTDN_XMLNS - end - - def serialize_status_attributes(entry, status) - append_element(entry, 'link', nil, rel: :alternate, type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(status)) if status.account.local? - - append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text? - append_element(entry, 'content', Formatter.instance.format(status, inline_poll_options: true).to_str || '.', type: 'html', 'xml:lang': status.language) - - status.active_mentions.sort_by(&:id).each do |mentioned| - append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account)) - end - - append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:collection], href: OStatus::TagManager::COLLECTIONS[:public]) if status.public_visibility? - - status.tags.each do |tag| - append_element(entry, 'category', nil, term: tag.name) - end - - status.media_attachments.each do |media| - append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false))) - end - - append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive? && status.media_attachments.any? - append_element(entry, 'mastodon:scope', status.visibility) - - status.emojis.each do |emoji| - append_element(entry, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode) - end - end -end diff --git a/app/lib/request.rb b/app/lib/request.rb index e555ae6a109b759255f5333c801345ed64413ccf..42ccc6513d175136fb2008585deecd0d2ce24c76 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -17,15 +17,22 @@ end class Request REQUEST_TARGET = '(request-target)' + # We enforce a 5s timeout on DNS resolving, 5s timeout on socket opening + # and 5s timeout on the TLS handshake, meaning the worst case should take + # about 15s in total + TIMEOUT = { connect: 5, read: 10, write: 10 }.freeze + include RoutingHelper def initialize(verb, url, **options) raise ArgumentError if url.blank? - @verb = verb - @url = Addressable::URI.parse(url).normalize - @options = options.merge(use_proxy? ? Rails.configuration.x.http_client_proxy : { socket_class: Socket }) - @headers = {} + @verb = verb + @url = Addressable::URI.parse(url).normalize + @http_client = options.delete(:http_client) + @options = options.merge(socket_class: use_proxy? ? ProxySocket : Socket) + @options = @options.merge(Rails.configuration.x.http_client_proxy) if use_proxy? + @headers = {} raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service? @@ -33,8 +40,8 @@ class Request set_digest! if options.key?(:body) end - def on_behalf_of(account, key_id_format = :acct, sign_with: nil) - raise ArgumentError unless account.local? + def on_behalf_of(account, key_id_format = :uri, sign_with: nil) + raise ArgumentError, 'account must not be nil' if account.nil? @account = account @keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : @account.keypair @@ -50,15 +57,24 @@ class Request def perform begin - response = http_client.headers(headers).public_send(@verb, @url.to_s, @options) + response = http_client.public_send(@verb, @url.to_s, @options.merge(headers: headers)) rescue => e - raise e.class, "#{e.message} on #{@url}", e.backtrace[0] + raise e.class, "#{e.message} on #{@url}", e.backtrace end begin - yield response.extend(ClientLimit) if block_given? + response = response.extend(ClientLimit) + + # If we are using a persistent connection, we have to + # read every response to be able to move forward at all. + # However, simply calling #to_s or #flush may not be safe, + # as the response body, if malicious, could be too big + # for our memory. So we use the #body_with_limit method + response.body_with_limit if http_client.persistent? + + yield response if block_given? ensure - http_client.close + http_client.close unless http_client.persistent? end end @@ -76,6 +92,10 @@ class Request %w(http https).include?(parsed_url.scheme) && parsed_url.host.present? end + + def http_client + HTTP.use(:auto_inflate).timeout(:per_operation, TIMEOUT.dup).follow(max_hops: 2) + end end private @@ -116,16 +136,8 @@ class Request end end - def timeout - # We enforce a 1s timeout on DNS resolving, 10s timeout on socket opening - # and 5s timeout on the TLS handshake, meaning the worst case should take - # about 16s in total - - { connect: 5, read: 10, write: 10 } - end - def http_client - @http_client ||= HTTP.use(:auto_inflate).timeout(:per_operation, timeout).follow(max_hops: 2) + @http_client ||= Request.http_client end def use_proxy? @@ -166,26 +178,67 @@ class Request class Socket < TCPSocket class << self def open(host, *args) - return super(host, *args) if thru_hidden_service?(host) - outer_e = nil + port = args.first - Resolv::DNS.open do |dns| - dns.timeouts = 5 + addresses = [] + begin + addresses = [IPAddr.new(host)] + rescue IPAddr::InvalidAddressError + Resolv::DNS.open do |dns| + dns.timeouts = 5 + addresses = dns.getaddresses(host).take(2) + end + end - addresses = dns.getaddresses(host).take(2) - time_slot = 10.0 / addresses.size + socks = [] + addr_by_socket = {} - addresses.each do |address| - begin - raise Mastodon::HostValidationError if PrivateAddressCheck.private_address?(IPAddr.new(address.to_s)) + addresses.each do |address| + begin + check_private_address(address) + + sock = ::Socket.new(address.is_a?(Resolv::IPv6) ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0) + sockaddr = ::Socket.pack_sockaddr_in(port, address.to_s) + + sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1) - ::Timeout.timeout(time_slot, HTTP::TimeoutError) do - return super(address.to_s, *args) - end + sock.connect_nonblock(sockaddr) + + # If that hasn't raised an exception, we somehow managed to connect + # immediately, close pending sockets and return immediately + socks.each(&:close) + return sock + rescue IO::WaitWritable + socks << sock + addr_by_socket[sock] = sockaddr + rescue => e + outer_e = e + end + end + + until socks.empty? + _, available_socks, = IO.select(nil, socks, nil, Request::TIMEOUT[:connect]) + + if available_socks.nil? + socks.each(&:close) + raise HTTP::TimeoutError, "Connect timed out after #{Request::TIMEOUT[:connect]} seconds" + end + + available_socks.each do |sock| + socks.delete(sock) + + begin + sock.connect_nonblock(addr_by_socket[sock]) + rescue Errno::EISCONN rescue => e + sock.close outer_e = e + next end + + socks.each(&:close) + return sock end end @@ -198,11 +251,21 @@ class Request alias new open - def thru_hidden_service?(host) - Rails.configuration.x.access_to_hidden_service && /\.(onion|i2p)$/.match(host) + def check_private_address(address) + raise Mastodon::HostValidationError if PrivateAddressCheck.private_address?(IPAddr.new(address.to_s)) + end + end + end + + class ProxySocket < Socket + class << self + def check_private_address(_address) + # Accept connections to private addresses as HTTP proxies will usually + # be on local addresses + nil end end end - private_constant :ClientLimit, :Socket + private_constant :ClientLimit, :Socket, :ProxySocket end diff --git a/app/lib/request_pool.rb b/app/lib/request_pool.rb new file mode 100644 index 0000000000000000000000000000000000000000..e5899a79aab6cc50d8a4a2a2edef0a34f941a3cb --- /dev/null +++ b/app/lib/request_pool.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require_relative './connection_pool/shared_connection_pool' + +class RequestPool + def self.current + @current ||= RequestPool.new + end + + class Reaper + attr_reader :pool, :frequency + + def initialize(pool, frequency) + @pool = pool + @frequency = frequency + end + + def run + return unless frequency&.positive? + + Thread.new(frequency, pool) do |t, p| + loop do + sleep t + p.flush + end + end + end + end + + MAX_IDLE_TIME = 30 + WAIT_TIMEOUT = 5 + MAX_POOL_SIZE = ENV.fetch('MAX_REQUEST_POOL_SIZE', 512).to_i + + class Connection + attr_reader :site, :last_used_at, :created_at, :in_use, :dead, :fresh + + def initialize(site) + @site = site + @http_client = http_client + @last_used_at = nil + @created_at = current_time + @dead = false + @fresh = true + end + + def use + @last_used_at = current_time + @in_use = true + + retries = 0 + + begin + yield @http_client + rescue HTTP::ConnectionError + # It's possible the connection was closed, so let's + # try re-opening it once + + close + + if @fresh || retries.positive? + raise + else + @http_client = http_client + retries += 1 + retry + end + rescue StandardError + # If this connection raises errors of any kind, it's + # better if it gets reaped as soon as possible + + close + @dead = true + raise + end + ensure + @fresh = false + @in_use = false + end + + def seconds_idle + current_time - (@last_used_at || @created_at) + end + + def close + @http_client.close + end + + private + + def http_client + Request.http_client.persistent(@site, timeout: MAX_IDLE_TIME) + end + + def current_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + end + + def initialize + @pool = ConnectionPool::SharedConnectionPool.new(size: MAX_POOL_SIZE, timeout: WAIT_TIMEOUT) { |site| Connection.new(site) } + @reaper = Reaper.new(self, 30) + @reaper.run + end + + def with(site, &block) + @pool.with(site) do |connection| + ActiveSupport::Notifications.instrument('with.request_pool', miss: connection.fresh, host: connection.site) do + connection.use(&block) + end + end + end + + delegate :size, :flush, to: :@pool +end diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb new file mode 100644 index 0000000000000000000000000000000000000000..15956d4cfd207502db16b8468dc3e99ec49c7cbc --- /dev/null +++ b/app/lib/search_query_parser.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class SearchQueryParser < Parslet::Parser + rule(:term) { match('[^\s":]').repeat(1).as(:term) } + rule(:quote) { str('"') } + rule(:colon) { str(':') } + rule(:space) { match('\s').repeat(1) } + rule(:operator) { (str('+') | str('-')).as(:operator) } + rule(:prefix) { (term >> colon).as(:prefix) } + rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } + rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } + rule(:clause) { (prefix.maybe >> operator.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:query) { (clause >> space.maybe).repeat.as(:query) } + root(:query) +end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb new file mode 100644 index 0000000000000000000000000000000000000000..6a299f59d8a27d9565659b0627cc43f3bc74c919 --- /dev/null +++ b/app/lib/search_query_transformer.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +class SearchQueryTransformer < Parslet::Transform + class Query + attr_reader :should_clauses, :must_not_clauses, :must_clauses + + def initialize(clauses) + grouped = clauses.chunk(&:operator).to_h + @should_clauses = grouped.fetch(:should, []) + @must_not_clauses = grouped.fetch(:must_not, []) + @must_clauses = grouped.fetch(:must, []) + end + + def apply(search) + should_clauses.each { |clause| search = search.query.should(clause_to_query(clause)) } + must_clauses.each { |clause| search = search.query.must(clause_to_query(clause)) } + must_not_clauses.each { |clause| search = search.query.must_not(clause_to_query(clause)) } + search.query.minimum_should_match(1) + end + + private + + def clause_to_query(clause) + case clause + when TermClause + { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'] } } + when PhraseClause + { match_phrase: { text: { query: clause.phrase } } } + else + raise "Unexpected clause type: #{clause}" + end + end + end + + class Operator + class << self + def symbol(str) + case str + when '+' + :must + when '-' + :must_not + when nil + :should + else + raise "Unknown operator: #{str}" + end + end + end + end + + class TermClause + attr_reader :prefix, :operator, :term + + def initialize(prefix, operator, term) + @prefix = prefix + @operator = Operator.symbol(operator) + @term = term + end + end + + class PhraseClause + attr_reader :prefix, :operator, :phrase + + def initialize(prefix, operator, phrase) + @prefix = prefix + @operator = Operator.symbol(operator) + @phrase = phrase + end + end + + rule(clause: subtree(:clause)) do + prefix = clause[:prefix][:term].to_s if clause[:prefix] + operator = clause[:operator]&.to_s + + if clause[:term] + TermClause.new(prefix, operator, clause[:term].to_s) + elsif clause[:shortcode] + TermClause.new(prefix, operator, ":#{clause[:term]}:") + elsif clause[:phrase] + PhraseClause.new(prefix, operator, clause[:phrase].map { |p| p[:term].to_s }.join(' ')) + else + raise "Unexpected clause type: #{clause}" + end + end + + rule(query: sequence(:clauses)) { Query.new(clauses) } +end diff --git a/app/lib/settings/scoped_settings.rb b/app/lib/settings/scoped_settings.rb index 3653ab1149d5432c53cb709f0b6e3ba4e5e022c9..9ca39510acd3a3ac567de71cd6b60c021946055f 100644 --- a/app/lib/settings/scoped_settings.rb +++ b/app/lib/settings/scoped_settings.rb @@ -4,6 +4,7 @@ module Settings class ScopedSettings DEFAULTING_TO_UNSCOPED = %w( theme + noindex ).freeze def initialize(object) diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb new file mode 100644 index 0000000000000000000000000000000000000000..5b40514fd064017422d35b25d2291adfe162f227 --- /dev/null +++ b/app/lib/spam_check.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +class SpamCheck + include Redisable + include ActionView::Helpers::TextHelper + + # Threshold over which two Nilsimsa values are considered + # to refer to the same text + NILSIMSA_COMPARE_THRESHOLD = 95 + + # Nilsimsa doesn't work well on small inputs, so below + # this size, we check only for exact matches with MD5 + NILSIMSA_MIN_SIZE = 10 + + # How long to keep the trail of digests between updates, + # there is no reason to store it forever + EXPIRE_SET_AFTER = 1.week.seconds + + # How many digests to keep in an account's trail. If it's + # too small, spam could rotate around different message templates + MAX_TRAIL_SIZE = 10 + + # How many detected duplicates to allow through before + # considering the message as spam + THRESHOLD = 5 + + def initialize(status) + @account = status.account + @status = status + end + + def skip? + disabled? || already_flagged? || trusted? || no_unsolicited_mentions? || solicited_reply? + end + + def spam? + if insufficient_data? + false + elsif nilsimsa? + digests_over_threshold?('nilsimsa') { |_, other_digest| nilsimsa_compare_value(digest, other_digest) >= NILSIMSA_COMPARE_THRESHOLD } + else + digests_over_threshold?('md5') { |_, other_digest| other_digest == digest } + end + end + + def flag! + auto_report_status! + end + + def remember! + # The scores in sorted sets don't actually have enough bits to hold an exact + # value of our snowflake IDs, so we use it only for its ordering property. To + # get the correct status ID back, we have to save it in the string value + + redis.zadd(redis_key, @status.id, digest_with_algorithm) + redis.zremrangebyrank(redis_key, 0, -(MAX_TRAIL_SIZE + 1)) + redis.expire(redis_key, EXPIRE_SET_AFTER) + end + + def reset! + redis.del(redis_key) + end + + def hashable_text + return @hashable_text if defined?(@hashable_text) + + @hashable_text = @status.text + @hashable_text = remove_mentions(@hashable_text) + @hashable_text = strip_tags(@hashable_text) unless @status.local? + @hashable_text = normalize_unicode(@status.spoiler_text + ' ' + @hashable_text) + @hashable_text = remove_whitespace(@hashable_text) + end + + def insufficient_data? + hashable_text.blank? + end + + def digest + @digest ||= begin + if nilsimsa? + Nilsimsa.new(hashable_text).hexdigest + else + Digest::MD5.hexdigest(hashable_text) + end + end + end + + def digest_with_algorithm + if nilsimsa? + ['nilsimsa', digest, @status.id].join(':') + else + ['md5', digest, @status.id].join(':') + end + end + + class << self + def perform(status) + spam_check = new(status) + + return if spam_check.skip? + + if spam_check.spam? + spam_check.flag! + else + spam_check.remember! + end + end + end + + private + + def disabled? + !Setting.spam_check_enabled + end + + def remove_mentions(text) + return text.gsub(Account::MENTION_RE, '') if @status.local? + + Nokogiri::HTML.fragment(text).tap do |html| + mentions = @status.mentions.map { |mention| ActivityPub::TagManager.instance.url_for(mention.account) } + + html.traverse do |element| + element.unlink if element.name == 'a' && mentions.include?(element['href']) + end + end.to_s + end + + def normalize_unicode(text) + text.unicode_normalize(:nfkc).downcase + end + + def remove_whitespace(text) + text.gsub(/\s+/, ' ').strip + end + + def auto_report_status! + status_ids = Status.where(visibility: %i(public unlisted)).where(id: matching_status_ids).pluck(:id) + [@status.id] if @status.distributable? + ReportService.new.call(Account.representative, @account, status_ids: status_ids, comment: I18n.t('spam_check.spam_detected')) + end + + def already_flagged? + @account.silenced? || @account.targeted_reports.unresolved.where(account_id: -99).exists? + end + + def trusted? + @account.trust_level > Account::TRUST_LEVELS[:untrusted] + end + + def no_unsolicited_mentions? + @status.mentions.all? { |mention| mention.silent? || (!@account.local? && !mention.account.local?) || mention.account.following?(@account) } + end + + def solicited_reply? + !@status.thread.nil? && @status.thread.mentions.where(account: @account).exists? + end + + def nilsimsa_compare_value(first, second) + first = [first].pack('H*') + second = [second].pack('H*') + bits = 0 + + 0.upto(31) do |i| + bits += Nilsimsa::POPC[255 & (first[i].ord ^ second[i].ord)].ord + end + + 128 - bits # -128 <= Nilsimsa Compare Value <= 128 + end + + def nilsimsa? + hashable_text.size > NILSIMSA_MIN_SIZE + end + + def other_digests + redis.zrange(redis_key, 0, -1) + end + + def digests_over_threshold?(filter_algorithm) + other_digests.select do |record| + algorithm, other_digest, status_id = record.split(':') + + next unless algorithm == filter_algorithm + + yield algorithm, other_digest, status_id + end.size >= THRESHOLD + end + + def matching_status_ids + if nilsimsa? + other_digests.select { |record| record.start_with?('nilsimsa') && nilsimsa_compare_value(digest, record.split(':')[1]) >= NILSIMSA_COMPARE_THRESHOLD }.map { |record| record.split(':')[2] }.compact + else + other_digests.select { |record| record.start_with?('md5') && record.split(':')[1] == digest }.map { |record| record.split(':')[2] }.compact + end + end + + def redis_key + @redis_key ||= "spam_check:#{@account.id}" + end +end diff --git a/app/lib/status_finder.rb b/app/lib/status_finder.rb index 4d1aed297952c7a7f9fb756d78c6e42a988c1b42..22ced8bf82d8648de62f03c1d20e337f39e79969 100644 --- a/app/lib/status_finder.rb +++ b/app/lib/status_finder.rb @@ -13,8 +13,6 @@ class StatusFinder raise ActiveRecord::RecordNotFound unless TagManager.instance.local_url?(url) case recognized_params[:controller] - when 'stream_entries' - StreamEntry.find(recognized_params[:id]).status when 'statuses' Status.find(recognized_params[:id]) else diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb index fb364cb98aea39385b40b968af336f5a984cb25a..c88cf499474634d50695df18f6a4ed72ec2bedf3 100644 --- a/app/lib/tag_manager.rb +++ b/app/lib/tag_manager.rb @@ -24,24 +24,16 @@ class TagManager def same_acct?(canonical, needle) return true if canonical.casecmp(needle).zero? + username, domain = needle.split('@') + local_domain?(domain) && canonical.casecmp(username).zero? end def local_url?(url) uri = Addressable::URI.parse(url).normalize domain = uri.host + (uri.port ? ":#{uri.port}" : '') - TagManager.instance.web_domain?(domain) - end - - def url_for(target) - return target.url if target.respond_to?(:local?) && !target.local? - case target.object_type - when :person - short_account_url(target) - when :note, :comment, :activity - short_account_status_url(target.account, target) - end + TagManager.instance.web_domain?(domain) end end diff --git a/app/lib/toc_generator.rb b/app/lib/toc_generator.rb new file mode 100644 index 0000000000000000000000000000000000000000..0c8f766ca42fdde089afdac7795c3ab5cbf81963 --- /dev/null +++ b/app/lib/toc_generator.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +class TOCGenerator + TARGET_ELEMENTS = %w(h1 h2 h3 h4 h5 h6).freeze + LISTED_ELEMENTS = %w(h2 h3).freeze + + class Section + attr_accessor :depth, :title, :children, :anchor + + def initialize(depth, title, anchor) + @depth = depth + @title = title + @children = [] + @anchor = anchor + end + + delegate :<<, to: :children + end + + def initialize(source_html) + @source_html = source_html + @processed = false + @target_html = '' + @headers = [] + @slugs = Hash.new { |h, k| h[k] = 0 } + end + + def html + parse_and_transform unless @processed + @target_html + end + + def toc + parse_and_transform unless @processed + @headers + end + + private + + def parse_and_transform + return if @source_html.blank? + + parsed_html = Nokogiri::HTML.fragment(@source_html) + + parsed_html.traverse do |node| + next unless TARGET_ELEMENTS.include?(node.name) + + anchor = node['id'] || node.text.parameterize.presence || 'sec' + @slugs[anchor] += 1 + anchor = "#{anchor}-#{@slugs[anchor]}" if @slugs[anchor] > 1 + + node['id'] = anchor + + next unless LISTED_ELEMENTS.include?(node.name) + + depth = node.name[1..-1] + latest_section = @headers.last + + if latest_section.nil? || latest_section.depth >= depth + @headers << Section.new(depth, node.text, anchor) + else + latest_section << Section.new(depth, node.text, anchor) + end + end + + @target_html = parsed_html.to_s + @processed = true + end +end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 891a87a45c8e833a079ac4b42a7971d4b50ec5e4..52249f940111fd9c652f1985380e82b18e824427 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -35,6 +35,9 @@ class UserSettingsDecorator user.settings['aggregate_reblogs'] = aggregate_reblogs_preference if change?('setting_aggregate_reblogs') user.settings['show_application'] = show_application_preference if change?('setting_show_application') user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout') + user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash') + user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items') + user.settings['trends'] = trends_preference if change?('setting_trends') end def merged_notification_emails @@ -117,6 +120,18 @@ class UserSettingsDecorator boolean_cast_setting 'setting_advanced_layout' end + def use_blurhash_preference + boolean_cast_setting 'setting_use_blurhash' + end + + def use_pending_items_preference + boolean_cast_setting 'setting_use_pending_items' + end + + def trends_preference + boolean_cast_setting 'setting_trends' + end + def boolean_cast_setting(key) ActiveModel::Type::Boolean.new.cast(settings[key]) end diff --git a/app/lib/webfinger_resource.rb b/app/lib/webfinger_resource.rb index a54a702a2e9c5a66b6515701039f77a4e7668d63..22d78874a4b263ef2c1a359c4bd7076936d340a1 100644 --- a/app/lib/webfinger_resource.rb +++ b/app/lib/webfinger_resource.rb @@ -23,11 +23,17 @@ class WebfingerResource def username_from_url if account_show_page? path_params[:username] + elsif instance_actor_page? + Rails.configuration.x.local_domain else raise ActiveRecord::RecordNotFound end end + def instance_actor_page? + path_params[:controller] == 'instance_actors' + end + def account_show_page? path_params[:controller] == 'accounts' && path_params[:action] == 'show' end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index db154cad53afe9a5b5b25c92f7286676ecbcb4d6..8abce5f055d568648a9546f039329eb87438ef99 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -3,7 +3,7 @@ class AdminMailer < ApplicationMailer layout 'plain_mailer' - helper :stream_entries + helper :statuses def new_report(recipient, report) @report = report @@ -24,4 +24,14 @@ class AdminMailer < ApplicationMailer mail to: @me.user_email, subject: I18n.t('admin_mailer.new_pending_account.subject', instance: @instance, username: @account.username) end end + + def new_trending_tag(recipient, tag) + @tag = tag + @me = recipient + @instance = Rails.configuration.x.local_domain + + locale_for_account(@me) do + mail to: @me.user_email, subject: I18n.t('admin_mailer.new_trending_tag.subject', instance: @instance, name: @tag.name) + end + end end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 66fa337c1f277a6c276fbee0e92cfc44c1c16998..723d901fc6e4d2803aa45236a2766b1353415873 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class NotificationMailer < ApplicationMailer - helper :stream_entries + helper :statuses add_template_helper RoutingHelper diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 8f3a4ab3aa269f32857fe58ffa8bfd881a2aa945..6b81f68739c8811e0a5dccc1a2b478e241f017d4 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -5,6 +5,7 @@ class UserMailer < Devise::Mailer helper :application helper :instance + helper :statuses add_template_helper RoutingHelper @@ -56,6 +57,39 @@ class UserMailer < Devise::Mailer end end + def two_factor_enabled(user, **) + @resource = user + @instance = Rails.configuration.x.local_domain + + return if @resource.disabled? + + I18n.with_locale(@resource.locale || I18n.default_locale) do + mail to: @resource.email, subject: I18n.t('devise.mailer.two_factor_enabled.subject') + end + end + + def two_factor_disabled(user, **) + @resource = user + @instance = Rails.configuration.x.local_domain + + return if @resource.disabled? + + I18n.with_locale(@resource.locale || I18n.default_locale) do + mail to: @resource.email, subject: I18n.t('devise.mailer.two_factor_disabled.subject') + end + end + + def two_factor_recovery_codes_changed(user, **) + @resource = user + @instance = Rails.configuration.x.local_domain + + return if @resource.disabled? + + I18n.with_locale(@resource.locale || I18n.default_locale) do + mail to: @resource.email, subject: I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject') + end + end + def welcome(user) @resource = user @instance = Rails.configuration.x.local_domain @@ -79,10 +113,11 @@ class UserMailer < Devise::Mailer end end - def warning(user, warning) + def warning(user, warning, status_ids = nil) @resource = user @warning = warning @instance = Rails.configuration.x.local_domain + @statuses = Status.where(id: status_ids).includes(:account) if status_ids.is_a?(Array) I18n.with_locale(@resource.locale || I18n.default_locale) do mail to: @resource.email, diff --git a/app/models/account.rb b/app/models/account.rb index c588451fce0286dce224f78b07b3ef628017fdcb..05936def3204db7a7e6182cd57ac7afb60f21cd9 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -45,12 +45,12 @@ # also_known_as :string is an Array # silenced_at :datetime # suspended_at :datetime +# trust_level :integer # class Account < ApplicationRecord USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i - MIN_FOLLOWERS_DISCOVERY = 10 include AccountAssociations include AccountAvatar @@ -62,6 +62,11 @@ class Account < ApplicationRecord include AccountCounters include DomainNormalizable + TRUST_LEVELS = { + untrusted: 0, + trusted: 1, + }.freeze + enum protocol: [:ostatus, :activitypub] validates :username, presence: true @@ -71,7 +76,7 @@ class Account < ApplicationRecord validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, if: -> { !local? && will_save_change_to_username? } # Local user validations - validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } + validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' } validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? } validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } @@ -94,11 +99,13 @@ class Account < ApplicationRecord scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) } scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } scope :searchable, -> { without_suspended.where(moved_to_account_id: nil) } - scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).joins(:account_stat).where(AccountStat.arel_table[:followers_count].gteq(MIN_FOLLOWERS_DISCOVERY)) } + scope :discoverable, -> { searchable.without_silenced.where(discoverable: true).left_outer_joins(:account_stat) } scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) } - scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc')) } + scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) } scope :popular, -> { order('account_stats.followers_count desc') } scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches('%.' + domain))) } + scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) } + scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) } delegate :email, :unconfirmed_email, @@ -108,6 +115,7 @@ class Account < ApplicationRecord :approved?, :pending?, :disabled?, + :unconfirmed_or_pending?, :role, :admin?, :moderator?, @@ -121,6 +129,8 @@ class Account < ApplicationRecord delegate :chosen_languages, to: :user, prefix: false, allow_nil: true + update_index('accounts#account', :self) + def local? domain.nil? end @@ -133,6 +143,10 @@ class Account < ApplicationRecord %w(Application Service).include? actor_type end + def instance_actor? + id == -99 + end + alias bot bot? def bot=(val) @@ -159,21 +173,27 @@ class Account < ApplicationRecord subscription_expires_at.present? end + def searchable? + !(suspended? || moved?) + end + def possibly_stale? last_webfingered_at.nil? || last_webfingered_at <= 1.day.ago end + def trust_level + self[:trust_level] || 0 + end + def refresh! - return if local? - ResolveAccountService.new.call(acct) + ResolveAccountService.new.call(acct) unless local? end def silenced? silenced_at.present? end - def silence!(date = nil) - date ||= Time.now.utc + def silence!(date = Time.now.utc) update!(silenced_at: date) end @@ -185,8 +205,7 @@ class Account < ApplicationRecord suspended_at.present? end - def suspend!(date = nil) - date ||= Time.now.utc + def suspend!(date = Time.now.utc) transaction do user&.disable! if local? update!(suspended_at: date) @@ -216,17 +235,7 @@ class Account < ApplicationRecord end def tags_as_strings=(tag_names) - tag_names.map! { |name| name.mb_chars.downcase.to_s } - tag_names.uniq! - - # Existing hashtags - hashtags_map = Tag.where(name: tag_names).each_with_object({}) { |tag, h| h[tag.name] = tag } - - # Initialize not yet existing hashtags - tag_names.each do |name| - next if hashtags_map.key?(name) - hashtags_map[name] = Tag.new(name: name) - end + hashtags_map = Tag.find_or_create_by_names(tag_names).each_with_object({}) { |tag, h| h[tag.name] = tag } # Remove hashtags that are to be deleted tags.each do |tag| @@ -294,21 +303,6 @@ class Account < ApplicationRecord self.fields = tmp end - def magic_key - modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component| - result = [] - - until component.zero? - result << [component % 256].pack('C') - component >>= 8 - end - - result.reverse.join - end - - (['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.') - end - def subscription(webhook_url) @subscription ||= OStatus2::Subscription.new(remote_url, secret: secret, webhook: webhook_url, hub: hub_url) end @@ -316,10 +310,9 @@ class Account < ApplicationRecord def save_with_optional_media! save! rescue ActiveRecord::RecordInvalid - self.avatar = nil - self.header = nil - self[:avatar_remote_url] = '' - self[:header_remote_url] = '' + self.avatar = nil + self.header = nil + save! end @@ -506,7 +499,7 @@ class Account < ApplicationRecord end def generate_keys - return unless local? && !Rails.env.test? + return unless local? && private_key.blank? && public_key.blank? keypair = OpenSSL::PKey::RSA.new(2048) self.private_key = keypair.to_pem @@ -520,7 +513,7 @@ class Account < ApplicationRecord end def emojifiable_text - [note, display_name, fields.map(&:value)].join(' ') + [note, display_name, fields.map(&:name), fields.map(&:value)].join(' ') end def clean_feed_manager diff --git a/app/models/account_alias.rb b/app/models/account_alias.rb new file mode 100644 index 0000000000000000000000000000000000000000..66f8ce40953c9a8e5b2b9198bbb899610c3cb6de --- /dev/null +++ b/app/models/account_alias.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: account_aliases +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) +# acct :string default(""), not null +# uri :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class AccountAlias < ApplicationRecord + belongs_to :account + + validates :acct, presence: true, domain: { acct: true } + validates :uri, presence: true + validates :uri, uniqueness: { scope: :account_id } + + before_validation :set_uri + after_create :add_to_account + after_destroy :remove_from_account + + def acct=(val) + val = val.to_s.strip + super(val.start_with?('@') ? val[1..-1] : val) + end + + private + + def set_uri + target_account = ResolveAccountService.new.call(acct) + self.uri = ActivityPub::TagManager.instance.uri_for(target_account) unless target_account.nil? + rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error + # Validation will take care of it + end + + def add_to_account + account.update(also_known_as: account.also_known_as + [uri]) + end + + def remove_from_account + account.update(also_known_as: account.also_known_as.reject { |x| x == uri }) + end +end diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb new file mode 100644 index 0000000000000000000000000000000000000000..681b5b2cd0bc9e76d1f08e2d1a8c72dd7a26d3f3 --- /dev/null +++ b/app/models/account_migration.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: account_migrations +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) +# acct :string default(""), not null +# followers_count :bigint(8) default(0), not null +# target_account_id :bigint(8) +# created_at :datetime not null +# updated_at :datetime not null +# + +class AccountMigration < ApplicationRecord + COOLDOWN_PERIOD = 30.days.freeze + + belongs_to :account + belongs_to :target_account, class_name: 'Account' + + before_validation :set_target_account + before_validation :set_followers_count + + validates :acct, presence: true, domain: { acct: true } + validate :validate_migration_cooldown + validate :validate_target_account + + scope :within_cooldown, ->(now = Time.now.utc) { where(arel_table[:created_at].gteq(now - COOLDOWN_PERIOD)) } + + attr_accessor :current_password, :current_username + + def save_with_challenge(current_user) + if current_user.encrypted_password.present? + errors.add(:current_password, :invalid) unless current_user.valid_password?(current_password) + else + errors.add(:current_username, :invalid) unless account.username == current_username + end + + return false unless errors.empty? + + save + end + + def cooldown_at + created_at + COOLDOWN_PERIOD + end + + def acct=(val) + super(val.to_s.strip.gsub(/\A@/, '')) + end + + private + + def set_target_account + self.target_account = ResolveAccountService.new.call(acct) + rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error + # Validation will take care of it + end + + def set_followers_count + self.followers_count = account.followers_count + end + + def validate_target_account + if target_account.nil? + errors.add(:acct, I18n.t('migrations.errors.not_found')) + else + errors.add(:acct, I18n.t('migrations.errors.missing_also_known_as')) unless target_account.also_known_as.include?(ActivityPub::TagManager.instance.uri_for(account)) + errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved_to_account_id.present? && account.moved_to_account_id == target_account.id + errors.add(:acct, I18n.t('migrations.errors.move_to_self')) if account.id == target_account.id + end + end + + def validate_migration_cooldown + errors.add(:base, I18n.t('migrations.errors.on_cooldown')) if account.migrations.within_cooldown.exists? + end +end diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb index 9813aa84ff105420b0629e5a9f501ba78296ff30..c84e4217c8c6b11d5a0770ff443a6de39db1afd6 100644 --- a/app/models/account_stat.rb +++ b/app/models/account_stat.rb @@ -11,17 +11,36 @@ # created_at :datetime not null # updated_at :datetime not null # last_status_at :datetime +# lock_version :integer default(0), not null # class AccountStat < ApplicationRecord belongs_to :account, inverse_of: :account_stat + update_index('accounts#account', :account) + def increment_count!(key) update(attributes_for_increment(key)) + rescue ActiveRecord::StaleObjectError + begin + reload_with_id + rescue ActiveRecord::RecordNotFound + # Nothing to do + else + retry + end end def decrement_count!(key) update(key => [public_send(key) - 1, 0].max) + rescue ActiveRecord::StaleObjectError + begin + reload_with_id + rescue ActiveRecord::RecordNotFound + # Nothing to do + else + retry + end end private @@ -31,4 +50,9 @@ class AccountStat < ApplicationRecord attrs[:last_status_at] = Time.now.utc if key == :statuses_count attrs end + + def reload_with_id + self.id = find_by!(account: account).id if new_record? + reload + end end diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index bdbd342fb3ccf3fb0444a831125b72d90ef5f8e9..e9da003a30640e804cefa92e7d51912e77d8a462 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -19,20 +19,25 @@ class Admin::AccountAction :report_id, :warning_preset_id - attr_reader :warning, :send_email_notification + attr_reader :warning, :send_email_notification, :include_statuses def send_email_notification=(value) @send_email_notification = ActiveModel::Type::Boolean.new.cast(value) end + def include_statuses=(value) + @include_statuses = ActiveModel::Type::Boolean.new.cast(value) + end + def save! ApplicationRecord.transaction do process_action! process_warning! end - queue_email! + process_email! process_reports! + process_queue! end def report @@ -57,6 +62,8 @@ class Admin::AccountAction def process_action! case type + when 'none' + handle_resolve! when 'disable' handle_disable! when 'silence' @@ -78,19 +85,33 @@ class Admin::AccountAction # A log entry is only interesting if the warning contains # custom text from someone. Otherwise it's just noise. + log_action(:create, warning) if warning.text.present? end def process_reports! - return if report_id.blank? + # If we're doing "mark as resolved" on a single report, + # then we want to keep other reports open in case they + # contain new actionable information. + # + # Otherwise, we will mark all unresolved reports about + # the account as resolved. - authorize(report, :update?) + reports.each { |report| authorize(report, :update?) } - if type == 'none' + reports.each do |report| log_action(:resolve, report) report.resolve!(current_account) - else - Report.where(target_account: target_account).unresolved.update_all(action_taken: true, action_taken_by_account_id: current_account.id) + end + end + + def handle_resolve! + if with_report? && report.account_id == -99 && target_account.trust_level == Account::TRUST_LEVELS[:untrusted] + # This is an automated report and it is being dismissed, so it's + # a false positive, in which case update the account's trust level + # to prevent further spam checks + + target_account.update(trust_level: Account::TRUST_LEVELS[:trusted]) end end @@ -110,7 +131,6 @@ class Admin::AccountAction authorize(target_account, :suspend?) log_action(:suspend, target_account) target_account.suspend! - queue_suspension_worker! end def text_for_warning @@ -121,16 +141,32 @@ class Admin::AccountAction Admin::SuspensionWorker.perform_async(target_account.id) end - def queue_email! - return unless warnable? + def process_queue! + queue_suspension_worker! if type == 'suspend' + end - UserMailer.warning(target_account.user, warning).deliver_later! + def process_email! + UserMailer.warning(target_account.user, warning, status_ids).deliver_now! if warnable? end def warnable? send_email_notification && target_account.local? end + def status_ids + @report.status_ids if @report && include_statuses + end + + def reports + @reports ||= begin + if type == 'none' && with_report? + [report] + else + Report.where(target_account: target_account).unresolved + end + end + end + def warning_preset @warning_preset ||= AccountWarningPreset.find(warning_preset_id) if warning_preset_id.present? end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 83134d41a4705fcc2d42ad24982bcebf56541cfe..5d7d3a09610a375633fde62188cfe514801b2f72 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -2,5 +2,22 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true + include Remotable + + class << self + def update_index(_type_name, *_args, &_block) + super if Chewy.enabled? + end + end + + def boolean_with_default(key, default_value) + value = attributes[key] + + if value.nil? + default_value + else + value + end + end end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index 70855e0543ee83464b1d588ffaacc69554d019b8..c9cc5c610b8e522cdebfbd1022e784c11b3a1b9f 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -11,7 +11,6 @@ module AccountAssociations has_many :identity_proofs, class_name: 'AccountIdentityProof', dependent: :destroy, inverse_of: :account # Timelines - has_many :stream_entries, inverse_of: :account, dependent: :destroy has_many :statuses, inverse_of: :account, dependent: :destroy has_many :favourites, inverse_of: :account, dependent: :destroy has_many :mentions, inverse_of: :account, dependent: :destroy @@ -31,9 +30,6 @@ module AccountAssociations has_many :media_attachments, dependent: :destroy has_many :polls, dependent: :destroy - # PuSH subscriptions - has_many :subscriptions, dependent: :destroy - # Report relationships has_many :reports, dependent: :destroy, inverse_of: :account has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account @@ -56,6 +52,8 @@ module AccountAssociations # Account migrations belongs_to :moved_to_account, class_name: 'Account', optional: true + has_many :migrations, class_name: 'AccountMigration', dependent: :destroy, inverse_of: :account + has_many :aliases, class_name: 'AccountAlias', dependent: :destroy, inverse_of: :account # Hashtags has_and_belongs_to_many :tags diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb index 5fff3ef5d2128399463897dc837fd402045bd3c8..2d5ebfca35aea67c09a2d5bb1862e2ee5cb8a1d1 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account_avatar.rb @@ -3,7 +3,7 @@ module AccountAvatar extend ActiveSupport::Concern - IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze + IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze LIMIT = 2.megabytes class_methods do diff --git a/app/models/concerns/account_counters.rb b/app/models/concerns/account_counters.rb index 3581df8dd8b2ac69b6806eb4b0bea7c3651eec06..6e25e1905e4d6919ed91c3fda3497ffb2933d04a 100644 --- a/app/models/concerns/account_counters.rb +++ b/app/models/concerns/account_counters.rb @@ -26,7 +26,8 @@ module AccountCounters private def save_account_stat - return unless account_stat&.changed? + return unless association(:account_stat).loaded? && account_stat&.changed? + account_stat.save end end diff --git a/app/models/concerns/account_finder_concern.rb b/app/models/concerns/account_finder_concern.rb index ccd7bfa12311310d8ce3cbf66677e7fc86ae3780..a54c2174d490db945ae8c352cac4b8c3bb371214 100644 --- a/app/models/concerns/account_finder_concern.rb +++ b/app/models/concerns/account_finder_concern.rb @@ -13,7 +13,7 @@ module AccountFinderConcern end def representative - find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) || Account.local.without_suspended.first + Account.find(-99) end def find_local(username) diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account_header.rb index a748fdff72df33e23613364f55ecb53950febf6e..067e166eb6ce5e340a86489c1c12d525c2c24dfd 100644 --- a/app/models/concerns/account_header.rb +++ b/app/models/concerns/account_header.rb @@ -3,7 +3,7 @@ module AccountHeader extend ActiveSupport::Concern - IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze + IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze LIMIT = 2.megabytes MAX_PIXELS = 750_000 # 1500x500px diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb index 7c78bb45698dccf8e9036cc3e2eb53272ee2ba43..3bbc6453c7f5575937352fb4c5b0f224a6d3dfbf 100644 --- a/app/models/concerns/attachmentable.rb +++ b/app/models/concerns/attachmentable.rb @@ -6,6 +6,7 @@ module Attachmentable extend ActiveSupport::Concern MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB + GIF_MATRIX_LIMIT = 921_600 # 1280x720px included do before_post_process :set_file_extensions @@ -42,8 +43,9 @@ module Attachmentable next if attachment.blank? || !/image.*/.match?(attachment.content_type) || attachment.queued_for_write[:original].blank? width, height = FastImage.size(attachment.queued_for_write[:original].path) + matrix_limit = attachment.content_type == 'image/gif' ? GIF_MATRIX_LIMIT : MAX_MATRIX_LIMIT - raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height >= MAX_MATRIX_LIMIT) + raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height > matrix_limit) end end diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/ldap_authenticable.rb index 84ff84c4b05c856c30cf7190db6ed392df3dd063..1179939478454401bf220317a718c0db6b27efcb 100644 --- a/app/models/concerns/ldap_authenticable.rb +++ b/app/models/concerns/ldap_authenticable.rb @@ -3,24 +3,50 @@ module LdapAuthenticable extend ActiveSupport::Concern - def ldap_setup(_attributes) - self.confirmed_at = Time.now.utc - self.admin = false - self.external = true + class_methods do + def authenticate_with_ldap(params = {}) + ldap = Net::LDAP.new(ldap_options) + filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, email: params[:email]) - save! - end + if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: filter, password: params[:password])) + ldap_get_user(user_info.first) + end + end - class_methods do def ldap_get_user(attributes = {}) resource = joins(:account).find_by(accounts: { username: attributes[Devise.ldap_uid.to_sym].first }) if resource.blank? - resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first }) - resource.ldap_setup(attributes) + resource = new(email: attributes[:mail].first, agreement: true, account_attributes: { username: attributes[Devise.ldap_uid.to_sym].first }, admin: false, external: true, confirmed_at: Time.now.utc) + resource.save! end resource end + + def ldap_options + opts = { + host: Devise.ldap_host, + port: Devise.ldap_port, + base: Devise.ldap_base, + + auth: { + method: :simple, + username: Devise.ldap_bind_dn, + password: Devise.ldap_password, + }, + + connect_timeout: 10, + } + + if [:simple_tls, :start_tls].include?(Devise.ldap_method) + opts[:encryption] = { + method: Devise.ldap_method, + tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.tap { |options| options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Devise.ldap_tls_no_verify }, + } + end + + opts + end end end diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb index 283033083959caf767db8450ddde1c1f6f77d382..960784222702ce08deef9772048ab495b53d32f4 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/omniauthable.rb @@ -4,7 +4,7 @@ module Omniauthable extend ActiveSupport::Concern TEMP_EMAIL_PREFIX = 'change@me' - TEMP_EMAIL_REGEX = /\Achange@me/ + TEMP_EMAIL_REGEX = /\A#{TEMP_EMAIL_PREFIX}/.freeze included do devise :omniauthable @@ -28,8 +28,8 @@ module Omniauthable # to prevent the identity being locked with accidentally created accounts. # Note that this may leave zombie accounts (with no associated identity) which # can be cleaned up at a later date. - user = signed_in_resource || identity.user - user = create_for_oauth(auth) if user.nil? + user = signed_in_resource || identity.user + user ||= create_for_oauth(auth) if identity.user.nil? identity.user = user @@ -43,9 +43,20 @@ module Omniauthable # Check if the user exists with provided email if the provider gives us a # verified email. If no verified email was provided or the user already # exists, we assign a temporary email and ask the user to verify it on - # the next step via Auth::ConfirmationsController.finish_signup + # the next step via Auth::SetupController.show + + strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy + assume_verified = strategy&.security&.assume_email_is_verified + email_is_verified = auth.info.verified || auth.info.verified_email || assume_verified + email = auth.info.verified_email || auth.info.email + email = nil unless email_is_verified + + user = User.find_by(email: email) if email_is_verified + + return user unless user.nil? + + user = User.new(user_params_from_auth(email, auth)) - user = User.new(user_params_from_auth(auth)) user.account.avatar_remote_url = auth.info.image if auth.info.image =~ /\A#{URI.regexp(%w(http https))}\z/ user.skip_confirmation! user.save! @@ -54,14 +65,7 @@ module Omniauthable private - def user_params_from_auth(auth) - strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy - assume_verified = strategy.try(:security).try(:assume_email_is_verified) - email_is_verified = auth.info.verified || auth.info.verified_email || assume_verified - email = auth.info.verified_email || auth.info.email - email = email_is_verified && !User.exists?(email: auth.info.email) && email - display_name = auth.info.full_name || [auth.info.first_name, auth.info.last_name].join(' ') - + def user_params_from_auth(email, auth) { email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", password: Devise.friendly_token[0, 20], @@ -69,7 +73,7 @@ module Omniauthable external: true, account_attributes: { username: ensure_unique_username(auth.uid), - display_name: display_name, + display_name: auth.info.full_name || [auth.info.first_name, auth.info.last_name].join(' '), }, } end diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 9372a963bf64eef747b062399d3e05639def5567..b7a476c87a51548bc8cc47434f50dadceb23c799 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -4,7 +4,7 @@ module Remotable extend ActiveSupport::Concern class_methods do - def remotable_attachment(attachment_name, limit) + def remotable_attachment(attachment_name, limit, suppress_errors: true) attribute_name = "#{attachment_name}_remote_url".to_sym method_name = "#{attribute_name}=".to_sym alt_method_name = "reset_#{attachment_name}!".to_sym @@ -18,11 +18,11 @@ module Remotable return end - return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || self[attribute_name] == url + return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || (self[attribute_name] == url && send("#{attachment_name}_file_name").present?) begin Request.new(:get, url).perform do |response| - next if response.code != 200 + raise Mastodon::UnexpectedResponseError, response unless (200...300).cover?(response.code) content_type = parse_content_type(response.headers.get('content-type').last) extname = detect_extname_from_content_type(content_type) @@ -41,11 +41,11 @@ module Remotable self[attribute_name] = url if has_attribute?(attribute_name) end - rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e + rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e + Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" + raise e unless suppress_errors + rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError => e Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" - nil - rescue Paperclip::Error, Mastodon::DimensionsValidationError => e - Rails.logger.debug "Error processing remote #{attachment_name}: #{e}" nil end end diff --git a/app/models/concerns/streamable.rb b/app/models/concerns/streamable.rb deleted file mode 100644 index 7c9edb8ef8f6f89cd5f40d842a2d89497d69b081..0000000000000000000000000000000000000000 --- a/app/models/concerns/streamable.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Streamable - extend ActiveSupport::Concern - - included do - has_one :stream_entry, as: :activity - - after_create do - account.stream_entries.create!(activity: self, hidden: hidden?) if needs_stream_entry? - end - end - - def title - super - end - - def content - title - end - - def target - super - end - - def object_type - :activity - end - - def thread - super - end - - def hidden? - false - end - - private - - def needs_stream_entry? - account.local? - end -end diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index def1b3faa162057c89a80601ac45df7f5a3ae9c1..0dacaf654bc7c4f78cf074a9b592fc271aba6d59 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -16,6 +16,7 @@ # uri :string # image_remote_url :string # visible_in_picker :boolean default(TRUE), not null +# category_id :bigint(8) # class CustomEmoji < ApplicationRecord @@ -27,8 +28,9 @@ class CustomEmoji < ApplicationRecord :(#{SHORTCODE_RE_FRAGMENT}): (?=[^[:alnum:]:]|$)/x - IMAGE_MIME_TYPES = %w(image/png image/gif image/webp).freeze + IMAGE_MIME_TYPES = %w(image/png image/gif).freeze + belongs_to :category, class_name: 'CustomEmojiCategory', optional: true has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce -strip' } } @@ -38,10 +40,11 @@ class CustomEmoji < ApplicationRecord validates_attachment :image, content_type: { content_type: IMAGE_MIME_TYPES }, presence: true, size: { less_than: LIMIT } validates :shortcode, uniqueness: { scope: :domain }, format: { with: /\A#{SHORTCODE_RE_FRAGMENT}\z/ }, length: { minimum: 2 } - scope :local, -> { where(domain: nil) } - scope :remote, -> { where.not(domain: nil) } + scope :local, -> { where(domain: nil) } + scope :remote, -> { where.not(domain: nil) } scope :alphabetic, -> { order(domain: :asc, shortcode: :asc) } scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches('%.' + domain))) } + scope :listed, -> { local.where(disabled: false).where(visible_in_picker: true) } remotable_attachment :image, LIMIT @@ -57,6 +60,12 @@ class CustomEmoji < ApplicationRecord :emoji end + def copy! + copy = self.class.find_or_initialize_by(domain: nil, shortcode: shortcode) + copy.image = image + copy.tap(&:save!) + end + class << self def from_text(text, domain) return [] if text.blank? diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb new file mode 100644 index 0000000000000000000000000000000000000000..3c87f2b2e5644471f14a25ca79429846a0377bde --- /dev/null +++ b/app/models/custom_emoji_category.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: custom_emoji_categories +# +# id :bigint(8) not null, primary key +# name :string +# created_at :datetime not null +# updated_at :datetime not null +# + +class CustomEmojiCategory < ApplicationRecord + has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category + + validates :name, presence: true, uniqueness: true +end diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb index 7649055d2839037ded1cd8eef11f3244c1258a0a..15b8da1d12f54ff6d62548dc5d33d1abd3868cc7 100644 --- a/app/models/custom_emoji_filter.rb +++ b/app/models/custom_emoji_filter.rb @@ -11,6 +11,8 @@ class CustomEmojiFilter scope = CustomEmoji.alphabetic params.each do |key, value| + next if key.to_s == 'page' + scope.merge!(scope_for(key, value)) if value.present? end @@ -22,13 +24,13 @@ class CustomEmojiFilter def scope_for(key, value) case key.to_s when 'local' - CustomEmoji.local + CustomEmoji.local.left_joins(:category).reorder(Arel.sql('custom_emoji_categories.name ASC NULLS FIRST, custom_emojis.shortcode ASC')) when 'remote' CustomEmoji.remote when 'by_domain' - CustomEmoji.where(domain: value.downcase) + CustomEmoji.where(domain: value.strip.downcase) when 'shortcode' - CustomEmoji.search(value) + CustomEmoji.search(value.strip) else raise "Unknown filter: #{key}" end diff --git a/app/models/domain_allow.rb b/app/models/domain_allow.rb new file mode 100644 index 0000000000000000000000000000000000000000..5fe0e3a29c340d8a2da1dfdbdea1e32577b0880f --- /dev/null +++ b/app/models/domain_allow.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: domain_allows +# +# id :bigint(8) not null, primary key +# domain :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class DomainAllow < ApplicationRecord + include DomainNormalizable + + validates :domain, presence: true, uniqueness: true, domain: true + + scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } + + class << self + def allowed?(domain) + !rule_for(domain).nil? + end + + def rule_for(domain) + return if domain.blank? + + uri = Addressable::URI.new.tap { |u| u.host = domain.gsub(/[\/]/, '') } + + find_by(domain: uri.normalized_host) + end + end +end diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index c230fb14bda41a021f62e5eb4eb1ae4dd8fa2c09..4e865b850a970145d39ea27aaeb6033b6134d0da 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -3,13 +3,15 @@ # # Table name: domain_blocks # -# id :bigint(8) not null, primary key -# domain :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# severity :integer default("silence") -# reject_media :boolean default(FALSE), not null -# reject_reports :boolean default(FALSE), not null +# id :bigint(8) not null, primary key +# domain :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# severity :integer default("silence") +# reject_media :boolean default(FALSE), not null +# reject_reports :boolean default(FALSE), not null +# private_comment :text +# public_comment :text # class DomainBlock < ApplicationRecord @@ -23,6 +25,8 @@ class DomainBlock < ApplicationRecord delegate :count, to: :accounts, prefix: true scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } + scope :with_user_facing_limitations, -> { where(severity: [:silence, :suspend]).or(where(reject_media: true)) } + scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), reject_media, domain')) } class << self def suspend?(domain) diff --git a/app/models/favourite.rb b/app/models/favourite.rb index 17f8c9fa631d2a758512dff8cb458ad2e92e1505..bf0ec4449c4615c6aee68c89b5c890bd18831330 100644 --- a/app/models/favourite.rb +++ b/app/models/favourite.rb @@ -13,7 +13,7 @@ class Favourite < ApplicationRecord include Paginable - update_index('statuses#status', :status) if Chewy.enabled? + update_index('statuses#status', :status) belongs_to :account, inverse_of: :favourites belongs_to :status, inverse_of: :favourites diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb index d06ae26a8902995bbfaa870812380703ff6b6a44..e02ae0705a2fceb71de5f4501205311ce5c6ca43 100644 --- a/app/models/featured_tag.rb +++ b/app/models/featured_tag.rb @@ -23,7 +23,7 @@ class FeaturedTag < ApplicationRecord validate :validate_featured_tags_limit, on: :create def name=(str) - self.tag = Tag.find_or_initialize_by(name: str.strip.delete('#').mb_chars.downcase.to_s) + self.tag = Tag.find_or_create_by_names(str.strip)&.first end def increment(timestamp) diff --git a/app/models/feed.rb b/app/models/feed.rb index 0e8943ff863bde253b75912a6870345bf974f048..36e0c1e0a09b94375a4ba3d2178c5dd00b7df0e0 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -9,6 +9,11 @@ class Feed end def get(limit, max_id = nil, since_id = nil, min_id = nil) + limit = limit.to_i + max_id = max_id.to_i if max_id.present? + since_id = since_id.to_i if since_id.present? + min_id = min_id.to_i if min_id.present? + from_redis(limit, max_id, since_id, min_id) end diff --git a/app/models/form/account_batch.rb b/app/models/form/account_batch.rb index f1b7a45664f25173a1000a7fdabee37876c77e9f..0b285fde92caa40d895f558d6aa9a0ff9bd116b6 100644 --- a/app/models/form/account_batch.rb +++ b/app/models/form/account_batch.rb @@ -69,6 +69,6 @@ class Form::AccountBatch records = accounts.includes(:user) records.each { |account| authorize(account.user, :reject?) } - .each { |account| SuspendAccountService.new.call(account, including_user: true, destroy: true, skip_distribution: true) } + .each { |account| SuspendAccountService.new.call(account, reserve_email: false, reserve_username: false) } end end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 86a86ec6661275de64b9d270a358c66674b50718..70e9c21f166613626d4eb279971749c6d4876b18 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -28,6 +28,12 @@ class Form::AdminSettings thumbnail hero mascot + spam_check_enabled + trends + trendable_by_default + show_domain_blocks + show_domain_blocks_rationale + noindex ).freeze BOOLEAN_KEYS = %i( @@ -39,6 +45,10 @@ class Form::AdminSettings show_known_fediverse_at_about_page preview_sensitive_media profile_directory + spam_check_enabled + trends + trendable_by_default + noindex ).freeze UPLOAD_KEYS = %i( @@ -56,6 +66,8 @@ class Form::AdminSettings validates :site_contact_email, :site_contact_username, presence: true validates :site_contact_username, existing_username: true validates :bootstrap_timeline_accounts, existing_username: { multiple: true } + validates :show_domain_blocks, inclusion: { in: %w(disabled users all) } + validates :show_domain_blocks_rationale, inclusion: { in: %w(disabled users all) } def initialize(_attributes = {}) super diff --git a/app/models/form/challenge.rb b/app/models/form/challenge.rb new file mode 100644 index 0000000000000000000000000000000000000000..40c99649cce8b21e3184bd0b26527ffb6dcdfaab --- /dev/null +++ b/app/models/form/challenge.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Form::Challenge + include ActiveModel::Model + + attr_accessor :current_password, :current_username, + :return_to +end diff --git a/app/models/form/custom_emoji_batch.rb b/app/models/form/custom_emoji_batch.rb new file mode 100644 index 0000000000000000000000000000000000000000..076e8c9e39aebd7b620c473cc11d6d0af7b93e04 --- /dev/null +++ b/app/models/form/custom_emoji_batch.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +class Form::CustomEmojiBatch + include ActiveModel::Model + include Authorization + include AccountableConcern + + attr_accessor :custom_emoji_ids, :action, :current_account, + :category_id, :category_name, :visible_in_picker + + def save + case action + when 'update' + update! + when 'list' + list! + when 'unlist' + unlist! + when 'enable' + enable! + when 'disable' + disable! + when 'copy' + copy! + when 'delete' + delete! + end + end + + private + + def custom_emojis + CustomEmoji.where(id: custom_emoji_ids) + end + + def update! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) } + + category = begin + if category_id.present? + CustomEmojiCategory.find(category_id) + elsif category_name.present? + CustomEmojiCategory.create!(name: category_name) + end + end + + custom_emojis.each do |custom_emoji| + custom_emoji.update(category_id: category&.id) + log_action :update, custom_emoji + end + end + + def list! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) } + + custom_emojis.each do |custom_emoji| + custom_emoji.update(visible_in_picker: true) + log_action :update, custom_emoji + end + end + + def unlist! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) } + + custom_emojis.each do |custom_emoji| + custom_emoji.update(visible_in_picker: false) + log_action :update, custom_emoji + end + end + + def enable! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :enable?) } + + custom_emojis.each do |custom_emoji| + custom_emoji.update(disabled: false) + log_action :enable, custom_emoji + end + end + + def disable! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :disable?) } + + custom_emojis.each do |custom_emoji| + custom_emoji.update(disabled: true) + log_action :disable, custom_emoji + end + end + + def copy! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :copy?) } + + custom_emojis.each do |custom_emoji| + copied_custom_emoji = custom_emoji.copy! + log_action :create, copied_custom_emoji + end + end + + def delete! + custom_emojis.each { |custom_emoji| authorize(custom_emoji, :destroy?) } + + custom_emojis.each do |custom_emoji| + custom_emoji.destroy + log_action :destroy, custom_emoji + end + end +end diff --git a/app/models/form/delete_confirmation.rb b/app/models/form/delete_confirmation.rb index 0884a09b882c9b1630a7686fba3cbaa23ace7437..99d04b331a588427be841f07b4f4f833cb9c0ad8 100644 --- a/app/models/form/delete_confirmation.rb +++ b/app/models/form/delete_confirmation.rb @@ -3,5 +3,5 @@ class Form::DeleteConfirmation include ActiveModel::Model - attr_accessor :password + attr_accessor :password, :username end diff --git a/app/models/form/migration.rb b/app/models/form/migration.rb deleted file mode 100644 index c2a8655e13211d8c41bd7d39fbb93e6cd4a34cfd..0000000000000000000000000000000000000000 --- a/app/models/form/migration.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class Form::Migration - include ActiveModel::Validations - - attr_accessor :acct, :account - - def initialize(attrs = {}) - @account = attrs[:account] - @acct = attrs[:account].acct unless @account.nil? - @acct = attrs[:acct].gsub(/\A@/, '').strip unless attrs[:acct].nil? - end - - def valid? - return false unless super - set_account - errors.empty? - end - - private - - def set_account - self.account = (ResolveAccountService.new.call(acct) if account.nil? && acct.present?) - end -end diff --git a/app/models/form/redirect.rb b/app/models/form/redirect.rb new file mode 100644 index 0000000000000000000000000000000000000000..a7961f8e8aa00e7b311a3742644e3f79c89ebf61 --- /dev/null +++ b/app/models/form/redirect.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class Form::Redirect + include ActiveModel::Model + + attr_accessor :account, :target_account, :current_password, + :current_username + + attr_reader :acct + + validates :acct, presence: true, domain: { acct: true } + validate :validate_target_account + + def valid_with_challenge?(current_user) + if current_user.encrypted_password.present? + errors.add(:current_password, :invalid) unless current_user.valid_password?(current_password) + else + errors.add(:current_username, :invalid) unless account.username == current_username + end + + return false unless errors.empty? + + set_target_account + valid? + end + + def acct=(val) + @acct = val.to_s.strip.gsub(/\A@/, '') + end + + private + + def set_target_account + @target_account = ResolveAccountService.new.call(acct) + rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error + # Validation will take care of it + end + + def validate_target_account + if target_account.nil? + errors.add(:acct, I18n.t('migrations.errors.not_found')) + else + errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved_to_account_id.present? && account.moved_to_account_id == target_account.id + errors.add(:acct, I18n.t('migrations.errors.move_to_self')) if account.id == target_account.id + end + end +end diff --git a/app/models/form/status_batch.rb b/app/models/form/status_batch.rb index 933dfdaca1ba5a7465093b921833b05f6da960f5..c4943a7eabd73a73c21b6024102e71f2c8e21a1a 100644 --- a/app/models/form/status_batch.rb +++ b/app/models/form/status_batch.rb @@ -34,7 +34,8 @@ class Form::StatusBatch def delete_statuses Status.where(id: status_ids).reorder(nil).find_each do |status| - RemovalWorker.perform_async(status.id) + status.discard + RemovalWorker.perform_async(status.id, immediate: true) Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) log_action :destroy, status end diff --git a/app/models/form/tag_batch.rb b/app/models/form/tag_batch.rb new file mode 100644 index 0000000000000000000000000000000000000000..fd517a1a64107e81b6d4706693b86093c7a79f3c --- /dev/null +++ b/app/models/form/tag_batch.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class Form::TagBatch + include ActiveModel::Model + include Authorization + + attr_accessor :tag_ids, :action, :current_account + + def save + case action + when 'approve' + approve! + when 'reject' + reject! + end + end + + private + + def tags + Tag.where(id: tag_ids) + end + + def approve! + tags.each { |tag| authorize(tag, :update?) } + tags.update_all(trendable: true, reviewed_at: Time.now.utc) + end + + def reject! + tags.each { |tag| authorize(tag, :update?) } + tags.update_all(trendable: false, reviewed_at: Time.now.utc) + end +end diff --git a/app/models/form/two_factor_confirmation.rb b/app/models/form/two_factor_confirmation.rb index b8cf76d05887c7a1e00bf8eb565eab3fd590b4ae..27ada65333f4390d66ca819d0eb22af0a72265a0 100644 --- a/app/models/form/two_factor_confirmation.rb +++ b/app/models/form/two_factor_confirmation.rb @@ -3,5 +3,5 @@ class Form::TwoFactorConfirmation include ActiveModel::Model - attr_accessor :code + attr_accessor :otp_attempt end diff --git a/app/models/home_feed.rb b/app/models/home_feed.rb index ba7564983b0f8bc9820759773c7d53b0ec075578..1fd506138be7db87e0730427393f792abca52370 100644 --- a/app/models/home_feed.rb +++ b/app/models/home_feed.rb @@ -7,19 +7,7 @@ class HomeFeed < Feed @account = account end - def get(limit, max_id = nil, since_id = nil, min_id = nil) - if redis.exists("account:#{@account.id}:regeneration") - from_database(limit, max_id, since_id, min_id) - else - super - end - end - - private - - def from_database(limit, max_id, since_id, min_id) - Status.as_home_timeline(@account) - .paginate_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id) - .reject { |status| FeedManager.instance.filter?(:home, status, @account.id) } + def regenerating? + redis.exists("account:#{@id}:regeneration") end end diff --git a/app/models/instance.rb b/app/models/instance.rb index 797a191e07698294b7f82b91aa96209171e4213c..3c740f8a2bc7dc50554db94fe082a12439fcc2bf 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -7,8 +7,9 @@ class Instance def initialize(resource) @domain = resource.domain - @accounts_count = resource.is_a?(DomainBlock) ? nil : resource.accounts_count + @accounts_count = resource.respond_to?(:accounts_count) ? resource.accounts_count : nil @domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain) + @domain_allow = resource.is_a?(DomainAllow) ? resource : DomainAllow.rule_for(domain) end def countable? diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb index 848fff53e15c5ee416224e39624d64b96295bc8c..8bfab826d7393685c8d0e3a545698c37015ad68d 100644 --- a/app/models/instance_filter.rb +++ b/app/models/instance_filter.rb @@ -12,6 +12,10 @@ class InstanceFilter scope = DomainBlock scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? scope.order(id: :desc) + elsif params[:allowed].present? + scope = DomainAllow + scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? + scope.order(id: :desc) else scope = Account.remote scope = scope.matches_domain(params[:by_domain]) if params[:by_domain].present? diff --git a/app/models/invite.rb b/app/models/invite.rb index 02ab8e0b2170926fc0e1125163615643f8b5115f..29d25eae801c3ea576bf45cffbd05d92024424f5 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -12,6 +12,7 @@ # created_at :datetime not null # updated_at :datetime not null # autofollow :boolean default(FALSE), not null +# comment :text # class Invite < ApplicationRecord @@ -22,6 +23,8 @@ class Invite < ApplicationRecord scope :available, -> { where(expires_at: nil).or(where('expires_at >= ?', Time.now.utc)) } + validates :comment, length: { maximum: 420 } + before_validation :set_code def valid_for_use? diff --git a/app/models/marker.rb b/app/models/marker.rb new file mode 100644 index 0000000000000000000000000000000000000000..a5bd2176a84e25d0b00b56ae8b573399cdde894e --- /dev/null +++ b/app/models/marker.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: markers +# +# id :bigint(8) not null, primary key +# user_id :bigint(8) +# timeline :string default(""), not null +# last_read_id :bigint(8) default(0), not null +# lock_version :integer default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class Marker < ApplicationRecord + TIMELINES = %w(home notifications).freeze + + belongs_to :user + + validates :timeline, :last_read_id, presence: true + validates :timeline, inclusion: { in: TIMELINES } +end diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index b1c5589478fb194f88a2b11f1a930af328459465..9c6c04556e282e971b6dd87e3f4b520b5bc936b9 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -26,14 +26,14 @@ class MediaAttachment < ApplicationRecord enum type: [:image, :gifv, :video, :unknown, :audio] - IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].freeze - VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v', '.mov'].freeze - AUDIO_FILE_EXTENSIONS = ['.ogg', '.oga', '.mp3', '.wav', '.flac', '.opus'].freeze + IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif).freeze + VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze + AUDIO_FILE_EXTENSIONS = %w(.ogg .oga .mp3 .wav .flac .opus .aac .m4a .3gp .wma).freeze - IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze - VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime', 'video/ogg'].freeze - VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm', 'video/quicktime'].freeze - AUDIO_MIME_TYPES = ['audio/wave', 'audio/wav', 'audio/x-wav', 'audio/x-pn-wave', 'audio/ogg', 'audio/mpeg', 'audio/mp3', 'audio/webm', 'audio/flac'].freeze + IMAGE_MIME_TYPES = %w(image/jpeg image/png image/gif).freeze + VIDEO_MIME_TYPES = %w(video/webm video/mp4 video/quicktime video/ogg).freeze + VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime).freeze + AUDIO_MIME_TYPES = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/ogg audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze BLURHASH_OPTIONS = { x_comp: 4, @@ -57,6 +57,7 @@ class MediaAttachment < ApplicationRecord small: { convert_options: { output: { + 'loglevel' => 'fatal', vf: 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease', }, }, @@ -65,6 +66,18 @@ class MediaAttachment < ApplicationRecord file_geometry_parser: FastGeometryParser, blurhash: BLURHASH_OPTIONS, }, + + original: { + keep_same_format: true, + convert_options: { + output: { + 'loglevel' => 'fatal', + 'map_metadata' => '-1', + 'c:v' => 'copy', + 'c:a' => 'copy', + }, + }, + }, }.freeze AUDIO_STYLES = { @@ -73,6 +86,7 @@ class MediaAttachment < ApplicationRecord content_type: 'audio/mpeg', convert_options: { output: { + 'loglevel' => 'fatal', 'q:a' => 2, }, }, @@ -86,14 +100,15 @@ class MediaAttachment < ApplicationRecord output: { 'loglevel' => 'fatal', 'movflags' => 'faststart', - 'pix_fmt' => 'yuv420p', - 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', - 'vsync' => 'cfr', - 'c:v' => 'h264', - 'b:v' => '500K', - 'maxrate' => '1300K', - 'bufsize' => '1300K', - 'crf' => 18, + 'pix_fmt' => 'yuv420p', + 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', + 'vsync' => 'cfr', + 'c:v' => 'h264', + 'maxrate' => '1300K', + 'bufsize' => '1300K', + 'frames:v' => 60 * 60 * 3, + 'crf' => 18, + 'map_metadata' => '-1', }, }, }.freeze @@ -103,7 +118,7 @@ class MediaAttachment < ApplicationRecord original: VIDEO_FORMAT, }.freeze - IMAGE_LIMIT = 8.megabytes + IMAGE_LIMIT = 10.megabytes VIDEO_LIMIT = 40.megabytes belongs_to :account, inverse_of: :media_attachments, optional: true @@ -118,17 +133,18 @@ class MediaAttachment < ApplicationRecord validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :larger_media_format? validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :larger_media_format? - remotable_attachment :file, VIDEO_LIMIT + remotable_attachment :file, VIDEO_LIMIT, suppress_errors: false include Attachmentable validates :account, presence: true - validates :description, length: { maximum: 420 }, if: :local? + validates :description, length: { maximum: 1_500 }, if: :local? scope :attached, -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) } scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) } scope :local, -> { where(remote_url: '') } scope :remote, -> { where.not(remote_url: '') } + scope :cached, -> { remote.where.not(file_file_name: nil) } default_scope { order(id: :asc) } @@ -243,7 +259,9 @@ class MediaAttachment < ApplicationRecord def set_meta meta = populate_meta + return if meta == {} + file.instance_write :meta, meta end @@ -286,6 +304,7 @@ class MediaAttachment < ApplicationRecord def reset_parent_cache return if status_id.nil? + Rails.cache.delete("statuses/#{status_id}") end end diff --git a/app/models/poll.rb b/app/models/poll.rb index 8f72c7b1124312ae20fc53edf067a6f2f610f4e6..5427368fd04401cc963be90972ebe4dba62a5a36 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -16,6 +16,7 @@ # created_at :datetime not null # updated_at :datetime not null # lock_version :integer default(0), not null +# voters_count :bigint(8) # class Poll < ApplicationRecord @@ -54,6 +55,10 @@ class Poll < ApplicationRecord account.id == account_id || votes.where(account: account).exists? end + def own_votes(account) + votes.where(account: account).pluck(:choice) + end + delegate :local?, to: :account def remote? diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index f26ea0c748085001a28c459e28569d0daa48b282..4e89fbf8510f9a33783aca9aa695d4b1580eb0c3 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -25,7 +25,7 @@ # class PreviewCard < ApplicationRecord - IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze + IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze LIMIT = 1.megabytes self.inheritance_column = false @@ -43,8 +43,14 @@ class PreviewCard < ApplicationRecord validates_attachment_size :image, less_than: LIMIT remotable_attachment :image, LIMIT + scope :cached, -> { where.not(image_file_name: [nil, '']) } + before_save :extract_dimensions, if: :link? + def missing_image? + width.present? && height.present? && image_file_name.blank? + end + def save_with_optional_image! save! rescue ActiveRecord::RecordInvalid diff --git a/app/models/relay.rb b/app/models/relay.rb index 6934a5c628760f689ceb60244f07ef3eef06ef9d..8c8a97db3290c704ceedb37795c384c1c8c145af 100644 --- a/app/models/relay.rb +++ b/app/models/relay.rb @@ -12,8 +12,6 @@ # class Relay < ApplicationRecord - PRESET_RELAY = 'https://relay.joinmastodon.org/inbox' - validates :inbox_url, presence: true, uniqueness: true, url: true, if: :will_save_change_to_inbox_url? enum state: [:idle, :pending, :accepted, :rejected] @@ -74,7 +72,6 @@ class Relay < ApplicationRecord end def ensure_disabled - return unless enabled? - disable! + disable! if enabled? end end diff --git a/app/models/remote_follow.rb b/app/models/remote_follow.rb index 2537de36c7b3f3ef8b6526114ebefd8196b4d293..5ea53528727b0a4f66f4041d96149d6468ad6427 100644 --- a/app/models/remote_follow.rb +++ b/app/models/remote_follow.rb @@ -2,24 +2,26 @@ class RemoteFollow include ActiveModel::Validations + include RoutingHelper attr_accessor :acct, :addressable_template - validates :acct, presence: true + validates :acct, presence: true, domain: { acct: true } - def initialize(attrs = nil) - @acct = attrs[:acct].gsub(/\A@/, '').strip if !attrs.nil? && !attrs[:acct].nil? + def initialize(attrs = {}) + @acct = normalize_acct(attrs[:acct]) end def valid? return false unless super - populate_template + fetch_template! + errors.empty? end def subscribe_address_for(account) - addressable_template.expand(uri: account.local_username_and_domain).to_s + addressable_template.expand(uri: ActivityPub::TagManager.instance.uri_for(account)).to_s end def interact_address_for(status) @@ -28,8 +30,32 @@ class RemoteFollow private - def populate_template - if acct.blank? || redirect_url_link.nil? || redirect_url_link.template.nil? + def normalize_acct(value) + return if value.blank? + + username, domain = value.strip.gsub(/\A@/, '').split('@') + + domain = begin + if TagManager.instance.local_domain?(domain) + nil + else + TagManager.instance.normalize_domain(domain) + end + end + + [username, domain].compact.join('@') + rescue Addressable::URI::InvalidURIError + value + end + + def fetch_template! + return missing_resource_error if acct.blank? + + _, domain = acct.split('@') + + if domain.nil? + @addressable_template = Addressable::Template.new("#{authorize_interaction_url}?uri={uri}") + elsif redirect_url_link.nil? || redirect_url_link.template.nil? missing_resource_error else @addressable_template = Addressable::Template.new(redirect_uri_template) @@ -45,7 +71,7 @@ class RemoteFollow end def acct_resource - @_acct_resource ||= Goldfinger.finger("acct:#{acct}") + @acct_resource ||= Goldfinger.finger("acct:#{acct}") rescue Goldfinger::Error, HTTP::ConnectionError nil end diff --git a/app/models/remote_profile.rb b/app/models/remote_profile.rb deleted file mode 100644 index 742d2b56f4c61e6864d3e34e34d1b1ac3cb23575..0000000000000000000000000000000000000000 --- a/app/models/remote_profile.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -class RemoteProfile - include ActiveModel::Model - - attr_reader :document - - def initialize(body) - @document = Nokogiri::XML.parse(body, nil, 'utf-8') - end - - def root - @root ||= document.at_xpath('/atom:feed|/atom:entry', atom: OStatus::TagManager::XMLNS) - end - - def author - @author ||= root.at_xpath('./atom:author|./dfrn:owner', atom: OStatus::TagManager::XMLNS, dfrn: OStatus::TagManager::DFRN_XMLNS) - end - - def hub_link - @hub_link ||= link_href_from_xml(root, 'hub') - end - - def display_name - @display_name ||= author.at_xpath('./poco:displayName', poco: OStatus::TagManager::POCO_XMLNS)&.content - end - - def note - @note ||= author.at_xpath('./atom:summary|./poco:note', atom: OStatus::TagManager::XMLNS, poco: OStatus::TagManager::POCO_XMLNS)&.content - end - - def scope - @scope ||= author.at_xpath('./mastodon:scope', mastodon: OStatus::TagManager::MTDN_XMLNS)&.content - end - - def avatar - @avatar ||= link_href_from_xml(author, 'avatar') - end - - def header - @header ||= link_href_from_xml(author, 'header') - end - - def emojis - @emojis ||= author.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS) - end - - def locked? - scope == 'private' - end - - private - - def link_href_from_xml(xml, type) - xml.at_xpath(%(./atom:link[@rel="#{type}"]/@href), atom: OStatus::TagManager::XMLNS)&.content - end -end diff --git a/app/models/report.rb b/app/models/report.rb index 5192ceef799cceee317fc8322eb23c81b2658596..fb2e040ee8bc8f871f82af0d1585f5d0222080ff 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -43,7 +43,7 @@ class Report < ApplicationRecord end def statuses - Status.where(id: status_ids).includes(:account, :media_attachments, :mentions) + Status.with_discarded.where(id: status_ids).includes(:account, :media_attachments, :mentions) end def media_attachments @@ -59,6 +59,7 @@ class Report < ApplicationRecord end def resolve!(acting_account) + RemovalWorker.push_bulk(Status.with_discarded.discarded.where(id: status_ids).pluck(:id)) { |status_id| [status_id, { immediate: true }] } update!(action_taken: true, action_taken_by_account_id: acting_account.id) end diff --git a/app/models/status.rb b/app/models/status.rb index acbebf0cb4caa38e9cf8310d195ffbf54eb5a8db..8dd50e40c45756d1cf4b2a49be9aeb93285aeb0b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -22,22 +22,25 @@ # application_id :bigint(8) # in_reply_to_account_id :bigint(8) # poll_id :bigint(8) +# deleted_at :datetime # local_only :boolean # class Status < ApplicationRecord before_destroy :unlink_from_conversations + include Discard::Model include Paginable - include Streamable include Cacheable include StatusThreadingConcern + self.discard_column = :deleted_at + # If `override_timestamps` is set at creation time, Snowflake ID creation # will be based on current time instead of `created_at` attr_accessor :override_timestamps - update_index('statuses#status', :proper) if Chewy.enabled? + update_index('statuses#status', :proper) enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility @@ -62,7 +65,6 @@ class Status < ApplicationRecord has_and_belongs_to_many :preview_cards has_one :notification, as: :activity, dependent: :destroy - has_one :stream_entry, as: :activity, inverse_of: :status has_one :status_stat, inverse_of: :status has_one :poll, inverse_of: :status, dependent: :destroy @@ -75,7 +77,7 @@ class Status < ApplicationRecord accepts_nested_attributes_for :poll - default_scope { recent } + default_scope { recent.kept } scope :recent, -> { reorder(id: :desc) } scope :remote, -> { where(local: false).where.not(uri: nil) } @@ -108,13 +110,11 @@ class Status < ApplicationRecord :status_stat, :tags, :preview_cards, - :stream_entry, :preloadable_poll, account: :account_stat, active_mentions: { account: :account_stat }, reblog: [ :application, - :stream_entry, :tags, :preview_cards, :media_attachments, @@ -131,12 +131,14 @@ class Status < ApplicationRecord REAL_TIME_WINDOW = 6.hours def searchable_by(preloaded = nil) - ids = [account_id] + ids = [] + + ids << account_id if local? if preloaded.nil? - ids += mentions.pluck(:account_id) - ids += favourites.pluck(:account_id) - ids += reblogs.pluck(:account_id) + ids += mentions.where(account: Account.local).pluck(:account_id) + ids += favourites.where(account: Account.local).pluck(:account_id) + ids += reblogs.where(account: Account.local).pluck(:account_id) else ids += preloaded.mentions[id] || [] ids += preloaded.favourites[id] || [] @@ -203,7 +205,7 @@ class Status < ApplicationRecord end def hidden? - private_visibility? || direct_visibility? || limited_visibility? + !distributable? end def distributable? @@ -220,6 +222,10 @@ class Status < ApplicationRecord !sensitive? && with_media? end + def reported? + @reported ||= Report.where(target_account: account).unresolved.where('? = ANY(status_ids)', id).exists? + end + def emojis return @emojis if defined?(@emojis) @@ -284,51 +290,6 @@ class Status < ApplicationRecord where(language: nil).or where(language: account.chosen_languages) end - def as_home_timeline(account) - where(account: [account] + account.following).where(visibility: [:public, :unlisted, :private]) - end - - def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false) - # direct timeline is mix of direct message from_me and to_me. - # 2 queries are executed with pagination. - # constant expression using arel_table is required for partial index - - # _from_me part does not require any timeline filters - query_from_me = where(account_id: account.id) - .where(Status.arel_table[:visibility].eq(3)) - .limit(limit) - .order('statuses.id DESC') - - # _to_me part requires mute and block filter. - # FIXME: may we check mutes.hide_notifications? - query_to_me = Status - .joins(:mentions) - .merge(Mention.where(account_id: account.id)) - .where(Status.arel_table[:visibility].eq(3)) - .limit(limit) - .order('mentions.status_id DESC') - .not_excluded_by_account(account) - - if max_id.present? - query_from_me = query_from_me.where('statuses.id < ?', max_id) - query_to_me = query_to_me.where('mentions.status_id < ?', max_id) - end - - if since_id.present? - query_from_me = query_from_me.where('statuses.id > ?', since_id) - query_to_me = query_to_me.where('mentions.status_id > ?', since_id) - end - - if cache_ids - # returns array of cache_ids object that have id and updated_at - (query_from_me.cache_ids.to_a + query_to_me.cache_ids.to_a).uniq(&:id).sort_by(&:id).reverse.take(limit) - else - # returns ActiveRecord.Relation - items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit) - Status.where(id: items.map(&:id)) - end - end - def as_public_timeline(account = nil, local_only = false) query = timeline_scope(local_only).without_replies @@ -350,7 +311,7 @@ class Status < ApplicationRecord end def reblogs_map(status_ids, account_id) - select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).reorder(nil).each_with_object({}) { |s, h| h[s.reblog_of_id] = true } + unscoped.select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).each_with_object({}) { |s, h| h[s.reblog_of_id] = true } end def mutes_map(conversation_ids, account_id) @@ -441,13 +402,16 @@ class Status < ApplicationRecord end end + def status_stat + super || build_status_stat + end + private def update_status_stat!(attrs) return if marked_for_destruction? || destroyed? - record = status_stat || build_status_stat - record.update(attrs) + status_stat.update(attrs) end def store_uri @@ -503,7 +467,8 @@ class Status < ApplicationRecord end def update_statistics - return unless public_visibility? || unlisted_visibility? + return unless distributable? + ActivityTracker.increment('activity:statuses:local') end @@ -512,7 +477,7 @@ class Status < ApplicationRecord account&.increment_count!(:statuses_count) reblog&.increment_count!(:reblogs_count) if reblog? - thread&.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?) + thread&.increment_count!(:replies_count) if in_reply_to_id.present? && distributable? end def decrement_counter_caches @@ -520,7 +485,7 @@ class Status < ApplicationRecord account&.decrement_count!(:statuses_count) reblog&.decrement_count!(:reblogs_count) if reblog? - thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?) + thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && distributable? end def unlink_from_conversations diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb deleted file mode 100644 index b0990df90ccb9b9b74accbfaf7cbb59e3493d1fc..0000000000000000000000000000000000000000 --- a/app/models/stream_entry.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: stream_entries -# -# id :bigint(8) not null, primary key -# activity_id :bigint(8) -# activity_type :string -# created_at :datetime not null -# updated_at :datetime not null -# hidden :boolean default(FALSE), not null -# account_id :bigint(8) -# - -class StreamEntry < ApplicationRecord - include Paginable - - belongs_to :account, inverse_of: :stream_entries - belongs_to :activity, polymorphic: true - belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', inverse_of: :stream_entry - - validates :account, :activity, presence: true - - STATUS_INCLUDES = [:account, :stream_entry, :conversation, :media_attachments, :tags, mentions: :account, reblog: [:stream_entry, :account, :conversation, :media_attachments, :tags, mentions: :account], thread: [:stream_entry, :account]].freeze - - default_scope { where(activity_type: 'Status') } - scope :recent, -> { reorder(id: :desc) } - scope :with_includes, -> { includes(:account, status: STATUS_INCLUDES) } - scope :without_local_only, -> { where(statuses: { local_only: [false, nil] }) } - - delegate :target, :title, :content, :thread, :local_only?, - to: :status, - allow_nil: true - - def object_type - orphaned? || targeted? ? :activity : status.object_type - end - - def verb - orphaned? ? :delete : status.verb - end - - def targeted? - [:follow, :request_friend, :authorize, :reject, :unfollow, :block, :unblock, :share, :favorite].include? verb - end - - def threaded? - (verb == :favorite || object_type == :comment) && !thread.nil? - end - - def mentions - orphaned? ? [] : status.active_mentions.map(&:account) - end - - private - - def orphaned? - status.nil? - end -end diff --git a/app/models/subscription.rb b/app/models/subscription.rb deleted file mode 100644 index 79b81828da51eed043e0d5df28717ef566bbd266..0000000000000000000000000000000000000000 --- a/app/models/subscription.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: subscriptions -# -# id :bigint(8) not null, primary key -# callback_url :string default(""), not null -# secret :string -# expires_at :datetime -# confirmed :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null -# last_successful_delivery_at :datetime -# domain :string -# account_id :bigint(8) not null -# - -class Subscription < ApplicationRecord - MIN_EXPIRATION = 1.day.to_i - MAX_EXPIRATION = 30.days.to_i - - belongs_to :account - - validates :callback_url, presence: true - validates :callback_url, uniqueness: { scope: :account_id } - - scope :confirmed, -> { where(confirmed: true) } - scope :future_expiration, -> { where(arel_table[:expires_at].gt(Time.now.utc)) } - scope :expired, -> { where(arel_table[:expires_at].lt(Time.now.utc)) } - scope :active, -> { confirmed.future_expiration } - - def lease_seconds=(value) - self.expires_at = future_expiration(value) - end - - def lease_seconds - (expires_at - Time.now.utc).to_i - end - - def expired? - Time.now.utc > expires_at - end - - before_validation :set_min_expiration - - private - - def future_expiration(value) - Time.now.utc + future_offset(value).seconds - end - - def future_offset(seconds) - [ - [MIN_EXPIRATION, seconds.to_i].max, - MAX_EXPIRATION, - ].min - end - - def set_min_expiration - self.lease_seconds = 0 unless expires_at - end -end diff --git a/app/models/tag.rb b/app/models/tag.rb index b371d59c1be396a5070e12d913aff4b6fe616e27..d3a7e1e6d4e2d8f1c6cf47a1609b03d47ac8d3bc 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -3,38 +3,55 @@ # # Table name: tags # -# id :bigint(8) not null, primary key -# name :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# name :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# usable :boolean +# trendable :boolean +# listable :boolean +# reviewed_at :datetime +# requested_review_at :datetime +# last_status_at :datetime +# max_score :float +# max_score_at :datetime # class Tag < ApplicationRecord has_and_belongs_to_many :statuses has_and_belongs_to_many :accounts - has_and_belongs_to_many :sample_accounts, -> { searchable.discoverable.popular.limit(3) }, class_name: 'Account' + has_and_belongs_to_many :sample_accounts, -> { local.discoverable.popular.limit(3) }, class_name: 'Account' has_many :featured_tags, dependent: :destroy, inverse_of: :tag has_one :account_tag_stat, dependent: :destroy - HASHTAG_NAME_RE = '([[:word:]_][[:word:]_·]*[[:alpha:]_·][[:word:]_·]*[[:word:]_])|([[:word:]_]*[[:alpha:]][[:word:]_]*)' - HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i + HASHTAG_SEPARATORS = "_\u00B7\u200c" + HASHTAG_NAME_RE = "([[:word:]_][[:word:]#{HASHTAG_SEPARATORS}]*[[:alpha:]#{HASHTAG_SEPARATORS}][[:word:]#{HASHTAG_SEPARATORS}]*[[:word:]_])|([[:word:]_]*[[:alpha:]][[:word:]_]*)" + HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_RE})/i - validates :name, presence: true, uniqueness: true, format: { with: /\A(#{HASHTAG_NAME_RE})\z/i } + validates :name, presence: true, format: { with: /\A(#{HASHTAG_NAME_RE})\z/i } + validate :validate_name_change, if: -> { !new_record? && name_changed? } - scope :discoverable, -> { joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).where(account_tag_stats: { hidden: false }).order(Arel.sql('account_tag_stats.accounts_count desc')) } - scope :hidden, -> { where(account_tag_stats: { hidden: true }) } + scope :reviewed, -> { where.not(reviewed_at: nil) } + scope :unreviewed, -> { where(reviewed_at: nil) } + scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) } + scope :usable, -> { where(usable: [true, nil]) } + scope :listable, -> { where(listable: [true, nil]) } + scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) } + scope :discoverable, -> { listable.joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) } scope :most_used, ->(account) { joins(:statuses).where(statuses: { account: account }).group(:id).order(Arel.sql('count(*) desc')) } + scope :matches_name, ->(value) { where(arel_table[:name].matches("#{value}%")) } delegate :accounts_count, :accounts_count=, :increment_count!, :decrement_count!, - :hidden?, to: :account_tag_stat after_save :save_account_tag_stat + update_index('tags#tag', :self) + def account_tag_stat super || build_account_tag_stat end @@ -47,6 +64,40 @@ class Tag < ApplicationRecord name end + def usable + boolean_with_default('usable', true) + end + + alias usable? usable + + def listable + boolean_with_default('listable', true) + end + + alias listable? listable + + def trendable + boolean_with_default('trendable', Setting.trendable_by_default) + end + + alias trendable? trendable + + def requires_review? + reviewed_at.nil? + end + + def reviewed? + reviewed_at.present? + end + + def requested_review? + requested_review_at.present? + end + + def trending? + TrendingTags.trending?(self) + end + def history days = [] @@ -64,22 +115,50 @@ class Tag < ApplicationRecord end class << self - def search_for(term, limit = 5, offset = 0) - pattern = sanitize_sql_like(term.strip) + '%' + def find_or_create_by_names(name_or_names) + Array(name_or_names).map(&method(:normalize)).uniq { |str| str.mb_chars.downcase.to_s }.map do |normalized_name| + tag = matching_name(normalized_name).first || create!(name: normalized_name) + + yield tag if block_given? - Tag.where('lower(name) like lower(?)', pattern) - .order(:name) - .limit(limit) - .offset(offset) + tag + end + end + + def search_for(term, limit = 5, offset = 0, options = {}) + normalized_term = normalize(term.strip).mb_chars.downcase.to_s + pattern = sanitize_sql_like(normalized_term) + '%' + query = Tag.listable.where(arel_table[:name].lower.matches(pattern)) + query = query.where(arel_table[:name].lower.eq(normalized_term).or(arel_table[:reviewed_at].not_eq(nil))) if options[:exclude_unreviewed] + + query.order(Arel.sql('length(name) ASC, name ASC')) + .limit(limit) + .offset(offset) end def find_normalized(name) - find_by(name: name.mb_chars.downcase.to_s) + matching_name(name).first end def find_normalized!(name) find_normalized(name) || raise(ActiveRecord::RecordNotFound) end + + def matching_name(name_or_names) + names = Array(name_or_names).map { |name| normalize(name).mb_chars.downcase.to_s } + + if names.size == 1 + where(arel_table[:name].lower.eq(names.first)) + else + where(arel_table[:name].lower.in(names)) + end + end + + private + + def normalize(str) + str.gsub(/\A#/, '') + end end private @@ -88,4 +167,8 @@ class Tag < ApplicationRecord return unless account_tag_stat&.changed? account_tag_stat.save end + + def validate_name_change + errors.add(:name, I18n.t('tags.does_not_match_previous_name')) unless name_was.mb_chars.casecmp(name.mb_chars).zero? + end end diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb new file mode 100644 index 0000000000000000000000000000000000000000..8921e186b05b498184cdcb77e2a9ddc7a795f574 --- /dev/null +++ b/app/models/tag_filter.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class TagFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = Tag.unscoped + + params.each do |key, value| + next if key.to_s == 'page' + + scope.merge!(scope_for(key, value.to_s.strip)) if value.present? + end + + scope.order(id: :desc) + end + + private + + def scope_for(key, value) + case key.to_s + when 'directory' + Tag.discoverable + when 'reviewed' + Tag.reviewed.order(reviewed_at: :desc) + when 'unreviewed' + Tag.unreviewed + when 'pending_review' + Tag.pending_review.order(requested_review_at: :desc) + when 'popular' + Tag.order('max_score DESC NULLS LAST') + when 'active' + Tag.order('last_status_at DESC NULLS LAST') + when 'name' + Tag.matches_name(value) + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/trending_tags.rb b/app/models/trending_tags.rb index 148535c2121e0a2c9d783a8fb971226ac5d00e29..c69f6d3c331cc8ca1e0b692be02dc46bc2f5fe4c 100644 --- a/app/models/trending_tags.rb +++ b/app/models/trending_tags.rb @@ -5,23 +5,100 @@ class TrendingTags EXPIRE_HISTORY_AFTER = 7.days.seconds EXPIRE_TRENDS_AFTER = 1.day.seconds THRESHOLD = 5 + LIMIT = 10 + REVIEW_THRESHOLD = 3 + MAX_SCORE_COOLDOWN = 2.days.freeze + MAX_SCORE_HALFLIFE = 2.hours.freeze class << self include Redisable def record_use!(tag, account, at_time = Time.now.utc) - return if disallowed_hashtags.include?(tag.name) || account.silenced? || account.bot? + return if account.silenced? || account.bot? || !tag.usable? || !(tag.trendable? || tag.requires_review?) increment_historical_use!(tag.id, at_time) increment_unique_use!(tag.id, account.id, at_time) - increment_vote!(tag.id, at_time) + increment_use!(tag.id, at_time) + + tag.update(last_status_at: Time.now.utc) if tag.last_status_at.nil? || tag.last_status_at < 12.hours.ago + end + + def update!(at_time = Time.now.utc) + tag_ids = redis.smembers("#{KEY}:used:#{at_time.beginning_of_day.to_i}") + redis.zrange(KEY, 0, -1) + tags = Tag.where(id: tag_ids.uniq) + + # First pass to calculate scores and update the set + + tags.each do |tag| + expected = redis.pfcount("activity:tags:#{tag.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f + expected = 1.0 if expected.zero? + observed = redis.pfcount("activity:tags:#{tag.id}:#{at_time.beginning_of_day.to_i}:accounts").to_f + max_time = tag.max_score_at + max_score = tag.max_score + max_score = 0 if max_time.nil? || max_time < (at_time - MAX_SCORE_COOLDOWN) + + score = begin + if expected > observed || observed < THRESHOLD + 0 + else + ((observed - expected)**2) / expected + end + end + + if score > max_score + max_score = score + max_time = at_time + + # Not interested in triggering any callbacks for this + tag.update_columns(max_score: max_score, max_score_at: max_time) + end + + decaying_score = max_score * (0.5**((at_time.to_f - max_time.to_f) / MAX_SCORE_HALFLIFE.to_f)) + + if decaying_score.zero? + redis.zrem(KEY, tag.id) + else + redis.zadd(KEY, decaying_score, tag.id) + end + end + + users_for_review = User.staff.includes(:account).to_a.select(&:allows_trending_tag_emails?) + + # Second pass to notify about previously unreviewed trends + + tags.each do |tag| + current_rank = redis.zrevrank(KEY, tag.id) + needs_review_notification = tag.requires_review? && !tag.requested_review? + rank_passes_threshold = current_rank.present? && current_rank <= REVIEW_THRESHOLD + + next unless !tag.trendable? && rank_passes_threshold && needs_review_notification + + tag.touch(:requested_review_at) + + users_for_review.each do |user| + AdminMailer.new_trending_tag(user.account, tag).deliver_later! + end + end + + # Trim older items + + redis.zremrangebyrank(KEY, 0, -(LIMIT + 1)) + redis.zremrangebyscore(KEY, '(0.3', '-inf') end - def get(limit) - key = "#{KEY}:#{Time.now.utc.beginning_of_day.to_i}" - tag_ids = redis.zrevrange(key, 0, limit - 1).map(&:to_i) - tags = Tag.where(id: tag_ids).to_a.each_with_object({}) { |tag, h| h[tag.id] = tag } - tag_ids.map { |tag_id| tags[tag_id] }.compact + def get(limit, filtered: true) + tag_ids = redis.zrevrange(KEY, 0, LIMIT - 1).map(&:to_i) + + tags = Tag.where(id: tag_ids) + tags = tags.trendable if filtered + tags = tags.each_with_object({}) { |tag, h| h[tag.id] = tag } + + tag_ids.map { |tag_id| tags[tag_id] }.compact.take(limit) + end + + def trending?(tag) + rank = redis.zrevrank(KEY, tag.id) + rank.present? && rank < LIMIT end private @@ -38,28 +115,10 @@ class TrendingTags redis.expire(key, EXPIRE_HISTORY_AFTER) end - def increment_vote!(tag_id, at_time) - key = "#{KEY}:#{at_time.beginning_of_day.to_i}" - expected = redis.pfcount("activity:tags:#{tag_id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts").to_f - expected = 1.0 if expected.zero? - observed = redis.pfcount("activity:tags:#{tag_id}:#{at_time.beginning_of_day.to_i}:accounts").to_f - - if expected > observed || observed < THRESHOLD - redis.zrem(key, tag_id.to_s) - else - score = ((observed - expected)**2) / expected - redis.zadd(key, score, tag_id.to_s) - end - - redis.expire(key, EXPIRE_TRENDS_AFTER) - end - - def disallowed_hashtags - return @disallowed_hashtags if defined?(@disallowed_hashtags) - - @disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags - @disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String - @disallowed_hashtags = @disallowed_hashtags.map(&:downcase) + def increment_use!(tag_id, at_time) + key = "#{KEY}:used:#{at_time.beginning_of_day.to_i}" + redis.sadd(key, tag_id) + redis.expire(key, EXPIRE_HISTORY_AFTER) end end end diff --git a/app/models/user.rb b/app/models/user.rb index 2ff886531a30d2632c1d8a6c5bfe9c92281d126c..b34546ab98447d67a13ce94f8057346a3ae923eb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -74,6 +74,7 @@ class User < ApplicationRecord has_many :applications, class_name: 'Doorkeeper::Application', as: :owner has_many :backups, inverse_of: :user has_many :invites, inverse_of: :user + has_many :markers, inverse_of: :user, dependent: :destroy has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? } @@ -107,7 +108,8 @@ class User < ApplicationRecord delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network, :expand_spoilers, :default_language, :aggregate_reblogs, :show_application, - :advanced_layout, :default_federation, to: :settings, prefix: :setting, allow_nil: false + :advanced_layout, :use_blurhash, :use_pending_items, :trends, :default_federation, + to: :settings, prefix: :setting, allow_nil: false attr_reader :invite_code attr_writer :external @@ -162,7 +164,15 @@ class User < ApplicationRecord end def active_for_authentication? - super && approved? + true + end + + def functional? + confirmed? && approved? && !disabled? && !account.suspended? && account.moved_to_account_id.nil? + end + + def unconfirmed_or_pending? + !(confirmed? && approved?) end def inactive_message @@ -203,6 +213,10 @@ class User < ApplicationRecord settings.notification_emails['pending_account'] end + def allows_trending_tag_emails? + settings.notification_emails['trending_tag'] + end + def hides_network? @hides_network ||= settings.hide_network end @@ -250,17 +264,20 @@ class User < ApplicationRecord end def password_required? - return false if Devise.pam_authentication || Devise.ldap_authentication + return false if external? + super end def send_reset_password_instructions - return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication) + return false if encrypted_password.blank? + super end def reset_password!(new_password, new_password_confirmation) - return false if encrypted_password.blank? && (Devise.pam_authentication || Devise.ldap_authentication) + return false if encrypted_password.blank? + super end diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index b57807d1c2bd5a38144cc617d4911eaabeeb5e97..c5dbb58baad0e5d01340074118ef9577dec9006f 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -20,6 +20,10 @@ class Web::PushSubscription < ApplicationRecord has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription + validates :endpoint, presence: true + validates :key_p256dh, presence: true + validates :key_auth, presence: true + def push(notification) I18n.with_locale(associated_user&.locale || I18n.default_locale) do push_payload(payload_for_notification(notification), 48.hours.seconds) diff --git a/app/policies/domain_allow_policy.rb b/app/policies/domain_allow_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..5030453bbcc2cb86ce07b079f9ec1a83353e9169 --- /dev/null +++ b/app/policies/domain_allow_policy.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class DomainAllowPolicy < ApplicationPolicy + def create? + admin? + end + + def destroy? + admin? + end +end diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index a5646d43a48c923ead46e329818edf861776fe3a..00e12bcbc9b18e308e0298dedbc5c7d0276aa6e8 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -19,7 +19,7 @@ class StatusPolicy < ApplicationPolicy elsif private? owned? || following_author? || mention_exists? else - current_account.nil? || !author_blocking? + current_account.nil? || (!author_blocking? && !author_blocking_domain?) end end @@ -65,6 +65,12 @@ class StatusPolicy < ApplicationPolicy end end + def author_blocking_domain? + return false if current_account.nil? || current_account.domain.nil? + + author.domain_blocking?(current_account.domain) + end + def blocking_author? return false if current_account.nil? diff --git a/app/policies/subscription_policy.rb b/app/policies/subscription_policy.rb deleted file mode 100644 index ac9a8a6c44189dca9dc4d2674ba6931ddd5f5ce9..0000000000000000000000000000000000000000 --- a/app/policies/subscription_policy.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class SubscriptionPolicy < ApplicationPolicy - def index? - admin? - end -end diff --git a/app/policies/tag_policy.rb b/app/policies/tag_policy.rb index c63de01dbe9ef4a34de76da9b90c77438928c8e4..aaf70fcabc4c53afd67a6ee5283e2d9ee1cda9a2 100644 --- a/app/policies/tag_policy.rb +++ b/app/policies/tag_policy.rb @@ -5,11 +5,11 @@ class TagPolicy < ApplicationPolicy staff? end - def hide? + def show? staff? end - def unhide? + def update? staff? end end diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb index f3a73209afe56658c78372d3f2b272e5fbcdd10b..c150bf742be5799816de757aa6d0795326dddaff 100644 --- a/app/presenters/instance_presenter.rb +++ b/app/presenters/instance_presenter.rb @@ -20,8 +20,8 @@ class InstancePresenter Rails.cache.fetch('user_count') { User.confirmed.joins(:account).merge(Account.without_suspended).count } end - def active_user_count - Rails.cache.fetch('active_user_count') { Redis.current.pfcount(*(0..3).map { |i| "activity:logins:#{i.weeks.ago.utc.to_date.cweek}" }) } + def active_user_count(weeks = 4) + Rails.cache.fetch("active_user_count/#{weeks}") { Redis.current.pfcount(*(0...weeks).map { |i| "activity:logins:#{i.weeks.ago.utc.to_date.cweek}" }) } end def status_count @@ -33,7 +33,7 @@ class InstancePresenter end def sample_accounts - Rails.cache.fetch('sample_accounts', expires_in: 12.hours) { Account.discoverable.popular.limit(3) } + Rails.cache.fetch('sample_accounts', expires_in: 12.hours) { Account.local.discoverable.popular.limit(3) } end def version_number diff --git a/app/serializers/activitypub/activity_serializer.rb b/app/serializers/activitypub/activity_serializer.rb index c06d5c87ca3855f0a9c6d6dd20c10ec1eebec1df..d0edad78682ba3383cd2c252c6a5d4055e4f6e0b 100644 --- a/app/serializers/activitypub/activity_serializer.rb +++ b/app/serializers/activitypub/activity_serializer.rb @@ -4,6 +4,7 @@ class ActivityPub::ActivitySerializer < ActivityPub::Serializer attributes :id, :type, :actor, :published, :to, :cc has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, if: :serialize_object? + attribute :proper_uri, key: :object, unless: :serialize_object? attribute :atom_uri, if: :announce? diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index 0644219fb69bf5babc78e725150f70cfa480dc51..17df85de31a6fbc60dee5324ab5570b7d6ac4522 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -6,12 +6,14 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer context :security context_extensions :manually_approves_followers, :featured, :also_known_as, - :moved_to, :property_value, :hashtag, :emoji, :identity_proof + :moved_to, :property_value, :identity_proof, + :discoverable attributes :id, :type, :following, :followers, :inbox, :outbox, :featured, :preferred_username, :name, :summary, - :url, :manually_approves_followers + :url, :manually_approves_followers, + :discoverable has_one :public_key, serializer: ActivityPub::PublicKeySerializer @@ -39,11 +41,17 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer delegate :moved?, to: :object def id - account_url(object) + object.instance_actor? ? instance_actor_url : account_url(object) end def type - object.bot? ? 'Service' : 'Person' + if object.instance_actor? + 'Application' + elsif object.bot? + 'Service' + else + 'Person' + end end def following @@ -55,7 +63,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def inbox - account_inbox_url(object) + object.instance_actor? ? instance_actor_inbox_url : account_inbox_url(object) end def outbox @@ -95,7 +103,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def url - short_account_url(object) + object.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(object) end def avatar_exists? @@ -130,6 +138,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end class TagSerializer < ActivityPub::Serializer + context_extensions :hashtag + include RoutingHelper attributes :type, :href, :name diff --git a/app/serializers/activitypub/move_serializer.rb b/app/serializers/activitypub/move_serializer.rb new file mode 100644 index 0000000000000000000000000000000000000000..5675875fa52e588d7e990b72b7b19a46a90c2f80 --- /dev/null +++ b/app/serializers/activitypub/move_serializer.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class ActivityPub::MoveSerializer < ActivityPub::Serializer + attributes :id, :type, :target, :actor + attribute :virtual_object, key: :object + + def id + [ActivityPub::TagManager.instance.uri_for(object.account), '#moves/', object.id].join + end + + def type + 'Move' + end + + def target + ActivityPub::TagManager.instance.uri_for(object.target_account) + end + + def virtual_object + ActivityPub::TagManager.instance.uri_for(object.account) + end + + def actor + ActivityPub::TagManager.instance.uri_for(object.account) + end +end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 67f596e78a65042a85821ae7693047dbfefc9e4e..110621a28bf88ec6498b776368bac696bfa0fdc5 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true class ActivityPub::NoteSerializer < ActivityPub::Serializer - context_extensions :atom_uri, :conversation, :sensitive, - :hashtag, :emoji, :focal_point, :blurhash + context_extensions :atom_uri, :conversation, :sensitive, :voters_count attributes :id, :type, :summary, :in_reply_to, :published, :url, @@ -24,6 +23,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer attribute :end_time, if: :poll_and_expires? attribute :closed, if: :poll_and_expired? + attribute :voters_count, if: :poll_and_voters_count? + def id ActivityPub::TagManager.instance.uri_for(object) end @@ -55,7 +56,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer type: :unordered, part_of: ActivityPub::TagManager.instance.replies_uri_for(object), items: replies.map(&:second), - next: last_id ? ActivityPub::TagManager.instance.replies_uri_for(object, page: true, min_id: last_id) : nil + next: last_id ? ActivityPub::TagManager.instance.replies_uri_for(object, page: true, min_id: last_id) : ActivityPub::TagManager.instance.replies_uri_for(object, page: true, only_other_accounts: true) ) ) end @@ -142,6 +143,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer alias end_time closed + def voters_count + object.preloadable_poll.voters_count + end + def poll_and_expires? object.preloadable_poll&.expires_at&.present? end @@ -150,7 +155,13 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer object.preloadable_poll&.expired? end + def poll_and_voters_count? + object.preloadable_poll&.voters_count + end + class MediaAttachmentSerializer < ActivityPub::Serializer + context_extensions :blurhash, :focal_point + include RoutingHelper attributes :type, :media_type, :url, :name, :blurhash @@ -198,6 +209,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer end class TagSerializer < ActivityPub::Serializer + context_extensions :hashtag + include RoutingHelper attributes :type, :href, :name diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 883598eb0b8642bbcbdd58787ba4e95c74f8b8a0..e5c43bd7bfd8b6eb75f0c19296ab057c4167a93b 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -12,6 +12,7 @@ class InitialStateSerializer < ActiveModel::Serializer access_token: object.token, locale: I18n.locale, domain: Rails.configuration.x.local_domain, + title: instance_presenter.site_title, admin: object.admin&.id&.to_s, search_enabled: Chewy.enabled?, repository: Mastodon::Version.repository, @@ -20,19 +21,28 @@ class InitialStateSerializer < ActiveModel::Serializer invites_enabled: Setting.min_invite_role == 'user', mascot: instance_presenter.mascot&.file&.url, profile_directory: Setting.profile_directory, + trends: Setting.trends, } if object.current_account - store[:me] = object.current_account.id.to_s - store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal - store[:boost_modal] = object.current_account.user.setting_boost_modal - store[:delete_modal] = object.current_account.user.setting_delete_modal - store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif - store[:display_media] = object.current_account.user.setting_display_media - store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers - store[:reduce_motion] = object.current_account.user.setting_reduce_motion - store[:advanced_layout] = object.current_account.user.setting_advanced_layout - store[:is_staff] = object.current_account.user.staff? + store[:me] = object.current_account.id.to_s + store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal + store[:boost_modal] = object.current_account.user.setting_boost_modal + store[:delete_modal] = object.current_account.user.setting_delete_modal + store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif + store[:display_media] = object.current_account.user.setting_display_media + store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers + store[:reduce_motion] = object.current_account.user.setting_reduce_motion + store[:advanced_layout] = object.current_account.user.setting_advanced_layout + store[:use_blurhash] = object.current_account.user.setting_use_blurhash + store[:use_pending_items] = object.current_account.user.setting_use_pending_items + store[:is_staff] = object.current_account.user.staff? + store[:trends] = Setting.trends && object.current_account.user.setting_trends + else + store[:auto_play_gif] = Setting.auto_play_gif + store[:display_media] = Setting.display_media + store[:reduce_motion] = Setting.reduce_motion + store[:use_blurhash] = Setting.use_blurhash end store diff --git a/app/serializers/manifest_serializer.rb b/app/serializers/manifest_serializer.rb index 28127437d568831e33836d2bef63ae2addcc6b73..21ec0d4bed2f96f38b9ec58f1f6dc4950a6413fc 100644 --- a/app/serializers/manifest_serializer.rb +++ b/app/serializers/manifest_serializer.rb @@ -55,6 +55,8 @@ class ManifestSerializer < ActiveModel::Serializer { url_template: 'share?title={title}&text={text}&url={url}', action: 'share', + method: 'GET', + enctype: 'application/x-www-form-urlencoded', params: { title: 'title', text: 'text', diff --git a/app/serializers/nodeinfo/discovery_serializer.rb b/app/serializers/nodeinfo/discovery_serializer.rb new file mode 100644 index 0000000000000000000000000000000000000000..07ab2a6eee304e3f64bf51ab626e3969ec93c89f --- /dev/null +++ b/app/serializers/nodeinfo/discovery_serializer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class NodeInfo::DiscoverySerializer < ActiveModel::Serializer + include RoutingHelper + + attribute :links + + def links + [{ rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', href: nodeinfo_schema_url }] + end +end diff --git a/app/serializers/nodeinfo/serializer.rb b/app/serializers/nodeinfo/serializer.rb new file mode 100644 index 0000000000000000000000000000000000000000..7ff8aabecc56c50eddde2294511bc26635a3d646 --- /dev/null +++ b/app/serializers/nodeinfo/serializer.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class NodeInfo::Serializer < ActiveModel::Serializer + include RoutingHelper + + attributes :version, :software, :protocols, :usage, :open_registrations + + def version + '2.0' + end + + def software + { name: 'mastodon', version: Mastodon::Version.to_s } + end + + def services + { outbound: [], inbound: [] } + end + + def protocols + %w(activitypub) + end + + def usage + { + users: { + total: instance_presenter.user_count, + active_month: instance_presenter.active_user_count(4), + active_halfyear: instance_presenter.active_user_count(24), + }, + + local_posts: instance_presenter.status_count, + } + end + + def open_registrations + Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode + end + + private + + def instance_presenter + @instance_presenter ||= InstancePresenter.new + end +end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 12adc971cb76a6894338b2dcf342d34026b287a3..75b6cf13bb7b74e96229838fdcef4aefd39d0cff 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -5,7 +5,7 @@ class REST::AccountSerializer < ActiveModel::Serializer attributes :id, :username, :acct, :display_name, :locked, :bot, :created_at, :note, :url, :avatar, :avatar_static, :header, :header_static, - :followers_count, :following_count, :statuses_count + :followers_count, :following_count, :statuses_count, :last_status_at has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested? has_many :emojis, serializer: REST::CustomEmojiSerializer @@ -29,7 +29,7 @@ class REST::AccountSerializer < ActiveModel::Serializer end def url - TagManager.instance.url_for(object) + ActivityPub::TagManager.instance.url_for(object) end def avatar diff --git a/app/serializers/rest/credential_account_serializer.rb b/app/serializers/rest/credential_account_serializer.rb index f07f9f05802386365fa3c0aad9d924afd84bbc85..fa8f900cc594170dc646a0fab0fee5643a6082b3 100644 --- a/app/serializers/rest/credential_account_serializer.rb +++ b/app/serializers/rest/credential_account_serializer.rb @@ -13,6 +13,7 @@ class REST::CredentialAccountSerializer < REST::AccountSerializer federation: user.setting_default_federation, note: object.note, fields: object.fields.map(&:to_h), + follow_requests_count: FollowRequest.where(target_account: object).limit(40).count, } end end diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index 65686a86668d2fc450a73bf4fe9bacdfd325dcc6..aff58e4d9466accb3b61142fa2b69b9d65d48fa6 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -5,6 +5,8 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer attributes :shortcode, :url, :static_url, :visible_in_picker + attribute :category, if: :category_loaded? + def url full_asset_url(object.image.url) end @@ -12,4 +14,12 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer def static_url full_asset_url(object.image.url(:static)) end + + def category + object.category.name + end + + def category_loaded? + object.association(:category).loaded? && object.category.present? + end end diff --git a/app/serializers/rest/featured_tag_serializer.rb b/app/serializers/rest/featured_tag_serializer.rb new file mode 100644 index 0000000000000000000000000000000000000000..08121ff16db2be23bcb352b5a4f4cfc129eb74a0 --- /dev/null +++ b/app/serializers/rest/featured_tag_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::FeaturedTagSerializer < ActiveModel::Serializer + attributes :id, :name, :statuses_count, :last_status_at + + def id + object.id.to_s + end +end diff --git a/app/serializers/rest/marker_serializer.rb b/app/serializers/rest/marker_serializer.rb new file mode 100644 index 0000000000000000000000000000000000000000..2eaf3d5076724ce8f0195655fa8c8628d24d92bd --- /dev/null +++ b/app/serializers/rest/marker_serializer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class REST::MarkerSerializer < ActiveModel::Serializer + attributes :last_read_id, :version, :updated_at + + def last_read_id + object.last_read_id.to_s + end + + def version + object.lock_version + end +end diff --git a/app/serializers/rest/poll_serializer.rb b/app/serializers/rest/poll_serializer.rb index 356c45b83868de1d96d257cc5838d0085df37e67..df6ebd0d44676c5f5a9d4014fccb031015ae1cd3 100644 --- a/app/serializers/rest/poll_serializer.rb +++ b/app/serializers/rest/poll_serializer.rb @@ -2,12 +2,13 @@ class REST::PollSerializer < ActiveModel::Serializer attributes :id, :expires_at, :expired, - :multiple, :votes_count + :multiple, :votes_count, :voters_count has_many :loaded_options, key: :options has_many :emojis, serializer: REST::CustomEmojiSerializer attribute :voted, if: :current_user? + attribute :own_votes, if: :current_user? def id object.id.to_s @@ -21,6 +22,10 @@ class REST::PollSerializer < ActiveModel::Serializer object.voted?(current_user.account) end + def own_votes + object.own_votes(current_user.account) + end + def current_user? !current_user.nil? end diff --git a/app/serializers/rest/search_serializer.rb b/app/serializers/rest/search_serializer.rb index 157f543aef716a9d413e13ed2e3f4f18cdc4ce5a..ee9b421eb223daf94650dc58f8e07fbb1e7c925b 100644 --- a/app/serializers/rest/search_serializer.rb +++ b/app/serializers/rest/search_serializer.rb @@ -1,12 +1,7 @@ # frozen_string_literal: true class REST::SearchSerializer < ActiveModel::Serializer - attributes :hashtags - has_many :accounts, serializer: REST::AccountSerializer has_many :statuses, serializer: REST::StatusSerializer - - def hashtags - object.hashtags.map(&:name) - end + has_many :hashtags, serializer: REST::TagSerializer end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 7d2dc4e82d27ce17965a0352f99ae41156e30fcf..ad0287a91c9cdfc93dc7d1ed73282ce6e82f6c16 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -58,7 +58,7 @@ class REST::StatusSerializer < ActiveModel::Serializer end def uri - OStatus::TagManager.instance.uri_for(object) + ActivityPub::TagManager.instance.uri_for(object) end def content @@ -66,7 +66,7 @@ class REST::StatusSerializer < ActiveModel::Serializer end def url - TagManager.instance.url_for(object) + ActivityPub::TagManager.instance.url_for(object) end def favourited @@ -132,7 +132,7 @@ class REST::StatusSerializer < ActiveModel::Serializer end def url - TagManager.instance.url_for(object.account) + ActivityPub::TagManager.instance.url_for(object.account) end def acct diff --git a/app/serializers/rest/v2/search_serializer.rb b/app/serializers/rest/v2/search_serializer.rb deleted file mode 100644 index cdb6b3a53012c974414fa90aa0928983e732aaa4..0000000000000000000000000000000000000000 --- a/app/serializers/rest/v2/search_serializer.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class REST::V2::SearchSerializer < ActiveModel::Serializer - has_many :accounts, serializer: REST::AccountSerializer - has_many :statuses, serializer: REST::StatusSerializer - has_many :hashtags, serializer: REST::TagSerializer -end diff --git a/app/serializers/rss/account_serializer.rb b/app/serializers/rss/account_serializer.rb index 88eca79ed2311cacf7ceb4b58da32942c8586cce..e39b2b3720ebdeafa6ebb5187d55a9c59171ea9c 100644 --- a/app/serializers/rss/account_serializer.rb +++ b/app/serializers/rss/account_serializer.rb @@ -2,15 +2,15 @@ class RSS::AccountSerializer include ActionView::Helpers::NumberHelper - include StreamEntriesHelper + include StatusesHelper include RoutingHelper - def render(account, statuses) + def render(account, statuses, tag) builder = RSSBuilder.new builder.title("#{display_name(account)} (@#{account.local_username_and_domain})") .description(account_description(account)) - .link(TagManager.instance.url_for(account)) + .link(tag.present? ? short_account_tag_url(account, tag) : short_account_url(account)) .logo(full_pack_url('media/images/logo.svg')) .accent_color('2b90d9') @@ -20,12 +20,12 @@ class RSS::AccountSerializer statuses.each do |status| builder.item do |item| item.title(status.title) - .link(TagManager.instance.url_for(status)) + .link(ActivityPub::TagManager.instance.url_for(status)) .pub_date(status.created_at) .description(status.spoiler_text.presence || Formatter.instance.format(status, inline_poll_options: true).to_str) status.media_attachments.each do |media| - item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, length: media.file.size) + item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) end end end @@ -33,7 +33,7 @@ class RSS::AccountSerializer builder.to_xml end - def self.render(account, statuses) - new.render(account, statuses) + def self.render(account, statuses, tag) + new.render(account, statuses, tag) end end diff --git a/app/serializers/rss/tag_serializer.rb b/app/serializers/rss/tag_serializer.rb index 644380149bab9ceae35120d0a077687a038635ef..6737fb2c90088eef2550c615988c4062ec2e47c3 100644 --- a/app/serializers/rss/tag_serializer.rb +++ b/app/serializers/rss/tag_serializer.rb @@ -3,7 +3,7 @@ class RSS::TagSerializer include ActionView::Helpers::NumberHelper include ActionView::Helpers::SanitizeHelper - include StreamEntriesHelper + include StatusesHelper include RoutingHelper def render(tag, statuses) @@ -18,12 +18,12 @@ class RSS::TagSerializer statuses.each do |status| builder.item do |item| item.title(status.title) - .link(TagManager.instance.url_for(status)) + .link(ActivityPub::TagManager.instance.url_for(status)) .pub_date(status.created_at) .description(status.spoiler_text.presence || Formatter.instance.format(status).to_str) status.media_attachments.each do |media| - item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, length: media.file.size) + item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) end end end diff --git a/app/serializers/webfinger_serializer.rb b/app/serializers/webfinger_serializer.rb index 8c0b077020dcd2ce7833cf7063c8db3bf764bfb0..c67363b8f8be6880bca876f5aae2ff88d8a9b5dd 100644 --- a/app/serializers/webfinger_serializer.rb +++ b/app/serializers/webfinger_serializer.rb @@ -10,17 +10,25 @@ class WebfingerSerializer < ActiveModel::Serializer end def aliases - [short_account_url(object), account_url(object)] + if object.instance_actor? + [instance_actor_url] + else + [short_account_url(object), account_url(object)] + end end def links - [ - { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) }, - { rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(object, format: 'atom') }, - { rel: 'self', type: 'application/activity+json', href: account_url(object) }, - { rel: 'salmon', href: api_salmon_url(object.id) }, - { rel: 'magic-public-key', href: "data:application/magic-public-key,#{object.magic_key}" }, - { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, - ] + if object.instance_actor? + [ + { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) }, + { rel: 'self', type: 'application/activity+json', href: instance_actor_url }, + ] + else + [ + { rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: short_account_url(object) }, + { rel: 'self', type: 'application/activity+json', href: account_url(object) }, + { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" }, + ] + end end end diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb index 7bdffbbd22c94f9bd3096c727d86819bfd0810d4..40c5f8590c9888a5278adf7ca1485b4c35a3ca56 100644 --- a/app/services/account_search_service.rb +++ b/app/services/account_search_service.rb @@ -4,98 +4,177 @@ class AccountSearchService < BaseService attr_reader :query, :limit, :offset, :options, :account def call(query, account = nil, options = {}) - @query = query.strip - @limit = options[:limit].to_i - @offset = options[:offset].to_i - @options = options - @account = account + @acct_hint = query.start_with?('@') + @query = query.strip.gsub(/\A@/, '') + @limit = options[:limit].to_i + @offset = options[:offset].to_i + @options = options + @account = account - search_service_results + search_service_results.compact.uniq end private def search_service_results - return [] if query_blank_or_hashtag? || limit < 1 + return [] if query.blank? || limit < 1 - if resolving_non_matching_remote_account? - [ResolveAccountService.new.call("#{query_username}@#{query_domain}")].compact - else - search_results_and_exact_match.compact.uniq.slice(0, limit) - end + [exact_match] + search_results end - def resolving_non_matching_remote_account? - options[:resolve] && !exact_match && !domain_is_local? - end + def exact_match + return unless offset.zero? && username_complete? - def search_results_and_exact_match - exact = [exact_match] - return exact if !exact[0].nil? && limit == 1 - exact + search_results.to_a - end + return @exact_match if defined?(@exact_match) - def query_blank_or_hashtag? - query.blank? || query.start_with?('#') + @exact_match = begin + if options[:resolve] + ResolveAccountService.new.call(query) + elsif domain_is_local? + Account.find_local(query_username) + else + Account.find_remote(query_username, query_domain) + end + end end - def split_query_string - @_split_query_string ||= query.gsub(/\A@/, '').split('@') - end + def search_results + return [] if limit_for_non_exact_results.zero? - def query_username - @_query_username ||= split_query_string.first || '' + @search_results ||= begin + results = from_elasticsearch if Chewy.enabled? + results ||= from_database + results + end end - def query_domain - @_query_domain ||= query_without_split? ? nil : split_query_string.last + def from_database + if account + advanced_search_results + else + simple_search_results + end end - def query_without_split? - split_query_string.size == 1 + def advanced_search_results + Account.advanced_search_for(terms_for_query, account, limit_for_non_exact_results, options[:following], offset) end - def domain_is_local? - @_domain_is_local ||= TagManager.instance.local_domain?(query_domain) + def simple_search_results + Account.search_for(terms_for_query, limit_for_non_exact_results, offset) end - def search_from - options[:following] && account ? account.following : Account - end + def from_elasticsearch + must_clauses = [{ multi_match: { query: terms_for_query, fields: likely_acct? ? %w(acct.edge_ngram acct) : %w(acct.edge_ngram acct display_name.edge_ngram display_name), type: 'most_fields', operator: 'and' } }] + should_clauses = [] - def exact_match - @_exact_match ||= begin - if domain_is_local? - search_from.without_suspended.find_local(query_username) - else - search_from.without_suspended.find_remote(query_username, query_domain) + if account + return [] if options[:following] && following_ids.empty? + + if options[:following] + must_clauses << { terms: { id: following_ids } } + elsif following_ids.any? + should_clauses << { terms: { id: following_ids, boost: 100 } } end end + + query = { bool: { must: must_clauses, should: should_clauses } } + functions = [reputation_score_function, followers_score_function, time_distance_function] + + records = AccountsIndex.query(function_score: { query: query, functions: functions, boost_mode: 'multiply', score_mode: 'avg' }) + .limit(limit_for_non_exact_results) + .offset(offset) + .objects + .compact + + ActiveRecord::Associations::Preloader.new.preload(records, :account_stat) + + records + rescue Faraday::ConnectionFailed, Parslet::ParseFailed + nil end - def search_results - @_search_results ||= begin - if account - advanced_search_results - else - simple_search_results - end - end + def reputation_score_function + { + script_score: { + script: { + source: "(doc['followers_count'].value + 0.0) / (doc['followers_count'].value + doc['following_count'].value + 1)", + }, + }, + } end - def advanced_search_results - Account.advanced_search_for(terms_for_query, account, limit, options[:following], offset) + def followers_score_function + { + field_value_factor: { + field: 'followers_count', + modifier: 'log2p', + missing: 0, + }, + } end - def simple_search_results - Account.search_for(terms_for_query, limit, offset) + def time_distance_function + { + gauss: { + last_status_at: { + scale: '30d', + offset: '30d', + decay: 0.3, + }, + }, + } + end + + def following_ids + @following_ids ||= account.active_relationships.pluck(:target_account_id) + end + + def limit_for_non_exact_results + if exact_match? + limit - 1 + else + limit + end end def terms_for_query if domain_is_local? query_username else - "#{query_username} #{query_domain}" + query end end + + def split_query_string + @split_query_string ||= query.split('@') + end + + def query_username + @query_username ||= split_query_string.first || '' + end + + def query_domain + @query_domain ||= query_without_split? ? nil : split_query_string.last + end + + def query_without_split? + split_query_string.size == 1 + end + + def domain_is_local? + @domain_is_local ||= TagManager.instance.local_domain?(query_domain) + end + + def exact_match? + exact_match.present? + end + + def username_complete? + query.include?('@') && "@#{query}" =~ Account::MENTION_RE + end + + def likely_acct? + @acct_hint || username_complete? + end end diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb index 6a137b520bdc4906105dea7099aadb6061fae4d3..2c27704661c9448d57b0163244b7fb91652b02f4 100644 --- a/app/services/activitypub/fetch_featured_collection_service.rb +++ b/app/services/activitypub/fetch_featured_collection_service.rb @@ -4,13 +4,12 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService include JsonLdHelper def call(account) - return if account.featured_collection_url.blank? + return if account.featured_collection_url.blank? || account.suspended? || account.local? @account = account @json = fetch_resource(@account.featured_collection_url, true) return unless supported_context? - return if @account.suspended? || @account.local? case @json['type'] when 'Collection', 'CollectionPage' diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index 3c204494128ec10afe6da731ac1c196a37b699f5..d65c8f95117c65949eb920e2e0ed6a5f5b17643c 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -2,18 +2,22 @@ class ActivityPub::FetchRemoteAccountService < BaseService include JsonLdHelper + include DomainControlHelper SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze # Does a WebFinger roundtrip on each call, unless `only_key` is true def call(uri, id: true, prefetched_body: nil, break_on_redirect: false, only_key: false) + return if domain_not_allowed?(uri) return ActivityPub::TagManager.instance.uri_to_resource(uri, Account) if ActivityPub::TagManager.instance.local_uri?(uri) - @json = if prefetched_body.nil? - fetch_resource(uri, id) - else - body_to_json(prefetched_body, compare_id: id ? uri : nil) - end + @json = begin + if prefetched_body.nil? + fetch_resource(uri, id) + else + body_to_json(prefetched_body, compare_id: id ? uri : nil) + end + end return if !supported_context? || !expected_type? || (break_on_redirect && @json['movedTo'].present?) diff --git a/app/services/activitypub/fetch_remote_poll_service.rb b/app/services/activitypub/fetch_remote_poll_service.rb index 854a32d050a75e0d0e9f58e804556de873ab5526..1c79ecf1162f6e2d7b4c97ab0bbad9ad71e208d8 100644 --- a/app/services/activitypub/fetch_remote_poll_service.rb +++ b/app/services/activitypub/fetch_remote_poll_service.rb @@ -5,7 +5,9 @@ class ActivityPub::FetchRemotePollService < BaseService def call(poll, on_behalf_of = nil) json = fetch_resource(poll.status.uri, true, on_behalf_of) + return unless supported_context?(json) + ActivityPub::ProcessPollService.new.call(poll, json) end end diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 469821032ab69746c12ea79b868f1e4b1626fa9c..cf4f62899fb85dcf871d5978f1ea097d55275b00 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -5,18 +5,18 @@ class ActivityPub::FetchRemoteStatusService < BaseService # Should be called when uri has already been checked for locality def call(uri, id: true, prefetched_body: nil, on_behalf_of: nil) - @json = if prefetched_body.nil? - fetch_resource(uri, id, on_behalf_of) - else - body_to_json(prefetched_body, compare_id: id ? uri : nil) - end + @json = begin + if prefetched_body.nil? + fetch_resource(uri, id, on_behalf_of) + else + body_to_json(prefetched_body, compare_id: id ? uri : nil) + end + end - return unless supported_context? && expected_type? - - return if actor_id.nil? || !trustworthy_attribution?(@json['id'], actor_id) + return if !(supported_context? && expected_type?) || actor_id.nil? || !trustworthy_attribution?(@json['id'], actor_id) actor = ActivityPub::TagManager.instance.uri_to_resource(actor_id, Account) - actor = ActivityPub::FetchRemoteAccountService.new.call(actor_id, id: true) if actor.nil? || needs_update(actor) + actor = ActivityPub::FetchRemoteAccountService.new.call(actor_id, id: true) if actor.nil? || needs_update?(actor) return if actor.nil? || actor.suspended? @@ -46,7 +46,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService equals_or_includes_any?(@json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) end - def needs_update(actor) + def needs_update?(actor) actor.possibly_stale? end end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 3857e7c16d58991b3f6fc2f55adbe187e45f6ad1..9c649d35cec1675a659c73da5c996544f9d079f0 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -2,11 +2,12 @@ class ActivityPub::ProcessAccountService < BaseService include JsonLdHelper + include DomainControlHelper # Should be called with confirmed valid JSON # and WebFinger-resolved username and domain def call(username, domain, json, options = {}) - return if json['inbox'].blank? || unsupported_uri_scheme?(json['id']) + return if json['inbox'].blank? || unsupported_uri_scheme?(json['id']) || domain_not_allowed?(domain) @options = options @json = json @@ -84,6 +85,7 @@ class ActivityPub::ProcessAccountService < BaseService @account.fields = property_values || {} @account.also_known_as = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) } @account.actor_type = actor_type + @account.discoverable = @json['discoverable'] || false end def set_fetchable_attributes! diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb index 881df478bf53c7b265df09c1cf01917c4a9ae382..a2a2e7071545aae129896eac2102a9317cb23aaa 100644 --- a/app/services/activitypub/process_collection_service.rb +++ b/app/services/activitypub/process_collection_service.rb @@ -8,9 +8,7 @@ class ActivityPub::ProcessCollectionService < BaseService @json = Oj.load(body, mode: :strict) @options = options - return unless supported_context? - return if different_actor? && verify_account!.nil? - return if @account.suspended? || @account.local? + return if !supported_context? || (different_actor? && verify_account!.nil?) || @account.suspended? || @account.local? case @json['type'] when 'Collection', 'CollectionPage' diff --git a/app/services/activitypub/process_poll_service.rb b/app/services/activitypub/process_poll_service.rb index 61357abd3ca6aa24812159a743cc61385a7171a8..cb4a0d46025a82153142860d7c3b87edeb3353a8 100644 --- a/app/services/activitypub/process_poll_service.rb +++ b/app/services/activitypub/process_poll_service.rb @@ -5,6 +5,7 @@ class ActivityPub::ProcessPollService < BaseService def call(poll, json) @json = json + return unless expected_type? previous_expires_at = poll.expires_at @@ -27,6 +28,8 @@ class ActivityPub::ProcessPollService < BaseService end end + voters_count = @json['votersCount'] + latest_options = items.map { |item| item['name'].presence || item['content'] } # If for some reasons the options were changed, it invalidates all previous @@ -38,7 +41,8 @@ class ActivityPub::ProcessPollService < BaseService last_fetched_at: Time.now.utc, expires_at: expires_at, options: latest_options, - cached_tallies: items.map { |item| item.dig('replies', 'totalItems') || 0 } + cached_tallies: items.map { |item| item.dig('replies', 'totalItems') || 0 }, + voters_count: voters_count ) rescue ActiveRecord::StaleObjectError poll.reload diff --git a/app/services/app_sign_up_service.rb b/app/services/app_sign_up_service.rb index 6dee9cd81568eb1b208183e70a90df3a66701954..c9739c77d194796e738057d8856fc4421f2c01af 100644 --- a/app/services/app_sign_up_service.rb +++ b/app/services/app_sign_up_service.rb @@ -4,9 +4,10 @@ class AppSignUpService < BaseService def call(app, params) return unless allowed_registrations? - user_params = params.slice(:email, :password, :agreement, :locale) - account_params = params.slice(:username) - user = User.create!(user_params.merge(created_by_application: app, password_confirmation: user_params[:password], account_attributes: account_params)) + user_params = params.slice(:email, :password, :agreement, :locale) + account_params = params.slice(:username) + invite_request_params = { text: params[:reason] } + user = User.create!(user_params.merge(created_by_application: app, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params)) Doorkeeper::AccessToken.create!(application: app, resource_owner_id: user.id, diff --git a/app/services/authorize_follow_service.rb b/app/services/authorize_follow_service.rb index 29b8700c7c7d06781d00d8d11b695f8da7b779b0..49bef727e6a8c9d90df07728a86d536ca75e506d 100644 --- a/app/services/authorize_follow_service.rb +++ b/app/services/authorize_follow_service.rb @@ -11,25 +11,17 @@ class AuthorizeFollowService < BaseService follow_request.authorize! end - create_notification(follow_request) unless source_account.local? + create_notification(follow_request) if !source_account.local? && source_account.activitypub? follow_request end private def create_notification(follow_request) - if follow_request.account.ostatus? - NotificationWorker.perform_async(build_xml(follow_request), follow_request.target_account_id, follow_request.account_id) - elsif follow_request.account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), follow_request.target_account_id, follow_request.account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), follow_request.target_account_id, follow_request.account.inbox_url) end def build_json(follow_request) Oj.dump(serialize_payload(follow_request, ActivityPub::AcceptFollowSerializer)) end - - def build_xml(follow_request) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)) - end end diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index e328b17391b94008a2027295cf5cc7692fe97d25..3638134be76447612ded2a6fcc9e6766aef8ee58 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class BatchedRemoveStatusService < BaseService - include StreamEntryRenderer include Redisable # Delete given statuses and reblogs of them @@ -9,19 +8,16 @@ class BatchedRemoveStatusService < BaseService # Dispatch Salmon deletes, unique per domain, of the deleted statuses, but only local ones # Remove statuses from home feeds # Push delete events to streaming API for home feeds and public feeds - # @param [Status] statuses A preferably batched array of statuses + # @param [Enumerable<Status>] statuses A preferably batched array of statuses # @param [Hash] options # @option [Boolean] :skip_side_effects def call(statuses, **options) - statuses = Status.where(id: statuses.map(&:id)).includes(:account, :stream_entry).flat_map { |status| [status] + status.reblogs.includes(:account, :stream_entry).to_a } + statuses = Status.where(id: statuses.map(&:id)).includes(:account).flat_map { |status| [status] + status.reblogs.includes(:account).to_a } @mentions = statuses.each_with_object({}) { |s, h| h[s.id] = s.active_mentions.includes(:account).to_a } @tags = statuses.each_with_object({}) { |s, h| h[s.id] = s.tags.pluck(:name) } - @stream_entry_batches = [] - @salmon_batches = [] - @json_payloads = statuses.each_with_object({}) { |s, h| h[s.id] = Oj.dump(event: :delete, payload: s.id.to_s) } - @activity_xml = {} + @json_payloads = statuses.each_with_object({}) { |s, h| h[s.id] = Oj.dump(event: :delete, payload: s.id.to_s) } # Ensure that rendered XML reflects destroyed state statuses.each do |status| @@ -39,28 +35,16 @@ class BatchedRemoveStatusService < BaseService unpush_from_home_timelines(account, account_statuses) unpush_from_list_timelines(account, account_statuses) - - batch_stream_entries(account, account_statuses) if account.local? end # Cannot be batched statuses.each do |status| unpush_from_public_timelines(status) - batch_salmon_slaps(status) if status.local? end - - Pubsubhubbub::RawDistributionWorker.push_bulk(@stream_entry_batches) { |batch| batch } - NotificationWorker.push_bulk(@salmon_batches) { |batch| batch } end private - def batch_stream_entries(account, statuses) - statuses.each do |status| - @stream_entry_batches << [build_xml(status.stream_entry), account.id] - end - end - def unpush_from_home_timelines(account, statuses) recipients = account.followers_for_local_distribution.to_a @@ -96,25 +80,9 @@ class BatchedRemoveStatusService < BaseService end @tags[status.id].each do |hashtag| - redis.publish("timeline:hashtag:#{hashtag}", payload) - redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local? + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", payload) + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", payload) if status.local? end end end - - def batch_salmon_slaps(status) - return if @mentions[status.id].empty? - - recipients = @mentions[status.id].map(&:account).reject(&:local?).select(&:ostatus?).uniq(&:domain).map(&:id) - - recipients.each do |recipient_id| - @salmon_batches << [build_xml(status.stream_entry), status.account_id, recipient_id] - end - end - - def build_xml(stream_entry) - return @activity_xml[stream_entry.id] if @activity_xml.key?(stream_entry.id) - - @activity_xml[stream_entry.id] = stream_entry_to_xml(stream_entry) - end end diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb index c6eef04d420fe75a7ee8d4e79e965a16ecf719f1..ae461abf248e89e5502ba5e5fc21d057f9223442 100644 --- a/app/services/block_domain_service.rb +++ b/app/services/block_domain_service.rb @@ -3,13 +3,22 @@ class BlockDomainService < BaseService attr_reader :domain_block - def call(domain_block) + def call(domain_block, update = false) @domain_block = domain_block process_domain_block! + process_retroactive_updates! if update end private + def process_retroactive_updates! + # If the domain block severity has been changed, undo the appropriate limitations + scope = Account.by_domain_and_subdomains(domain_block.domain) + + scope.where(silenced_at: domain_block.created_at).in_batches.update_all(silenced_at: nil) unless domain_block.silence? + scope.where(suspended_at: domain_block.created_at).in_batches.update_all(suspended_at: nil) unless domain_block.suspend? + end + def process_domain_block! clear_media! if domain_block.reject_media? @@ -44,8 +53,7 @@ class BlockDomainService < BaseService def suspend_accounts! blocked_domain_accounts.without_suspended.reorder(nil).find_each do |account| - UnsubscribeService.new.call(account) if account.subscribed? - SuspendAccountService.new.call(account, suspended_at: @domain_block.created_at) + SuspendAccountService.new.call(account, reserve_username: true, suspended_at: @domain_block.created_at) end end diff --git a/app/services/block_service.rb b/app/services/block_service.rb index 0d9a6eccda47e47ed190285c467b79e2964d7e36..266a0f4b9d0384b107800f61c2e1c37bf91afc4e 100644 --- a/app/services/block_service.rb +++ b/app/services/block_service.rb @@ -13,25 +13,17 @@ class BlockService < BaseService block = account.block!(target_account) BlockWorker.perform_async(account.id, target_account.id) - create_notification(block) unless target_account.local? + create_notification(block) if !target_account.local? && target_account.activitypub? block end private def create_notification(block) - if block.target_account.ostatus? - NotificationWorker.perform_async(build_xml(block), block.account_id, block.target_account_id) - elsif block.target_account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(block), block.account_id, block.target_account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(block), block.account_id, block.target_account.inbox_url) end def build_json(block) Oj.dump(serialize_payload(block, ActivityPub::BlockSerializer)) end - - def build_xml(block) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.block_salmon(block)) - end end diff --git a/app/services/bootstrap_timeline_service.rb b/app/services/bootstrap_timeline_service.rb index db2c83e5d1ced84ab75a70fdd61cd257ec498bb8..c489601c155b72ce40d07fdb6aad1ff544d61945 100644 --- a/app/services/bootstrap_timeline_service.rb +++ b/app/services/bootstrap_timeline_service.rb @@ -17,7 +17,11 @@ class BootstrapTimelineService < BaseService def autofollow_bootstrap_timeline_accounts! bootstrap_timeline_accounts.each do |target_account| - FollowService.new.call(@source_account, target_account) + begin + FollowService.new.call(@source_account, target_account) + rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError + nil + end end end @@ -40,7 +44,9 @@ class BootstrapTimelineService < BaseService def local_unlocked_accounts(usernames) Account.local + .without_suspended .where(username: usernames) .where(locked: false) + .where(moved_to_account_id: nil) end end diff --git a/app/services/concerns/author_extractor.rb b/app/services/concerns/author_extractor.rb deleted file mode 100644 index c2419e9ecb23d6520897848a6bcd4396d87246de..0000000000000000000000000000000000000000 --- a/app/services/concerns/author_extractor.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module AuthorExtractor - def author_from_xml(xml, update_profile = true) - return nil if xml.nil? - - # Try <email> for acct - acct = xml.at_xpath('./xmlns:author/xmlns:email', xmlns: OStatus::TagManager::XMLNS)&.content - - # Try <name> + <uri> - if acct.blank? - username = xml.at_xpath('./xmlns:author/xmlns:name', xmlns: OStatus::TagManager::XMLNS)&.content - uri = xml.at_xpath('./xmlns:author/xmlns:uri', xmlns: OStatus::TagManager::XMLNS)&.content - - return nil if username.blank? || uri.blank? - - domain = Addressable::URI.parse(uri).normalized_host - acct = "#{username}@#{domain}" - end - - ResolveAccountService.new.call(acct, update_profile: update_profile) - end -end diff --git a/app/services/concerns/payloadable.rb b/app/services/concerns/payloadable.rb index 13d9c3548353e538be69e52a9abaf69b3da7283a..7f9f21c4b95f49eb82ca4cf4e5aa9b14ca0ed556 100644 --- a/app/services/concerns/payloadable.rb +++ b/app/services/concerns/payloadable.rb @@ -14,6 +14,6 @@ module Payloadable end def signing_enabled? - true + ENV['AUTHORIZED_FETCH'] != 'true' && !Rails.configuration.x.whitelist_mode end end diff --git a/app/services/concerns/stream_entry_renderer.rb b/app/services/concerns/stream_entry_renderer.rb deleted file mode 100644 index 9f6c8a082aed307f16306251276be56417f84fe6..0000000000000000000000000000000000000000 --- a/app/services/concerns/stream_entry_renderer.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module StreamEntryRenderer - def stream_entry_to_xml(stream_entry) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(stream_entry, true)) - end -end diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index f3e9c855dc55441652a6e9cdf2fa3f3773c78b72..95336ef45023586f33640e1ccc763b29f0e265bd 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -72,8 +72,8 @@ class FanOutOnWriteService < BaseService Rails.logger.debug "Delivering status #{status.id} to hashtags" status.tags.pluck(:name).each do |hashtag| - Redis.current.publish("timeline:hashtag:#{hashtag}", @payload) - Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local? + Redis.current.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload) + Redis.current.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if status.local? end end diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index 128a24ad61e59f484ebda27e432d4f7ee32d96e3..02b26458a6fa09e7934354c22443ec1768d55d1b 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -30,8 +30,6 @@ class FavouriteService < BaseService if status.account.local? NotifyService.new.call(status.account, favourite) - elsif status.account.ostatus? - NotificationWorker.perform_async(build_xml(favourite), favourite.account_id, status.account_id) elsif status.account.activitypub? ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url) end @@ -46,8 +44,4 @@ class FavouriteService < BaseService def build_json(favourite) Oj.dump(serialize_payload(favourite, ActivityPub::LikeSerializer)) end - - def build_xml(favourite) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.favourite_salmon(favourite)) - end end diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb deleted file mode 100644 index d6508a9888a69fad1b695a3cd59a4cb971120cac..0000000000000000000000000000000000000000 --- a/app/services/fetch_atom_service.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -class FetchAtomService < BaseService - include JsonLdHelper - - def call(url) - return if url.blank? - - result = process(url) - - # retry without ActivityPub - result ||= process(url) if @unsupported_activity - - result - rescue OpenSSL::SSL::SSLError => e - Rails.logger.debug "SSL error: #{e}" - nil - rescue HTTP::ConnectionError => e - Rails.logger.debug "HTTP ConnectionError: #{e}" - nil - end - - private - - def process(url, terminal = false) - @url = url - perform_request { |response| process_response(response, terminal) } - end - - def perform_request(&block) - accept = 'text/html' - accept = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/atom+xml, ' + accept unless @unsupported_activity - - Request.new(:get, @url).add_headers('Accept' => accept).perform(&block) - end - - def process_response(response, terminal = false) - return nil if response.code != 200 - - if response.mime_type == 'application/atom+xml' - [@url, { prefetched_body: response.body_with_limit }, :ostatus] - elsif ['application/activity+json', 'application/ld+json'].include?(response.mime_type) - body = response.body_with_limit - json = body_to_json(body) - if supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) && json['inbox'].present? - [json['id'], { prefetched_body: body, id: true }, :activitypub] - elsif supported_context?(json) && expected_type?(json) - [json['id'], { prefetched_body: body, id: true }, :activitypub] - else - @unsupported_activity = true - nil - end - elsif !terminal - link_header = response['Link'] && parse_link_header(response) - - if link_header&.find_link(%w(rel alternate)) - process_link_headers(link_header) - elsif response.mime_type == 'text/html' - process_html(response) - end - end - end - - def expected_type?(json) - equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) - end - - def process_html(response) - page = Nokogiri::HTML(response.body_with_limit) - - json_link = page.xpath('//link[@rel="alternate"]').find { |link| ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(link['type']) } - atom_link = page.xpath('//link[@rel="alternate"]').find { |link| link['type'] == 'application/atom+xml' } - - result ||= process(json_link['href'], terminal: true) unless json_link.nil? || @unsupported_activity - result ||= process(atom_link['href'], terminal: true) unless atom_link.nil? - - result - end - - def process_link_headers(link_header) - json_link = link_header.find_link(%w(rel alternate), %w(type application/activity+json)) || link_header.find_link(%w(rel alternate), ['type', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']) - atom_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml)) - - result ||= process(json_link.href, terminal: true) unless json_link.nil? || @unsupported_activity - result ||= process(atom_link.href, terminal: true) unless atom_link.nil? - - result - end - - def parse_link_header(response) - LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link']) - end -end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index 494aaed759c3899961a15f1a06fb3e545df37675..f0b1169db8516ebf17a21b4df2e3e764a1bab4f2 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -22,14 +22,14 @@ class FetchLinkCardService < BaseService RedisLock.acquire(lock_options) do |lock| if lock.acquired? @card = PreviewCard.find_by(url: @url) - process_url if @card.nil? || @card.updated_at <= 2.weeks.ago + process_url if @card.nil? || @card.updated_at <= 2.weeks.ago || @card.missing_image? else raise Mastodon::RaceConditionError end end attach_card if @card&.persisted? - rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e + rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e Rails.logger.debug "Error fetching link #{@url}: #{e}" nil end @@ -39,12 +39,6 @@ class FetchLinkCardService < BaseService def process_url @card ||= PreviewCard.new(url: @url) - failed = Request.new(:head, @url).perform do |res| - res.code != 405 && res.code != 501 && (res.code != 200 || res.mime_type != 'text/html') - end - - return if failed - Request.new(:get, @url).perform do |res| if res.code == 200 && res.mime_type == 'text/html' @html = res.body_with_limit @@ -84,7 +78,7 @@ class FetchLinkCardService < BaseService def mention_link?(a) @status.mentions.any? do |mention| - a['href'] == TagManager.instance.url_for(mention.account) + a['href'] == ActivityPub::TagManager.instance.url_for(mention.account) end end diff --git a/app/services/fetch_remote_account_service.rb b/app/services/fetch_remote_account_service.rb index cfc560022fe6d6f7aae8fb122c798aab0bb4dde3..3cd06e30f6ea75d9884b16e786143105221dd967 100644 --- a/app/services/fetch_remote_account_service.rb +++ b/app/services/fetch_remote_account_service.rb @@ -1,45 +1,17 @@ # frozen_string_literal: true class FetchRemoteAccountService < BaseService - include AuthorExtractor - def call(url, prefetched_body = nil, protocol = :ostatus) if prefetched_body.nil? - resource_url, resource_options, protocol = FetchAtomService.new.call(url) + resource_url, resource_options, protocol = FetchResourceService.new.call(url) else resource_url = url resource_options = { prefetched_body: prefetched_body } end case protocol - when :ostatus - process_atom(resource_url, **resource_options) when :activitypub ActivityPub::FetchRemoteAccountService.new.call(resource_url, **resource_options) end end - - private - - def process_atom(url, prefetched_body:) - xml = Nokogiri::XML(prefetched_body) - xml.encoding = 'utf-8' - - account = author_from_xml(xml.at_xpath('/xmlns:feed', xmlns: OStatus::TagManager::XMLNS), false) - - UpdateRemoteProfileService.new.call(xml, account) if account.present? && trusted_domain?(url, account) - - account - rescue TypeError - Rails.logger.debug "Unparseable URL given: #{url}" - nil - rescue Nokogiri::XML::XPath::SyntaxError - Rails.logger.debug 'Invalid XML or missing namespace' - nil - end - - def trusted_domain?(url, account) - domain = Addressable::URI.parse(url).normalized_host - domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url.presence || account.uri).normalized_host).zero? - end end diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb index 9c3008035d5431e07d1ef0f8c86fdcec0e378e96..208dc7809c1082d5f8c9b6dc438f2f6eede3952e 100644 --- a/app/services/fetch_remote_status_service.rb +++ b/app/services/fetch_remote_status_service.rb @@ -1,45 +1,17 @@ # frozen_string_literal: true class FetchRemoteStatusService < BaseService - include AuthorExtractor - def call(url, prefetched_body = nil, protocol = :ostatus) if prefetched_body.nil? - resource_url, resource_options, protocol = FetchAtomService.new.call(url) + resource_url, resource_options, protocol = FetchResourceService.new.call(url) else resource_url = url resource_options = { prefetched_body: prefetched_body } end case protocol - when :ostatus - process_atom(resource_url, **resource_options) when :activitypub ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options) end end - - private - - def process_atom(url, prefetched_body:) - Rails.logger.debug "Processing Atom for remote status at #{url}" - - xml = Nokogiri::XML(prefetched_body) - xml.encoding = 'utf-8' - - account = author_from_xml(xml.at_xpath('/xmlns:entry', xmlns: OStatus::TagManager::XMLNS)) - domain = Addressable::URI.parse(url).normalized_host - - return nil unless !account.nil? && confirmed_domain?(domain, account) - - statuses = ProcessFeedService.new.call(prefetched_body, account) - statuses.first - rescue Nokogiri::XML::XPath::SyntaxError - Rails.logger.debug 'Invalid XML or missing namespace' - nil - end - - def confirmed_domain?(domain, account) - account.domain.nil? || domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url.presence || account.uri).normalized_host).zero? - end end diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..3676d899d254bc25d692fa392169c3b00fab0307 --- /dev/null +++ b/app/services/fetch_resource_service.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +class FetchResourceService < BaseService + include JsonLdHelper + + ACCEPT_HEADER = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html' + + def call(url) + return if url.blank? + + process(url) + rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e + Rails.logger.debug "Error fetching resource #{@url}: #{e}" + nil + end + + private + + def process(url, terminal = false) + @url = url + + perform_request { |response| process_response(response, terminal) } + end + + def perform_request(&block) + Request.new(:get, @url).add_headers('Accept' => ACCEPT_HEADER).on_behalf_of(Account.representative).perform(&block) + end + + def process_response(response, terminal = false) + return nil if response.code != 200 + + if ['application/activity+json', 'application/ld+json'].include?(response.mime_type) + body = response.body_with_limit + json = body_to_json(body) + + [json['id'], { prefetched_body: body, id: true }, :activitypub] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json)) + elsif !terminal + link_header = response['Link'] && parse_link_header(response) + + if link_header&.find_link(%w(rel alternate)) + process_link_headers(link_header) + elsif response.mime_type == 'text/html' + process_html(response) + end + end + end + + def expected_type?(json) + equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) + end + + def process_html(response) + page = Nokogiri::HTML(response.body_with_limit) + json_link = page.xpath('//link[@rel="alternate"]').find { |link| ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(link['type']) } + + process(json_link['href'], terminal: true) unless json_link.nil? + end + + def process_link_headers(link_header) + json_link = link_header.find_link(%w(rel alternate), %w(type application/activity+json)) || link_header.find_link(%w(rel alternate), ['type', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']) + + process(json_link.href, terminal: true) unless json_link.nil? + end + + def parse_link_header(response) + LinkHeader.parse(response['Link'].is_a?(Array) ? response['Link'].first : response['Link']) + end +end diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 17fbb9cb4fa3dc68af2e9993291d378aa676e4de..1941c2e2d8864a352e2de04239391ea2ee5bdee2 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -13,7 +13,7 @@ class FollowService < BaseService target_account = ResolveAccountService.new.call(target_account, skip_webfinger: true) raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended? - raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || target_account.moved? || source_account.domain_blocking?(target_account.domain) + raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account) || target_account.moved? || (!target_account.local? && target_account.ostatus?) || source_account.domain_blocking?(target_account.domain) if source_account.following?(target_account) # We're already following this account, but we'll call follow! again to @@ -30,9 +30,9 @@ class FollowService < BaseService ActivityTracker.increment('activity:interactions') - if target_account.locked? || target_account.activitypub? + if target_account.locked? || source_account.silenced? || target_account.activitypub? request_follow(source_account, target_account, reblogs: reblogs) - else + elsif target_account.local? direct_follow(source_account, target_account, reblogs: reblogs) end end @@ -44,9 +44,6 @@ class FollowService < BaseService if target_account.local? LocalNotificationWorker.perform_async(target_account.id, follow_request.id, follow_request.class.name) - elsif target_account.ostatus? - NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id) - AfterRemoteFollowRequestWorker.perform_async(follow_request.id) elsif target_account.activitypub? ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), source_account.id, target_account.inbox_url) end @@ -57,27 +54,12 @@ class FollowService < BaseService def direct_follow(source_account, target_account, reblogs: true) follow = source_account.follow!(target_account, reblogs: reblogs) - if target_account.local? - LocalNotificationWorker.perform_async(target_account.id, follow.id, follow.class.name) - else - Pubsubhubbub::SubscribeWorker.perform_async(target_account.id) unless target_account.subscribed? - NotificationWorker.perform_async(build_follow_xml(follow), source_account.id, target_account.id) - AfterRemoteFollowWorker.perform_async(follow.id) - end - + LocalNotificationWorker.perform_async(target_account.id, follow.id, follow.class.name) MergeWorker.perform_async(target_account.id, source_account.id) follow end - def build_follow_request_xml(follow_request) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.follow_request_salmon(follow_request)) - end - - def build_follow_xml(follow) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.follow_salmon(follow)) - end - def build_json(follow_request) Oj.dump(serialize_payload(follow_request, ActivityPub::FollowSerializer)) end diff --git a/app/services/hashtag_query_service.rb b/app/services/hashtag_query_service.rb index 5773d78c6e2d2a38275558dce58b59878d59b001..196de0639205b20596ea5968210234ab390c5b08 100644 --- a/app/services/hashtag_query_service.rb +++ b/app/services/hashtag_query_service.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class HashtagQueryService < BaseService + LIMIT_PER_MODE = 4 + def call(tag, params, account = nil, local = false) tags = tags_for(Array(tag.name) | Array(params[:any])).pluck(:id) all = tags_for(params[:all]) @@ -14,7 +16,7 @@ class HashtagQueryService < BaseService private - def tags_for(tags) - Tag.where(name: tags.map(&:downcase)) if tags.presence + def tags_for(names) + Tag.matching_name(Array(names).take(LIMIT_PER_MODE)) if names.present? end end diff --git a/app/services/move_service.rb b/app/services/move_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..da0c62c4eb24eb61595bd9a3fe5b6be8efee72d6 --- /dev/null +++ b/app/services/move_service.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class MoveService < BaseService + def call(migration) + @migration = migration + @source_account = migration.account + @target_account = migration.target_account + + update_redirect! + process_local_relationships! + distribute_update! + distribute_move! + end + + private + + def update_redirect! + @source_account.update!(moved_to_account: @target_account) + end + + def process_local_relationships! + MoveWorker.perform_async(@source_account.id, @target_account.id) + end + + def distribute_update! + ActivityPub::UpdateDistributionWorker.perform_async(@source_account.id) + end + + def distribute_move! + ActivityPub::MoveDistributionWorker.perform_async(@migration.id) + end +end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 36b9c9e84b2b3e2e4b4aa9153b00a9ec363f1bb8..b59982d84531c2369e6e94ec79933c0abb824176 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -95,7 +95,6 @@ class PostStatusService < BaseService LinkCrawlWorker.perform_async(@status.id) unless @status.spoiler_text? DistributionWorker.perform_async(@status.id) unless @status.local_only? - Pubsubhubbub::DistributionWorker.perform_async(@status.stream_entry.id) ActivityPub::DistributionWorker.perform_async(@status.id) PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll end @@ -184,7 +183,7 @@ class PostStatusService < BaseService def poll_attributes return if @options[:poll].blank? - @options[:poll].merge(account: @account) + @options[:poll].merge(account: @account, voters_count: 0) end def scheduled_options diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb deleted file mode 100644 index 30a9dd85ebcb176abc9b7bd2df31603f1546556a..0000000000000000000000000000000000000000 --- a/app/services/process_feed_service.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class ProcessFeedService < BaseService - def call(body, account, **options) - @options = options - - xml = Nokogiri::XML(body) - xml.encoding = 'utf-8' - - update_author(body, account) - process_entries(xml, account) - end - - private - - def update_author(body, account) - RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), true) - end - - def process_entries(xml, account) - xml.xpath('//xmlns:entry', xmlns: OStatus::TagManager::XMLNS).reverse_each.map { |entry| process_entry(entry, account) }.compact - end - - def process_entry(xml, account) - activity = OStatus::Activity::General.new(xml, account, @options) - activity.specialize&.perform if activity.status? - rescue ActiveRecord::RecordInvalid => e - Rails.logger.debug "Nothing was saved for #{activity.id} because: #{e}" - nil - end -end diff --git a/app/services/process_hashtags_service.rb b/app/services/process_hashtags_service.rb index d5ec076a8b24f4cdb34885fc82961cc11fdb5a29..e8e139b058b77a3ea25e7a7b29bc0896a888c841 100644 --- a/app/services/process_hashtags_service.rb +++ b/app/services/process_hashtags_service.rb @@ -5,16 +5,14 @@ class ProcessHashtagsService < BaseService tags = Extractor.extract_hashtags(status.text) if status.local? records = [] - tags.map { |str| str.mb_chars.downcase }.uniq(&:to_s).each do |name| - tag = Tag.where(name: name).first_or_create(name: name) - + Tag.find_or_create_by_names(tags) do |tag| status.tags << tag records << tag TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility? end - return unless status.public_visibility? || status.unlisted_visibility? + return unless status.distributable? status.account.featured_tags.where(tag_id: records.map(&:id)).each do |featured_tag| featured_tag.increment(status.created_at) diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb deleted file mode 100644 index 1fca3832b74fc65251111d3d5b716aabf226204e..0000000000000000000000000000000000000000 --- a/app/services/process_interaction_service.rb +++ /dev/null @@ -1,151 +0,0 @@ -# frozen_string_literal: true - -class ProcessInteractionService < BaseService - include AuthorExtractor - include Authorization - - # Record locally the remote interaction with our user - # @param [String] envelope Salmon envelope - # @param [Account] target_account Account the Salmon was addressed to - def call(envelope, target_account) - body = salmon.unpack(envelope) - - xml = Nokogiri::XML(body) - xml.encoding = 'utf-8' - - account = author_from_xml(xml.at_xpath('/xmlns:entry', xmlns: OStatus::TagManager::XMLNS)) - - return if account.nil? || account.suspended? - - if salmon.verify(envelope, account.keypair) - RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), true) - - case verb(xml) - when :follow - follow!(account, target_account) unless target_account.locked? || target_account.blocking?(account) || target_account.domain_blocking?(account.domain) - when :request_friend - follow_request!(account, target_account) unless !target_account.locked? || target_account.blocking?(account) || target_account.domain_blocking?(account.domain) - when :authorize - authorize_follow_request!(account, target_account) - when :reject - reject_follow_request!(account, target_account) - when :unfollow - unfollow!(account, target_account) - when :favorite - favourite!(xml, account) - when :unfavorite - unfavourite!(xml, account) - when :post - add_post!(body, account) if mentions_account?(xml, target_account) - when :share - add_post!(body, account) unless status(xml).nil? - when :delete - delete_post!(xml, account) - when :block - reflect_block!(account, target_account) - when :unblock - reflect_unblock!(account, target_account) - end - end - rescue HTTP::Error, OStatus2::BadSalmonError, Mastodon::NotPermittedError - nil - end - - private - - def mentions_account?(xml, account) - xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]', xmlns: OStatus::TagManager::XMLNS).each { |mention_link| return true if [OStatus::TagManager.instance.uri_for(account), OStatus::TagManager.instance.url_for(account)].include?(mention_link.attribute('href').value) } - false - end - - def verb(xml) - raw = xml.at_xpath('//activity:verb', activity: OStatus::TagManager::AS_XMLNS).content - OStatus::TagManager::VERBS.key(raw) - rescue - :post - end - - def follow!(account, target_account) - follow = account.follow!(target_account) - FollowRequest.find_by(account: account, target_account: target_account)&.destroy - NotifyService.new.call(target_account, follow) - end - - def follow_request!(account, target_account) - return if account.requested?(target_account) - - follow_request = FollowRequest.create!(account: account, target_account: target_account) - NotifyService.new.call(target_account, follow_request) - end - - def authorize_follow_request!(account, target_account) - follow_request = FollowRequest.find_by(account: target_account, target_account: account) - follow_request&.authorize! - Pubsubhubbub::SubscribeWorker.perform_async(account.id) unless account.subscribed? - end - - def reject_follow_request!(account, target_account) - follow_request = FollowRequest.find_by(account: target_account, target_account: account) - follow_request&.reject! - end - - def unfollow!(account, target_account) - account.unfollow!(target_account) - FollowRequest.find_by(account: account, target_account: target_account)&.destroy - end - - def reflect_block!(account, target_account) - UnfollowService.new.call(target_account, account) if target_account.following?(account) - account.block!(target_account) - end - - def reflect_unblock!(account, target_account) - UnblockService.new.call(account, target_account) - end - - def delete_post!(xml, account) - status = Status.find(xml.at_xpath('//xmlns:id', xmlns: OStatus::TagManager::XMLNS).content) - - return if status.nil? - - authorize_with account, status, :destroy? - - RemovalWorker.perform_async(status.id) - end - - def favourite!(xml, from_account) - current_status = status(xml) - - return if current_status.nil? - - favourite = current_status.favourites.where(account: from_account).first_or_create!(account: from_account) - NotifyService.new.call(current_status.account, favourite) - end - - def unfavourite!(xml, from_account) - current_status = status(xml) - - return if current_status.nil? - - favourite = current_status.favourites.where(account: from_account).first - favourite&.destroy - end - - def add_post!(body, account) - ProcessingWorker.perform_async(account.id, body.force_encoding('UTF-8')) - end - - def status(xml) - uri = activity_id(xml) - return nil unless OStatus::TagManager.instance.local_id?(uri) - Status.find(OStatus::TagManager.instance.unique_tag_to_local_id(uri, 'Status')) - end - - def activity_id(xml) - xml.at_xpath('//activity:object', activity: OStatus::TagManager::AS_XMLNS).at_xpath('./xmlns:id', xmlns: OStatus::TagManager::XMLNS).content - end - - def salmon - @salmon ||= OStatus2::Salmon.new - end -end diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index 1804e0c93ff53fae4bd8812948f1a405b2fa0a83..19de377174a724897466c95a1d7720c95d6d1900 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class ProcessMentionsService < BaseService - include StreamEntryRenderer include Payloadable # Scan status for mentions and fetch remote mentioned users, create @@ -34,6 +33,7 @@ class ProcessMentionsService < BaseService end status.save! + check_for_spam(status) mentions.each { |mention| create_notification(mention) } end @@ -41,7 +41,7 @@ class ProcessMentionsService < BaseService private def mention_undeliverable?(mentioned_account) - mentioned_account.nil? || (!mentioned_account.local? && mentioned_account.ostatus? && @status.stream_entry.hidden?) + mentioned_account.nil? || (!mentioned_account.local? && mentioned_account.ostatus?) end def create_notification(mention) @@ -49,17 +49,11 @@ class ProcessMentionsService < BaseService if mentioned_account.local? LocalNotificationWorker.perform_async(mentioned_account.id, mention.id, mention.class.name) - elsif mentioned_account.ostatus? && !@status.stream_entry.hidden? && !@status.local_only? - NotificationWorker.perform_async(ostatus_xml, @status.account_id, mentioned_account.id) elsif mentioned_account.activitypub? && !@status.local_only? ActivityPub::DeliveryWorker.perform_async(activitypub_json, mention.status.account_id, mentioned_account.inbox_url) end end - def ostatus_xml - @ostatus_xml ||= stream_entry_to_xml(@status.stream_entry) - end - def activitypub_json return @activitypub_json if defined?(@activitypub_json) @activitypub_json = Oj.dump(serialize_payload(@status, ActivityPub::ActivitySerializer, signer: @status.account)) @@ -68,4 +62,8 @@ class ProcessMentionsService < BaseService def resolve_account_service ResolveAccountService.new end + + def check_for_spam(status) + SpamCheck.perform(status) + end end diff --git a/app/services/pubsubhubbub/subscribe_service.rb b/app/services/pubsubhubbub/subscribe_service.rb deleted file mode 100644 index 550da632812a5163d091ed35ca5b95d10c7cac3b..0000000000000000000000000000000000000000 --- a/app/services/pubsubhubbub/subscribe_service.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -class Pubsubhubbub::SubscribeService < BaseService - URL_PATTERN = /\A#{URI.regexp(%w(http https))}\z/ - - attr_reader :account, :callback, :secret, - :lease_seconds, :domain - - def call(account, callback, secret, lease_seconds, verified_domain = nil) - @account = account - @callback = Addressable::URI.parse(callback).normalize.to_s - @secret = secret - @lease_seconds = lease_seconds - @domain = verified_domain - - process_subscribe - end - - private - - def process_subscribe - if account.nil? - ['Invalid topic URL', 422] - elsif !valid_callback? - ['Invalid callback URL', 422] - elsif blocked_domain? - ['Callback URL not allowed', 403] - else - confirm_subscription - ['', 202] - end - end - - def confirm_subscription - subscription = locate_subscription - Pubsubhubbub::ConfirmationWorker.perform_async(subscription.id, 'subscribe', secret, lease_seconds) - end - - def valid_callback? - callback.present? && callback =~ URL_PATTERN - end - - def blocked_domain? - DomainBlock.blocked? Addressable::URI.parse(callback).host - end - - def locate_subscription - subscription = Subscription.find_or_initialize_by(account: account, callback_url: callback) - subscription.domain = domain - subscription.save! - subscription - end -end diff --git a/app/services/pubsubhubbub/unsubscribe_service.rb b/app/services/pubsubhubbub/unsubscribe_service.rb deleted file mode 100644 index 646150f7bb450a1fed75ffc8a16cef23685da841..0000000000000000000000000000000000000000 --- a/app/services/pubsubhubbub/unsubscribe_service.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class Pubsubhubbub::UnsubscribeService < BaseService - attr_reader :account, :callback - - def call(account, callback) - @account = account - @callback = Addressable::URI.parse(callback).normalize.to_s - - process_unsubscribe - end - - private - - def process_unsubscribe - if account.nil? - ['Invalid topic URL', 422] - else - confirm_unsubscribe unless subscription.nil? - ['', 202] - end - end - - def confirm_unsubscribe - Pubsubhubbub::ConfirmationWorker.perform_async(subscription.id, 'unsubscribe') - end - - def subscription - @_subscription ||= Subscription.find_by(account: account, callback_url: callback) - end -end diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb index 09403bae0f45e0cb2c94d97e29602cc0d9ab4657..9c409da801e1f6257756c3f04ad0b71e489d6cff 100644 --- a/app/services/reblog_service.rb +++ b/app/services/reblog_service.rb @@ -2,7 +2,6 @@ class ReblogService < BaseService include Authorization - include StreamEntryRenderer include Payloadable # Reblog a status and notify its remote author @@ -24,9 +23,7 @@ class ReblogService < BaseService reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility) DistributionWorker.perform_async(reblog.id) - unless reblogged_status.local_only? - Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id) ActivityPub::DistributionWorker.perform_async(reblog.id) end @@ -43,8 +40,6 @@ class ReblogService < BaseService if reblogged_status.account.local? LocalNotificationWorker.perform_async(reblogged_status.account_id, reblog.id, reblog.class.name) - elsif reblogged_status.account.ostatus? - NotificationWorker.perform_async(stream_entry_to_xml(reblog.stream_entry), reblog.account_id, reblogged_status.account_id) elsif reblogged_status.account.activitypub? && !reblogged_status.account.following?(reblog.account) ActivityPub::DeliveryWorker.perform_async(build_json(reblog), reblog.account_id, reblogged_status.account.inbox_url) end diff --git a/app/services/reject_follow_service.rb b/app/services/reject_follow_service.rb index f87d0ba914f6382fc5904f885b21da6384c26543..bc0000c8c886b08698d0aae7b9892cc3e58909c7 100644 --- a/app/services/reject_follow_service.rb +++ b/app/services/reject_follow_service.rb @@ -6,25 +6,17 @@ class RejectFollowService < BaseService def call(source_account, target_account) follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account) follow_request.reject! - create_notification(follow_request) unless source_account.local? + create_notification(follow_request) if !source_account.local? && source_account.activitypub? follow_request end private def create_notification(follow_request) - if follow_request.account.ostatus? - NotificationWorker.perform_async(build_xml(follow_request), follow_request.target_account_id, follow_request.account_id) - elsif follow_request.account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), follow_request.target_account_id, follow_request.account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(follow_request), follow_request.target_account_id, follow_request.account.inbox_url) end def build_json(follow_request) Oj.dump(serialize_payload(follow_request, ActivityPub::RejectFollowSerializer)) end - - def build_xml(follow_request) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)) - end end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 81adc5aae54dcb0fe8c4c7f1849cd0e493db3dc2..f9352ed3d319f8ed030af066baf2e065bf5eb182 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -1,19 +1,23 @@ # frozen_string_literal: true class RemoveStatusService < BaseService - include StreamEntryRenderer include Redisable include Payloadable + # Delete a status + # @param [Status] status + # @param [Hash] options + # @option [Boolean] :redraft + # @option [Boolean] :immediate + # @option [Boolean] :original_removed def call(status, **options) - @payload = Oj.dump(event: :delete, payload: status.id.to_s) - @status = status - @account = status.account - @tags = status.tags.pluck(:name).to_a - @mentions = status.active_mentions.includes(:account).to_a - @reblogs = status.reblogs.includes(:account).to_a - @stream_entry = status.stream_entry - @options = options + @payload = Oj.dump(event: :delete, payload: status.id.to_s) + @status = status + @account = status.account + @tags = status.tags.pluck(:name).to_a + @mentions = status.active_mentions.includes(:account).to_a + @reblogs = status.reblogs.includes(:account).to_a + @options = options RedisLock.acquire(lock_options) do |lock| if lock.acquired? @@ -25,8 +29,10 @@ class RemoveStatusService < BaseService remove_from_hashtags remove_from_public remove_from_media if status.media_attachments.any? + remove_from_spam_check + remove_media - @status.destroy! + @status.destroy! if @options[:immediate] || !@status.reported? else raise Mastodon::RaceConditionError end @@ -78,11 +84,6 @@ class RemoveStatusService < BaseService target_accounts << @status.reblog.account if @status.reblog? && !@status.reblog.account.local? target_accounts.uniq!(&:id) - # Ostatus - NotificationWorker.push_bulk(target_accounts.select(&:ostatus?).uniq(&:domain)) do |target_account| - [salmon_xml, @account.id, target_account.id] - end - # ActivityPub ActivityPub::DeliveryWorker.push_bulk(target_accounts.select(&:activitypub?).uniq(&:preferred_inbox_url)) do |target_account| [signed_activity_json, @account.id, target_account.preferred_inbox_url] @@ -90,9 +91,6 @@ class RemoveStatusService < BaseService end def remove_from_remote_followers - # OStatus - Pubsubhubbub::RawDistributionWorker.perform_async(salmon_xml, @account.id) - # ActivityPub ActivityPub::DeliveryWorker.push_bulk(@account.followers.inboxes) do |inbox_url| [signed_activity_json, @account.id, inbox_url] @@ -111,10 +109,6 @@ class RemoveStatusService < BaseService end end - def salmon_xml - @salmon_xml ||= stream_entry_to_xml(@stream_entry) - end - def signed_activity_json @signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account)) end @@ -137,8 +131,8 @@ class RemoveStatusService < BaseService return unless @status.public_visibility? @tags.each do |hashtag| - redis.publish("timeline:hashtag:#{hashtag}", @payload) - redis.publish("timeline:hashtag:#{hashtag}:local", @payload) if @status.local? + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}", @payload) + redis.publish("timeline:hashtag:#{hashtag.mb_chars.downcase}:local", @payload) if @status.local? end end @@ -156,6 +150,16 @@ class RemoveStatusService < BaseService redis.publish('timeline:public:local:media', @payload) if @status.local? end + def remove_media + return if @options[:redraft] || (!@options[:immediate] && @status.reported?) + + @status.media_attachments.destroy_all + end + + def remove_from_spam_check + redis.zremrangebyscore("spam_check:#{@status.account_id}", @status.id, @status.id) + end + def lock_options { redis: Redis.current, key: "distribute:#{@status.id}" } end diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index e557706da5c12a6d06798f8a77163b6188c0f2cd..12e6544a0f6d3ab4ed035bc206cfb27d4db9e4f5 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -1,89 +1,113 @@ # frozen_string_literal: true class ResolveAccountService < BaseService - include OStatus2::MagicKey include JsonLdHelper + include DomainControlHelper - DFRN_NS = 'http://purl.org/macgirvin/dfrn/1.0' + class WebfingerRedirectError < StandardError; end - # Find or create a local account for a remote user. - # When creating, look up the user's webfinger and fetch all - # important information from their feed - # @param [String, Account] uri User URI in the form of username@domain + # Find or create an account record for a remote user. When creating, + # look up the user's webfinger and fetch ActivityPub data + # @param [String, Account] uri URI in the username@domain format or account record # @param [Hash] options + # @option options [Boolean] :redirected Do not follow further Webfinger redirects + # @option options [Boolean] :skip_webfinger Do not attempt to refresh account data # @return [Account] def call(uri, options = {}) + return if uri.blank? + + process_options!(uri, options) + + # First of all we want to check if we've got the account + # record with the URI already, and if so, we can exit early + + return if domain_not_allowed?(@domain) + + @account ||= Account.find_remote(@username, @domain) + + return @account if @account&.local? || !webfinger_update_due? + + # At this point we are in need of a Webfinger query, which may + # yield us a different username/domain through a redirect + + process_webfinger!(@uri) + + # Because the username/domain pair may be different than what + # we already checked, we need to check if we've already got + # the record with that URI, again + + return if domain_not_allowed?(@domain) + + @account ||= Account.find_remote(@username, @domain) + + return @account if @account&.local? || !webfinger_update_due? + + # Now it is certain, it is definitely a remote account, and it + # either needs to be created, or updated from fresh data + + process_account! + rescue Goldfinger::Error, WebfingerRedirectError, Oj::ParseError => e + Rails.logger.debug "Webfinger query for #{@uri} failed: #{e}" + nil + end + + private + + def process_options!(uri, options) @options = options if uri.is_a?(Account) @account = uri @username = @account.username @domain = @account.domain - uri = "#{@username}@#{@domain}" - - return @account if @account.local? || !webfinger_update_due? else @username, @domain = uri.split('@') - - return Account.find_local(@username) if TagManager.instance.local_domain?(@domain) - - @account = Account.find_remote(@username, @domain) - - return @account unless webfinger_update_due? end - Rails.logger.debug "Looking up webfinger for #{uri}" + @domain = begin + if TagManager.instance.local_domain?(@domain) + nil + else + TagManager.instance.normalize_domain(@domain) + end + end - @webfinger = Goldfinger.finger("acct:#{uri}") + @uri = [@username, @domain].compact.join('@') + end + def process_webfinger!(uri, redirected = false) + @webfinger = Goldfinger.finger("acct:#{uri}") confirmed_username, confirmed_domain = @webfinger.subject.gsub(/\Aacct:/, '').split('@') if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero? @username = confirmed_username @domain = confirmed_domain - elsif options[:redirected].nil? - return call("#{confirmed_username}@#{confirmed_domain}", options.merge(redirected: true)) + @uri = uri + elsif !redirected + return process_webfinger!("#{confirmed_username}@#{confirmed_domain}", true) else - Rails.logger.debug 'Requested and returned acct URIs do not match' - return + raise WebfingerRedirectError, "The URI #{uri} tries to hijack #{@username}@#{@domain}" end - return if links_missing? || auto_suspend? - return Account.find_local(@username) if TagManager.instance.local_domain?(@domain) + @domain = nil if TagManager.instance.local_domain?(@domain) + end + + def process_account! + return unless activitypub_ready? RedisLock.acquire(lock_options) do |lock| if lock.acquired? @account = Account.find_remote(@username, @domain) - if activitypub_ready? || @account&.activitypub? - handle_activitypub - else - handle_ostatus - end + next if (@account.present? && !@account.activitypub?) || actor_json.nil? + + @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json) else raise Mastodon::RaceConditionError end end @account - rescue Goldfinger::Error => e - Rails.logger.debug "Webfinger query for #{uri} unsuccessful: #{e}" - nil - end - - private - - def links_missing? - !(activitypub_ready? || ostatus_ready?) - end - - def ostatus_ready? - !(@webfinger.link('http://schemas.google.com/g/2010#updates-from').nil? || - @webfinger.link('salmon').nil? || - @webfinger.link('http://webfinger.net/rel/profile-page').nil? || - @webfinger.link('magic-public-key').nil? || - canonical_uri.nil? || - hub_url.nil?) end def webfinger_update_due? @@ -91,113 +115,13 @@ class ResolveAccountService < BaseService end def activitypub_ready? - !@webfinger.link('self').nil? && - ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self').type) && - !actor_json.nil? && - actor_json['inbox'].present? - end - - def handle_ostatus - create_account if @account.nil? - update_account - update_account_profile if update_profile? - end - - def update_profile? - @options[:update_profile] - end - - def handle_activitypub - return if actor_json.nil? - - @account = ActivityPub::ProcessAccountService.new.call(@username, @domain, actor_json) - rescue Oj::ParseError - nil - end - - def create_account - Rails.logger.debug "Creating new remote account for #{@username}@#{@domain}" - - @account = Account.new(username: @username, domain: @domain) - @account.suspended_at = domain_block.created_at if auto_suspend? - @account.silenced_at = domain_block.created_at if auto_silence? - @account.private_key = nil - end - - def update_account - @account.last_webfingered_at = Time.now.utc - @account.protocol = :ostatus - @account.remote_url = atom_url - @account.salmon_url = salmon_url - @account.url = url - @account.public_key = public_key - @account.uri = canonical_uri - @account.hub_url = hub_url - @account.save! - end - - def auto_suspend? - domain_block&.suspend? - end - - def auto_silence? - domain_block&.silence? - end - - def domain_block - return @domain_block if defined?(@domain_block) - @domain_block = DomainBlock.rule_for(@domain) - end - - def atom_url - @atom_url ||= @webfinger.link('http://schemas.google.com/g/2010#updates-from').href - end - - def salmon_url - @salmon_url ||= @webfinger.link('salmon').href + !@webfinger.link('self').nil? && ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self').type) end def actor_url @actor_url ||= @webfinger.link('self').href end - def url - @url ||= @webfinger.link('http://webfinger.net/rel/profile-page').href - end - - def public_key - @public_key ||= magic_key_to_pem(@webfinger.link('magic-public-key').href) - end - - def canonical_uri - return @canonical_uri if defined?(@canonical_uri) - - author_uri = atom.at_xpath('/xmlns:feed/xmlns:author/xmlns:uri') - - if author_uri.nil? - owner = atom.at_xpath('/xmlns:feed').at_xpath('./dfrn:owner', dfrn: DFRN_NS) - author_uri = owner.at_xpath('./xmlns:uri') unless owner.nil? - end - - @canonical_uri = author_uri.nil? ? nil : author_uri.content - end - - def hub_url - return @hub_url if defined?(@hub_url) - - hubs = atom.xpath('//xmlns:link[@rel="hub"]') - @hub_url = hubs.empty? || hubs.first['href'].nil? ? nil : hubs.first['href'] - end - - def atom_body - return @atom_body if defined?(@atom_body) - - @atom_body = Request.new(:get, atom_url).perform do |response| - raise Mastodon::UnexpectedResponseError, response unless response.code == 200 - response.body_with_limit - end - end - def actor_json return @actor_json if defined?(@actor_json) @@ -205,15 +129,6 @@ class ResolveAccountService < BaseService @actor_json = supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) ? json : nil end - def atom - return @atom if defined?(@atom) - @atom = Nokogiri::XML(atom_body) - end - - def update_account_profile - RemoteProfileUpdateWorker.perform_async(@account.id, atom_body.force_encoding('UTF-8'), false) - end - def lock_options { redis: Redis.current, key: "resolve:#{@username}@#{@domain}" } end diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index b98759bf68b698d5bd5fce4d5f74e380ab7a8725..aa883597a467e3d133cc2627cff34aaac785ee0a 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -4,64 +4,51 @@ class ResolveURLService < BaseService include JsonLdHelper include Authorization - attr_reader :url - def call(url, on_behalf_of: nil) - @url = url + @url = url @on_behalf_of = on_behalf_of - return process_local_url if local_url? - - process_url unless fetched_atom_feed.nil? + if local_url? + process_local_url + elsif !fetched_resource.nil? + process_url + end end private def process_url - if equals_or_includes_any?(type, %w(Application Group Organization Person Service)) - FetchRemoteAccountService.new.call(atom_url, body, protocol) - elsif equals_or_includes_any?(type, %w(Note Article Image Video Page Question)) - FetchRemoteStatusService.new.call(atom_url, body, protocol) + if equals_or_includes_any?(type, ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) + FetchRemoteAccountService.new.call(resource_url, body, protocol) + elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) + status = FetchRemoteStatusService.new.call(resource_url, body, protocol) + authorize_with @on_behalf_of, status, :show? unless status.nil? + status end end - def fetched_atom_feed - @_fetched_atom_feed ||= FetchAtomService.new.call(url) + def fetched_resource + @fetched_resource ||= FetchResourceService.new.call(@url) end - def atom_url - fetched_atom_feed.first + def resource_url + fetched_resource.first end def body - fetched_atom_feed.second[:prefetched_body] + fetched_resource.second[:prefetched_body] end def protocol - fetched_atom_feed.third + fetched_resource.third end def type return json_data['type'] if protocol == :activitypub - - case xml_root - when 'feed' - 'Person' - when 'entry' - 'Note' - end end def json_data - @_json_data ||= body_to_json(body) - end - - def xml_root - xml_data.root.name - end - - def xml_data - @_xml_data ||= Nokogiri::XML(body, nil, 'utf-8') + @json_data ||= body_to_json(body) end def local_url? @@ -73,10 +60,7 @@ class ResolveURLService < BaseService return unless recognized_params[:action] == 'show' - if recognized_params[:controller] == 'stream_entries' - status = StreamEntry.find_by(id: recognized_params[:id])&.status - check_local_status(status) - elsif recognized_params[:controller] == 'statuses' + if recognized_params[:controller] == 'statuses' status = Status.find_by(id: recognized_params[:id]) check_local_status(status) elsif recognized_params[:controller] == 'accounts' @@ -86,10 +70,10 @@ class ResolveURLService < BaseService def check_local_status(status) return if status.nil? + authorize_with @on_behalf_of, status, :show? status rescue Mastodon::NotPermittedError - # Do not disclose the existence of status the user is not authorized to see nil end end diff --git a/app/services/search_service.rb b/app/services/search_service.rb index e0da61dac5b81545ffce7e158d1c8c7b408b8f39..3a498dcf4757afb1bbf296ec0a49c8623fbfba96 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -11,7 +11,7 @@ class SearchService < BaseService default_results.tap do |results| if url_query? - results.merge!(url_resource_results) unless url_resource.nil? + results.merge!(url_resource_results) unless url_resource.nil? || (@options[:type].present? && url_resource_symbol != @options[:type].to_sym) elsif @query.present? results[:accounts] = perform_accounts_search! if account_searchable? results[:statuses] = perform_statuses_search! if full_text_searchable? @@ -33,8 +33,7 @@ class SearchService < BaseService end def perform_statuses_search! - definition = StatusesIndex.filter(term: { searchable_by: @account.id }) - .query(multi_match: { type: 'most_fields', query: @query, operator: 'and', fields: %w(text text.stemmed) }) + definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id })) if @options[:account_id].present? definition = definition.filter(term: { account_id: @options[:account_id] }) @@ -53,15 +52,16 @@ class SearchService < BaseService preloaded_relations = relations_map_for_account(@account, account_ids, account_domains) results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } - rescue Faraday::ConnectionFailed + rescue Faraday::ConnectionFailed, Parslet::ParseFailed [] end def perform_hashtags_search! - Tag.search_for( - @query.gsub(/\A#/, ''), - @limit, - @offset + TagSearchService.new.call( + @query, + limit: @limit, + offset: @offset, + exclude_unreviewed: @options[:exclude_unreviewed] ) end @@ -70,7 +70,7 @@ class SearchService < BaseService end def url_query? - @options[:type].blank? && @query =~ /\Ahttps?:\/\// + @resolve && @query =~ /\Ahttps?:\/\// end def url_resource_results @@ -120,4 +120,8 @@ class SearchService < BaseService domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id), } end + + def parsed_query + SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query)) + end end diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb deleted file mode 100644 index 3419043e5668fd5edf67958558f08ba59f7b2f54..0000000000000000000000000000000000000000 --- a/app/services/send_interaction_service.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -class SendInteractionService < BaseService - # Send an Atom representation of an interaction to a remote Salmon endpoint - # @param [String] Entry XML - # @param [Account] source_account - # @param [Account] target_account - def call(xml, source_account, target_account) - @xml = xml - @source_account = source_account - @target_account = target_account - - return if !target_account.ostatus? || block_notification? - - build_request.perform do |delivery| - raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300 - end - end - - private - - def build_request - request = Request.new(:post, @target_account.salmon_url, body: envelope) - request.add_headers('Content-Type' => 'application/magic-envelope+xml') - request - end - - def envelope - salmon.pack(@xml, @source_account.keypair) - end - - def block_notification? - DomainBlock.blocked?(@target_account.domain) - end - - def salmon - @salmon ||= OStatus2::Salmon.new - end -end diff --git a/app/services/subscribe_service.rb b/app/services/subscribe_service.rb deleted file mode 100644 index 83fd64396a1057d981ed67ca6f7c2e506ca78aac..0000000000000000000000000000000000000000 --- a/app/services/subscribe_service.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -class SubscribeService < BaseService - def call(account) - return if account.hub_url.blank? - - @account = account - @account.secret = SecureRandom.hex - - build_request.perform do |response| - if response_failed_permanently? response - # We're not allowed to subscribe. Fail and move on. - @account.secret = '' - @account.save! - elsif response_successful? response - # The subscription will be confirmed asynchronously. - @account.save! - else - # The response was either a 429 rate limit, or a 5xx error. - # We need to retry at a later time. Fail loudly! - raise Mastodon::UnexpectedResponseError, response - end - end - end - - private - - def build_request - request = Request.new(:post, @account.hub_url, form: subscription_params) - request.on_behalf_of(some_local_account) if some_local_account - request - end - - def subscription_params - { - 'hub.topic': @account.remote_url, - 'hub.mode': 'subscribe', - 'hub.callback': api_subscription_url(@account.id), - 'hub.verify': 'async', - 'hub.secret': @account.secret, - 'hub.lease_seconds': 7.days.seconds, - } - end - - def some_local_account - @some_local_account ||= Account.local.without_suspended.first - end - - # Any response in the 3xx or 4xx range, except for 429 (rate limit) - def response_failed_permanently?(response) - (response.status.redirect? || response.status.client_error?) && !response.status.too_many_requests? - end - - # Any response in the 2xx range - def response_successful?(response) - response.status.success? - end -end diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 5dd01da52582b3440aa675b3955b1c78b2d59a73..ecc893931d596d9fd00a8a0911ce910be7bf9f95 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -15,7 +15,6 @@ class SuspendAccountService < BaseService favourites follow_requests list_accounts - media_attachments mute_relationships muted_by_relationships notifications @@ -24,8 +23,6 @@ class SuspendAccountService < BaseService report_notes scheduled_statuses status_pins - stream_entries - subscriptions ).freeze ASSOCIATIONS_ON_DESTROY = %w( @@ -34,14 +31,26 @@ class SuspendAccountService < BaseService targeted_reports ).freeze - # Suspend an account and remove as much of its data as possible + # Suspend or remove an account and remove as much of its data + # as possible. If it's a local account and it has not been confirmed + # or never been approved, then side effects are skipped and both + # the user and account records are removed fully. Otherwise, + # it is controlled by options. # @param [Account] # @param [Hash] options - # @option [Boolean] :including_user Remove the user record as well - # @option [Boolean] :destroy Remove the account record instead of suspending + # @option [Boolean] :reserve_email Keep user record. Only applicable for local accounts + # @option [Boolean] :reserve_username Keep account record + # @option [Boolean] :skip_side_effects Side effects are ActivityPub and streaming API payloads + # @option [Time] :suspended_at Only applicable when :reserve_username is true def call(account, **options) @account = account - @options = options + @options = { reserve_username: true, reserve_email: true }.merge(options) + + if @account.local? && @account.user_unconfirmed_or_pending? + @options[:reserve_email] = false + @options[:reserve_username] = false + @options[:skip_side_effects] = true + end reject_follows! purge_user! @@ -62,26 +71,39 @@ class SuspendAccountService < BaseService def purge_user! return if !@account.local? || @account.user.nil? - if @options[:including_user] - @account.user.destroy - else + if @options[:reserve_email] @account.user.disable! @account.user.invites.where(uses: 0).destroy_all + else + @account.user.destroy end end def purge_content! - distribute_delete_actor! if @account.local? && !@options[:skip_distribution] + distribute_delete_actor! if @account.local? && !@options[:skip_side_effects] @account.statuses.reorder(nil).find_in_batches do |statuses| - BatchedRemoveStatusService.new.call(statuses, skip_side_effects: @options[:destroy]) + statuses.reject! { |status| reported_status_ids.include?(status.id) } if @options[:reserve_username] + BatchedRemoveStatusService.new.call(statuses, skip_side_effects: @options[:skip_side_effects]) + end + + @account.media_attachments.reorder(nil).find_each do |media_attachment| + next if @options[:reserve_username] && reported_status_ids.include?(media_attachment.status_id) + + media_attachment.destroy + end + + @account.polls.reorder(nil).find_each do |poll| + next if @options[:reserve_username] && reported_status_ids.include?(poll.status_id) + + poll.destroy end associations_for_destruction.each do |association_name| destroy_all(@account.public_send(association_name)) end - @account.destroy if @options[:destroy] + @account.destroy unless @options[:reserve_username] end def purge_profile! @@ -89,11 +111,13 @@ class SuspendAccountService < BaseService # there is no point wasting time updating # its values first - return if @options[:destroy] + return unless @options[:reserve_username] @account.silenced_at = nil @account.suspended_at = @options[:suspended_at] || Time.now.utc @account.locked = false + @account.memorial = false + @account.discoverable = false @account.display_name = '' @account.note = '' @account.fields = [] @@ -101,6 +125,7 @@ class SuspendAccountService < BaseService @account.followers_count = 0 @account.following_count = 0 @account.moved_to_account = nil + @account.trust_level = :untrusted @account.avatar.destroy @account.header.destroy @account.save! @@ -136,11 +161,15 @@ class SuspendAccountService < BaseService Account.inboxes - delivery_inboxes end + def reported_status_ids + @reported_status_ids ||= Report.where(target_account: @account).unresolved.pluck(:status_ids).flatten.uniq + end + def associations_for_destruction - if @options[:destroy] - ASSOCIATIONS_ON_SUSPEND + ASSOCIATIONS_ON_DESTROY - else + if @options[:reserve_username] ASSOCIATIONS_ON_SUSPEND + else + ASSOCIATIONS_ON_SUSPEND + ASSOCIATIONS_ON_DESTROY end end end diff --git a/app/services/tag_search_service.rb b/app/services/tag_search_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..b78d65625d5f75746f3121bd24018b15e16e35b8 --- /dev/null +++ b/app/services/tag_search_service.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +class TagSearchService < BaseService + def call(query, options = {}) + @query = query.strip.gsub(/\A#/, '') + @offset = options.delete(:offset).to_i + @limit = options.delete(:limit).to_i + @options = options + + results = from_elasticsearch if Chewy.enabled? + results ||= from_database + + results + end + + private + + def from_elasticsearch + query = { + function_score: { + query: { + multi_match: { + query: @query, + fields: %w(name.edge_ngram name), + type: 'most_fields', + operator: 'and', + }, + }, + + functions: [ + { + field_value_factor: { + field: 'usage', + modifier: 'log2p', + missing: 0, + }, + }, + + { + gauss: { + last_status_at: { + scale: '7d', + offset: '14d', + decay: 0.5, + }, + }, + }, + ], + + boost_mode: 'multiply', + }, + } + + filter = { + bool: { + should: [ + { + term: { + reviewed: { + value: true, + }, + }, + }, + + { + match: { + name: { + query: @query, + }, + }, + }, + ], + }, + } + + definition = TagsIndex.query(query) + definition = definition.filter(filter) if @options[:exclude_unreviewed] + + definition.limit(@limit).offset(@offset).objects.compact + rescue Faraday::ConnectionFailed, Parslet::ParseFailed + nil + end + + def from_database + Tag.search_for(@query, @limit, @offset, @options) + end +end diff --git a/app/services/unallow_domain_service.rb b/app/services/unallow_domain_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..bd1ad328dacae06933039aae85da51644e8a4a90 --- /dev/null +++ b/app/services/unallow_domain_service.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class UnallowDomainService < BaseService + def call(domain_allow) + Account.where(domain: domain_allow.domain).find_each do |account| + SuspendAccountService.new.call(account, reserve_username: false) + end + + domain_allow.destroy + end +end diff --git a/app/services/unblock_domain_service.rb b/app/services/unblock_domain_service.rb index fc262a50ada223034602d078824ff55dba62ceb4..d502d9e4925cf1227cb7bbbf0454e2938e2bcb98 100644 --- a/app/services/unblock_domain_service.rb +++ b/app/services/unblock_domain_service.rb @@ -10,24 +10,9 @@ class UnblockDomainService < BaseService end def process_retroactive_updates - blocked_accounts.in_batches.update_all(update_options) unless domain_block.noop? - end - - def blocked_accounts scope = Account.by_domain_and_subdomains(domain_block.domain) - if domain_block.silence? - scope.where(silenced_at: @domain_block.created_at) - else - scope.where(suspended_at: @domain_block.created_at) - end - end - - def update_options - { domain_block_impact => nil } - end - - def domain_block_impact - domain_block.silence? ? :silenced_at : :suspended_at + scope.where(silenced_at: domain_block.created_at).in_batches.update_all(silenced_at: nil) unless domain_block.noop? + scope.where(suspended_at: domain_block.created_at).in_batches.update_all(suspended_at: nil) if domain_block.suspend? end end diff --git a/app/services/unblock_service.rb b/app/services/unblock_service.rb index 95a858e9f59a8174502a40fe705ef9125ecdf1a3..c263ac8afe054842756b311ca1d37efd70ae4e68 100644 --- a/app/services/unblock_service.rb +++ b/app/services/unblock_service.rb @@ -7,25 +7,17 @@ class UnblockService < BaseService return unless account.blocking?(target_account) unblock = account.unblock!(target_account) - create_notification(unblock) unless target_account.local? + create_notification(unblock) if !target_account.local? && target_account.activitypub? unblock end private def create_notification(unblock) - if unblock.target_account.ostatus? - NotificationWorker.perform_async(build_xml(unblock), unblock.account_id, unblock.target_account_id) - elsif unblock.target_account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(unblock), unblock.account_id, unblock.target_account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(unblock), unblock.account_id, unblock.target_account.inbox_url) end def build_json(unblock) Oj.dump(serialize_payload(unblock, ActivityPub::UndoBlockSerializer)) end - - def build_xml(block) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.unblock_salmon(block)) - end end diff --git a/app/services/unfavourite_service.rb b/app/services/unfavourite_service.rb index dcc890b7de5f40e6fb3a8a439cf5af0dee31280c..37917a64f1bc52035932d8a136c95ac1ad376fe7 100644 --- a/app/services/unfavourite_service.rb +++ b/app/services/unfavourite_service.rb @@ -6,7 +6,7 @@ class UnfavouriteService < BaseService def call(account, status) favourite = Favourite.find_by!(account: account, status: status) favourite.destroy! - create_notification(favourite) unless status.local? + create_notification(favourite) if !status.account.local? && status.account.activitypub? favourite end @@ -14,19 +14,10 @@ class UnfavouriteService < BaseService def create_notification(favourite) status = favourite.status - - if status.account.ostatus? - NotificationWorker.perform_async(build_xml(favourite), favourite.account_id, status.account_id) - elsif status.account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url) end def build_json(favourite) Oj.dump(serialize_payload(favourite, ActivityPub::UndoLikeSerializer)) end - - def build_xml(favourite) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.unfavourite_salmon(favourite)) - end end diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb index 17dc29735f9d5006fd7ece962c2c22b6d97de635..151f3674fd63a32334b7dd1048b954f09190ec2e 100644 --- a/app/services/unfollow_service.rb +++ b/app/services/unfollow_service.rb @@ -6,9 +6,12 @@ class UnfollowService < BaseService # Unfollow and notify the remote user # @param [Account] source_account Where to unfollow from # @param [Account] target_account Which to unfollow - def call(source_account, target_account) + # @param [Hash] options + # @option [Boolean] :skip_unmerge + def call(source_account, target_account, options = {}) @source_account = source_account @target_account = target_account + @options = options unfollow! || undo_follow_request! end @@ -21,9 +24,11 @@ class UnfollowService < BaseService return unless follow follow.destroy! - create_notification(follow) unless @target_account.local? - create_reject_notification(follow) if @target_account.local? && !@source_account.local? - UnmergeWorker.perform_async(@target_account.id, @source_account.id) + + create_notification(follow) if !@target_account.local? && @target_account.activitypub? + create_reject_notification(follow) if @target_account.local? && !@source_account.local? && @source_account.activitypub? + UnmergeWorker.perform_async(@target_account.id, @source_account.id) unless @options[:skip_unmerge] + follow end @@ -33,21 +38,17 @@ class UnfollowService < BaseService return unless follow_request follow_request.destroy! + create_notification(follow_request) unless @target_account.local? + follow_request end def create_notification(follow) - if follow.target_account.ostatus? - NotificationWorker.perform_async(build_xml(follow), follow.account_id, follow.target_account_id) - elsif follow.target_account.activitypub? - ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.account_id, follow.target_account.inbox_url) - end + ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.account_id, follow.target_account.inbox_url) end def create_reject_notification(follow) - # Rejecting an already-existing follow request - return unless follow.account.activitypub? ActivityPub::DeliveryWorker.perform_async(build_reject_json(follow), follow.target_account_id, follow.account.inbox_url) end @@ -58,8 +59,4 @@ class UnfollowService < BaseService def build_reject_json(follow) Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)) end - - def build_xml(follow) - OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.unfollow_salmon(follow)) - end end diff --git a/app/services/unsubscribe_service.rb b/app/services/unsubscribe_service.rb deleted file mode 100644 index 95c1fb4fc01acf28e241d1cd6903734d880880c3..0000000000000000000000000000000000000000 --- a/app/services/unsubscribe_service.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -class UnsubscribeService < BaseService - def call(account) - return if account.hub_url.blank? - - @account = account - - begin - build_request.perform do |response| - Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{response.status}" unless response.status.success? - end - rescue HTTP::Error, OpenSSL::SSL::SSLError => e - Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{e}" - end - - @account.secret = '' - @account.subscription_expires_at = nil - @account.save! - end - - private - - def build_request - Request.new(:post, @account.hub_url, form: subscription_params) - end - - def subscription_params - { - 'hub.topic': @account.remote_url, - 'hub.mode': 'unsubscribe', - 'hub.callback': api_subscription_url(@account.id), - 'hub.verify': 'async', - } - end -end diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb index 01756a73d4bbeafdc8cef1af40849c59b916a575..4172d577408f4ae88aaeb91ab2fc8b64548f7313 100644 --- a/app/services/update_account_service.rb +++ b/app/services/update_account_service.rb @@ -20,7 +20,9 @@ class UpdateAccountService < BaseService private def authorize_all_follow_requests(account) - AuthorizeFollowWorker.push_bulk(FollowRequest.where(target_account: account).select(:account_id, :target_account_id)) do |req| + follow_requests = FollowRequest.where(target_account: account) + follow_requests = follow_requests.preload(:account).select { |req| !req.account.silenced? } + AuthorizeFollowWorker.push_bulk(follow_requests) do |req| [req.account_id, req.target_account_id] end end diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb deleted file mode 100644 index 403395a0ddb1b0f620f61048f65b5ff17d99a71d..0000000000000000000000000000000000000000 --- a/app/services/update_remote_profile_service.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -class UpdateRemoteProfileService < BaseService - attr_reader :account, :remote_profile - - def call(body, account, resubscribe = false) - @account = account - @remote_profile = RemoteProfile.new(body) - - return if remote_profile.root.nil? - - update_account unless remote_profile.author.nil? - - old_hub_url = account.hub_url - account.hub_url = remote_profile.hub_link if remote_profile.hub_link.present? && remote_profile.hub_link != old_hub_url - - account.save_with_optional_media! - - Pubsubhubbub::SubscribeWorker.perform_async(account.id) if resubscribe && account.hub_url != old_hub_url - end - - private - - def update_account - account.display_name = remote_profile.display_name || '' - account.note = remote_profile.note || '' - account.locked = remote_profile.locked? - - if !account.suspended? && !DomainBlock.reject_media?(account.domain) - if remote_profile.avatar.present? - account.avatar_remote_url = remote_profile.avatar - else - account.avatar_remote_url = '' - account.avatar.destroy - end - - if remote_profile.header.present? - account.header_remote_url = remote_profile.header - else - account.header_remote_url = '' - account.header.destroy - end - - save_emojis if remote_profile.emojis.present? - end - end - - def save_emojis - do_not_download = DomainBlock.reject_media?(account.domain) - - return if do_not_download - - remote_profile.emojis.each do |link| - next unless link['href'] && link['name'] - - shortcode = link['name'].delete(':') - emoji = CustomEmoji.find_by(shortcode: shortcode, domain: account.domain) - - next unless emoji.nil? - - emoji = CustomEmoji.new(shortcode: shortcode, domain: account.domain) - emoji.image_remote_url = link['href'] - emoji.save - end - end -end diff --git a/app/services/verify_salmon_service.rb b/app/services/verify_salmon_service.rb deleted file mode 100644 index 205b35d8b19d2b3894b48914cd6a7298267cf40b..0000000000000000000000000000000000000000 --- a/app/services/verify_salmon_service.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -class VerifySalmonService < BaseService - include AuthorExtractor - - def call(payload) - body = salmon.unpack(payload) - - xml = Nokogiri::XML(body) - xml.encoding = 'utf-8' - - account = author_from_xml(xml.at_xpath('/xmlns:entry', xmlns: OStatus::TagManager::XMLNS)) - - if account.nil? - false - else - salmon.verify(payload, account.keypair) - end - end - - private - - def salmon - @salmon ||= OStatus2::Salmon.new - end -end diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb index 0eeb8fd56a95f5a6c2dbb87a9638037ba1b1ce27..cb7dce6e8a41caa51d9ff49c363e063a854d69db 100644 --- a/app/services/vote_service.rb +++ b/app/services/vote_service.rb @@ -12,12 +12,24 @@ class VoteService < BaseService @choices = choices @votes = [] - ApplicationRecord.transaction do - @choices.each do |choice| - @votes << @poll.votes.create!(account: @account, choice: choice) + already_voted = true + + RedisLock.acquire(lock_options) do |lock| + if lock.acquired? + already_voted = @poll.votes.where(account: @account).exists? + + ApplicationRecord.transaction do + @choices.each do |choice| + @votes << @poll.votes.create!(account: @account, choice: choice) + end + end + else + raise Mastodon::RaceConditionError end end + increment_voters_count! unless already_voted + ActivityTracker.increment('activity:interactions') if @poll.account.local? @@ -53,4 +65,18 @@ class VoteService < BaseService def build_json(vote) Oj.dump(serialize_payload(vote, ActivityPub::VoteSerializer)) end + + def increment_voters_count! + unless @poll.voters_count.nil? + @poll.voters_count = @poll.voters_count + 1 + @poll.save + end + rescue ActiveRecord::StaleObjectError + @poll.reload + retry + end + + def lock_options + { redis: Redis.current, key: "vote:#{@poll.id}:#{@account.id}" } + end end diff --git a/app/validators/disallowed_hashtags_validator.rb b/app/validators/disallowed_hashtags_validator.rb index ee06b20f6b49bd03f5f71a86daafa4c5dda3a748..d745b767f6d937c095416483b969f1c771304c92 100644 --- a/app/validators/disallowed_hashtags_validator.rb +++ b/app/validators/disallowed_hashtags_validator.rb @@ -4,24 +4,7 @@ class DisallowedHashtagsValidator < ActiveModel::Validator def validate(status) return unless status.local? && !status.reblog? - @status = status - tags = select_tags - - status.errors.add(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size)) unless tags.empty? - end - - private - - def select_tags - tags = Extractor.extract_hashtags(@status.text) - tags.keep_if { |tag| disallowed_hashtags.include? tag.downcase } - end - - def disallowed_hashtags - return @disallowed_hashtags if @disallowed_hashtags - - @disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags - @disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String - @disallowed_hashtags = @disallowed_hashtags.map(&:downcase) + disallowed_hashtags = Tag.matching_name(Extractor.extract_hashtags(status.text)).reject(&:usable?) + status.errors.add(:text, I18n.t('statuses.disallowed_hashtags', tags: disallowed_hashtags.map(&:name).join(', '), count: disallowed_hashtags.size)) unless disallowed_hashtags.empty? end end diff --git a/app/validators/domain_validator.rb b/app/validators/domain_validator.rb index ae07f17980b1c894e52b78025c8c1d86112d44c1..6e4a854ff25195f40966835932446f09eca575c3 100644 --- a/app/validators/domain_validator.rb +++ b/app/validators/domain_validator.rb @@ -4,14 +4,22 @@ class DomainValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return if value.blank? - record.errors.add(attribute, I18n.t('domain_validator.invalid_domain')) unless compliant?(value) + domain = begin + if options[:acct] + value.split('@').last + else + value + end + end + + record.errors.add(attribute, I18n.t('domain_validator.invalid_domain')) unless compliant?(domain) end private def compliant?(value) Addressable::URI.new.tap { |uri| uri.host = value } - rescue Addressable::URI::InvalidURIError + rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError false end end diff --git a/app/validators/email_mx_validator.rb b/app/validators/email_mx_validator.rb index 96fbedcfcf9b813dccba10d44238a3b1f6226ddd..9b5009966833ac391ce42e631e836aefd7e43525 100644 --- a/app/validators/email_mx_validator.rb +++ b/app/validators/email_mx_validator.rb @@ -14,6 +14,7 @@ class EmailMxValidator < ActiveModel::Validator return true if domain.nil? + domain = TagManager.instance.normalize_domain(domain) hostnames = [] ips = [] @@ -29,6 +30,8 @@ class EmailMxValidator < ActiveModel::Validator end ips.empty? || on_blacklist?(hostnames + ips) + rescue Addressable::URI::InvalidURIError + true end def on_blacklist?(values) diff --git a/app/validators/unique_username_validator.rb b/app/validators/unique_username_validator.rb index fb67105dd6ba6e3116b24b57e2884aa2e62fb3ca..4e24e3f5fe90f4c9224be1ce9505c457bf5d7b38 100644 --- a/app/validators/unique_username_validator.rb +++ b/app/validators/unique_username_validator.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true +# See also: USERNAME_RE in the Account class + class UniqueUsernameValidator < ActiveModel::Validator def validate(account) return if account.username.nil? - normalized_username = account.username.downcase.delete('.') + normalized_username = account.username.downcase scope = Account.where(domain: nil).where('lower(username) = ?', normalized_username) scope = scope.where.not(id: account.id) if account.persisted? diff --git a/app/views/about/_domain_blocks.html.haml b/app/views/about/_domain_blocks.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..e0c5df41d632aa82219911c14db70af38e5be4f3 --- /dev/null +++ b/app/views/about/_domain_blocks.html.haml @@ -0,0 +1,12 @@ +%table + %thead + %tr + %th= t('about.unavailable_content_description.domain') + %th= t('about.unavailable_content_description.reason') + %tbody + - domain_blocks.each do |domain_block| + %tr + %td.nowrap + %span{ title: domain_block.domain }= domain_block.domain + %td + = domain_block.public_comment if display_blocks_rationale? diff --git a/app/views/about/_login.html.haml b/app/views/about/_login.html.haml index d286f0d3c6e2411c81759802373e7cc9fceff6e3..fa58f04d7369ae0d67cd31be2bc2c1878dfb9235 100644 --- a/app/views/about/_login.html.haml +++ b/app/views/about/_login.html.haml @@ -1,4 +1,4 @@ -= simple_form_for(new_user, url: user_session_path) do |f| += simple_form_for(new_user, url: user_session_path, namespace: 'login') do |f| .fields-group - if use_seamless_external_login? = f.input :email, placeholder: t('simple_form.labels.defaults.username_or_email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.username_or_email') }, hint: false diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index ff32ec8c46caec1f8bb8cf6c541daea6de49c7b3..1333c68c4f1c7aaa1eb6ea07b536f4cbf94d79ef 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -1,4 +1,4 @@ -= simple_form_for(new_user, url: user_registration_path) do |f| += simple_form_for(new_user, url: user_registration_path, namespace: 'registration') do |f| .simple_form__overlay-area %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index b248ed1d2300c28f4df6ed9059cb16217b7847b6..7e156db61b636765da490232c17c7b2694a73328 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -5,7 +5,7 @@ = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' = render partial: 'shared/og' -.grid-3 +.grid-4 .column-0 .public-account-header.public-account-header--no-bar .public-account-header__image @@ -28,20 +28,53 @@ = image_tag @instance_presenter.mascot&.file&.url || asset_pack_path('media/images/elephant_ui_plane.svg'), alt: '' .column-2 - .landing-page__information.contact-widget - %p - %strong= t 'about.administered_by' + .contact-widget + %h4= t 'about.administered_by' = account_link_to(@instance_presenter.contact_account) - if @instance_presenter.site_contact_email.present? - %p.contact-widget__mail - %strong - = succeed ':' do - = t 'about.contact' - %br/ - = mail_to @instance_presenter.site_contact_email, nil, title: @instance_presenter.site_contact_email + %h4 + = succeed ':' do + = t 'about.contact' + + = mail_to @instance_presenter.site_contact_email, nil, title: @instance_presenter.site_contact_email .column-3 - .box-widget - .rich-formatting= @instance_presenter.site_extended_description.html_safe.presence || t('about.extended_description_html') + = render 'application/flashes' + + - if @contents.blank? && (!display_blocks? || @blocks&.empty?) + = nothing_here + - else + .box-widget + .rich-formatting + = @contents.html_safe + + - if display_blocks? && !@blocks.empty? + %h2#unavailable-content= t('about.unavailable_content') + + %p= t('about.unavailable_content_html') + + - if (blocks = @blocks.select(&:reject_media?)) && !blocks.empty? + %p= t('about.unavailable_content_description.rejecting_media') + = render partial: 'domain_blocks', locals: { domain_blocks: blocks } + - if (blocks = @blocks.select(&:silence?)) && !blocks.empty? + %p= t('about.unavailable_content_description.silenced') + = render partial: 'domain_blocks', locals: { domain_blocks: blocks } + - if (blocks = @blocks.select(&:suspend?)) && !blocks.empty? + %p= t('about.unavailable_content_description.suspended') + = render partial: 'domain_blocks', locals: { domain_blocks: blocks } + + .column-4 + %ul.table-of-contents + - @table_of_contents.each do |item| + %li + = link_to item.title, "##{item.anchor}" + + - unless item.children.empty? + %ul + - item.children.each do |sub_item| + %li= link_to sub_item.title, "##{sub_item.anchor}" + + - if display_blocks? && !@blocks.empty? + %li= link_to t('about.unavailable_content'), '#unavailable-content' diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index f24f4e1957d91d5f8430e1f11e7dc8cab2c5509c..80f4cd8287279a9b236b27a1ecf0e4b2a90ba51b 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -52,13 +52,12 @@ .hero-widget__img = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title - - if @instance_presenter.site_short_description.present? - .hero-widget__text - %p - = @instance_presenter.site_short_description.html_safe.presence - = link_to about_more_path do - = t('about.learn_more') - = fa_icon 'angle-double-right' + .hero-widget__text + %p + = @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html') + = link_to about_more_path do + = t('about.learn_more') + = fa_icon 'angle-double-right' .hero-widget__footer .hero-widget__footer__column diff --git a/app/views/accounts/_moved.html.haml b/app/views/accounts/_moved.html.haml index 7a777bfea84f1c9e1644e4dd198eeaf39d2e502c..02fd7bf429d7f0c152a79c9ad84c88aa3d3db51f 100644 --- a/app/views/accounts/_moved.html.haml +++ b/app/views/accounts/_moved.html.haml @@ -3,10 +3,10 @@ .moved-account-widget .moved-account-widget__message = fa_icon 'suitcase' - = t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention')) + = t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'mention')) .moved-account-widget__card - = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener' do + = link_to ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener' do .detailed-status__display-avatar .account__avatar-overlay .account__avatar-overlay-base{ style: "background-image: url('#{moved_to_account.avatar.url(:original)}')" } diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 8f8647536b1969ce81b8580efc92ae35a351e77f..9c26dbabce7f093009c8b59e10b2d154c2eb4955 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -7,9 +7,7 @@ - if @account.user&.setting_noindex %meta{ name: 'robots', content: 'noindex, noarchive' }/ - %link{ rel: 'salmon', href: api_salmon_url(@account.id) }/ - %link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/ - %link{ rel: 'alternate', type: 'application/rss+xml', href: account_url(@account, format: 'rss') }/ + %link{ rel: 'alternate', type: 'application/rss+xml', href: @rss_url }/ %link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@account) }/ - if @older_url @@ -40,12 +38,12 @@ - else .activity-stream.activity-stream--under-tabs - if params[:page].to_i.zero? - = render partial: 'stream_entries/status', collection: @pinned_statuses, as: :status, locals: { pinned: true } + = render partial: 'statuses/status', collection: @pinned_statuses, as: :status, locals: { pinned: true } - if @newer_url .entry= link_to_more @newer_url - = render partial: 'stream_entries/status', collection: @statuses, as: :status + = render partial: 'statuses/status', collection: @statuses, as: :status - if @older_url .entry= link_to_more @older_url @@ -58,24 +56,33 @@ = render 'bio', account: @account - - unless @endorsed_accounts.empty? + - if @endorsed_accounts.empty? && @account.id == current_account&.id + .placeholder-widget= t('accounts.endorsements_hint') + - elsif !@endorsed_accounts.empty? .endorsements-widget %h4= t 'accounts.choices_html', name: content_tag(:bdi, display_name(@account, custom_emojify: true)) - @endorsed_accounts.each do |account| = account_link_to account - - @account.featured_tags.order(statuses_count: :desc).each do |featured_tag| - .directory__tag{ class: params[:tag] == featured_tag.name ? 'active' : nil } - = link_to short_account_tag_path(@account, featured_tag.tag) do - %h4 - = fa_icon 'hashtag' - = featured_tag.name - %small - - if featured_tag.last_status_at.nil? - = t('accounts.nothing_here') - - else - %time.formatted{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at - .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true + - if @featured_hashtags.empty? && @account.id == current_account&.id + .placeholder-widget + = t('accounts.featured_tags_hint') + = link_to settings_featured_tags_path do + = t('featured_tags.add_new') + = fa_icon 'chevron-right fw' + - else + - @featured_hashtags.each do |featured_tag| + .directory__tag{ class: params[:tag] == featured_tag.name ? 'active' : nil } + = link_to short_account_tag_path(@account, featured_tag.tag) do + %h4 + = fa_icon 'hashtag' + = featured_tag.name + %small + - if featured_tag.last_status_at.nil? + = t('accounts.nothing_here') + - else + %time.formatted{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at + .trends__item__current= number_to_human featured_tag.statuses_count, strip_insignificant_zeros: true = render 'application/sidebar' diff --git a/app/views/admin/account_actions/new.html.haml b/app/views/admin/account_actions/new.html.haml index 97286c8e5b5ef1edd35d7b6ddd14a450d24dce71..20fbeef335bebb9fb5217aeea2ad4ec5ab3bb2f6 100644 --- a/app/views/admin/account_actions/new.html.haml +++ b/app/views/admin/account_actions/new.html.haml @@ -13,6 +13,10 @@ .fields-group = f.input :send_email_notification, as: :boolean, wrapper: :with_label + - if params[:report_id].present? + .fields-group + = f.input :include_statuses, as: :boolean, wrapper: :with_label + %hr.spacer/ - unless @warning_presets.empty? diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml index eba3ad804123bbd56363a8e980473aec022b811f..b057d3e42e0e327b6fbf538fb747dcf2eeb3de87 100644 --- a/app/views/admin/accounts/_account.html.haml +++ b/app/views/admin/accounts/_account.html.haml @@ -19,4 +19,4 @@ = table_link_to 'times', t('admin.accounts.reject'), reject_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:reject, account.user) - else = table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}") - = table_link_to 'globe', t('admin.accounts.public'), TagManager.instance.url_for(account) + = table_link_to 'globe', t('admin.accounts.public'), ActivityPub::TagManager.instance.url_for(account) diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 7494c9fa2fd6d3a3be53f6e066e1583311f70054..40a936e86933babae3a98d24d46a63c7fcd03e67 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -3,6 +3,34 @@ = render 'application/card', account: @account +- account = @account +- proofs = account.identity_proofs.active +- fields = account.fields +- unless fields.empty? && proofs.empty? && account.note.blank? + .admin-account-bio + - unless fields.empty? && proofs.empty? + %div + .account__header__fields + - proofs.each do |proof| + %dl + %dt= proof.provider.capitalize + %dd.verified + = link_to fa_icon('check'), proof.badge.proof_url, class: 'verified__mark', title: t('accounts.link_verified_on', date: l(proof.updated_at)) + = link_to proof.provider_username, proof.badge.profile_url + + - fields.each do |field| + %dl + %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true) + %dd{ title: field.value, class: custom_field_classes(field) } + - if field.verified? + %span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) } + = fa_icon 'check' + = Formatter.instance.format_field(account, field.value, custom_emojify: true) + + - if account.note.present? + %div + .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) + .dashboard__counters{ style: 'margin-top: 10px' } %div = link_to admin_account_statuses_path(@account.id) do @@ -174,7 +202,7 @@ - unless @account.local? - if DomainBlock.where(domain: @account.domain).exists? - = link_to t('admin.domain_blocks.undo'), admin_instance_path(@account.domain), class: 'button' + = link_to t('admin.domain_blocks.view'), admin_instance_path(@account.domain), class: 'button' - else = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive' diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index fbaa9a1749066f8c6b79597fbd32a73cb36e13e4..526c844e925d90e972e0b2b903d83fcd8e635740 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -1,28 +1,35 @@ -%tr - %td - = custom_emoji_tag(custom_emoji) - %td - %samp= ":#{custom_emoji.shortcode}:" - %td - - if custom_emoji.local? - = t('admin.accounts.location.local') - - else - = link_to custom_emoji.domain, admin_custom_emojis_path(by_domain: custom_emoji.domain) - %td - - if custom_emoji.local? - - if custom_emoji.visible_in_picker - = table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }, page: params[:page], **@filter_params), method: :patch +.batch-table__row + %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox + = f.check_box :custom_emoji_ids, { multiple: true, include_hidden: false }, custom_emoji.id + .batch-table__row__content.batch-table__row__content--with-image + .batch-table__row__content__image + = custom_emoji_tag(custom_emoji, animate = current_account&.user&.setting_auto_play_gif) + + .batch-table__row__content__text + %samp= ":#{custom_emoji.shortcode}:" + + - if custom_emoji.local? + %span.account-role.bot= custom_emoji.category&.name || t('admin.custom_emojis.uncategorized') + + .batch-table__row__content__extra + - if custom_emoji.local? + = t('admin.accounts.location.local') - else - = table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }, page: params[:page], **@filter_params), method: :patch - - else - - if custom_emoji.local_counterpart.present? - = link_to safe_join([custom_emoji_tag(custom_emoji.local_counterpart), t('admin.custom_emojis.overwrite')]), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, class: 'table-action-link' + = custom_emoji.domain + + - if custom_emoji.local_counterpart.present? + • + = t('admin.accounts.location.local') + + %br/ + + - if custom_emoji.disabled? + = t('admin.custom_emojis.disabled') - else - = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post - %td - - if custom_emoji.disabled? - = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } - - else - = table_link_to 'power-off', t('admin.custom_emojis.disable'), disable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } - %td - = table_link_to 'times', t('admin.custom_emojis.delete'), admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } + = t('admin.custom_emojis.enabled') + - if custom_emoji.local? + • + - if custom_emoji.visible_in_picker? + = t('admin.custom_emojis.listed') + - else + = t('admin.custom_emojis.unlisted') diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 3a119276ca7e92110ddebbf2f2347e608eaba567..7320ce1bbda7db07c9b2203131aaaaf4acda1ce4 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -1,6 +1,9 @@ - content_for :page_title do = t('admin.custom_emojis.title') +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + .filters .filter-subset %strong= t('admin.accounts.location.title') @@ -20,8 +23,7 @@ = form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do .fields-group - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key| - - if params[key].present? - = hidden_field_tag key, params[key] + = hidden_field_tag key, params[key] if params[key].present? - %i(shortcode by_domain).each do |key| .input.string.optional @@ -31,18 +33,54 @@ %button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_custom_emojis_path, class: 'button negative' -.table-wrapper - %table.table - %thead - %tr - %th= t('admin.custom_emojis.emoji') - %th= t('admin.custom_emojis.shortcode') - %th= t('admin.accounts.domain') - %th - %th - %th - %tbody - = render @custom_emojis += form_for(@form, url: batch_admin_custom_emojis_path) do |f| + = hidden_field_tag :page, params[:page] || 1 + + - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key| + = hidden_field_tag key, params[key] if params[key].present? + + .batch-table + .batch-table__toolbar + %label.batch-table__toolbar__select.batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + .batch-table__toolbar__actions + - if params[:local] == '1' + = f.button safe_join([fa_icon('save'), t('generic.save_changes')]), name: :update, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('eye'), t('admin.custom_emojis.list')]), name: :list, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('eye-slash'), t('admin.custom_emojis.unlist')]), name: :unlist, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('power-off'), t('admin.custom_emojis.enable')]), name: :enable, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('power-off'), t('admin.custom_emojis.disable')]), name: :disable, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('times'), t('admin.custom_emojis.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + - unless params[:local] == '1' + = f.button safe_join([fa_icon('copy'), t('admin.custom_emojis.copy')]), name: :copy, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + - if params[:local] == '1' + .batch-table__form.simple_form + .fields-row + .fields-group.fields-row__column.fields-row__column-6 + .input.select.optional + .label_input + = f.select :category_id, options_from_collection_for_select(CustomEmojiCategory.all, 'id', 'name'), prompt: t('admin.custom_emojis.assign_category'), class: 'select optional', 'aria-label': t('admin.custom_emojis.assign_category') + + .fields-group.fields-row__column.fields-row__column-6 + .input.string.optional + .label_input + = f.text_field :category_name, class: 'string optional', placeholder: t('admin.custom_emojis.create_new_category'), 'aria-label': t('admin.custom_emojis.create_new_category') + + .batch-table__body + - if @custom_emojis.empty? + = nothing_here 'nothing-here--under-tabs' + - else + = render partial: 'custom_emoji', collection: @custom_emojis, locals: { f: f } = paginate @custom_emojis + +%hr.spacer/ + = link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button' diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index d448e386297e2e4492d438ccdccef0db41f291c3..2849f07aab8ce2b8b146fdb537a376983b8f86dd 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -4,27 +4,43 @@ .dashboard__counters %div = link_to admin_accounts_url(local: 1, recent: 1) do - .dashboard__counters__num= number_with_delimiter @users_count + .dashboard__counters__num{ title: number_with_delimiter(@users_count, strip_insignificant_zeros: true) } + = number_to_human @users_count, strip_insignificant_zeros: true .dashboard__counters__label= t 'admin.dashboard.total_users' %div %div - .dashboard__counters__num= number_with_delimiter @registrations_week + .dashboard__counters__num{ title: number_with_delimiter(@registrations_week, strip_insignificant_zeros: true) } + = number_to_human @registrations_week, strip_insignificant_zeros: true .dashboard__counters__label= t 'admin.dashboard.week_users_new' %div %div - .dashboard__counters__num= number_with_delimiter @logins_week + .dashboard__counters__num{ title: number_with_delimiter(@logins_week, strip_insignificant_zeros: true) } + = number_to_human @logins_week, strip_insignificant_zeros: true .dashboard__counters__label= t 'admin.dashboard.week_users_active' %div - %div - .dashboard__counters__num= number_with_delimiter @interactions_week - .dashboard__counters__label= t 'admin.dashboard.week_interactions' + = link_to admin_pending_accounts_path do + .dashboard__counters__num{ title: number_with_delimiter(@pending_users_count, strip_insignificant_zeros: true) } + = number_to_human @pending_users_count, strip_insignificant_zeros: true + .dashboard__counters__label= t 'admin.dashboard.pending_users' %div = link_to admin_reports_url do - .dashboard__counters__num= number_with_delimiter @reports_count + .dashboard__counters__num{ title: number_with_delimiter(@reports_count, strip_insignificant_zeros: true) } + = number_to_human @reports_count, strip_insignificant_zeros: true .dashboard__counters__label= t 'admin.dashboard.open_reports' + %div + = link_to admin_tags_path(pending_review: '1') do + .dashboard__counters__num{ title: number_with_delimiter(@pending_tags_count, strip_insignificant_zeros: true) } + = number_to_human @pending_tags_count, strip_insignificant_zeros: true + .dashboard__counters__label= t 'admin.dashboard.pending_tags' + %div + %div + .dashboard__counters__num{ title: number_with_delimiter(@interactions_week, strip_insignificant_zeros: true) } + = number_to_human @interactions_week, strip_insignificant_zeros: true + .dashboard__counters__label= t 'admin.dashboard.week_interactions' %div = link_to sidekiq_url do - .dashboard__counters__num= number_with_delimiter @queue_backlog + .dashboard__counters__num{ title: number_with_delimiter(@queue_backlog, strip_insignificant_zeros: true) } + = number_to_human @queue_backlog, strip_insignificant_zeros: true .dashboard__counters__label= t 'admin.dashboard.backlog' .dashboard__widgets @@ -49,8 +65,12 @@ = feature_hint(link_to(t('admin.dashboard.feature_profile_directory'), edit_admin_settings_path), @profile_directory) %li = feature_hint(link_to(t('admin.dashboard.feature_timeline_preview'), edit_admin_settings_path), @timeline_preview) + %li + = feature_hint(link_to(t('admin.dashboard.trends'), edit_admin_settings_path), @trends_enabled) %li = feature_hint(link_to(t('admin.dashboard.feature_relay'), admin_relays_path), @relay_enabled) + %li + = feature_hint(link_to(t('admin.dashboard.feature_spam_check'), edit_admin_settings_path), @spam_check_enabled) .dashboard__widgets__versions %div @@ -88,6 +108,10 @@ = feature_hint(t('admin.dashboard.search'), @search_enabled) %li = feature_hint(t('admin.dashboard.single_user_mode'), @single_user_mode) + %li + = feature_hint(t('admin.dashboard.authorized_fetch_mode'), @authorized_fetch) + %li + = feature_hint(t('admin.dashboard.whitelist_mode'), @whitelist_enabled) %li = feature_hint('LDAP', @ldap_enabled) %li @@ -105,5 +129,5 @@ %ul - @trending_hashtags.each do |tag| %li - = link_to "##{tag.name}", web_url("timelines/tag/#{tag.name}") + = link_to content_tag(:span, "##{tag.name}", class: !tag.trendable? && !tag.reviewed? ? 'warning-hint' : (!tag.trendable? ? 'negative-hint' : nil)), admin_tag_path(tag.id) %span.pull-right= number_with_delimiter(tag.history[0][:accounts].to_i) diff --git a/app/views/admin/domain_allows/new.html.haml b/app/views/admin/domain_allows/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..52599857aabc43b8a55733c84e6c0200a272059c --- /dev/null +++ b/app/views/admin/domain_allows/new.html.haml @@ -0,0 +1,14 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + +- content_for :page_title do + = t('admin.domain_allows.add_new') + += simple_form_for @domain_allow, url: admin_domain_allows_path do |f| + = render 'shared/error_messages', object: @domain_allow + + .fields-group + = f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), required: true + + .actions + = f.button :button, t('admin.domain_allows.add_new'), type: :submit diff --git a/app/views/admin/domain_blocks/edit.html.haml b/app/views/admin/domain_blocks/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..29e47ef3bdd78af7919145bd00f498f13449752b --- /dev/null +++ b/app/views/admin/domain_blocks/edit.html.haml @@ -0,0 +1,30 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + +- content_for :page_title do + = t('admin.domain_blocks.edit') + += simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :put do |f| + = render 'shared/error_messages', object: @domain_block + + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :domain, wrapper: :with_label, label: t('admin.domain_blocks.domain'), hint: t('admin.domain_blocks.new.hint'), required: true, readonly: true, disabled: true + + .fields-row__column.fields-row__column-6.fields-group + = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| t("admin.domain_blocks.new.severity.#{type}") }, hint: t('admin.domain_blocks.new.severity.desc_html') + + .fields-group + = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint') + + .fields-group + = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint') + + .field-group + = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6 + + .field-group + = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6 + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 055d2fbd7e44dc81cb490ccbb64d7ec4844b238a..ed1581936a8fac4e9b60df1c57a13e337e3f9592 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -20,5 +20,11 @@ .fields-group = f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint') + .field-group + = f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6 + + .field-group + = f.input :public_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.public_comment'), hint: t('admin.domain_blocks.public_comment_hint'), rows: 6 + .actions = f.button :button, t('.create'), type: :submit diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml index dca4dbac777f9302dd0ebbba1a6b7fd1fe82b042..e64aaa6295f81308f8ffd74af01af5f5ae6af911 100644 --- a/app/views/admin/domain_blocks/show.html.haml +++ b/app/views/admin/domain_blocks/show.html.haml @@ -1,6 +1,18 @@ - content_for :page_title do = t('admin.domain_blocks.show.title', domain: @domain_block.domain) +- if @domain_block.private_comment.present? + .speech-bubble + .speech-bubble__bubble + = simple_format(h(@domain_block.private_comment)) + .speech-bubble__owner= t 'admin.instances.private_comment' + +- if @domain_block.public_comment.present? + .speech-bubble + .speech-bubble__bubble + = simple_format(h(@domain_block.public_comment)) + .speech-bubble__owner= t 'admin.instances.public_comment' + = simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :delete do |f| - unless (@domain_block.noop?) diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml index 61e578409a4ae0478e01fe1dab17f030677c4712..1d85aa75e018d5c08321c76e74987466930408dd 100644 --- a/app/views/admin/instances/index.html.haml +++ b/app/views/admin/instances/index.html.haml @@ -6,24 +6,30 @@ %strong= t('admin.instances.moderation.title') %ul %li= filter_link_to t('admin.instances.moderation.all'), limited: nil - %li= filter_link_to t('admin.instances.moderation.limited'), limited: '1' + + - unless whitelist_mode? + %li= filter_link_to t('admin.instances.moderation.limited'), limited: '1' %div{ style: 'flex: 1 1 auto; text-align: right' } - = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path, class: 'button' + - if whitelist_mode? + = link_to t('admin.domain_allows.add_new'), new_admin_domain_allow_path, class: 'button' + - else + = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path, class: 'button' -= form_tag admin_instances_url, method: 'GET', class: 'simple_form' do - .fields-group - - Admin::FilterHelper::INSTANCES_FILTERS.each do |key| - - if params[key].present? - = hidden_field_tag key, params[key] +- unless whitelist_mode? + = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do + .fields-group + - Admin::FilterHelper::INSTANCES_FILTERS.each do |key| + - if params[key].present? + = hidden_field_tag key, params[key] - - %i(by_domain).each do |key| - .input.string.optional - = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.instances.#{key}") + - %i(by_domain).each do |key| + .input.string.optional + = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.instances.#{key}") - .actions - %button= t('admin.accounts.search') - = link_to t('admin.accounts.reset'), admin_instances_path, class: 'button negative' + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_instances_path, class: 'button negative' %hr.spacer/ @@ -38,17 +44,21 @@ - if !instance.domain_block.noop? = t("admin.domain_blocks.severity.#{instance.domain_block.severity}") - first_item = false - - if instance.domain_block.reject_media? - - unless first_item - • - = t('admin.domain_blocks.rejecting_media') - - first_item = false - - if instance.domain_block.reject_reports? - - unless first_item - • - = t('admin.domain_blocks.rejecting_reports') + - unless instance.domain_block.suspend? + - if instance.domain_block.reject_media? + - unless first_item + • + = t('admin.domain_blocks.rejecting_media') + - first_item = false + - if instance.domain_block.reject_reports? + - unless first_item + • + = t('admin.domain_blocks.rejecting_reports') + - elsif whitelist_mode? + = t('admin.accounts.whitelisted') - else = t('admin.accounts.no_limits_imposed') - if instance.countable? .trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true + = paginate paginated_instances diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml index c7992a490d09b576e8cc9bf595bfbc9cbdcfda86..294c9495db8096797329ed3866ada2f51b1e2f02 100644 --- a/app/views/admin/instances/show.html.haml +++ b/app/views/admin/instances/show.html.haml @@ -31,6 +31,18 @@ = fa_icon 'times' .dashboard__counters__label= t 'admin.instances.delivery_available' +- if @private_comment.present? + .speech-bubble + .speech-bubble__bubble + = simple_format(h(@private_comment)) + .speech-bubble__owner= t 'admin.instances.private_comment' + +- if @public_comment.present? + .speech-bubble + .speech-bubble__bubble + = simple_format(h(@public_comment)) + .speech-bubble__owner= t 'admin.instances.public_comment' + %hr.spacer/ %div{ style: 'overflow: hidden' } @@ -38,7 +50,10 @@ = link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button' %div{ style: 'float: right' } - - if @domain_block + - if @domain_allow + = link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete } + - elsif @domain_block + = link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@domain_block), class: 'button' = link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button' - else = link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button' diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml index 9bb751a9c2bc0e616596faa84c56777b3a5433e1..6640f30e0fa661d3670f067ce58c01f3acc9deb4 100644 --- a/app/views/admin/reports/_status.html.haml +++ b/app/views/admin/reports/_status.html.haml @@ -16,11 +16,14 @@ - video = status.proper.media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description - else - = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } + = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } .detailed-status__meta - = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener' do + = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener' do %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) + - if status.discarded? + · + %span.negative-hint= t('admin.statuses.deleted') · - if status.reblog? = fa_icon('retweet fw') diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index d73faccb0d9d971b6c4a8144bfdb95f9e9e0ae01..bfbd32108e468e725aa31fe052cb1248b51cce70 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -28,7 +28,9 @@ - reports.each do |report| .report-card__summary__item .report-card__summary__item__reported-by - - if report.account.local? + - if report.account.instance_actor? + = site_hostname + - elsif report.account.local? = admin_account_link_to report.account - else = report.account.domain diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 863dada9e27c1e5acc9bbe5b462705e777e97087..bbe83c979a0fe88ab6f431054b24bee6ce0b599f 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -29,7 +29,9 @@ %td= table_link_to 'file', pluralize(@report.target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.target_account.id) %tr %th= t('admin.reports.reported_by') - - if @report.account.local? + - if @report.account.instance_actor? + %td{ colspan: 3 }= site_hostname + - elsif @report.account.local? %td= admin_account_link_to @report.account %td= table_link_to 'flag', pluralize(@report.account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.account.id) %td= table_link_to 'file', pluralize(@report.account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.account.id) diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index a67e6a2c82300eb060defcfd6860a103a120e88c..6282bb39c0a1a9b736bc2800c5e88a5a9d564378 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -20,10 +20,10 @@ = f.input :site_contact_email, wrapper: :with_label, label: t('admin.settings.contact_information.email') .fields-group - = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 4 } + = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } .fields-group - = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 } + = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 2 } .fields-row .fields-row__column.fields-row__column-6.fields-group @@ -42,11 +42,12 @@ %hr.spacer/ - .fields-group - = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html') + - unless whitelist_mode? + .fields-group + = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html') - .fields-group - = f.input :show_known_fediverse_at_about_page, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_known_fediverse_at_about_page.title'), hint: t('admin.settings.show_known_fediverse_at_about_page.desc_html') + .fields-group + = f.input :show_known_fediverse_at_about_page, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_known_fediverse_at_about_page.title'), hint: t('admin.settings.show_known_fediverse_at_about_page.desc_html') .fields-group = f.input :show_staff_badge, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_staff_badge.title'), hint: t('admin.settings.show_staff_badge.desc_html') @@ -54,26 +55,45 @@ .fields-group = f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html') - .fields-group - = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html') + - unless whitelist_mode? + .fields-group + = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html') - .fields-group - = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html') + .fields-group + = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html') - .fields-group - = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html') + .fields-group + = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html') + + .fields-group + = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html') + + .fields-group + = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html') + + .fields-group + = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html') + + .fields-group + = f.input :noindex, as: :boolean, wrapper: :with_label, label: t('admin.settings.default_noindex.title'), hint: t('admin.settings.default_noindex.desc_html') .fields-group - = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html') + = f.input :spam_check_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.spam_check_enabled.title'), hint: t('admin.settings.spam_check_enabled.desc_html') %hr.spacer/ .fields-group = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + .fields-row + .fields-row__column.fields-row__column-6.fields-group + = f.input :show_domain_blocks, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + .fields-row__column.fields-row__column-6.fields-group + = f.input :show_domain_blocks_rationale, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks_rationale.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + .fields-group + = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode? = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 } - = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 } = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html') diff --git a/app/views/admin/subscriptions/_subscription.html.haml b/app/views/admin/subscriptions/_subscription.html.haml deleted file mode 100644 index 1dec8e39623463178140d5807311105542aaefc4..0000000000000000000000000000000000000000 --- a/app/views/admin/subscriptions/_subscription.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -%tr - %td - %samp= subscription.account.acct - %td - %samp= subscription.callback_url - %td - - if subscription.confirmed? - %i.fa.fa-check - %td{ style: "color: #{subscription.expired? ? 'red' : 'inherit'};" } - %time.time-ago{ datetime: subscription.expires_at.iso8601, title: l(subscription.expires_at) } - = precede subscription.expired? ? '-' : '' do - = time_ago_in_words(subscription.expires_at) - %td - - if subscription.last_successful_delivery_at? - %time.formatted{ datetime: subscription.last_successful_delivery_at.iso8601, title: l(subscription.last_successful_delivery_at) } - = l subscription.last_successful_delivery_at - - else - %i.fa.fa-times diff --git a/app/views/admin/subscriptions/index.html.haml b/app/views/admin/subscriptions/index.html.haml deleted file mode 100644 index 83704c8ee5bbff940f92baf48906af87b19a6ec8..0000000000000000000000000000000000000000 --- a/app/views/admin/subscriptions/index.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -- content_for :page_title do - = t('admin.subscriptions.title') - -.table-wrapper - %table.table - %thead - %tr - %th= t('admin.subscriptions.topic') - %th= t('admin.subscriptions.callback_url') - %th= t('admin.subscriptions.confirmed') - %th= t('admin.subscriptions.expires_in') - %th= t('admin.subscriptions.last_delivery') - %tbody - = render @subscriptions - -= paginate @subscriptions diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml index 961b83f93c23c1b4f1d235e390d50eb284db3c44..670f3bc0598ddfc0cdf39f72bf38189def0fd099 100644 --- a/app/views/admin/tags/_tag.html.haml +++ b/app/views/admin/tags/_tag.html.haml @@ -1,12 +1,20 @@ -%tr - %td - = link_to explore_hashtag_path(tag) do - = fa_icon 'hashtag' - = tag.name - %td - = t('directories.people', count: tag.accounts_count) - %td - - if tag.hidden? - = table_link_to 'eye', t('admin.tags.unhide'), unhide_admin_tag_path(tag.id, **@filter_params), method: :post - - else - = table_link_to 'eye-slash', t('admin.tags.hide'), hide_admin_tag_path(tag.id, **@filter_params), method: :post +.batch-table__row + %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox + = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id + + .directory__tag + = link_to admin_tag_path(tag.id) do + %h4 + = fa_icon 'hashtag' + = tag.name + + %small + = t('admin.tags.in_directory', count: tag.accounts_count) + • + = t('admin.tags.unique_uses_today', count: tag.history.first[:accounts]) + + - if tag.trending? + = fa_icon 'fire fw' + = t('admin.tags.trending_right_now') + + .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml index 4ba3958605d69e1d989ce05bc83417f824010d0a..8b1182dbb11f70954bc5ce03f07b6dd3bff5966e 100644 --- a/app/views/admin/tags/index.html.haml +++ b/app/views/admin/tags/index.html.haml @@ -1,19 +1,79 @@ - content_for :page_title do = t('admin.tags.title') +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + .filters .filter-subset - %strong= t('admin.reports.status') + %strong= t('admin.tags.context') + %ul + %li= filter_link_to t('generic.all'), directory: nil + %li= filter_link_to t('admin.tags.directory'), directory: '1' + + .filter-subset + %strong= t('admin.tags.review') + %ul + %li= filter_link_to t('generic.all'), reviewed: nil, unreviewed: nil, pending_review: nil + %li= filter_link_to t('admin.tags.unreviewed'), unreviewed: '1', reviewed: nil, pending_review: nil + %li= filter_link_to t('admin.tags.reviewed'), reviewed: '1', unreviewed: nil, pending_review: nil + %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{Tag.pending_review.count})"], ' '), pending_review: '1', reviewed: nil, unreviewed: nil + + .filter-subset + %strong= t('generic.order_by') %ul - %li= filter_link_to t('admin.tags.visible'), hidden: nil - %li= filter_link_to t('admin.tags.hidden'), hidden: '1' - -.table-wrapper - %table.table - %thead - %tr - %th= t('admin.tags.name') - %th= t('admin.tags.accounts') - %th - %tbody - = render @tags + %li= filter_link_to t('admin.tags.most_recent'), popular: nil, active: nil + %li= filter_link_to t('admin.tags.most_popular'), popular: '1', active: nil + %li= filter_link_to t('admin.tags.last_active'), active: '1', popular: nil + += form_tag admin_tags_url, method: 'GET', class: 'simple_form' do + .fields-group + - Admin::FilterHelper::TAGS_FILTERS.each do |key| + = hidden_field_tag key, params[key] if params[key].present? + + - %i(name).each do |key| + .input.string.optional + = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.tags.#{key}") + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_tags_path, class: 'button negative' + +%hr.spacer/ + += form_for(@form, url: batch_admin_tags_path) do |f| + = hidden_field_tag :page, params[:page] || 1 + = hidden_field_tag :name, params[:name] if params[:name].present? + + - Admin::FilterHelper::TAGS_FILTERS.each do |key| + = hidden_field_tag key, params[key] if params[key].present? + + .batch-table + .batch-table__toolbar + %label.batch-table__toolbar__select.batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + .batch-table__toolbar__actions + - if params[:pending_review] == '1' + = f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + - else + %span.neutral-hint= t('generic.no_batch_actions_available') + + .batch-table__body + - if @tags.empty? + = nothing_here 'nothing-here--under-tabs' + - else + = render partial: 'tag', collection: @tags, locals: { f: f } + += paginate @tags + +- if params[:pending_review] == '1' + %hr.spacer/ + + %div{ style: 'overflow: hidden' } + %div{ style: 'float: right' } + = link_to t('admin.accounts.reject_all'), reject_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' + + %div + = link_to t('admin.accounts.approve_all'), approve_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' diff --git a/app/views/admin/tags/show.html.haml b/app/views/admin/tags/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5799e5973d9fbc095c4ae9637f9d973b654949fc --- /dev/null +++ b/app/views/admin/tags/show.html.haml @@ -0,0 +1,52 @@ +- content_for :page_title do + = "##{@tag.name}" + +.dashboard__counters + %div + = link_to tag_url(@tag), target: '_blank', rel: 'noopener' do + .dashboard__counters__num= number_with_delimiter @accounts_today + .dashboard__counters__label= t 'admin.tags.accounts_today' + %div + %div + .dashboard__counters__num= number_with_delimiter @accounts_week + .dashboard__counters__label= t 'admin.tags.accounts_week' + %div + - if @tag.accounts_count > 0 + = link_to explore_hashtag_path(@tag) do + .dashboard__counters__num= number_with_delimiter @tag.accounts_count + .dashboard__counters__label= t 'admin.tags.directory' + - else + %div + .dashboard__counters__num= number_with_delimiter @tag.accounts_count + .dashboard__counters__label= t 'admin.tags.directory' + +%hr.spacer/ + += simple_form_for @tag, url: admin_tag_path(@tag.id) do |f| + = render 'shared/error_messages', object: @tag + + .fields-group + = f.input :name, wrapper: :with_block_label + + .fields-group + = f.input :usable, as: :boolean, wrapper: :with_label + = f.input :trendable, as: :boolean, wrapper: :with_label, disabled: !Setting.trends + = f.input :listable, as: :boolean, wrapper: :with_label, disabled: !Setting.profile_directory + + .actions + = f.button :button, t('generic.save_changes'), type: :submit + +%hr.spacer/ + +%h3= t 'admin.tags.breakdown' + +.table-wrapper + %table.table + %tbody + - total = @usage_by_domain.sum(&:last).to_f + + - @usage_by_domain.each do |(domain, count)| + %tr + %th= domain || site_hostname + %td= number_to_percentage((count / total) * 100, precision: 1) + %td= number_with_delimiter count diff --git a/app/views/admin_mailer/new_trending_tag.text.erb b/app/views/admin_mailer/new_trending_tag.text.erb new file mode 100644 index 0000000000000000000000000000000000000000..e4bfdc5912ebbcadeef0412000499b7ef351d80a --- /dev/null +++ b/app/views/admin_mailer/new_trending_tag.text.erb @@ -0,0 +1,5 @@ +<%= raw t('application_mailer.salutation', name: display_name(@me)) %> + +<%= raw t('admin_mailer.new_trending_tag.body', name: @tag.name) %> + +<%= raw t('application_mailer.view')%> <%= admin_tags_url(pending_review: '1') %> diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml index e6059b03507aa0e7322474cbd973f263fe0ac59b..8719ce48444048f138ff9f762efbe7d2062c65b9 100644 --- a/app/views/application/_card.html.haml +++ b/app/views/application/_card.html.haml @@ -1,4 +1,4 @@ -- account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account) +- account_url = local_assigns[:admin] ? admin_account_path(account.id) : ActivityPub::TagManager.instance.url_for(account) .card.h-card = link_to account_url, target: '_blank', rel: 'noopener' do @@ -9,7 +9,7 @@ = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' .display-name - %span{id: "default_account_display_name", style: "display:none;"}= account.username + %span{ id: "default_account_display_name", style: "display: none" }= account.username %bdi %strong.emojify.p-name= display_name(account, custom_emojify: true) %span diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml index b5ce5845e3eb884c990a0e9a31ee6c5920df7d8a..7ec91c06aa32cba5474c7744c1e969942e832c9c 100644 --- a/app/views/application/_sidebar.html.haml +++ b/app/views/application/_sidebar.html.haml @@ -3,4 +3,14 @@ = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title .hero-widget__text - %p= @instance_presenter.site_short_description.html_safe.presence || @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname) + %p= @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html') + +- if Setting.trends && !(user_signed_in? && !current_user.setting_trends) + - trends = TrendingTags.get(3) + + - unless trends.empty? + .endorsements-widget.trends-widget + %h4.emojify= t('footer.trending_now') + + - trends.each do |tag| + = react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer).as_json diff --git a/app/views/auth/challenges/new.html.haml b/app/views/auth/challenges/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..9aef2c35d6e5015d317a8045fe226c500e6944ec --- /dev/null +++ b/app/views/auth/challenges/new.html.haml @@ -0,0 +1,15 @@ +- content_for :page_title do + = t('challenge.prompt') + += simple_form_for @challenge, url: request.get? ? auth_challenge_path : '' do |f| + = f.input :return_to, as: :hidden + + .field-group + = f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off', :autofocus => true }, label: t('challenge.prompt'), required: true + + .actions + = f.button :button, t('challenge.confirm'), type: :submit + + %p.hint.subtle-hint= t('challenge.hint_html') + +.form-footer= render 'auth/shared/links' diff --git a/app/views/auth/confirmations/finish_signup.html.haml b/app/views/auth/confirmations/finish_signup.html.haml deleted file mode 100644 index 9d09b74e1627515eed3641c78374041ddcdd36af..0000000000000000000000000000000000000000 --- a/app/views/auth/confirmations/finish_signup.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -- content_for :page_title do - = t('auth.confirm_email') - -= simple_form_for(current_user, as: 'user', url: finish_signup_path, html: { role: 'form'}) do |f| - - if @show_errors && current_user.errors.any? - #error_explanation - - current_user.errors.full_messages.each do |msg| - = msg - %br - - .fields-group - = f.input :email, wrapper: :with_label, required: true, hint: false - - .actions - = f.submit t('auth.confirm_email'), class: 'button' diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml index d7d96a1bb3338617854e9063e5b88eb344142187..395e36a9fd6d07373a3e4ca01bad451bab6b31d5 100644 --- a/app/views/auth/registrations/_sessions.html.haml +++ b/app/views/auth/registrations/_sessions.html.haml @@ -1,6 +1,8 @@ -%h4= t 'sessions.title' +%h3= t 'sessions.title' %p.muted-hint= t 'sessions.explanation' +%hr.spacer/ + .table-wrapper %table.table.inline-table %thead diff --git a/app/views/auth/registrations/_status.html.haml b/app/views/auth/registrations/_status.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..47112dae07cc6474206ff7cc65dbaa14e512b941 --- /dev/null +++ b/app/views/auth/registrations/_status.html.haml @@ -0,0 +1,22 @@ +%h3= t('auth.status.account_status') + +.simple_form + %p.hint + - if @user.account.suspended? + %span.negative-hint= t('user_mailer.warning.explanation.suspend') + - elsif @user.disabled? + %span.negative-hint= t('user_mailer.warning.explanation.disable') + - elsif @user.account.silenced? + %span.warning-hint= t('user_mailer.warning.explanation.silence') + - elsif !@user.confirmed? + %span.warning-hint= t('auth.status.confirming') + = link_to t('auth.didnt_get_confirmation'), new_user_confirmation_path + - elsif !@user.approved? + %span.warning-hint= t('auth.status.pending') + - elsif @user.account.moved_to_account_id.present? + %span.positive-hint= t('auth.status.redirecting_to', acct: @user.account.moved_to_account.acct) + = link_to t('migrations.cancel'), settings_migration_path + - else + %span.positive-hint= t('auth.status.functional') + +%hr.spacer/ diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml index 694461fdf2ec4f71161201e20e010e08ca5bbece..a155c75c987e9c69d220e905a66c3d0093f97543 100644 --- a/app/views/auth/registrations/edit.html.haml +++ b/app/views/auth/registrations/edit.html.haml @@ -1,25 +1,28 @@ - content_for :page_title do - = t('auth.security') + = t('settings.account_settings') + += render 'status' + +%h3= t('auth.security') = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f| = render 'shared/error_messages', object: resource - if !use_seamless_external_login? || resource.encrypted_password.present? - .fields-group - = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, hint: false - - .fields-group - = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true - - .fields-group - = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: false - - .fields-group - = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' } - + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :email, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, required: true, disabled: current_account.suspended? + .fields-row__column.fields-group.fields-row__column-6 + = f.input :current_password, wrapper: :with_label, input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, required: true, disabled: current_account.suspended?, hint: false + + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended? + .fields-row__column.fields-group.fields-row__column-6 + = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended? .actions - = f.button :button, t('generic.save_changes'), type: :submit + = f.button :button, t('generic.save_changes'), type: :submit, class: 'button', disabled: current_account.suspended? - else %p.hint= t('users.seamless_external_login') @@ -27,7 +30,18 @@ = render 'sessions' -- if open_deletion? +%hr.spacer/ + +%h3= t('auth.migrate_account') +%p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path) + +%hr.spacer/ + +%h3= t('migrations.incoming_migrations') +%p.muted-hint= t('migrations.incoming_migrations_html', path: settings_aliases_path) + +- if open_deletion? && !current_account.suspended? %hr.spacer/ - %h4= t('auth.delete_account') + + %h3= t('auth.delete_account') %p.muted-hint= t('auth.delete_account_html', path: settings_delete_path) diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index b4a7cced55c69d6fab5164908d0bbe6ae050bac8..e807c8d86764e31dcf3e118699c7488df843e755 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -2,7 +2,7 @@ = t('auth.register') - content_for :header_tags do - = render partial: 'shared/og' + = render partial: 'shared/og', locals: { description: description_for_sign_up } = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = render 'shared/error_messages', object: resource @@ -33,7 +33,7 @@ = f.input :invite_code, as: :hidden .fields-group - = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path) + = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path) .actions = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit diff --git a/app/views/auth/setup/show.html.haml b/app/views/auth/setup/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..c14fed56f8b1e5b837538875b71e765a12153846 --- /dev/null +++ b/app/views/auth/setup/show.html.haml @@ -0,0 +1,20 @@ +- content_for :page_title do + = t('auth.setup.title') + +- if missing_email? + = simple_form_for(@user, url: auth_setup_path) do |f| + = render 'shared/error_messages', object: @user + + .fields-group + %p.hint= t('auth.setup.email_below_hint_html') + + .fields-group + = f.input :email, required: true, hint: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' } + + .actions + = f.submit t('admin.accounts.change_email.label'), class: 'button' +- else + .simple_form + %p.hint= t('auth.setup.email_settings_hint_html', email: content_tag(:strong, @user.email)) + +.form-footer= render 'auth/shared/links' diff --git a/app/views/auth/shared/_links.html.haml b/app/views/auth/shared/_links.html.haml index 3c68ccd222086da4f0ffde12cfbc0a7a0fa5a718..66ed5b93f38e7e63535c01601e706288bc5bbe94 100644 --- a/app/views/auth/shared/_links.html.haml +++ b/app/views/auth/shared/_links.html.haml @@ -1,12 +1,18 @@ %ul.no-list - - if controller_name != 'sessions' - %li= link_to t('auth.login'), new_session_path(resource_name) + - if user_signed_in? + %li= link_to t('settings.account_settings'), edit_user_registration_path + - else + - if controller_name != 'sessions' + %li= link_to t('auth.login'), new_user_session_path - - if devise_mapping.registerable? && controller_name != 'registrations' - %li= link_to t('auth.register'), available_sign_up_path + - if controller_name != 'registrations' + %li= link_to t('auth.register'), available_sign_up_path - - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' - %li= link_to t('auth.forgot_password'), new_password_path(resource_name) + - if controller_name != 'passwords' && controller_name != 'registrations' + %li= link_to t('auth.forgot_password'), new_user_password_path - - if devise_mapping.confirmable? && controller_name != 'confirmations' - %li= link_to t('auth.didnt_get_confirmation'), new_confirmation_path(resource_name) + - if controller_name != 'confirmations' && (!user_signed_in? || !current_user.confirmed? || current_user.unconfirmed_email.present?) + %li= link_to t('auth.didnt_get_confirmation'), new_user_confirmation_path + + - if user_signed_in? && controller_name != 'setup' + %li= link_to t('auth.logout'), destroy_user_session_path, data: { method: :delete } diff --git a/app/views/authorize_interactions/_post_follow_actions.html.haml b/app/views/authorize_interactions/_post_follow_actions.html.haml index 561c60137fc4ac7a01f201499297fd73555b7fe7..dd71160e2d7c50d07922b28beee6384fd157a566 100644 --- a/app/views/authorize_interactions/_post_follow_actions.html.haml +++ b/app/views/authorize_interactions/_post_follow_actions.html.haml @@ -1,4 +1,4 @@ .post-follow-actions %div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@resource.id}"), class: 'button button--block' - %div= link_to t('authorize_follow.post_follow.return'), TagManager.instance.url_for(@resource), class: 'button button--block' + %div= link_to t('authorize_follow.post_follow.return'), ActivityPub::TagManager.instance.url_for(@resource), class: 'button button--block' %div= t('authorize_follow.post_follow.close') diff --git a/app/views/directories/index.html.haml b/app/views/directories/index.html.haml index a8aa68cc434e1e6c1d35fa25d0c03e9c276f5b2a..6bf2ec81ea922afab07ad45a08c7398308647a4b 100644 --- a/app/views/directories/index.html.haml +++ b/app/views/directories/index.html.haml @@ -14,58 +14,43 @@ %h1= t('directories.explore_mastodon', title: site_title) %p= t('directories.explanation') -.grid - .column-0 - - if @accounts.empty? - = nothing_here - - else - .directory - %table.accounts-table - %tbody - - @accounts.each do |account| - %tr - %td= account_link_to account - %td.accounts-table__count.optional - = number_to_human account.statuses_count, strip_insignificant_zeros: true - %small= t('accounts.posts', count: account.statuses_count).downcase - %td.accounts-table__count.optional - = number_to_human account.followers_count, strip_insignificant_zeros: true - %small= t('accounts.followers', count: account.followers_count).downcase - %td.accounts-table__count - - if account.last_status_at.present? - %time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at - - else - \- - %small= t('accounts.last_active') - - = paginate @accounts - - .column-1 - - if user_signed_in? - .box-widget.notice-widget - - if current_account.discoverable? - - if current_account.followers_count < Account::MIN_FOLLOWERS_DISCOVERY - %p= t('directories.enabled_but_waiting', min_followers: Account::MIN_FOLLOWERS_DISCOVERY) - - else - %p= t('directories.enabled') - - else - %p= t('directories.how_to_enable') - - = link_to settings_profile_path do - = t('settings.edit_profile') - = fa_icon 'chevron-right fw' - - - if @tags.empty? && !user_signed_in? - .nothing-here - - else - - @tags.each do |tag| - .directory__tag{ class: tag.id == @tag&.id ? 'active' : nil } - = link_to explore_hashtag_path(tag) do - %h4 - = fa_icon 'hashtag' - = tag.name - %small= t('directories.people', count: tag.accounts_count) - - .avatar-stack - - tag.cached_sample_accounts.each do |account| - = image_tag current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url, width: 48, height: 48, alt: '', class: 'account__avatar' +- if @accounts.empty? + = nothing_here +- else + .directory__list + - @accounts.each do |account| + .directory__card + .directory__card__img + = image_tag account.header.url, alt: '' + .directory__card__bar + = link_to TagManager.instance.url_for(account), class: 'directory__card__bar__name' do + .avatar + = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' + + .display-name + %span{ id: "default_account_display_name", style: "display: none" }= account.username + %bdi + %strong.emojify.p-name= display_name(account, custom_emojify: true) + %span= acct(account) + .directory__card__bar__relationship.account__relationship + = minimal_account_action_button(account) + + .directory__card__extra + .account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) + + .directory__card__extra + .accounts-table__count + = number_to_human account.statuses_count, strip_insignificant_zeros: true + %small= t('accounts.posts', count: account.statuses_count).downcase + .accounts-table__count + = number_to_human account.followers_count, strip_insignificant_zeros: true + %small= t('accounts.followers', count: account.followers_count).downcase + .accounts-table__count + - if account.last_status_at.present? + %time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at + - else + = t('accounts.never_active') + + %small= t('accounts.last_active') + + = paginate @accounts diff --git a/app/views/errors/400.html.haml b/app/views/errors/400.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..11fbdd40cf104e33ca048a7c1aca9116e35c97a5 --- /dev/null +++ b/app/views/errors/400.html.haml @@ -0,0 +1,5 @@ +- content_for :page_title do + = t('errors.400') + +- content_for :content do + = t('errors.400') diff --git a/app/views/errors/406.html.haml b/app/views/errors/406.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..0ef815df323b42bee8db3a53fb2afc1d473a02f6 --- /dev/null +++ b/app/views/errors/406.html.haml @@ -0,0 +1,5 @@ +- content_for :page_title do + = t('errors.406') + +- content_for :content do + = t('errors.406') diff --git a/app/views/errors/503.html.haml b/app/views/errors/503.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b0c895aa5d78cd98b165f3b32fcfc633a0849daf --- /dev/null +++ b/app/views/errors/503.html.haml @@ -0,0 +1,5 @@ +- content_for :page_title do + = t('errors.503') + +- content_for :content do + = t('errors.503') diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 4c7fac0b6ae3f7f5afb034211facec0addd74c2d..30c7aab194f3aa6365f1839ebe5634cfbc184051 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -5,8 +5,7 @@ = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) - + = render_initial_state = javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous' .app-holder#mastodon{ data: { props: Oj.dump(default_props) } } diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 083f2fac7644dd78f66538c4f30b7555f797f434..57bda45e23ac42a551fceb4d8200c0be2aab6701 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -4,11 +4,21 @@ - content_for :content do .admin-wrapper .sidebar-wrapper - .sidebar - = link_to root_path do - = image_pack_tag 'logo.svg', class: 'logo', alt: 'Mastodon' + .sidebar-wrapper__inner + .sidebar + = link_to root_path do + = image_pack_tag 'logo.svg', class: 'logo', alt: 'Mastodon' + + .sidebar__toggle + .sidebar__toggle__logo + = link_to root_path do + = svg_logo_full + + = link_to '#', class: 'sidebar__toggle__icon' do + = fa_icon 'bars' + + = render_navigation - = render_navigation .content-wrapper .content %h2= yield :page_title @@ -17,4 +27,6 @@ = yield + .sidebar-wrapper.sidebar-wrapper--empty + = render template: 'layouts/application' diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index 2929ac599cce8b7762c78d51c2bf873225659033..b9179e23d17c528ab0530800e64a14e500395d50 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -1,4 +1,5 @@ - content_for :header_tags do + = render_initial_state = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' - content_for :content do @@ -10,10 +11,13 @@ = link_to root_url, class: 'brand' do = svg_logo_full - = link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory - = link_to t('about.about_this'), about_more_path, class: 'nav-link optional' - = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional' + - unless whitelist_mode? + = link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory + = link_to t('about.about_this'), about_more_path, class: 'nav-link optional' + = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional' + .nav-center + .nav-right - if user_signed_in? = link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn' diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml index 57b5688bd6fffec7a95de1ddce00df5fe1687a5c..e992e5563dc82a51a6ccddf6811fbee4fa143c8e 100644 --- a/app/views/notification_mailer/_status.html.haml +++ b/app/views/notification_mailer/_status.html.haml @@ -1,4 +1,5 @@ - i ||= 0 +- highlighted ||= false %table.email-table{ cellspacing: 0, cellpadding: 0, dir: 'ltr' } %tbody @@ -14,7 +15,7 @@ %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.column-cell.padded.status + %td.column-cell.padded.status{ class: highlighted ? 'status--highlighted' : '' } %table.status-header{ cellspacing: 0, cellpadding: 0 } %tbody %tr @@ -32,5 +33,13 @@ %div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' } = Formatter.instance.format(status) + - if status.media_attachments.size > 0 + %p + - status.media_attachments.each do |a| + - if status.local? + = link_to medium_url(a), medium_url(a) + - else + = link_to a.remote_url, a.remote_url + %p.status-footer = link_to l(status.created_at), web_url("statuses/#{status.id}") diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index 19af5f55db28456bac549ecba8be2b5a249178f3..7203d758da4e90cfc3549538599da4fcc62455cd 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -17,7 +17,7 @@ = application.name - else = link_to application.name, application.website, target: '_blank', rel: 'noopener' - %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join('<br />') + %th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join(', ') %td= l application.created_at %td - unless application.superapp? diff --git a/app/views/public_timelines/show.html.haml b/app/views/public_timelines/show.html.haml index 913d5d855d2eb2e57d1daa20315775c158c7910f..07215efdfdf389dab83c03d409fcac9e3e06223f 100644 --- a/app/views/public_timelines/show.html.haml +++ b/app/views/public_timelines/show.html.haml @@ -3,7 +3,6 @@ - content_for :header_tags do %meta{ name: 'robots', content: 'noindex' }/ - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' .page-header diff --git a/app/views/remote_interaction/new.html.haml b/app/views/remote_interaction/new.html.haml index c8c08991f059b13075b0b7037b08b676447e3810..2cc0fcb93d6091c312eee8822003920d1a1e9f13 100644 --- a/app/views/remote_interaction/new.html.haml +++ b/app/views/remote_interaction/new.html.haml @@ -7,7 +7,7 @@ .public-layout .activity-stream.activity-stream--highlighted - = render 'stream_entries/status', status: @status + = render 'statuses/status', status: @status = simple_form_for @remote_follow, as: :remote_follow, url: remote_interaction_path(@status) do |f| = render 'shared/error_messages', object: @remote_follow diff --git a/app/views/remote_unfollows/_card.html.haml b/app/views/remote_unfollows/_card.html.haml deleted file mode 100644 index 9abcfd37e153ed2f18f79f05f722b50f34fa5d9f..0000000000000000000000000000000000000000 --- a/app/views/remote_unfollows/_card.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -.account-card - .detailed-status__display-name - %div - = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' - - %span.display-name - - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account) - = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do - %strong.emojify= display_name(account, custom_emojify: true) - %span @#{account.acct} - - - if account.note? - .account__header__content.emojify= Formatter.instance.simplified_format(account) diff --git a/app/views/remote_unfollows/_post_follow_actions.html.haml b/app/views/remote_unfollows/_post_follow_actions.html.haml deleted file mode 100644 index 2a9c062e9c78e3a4975e390ddef01f6a5bf57bc8..0000000000000000000000000000000000000000 --- a/app/views/remote_unfollows/_post_follow_actions.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.post-follow-actions - %div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@account.id}"), class: 'button button--block' - %div= link_to t('authorize_follow.post_follow.return'), TagManager.instance.url_for(@account), class: 'button button--block' - %div= t('authorize_follow.post_follow.close') diff --git a/app/views/remote_unfollows/error.html.haml b/app/views/remote_unfollows/error.html.haml deleted file mode 100644 index cb63f02be31a24c6d351cc12727539655857f528..0000000000000000000000000000000000000000 --- a/app/views/remote_unfollows/error.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -.form-container - .flash-message#error_explanation - = t('remote_unfollow.error') diff --git a/app/views/remote_unfollows/success.html.haml b/app/views/remote_unfollows/success.html.haml deleted file mode 100644 index b007eedc7e43c6f5de7295e8f3f904b08a5326ac..0000000000000000000000000000000000000000 --- a/app/views/remote_unfollows/success.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- content_for :page_title do - = t('remote_unfollow.title', acct: @account.acct) - -.form-container - .follow-prompt - %h2= t('remote_unfollow.unfollowed') - - = render 'application/card', account: @account - - = render 'post_follow_actions' diff --git a/app/views/settings/aliases/index.html.haml b/app/views/settings/aliases/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5b698636815f423e0aef794a491050ad03444626 --- /dev/null +++ b/app/views/settings/aliases/index.html.haml @@ -0,0 +1,29 @@ +- content_for :page_title do + = t('settings.aliases') + += simple_form_for @alias, url: settings_aliases_path do |f| + = render 'shared/error_messages', object: @alias + + %p.hint= t('aliases.hint_html') + + %hr.spacer/ + + .fields-group + = f.input :acct, wrapper: :with_block_label, input_html: { autocapitalize: 'none', autocorrect: 'off' } + + .actions + = f.button :button, t('aliases.add_new'), type: :submit, class: 'button' + +%hr.spacer/ + +.table-wrapper + %table.table.inline-table + %thead + %tr + %th= t('simple_form.labels.account_alias.acct') + %th + %tbody + - @aliases.each do |account_alias| + %tr + %td= account_alias.acct + %td= table_link_to 'trash', t('aliases.remove'), settings_alias_path(account_alias), data: { method: :delete } diff --git a/app/views/settings/deletes/show.html.haml b/app/views/settings/deletes/show.html.haml index b246f83a16eb3bb8d4d02b228529f391318af4db..08792e0afdefc9c3c73b30fd706593f46914896a 100644 --- a/app/views/settings/deletes/show.html.haml +++ b/app/views/settings/deletes/show.html.haml @@ -2,15 +2,28 @@ = t('settings.delete') = simple_form_for @confirmation, url: settings_delete_path, method: :delete do |f| - .warning - %strong - = fa_icon('warning') - = t('deletes.warning_title') - = t('deletes.warning_html') + %p.hint= t('deletes.warning.before') - %p.hint= t('deletes.description_html') + %ul.hint + - if current_user.confirmed? && current_user.approved? + %li.warning-hint= t('deletes.warning.irreversible') + %li.warning-hint= t('deletes.warning.username_unavailable') + %li.warning-hint= t('deletes.warning.data_removal') + %li.warning-hint= t('deletes.warning.caches') + - else + %li.positive-hint= t('deletes.warning.email_change_html', path: edit_user_registration_path) + %li.positive-hint= t('deletes.warning.email_reconfirmation_html', path: new_user_confirmation_path) + %li.positive-hint= t('deletes.warning.email_contact_html', email: Setting.site_contact_email) + %li.positive-hint= t('deletes.warning.username_available') - = f.input :password, placeholder: t('simple_form.labels.defaults.current_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password'), :autocomplete => 'off' }, hint: t('deletes.confirm_password') + %p.hint= t('deletes.warning.more_details_html', terms_path: terms_path) + + %hr.spacer/ + + - if current_user.encrypted_password.present? + = f.input :password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, hint: t('deletes.confirm_password') + - else + = f.input :username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, hint: t('deletes.confirm_username') .actions = f.button :button, t('deletes.proceed'), type: :submit, class: 'negative' diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml index b13cea976a1de6e1a4b20a6418259f033dd0053c..76ff76bd9c6550c08f4aab80bbdd9bf8d0abbc19 100644 --- a/app/views/settings/exports/show.html.haml +++ b/app/views/settings/exports/show.html.haml @@ -37,12 +37,16 @@ %td= number_with_delimiter @export.total_domain_blocks %td= table_link_to 'download', t('exports.csv'), settings_exports_domain_blocks_path(format: :csv) +%hr.spacer/ + %p.muted-hint= t('exports.archive_takeout.hint_html') - if policy(:backup).create? %p= link_to t('exports.archive_takeout.request'), settings_export_path, class: 'button', method: :post - unless @backups.empty? + %hr.spacer/ + .table-wrapper %table.table %thead diff --git a/app/views/settings/featured_tags/index.html.haml b/app/views/settings/featured_tags/index.html.haml index 5f69517f3af8bb5525ea9a93143cd35e0bd60b1d..6734d027c5409dbb9bdb5e3766680ce527b4dcc6 100644 --- a/app/views/settings/featured_tags/index.html.haml +++ b/app/views/settings/featured_tags/index.html.haml @@ -1,6 +1,10 @@ - content_for :page_title do = t('settings.featured_tags') +%p= t('featured_tags.hint_html') + +%hr.spacer/ + = simple_form_for @featured_tag, url: settings_featured_tags_path do |f| = render 'shared/error_messages', object: @featured_tag diff --git a/app/views/settings/migration/redirects/new.html.haml b/app/views/settings/migration/redirects/new.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..017450f4b94030b9418f3668d8f0a54452ae9789 --- /dev/null +++ b/app/views/settings/migration/redirects/new.html.haml @@ -0,0 +1,27 @@ +- content_for :page_title do + = t('settings.migrate') + += simple_form_for @redirect, url: settings_migration_redirect_path do |f| + %p.hint= t('migrations.warning.before') + + %ul.hint + %li.warning-hint= t('migrations.warning.redirect') + %li.warning-hint= t('migrations.warning.other_data') + %li.warning-hint= t('migrations.warning.disabled_account') + + %hr.spacer/ + + = render 'shared/error_messages', object: @redirect + + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :acct, wrapper: :with_block_label, input_html: { autocapitalize: 'none', autocorrect: 'off' }, label: t('simple_form.labels.account_migration.acct'), hint: t('simple_form.hints.account_migration.acct') + + .fields-row__column.fields-group.fields-row__column-6 + - if current_user.encrypted_password.present? + = f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true + - else + = f.input :current_username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true + + .actions + = f.button :button, t('migrations.set_redirect'), type: :submit, class: 'button button--destructive' diff --git a/app/views/settings/migrations/show.html.haml b/app/views/settings/migrations/show.html.haml index c69061d50bcd023a2b0c82e7ad6e5f253b1db4c7..078eaebc6c18525b513f8e7d0a3f904d24e95ea8 100644 --- a/app/views/settings/migrations/show.html.haml +++ b/app/views/settings/migrations/show.html.haml @@ -1,17 +1,89 @@ - content_for :page_title do = t('settings.migrate') -= simple_form_for @migration, as: :migration, url: settings_migration_path, html: { method: :put } do |f| - - if @migration.account - %p.hint= t('migrations.currently_redirecting') +.simple_form + - if current_account.moved_to_account.present? + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = render 'application/card', account: current_account.moved_to_account + .fields-row__column.fields-group.fields-row__column-6 + %p.hint + %span.positive-hint= t('migrations.redirecting_to', acct: current_account.moved_to_account.acct) - .fields-group - = render partial: 'application/card', locals: { account: @migration.account } + %p.hint= t('migrations.cancel_explanation') + + %p.hint= link_to t('migrations.cancel'), settings_migration_redirect_path, data: { method: :delete } + - else + %p.hint + %span.positive-hint= t('migrations.not_redirecting') + +%hr.spacer/ + +%h3= t('auth.migrate_account') + += simple_form_for @migration, url: settings_migration_path do |f| + - if on_cooldown? + %p.hint + %span.warning-hint= t('migrations.on_cooldown', count: ((@cooldown.cooldown_at - Time.now.utc) / 1.day.seconds).ceil) + - else + %p.hint= t('migrations.warning.before') + + %ul.hint + %li.warning-hint= t('migrations.warning.followers') + %li.warning-hint= t('migrations.warning.redirect') + %li.warning-hint= t('migrations.warning.other_data') + %li.warning-hint= t('migrations.warning.backreference_required') + %li.warning-hint= t('migrations.warning.cooldown') + %li.warning-hint= t('migrations.warning.disabled_account') + + %p.hint= t('migrations.warning.only_redirect_html', path: new_settings_migration_redirect_path) + + %hr.spacer/ = render 'shared/error_messages', object: @migration - .fields-group - = f.input :acct, placeholder: t('migrations.acct') + .fields-row + .fields-row__column.fields-group.fields-row__column-6 + = f.input :acct, wrapper: :with_block_label, input_html: { autocapitalize: 'none', autocorrect: 'off' }, disabled: on_cooldown? + + .fields-row__column.fields-group.fields-row__column-6 + - if current_user.encrypted_password.present? + = f.input :current_password, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true, disabled: on_cooldown? + - else + = f.input :current_username, wrapper: :with_block_label, input_html: { :autocomplete => 'off' }, required: true, disabled: on_cooldown? .actions - = f.button :button, t('migrations.proceed'), type: :submit, class: 'negative' + = f.button :button, t('migrations.proceed_with_move'), type: :submit, class: 'button button--destructive', disabled: on_cooldown? + +- unless @migrations.empty? + %hr.spacer/ + + %h3= t 'migrations.past_migrations' + + %hr.spacer/ + + .table-wrapper + %table.table.inline-table + %thead + %tr + %th= t('migrations.acct') + %th= t('migrations.followers_count') + %th + %tbody + - @migrations.each do |migration| + %tr + %td + - if migration.target_account.present? + = compact_account_link_to migration.target_account + - else + = migration.acct + + %td= number_with_delimiter migration.followers_count + + %td + %time.time-ago{ datetime: migration.created_at.iso8601, title: l(migration.created_at) }= l(migration.created_at) + +%hr.spacer/ + +%h3= t 'migrations.incoming_migrations' +%p.muted-hint= t('migrations.incoming_migrations_html', path: settings_aliases_path) diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml index 10f0092647a42a7b6cfee888e071110e59bc55fe..d6ee1933f86dbefcfaf96b35c8f86de725d18c45 100644 --- a/app/views/settings/preferences/appearance/show.html.haml +++ b/app/views/settings/preferences/appearance/show.html.haml @@ -17,11 +17,19 @@ %h4= t 'appearance.animations_and_accessibility' + .fields-group + = f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label + .fields-group = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label + %h4= t 'appearance.discovery' + + .fields-group + = f.input :setting_trends, as: :boolean, wrapper: :with_label + %h4= t 'appearance.confirmation_dialogs' .fields-group @@ -34,6 +42,9 @@ .fields-group = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label + .fields-group + = f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label + .fields-group = f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml index acc646fc3482cd534bd4c93711df805f93af8fa1..f666ae4ff6b1551b2c45d25f0ea2a38a6325f8f3 100644 --- a/app/views/settings/preferences/notifications/show.html.haml +++ b/app/views/settings/preferences/notifications/show.html.haml @@ -15,6 +15,7 @@ - if current_user.staff? = ff.input :report, as: :boolean, wrapper: :with_label = ff.input :pending_account, as: :boolean, wrapper: :with_label + = ff.input :trending_tag, as: :boolean, wrapper: :with_label .fields-group = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index f8a8fddd311e0035cf96faa294ac41e343372ad5..6929f54f30dbdff6f30c5e277b8b0464790194d2 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -28,7 +28,7 @@ - if Setting.profile_directory .fields-group - = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable_html', min_followers: Account::MIN_FOLLOWERS_DISCOVERY, path: explore_path), recommended: true + = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable'), recommended: true %hr.spacer/ @@ -60,6 +60,11 @@ %h6= t('auth.migrate_account') %p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path) +%hr.spacer/ + +%h6= t 'migrations.incoming_migrations' +%p.muted-hint= t('migrations.incoming_migrations_html', path: settings_aliases_path) + - if open_deletion? %hr.spacer/ diff --git a/app/views/settings/two_factor_authentication/confirmations/new.html.haml b/app/views/settings/two_factor_authentication/confirmations/new.html.haml index e641552991c0bb6ed494214c8482404f1b830f72..86cf1f695effc8880200268ec00c8b34a4c2bde2 100644 --- a/app/views/settings/two_factor_authentication/confirmations/new.html.haml +++ b/app/views/settings/two_factor_authentication/confirmations/new.html.haml @@ -12,7 +12,7 @@ %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ') .fields-group - = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true + = f.input :otp_attempt, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true .actions = f.button :button, t('two_factor_authentication.enable'), type: :submit diff --git a/app/views/settings/two_factor_authentications/show.html.haml b/app/views/settings/two_factor_authentications/show.html.haml index 259bcd1ef33aeb5256d8d50ff810b5d2c6aa751b..f1eecd0002d5ee4216a6884462fc751e643051b0 100644 --- a/app/views/settings/two_factor_authentications/show.html.haml +++ b/app/views/settings/two_factor_authentications/show.html.haml @@ -2,33 +2,35 @@ = t('settings.two_factor_authentication') - if current_user.otp_required_for_login - %p.positive-hint - = fa_icon 'check' - = ' ' - = t 'two_factor_authentication.enabled' + %p.hint + %span.positive-hint + = fa_icon 'check' + = ' ' + = t 'two_factor_authentication.enabled' - %hr/ + %hr.spacer/ = simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f| - = f.input :code, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true + .fields-group + = f.input :otp_attempt, wrapper: :with_block_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true .actions - = f.button :button, t('two_factor_authentication.disable'), type: :submit + = f.button :button, t('two_factor_authentication.disable'), type: :submit, class: 'negative' - %hr/ + %hr.spacer/ - %h6= t('two_factor_authentication.recovery_codes') - %p.muted-hint - = t('two_factor_authentication.lost_recovery_codes') - = link_to t('two_factor_authentication.generate_recovery_codes'), - settings_two_factor_authentication_recovery_codes_path, - data: { method: :post } + %h3= t('two_factor_authentication.recovery_codes') + %p.muted-hint= t('two_factor_authentication.lost_recovery_codes') + + %hr.spacer/ + + .simple_form + = link_to t('two_factor_authentication.generate_recovery_codes'), settings_two_factor_authentication_recovery_codes_path, data: { method: :post }, class: 'block-button' - else .simple_form %p.hint= t('two_factor_authentication.description_html') - = link_to t('two_factor_authentication.setup'), - settings_two_factor_authentication_path, - data: { method: :post }, - class: 'block-button' + %hr.spacer/ + + = link_to t('two_factor_authentication.setup'), settings_two_factor_authentication_path, data: { method: :post }, class: 'block-button' diff --git a/app/views/shared/_og.html.haml b/app/views/shared/_og.html.haml index 67238fc8bf892d3f2709ccb3875410defa21fcd0..c8f12974e29dff9b18b1945be5007f7ef3bc9108 100644 --- a/app/views/shared/_og.html.haml +++ b/app/views/shared/_og.html.haml @@ -1,5 +1,5 @@ -- thumbnail = @instance_presenter.thumbnail -- description = strip_tags(@instance_presenter.site_short_description.presence || @instance_presenter.site_description.presence || t('about.about_mastodon_html')) +- thumbnail = @instance_presenter.thumbnail +- description ||= strip_tags(@instance_presenter.site_short_description.presence || t('about.about_mastodon_html')) %meta{ name: 'description', content: description }/ diff --git a/app/views/shares/show.html.haml b/app/views/shares/show.html.haml index 44b6f145f68a639cf4bbee17e48005f5244a9e5b..f2f5479a79c4a6d9f4331d9f486e37d6ec189e10 100644 --- a/app/views/shares/show.html.haml +++ b/app/views/shares/show.html.haml @@ -1,5 +1,5 @@ - content_for :header_tags do - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) + = render_initial_state = javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous' #mastodon-compose{ data: { props: Oj.dump(default_props) } } diff --git a/app/views/stream_entries/_attachment_list.html.haml b/app/views/statuses/_attachment_list.html.haml similarity index 100% rename from app/views/stream_entries/_attachment_list.html.haml rename to app/views/statuses/_attachment_list.html.haml diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml similarity index 78% rename from app/views/stream_entries/_detailed_status.html.haml rename to app/views/statuses/_detailed_status.html.haml index 78367d82bab1276dc5aa184b6c0324e5feeceb0c..e6ea6c2a7d42a3edb8989e5454bb34f6db2a4c53 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -1,6 +1,6 @@ .detailed-status.detailed-status--flex .p-author.h-card - = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name u-url', target: stream_link_target, rel: 'noopener' do + = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'detailed-status__display-name u-url', target: stream_link_target, rel: 'noopener' do .detailed-status__display-avatar - if current_account&.user&.setting_auto_play_gif || autoplay = image_tag status.account.avatar_original_url, width: 48, height: 48, alt: '', class: 'account__avatar u-photo' @@ -20,27 +20,31 @@ %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)} %button.status__content__spoiler-link= t('statuses.show_more') - .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } + .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.preloadable_poll = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do - = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } + = render partial: 'statuses/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } - if !status.media_attachments.empty? - - if status.media_attachments.first.audio_or_video? + - if status.media_attachments.first.video? - video = status.media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do - = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments } + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + - elsif status.media_attachments.first.audio? + - audio = status.media_attachments.first + = react_component :audio, src: audio.file.url(:original), height: 130, alt: audio.description, preload: true, duration: audio.file.meta.dig(:original, :duration) do + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else = react_component :media_gallery, height: 380, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do - = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments } + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } - = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener' do + = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener' do %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) · - if status.application && @account.user&.setting_show_application diff --git a/app/views/stream_entries/_og_description.html.haml b/app/views/statuses/_og_description.html.haml similarity index 100% rename from app/views/stream_entries/_og_description.html.haml rename to app/views/statuses/_og_description.html.haml diff --git a/app/views/stream_entries/_og_image.html.haml b/app/views/statuses/_og_image.html.haml similarity index 100% rename from app/views/stream_entries/_og_image.html.haml rename to app/views/statuses/_og_image.html.haml diff --git a/app/views/stream_entries/_poll.html.haml b/app/views/statuses/_poll.html.haml similarity index 53% rename from app/views/stream_entries/_poll.html.haml rename to app/views/statuses/_poll.html.haml index ba34890dfee5e46b95940fa8e5a32599a80bd86f..d1aba6ef9cee1a7f9abe60342e56dba5631fa6e5 100644 --- a/app/views/stream_entries/_poll.html.haml +++ b/app/views/statuses/_poll.html.haml @@ -1,15 +1,20 @@ - show_results = (user_signed_in? && poll.voted?(current_account)) || poll.expired? +- own_votes = user_signed_in? ? poll.own_votes(current_account) : [] +- total_votes_count = poll.voters_count || poll.votes_count .poll %ul - - poll.loaded_options.each do |option| + - poll.loaded_options.each_with_index do |option, index| %li - if show_results - - percent = poll.votes_count > 0 ? 100 * option.votes_count / poll.votes_count : 0 + - percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0 %span.poll__chart{ style: "width: #{percent}%" } %label.poll__text>< - %span.poll__number= percent.round + %span.poll__number>< + - if own_votes.include?(index) + %i.poll__vote__mark.fa.fa-check + = percent.round = Formatter.instance.format_poll_option(status, option, autoplay: autoplay) - else %label.poll__text>< @@ -20,7 +25,10 @@ %button.button.button-secondary{ disabled: true } = t('statuses.poll.vote') - %span= t('statuses.poll.total_votes', count: poll.votes_count) + - if poll.voters_count.nil? + %span= t('statuses.poll.total_votes', count: poll.votes_count) + - else + %span= t('statuses.poll.total_people', count: poll.voters_count) - unless poll.expires_at.nil? · diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml similarity index 74% rename from app/views/stream_entries/_simple_status.html.haml rename to app/views/statuses/_simple_status.html.haml index 0381f8d781a861eddf71002093f4ec0a74126b05..9b89e30a92db5b6bf02865b01ea54ee81ea4242e 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -1,11 +1,11 @@ .status .status__info - = link_to TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do + = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener' do %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) %data.dt-published{ value: status.created_at.to_time.iso8601 } .p-author.h-card - = link_to TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener' do + = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener' do .status__avatar %div - if current_account&.user&.setting_auto_play_gif || autoplay @@ -24,20 +24,24 @@ %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }< %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)} %button.status__content__spoiler-link= t('statuses.show_more') - .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } + .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" } = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.preloadable_poll = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do - = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } + = render partial: 'statuses/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay } - if !status.media_attachments.empty? - - if status.media_attachments.first.audio_or_video? + - if status.media_attachments.first.video? - video = status.media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do - = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments } + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } + - elsif status.media_attachments.first.audio? + - audio = status.media_attachments.first + = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration) do + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do - = render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments } + = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json @@ -50,9 +54,9 @@ = fa_icon 'reply-all fw' .status__action-bar__counter__label= obscured_counter status.replies_count = link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do - - if status.public_visibility? || status.unlisted_visibility? + - if status.distributable? = fa_icon 'retweet fw' - - elsif status.private_visibility? + - elsif status.private_visibility? || status.limited_visibility? = fa_icon 'lock fw' - else = fa_icon 'envelope fw' diff --git a/app/views/stream_entries/_status.html.haml b/app/views/statuses/_status.html.haml similarity index 74% rename from app/views/stream_entries/_status.html.haml rename to app/views/statuses/_status.html.haml index 83887cd87437f7a73548a5e69062c5cd3038f5da..0e3652503067836203082446e3c3e125ee072f43 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -17,9 +17,9 @@ - if status.reply? && include_threads - if @next_ancestor .entry{ class: entry_classes } - = link_to_more TagManager.instance.url_for(@next_ancestor) + = link_to_more ActivityPub::TagManager.instance.url_for(@next_ancestor) - = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay + = render partial: 'statuses/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay .entry{ class: entry_classes } @@ -28,7 +28,7 @@ .status__prepend-icon-wrapper %i.status__prepend-icon.fa.fa-fw.fa-retweet %span - = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do + = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name muted' do %bdi %strong.emojify= display_name(status.account, custom_emojify: true) = t('stream_entries.reblogged') @@ -39,18 +39,18 @@ %span = t('stream_entries.pinned') - = render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper, autoplay: autoplay + = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay - if include_threads - if @since_descendant_thread_id .entry{ class: entry_classes } = link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) - @descendant_threads.each do |thread| - = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay + = render partial: 'statuses/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay - if thread[:next_status] .entry{ class: entry_classes } - = link_to_more TagManager.instance.url_for(thread[:next_status]) + = link_to_more ActivityPub::TagManager.instance.url_for(thread[:next_status]) - if @next_descendant_thread .entry{ class: entry_classes } = link_to_more short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) diff --git a/app/views/statuses/embed.html.haml b/app/views/statuses/embed.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..6f2ec646fe66704529189e126fcb338e17791094 --- /dev/null +++ b/app/views/statuses/embed.html.haml @@ -0,0 +1,3 @@ +- cache @status do + .activity-stream.activity-stream--headless + = render 'status', status: @status, centered: true, autoplay: @autoplay diff --git a/app/views/stream_entries/embed.html.haml b/app/views/stream_entries/embed.html.haml deleted file mode 100644 index 4871c101e1cbf38e2f6fc574d8850fdd3fab787e..0000000000000000000000000000000000000000 --- a/app/views/stream_entries/embed.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- cache @stream_entry.activity do - .activity-stream.activity-stream--headless - = render "stream_entries/#{@type}", @type.to_sym => @stream_entry.activity, centered: true, autoplay: @autoplay diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml deleted file mode 100644 index 1a6567512493a6f6f678e9fbf2aba884c4aeba4d..0000000000000000000000000000000000000000 --- a/app/views/stream_entries/show.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -- content_for :page_title do - = t('statuses.title', name: display_name(@account), quote: truncate(@stream_entry.activity.spoiler_text.presence || @stream_entry.activity.text, length: 50, omission: '…', escape: false)) - -- content_for :header_tags do - - if @account.user&.setting_noindex - %meta{ name: 'robots', content: 'noindex, noarchive' }/ - - %link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/ - %link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/ - %link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@stream_entry.activity) }/ - - = opengraph 'og:site_name', site_title - = opengraph 'og:type', 'article' - = opengraph 'og:title', "#{display_name(@account)} (@#{@account.local_username_and_domain})" - = opengraph 'og:url', short_account_status_url(@account, @stream_entry.activity) - - = render 'stream_entries/og_description', activity: @stream_entry.activity - = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account - -.grid - .column-0 - .activity-stream.h-entry - = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true } - .column-1 - = render 'application/sidebar' diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index cf42468224fa801c3c3e0b94092ef0b551d40ba9..6307022779422530707c1adef13dfcd3d130d169 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -5,7 +5,6 @@ %meta{ name: 'robots', content: 'noindex' }/ %link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/ - %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) = javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous' = render 'og' diff --git a/app/views/user_mailer/two_factor_disabled.html.haml b/app/views/user_mailer/two_factor_disabled.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..651c6f940e0dea9bb9da5f5b851652881149fd16 --- /dev/null +++ b/app/views/user_mailer/two_factor_disabled.html.haml @@ -0,0 +1,43 @@ +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.hero + .email-row + .col-6 + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.text-center.padded + %table.hero-icon.alert-icon{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td + = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: '' + + %h1= t 'devise.mailer.two_factor_disabled.title' + %p.lead= t 'devise.mailer.two_factor_disabled.explanation' + +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.content-start + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.button-cell + %table.button{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.button-primary + = link_to edit_user_registration_url do + %span= t('settings.account_settings') diff --git a/app/views/user_mailer/two_factor_disabled.text.erb b/app/views/user_mailer/two_factor_disabled.text.erb new file mode 100644 index 0000000000000000000000000000000000000000..73be1ddc2618d92fa06d9dba4ef968390328c5dc --- /dev/null +++ b/app/views/user_mailer/two_factor_disabled.text.erb @@ -0,0 +1,7 @@ +<%= t 'devise.mailer.two_factor_disabled.title' %> + +=== + +<%= t 'devise.mailer.two_factor_disabled.explanation' %> + +=> <%= edit_user_registration_url %> diff --git a/app/views/user_mailer/two_factor_enabled.html.haml b/app/views/user_mailer/two_factor_enabled.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..fc31bd979f50206f5422243494912b7887c07f1c --- /dev/null +++ b/app/views/user_mailer/two_factor_enabled.html.haml @@ -0,0 +1,43 @@ +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.hero + .email-row + .col-6 + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.text-center.padded + %table.hero-icon{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td + = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: '' + + %h1= t 'devise.mailer.two_factor_enabled.title' + %p.lead= t 'devise.mailer.two_factor_enabled.explanation' + +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.content-start + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.button-cell + %table.button{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.button-primary + = link_to edit_user_registration_url do + %span= t('settings.account_settings') diff --git a/app/views/user_mailer/two_factor_enabled.text.erb b/app/views/user_mailer/two_factor_enabled.text.erb new file mode 100644 index 0000000000000000000000000000000000000000..4319dddbfc49559563a2121d021a921e8dd75544 --- /dev/null +++ b/app/views/user_mailer/two_factor_enabled.text.erb @@ -0,0 +1,7 @@ +<%= t 'devise.mailer.two_factor_enabled.title' %> + +=== + +<%= t 'devise.mailer.two_factor_enabled.explanation' %> + +=> <%= edit_user_registration_url %> diff --git a/app/views/user_mailer/two_factor_recovery_codes_changed.html.haml b/app/views/user_mailer/two_factor_recovery_codes_changed.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..833708868badd5f871b4d8a22cd82c2acfef60ab --- /dev/null +++ b/app/views/user_mailer/two_factor_recovery_codes_changed.html.haml @@ -0,0 +1,43 @@ +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.hero + .email-row + .col-6 + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.text-center.padded + %table.hero-icon.alert-icon{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td + = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: '' + + %h1= t 'devise.mailer.two_factor_recovery_codes_changed.title' + %p.lead= t 'devise.mailer.two_factor_recovery_codes_changed.explanation' + +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell.content-start + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.button-cell + %table.button{ align: 'center', cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.button-primary + = link_to edit_user_registration_url do + %span= t('settings.account_settings') diff --git a/app/views/user_mailer/two_factor_recovery_codes_changed.text.erb b/app/views/user_mailer/two_factor_recovery_codes_changed.text.erb new file mode 100644 index 0000000000000000000000000000000000000000..6ed12fc08a20315fa1997941ea334dc1d269f2a7 --- /dev/null +++ b/app/views/user_mailer/two_factor_recovery_codes_changed.text.erb @@ -0,0 +1,7 @@ +<%= t 'devise.mailer.two_factor_recovery_codes_changed.title' %> + +=== + +<%= t 'devise.mailer.two_factor_recovery_codes_changed.explanation' %> + +=> <%= edit_user_registration_url %> diff --git a/app/views/user_mailer/warning.html.haml b/app/views/user_mailer/warning.html.haml index 72ea5e5d28f935650c2f886fcdceda46d2ac8a56..5a2911ecba574e3ab7808089df7c7527ed267a45 100644 --- a/app/views/user_mailer/warning.html.haml +++ b/app/views/user_mailer/warning.html.haml @@ -42,6 +42,14 @@ - unless @warning.text.blank? = Formatter.instance.linkify(@warning.text) + - if !@statuses.nil? && !@statuses.empty? + %p + %strong= t('user_mailer.warning.statuses') + +- if !@statuses.nil? && !@statuses.empty? + - @statuses.each_with_index do |status, i| + = render 'notification_mailer/status', status: status, i: i + 1, highlighted: true + %table.email-table{ cellspacing: 0, cellpadding: 0 } %tbody %tr @@ -50,7 +58,7 @@ %table.content-section{ cellspacing: 0, cellpadding: 0 } %tbody %tr - %td.content-cell + %td.content-cell{ class: @statuses.nil? || @statuses.empty? ? '' : 'content-start' } %table.column{ cellspacing: 0, cellpadding: 0 } %tbody %tr @@ -61,3 +69,20 @@ %td.button-primary = link_to about_more_url do %span= t 'user_mailer.warning.review_server_policies' + +%table.email-table{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.email-body + .email-container + %table.content-section{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.content-cell + .email-row + .col-6 + %table.column{ cellspacing: 0, cellpadding: 0 } + %tbody + %tr + %td.column-cell.text-center + %p= t 'user_mailer.warning.get_in_touch', instance: @instance diff --git a/app/views/user_mailer/warning.text.erb b/app/views/user_mailer/warning.text.erb index b4f2402cb3743e0d2f237f3e23e4bfcb1f3473d9..bb6610c79bd2476a71b6ce510b87c0f7137d44e2 100644 --- a/app/views/user_mailer/warning.text.erb +++ b/app/views/user_mailer/warning.text.erb @@ -7,3 +7,16 @@ <% end %> <%= @warning.text %> +<% if !@statuses.nil? && !@statuses.empty? %> +<%= t('user_mailer.warning.statuses') %> + +<% @statuses.each do |status| %> + +<%= render 'notification_mailer/status', status: status %> +--- +<% end %> +<% else %> +--- +<% end %> + +<%= t 'user_mailer.warning.get_in_touch', instance: @instance %> diff --git a/app/views/well_known/webfinger/show.xml.ruby b/app/views/well_known/webfinger/show.xml.ruby deleted file mode 100644 index 968c8c1380c0d70543e60ce8f9348e18a5f1735d..0000000000000000000000000000000000000000 --- a/app/views/well_known/webfinger/show.xml.ruby +++ /dev/null @@ -1,44 +0,0 @@ -doc = Ox::Document.new(version: '1.0') - -doc << Ox::Element.new('XRD').tap do |xrd| - xrd['xmlns'] = 'http://docs.oasis-open.org/ns/xri/xrd-1.0' - - xrd << (Ox::Element.new('Subject') << @account.to_webfinger_s) - xrd << (Ox::Element.new('Alias') << short_account_url(@account)) - xrd << (Ox::Element.new('Alias') << account_url(@account)) - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'http://webfinger.net/rel/profile-page' - link['type'] = 'text/html' - link['href'] = short_account_url(@account) - end - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'http://schemas.google.com/g/2010#updates-from' - link['type'] = 'application/atom+xml' - link['href'] = account_url(@account, format: 'atom') - end - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'self' - link['type'] = 'application/activity+json' - link['href'] = account_url(@account) - end - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'salmon' - link['href'] = api_salmon_url(@account.id) - end - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'magic-public-key' - link['href'] = "data:application/magic-public-key,#{@account.magic_key}" - end - - xrd << Ox::Element.new('Link').tap do |link| - link['rel'] = 'http://ostatus.org/schema/1.0/subscribe' - link['template'] = "#{authorize_interaction_url}?acct={uri}" - end -end - -('<?xml version="1.0" encoding="UTF-8"?>' + Ox.dump(doc, effort: :tolerant)).force_encoding('UTF-8') diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb index 00b5c6b7e7fac89174b41afa455236b930669932..5457d9d4b05a8aab344ebaa4eed248c3530bd226 100644 --- a/app/workers/activitypub/delivery_worker.rb +++ b/app/workers/activitypub/delivery_worker.rb @@ -2,6 +2,7 @@ class ActivityPub::DeliveryWorker include Sidekiq::Worker + include JsonLdHelper STOPLIGHT_FAILURE_THRESHOLD = 10 STOPLIGHT_COOLDOWN = 60 @@ -17,27 +18,35 @@ class ActivityPub::DeliveryWorker @json = json @source_account = Account.find(source_account_id) @inbox_url = inbox_url + @host = Addressable::URI.parse(inbox_url).normalized_site + @performed = false perform_request - - failure_tracker.track_success! - rescue => e - failure_tracker.track_failure! - raise e.class, "Delivery failed for #{inbox_url}: #{e.message}", e.backtrace[0] + ensure + if @performed + failure_tracker.track_success! + else + failure_tracker.track_failure! + end end private - def build_request - request = Request.new(:post, @inbox_url, body: @json) - request.on_behalf_of(@source_account, :uri, sign_with: @options[:sign_with]) - request.add_headers(HEADERS) + def build_request(http_client) + Request.new(:post, @inbox_url, body: @json, http_client: http_client).tap do |request| + request.on_behalf_of(@source_account, :uri, sign_with: @options[:sign_with]) + request.add_headers(HEADERS) + end end def perform_request light = Stoplight(@inbox_url) do - build_request.perform do |response| - raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) + request_pool.with(@host) do |http_client| + build_request(http_client).perform do |response| + raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) + + @performed = true + end end end @@ -46,15 +55,11 @@ class ActivityPub::DeliveryWorker .run end - def response_successful?(response) - (200...300).cover?(response.code) - end - - def response_error_unsalvageable?(response) - response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code)) - end - def failure_tracker @failure_tracker ||= DeliveryFailureTracker.new(@inbox_url) end + + def request_pool + RequestPool.current + end end diff --git a/app/workers/activitypub/move_distribution_worker.rb b/app/workers/activitypub/move_distribution_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..bf1c0e7ae467ebd82282ef24dd6d8d3251242497 --- /dev/null +++ b/app/workers/activitypub/move_distribution_worker.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class ActivityPub::MoveDistributionWorker + include Sidekiq::Worker + include Payloadable + + sidekiq_options queue: 'push' + + def perform(migration_id) + @migration = AccountMigration.find(migration_id) + @account = @migration.account + + ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url| + [signed_payload, @account.id, inbox_url] + end + + ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url| + [signed_payload, @account.id, inbox_url] + end + rescue ActiveRecord::RecordNotFound + true + end + + private + + def inboxes + @inboxes ||= @migration.account.followers.inboxes + end + + def signed_payload + @signed_payload ||= Oj.dump(serialize_payload(@migration, ActivityPub::MoveSerializer, signer: @account)) + end +end diff --git a/app/workers/admin/suspension_worker.rb b/app/workers/admin/suspension_worker.rb index ae8b24d8c3e34823caf1cbfc0ab2b7c083d6cb5c..83c815efd7c1ed624248b0b2213a9688a2aed2aa 100644 --- a/app/workers/admin/suspension_worker.rb +++ b/app/workers/admin/suspension_worker.rb @@ -6,6 +6,6 @@ class Admin::SuspensionWorker sidekiq_options queue: 'pull' def perform(account_id, remove_user = false) - SuspendAccountService.new.call(Account.find(account_id), including_user: remove_user) + SuspendAccountService.new.call(Account.find(account_id), reserve_username: true, reserve_email: !remove_user) end end diff --git a/app/workers/after_remote_follow_request_worker.rb b/app/workers/after_remote_follow_request_worker.rb index 84eb6ade23171f59780787f8440ef21fe7a7cbd2..ce9c65834c399d2b368beac36803adcfab9045e2 100644 --- a/app/workers/after_remote_follow_request_worker.rb +++ b/app/workers/after_remote_follow_request_worker.rb @@ -5,27 +5,5 @@ class AfterRemoteFollowRequestWorker sidekiq_options queue: 'pull', retry: 5 - attr_reader :follow_request - - def perform(follow_request_id) - @follow_request = FollowRequest.find(follow_request_id) - process_follow_service if processing_required? - rescue ActiveRecord::RecordNotFound - true - end - - private - - def process_follow_service - follow_request.destroy - FollowService.new.call(follow_request.account, updated_account.acct) - end - - def processing_required? - !updated_account.nil? && !updated_account.locked? - end - - def updated_account - @_updated_account ||= FetchRemoteAccountService.new.call(follow_request.target_account.remote_url) - end + def perform(follow_request_id); end end diff --git a/app/workers/after_remote_follow_worker.rb b/app/workers/after_remote_follow_worker.rb index edab83f853a6b66fc3640eae5c5c2cae1d91f02e..d9719f2bf8af418e6ddd315aaed30b117d05bbdd 100644 --- a/app/workers/after_remote_follow_worker.rb +++ b/app/workers/after_remote_follow_worker.rb @@ -5,27 +5,5 @@ class AfterRemoteFollowWorker sidekiq_options queue: 'pull', retry: 5 - attr_reader :follow - - def perform(follow_id) - @follow = Follow.find(follow_id) - process_follow_service if processing_required? - rescue ActiveRecord::RecordNotFound - true - end - - private - - def process_follow_service - follow.destroy - FollowService.new.call(follow.account, updated_account.acct) - end - - def updated_account - @_updated_account ||= FetchRemoteAccountService.new.call(follow.target_account.remote_url) - end - - def processing_required? - !updated_account.nil? && updated_account.locked? - end + def perform(follow_id); end end diff --git a/app/workers/domain_block_worker.rb b/app/workers/domain_block_worker.rb index 8844778291d41bf96b100f3f09e83a63bd8c94a9..35518d6b5e8c687c42abd21f8eded36385011a6d 100644 --- a/app/workers/domain_block_worker.rb +++ b/app/workers/domain_block_worker.rb @@ -3,8 +3,8 @@ class DomainBlockWorker include Sidekiq::Worker - def perform(domain_block_id) - BlockDomainService.new.call(DomainBlock.find(domain_block_id)) + def perform(domain_block_id, update = false) + BlockDomainService.new.call(DomainBlock.find(domain_block_id), update) rescue ActiveRecord::RecordNotFound true end diff --git a/app/workers/maintenance/destroy_media_worker.rb b/app/workers/maintenance/destroy_media_worker.rb deleted file mode 100644 index cde33d6d79a53df409cdb49c72ff7ea10e3e9351..0000000000000000000000000000000000000000 --- a/app/workers/maintenance/destroy_media_worker.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class Maintenance::DestroyMediaWorker - include Sidekiq::Worker - - sidekiq_options queue: 'pull' - - def perform(media_attachment_id) - media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id) - media.destroy - rescue ActiveRecord::RecordNotFound - true - end -end diff --git a/app/workers/maintenance/redownload_account_media_worker.rb b/app/workers/maintenance/redownload_account_media_worker.rb deleted file mode 100644 index 6afbe6e19cef35261697b5e92be3e1d438e36717..0000000000000000000000000000000000000000 --- a/app/workers/maintenance/redownload_account_media_worker.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -class Maintenance::RedownloadAccountMediaWorker - include Sidekiq::Worker - - sidekiq_options queue: 'pull', retry: false - - def perform(account_id) - account = account_id.is_a?(Account) ? account_id : Account.find(account_id) - account.reset_avatar! - account.reset_header! - account.save - rescue ActiveRecord::RecordNotFound - true - end -end diff --git a/app/workers/maintenance/uncache_media_worker.rb b/app/workers/maintenance/uncache_media_worker.rb deleted file mode 100644 index 4bc62ef755c30e712420a494fd71a82fcf623b09..0000000000000000000000000000000000000000 --- a/app/workers/maintenance/uncache_media_worker.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class Maintenance::UncacheMediaWorker - include Sidekiq::Worker - - sidekiq_options queue: 'pull' - - def perform(media_attachment_id) - media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id) - - return if media.file.blank? - - media.file.destroy - media.save - rescue ActiveRecord::RecordNotFound - true - end -end diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..83f7090eec1198f0d1d9472524304e769171cadb --- /dev/null +++ b/app/workers/move_worker.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class MoveWorker + include Sidekiq::Worker + + def perform(source_account_id, target_account_id) + @source_account = Account.find(source_account_id) + @target_account = Account.find(target_account_id) + + if @target_account.local? + rewrite_follows! + else + queue_follow_unfollows! + end + rescue ActiveRecord::RecordNotFound + true + end + + private + + def rewrite_follows! + @source_account.passive_relationships + .where(account: Account.local) + .in_batches + .update_all(target_account_id: @target_account.id) + end + + def queue_follow_unfollows! + @source_account.followers.local.select(:id).find_in_batches do |accounts| + UnfollowFollowWorker.push_bulk(accounts.map(&:id)) { |follower_id| [follower_id, @source_account.id, @target_account.id] } + end + end +end diff --git a/app/workers/notification_worker.rb b/app/workers/notification_worker.rb index da1d6ab455cbba498997f807bdd266857a26b702..1c0f001cf20fb9dedc084ce8e7272859f2352025 100644 --- a/app/workers/notification_worker.rb +++ b/app/workers/notification_worker.rb @@ -5,7 +5,5 @@ class NotificationWorker sidekiq_options queue: 'push', retry: 5 - def perform(xml, source_account_id, target_account_id) - SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id)) - end + def perform(xml, source_account_id, target_account_id); end end diff --git a/app/workers/processing_worker.rb b/app/workers/processing_worker.rb index 978c3aba26f2d0238fe0266868ca91ff6fc84d54..cf3bd83979a8c3182ad6e5906e3c2ccccf5a4ea4 100644 --- a/app/workers/processing_worker.rb +++ b/app/workers/processing_worker.rb @@ -5,7 +5,5 @@ class ProcessingWorker sidekiq_options backtrace: true - def perform(account_id, body) - ProcessFeedService.new.call(body, Account.find(account_id), override_timestamps: true) - end + def perform(account_id, body); end end diff --git a/app/workers/pubsubhubbub/confirmation_worker.rb b/app/workers/pubsubhubbub/confirmation_worker.rb index c0e7b677e41e0f2255bd63bba2e8bbdbc269e893..783a8c95fb0ffb70e1467845a53b62b695ed78d9 100644 --- a/app/workers/pubsubhubbub/confirmation_worker.rb +++ b/app/workers/pubsubhubbub/confirmation_worker.rb @@ -2,81 +2,8 @@ class Pubsubhubbub::ConfirmationWorker include Sidekiq::Worker - include RoutingHelper sidekiq_options queue: 'push', retry: false - attr_reader :subscription, :mode, :secret, :lease_seconds - - def perform(subscription_id, mode, secret = nil, lease_seconds = nil) - @subscription = Subscription.find(subscription_id) - @mode = mode - @secret = secret - @lease_seconds = lease_seconds - process_confirmation - end - - private - - def process_confirmation - prepare_subscription - - callback_get_with_params - logger.debug "Confirming PuSH subscription for #{subscription.callback_url} with challenge #{challenge}: #{@callback_response_body}" - - update_subscription - end - - def update_subscription - if successful_subscribe? - subscription.save! - elsif successful_unsubscribe? - subscription.destroy! - end - end - - def successful_subscribe? - subscribing? && response_matches_challenge? - end - - def successful_unsubscribe? - (unsubscribing? && response_matches_challenge?) || !subscription.confirmed? - end - - def response_matches_challenge? - @callback_response_body == challenge - end - - def subscribing? - mode == 'subscribe' - end - - def unsubscribing? - mode == 'unsubscribe' - end - - def callback_get_with_params - Request.new(:get, subscription.callback_url, params: callback_params).perform do |response| - @callback_response_body = response.body_with_limit - end - end - - def callback_params - { - 'hub.topic': account_url(subscription.account, format: :atom), - 'hub.mode': mode, - 'hub.challenge': challenge, - 'hub.lease_seconds': subscription.lease_seconds, - } - end - - def prepare_subscription - subscription.secret = secret - subscription.lease_seconds = lease_seconds - subscription.confirmed = true - end - - def challenge - @_challenge ||= SecureRandom.hex - end + def perform(subscription_id, mode, secret = nil, lease_seconds = nil); end end diff --git a/app/workers/pubsubhubbub/delivery_worker.rb b/app/workers/pubsubhubbub/delivery_worker.rb index 619bfa48aad215c36f357114e608c62d8cbee336..1260060bd5543cb3e91db9bc7810994cad0e6400 100644 --- a/app/workers/pubsubhubbub/delivery_worker.rb +++ b/app/workers/pubsubhubbub/delivery_worker.rb @@ -2,80 +2,8 @@ class Pubsubhubbub::DeliveryWorker include Sidekiq::Worker - include RoutingHelper sidekiq_options queue: 'push', retry: 3, dead: false - sidekiq_retry_in do |count| - 5 * (count + 1) - end - - attr_reader :subscription, :payload - - def perform(subscription_id, payload) - @subscription = Subscription.find(subscription_id) - @payload = payload - process_delivery unless blocked_domain? - rescue => e - raise e.class, "Delivery failed for #{subscription&.callback_url}: #{e.message}", e.backtrace[0] - end - - private - - def process_delivery - callback_post_payload do |payload_delivery| - raise Mastodon::UnexpectedResponseError, payload_delivery unless response_successful? payload_delivery - end - - subscription.touch(:last_successful_delivery_at) - end - - def callback_post_payload(&block) - request = Request.new(:post, subscription.callback_url, body: payload) - request.add_headers(headers) - request.perform(&block) - end - - def blocked_domain? - DomainBlock.blocked?(host) - end - - def host - Addressable::URI.parse(subscription.callback_url).normalized_host - end - - def headers - { - 'Content-Type' => 'application/atom+xml', - 'Link' => link_header, - }.merge(signature_headers.to_h) - end - - def link_header - LinkHeader.new([hub_link_header, self_link_header]).to_s - end - - def hub_link_header - [api_push_url, [%w(rel hub)]] - end - - def self_link_header - [account_url(subscription.account, format: :atom), [%w(rel self)]] - end - - def signature_headers - { 'X-Hub-Signature' => payload_signature } if subscription.secret? - end - - def payload_signature - "sha1=#{hmac_payload_digest}" - end - - def hmac_payload_digest - OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret, payload) - end - - def response_successful?(payload_delivery) - payload_delivery.code > 199 && payload_delivery.code < 300 - end + def perform(subscription_id, payload); end end diff --git a/app/workers/pubsubhubbub/distribution_worker.rb b/app/workers/pubsubhubbub/distribution_worker.rb index fed5e917d3dd2bd54a7b6634d31b8b4360fc39f6..75bac5d6fadbc7252b54f7ca8f80b11f182bfe4b 100644 --- a/app/workers/pubsubhubbub/distribution_worker.rb +++ b/app/workers/pubsubhubbub/distribution_worker.rb @@ -5,28 +5,5 @@ class Pubsubhubbub::DistributionWorker sidekiq_options queue: 'push' - def perform(stream_entry_ids) - stream_entries = StreamEntry.where(id: stream_entry_ids).includes(:status).reject { |e| e.status.nil? || e.status.hidden? } - - return if stream_entries.empty? - - @account = stream_entries.first.account - @subscriptions = active_subscriptions.to_a - - distribute_public!(stream_entries) - end - - private - - def distribute_public!(stream_entries) - @payload = OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, stream_entries)) - - Pubsubhubbub::DeliveryWorker.push_bulk(@subscriptions) do |subscription_id| - [subscription_id, @payload] - end - end - - def active_subscriptions - Subscription.where(account: @account).active.pluck(:id) - end + def perform(stream_entry_ids); end end diff --git a/app/workers/pubsubhubbub/raw_distribution_worker.rb b/app/workers/pubsubhubbub/raw_distribution_worker.rb index 16962a623ce72ad3d3381cec93a0711dbc4dc84a..ece9c80ac5d0bee1834ddd168eaa14d1b4426ab6 100644 --- a/app/workers/pubsubhubbub/raw_distribution_worker.rb +++ b/app/workers/pubsubhubbub/raw_distribution_worker.rb @@ -5,18 +5,5 @@ class Pubsubhubbub::RawDistributionWorker sidekiq_options queue: 'push' - def perform(xml, source_account_id) - @account = Account.find(source_account_id) - @subscriptions = active_subscriptions.to_a - - Pubsubhubbub::DeliveryWorker.push_bulk(@subscriptions) do |subscription| - [subscription.id, xml] - end - end - - private - - def active_subscriptions - Subscription.where(account: @account).active.select('id, callback_url, domain') - end + def perform(xml, source_account_id); end end diff --git a/app/workers/pubsubhubbub/subscribe_worker.rb b/app/workers/pubsubhubbub/subscribe_worker.rb index 2e176d1c106f3d8f7a8f5d8de8ec56c89b6bd49e..b861b5e67a1856d74fac071e537b97e40e4bcb50 100644 --- a/app/workers/pubsubhubbub/subscribe_worker.rb +++ b/app/workers/pubsubhubbub/subscribe_worker.rb @@ -5,30 +5,5 @@ class Pubsubhubbub::SubscribeWorker sidekiq_options queue: 'push', retry: 10, unique: :until_executed, dead: false - sidekiq_retry_in do |count| - case count - when 0 - 30.minutes.seconds - when 1 - 2.hours.seconds - when 2 - 12.hours.seconds - else - 24.hours.seconds * (count - 2) - end - end - - sidekiq_retries_exhausted do |msg, _e| - account = Account.find(msg['args'].first) - Sidekiq.logger.error "PuSH subscription attempts for #{account.acct} exhausted. Unsubscribing" - ::UnsubscribeService.new.call(account) - end - - def perform(account_id) - account = Account.find(account_id) - logger.debug "PuSH re-subscribing to #{account.acct}" - ::SubscribeService.new.call(account) - rescue => e - raise e.class, "Subscribe failed for #{account&.acct}: #{e.message}", e.backtrace[0] - end + def perform(account_id); end end diff --git a/app/workers/pubsubhubbub/unsubscribe_worker.rb b/app/workers/pubsubhubbub/unsubscribe_worker.rb index a271715b7a0e2bfb0796c6e2feaf01a51466acc6..0c1c263f6e07ee71e92157c5776bb3f502c845dc 100644 --- a/app/workers/pubsubhubbub/unsubscribe_worker.rb +++ b/app/workers/pubsubhubbub/unsubscribe_worker.rb @@ -5,11 +5,5 @@ class Pubsubhubbub::UnsubscribeWorker sidekiq_options queue: 'push', retry: false, unique: :until_executed, dead: false - def perform(account_id) - account = Account.find(account_id) - logger.debug "PuSH unsubscribing from #{account.acct}" - ::UnsubscribeService.new.call(account) - rescue ActiveRecord::RecordNotFound - true - end + def perform(account_id); end end diff --git a/app/workers/redownload_media_worker.rb b/app/workers/redownload_media_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..98e99591898e0e672769eb67c8f01b0fb0169cf1 --- /dev/null +++ b/app/workers/redownload_media_worker.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class RedownloadMediaWorker + include Sidekiq::Worker + include ExponentialBackoff + + sidekiq_options queue: 'pull', retry: 3 + + def perform(id) + media_attachment = MediaAttachment.find(id) + + return if media_attachment.remote_url.blank? + + media_attachment.reset_file! + media_attachment.save + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/remote_profile_update_worker.rb b/app/workers/remote_profile_update_worker.rb index 03585ad2d6e2485b5dc59a69d98be14f745cc7ba..01e8daf8f282fa40013f55b47a9c1bf74388be7b 100644 --- a/app/workers/remote_profile_update_worker.rb +++ b/app/workers/remote_profile_update_worker.rb @@ -5,9 +5,5 @@ class RemoteProfileUpdateWorker sidekiq_options queue: 'pull' - def perform(account_id, body, resubscribe) - UpdateRemoteProfileService.new.call(body, Account.find(account_id), resubscribe) - rescue ActiveRecord::RecordNotFound - true - end + def perform(account_id, body, resubscribe); end end diff --git a/app/workers/removal_worker.rb b/app/workers/removal_worker.rb index 19a660dd3e0190f19a6cfab2bc7389b3c57606ef..2a1eaa89b061d34b4b4fc7b0d07f04e00d81de2b 100644 --- a/app/workers/removal_worker.rb +++ b/app/workers/removal_worker.rb @@ -3,8 +3,8 @@ class RemovalWorker include Sidekiq::Worker - def perform(status_id) - RemoveStatusService.new.call(Status.find(status_id)) + def perform(status_id, options = {}) + RemoveStatusService.new.call(Status.with_discarded.find(status_id), **options.symbolize_keys) rescue ActiveRecord::RecordNotFound true end diff --git a/app/workers/salmon_worker.rb b/app/workers/salmon_worker.rb index d37d4043234b2e451865efbbd433da06acd7e478..10200b06cf95b937585ab44c85c6f0606763bc46 100644 --- a/app/workers/salmon_worker.rb +++ b/app/workers/salmon_worker.rb @@ -5,9 +5,5 @@ class SalmonWorker sidekiq_options backtrace: true - def perform(account_id, body) - ProcessInteractionService.new.call(body, Account.find(account_id)) - rescue Nokogiri::XML::XPath::SyntaxError, ActiveRecord::RecordNotFound - true - end + def perform(account_id, body); end end diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 42620332e7ee8cad0e42ae6622c0d96d1d59159c..4f44078d8e903971dab4be0a4897822dd5be0ef6 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -9,7 +9,7 @@ class Scheduler::IpCleanupScheduler def perform time_ago = RETENTION_PERIOD.ago - SessionActivation.where('updated_at < ?', time_ago).destroy_all - User.where('last_sign_in_at < ?', time_ago).update_all(last_sign_in_ip: nil) + SessionActivation.where('updated_at < ?', time_ago).in_batches.destroy_all + User.where('last_sign_in_at < ?', time_ago).where.not(last_sign_in_ip: nil).in_batches.update_all(last_sign_in_ip: nil) end end diff --git a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb index 5fba120f6ab339ccfede1b8b232a762b84f9f815..75fe681a9c3cfc72331ceb8a6ae109eafbae9f72 100644 --- a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb +++ b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb @@ -5,7 +5,5 @@ class Scheduler::SubscriptionsCleanupScheduler sidekiq_options unique: :until_executed, retry: 0 - def perform - Subscription.expired.in_batches.delete_all - end + def perform; end end diff --git a/app/workers/scheduler/subscriptions_scheduler.rb b/app/workers/scheduler/subscriptions_scheduler.rb index d5873bccb0ea2bfe9d392e827305a6e3a23b46b0..6903cadc7607f49a9ab3228608b0d4abb4b6b63f 100644 --- a/app/workers/scheduler/subscriptions_scheduler.rb +++ b/app/workers/scheduler/subscriptions_scheduler.rb @@ -5,13 +5,5 @@ class Scheduler::SubscriptionsScheduler sidekiq_options unique: :until_executed, retry: 0 - def perform - Pubsubhubbub::SubscribeWorker.push_bulk(expiring_accounts.pluck(:id)) - end - - private - - def expiring_accounts - Account.expiring(1.day.from_now).partitioned - end + def perform; end end diff --git a/app/workers/scheduler/trending_tags_scheduler.rb b/app/workers/scheduler/trending_tags_scheduler.rb new file mode 100644 index 0000000000000000000000000000000000000000..77f0d574757c920dab85212602053830c2434153 --- /dev/null +++ b/app/workers/scheduler/trending_tags_scheduler.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Scheduler::TrendingTagsScheduler + include Sidekiq::Worker + + sidekiq_options unique: :until_executed, retry: 0 + + def perform + TrendingTags.update! if Setting.trends + end +end diff --git a/app/workers/unfollow_follow_worker.rb b/app/workers/unfollow_follow_worker.rb index 50d3bf034adfb9acd9713465f921a7b6ffc245ef..95549e107ad70c760ea04d492e71574f8d2bad51 100644 --- a/app/workers/unfollow_follow_worker.rb +++ b/app/workers/unfollow_follow_worker.rb @@ -11,7 +11,7 @@ class UnfollowFollowWorker new_target_account = Account.find(new_target_account_id) FollowService.new.call(follower_account, new_target_account) - UnfollowService.new.call(follower_account, old_target_account) + UnfollowService.new.call(follower_account, old_target_account, skip_unmerge: true) rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError true end diff --git a/app/workers/web/push_notification_worker.rb b/app/workers/web/push_notification_worker.rb index 90104397581aacdcf9f21c58f518c1849bc2c6e0..46aeaa30b1d08ea58b5e3532f379ea2ff5c00380 100644 --- a/app/workers/web/push_notification_worker.rb +++ b/app/workers/web/push_notification_worker.rb @@ -11,7 +11,13 @@ class Web::PushNotificationWorker subscription.push(notification) unless notification.activity.nil? rescue Webpush::ResponseError => e - subscription.destroy! if (400..499).cover?(e.response.code.to_i) + code = e.response.code.to_i + + if (400..499).cover?(code) && ![408, 429].include?(code) + subscription.destroy! + else + raise e + end rescue ActiveRecord::RecordNotFound true end diff --git a/babel.config.js b/babel.config.js index a506ad8ce6e27031c5f021fd6e771991fc323b16..3f7ff84dbd13f5e8294109115ce599579d0823b2 100644 --- a/babel.config.js +++ b/babel.config.js @@ -20,6 +20,12 @@ module.exports = (api) => { ['react-intl', { messagesDir: './build/messages' }], 'preval', ], + overrides: [{ + test: /tesseract\.js/, + presets: [ + ['@babel/env', { ...envOptions, modules: 'commonjs' }], + ], + }], }; switch (env) { diff --git a/boxfile.yml b/boxfile.yml index 08526a57c918e9c09d4c56c87faeaa551b26ec29..1bc3929c86d2feff4fc640461ccc0335b765b3a6 100644 --- a/boxfile.yml +++ b/boxfile.yml @@ -67,6 +67,9 @@ deploy.config: bin/tootctl search deploy fi - bin/tootctl cache clear + after_live: + worker.sidekiq: + - bin/tootctl search deploy web.web: diff --git a/config/application.rb b/config/application.rb index f49deffbb6d4e14b78c5c10ff5087fe4a2e403e6..9be41b1a75c0fdf84bf2d8832d3ec33aef6e465a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,7 +13,9 @@ require_relative '../lib/paperclip/video_transcoder' require_relative '../lib/paperclip/type_corrector' require_relative '../lib/mastodon/snowflake' require_relative '../lib/mastodon/version' -require_relative '../lib/devise/ldap_authenticatable' +require_relative '../lib/devise/two_factor_ldap_authenticatable' +require_relative '../lib/devise/two_factor_pam_authenticatable' +require_relative '../lib/chewy/strategy/custom_sidekiq' Dotenv::Railtie.load @@ -37,11 +39,11 @@ module Mastodon # All translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.available_locales = [ - :en, :ar, :ast, :bg, :bn, + :br, :ca, :co, :cs, @@ -49,8 +51,11 @@ module Mastodon :da, :de, :el, + :en, :eo, + :'es-AR', :es, + :et, :eu, :fa, :fi, @@ -71,20 +76,22 @@ module Mastodon :ko, :lt, :lv, + :mk, :ms, :nl, + :nn, :no, :oc, :pl, - :pt, :'pt-BR', + :'pt-PT', :ro, :ru, :sk, :sl, :sq, - :sr, :'sr-Latn', + :sr, :sv, :ta, :te, diff --git a/config/deploy.rb b/config/deploy.rb index f0db50788c26b6c602dce3d2a907c3941898bb73..4dc36c65c29a8a1ad519555e9e60fab9a9b97dbb 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -lock '3.11.0' +lock '3.11.2' set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git') set :branch, ENV.fetch('BRANCH', 'master') diff --git a/config/environments/production.rb b/config/environments/production.rb index 70baa6ad1a7b7d993f8a08989487e8bda723510f..29d6194ddabdacd7eb0050a1449f952040b41371 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -71,13 +71,22 @@ Rails.application.configure do # Better log formatting config.lograge.enabled = true + config.lograge.custom_payload do |controller| + if controller.respond_to?(:signed_request?) && controller.signed_request? + { key: controller.signature_key_id } + end + end + # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false config.action_mailer.perform_caching = false # E-mails - config.action_mailer.default_options = { from: ENV.fetch('SMTP_FROM_ADDRESS', 'notifications@localhost') } + config.action_mailer.default_options = { + from: ENV.fetch('SMTP_FROM_ADDRESS', 'notifications@localhost'), + reply_to: ENV['SMTP_REPLY_TO'] + } config.action_mailer.smtp_settings = { :port => ENV['SMTP_PORT'], diff --git a/config/initializers/2_whitelist_mode.rb b/config/initializers/2_whitelist_mode.rb new file mode 100644 index 0000000000000000000000000000000000000000..a17ad07a2cfb052487d3562832e02f1d6fcd19f2 --- /dev/null +++ b/config/initializers/2_whitelist_mode.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Rails.application.configure do + config.x.whitelist_mode = ENV['WHITELIST_MODE'] == 'true' +end diff --git a/config/initializers/active_model_serializers.rb b/config/initializers/active_model_serializers.rb index 329a5fb2c3a005688bdf9a5e023815008a62f974..0e69e1d96c408ded0bb5d4d08a5b132408709771 100644 --- a/config/initializers/active_model_serializers.rb +++ b/config/initializers/active_model_serializers.rb @@ -3,22 +3,3 @@ ActiveModelSerializers.config.tap do |config| end ActiveSupport::Notifications.unsubscribe(ActiveModelSerializers::Logging::RENDER_EVENT) - -class ActiveModel::Serializer::Reflection - # We monkey-patch this method so that when we include associations in a serializer, - # the nested serializers can send information about used contexts upwards back to - # the root. We do this via instance_options because the nesting can be dynamic. - def build_association(parent_serializer, parent_serializer_options, include_slice = {}) - serializer = options[:serializer] - - parent_serializer_options.merge!(named_contexts: serializer._named_contexts, context_extensions: serializer._context_extensions) if serializer.respond_to?(:_named_contexts) - - association_options = { - parent_serializer: parent_serializer, - parent_serializer_options: parent_serializer_options, - include_slice: include_slice, - } - - ActiveModel::Serializer::Association.new(self, association_options) - end -end diff --git a/config/initializers/chewy.rb b/config/initializers/chewy.rb index d5347f2bfd0bcd049b5396489fbe42a0816f75b1..9ff0dccc1b499042ec80560fa23fcc2d6d46b3d9 100644 --- a/config/initializers/chewy.rb +++ b/config/initializers/chewy.rb @@ -12,8 +12,9 @@ Chewy.settings = { sidekiq: { queue: 'pull' }, } -Chewy.root_strategy = enabled ? :sidekiq : :bypass -Chewy.request_strategy = enabled ? :sidekiq : :bypass +Chewy.root_strategy = :custom_sidekiq +Chewy.request_strategy = :custom_sidekiq +Chewy.use_after_commit_callbacks = false module Chewy class << self diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 59cfbba173c33606540ae0e21b3bd7263d867ef1..af7d16aafc521f39556012e185b8af3e865b2761 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -2,9 +2,19 @@ # For further information see the following documentation # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy -base_host = Rails.configuration.x.web_domain +def host_to_url(str) + "http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}" unless str.blank? +end + +base_host = Rails.configuration.x.web_domain + assets_host = Rails.configuration.action_controller.asset_host -assets_host ||= "http#{Rails.configuration.x.use_https ? 's' : ''}://#{base_host}" +assets_host ||= host_to_url(base_host) + +media_host = host_to_url(ENV['S3_ALIAS_HOST']) +media_host ||= host_to_url(ENV['S3_CLOUDFRONT_HOST']) +media_host ||= host_to_url(ENV['S3_HOSTNAME']) if ENV['S3_ENABLED'] == 'true' +media_host ||= assets_host Rails.application.config.content_security_policy do |p| p.base_uri :none @@ -20,11 +30,13 @@ Rails.application.config.content_security_policy do |p| if Rails.env.development? webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{Webpacker.dev_server.host_with_port}" } - p.connect_src :self, :blob, assets_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls + p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host + p.worker_src :self, :blob, assets_host else - p.connect_src :self, :blob, assets_host, Rails.configuration.x.streaming_api_base_url + p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url p.script_src :self, assets_host + p.worker_src :self, :blob, assets_host end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index cd9bacf68033e609abf0e662b9958b8b00555499..fd9a5a8b9b96f14663fe38ddce0e990dcd3a6623 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -71,8 +71,8 @@ end Devise.setup do |config| config.warden do |manager| - manager.default_strategies(scope: :user).unshift :ldap_authenticatable if Devise.ldap_authentication - manager.default_strategies(scope: :user).unshift :pam_authenticatable if Devise.pam_authentication + manager.default_strategies(scope: :user).unshift :two_factor_ldap_authenticatable if Devise.ldap_authentication + manager.default_strategies(scope: :user).unshift :two_factor_pam_authenticatable if Devise.pam_authentication manager.default_strategies(scope: :user).unshift :two_factor_authenticatable manager.default_strategies(scope: :user).unshift :two_factor_backupable end diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb new file mode 100644 index 0000000000000000000000000000000000000000..6f1e78fed995f583300a1c04a9615a1a7b704151 --- /dev/null +++ b/config/initializers/health_check.rb @@ -0,0 +1,8 @@ +HealthCheck.setup do |config| + config.uri = 'health' + + config.standard_checks = %w(database migrations cache) + config.full_checks = %w(database migrations cache) + + config.include_error_in_response_body = false +end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index bf0cb52a30a2ac2832408f712a7e51dfa015885c..c65153b0a7a19d712a77666d346e53d72b81c59c 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -18,4 +18,5 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'PubSubHubbub' inflect.acronym 'ActivityStreams' inflect.acronym 'JsonLd' + inflect.acronym 'NodeInfo' end diff --git a/config/initializers/json_ld.rb b/config/initializers/json_ld.rb index d5575d135464f18fddbed4cd8353b3d59044c1d1..3ed3c4b31a31bb50c4281d1d514d36ac0c79ee8f 100644 --- a/config/initializers/json_ld.rb +++ b/config/initializers/json_ld.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true require_relative '../../lib/json_ld/security' +require_relative '../../lib/json_ld/identity' diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb index ce4185e024dbf92e7c02357e0dd37276a948ed3d..d3602e655e6083d74d14143c3afd52a898d8a921 100644 --- a/config/initializers/paperclip.rb +++ b/config/initializers/paperclip.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true -Paperclip.options[:read_timeout] = 60 - Paperclip.interpolates :filename do |attachment, style| - return attachment.original_filename if style == :original - [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.') + if style == :original + attachment.original_filename + else + [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.') + end end Paperclip::Attachment.default_options.merge!( @@ -24,21 +25,27 @@ if ENV['S3_ENABLED'] == 'true' storage: :s3, s3_protocol: s3_protocol, s3_host_name: s3_hostname, + s3_headers: { + 'X-Amz-Multipart-Threshold' => ENV.fetch('S3_MULTIPART_THRESHOLD') { 15.megabytes }.to_i, 'Cache-Control' => 'public, max-age=315576000, immutable', }, + s3_permissions: ENV.fetch('S3_PERMISSION') { 'public-read' }, s3_region: s3_region, + s3_credentials: { bucket: ENV['S3_BUCKET'], access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], }, + s3_options: { signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' }, http_open_timeout: 5, http_read_timeout: 5, http_idle_timeout: 5, + retry_limit: 0, } ) @@ -47,6 +54,7 @@ if ENV['S3_ENABLED'] == 'true' endpoint: ENV['S3_ENDPOINT'], force_path_style: true ) + Paperclip::Attachment.default_options[:url] = ':s3_path_url' end @@ -72,6 +80,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true' openstack_region: ENV['SWIFT_REGION'], openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 }, }, + fog_directory: ENV['SWIFT_CONTAINER'], fog_host: ENV['SWIFT_OBJECT_URL'], fog_public: true @@ -80,7 +89,7 @@ else Paperclip::Attachment.default_options.merge!( storage: :filesystem, use_timestamp: true, - path: (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename', - url: (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename', + path: ENV.fetch('PAPERCLIP_ROOT_PATH', ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename', + url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:class/:attachment/:id_partition/:style/:filename', ) end diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 24ba16ae36f5463d796a0345371c81fb29991e99..273cac9ca3d437c3108deeaf75a38095cf214d39 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -65,7 +65,7 @@ class Rack::Attack req.authenticated_user_id if req.post? && req.path.start_with?('/api/v1/media') end - throttle('throttle_media_proxy', limit: 30, period: 30.minutes) do |req| + throttle('throttle_media_proxy', limit: 30, period: 10.minutes) do |req| req.remote_ip if req.path.start_with?('/media_proxy') end diff --git a/config/locales/activerecord.bg.yml b/config/locales/activerecord.bg.yml index d0e375da96f10971c15d25ff3e8579573a9e6405..a1f60b45a797fc4d6d5b9701f9078dafc8f3f5c9 100644 --- a/config/locales/activerecord.bg.yml +++ b/config/locales/activerecord.bg.yml @@ -1 +1,7 @@ +--- bg: + activerecord: + attributes: + poll: + expires_at: Краен Ñрок + options: Избори diff --git a/config/locales/activerecord.bn.yml b/config/locales/activerecord.bn.yml index 152c698290639f4688cdb5184962daafc705fc52..e0e6ac90c2af66dba9aa6624de17525a9dba7ff5 100644 --- a/config/locales/activerecord.bn.yml +++ b/config/locales/activerecord.bn.yml @@ -1 +1,17 @@ +--- bn: + activerecord: + attributes: + poll: + expires_at: শেষ হবে + options: বিকলà§à¦ª + errors: + models: + account: + attributes: + username: + invalid: শà§à¦§à§à¦®à¦¾à¦¤à§à¦° অকà§à¦·à¦°, সংখà§à¦¯à¦¾ à¦à¦¬à¦‚ _ বেবহার করা যাবে + status: + attributes: + reblog: + taken: লেখাটি ইতিপূরà§à¦¬à§‡ ছিল diff --git a/config/locales/activerecord.br.yml b/config/locales/activerecord.br.yml new file mode 100644 index 0000000000000000000000000000000000000000..77fc4f2a1125d7fbbfd66b2168a3fbc43fa7afe5 --- /dev/null +++ b/config/locales/activerecord.br.yml @@ -0,0 +1,13 @@ +--- +br: + activerecord: + attributes: + poll: + expires_at: Deiziad termen + options: Dibaboù + errors: + models: + account: + attributes: + username: + invalid: nemet lizherennoù, niverennoù ha isbarrennigoù diff --git a/config/locales/activerecord.cy.yml b/config/locales/activerecord.cy.yml index 19547df986a48c5a4338158cc1b2335376b7282a..92fba043fb191dcbb3c96220aa8aeb8237af27c0 100644 --- a/config/locales/activerecord.cy.yml +++ b/config/locales/activerecord.cy.yml @@ -3,7 +3,7 @@ cy: activerecord: attributes: poll: - expires_at: Terfyn + expires_at: Terfyn amser options: Dewisiadau errors: models: diff --git a/config/locales/activerecord.da.yml b/config/locales/activerecord.da.yml index 5e7266ef10b0630f49a2bc6a8ac453ab393860bf..e7367a4af7ab0b891ab761ab044eedf0e41f50c5 100644 --- a/config/locales/activerecord.da.yml +++ b/config/locales/activerecord.da.yml @@ -1,6 +1,10 @@ --- da: activerecord: + attributes: + poll: + expires_at: Deadline + options: Valgmuligheder errors: models: account: diff --git a/config/locales/activerecord.es-AR.yml b/config/locales/activerecord.es-AR.yml new file mode 100644 index 0000000000000000000000000000000000000000..4cdd90278736f5006e50203d5cbd0be99033145e --- /dev/null +++ b/config/locales/activerecord.es-AR.yml @@ -0,0 +1,17 @@ +--- +es-AR: + activerecord: + attributes: + poll: + expires_at: Fecha lÃmite + options: Opciones + errors: + models: + account: + attributes: + username: + invalid: sólo letras, números y subguiones ("_") + status: + attributes: + reblog: + taken: del estado ya existe diff --git a/config/locales/activerecord.et.yml b/config/locales/activerecord.et.yml new file mode 100644 index 0000000000000000000000000000000000000000..5679a91fe206a936fc660ec33069a4dbde640273 --- /dev/null +++ b/config/locales/activerecord.et.yml @@ -0,0 +1,17 @@ +--- +et: + activerecord: + attributes: + poll: + expires_at: Tähtaeg + options: Valikud + errors: + models: + account: + attributes: + username: + invalid: ainult tähtmärgid, numbrid ja alakriipsud + status: + attributes: + reblog: + taken: staatusel juba eksisteerib diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml index bbc18bc33cb2ba3052fd09971182dd2d5c5a864f..ca5fbacb464edb0e8718352b241a69a9f9023109 100644 --- a/config/locales/activerecord.hu.yml +++ b/config/locales/activerecord.hu.yml @@ -11,3 +11,7 @@ hu: attributes: username: invalid: csak betűk, számok vagy alávonás + status: + attributes: + reblog: + taken: már létezik ehhez a bejegyzéshez diff --git a/config/locales/activerecord.id.yml b/config/locales/activerecord.id.yml index 9fa093e9dd94e34ed4f8f7c20d097bc04fc50db1..673d002e36a00b282608eefbe2d09978452a7fca 100644 --- a/config/locales/activerecord.id.yml +++ b/config/locales/activerecord.id.yml @@ -1,6 +1,10 @@ --- id: activerecord: + attributes: + poll: + expires_at: Tenggat waktu + options: Pilihan errors: models: account: diff --git a/config/locales/activerecord.mk.yml b/config/locales/activerecord.mk.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b9144a988941222eba3d4c61c19a62a8b057db4 --- /dev/null +++ b/config/locales/activerecord.mk.yml @@ -0,0 +1 @@ +mk: diff --git a/config/locales/activerecord.nn.yml b/config/locales/activerecord.nn.yml new file mode 100644 index 0000000000000000000000000000000000000000..777f4e600f65daa6a550f2cf0f04551a49cf4fcd --- /dev/null +++ b/config/locales/activerecord.nn.yml @@ -0,0 +1 @@ +nn: diff --git a/config/locales/activerecord.pt-BR.yml b/config/locales/activerecord.pt-BR.yml index 85150c1e75a7fc7ef4e1cd4d2d52d8e7182d0a1c..3f2b6aaaa4edd7ab6de5244615366414a169df50 100644 --- a/config/locales/activerecord.pt-BR.yml +++ b/config/locales/activerecord.pt-BR.yml @@ -10,7 +10,7 @@ pt-BR: account: attributes: username: - invalid: apenas letras, números e underscores + invalid: apenas letras, números e '_' são permitidos status: attributes: reblog: diff --git a/config/locales/activerecord.pt.yml b/config/locales/activerecord.pt-PT.yml similarity index 97% rename from config/locales/activerecord.pt.yml rename to config/locales/activerecord.pt-PT.yml index 556fcfc4fd9caabbdf4e62d682a67feef16696e9..cbb053a9005979179c92c95980dcf562558ba5f0 100644 --- a/config/locales/activerecord.pt.yml +++ b/config/locales/activerecord.pt-PT.yml @@ -1,5 +1,5 @@ --- -pt: +pt-PT: activerecord: errors: models: diff --git a/config/locales/activerecord.sv.yml b/config/locales/activerecord.sv.yml index 0ae4906d7d6d7b03075f0513bda530cf354ced09..8d142e7acd9aa0259e12980952b56e2358f83fe5 100644 --- a/config/locales/activerecord.sv.yml +++ b/config/locales/activerecord.sv.yml @@ -1,9 +1,13 @@ --- sv: activerecord: + attributes: + poll: + expires_at: Tidsgräns + options: Val errors: models: account: attributes: username: - invalid: enbart bokstäver, siffror och understreck + invalid: endast bokstäver, siffror och understrykning diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml index 1cc2d98760a7522c4d42458c590a7a8d8d2a436d..8ce55599cb3b96c656effaf35bdf402b3de8fcae 100644 --- a/config/locales/activerecord.tr.yml +++ b/config/locales/activerecord.tr.yml @@ -1,6 +1,10 @@ --- tr: activerecord: + attributes: + poll: + expires_at: Son Teslim Tarihi + options: Seçenekler errors: models: account: diff --git a/config/locales/activerecord.uk.yml b/config/locales/activerecord.uk.yml index 00b2b8d8b11629bc63666cf8581e022d2ff07141..ffbf5934670ed42aac60879ff8a7ddbf7d07e79a 100644 --- a/config/locales/activerecord.uk.yml +++ b/config/locales/activerecord.uk.yml @@ -1,12 +1,16 @@ --- uk: activerecord: + attributes: + poll: + expires_at: Кінцевий термін + options: Варіанти вибору errors: models: account: attributes: username: - invalid: тільки букви, цифри та нижні підкреÑÐ»ÑŽÐ²Ð°Ð½Ð½Ñ + invalid: тільки літери, цифри та підкреÑÐ»ÐµÐ½Ð½Ñ status: attributes: reblog: diff --git a/config/locales/activerecord.zh-TW.yml b/config/locales/activerecord.zh-TW.yml index cb82c0526113b986978e7a8f16024f9075f98428..4f938a7f5ebb29401dce3674ef8e522e4a0efe92 100644 --- a/config/locales/activerecord.zh-TW.yml +++ b/config/locales/activerecord.zh-TW.yml @@ -1 +1,17 @@ +--- zh-TW: + activerecord: + attributes: + poll: + expires_at: æˆªæ¢æ™‚é–“ + options: 鏿“‡ + errors: + models: + account: + attributes: + username: + invalid: åªèƒ½æœ‰å—æ¯ã€æ•¸å—åŠåº•ç·š + status: + attributes: + reblog: + taken: 嘟文已經å˜åœ¨ diff --git a/config/locales/ar.yml b/config/locales/ar.yml index eb6a5ef06c321c4f8a71f14eded723cf74a53705..2bf6789be1d7a65968e0c3b1b15fd35a97974501 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -5,18 +5,19 @@ ar: about_mastodon_html: ماستدون شبكة اجتماعية مبنية على Ø£Ø³ÙØ³ بروتوكولات برمجيات الويب Ø§Ù„ØØ±Ø© Ùˆ Ù…ÙØªÙˆØØ© المصدر. Ùˆ هو لامركزي تمامًا كالبريد الإلكتروني. about_this: عن مثيل الخادوم هذا active_count_after: نشط + active_footnote: مستخدم نشيط شهريا (MAU) administered_by: 'ÙŠÙØ¯ÙŠØ±Ù‡:' api: واجهة برمجة التطبيقات apps: تطبيقات الأجهزة المØÙ…ولة + apps_platforms: إستخدم ماستودون ÙÙŠ iOSØŒ أندرويد وأنظمة أخرى + browse_directory: ØªØµÙØ دليل Ø§Ù„ØµÙØØ§Øª التعريÙية وصÙّي Ø¨ØØ³Ø¨ الإهتمام + browse_public_posts: ØªØµÙØ تيارًا مباشرًا Ù…ÙÙ† منشورات عامة على ماستدون contact: للتواصل معنا contact_missing: لم يتم تعيينه contact_unavailable: غير Ù…ØªÙˆÙØ± discover_users: اكتش٠مستخدÙمين documentation: الدليل - extended_description_html: | - <h3>مكان جيد للقواعد</h3> - <p>لم يتم بعد إدخال الوص٠الطويل.</p> - generic_description: "%{domain} هو Ø³ÙŠØ±ÙØ± من بين Ø³ÙŠØ±ÙØ±Ø§Øª الشبكة" + federation_hint_html: بواسطة ØØ³Ø§Ø¨ ÙÙŠ %{instance} ستتمكن من تتبع أناس ÙÙŠ أي خادم ماستدون وأكثر. get_apps: جرّب تطبيقا على الموبايل hosted_on: ماستدون Ù…ÙØ³ØªØ¶Ø§Ù على %{domain} learn_more: تعلم المزيد @@ -34,6 +35,10 @@ ar: status_count_before: نشروا tagline: اتبع أصدقائك وصديقاتك واكتش٠آخرين وأخريات terms: شروط الخدمة + unavailable_content: Ù…ØØªÙˆÙ‰ غير Ù…ØªÙˆÙØ± + unavailable_content_description: + domain: الخادم + reason: 'السبب:' user_count_after: few: مستخدمين many: مستخدمين @@ -60,6 +65,7 @@ ar: media: الوسائط moved_html: "%{name} إنتقلَ إلى %{new_profile_link}:" network_hidden: إنّ المعطيات غير Ù…ØªÙˆÙØ±Ø© + never_active: أبدا nothing_here: لا يوجد أي شيء هنا! people_followed_by: الأشخاص الذين يتبعهم %{name} people_who_follow: الأشخاص الذين يتبعون %{name} @@ -79,7 +85,7 @@ ar: admin: المدير bot: روبوت moderator: Ù…ÙØ´Ø±ÙÙ - unavailable: Ø§Ù„ØØ³Ø§Ø¨ غير Ù…ØªÙˆÙØ± + unavailable: Ø§Ù„ØµÙØØ© التعريÙية غير Ù…ØªÙˆÙØ±Ø© unfollow: إلغاء المتابعة admin: account_actions: @@ -151,12 +157,12 @@ ar: outbox_url: رابط صندوق الصادر pending: ÙÙŠ انتظار المراجعة perform_full_suspension: تعليق Ø§Ù„ØØ³Ø§Ø¨ - profile_url: رابط المل٠الشخصي + profile_url: رابط Ø§Ù„ØµÙØØ© التعريÙية promote: ترقية protocol: البروتوكول public: عمومي push_subscription_expires: انتهاء الاشتراك â€PuSH“ - redownload: ØªØØ¯ÙŠØ« Ø§Ù„ØµÙØØ© الشخصية + redownload: انعش Ø§Ù„ØµÙØØ© التعريÙية reject: Ø§Ø±ÙØ¶ reject_all: Ø§Ø±ÙØ¶ الكل remove_avatar: ØØ°Ù الصورة الرمزية @@ -194,6 +200,7 @@ ar: username: اسم المستخدم warn: ØªØØ°ÙŠØ± web: الويب + whitelisted: ÙÙŠ القائمة البيضاء action_logs: actions: assigned_to_self_report: قام %{name} بتعيين التقرير %{target} Ù„Ø£Ù†ÙØ³Ù‡Ù… @@ -229,19 +236,24 @@ ar: deleted_status: "(منشور Ù…ØØ°ÙˆÙ)" title: Ø³ÙØ¬Ù„Ù‘ Ø§Ù„ØªÙØªÙŠØ´ Ùˆ المعاينة custom_emojis: + assign_category: اسند ÙØ¦Ø© by_domain: النطاق copied_msg: تم إنشاء نسخة Ù…ØÙ„ية للإيموجي Ø¨Ù†Ø¬Ø§Ø copy: نسخ copy_failed_msg: ÙØ´Ù„ت عملية إنشاء نسخة Ù…ØÙ„ية لهذا الإيموجي + create_new_category: انشئ ÙØ¦Ø© جديدة created_msg: تم إنشاء الإيموجي بنجاØ! delete: ØØ°Ù destroyed_msg: تمت عملية تدمير الإيموجي بنجاØ! disable: تعطيل + disabled: Ù…ÙØ¹Ø·Ù‘ÙŽÙ„ disabled_msg: تمت عملية تعطيل ذلك الإيموجي Ø¨Ù†Ø¬Ø§Ø emoji: إيموجي enable: ØªÙØ¹ÙŠÙ„ + enabled: Ù…ÙØ´ØºÙ‘ÙŽÙ„ enabled_msg: تم تنشيط ذاك الإيموجي Ø¨Ù†Ø¬Ø§Ø image_hint: مل٠PNG إلى غاية ØØ¬Ù… 50 Ùƒ.ب + list: القائمة listed: Ù…ÙØ¯Ø±ÙŽØ¬ new: title: Ø¥Ø¶Ø§ÙØ© إيموجي خاص جديد @@ -249,22 +261,27 @@ ar: shortcode: الترميز Ø§Ù„Ù…ÙØµÙŽØºÙ‘ر shortcode_hint: على الأقل ØØ±Ùين، Ùˆ Ùقط رموز أبجدية عددية Ùˆ أسطر سÙلية title: الإيموجي الخاصة + uncategorized: غير مصنّ٠unlisted: غير مدرج update_failed_msg: تعذرت عملية ØªØØ¯ÙŠØ« ذاك الإيموجي updated_msg: تم ØªØØ¯ÙŠØ« الإيموجي بنجاØ! upload: Ø±ÙØ¹ dashboard: + authorized_fetch_mode: الوضع الآمن backlog: الأعمال المتراكمة config: الإعداد feature_deletions: Ø§Ù„ØØ³Ø§Ø¨Ø§Øª Ø§Ù„Ù…ØØ°ÙˆÙØ© feature_invites: روابط الدعوات - feature_profile_directory: دليل Ø§Ù„ØØ³Ø§Ø¨Ø§Øª + feature_profile_directory: دليل Ø§Ù„Ù…Ù„ÙØ§Øª التعريÙية feature_registrations: التسجيلات feature_relay: Ø§Ù„Ù…ÙØ±ØÙ‘Ù„ Ø§Ù„ÙØ¯ÙŠØ±Ø§Ù„ÙŠ + feature_spam_check: Ù…ÙƒØ§ÙØ البريد المزعج feature_timeline_preview: معاينة الخيط الزمني features: الميّزات hidden_service: الÙيديرالية مع الخدمات الخÙية - open_reports: ÙØªØ الشكاوى + open_reports: شكاوي Ù…ÙØªÙˆØØ© + pending_tags: وسوم ÙÙŠ انتظار المراجعة + pending_users: مستخدمين ÙÙŠ انتظار مراجعة recent_users: Ø£ØØ¯Ø« المستخدÙمين search: Ø§Ù„Ø¨ØØ« النصي الكامل single_user_mode: وضع المستخدÙÙ… Ø§Ù„Ø£ÙˆØØ¯ @@ -272,24 +289,33 @@ ar: space: Ø§Ù„Ù…Ø³Ø§ØØ© المستخدَمة title: Ù„ÙˆØ Ø§Ù„Ù…Ø±Ø§Ù‚Ø¨Ø© total_users: إجمالي المستخدÙمين - trends: المؤشرات + trends: المتداوَلة week_interactions: ØªÙØ§Ø¹Ùلات هذا الأسبوع - week_users_active: نشط هذا الأسبوع + week_users_active: نشطاء هذا الأسبوع week_users_new: مستخدÙمين هذا الأسبوع + whitelist_mode: وضع القائمة البيضاء + domain_allows: + add_new: Ø¥Ø¶Ø§ÙØ© النطاق إلى القائمة البيضاء + created_msg: تمت Ø¥Ø¶Ø§ÙØ© النطاق إلى القائمة البيضاء Ø¨Ù†Ø¬Ø§Ø + destroyed_msg: تمت إزالة النطاق Ù…ÙÙ† القائمة البيضاء + undo: إزالة من القائمة البيضاء domain_blocks: add_new: Ø¥Ø¶Ø§ÙØ© ØØ¬Ø¨ جديد لنطاق created_msg: إنّ ØØ¬Ø¨ النطاق ØÙŠØ² التشغيل destroyed_msg: تم إلغاء Ø§Ù„ØØ¬Ø¨ Ø§Ù„Ù…ÙØ±ÙˆØ¶ على النطاق domain: النطاق + edit: ØªØØ±ÙŠØ± النطاق Ø§Ù„Ù…ØØ¸ÙˆØ± new: create: إنشاء ØØ¸Ø± hint: لن تمنع كتلة المجال إنشاء إدخالات ØØ³Ø§Ø¨ ÙÙŠ قاعدة البيانات ØŒ ولكنها ستطبق طرق Ø§Ù„Ø¥Ø´Ø±Ø§Ù Ø§Ù„Ù…ØØ¯Ø¯Ø© بأثر رجعي وتلقائي على هذه Ø§Ù„ØØ³Ø§Ø¨Ø§Øª. severity: - desc_html: "<strong>Silence</strong> سيجعل مشاركات Ø§Ù„ØØ³Ø§Ø¨ غير مرئية لأي شخص لا يتبعها. <strong>Suspend</strong> سيزيل كل Ù…ØØªÙˆÙ‰ Ø§Ù„ØØ³Ø§Ø¨ ووسائطه وبيانات ملÙÙ‡ الشخصي. Use <strong>None</strong> إذا كنت تريد Ùقط Ø±ÙØ¶ Ù…Ù„ÙØ§Øª الوسائط." + desc_html: "<strong>Silence</strong> سيجعل مشاركات Ø§Ù„ØØ³Ø§Ø¨ غير مرئية لأي شخص لا يتبعها. <strong>Suspend</strong> سيزيل كل Ù…ØØªÙˆÙ‰ Ø§Ù„ØØ³Ø§Ø¨ ووسائطه وبيانات ملÙÙ‡ التعريÙÙŠ. Use <strong>None</strong> إذا كنت تريد Ùقط Ø±ÙØ¶ Ù…Ù„ÙØ§Øª الوسائط." noop: لا شيء silence: كتم suspend: تعليق title: ØØ¬Ø¨ نطاق جديد + private_comment: تعليق خاص + public_comment: تعليق للعلن reject_media: Ø±ÙØ¶ Ù…Ù„ÙØ§Øª الوسائط reject_media_hint: يزيل Ù…Ù„ÙØ§Øª الوسائط المخزنة Ù…ØÙ„يًا ÙˆÙŠØ±ÙØ¶ تنزيل أي Ù…Ù„ÙØ§Øª ÙÙŠ المستقبل. غير ذي صلة للتعليق reject_reports: Ø±ÙØ¶ التقارير @@ -337,6 +363,8 @@ ar: all: ÙƒØ§ÙØªÙ‡Ø§ limited: Ù…ØØ¯ÙˆØ¯ title: الإشرا٠+ private_comment: تعليق خاص + public_comment: تعليق للعلن title: Ø§Ù„ÙØ¯ÙŠØ±Ø§Ù„ية total_blocked_by_us: Ø§Ù„Ù…ØØ¬ÙˆØ¨Ø© Ù…ÙÙ† طرÙنا total_followed_by_them: ÙŠÙØªØ§Ø¨Ùعونها @@ -413,6 +441,10 @@ ar: custom_css: desc_html: يقوم بتغيير المظهر بواسطة سي أس أس ÙŠÙØÙ…Ù‘ÙŽÙ„ على ÙƒØ§ÙØ© Ø§Ù„ØµÙØØ§Øª title: سي أس أس مخصص + domain_blocks: + all: للجميع + domain_blocks_rationale: + title: اظهر السبب hero: desc_html: معروض على Ø§Ù„ØµÙØØ© الأولى. لا يقل عن 600 × 100 بكسل. عند عدم التعيين ØŒ تعود الصورة إلى النسخة المصغرة على سبيل المثال title: الصورة الرأسية @@ -424,7 +456,7 @@ ar: title: إظهار الصور Ø§Ù„ØØ³Ø§Ø³Ø© ÙÙŠ Ù…ÙØ¹Ø§ÙŠÙ†Ø§Øª أوبن غرا٠profile_directory: desc_html: Ø§Ù„Ø³Ù…Ø§Ø Ù„Ù„Ù…Ø³ØªØ®Ø¯Ù…ÙŠÙ† الكش٠عن ØØ³Ø§Ø¨Ø§ØªÙ‡Ù… - title: ØªÙØ¹ÙŠÙ„ سجل Ø§Ù„Ù…Ù„ÙØ§Øª الشخصية + title: ØªÙØ¹ÙŠÙ„ دليل Ø§Ù„ØµÙØØ§Øª التعريÙية registrations: closed_message: desc_html: يتم عرضه على Ø§Ù„ØµÙØØ© الرئيسية عندما يتم غلق تسجيل Ø§Ù„ØØ³Ø§Ø¨Ø§Øª الجديدة. يمكنكم إستخدام علامات الأيتش تي أم أل HTML @@ -437,6 +469,7 @@ ar: title: المستخدÙمون Ø§Ù„Ù…ØµØ±Ø Ù„Ù‡Ù… لإرسال الدعوات registrations_mode: modes: + approved: طلب المواÙقة لازم عند إنشاء ØØ³Ø§Ø¨ none: لا Ø£ØØ¯ يمكنه إنشاء ØØ³Ø§Ø¨ open: يمكن للجميع إنشاء ØØ³Ø§Ø¨ title: طريقة إنشاء Ø§Ù„ØØ³Ø§Ø¨Ø§Øª @@ -458,6 +491,8 @@ ar: desc_html: يمكنك كتابة سياسة الخصوصية الخاصة بك ØŒ شروط الخدمة أو غيرها من القوانين. يمكنك استخدام علامات HTML title: شروط الخدمة المخصصة site_title: اسم مثيل الخادم + spam_check_enabled: + title: Ù…ÙƒØ§ÙØØ© البريد المزعج thumbnail: desc_html: يستخدم للعروض السابقة عبر Open Graph Ùˆ API. 1200x630px موصى به title: الصورة الرمزية المصغرة لمثيل الخادوم @@ -465,12 +500,15 @@ ar: desc_html: عرض الخيط العمومي على ØµÙØØ© الاستقبال title: Ù…ÙØ¹Ø§ÙŠÙ†Ø© الخيط العام title: إعدادات الموقع + trends: + title: الوسوم المتداولة statuses: back_to_account: العودة إلى ØµÙØØ© Ø§Ù„ØØ³Ø§Ø¨ batch: delete: ØØ°Ù nsfw_off: تعيينه كمنشور غير ØØ³Ø§Ø³ nsfw_on: تعيينه كمنشور ØØ³Ø§Ø³ + deleted: Ù…ØØ°ÙˆÙ failed_to_execute: خطأ ÙÙŠ Ø§Ù„ØªÙØ¹ÙŠÙ„ media: title: الوسائط @@ -478,20 +516,21 @@ ar: no_status_selected: لم يطرأ أي تغيير على أي منشور بما أنه لم يتم اختيار أي ÙˆØ§ØØ¯ title: منشورات Ø§Ù„ØØ³Ø§Ø¨ with_media: ØªØØªÙˆÙŠ Ø¹Ù„Ù‰ وسائط - subscriptions: - callback_url: عاود الاتصال بالعنوان - confirmed: مؤكَّد - expires_in: تنتهي مدة صلاØÙŠØªÙ‡Ø§ ÙÙŠ - last_delivery: آخر إيداع - topic: الموضوع tags: - accounts: Ø§Ù„ØØ³Ø§Ø¨Ø§Øª - hidden: المخÙية - hide: Ø§Ù„Ø¥Ø®ÙØ§Ø¡ عن سجل Ø§Ù„ØØ³Ø§Ø¨Ø§Øª + context: السياق + directory: ÙÙŠ دليل ØØ³Ø§Ø¨Ø§Øª المستخدمين + in_directory: "%{count} ÙÙŠ سجل ØØ³Ø§Ø¨Ø§Øª المستخدمين" + last_active: آخر نشاط + most_popular: الأكثر شعبية + most_recent: Ø§Ù„Ø£ØØ¯Ø« name: الوسم + review: ØØ§Ù„Ø© المراجعة + reviewed: Ù…ÙØ±Ø§Ø¬ÙŽØ¹ title: الوسوم - unhide: إظهاره ÙÙŠ سجل ØØ³Ø§Ø¨Ø§Øª المستخدمين - visible: ظاهر + trending_right_now: متداول Ø§Ù„Ù„ØØ¸Ø© + unique_uses_today: "%{count} منشورات اليوم" + unreviewed: غير Ù…ÙØ±Ø§Ø¬ÙŽØ¹ + updated_msg: تم ØªØØ¯ÙŠØ« إعدادات الوسوم Ø¨Ù†Ø¬Ø§Ø title: الإدارة warning_presets: add_new: Ø¥Ø¶Ø§ÙØ© ÙˆØ§ØØ¯ جديد @@ -508,13 +547,15 @@ ar: subject: تقرير جديد Ù„%{instance} (#%{id}) appearance: advanced_web_interface: واجهة الويب المتقدمة + animations_and_accessibility: Ø§Ù„Ø¥ØªØ§ØØ© ÙˆØ§Ù„ØØ±ÙƒØ© confirmation_dialogs: Ù†ÙˆØ§ÙØ° التأكيد + discovery: استكشا٠sensitive_content: Ù…ØØªÙˆÙ‰ ØØ³Ø§Ø³ application_mailer: notification_preferences: تعديل خيارات البريد الإلكتروني salutation: "%{name}ØŒ" settings: 'تغيير ØªÙØ¶ÙŠÙ„ات البريد الإلكتروني: %{link}' - view_profile: عرض المل٠الشخصي + view_profile: اعرض Ø§Ù„ØµÙØØ© التعريÙية view_status: عرض المنشور applications: created: تم إنشاء التطبيق Ø¨Ù†Ø¬Ø§Ø @@ -528,9 +569,12 @@ ar: apply_for_account: اطلب دعوة change_password: الكلمة السرية checkbox_agreement_html: أواÙÙ‚ على <a href="%{rules_path}" target="_blank">قواعد الخادم</a> Ùˆ <a href="%{terms_path}" target="_blank">شروط الخدمة</a> - confirm_email: تأكيد عنوان البريد الإلكتروني + checkbox_agreement_without_rules_html: أواÙÙ‚ على <a href="%{terms_path}" target="_blank">شروط الخدمة</a> delete_account: ØØ°Ù ØØ³Ø§Ø¨ delete_account_html: إن كنت ترغب ÙÙŠ ØØ°Ù ØØ³Ø§Ø¨Ùƒ ÙŠÙمكنك <a href="%{path}">المواصلة هنا</a>. Ø³ÙˆÙ ÙŠÙØ·Ù„َب٠منك التأكيد قبل Ø§Ù„ØØ°Ù. + description: + prefix_invited_by_user: يدعوك @%{name} Ù„Ù„Ø§ØªØØ§Ù‚ بخادم ماستدون هذا! + prefix_sign_up: أنشئ ØØ³Ø§Ø¨Ù‹Ø§ على ماستدون اليوم! didnt_get_confirmation: لم تتلق تعليمات التأكيد ØŸ forgot_password: نسيت كلمة المرور ØŸ invalid_reset_password_token: رمز إعادة تعيين كلمة المرور غير ØµØ§Ù„Ø Ø£Ùˆ منتهي الصلاØÙŠØ©. يرجى طلب ÙˆØ§ØØ¯ جديد. @@ -548,6 +592,12 @@ ar: reset_password: إعادة تعيين كلمة المرور security: الأمان set_new_password: إدخال كلمة مرور جديدة + setup: + title: الضبط + status: + account_status: ØØ§Ù„Ø© Ø§Ù„ØØ³Ø§Ø¨ + functional: ØØ³Ø§Ø¨Ùƒ جاهز. + redirecting_to: ØØ³Ø§Ø¨Ùƒ غير نشط لأنه تم تØÙˆÙŠÙ„Ù‡ ØØ§Ù„يا إلى %{acct}. trouble_logging_in: هل ØµØ§Ø¯ÙØªÙƒÙ… مشكلة ÙÙŠ الولوج؟ authorize_follow: already_following: أنت تتابع Ø¨Ø§Ù„ÙØ¹Ù„ هذا Ø§Ù„ØØ³Ø§Ø¨ @@ -557,9 +607,13 @@ ar: following: 'مرØÙ‰! أنت الآن تتبع:' post_follow: close: أو يمكنك إغلاق هذه Ø§Ù„Ù†Ø§ÙØ°Ø©. - return: عرض المل٠الشخصي للمستخدم + return: اظهر المل٠التعريÙÙŠ للمستخدم web: واصل إلى الويب title: إتباع %{acct} + challenge: + confirm: واصل + invalid_password: الكلمة السرية خاطئة + prompt: Ø£ÙƒÙ‘ÙØ¯ الكلمة السرية للمواصلة datetime: distance_in_words: about_x_hours: "%{count}سا" @@ -575,26 +629,30 @@ ar: x_months: "%{count} شه" x_seconds: "%{count}Ø«" deletes: - bad_password_msg: Ù…ØØ§ÙˆÙ„Ø© جيدة يا هاكرز! كلمة السر خاطئة + challenge_not_passed: المعلومات التي أدخلتها لم تكن صØÙŠØØ© confirm_password: قم بإدخال كلمتك السرية Ø§Ù„ØØ§Ù„ية للتØÙ‚Ù‚ من هويتك + confirm_username: ادخل اسم المستخدم الخاص بك لتأكيد الإجراء proceed: ØØ°Ù ØØ³Ø§Ø¨ success_msg: تم ØØ°Ù ØØ³Ø§Ø¨Ùƒ Ø¨Ù†Ø¬Ø§Ø - warning_title: ØªÙˆØ§ÙØ± Ø§Ù„Ù…ØØªÙˆÙ‰ المنشور Ùˆ المبعثَر + warning: + before: 'يرجى قراءة هذه Ø§Ù„Ù…Ù„Ø§ØØ¸Ø§Øª بتأنّي قبل المواصلة:' + data_removal: Ø³ÙˆÙ ØªÙØØ°ÙŽÙ Ù…Ù†Ø´ÙˆØ±Ø§ØªÙƒ والبيانات الأخرى نهائيا + email_change_html: بإمكانك <a href="%{path}">تغيير عنوان بريدك الإلكتروني</a> دون أن ÙŠÙØØ°Ù ØØ³Ø§Ø¨Ùƒ + irreversible: لن يكون بإمكانك استرجاع أو إعادة تنشيط ØØ³Ø§Ø¨Ùƒ + more_details_html: للمزيد Ù…ÙÙ† Ø§Ù„ØªÙØ§ØµÙŠÙ„ ØŒ يرجى الإطلاع على <a href="%{terms_path}">سياسة الخصوصية</a>. + username_available: Ø³ÙŠØµØ¨Ø Ø§Ø³Ù… مستخدمك Ù…ØªÙˆÙØ±Ø§ ثانية + username_unavailable: سيبقى اسم المستخدم الخاص بك غير Ù…ØªÙˆÙØ± directories: - directory: Ø³ÙØ¬Ù„Ù‘ Ø§Ù„ØØ³Ø§Ø¨Ø§Øª - enabled: إنّ ØØ³Ø§Ø¨Ùƒ الآن ضمن Ùهرس المستخدÙمين. + directory: Ø³ÙØ¬Ù„Ù‘ Ø§Ù„ØµÙØØ§Øª التعريÙية explanation: استكش٠مستخدÙمين آخرين ØØ³Ø¨ المواضيع التي تهمهم explore_mastodon: استكش٠%{title} - people: - few: "%{count} شخص" - many: "%{count} شخص" - one: "%{count} شخص" - other: "%{count} شخص" - two: "%{count} شخص" - zero: "%{count} شخص" + domain_validator: + invalid_domain: ليس بإسم نطاق ØµØ§Ù„Ø errors: + '400': The request you submitted was invalid or malformed. '403': ليس لك الصلاØÙŠØ§Øª الكاÙية لعرض هذه Ø§Ù„ØµÙØØ©. '404': إنّ Ø§Ù„ØµÙØØ© التي ØªØ¨ØØ« عنها لا وجود لها أصلا. + '406': This page is not available in the requested format. '410': إنّ Ø§Ù„ØµÙØØ© التي ØªØ¨ØØ« عنها لم تعد موجودة. '422': content: ÙØ´Ù„ التØÙ‚Ù‚ الآمن. ربما منعتَ كعكات الكوكيز؟ @@ -603,6 +661,7 @@ ar: '500': content: Ù†ØÙ† متأسÙون، لقد ØØ¯Ø« خطأ ما Ù…ÙÙ† جانبنا. title: هذه Ø§Ù„ØµÙØØ© خاطئة + '503': The page could not be served due to a temporary server failure. noscript_html: يرجى ØªÙØ¹ÙŠÙ„ Ø§Ù„Ø¬Ø§ÙØ§ سكريبت لاستخدام تطبيق الويب لماستدون، أو عÙوض ذلك قوموا بتجريب Ø¥ØØ¯Ù‰ <a href="%{apps_path}">التطبيقات الأصلية</a> الدّاعمة لماستدون على منصّتكم. existing_username_validator: not_found_multiple: تعذر العثور على %{usernames} @@ -622,7 +681,7 @@ ar: mutes: Ù‚Ùمتَ بكتم storage: ذاكرة التخزين featured_tags: - add_new: Ø¥Ø¶Ø§ÙØ© ÙˆØ§ØØ¯ + add_new: Ø£Ø¶Ù ÙˆØ§ØØ¯Ù‹Ø§ جديدا filters: contexts: home: الخيط الزمني الرئيسي @@ -643,6 +702,7 @@ ar: developers: المطورون more: المزيد… resources: الموارد + trending_now: المتداولة الآن generic: all: الكل changes_saved_msg: تم ØÙظ التعديلات بنجاØ! @@ -717,9 +777,21 @@ ar: too_many: لا يمكن Ø¥Ø±ÙØ§Ù‚ أكثر من 4 Ù…Ù„ÙØ§Øª migrations: acct: username@domain Ù„Ù„ØØ³Ø§Ø¨ الجديد - currently_redirecting: 'تم تØÙˆÙŠÙ„ رابط ملÙÙƒ الشخصي إلى:' - proceed: ØÙظ - updated_msg: تم ØªØØ¯ÙŠØ« إعدادات ترØÙŠÙ„ ØØ³Ø§Ø¨Ùƒ بنجاØ! + cancel: ألغ٠التوجيه + cancelled_msg: تم إلغاء التوجيه بنجاØ. + errors: + move_to_self: لا يمكنه أن يكون Ø§Ù„ØØ³Ø§Ø¨ Ø§Ù„ØØ§Ù„ÙŠ + not_found: تعذر العثور عليه + on_cooldown: إنّك ÙÙŠ مرØÙ„Ø© الجمود + followers_count: Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹ÙŠÙ† عند الإنتقال + incoming_migrations: الانتقال Ù…ÙÙ† ØØ³Ø§Ø¨ آخر + not_redirecting: ØØ§Ù„يا ØŒ ØØ³Ø§Ø¨Ùƒ لا يقوم بالتØÙˆÙŠÙ„ إلى أي ØØ³Ø§Ø¨ آخر. + past_migrations: التهجيرات السابقة + proceed_with_move: انقل مشارÙكيك + redirecting_to: ØØ³Ø§Ø¨Ùƒ موجَّه إلى %{acct}. + set_redirect: تعين إعادة التوجيه + warning: + before: 'يرجى قراءة هذه Ø§Ù„Ù…Ù„Ø§ØØ¸Ø§Øª بتأنّي قبل المواصلة:' moderation: title: الإشرا٠notification_mailer: @@ -784,6 +856,7 @@ ar: errors: already_voted: لقد قمت بالتصويت على استطلاع الرأي هذا Ù…ÙÙ† قبل duplicate_options: ÙŠØØªÙˆÙŠ Ø¹Ù„Ù‰ عناصر مكررة + duration_too_long: بعيد جدا ÙÙŠ المستقبَل duration_too_short: مبكّر جدا expired: لقد انتهى استطلاع الرأي preferences: @@ -796,6 +869,7 @@ ar: last_active: آخر نشاط most_recent: Ø§Ù„Ø£ØØ¯Ø« moved: هاجر + mutual: متبادل primary: رئيسي relationship: العلاقة remove_selected_domains: Ø§ØØ°Ù ÙƒØ§ÙØ© Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹ÙŠÙ† القادمين Ù…ÙÙ† النطاقات المختارة @@ -818,10 +892,9 @@ ar: reply: proceed: المواصلة إلى الرد prompt: 'ترغب ÙÙŠ الرد على هذا التبويق:' - remote_unfollow: - error: خطأ - title: العنوان - unfollowed: غير متابَع + scheduled_statuses: + over_total_limit: لقد بلغت ØØ¯ الـ %{limit} Ù…ÙÙ† التبويقات المبرمَجة + too_soon: يجب أن يكون تاريخ البرمجة ÙÙŠ المستقبَل sessions: activity: آخر نشاط browser: Ø§Ù„Ù…ØªØµÙØ @@ -871,7 +944,7 @@ ar: back: عودة إلى ماستدون delete: ØØ°Ù Ø§Ù„ØØ³Ø§Ø¨Ø§Øª development: التطوير - edit_profile: تعديل المل٠الشخصي + edit_profile: عدّل Ø§Ù„ØµÙØØ© التعريÙية export: تصدير البيانات featured_tags: الوسوم الشائعة identity_proofs: دلائل الهوية @@ -880,9 +953,11 @@ ar: migrate: تهجير Ø§Ù„ØØ³Ø§Ø¨ notifications: الإخطارات preferences: Ø§Ù„ØªÙØ¶ÙŠÙ„ات - profile: المل٠الشخصي + profile: المل٠التعريÙÙŠ relationships: Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹ÙˆÙ† والمتابَعون two_factor_authentication: Ø§Ù„Ù…ÙØµØ§Ø¯Ù‚Ø© Ø¨Ø®ÙØ·ÙˆÙŽØªÙŽÙŠÙ’Ù† + spam_check: + spam_detected: هذا إبلاغ تلقائي. تم اكتشا٠منشورات عشوائية غير مرغوب Ùيها. statuses: attached: description: 'Ù…ÙØ±ÙÙŽÙ‚: %{attached}' @@ -918,6 +993,13 @@ ar: private: لا يمكن تدبيس تبويق لم ÙŠÙنشر للعامة reblog: لا يمكن تثبيت ترقية poll: + total_votes: + few: "%{count} أصوات" + many: "%{count} أصوات" + one: صوت ÙˆØ§ØØ¯ %{count} + other: "%{count} صوتا" + two: صوتين %{count} + zero: بدون صوت %{count} vote: صوّت show_more: أظهر المزيد sign_in_to_participate: قم بتسجيل الدخول للمشاركة ÙÙŠ هذه Ø§Ù„Ù…ØØ§Ø¯Ø«Ø© @@ -939,6 +1021,10 @@ ar: contrast: ماستدون (تباين عالÙ) default: ماستدون (داكن) mastodon-light: ماستدون (ÙØ§ØªØ) + time: + formats: + default: "%b %d, %Y, %H:%M" + month: "%b %Y" two_factor_authentication: code_hint: قم بإدخال الرمز المÙوَلّد عبر تطبيق المصادقة للتأكيد description_html: ÙÙŠ ØØ§Ù„ ØªÙØ¹ÙŠÙ„ <strong>المصادقة بخطوتين </strong>ØŒ ÙØªØ³Ø¬ÙŠÙ„ الدخول يتطلب منك أن يكون بØÙˆØ²ØªÙƒ هاتÙÙƒ النقال قصد توليد الرمز الذي سيتم إدخاله. @@ -961,6 +1047,7 @@ ar: title: المغادرة Ø¨Ø£Ø±Ø´ÙŠÙ Ø§Ù„ØØ³Ø§Ø¨ warning: review_server_policies: مراجعة شروط Ø§Ù„Ø³ÙŠØ±ÙØ± + statuses: 'خصيصا لـ:' subject: disable: تم تجميد ØØ³Ø§Ø¨Ùƒ %{acct} none: ØªØØ°ÙŠØ± إلى %{acct} @@ -968,10 +1055,11 @@ ar: title: disable: Ø§Ù„ØØ³Ø§Ø¨ Ù…ÙØ¬Ù…َّد none: ØªØØ°ÙŠØ± + silence: Ø§Ù„ØØ³Ø§Ø¨ Ù…ØØ¯ÙˆØ¯ suspend: Ø§Ù„ØØ³Ø§Ø¨ Ù…ÙØ¹Ù„Ù‘ÙŽÙ‚ welcome: - edit_profile_action: تهيئة المل٠الشخصي - edit_profile_step: ÙŠÙمكنك·كي تخصيص ملÙÙƒ الشخصي عن طريق تØÙ…يل صورة رمزية ورأسية Ùˆ بتعديل اسمك·كي العلني وأكثر. Ùˆ إن أردت·تي معاينة Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹ÙŠÙ† Ùˆ المتابعات Ø§Ù„Ø¬ÙØ¯Ø¯ قبيل Ø§Ù„Ø³Ù…Ø§Ø Ù„Ù‡Ù…Â·Ù† بمتابَعتك Ùيمكنك·كي تأمين ØØ³Ø§Ø¨ÙƒÂ·ÙƒÙŠ. + edit_profile_action: تهيئة المل٠التعريÙÙŠ + edit_profile_step: ÙŠÙمكنك·كي تخصيص ØµÙØØªÙƒ التعريÙية عن طريق تØÙ…يل صورة رمزية ورأسية Ùˆ بتعديل اسمك·كي العلني وأكثر. Ùˆ إن أردت·تي معاينة Ø§Ù„Ù…ØªØ§Ø¨ÙØ¹ÙŠÙ† Ùˆ المتابعات Ø§Ù„Ø¬ÙØ¯Ø¯ قبيل Ø§Ù„Ø³Ù…Ø§Ø Ù„Ù‡Ù…Â·Ù† بمتابَعتك Ùيمكنك·كي تأمين ØØ³Ø§Ø¨ÙƒÂ·ÙƒÙŠ. explanation: ها هي بعض Ø§Ù„Ù†ØµØ§Ø¦Ø Ù‚Ø¨Ù„ بداية الاستخدام final_action: اشرَع ÙÙŠ النشر final_step: |- diff --git a/config/locales/ast.yml b/config/locales/ast.yml index ec545ca5786f965aa5998258571b1b3707c11129..ed8aef235433dc643b604981dc2de8cf48c7fb5e 100644 --- a/config/locales/ast.yml +++ b/config/locales/ast.yml @@ -8,9 +8,6 @@ ast: contact_missing: Nun s'afitó contact_unavailable: N/D documentation: Documentación - extended_description_html: | - <h3>Un llugar bonu pa les regles</h3> - <p>Entá nun se configuró la descripción estendida.</p> hosted_on: Mastodon ta agospiáu en %{domain} learn_more: Deprendi más source_code: Códigu fonte @@ -131,21 +128,19 @@ ast: half_a_minute: Púramente agora less_than_x_seconds: Púramente agora deletes: - bad_password_msg: "¡Bon intentu, crackers! Contraseña incorreuta" confirm_password: Introduz la contraseña pa verificar la to identidá - directories: - people: - one: "%{count} persona" - other: "%{count} persones" errors: + '400': The request you submitted was invalid or malformed. '403': Nun tienes permisu pa ver esta páxina. '404': La páxina que tabes guetando nun esiste. + '406': This page is not available in the requested format. '410': La páxina que tabes guetando yá nun esiste. '422': content: Falló la verificación de seguranza. ¿Tas bloquiando les cookies? title: Falló la verificación de seguranza '429': Ficiéronse milenta solicitúes '500': + '503': The page could not be served due to a temporary server failure. exports: archive_takeout: date: Data @@ -201,7 +196,6 @@ ast: too_many: Nun puen axuntase más de 4 ficheros migrations: acct: nome_usuariu@dominiu de la cuenta nueva - proceed: Guardar notification_mailer: digest: body: Equà hai un resume de los mensaxes que nun viesti dende la última visita'l %{since} @@ -227,8 +221,6 @@ ast: no_account_html: "¿Nun tienes una cuenta? Pues <a href='%{sign_up_path}' target='_blank'>rexistrate equÃ</a>" proceed: Siguir prompt: 'Vas siguir a:' - remote_unfollow: - error: Fallu sessions: browser: Restolador browsers: diff --git a/config/locales/bg.yml b/config/locales/bg.yml index e11340542fddec96cdf836bca27db8c8b236d27e..85431bc8dfd0cbd6f9f34d820e7ba9cc569a0052 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -48,12 +48,15 @@ bg: x_months: "%{count} м" x_seconds: "%{count} Ñек" errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. exports: blocks: Вашите Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð°Ð½Ð¸Ñ follows: Вашите ÑÐ»ÐµÐ´Ð²Ð°Ð½Ð¸Ñ diff --git a/config/locales/bn.yml b/config/locales/bn.yml index b3eb0bd624a6874bef540e85eeb401640c4072c1..9fdc3ed297f70e3263b7dde61413bf5513c60110 100644 --- a/config/locales/bn.yml +++ b/config/locales/bn.yml @@ -17,11 +17,7 @@ bn: contact_unavailable: পà§à¦°à¦¯à§‹à¦œà§à¦¯ নয় discover_users: বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারীদের দেখà§à¦¨ documentation: বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦¬à¦¿à¦²à¦¿ - extended_description_html: | - <h3>নিয়মের জনà§à¦¯ উপযà§à¦•à§à¦¤ জায়গা</h3> - <p>বিসà§à¦¤à¦¾à¦°à¦¿à¦¤ বিবরণ à¦à¦–নো যà§à¦•à§à¦¤ করা হয়নি</p> federation_hint_html: "%{instance}তে à¦à¦•টা নিবনà§à¦§à¦¨ থাকলে আপনি যেকোনো মাসà§à¦Ÿà¦¾à¦¡à¦¨ বা à¦à¦§à¦°à¦£à§‡à¦° অনà§à¦¯à¦¾à¦¨à§à¦¯ সারà§à¦à¦¾à¦°à§‡à¦° মানà§à¦·à§‡à¦° সাথে যà§à¦•à§à¦¤ হতে পারবেন ।" - generic_description: নেটওয়ারà§à¦•ের à¦à§‡à¦¤à¦°à§‡ %{domain} à¦à¦•টি সারà§à¦à¦¾à¦° get_apps: মোবাইল à¦à¦ªà§à¦ª à¦à¦•টা বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে পারেন hosted_on: à¦à¦‡ মাসà§à¦Ÿà¦¾à¦¡à¦¨à¦Ÿà¦¿ আছে %{domain} ঠlearn_more: বিসà§à¦¤à¦¾à¦°à¦¿à¦¤ জানà§à¦¨ @@ -140,12 +136,15 @@ bn: pending: পযà§à¦°à§à¦¯à¦¬à§‡à¦•à§à¦·à¦£à§‡à¦° অপেকà§à¦·à¦¾à§Ÿ আছে perform_full_suspension: বাতিল করা errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. invites: expires_in: '1800': 30 minutes diff --git a/config/locales/br.yml b/config/locales/br.yml new file mode 100644 index 0000000000000000000000000000000000000000..3710084e7df2f382222859c0539bc7c83bdf9cb0 --- /dev/null +++ b/config/locales/br.yml @@ -0,0 +1,20 @@ +--- +br: + errors: + '400': The request you submitted was invalid or malformed. + '403': You don't have permission to view this page. + '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. + '410': The page you were looking for doesn't exist here anymore. + '422': + '429': Throttled + '500': + '503': The page could not be served due to a temporary server failure. + invites: + expires_in: + '1800': 30 minutes + '21600': 6 hours + '3600': 1 hour + '43200': 12 hours + '604800': 1 week + '86400': 1 day diff --git a/config/locales/ca.yml b/config/locales/ca.yml index a5d96cc1caa55f6e83f680e01ab29d057a645abc..1d815adcfb2d2157005875d4304a1276e7821db3 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -17,13 +17,12 @@ ca: contact_unavailable: N/D discover_users: Descobreix usuaris documentation: Documentació - extended_description_html: | - <h3>Un bon lloc per les regles</h3> - <p>Encara no s'ha configurat la descripció ampliada.</p> federation_hint_html: Amb un compte de %{instance} podrà s seguir persones de qualsevol servidor Mastodon i altres. - generic_description: "%{domain} és un servidor a la xarxa" get_apps: Prova una aplicació mòbil hosted_on: Mastodon allotjat a %{domain} + instance_actor_flash: | + Aquest compte és un actor virtual utilitzat per a representar al propi servidor i no cap usuari individual. + S'utilitza per a propòsits de federació i no ha de ser bloquejat si no voleu bloquejar tota la instà ncia, en aquest cas haurÃeu d'utilitzar un bloqueig de domini. learn_more: Més informació privacy_policy: PolÃtica de privacitat see_whats_happening: Mira què està passant @@ -35,6 +34,14 @@ ca: status_count_before: Que han escrit tagline: Segueix els teus amics i descobreix-ne de nous terms: Termes del servei + unavailable_content: Contingut no disponible + unavailable_content_description: + domain: Servidor + reason: Raó + rejecting_media: 'Els arxius multimèdia d''aquests servidors no seran processats o emmagatzemats i cap miniatura serà mostrada, requerint clic manual a través de l''arxiu original:' + silenced: 'Les publicacions d''aquests servidors seran amagades en les lÃnies de temps públiques i converses, i cap notificació serà generada de les interaccions dels seus usuaris, llevat que estiguis seguint-los:' + suspended: 'Cap dada d''aquests servidors serà processada, emmagatzemada o intercanviada, fent impossible qualsevol interacció o comunicació amb els usuaris d''aquests servidors:' + unavailable_content_html: Mastodon generalment et permet per veure contingut i interaccionar amb usuaris de qualsevol altre servidor en el fedivers. Aquestes són les excepcions que s'han fet en aquest servidor particular. user_count_after: one: usuari other: usuaris @@ -42,6 +49,8 @@ ca: what_is_mastodon: Què és Mastodon? accounts: choices_html: 'Eleccions de %{name}:' + endorsements_hint: Pots recomanar persones que segueixes a l'interfÃcie de web, que apareixeran aquÃ. + featured_tags_hint: Pots presentar etiquetes especÃfiques que serà n mostrades aquÃ. follow: Segueix followers: one: Seguidor @@ -53,6 +62,7 @@ ca: media: Mèdia moved_html: "%{name} s'ha mogut a %{new_profile_link}:" network_hidden: Aquesta informació no està disponible + never_active: Mai nothing_here: No hi ha res aquÃ! people_followed_by: Usuaris seguits per %{name} people_who_follow: Usuaris que segueixen %{name} @@ -183,6 +193,7 @@ ca: username: Nom d'usuari warn: AvÃs web: Web + whitelisted: Llista blanca action_logs: actions: assigned_to_self_report: "%{name} han assignat l'informe %{target} a ells mateixos" @@ -218,19 +229,24 @@ ca: deleted_status: "(toot suprimit)" title: Registre d'auditoria custom_emojis: + assign_category: Assigna una categoria by_domain: Domini copied_msg: S'ha creat correctament la còpia local de l'emoji copy: Copia copy_failed_msg: No s'ha pogut fer una còpia local d'aquest emoji + create_new_category: Crea una categoria nova created_msg: Emoji creat amb èxit! delete: Suprimeix destroyed_msg: Emojo s'ha destruït amb èxit! disable: Inhabilita + disabled: Desactivat disabled_msg: S'ha inhabilitat l'emoji amb èxit emoji: Emoji enable: Habilita + enabled: Activat enabled_msg: S'ha habilitat amb èxit emoji image_hint: PNG de fins a 50 KB + list: Llista listed: Enumerat new: title: Afegeix emoji personalitzat nou @@ -238,11 +254,14 @@ ca: shortcode: Codi curt shortcode_hint: Com a mÃnim 2 carà cters, només carà cters alfanumèrics i guions baixos title: Emojis personalitzats + uncategorized: Sense categoria + unlist: No llistat unlisted: Sense classificar update_failed_msg: No s'ha pogut actualitzar aquest emoji updated_msg: Emoji s'ha actualitzat correctament! upload: Carrega dashboard: + authorized_fetch_mode: Mode d'obtenció autoritzat backlog: treballs en espera config: Configuració feature_deletions: Supressions del compte @@ -250,10 +269,13 @@ ca: feature_profile_directory: Directori de perfils feature_registrations: Registres feature_relay: Relay de la Federació + feature_spam_check: Anti-spam feature_timeline_preview: Vista previa de lÃnia de temps features: CaracterÃstiques hidden_service: Federació amb serveis ocults open_reports: informes oberts + pending_tags: etiquetes pendents de revisar + pending_users: usuaris pendents de revisar recent_users: Usuaris recents search: Cerca de text complet single_user_mode: Mode d'usuari únic @@ -265,11 +287,18 @@ ca: week_interactions: interaccions d'aquesta setmana week_users_active: usuaris actius aquesta setmana week_users_new: nous usuaris aquest setmana + whitelist_mode: Mode llista blanca + domain_allows: + add_new: Dominis autoritzats + created_msg: El domini ha estat correctament autoritzat + destroyed_msg: S'ha esborrat el domini de la llista blanca + undo: Treure de la llista blanca domain_blocks: add_new: Afegir nou bloqueig de domini created_msg: El bloqueig de domini ara s'està processant destroyed_msg: El bloqueig de domini s'ha desfet domain: Domini + edit: Editar el bloqueig del domini existing_domain_block_html: Ja has imposat uns limits més estrictes a %{name}, l'hauries de <a href="%{unblock_url}">desbloquejar-lo</a> primer. new: create: Crea un bloqueig @@ -280,6 +309,10 @@ ca: silence: Silenci suspend: Suspensió title: Bloqueig de domini nou + private_comment: Comentari privat + private_comment_hint: Comentari sobre aquesta limitació del domini per a ús intern dels moderadors. + public_comment: Comentari públic + public_comment_hint: Comentari pel públic general sobre aquesta limitació del domini, si la publicació de la llista de limitacions de domini està habilitada. reject_media: Rebutja els fitxers multimèdia reject_media_hint: Elimina els fitxers multimèdia emmagatzemats localment i impedeix baixar-ne cap en el futur. Irrellevant en les suspensions reject_reports: Rebutja informes @@ -299,6 +332,7 @@ ca: title: Desfés el bloqueig de domini de %{domain} undo: Desfés undo: Desfés el bloqueig del domini + view: Veure el bloqueig del domini email_domain_blocks: add_new: Afegir nou created_msg: S'ha creat el bloc de domini de correu electrònic @@ -322,6 +356,8 @@ ca: all: Totes limited: Limitades title: Moderació + private_comment: Comentari privat + public_comment: Comentari públic title: Federació total_blocked_by_us: Bloquejats per nosaltres total_followed_by_them: Seguits per ells @@ -351,6 +387,7 @@ ca: pending: S'està esperant l'aprovació del relay save_and_enable: Desa i activa setup: Configura una connexió de relay + signatures_not_enabled: Els relays no treballaran correctament mentre estiguin habilitats el mode segur o llista blanca status: Estat title: Relays report_notes: @@ -399,6 +436,16 @@ ca: custom_css: desc_html: Modifica l'aspecte amb CSS carregat a cada pà gina title: CSS personalitzat + default_noindex: + desc_html: Afecta a tots els usuaris que no han canviat aquest ajustament ells mateixos + title: Configurar per defecte als usuaris fora de l'indexació dels motor de cerca + domain_blocks: + all: Per a tothom + disabled: Per a ningú + title: Mostra els bloquejos de domini + users: Per als usuaris locals en lÃnia + domain_blocks_rationale: + title: Mostra el raonament hero: desc_html: Es mostra en pà gina frontal. Recomanat al menys 600x100px. Si no es configura es mostrarà el del servidor title: Imatge d’heroi @@ -449,6 +496,9 @@ ca: desc_html: Pots escriure la teva pròpia polÃtica de privadesa, els termes del servei o d'altres normes legals. Pots utilitzar etiquetes HTML title: Termes del servei personalitzats site_title: Nom del servidor + spam_check_enabled: + desc_html: Mastodon pot auto-silenciar i informar automà ticament de comptes basat en mesures com ara la detecció de comptes que envien missatges repetits no sol·licitats. Pot haver-hi falsos positius. + title: Anti-spam thumbnail: desc_html: S'utilitza per obtenir visualitzacions prèvies a través d'OpenGraph i API. Es recomana 1200x630px title: Miniatura del servidor @@ -456,12 +506,19 @@ ca: desc_html: Mostra la lÃnia de temps pública a la pà gina inicial title: Vista prèvia de la lÃnia de temps title: Configuració del lloc + trendable_by_default: + desc_html: Afecta a les etiquetes que no s'havien rebutjat prèviament + title: Permet que les etiquetes passin a la tendència sense revisió prèvia + trends: + desc_html: Mostra públicament les etiquetes revisades anteriorment que actualment estan en tendència + title: Etiquetes tendència statuses: back_to_account: Torna a la pà gina del compte batch: delete: Suprimeix nsfw_off: Marcar com a no sensible nsfw_on: Marcar com a sensible + deleted: Esborrats failed_to_execute: No s'ha pogut executar media: title: Contingut multimèdia @@ -469,21 +526,24 @@ ca: no_status_selected: No s’han canviat els estatus perquè cap no ha estat seleccionat title: Estats del compte with_media: Amb contingut multimèdia - subscriptions: - callback_url: URL de retorn - confirmed: Confirmat - expires_in: Expira en - last_delivery: Últim lliurament - title: WebSub - topic: Tema tags: - accounts: Comptes - hidden: Amagat - hide: Ocult del directori + accounts_today: Usos únics actuals + accounts_week: Usos únics d'aquesta setmana + breakdown: Desglossament de l’ús actual per origen + context: Context + directory: En el directori + in_directory: "%{count} en el directori" + last_active: Darrer actiu + most_popular: Més populars + most_recent: Més recent name: Etiqueta + review: Revisar l'estat + reviewed: Revisades title: Etiquetes - unhide: Mostra en el directori - visible: Visible + trending_right_now: Actualment en tendència + unique_uses_today: "%{count} publicacions d'avui" + unreviewed: Sense revisar + updated_msg: Ajustaments d'etiquetes actualitzats amb èxit title: Administració warning_presets: add_new: Afegir nou @@ -499,11 +559,21 @@ ca: body: "%{reporter} ha informat de %{target}" body_remote: Algú des de el domini %{domain} ha informat sobre %{target} subject: Informe nou per a %{instance} (#%{id}) + new_trending_tag: + body: 'La etiqueta #%{name} està actualment en tendència però no s''ha revisat prèviament. No es mostrarà públicament tret que ho permetis o guardis el formulari tal com està per a no tornar a escoltar res al respecte.' + subject: Nova etiqueta pendent de revisió a %{instance} (#%{name}) + aliases: + add_new: Crear à lies + created_msg: Nou à lies creat amb èxit. Ara pots iniciar el moviment des de'l compte vell. + deleted_msg: Àlies eliminat amb èxit. Moure't des d'aquell compte a aquests ja no serà possible. + hint_html: Si et vols moure des d'un altre compte a aquest, aquà pots crear un à lies, el qual és requerit abans que puguis procedir a moure els seguidors del compte vell a aquest. Aquesta acció és per si mateixa <strong>inofensiva i reversible</strong>. <strong>La migració del compte és iniciada des de'l compte vell</strong>. + remove: Desvincular l'à lies appearance: advanced_web_interface: InterfÃcie web avançada advanced_web_interface_hint: 'Si vols fer ús de tota l''amplada de la teva pantalla, l''interfÃcie web avançada et permet configurar diverses columnes per a veure molta més informació al mateix temps: Inici, notificacions, lÃnia de temps federada i qualsevol número de llistes i etiquetes.' animations_and_accessibility: Animacions i accessibilitat confirmation_dialogs: Dià legs de confirmació + discovery: Descobriment sensitive_content: Contingut sensible application_mailer: notification_preferences: Canvia les preferències de correu @@ -524,9 +594,13 @@ ca: apply_for_account: Demana una invitació change_password: Contrasenya checkbox_agreement_html: Estic d'acord amb les <a href="%{rules_path}" target="_blank">normes del servidor</a> i <a href="%{terms_path}" target="_blank"> els termes del servei</a> - confirm_email: Confirmar correu electrònic + checkbox_agreement_without_rules_html: Acepto els <a href="%{terms_path}" target="_blank">termes del servei</a> delete_account: Suprimeix el compte delete_account_html: Si vols suprimir el compte pots <a href="%{path}">fer-ho aquÃ</a>. Se't demanarà confirmació. + description: + prefix_invited_by_user: "@%{name} t'ha invitat a unir-te a aquest servidor de Mastodon!" + prefix_sign_up: Registra't avui a Mastodon! + suffix: Amb un compte serà s capaç de seguir persones, publicar i intercanviar missatges amb usuaris de qualsevol servidor de Mastodon i més! didnt_get_confirmation: No has rebut el correu de confirmació? forgot_password: Has oblidat la contrasenya? invalid_reset_password_token: L'enllaç de restabliment de la contrasenya no és và lid o ha caducat. Torna-ho a provar. @@ -544,6 +618,16 @@ ca: reset_password: Restableix la contrasenya security: Seguretat set_new_password: Estableix una contrasenya nova + setup: + email_below_hint_html: Si l’adreça de correu electrònic següent és incorrecta, podeu canviar-la aquà i rebre un nou correu electrònic de confirmació. + email_settings_hint_html: El correu electrònic de confirmació es va enviar a %{email}. Si aquesta adreça de correu electrònic no és correcta, la podeu canviar a la configuració del compte. + title: Configuració + status: + account_status: Estat del compte + confirming: Esperant que es completi la confirmació del correu electrònic. + functional: El teu compte és plenament operatiu. + pending: La vostra sol·licitud està pendent de revisió pel nostre personal. Això pot trigar una mica. Rebreu un correu electrònic quan sigui aprovada. + redirecting_to: El teu compte és inactiu perquè actualment està redirigint a %{acct}. trouble_logging_in: Problemes per iniciar la sessió? authorize_follow: already_following: Ja està s seguint aquest compte @@ -556,6 +640,11 @@ ca: return: Mostra el perfil de l'usuari web: Vés a la web title: Segueix %{acct} + challenge: + confirm: Continua + hint_html: "<strong>Pista:</strong> No et preguntarem un altre cop la teva contrasenya en la pròxima hora." + invalid_password: Contrasenya no và lida + prompt: Confirmi la contrasenya per a continuar datetime: distance_in_words: about_x_hours: "%{count} h" @@ -571,26 +660,33 @@ ca: x_months: "%{count} mesos" x_seconds: "%{count} s" deletes: - bad_password_msg: Bon intent hackers! La contrasenya no és correcta + challenge_not_passed: L'informació que has entrat no és correcte confirm_password: Introdueix la contrasenya actual per a verificar la identitat - description_html: Això eliminarà de forma <strong>irreversible i permanent</strong> el contingut del teu compte i el desactivarà . El teu nom d'usuari romandrà reservat per evitar que algú volgués fer-se passar per tu. + confirm_username: Escriu el teu nom d'usuari per a confirmar el procediment proceed: Suprimeix el compte success_msg: El compte s'ha eliminat correctament - warning_html: Només és garantida l'eliminació del contingut d'aquest servidor en particular. El contingut que s'ha compartit à mpliament deixa petjades. Els servidors fora de lÃnia i els que ja no estan subscrits no actualitzaran les seves bases de dades. - warning_title: Disponibilitat de contingut disseminat + warning: + before: 'Abans de procedir si us plau llegeix amb cura aquestes notes:' + caches: El contingut que ha estat memoritzat en la memòria cau per altres servidors pot persistir + data_removal: Les teves publicacions i altres dades seran permanentment eliminades + email_change_html: Pots <a href="%{path}">canviar la teva adreça de correu electrònic</a> sense eliminar el teu compte + email_contact_html: Si encara no arriba pots enviar un correu electrònic a <a href="mailto:%{email}">%{email}</a> per a demanar ajuda + email_reconfirmation_html: Si no està s rebent el correu electrònic de confirmació <a href="%{path}">pots demanar-lo un altre cop</a> + irreversible: No serà s capaç de restaurar o reactivar el teu compte + more_details_html: Per a més detalls, llegeix la <a href="%{terms_path}">polÃtica de privadesa</a>. + username_available: El teu nom d'usuari esdevindrà altre cop disponible + username_unavailable: El teu nom d'usuari quedarà inutilitzable directories: directory: Directori de perfils - enabled: Actualment està s inclòs al directori. - enabled_but_waiting: Has optat per aparèixer al directori però encara no tens el nombre mÃnim de seguidors (%{min_followers}) per ser-hi. explanation: Descobreix usuaris segons els seus interessos explore_mastodon: Explora %{title} - how_to_enable: Actualment no tens activat ser al directori. Pots optar-hi a continuació. Utilitza etiquetes en el teu text bio per incloure't sota etiquetes especifiques! - people: - one: "%{count} persona" - other: "%{count} gent" + domain_validator: + invalid_domain: no es un nom de domini và lid errors: + '400': La sol·licitud que vas emetre no era và lida o no era correcta. '403': No tens permÃs per a veure aquesta pà gina. '404': La pà gina que està s cercant no és aquÃ. + '406': Aquesta pà gina no está disponible en el format demanat. '410': La pà gina que està s cercant ja no existeix. '422': content: La verificació de seguretat ha fallat. Tens les galetes blocades? @@ -599,6 +695,7 @@ ca: '500': content: Ho sentim, però alguna cosa ha fallat a la nostra banda. title: Aquesta pà gina no es correcta + '503': La pà gina no podria ser servida a causa d'un error temporal del servidor. noscript_html: Per a utilitzar Mastodon, activa el JavaScript. També pots provar una de les <a href="%{apps_path}"> aplicacions natives</a> de Mastodon per a la vostra plataforma. existing_username_validator: not_found: no s'ha pogut trobar cap usuari local amb aquest nom d'usuari @@ -622,6 +719,7 @@ ca: add_new: Afegir nova errors: limit: Ja has mostrat la quantitat mà xima d'etiquetes + hint_html: "<strong>Què son les etiquetes destacades?</strong> Es mostren de manera destacada en el teu perfil públic i permeten a les persones navegar per les teves publicacions amb aquestes etiquetes. Són una gran eina per fer un seguiment de treballs creatius o de projectes a llarg termini." filters: contexts: home: LÃnia de temps Inici @@ -642,10 +740,12 @@ ca: developers: Desenvolupadors more: Més… resources: Recursos + trending_now: En tendència generic: all: Tot changes_saved_msg: Els canvis s'han desat correctament! copy: Copia + no_batch_actions_available: Cap accions de lot disponibles en aquesta pà gina order_by: Ordena per save_changes: Desa els canvis validation_errors: @@ -717,9 +817,34 @@ ca: too_many: No es poden adjuntar més de 4 fitxers migrations: acct: usuari@domini del nou compte - currently_redirecting: 'El teu perfil està configurat com a redirecció a:' - proceed: Desa - updated_msg: La configuració de la migració del compte s'ha actualitzat correctament! + cancel: Cancel·la redirecció + cancel_explanation: Cancel·lant la redirecció reactivará el teu compte actual però no recuperarà els seguidors que han estat moguts a aquell compte. + cancelled_msg: Redirecció cancel·lada amb èxit. + errors: + already_moved: és el mateix compte al que ja t'has mogut + missing_also_known_as: no fa referencia a aquest compte + move_to_self: no pot ser el compte actual + not_found: podria no ser trobat + on_cooldown: Està s en temps de recuperació + followers_count: Seguidors en el moment del moviment + incoming_migrations: Movent des d'un compte diferent + incoming_migrations_html: Per a moure't des d'un altre compte a aquest, primer necessites <a href="%{path}">crear un à lies de compte</a>. + moved_msg: El teu compte ara està redirigint a %{acct} i els teus seguidors estan sent moguts. + not_redirecting: El teu compte no està redirigint actualment a cap altre. + on_cooldown: Recentment has emigrat el teu compte. Aquesta funció esdevindrà un altre cop disponible en %{count} dies. + past_migrations: Migracions passades + proceed_with_move: Moure seguidors + redirecting_to: El teu compte està redirigint a %{acct}. + set_redirect: Ajusta la redirecció + warning: + backreference_required: El compte nou primer ha de ser configurat per a referenciar aquest + before: 'Abans de procedir si us plau llegeix amb cura aquestes notes:' + cooldown: Després de moure't hi ha un perÃode de recuperació durant el qual no serà s capaç de moure't un altre cop + disabled_account: El teu compte actual no serà plenament utilitzable després. Tanmateix, tindrà s accés a exportació de dades aixà com reactivació. + followers: Aquesta acció mourà tots els seguidors des de l'actual al compte nou + only_redirect_html: Alternativament, pots <a href="%{path}">posar només una redirecció en el teu perfil</a>. + other_data: Cap altre dada serà moguda automà ticament + redirect: El perfil del teu compte actual serà actualitzat amb un avÃs de redirecció i serà exclòs de les cerques moderation: title: Moderació notification_mailer: @@ -816,10 +941,6 @@ ca: reply: proceed: Procedir a respondre prompt: 'Vols respondre a aquest toot:' - remote_unfollow: - error: Error - title: TÃtol - unfollowed: Sense seguir scheduled_statuses: over_daily_limit: Has superat el lÃmit de %{limit} toots programats per a aquell dia over_total_limit: Has superat el limit de %{limit} toots programats @@ -868,6 +989,7 @@ ca: settings: account: Compte account_settings: Ajustos del compte + aliases: Àlies de compte appearance: Aparènça authorized_apps: Aplicacions autoritzades back: Torna a Mastodon @@ -908,6 +1030,9 @@ ca: private: No es pot fixar el toot no públic reblog: No es pot fixar un impuls poll: + total_people: + one: "%{count} persona" + other: "%{count} persones" total_votes: one: "%{count} vot" other: "%{count} vots" @@ -926,6 +1051,8 @@ ca: pinned: Toot fixat reblogged: ha impulsat sensitive_content: Contingut sensible + tags: + does_not_match_previous_name: no coincideix amb el nom anterior terms: body_html: | <h2>PolÃtica de Privacitat</h2> @@ -1043,7 +1170,9 @@ ca: disable: Mentre el teu compte estigui congelat les dades romandran intactes però no pots dur a terme cap acció fins que no estigui desbloquejat. silence: Mentre el teu compte estigui limitat només les persones que ja et segueixen veuen les teves dades en aquest servidor i pots ser exclòs de diverses llistes públiques. No obstant això, d'altres encara poden seguir-te manualment. suspend: El teu compte s'ha suspès i tots els teus toots i fitxers multimèdia penjats s'han eliminat irreversiblement d'aquest servidor i dels servidors on tenies seguidors. + get_in_touch: Pots respondre a aquest correu electrònic per a contactar amb el personal de %{instance}. review_server_policies: Revisa les polÃtiques del servidor + statuses: 'Concretament, per:' subject: disable: S'ha congelat el teu compte %{acct} none: AvÃs per a %{acct} diff --git a/config/locales/co.yml b/config/locales/co.yml index b3d14fdb538a194969c0b39a2dd5faab9214c729..41e2ccfbbf58312dc6093ba48842799cc174745d 100644 --- a/config/locales/co.yml +++ b/config/locales/co.yml @@ -17,13 +17,12 @@ co: contact_unavailable: Micca dispunibule discover_users: Scopre utilizatori documentation: Ducumentazione - extended_description_html: | - <h3>Una bona piazza per e regule</h3> - <p>A descrizzione stesa ùn hè micca stata riempiuta.</p> federation_hint_html: Cù un contu nant'à %{instance} puderete siguità ghjente da tutti i servori Mastodon è ancu più d'altri. - generic_description: "%{domain} hè un servore di a rete" get_apps: Pruvà un'applicazione di telefuninu hosted_on: Mastodon allughjatu nant’à %{domain} + instance_actor_flash: | + Stu contu ghjè un'attore virtuale chì ghjove à riprisentà u servore sanu è micca un veru utilizatore. + Hè utilizatu da a federazione è ùn deve micca esse bluccatu eccettu s'e voi vulete bluccà tuttu u servore, in quellu casu duvereste utilizà un blucchime di duminiu. learn_more: Amparà di più privacy_policy: Pulitica di vita privata see_whats_happening: Vede cio chì si passa @@ -35,6 +34,14 @@ co: status_count_before: chì anu pubblicatu tagline: Siguità amichi è scopre ancu di più altri terms: Cundizione di u serviziu + unavailable_content: Cuntinutu micca dispunibule + unavailable_content_description: + domain: Servore + reason: 'Ragione:' + rejecting_media: I fugliali media da stu servore ùn saranu micca arregistrati è e vignette ùn saranu micca affissate, duverete cliccà manualmente per accede à l'altru servore è vedeli. + silenced: I statuti da stu servore ùn saranu mai visti tranne nant'a vostra pagina d'accolta s'e voi siguitate l'autore. + suspended: Ùn puderete micca siguità qualsiasi nant'à stu servore, i dati versu o da quallà ùn saranu mai accessi, scambiati o arregistrati. + unavailable_content_html: Mastodon vi parmette in generale di vede u cuntinutu è interagisce cù l'utilizatori di tutti l'altri servori di u fediversu. Quessi sò l'eccezzione fatte nant'à stu servore in particulare. user_count_after: one: utilizatore other: utilizatori @@ -42,6 +49,8 @@ co: what_is_mastodon: Quale hè Mastodon? accounts: choices_html: "%{name} ricumanda:" + endorsements_hint: Pudete appughjà i conti chì siguitate dapoi l'interfaccia web, è saranu mustrati quì. + featured_tags_hint: Pudete mette in mostra qualchì hashtag chì saranu affissatu quì. follow: Siguità followers: one: Abbunatu·a @@ -53,6 +62,7 @@ co: media: Media moved_html: "%{name} hà cambiatu di contu, avà hè nant’à %{new_profile_link}:" network_hidden: St'infurmazione ùn hè micca dispunibule + never_active: Mai nothing_here: Ùn c’hè nunda quì! people_followed_by: Seguitati da %{name} people_who_follow: Seguitanu %{name} @@ -183,6 +193,7 @@ co: username: Cugnome warn: Averte web: Web + whitelisted: In a lista bianca action_logs: actions: assigned_to_self_report: "%{name} s’hè assignatu u signalamentu %{target}" @@ -218,31 +229,39 @@ co: deleted_status: "(statutu sguassatu)" title: Ghjurnale d’audit custom_emojis: + assign_category: Aghjunghje categuria by_domain: Duminiu copied_msg: Copia lucale di l’emoji creata copy: Cupià copy_failed_msg: Ùn s’hè micca pussutu creà una copia di l’emoji + create_new_category: Creà nova categuria created_msg: L’emoji hè stata creata! delete: Toglie destroyed_msg: L’emoji hè stata tolta! disable: Disattivà + disabled: Disattivatu disabled_msg: L’emoji hè stata disattivata emoji: Emoji enable: Attivà + enabled: Attivate enabled_msg: L’emoji hè stata attivata image_hint: PNG di 50Ko o menu + list: Listà listed: Listata new: - title: Aghjustà una nov’emoji + title: Aghjunghje una nov’emoji overwrite: Soprascrive shortcode: Accorta shortcode_hint: 2 caratteri o più, solu lettere, numeri è liniette basse title: Emoji parsunalizate + uncategorized: Micca categurizatu + unlist: Slistà unlisted: Micca listata update_failed_msg: Ùn s’hè micca pussutu mette à ghjornu l’emoji updated_msg: L’emoji hè stata messa à ghjornu! upload: Caricà dashboard: + authorized_fetch_mode: Modu ricuperazione auturizata backlog: travagli in attesa config: Cunfigurazione feature_deletions: Sguassamenti di conti @@ -250,10 +269,13 @@ co: feature_profile_directory: Annuariu di i prufili feature_registrations: Arregistramenti feature_relay: Ripetitore di federazione + feature_spam_check: Anti-spam feature_timeline_preview: Vista di a linea pubblica features: Funziunalità hidden_service: Federazione cù servizii piattati open_reports: signalamenti aperti + pending_tags: hashtag in attesa di verificazione + pending_users: utilizatori in attesa di rivista recent_users: Utilizatori ricenti search: Ricerca di testu sanu single_user_mode: Modu utilizatore unicu @@ -265,11 +287,18 @@ co: week_interactions: interazzione sta settimana week_users_active: attivi sta settimana week_users_new: utilizatori sta settimana + whitelist_mode: Modu lista bianca + domain_allows: + add_new: Aghjunghje à a lista bianca + created_msg: U duminiu hè statu aghjuntu à a lista bianca + destroyed_msg: U duminiu hè statu sguassatu da a lista bianca + undo: Toglie di a lista bianca domain_blocks: - add_new: Aghjustà novu blucchime di duminiu + add_new: Aghjunghje novu blucchime di duminiu created_msg: U blucchime di u duminiu hè attivu destroyed_msg: U blucchime di u duminiu ùn hè più attivu domain: Duminiu + edit: Mudificà u blucchime di duminiu existing_domain_block_html: Avete digià impostu limite più strette nant'à %{name}, duvete <a href="%{unblock_url}">sbluccallu</a> primu. new: create: Creà un blucchime @@ -280,6 +309,10 @@ co: silence: Silenzà suspend: Suspende title: Novu blucchime di duminiu + private_comment: Cummentariu privatu + private_comment_hint: Cummentariu nant'à a limitazione di stu duminiu per l'usu internu di i muderatori. + public_comment: Cummentariu pubblicu + public_comment_hint: Cummentariu nant'à a limitazione di stu duminiu per u pubblicu generale, s'ella hè attivata a rivelazione di a lista di limitazione di duminiu. reject_media: Righjittà i fugliali media reject_media_hint: Sguassa tutti i media caricati è ricusa caricamenti futuri. Inutile per una suspensione reject_reports: Righjittà i rapporti @@ -299,8 +332,9 @@ co: title: Ùn bluccà più u duminiu %{domain} undo: Annullà undo: Annullà u blucchime di duminiu + view: Vede blucchime di duminiu email_domain_blocks: - add_new: Aghjustà + add_new: Aghjunghje created_msg: U blucchime di u duminiu d’e-mail hè attivu delete: Toglie destroyed_msg: U blucchime di u duminiu d’e-mail ùn hè più attivu @@ -322,6 +356,8 @@ co: all: Tuttu limited: Limitatu title: Muderazione + private_comment: Cummentariu privatu + public_comment: Cummentariu pubblicu title: Federazione total_blocked_by_us: Bluccati da noi total_followed_by_them: Siguitati da elli @@ -339,7 +375,7 @@ co: pending_accounts: title: Conti in attesa (%{count}) relays: - add_new: Aghjustà un ripetitore + add_new: Aghjunghje un ripetitore delete: Sguassà description_html: Un <strong>ripetitore di federazione</strong> ghjè un servore intermediariu chì manda statuti pubblichi trà l'istanze abbunate. <strong>Pò aiutà l'istanze chjuche è mezane à scuprì u cuntinutu di u fediverse</strong> senza chì l'utilizatori appianu bisognu di seguità tutti i conti di l'altri servori. disable: Disattivà @@ -351,6 +387,7 @@ co: pending: In attesa di l'apprubazione di u ripetitore save_and_enable: Salvà è attivà setup: Creà una cunnessione cù un ripetitore + signatures_not_enabled: I ripetitori ùn marchjeranu micca currettamente mentre chì u modu sicurizatu o à lista bianca hè attivatu status: Statutu title: Ripetitori report_notes: @@ -399,6 +436,16 @@ co: custom_css: desc_html: Mudificà l'apparenza cù CSS caricatu nant'à ogni pagina title: CSS persunalizatu + default_noindex: + desc_html: Tocca tutti quelli ch'ùn anu micca cambiatu stu parametru + title: Ritirà l'utilizatori di l'indicazione nant'à i mutori di ricerca + domain_blocks: + all: À tutti + disabled: À nimu + title: Mustrà blucchime di duminiu + users: À l'utilizatori lucali cunnettati + domain_blocks_rationale: + title: Vede ragiò hero: desc_html: Affissatu nant’a pagina d’accolta. Ricumandemu almenu 600x100px. S’ellu ùn hè micca definiti, a vignetta di u servore sarà usata title: Ritrattu di cuprendula @@ -449,6 +496,9 @@ co: desc_html: Quì pudete scrive e vostre regule di cunfidenzialità , cundizione d’usu o altre menzione legale. Pudete fà usu di marchi HTML title: Termini persunalizati site_title: Nome di u servore + spam_check_enabled: + desc_html: Mastodon pò autosilenzà è autosignalà conti, per esempiu ditettendu quelli chì mandanu missaghji micca sullicitati à ripetizione. Ci ponu esse falzi pusitivi. + title: Anti-spam thumbnail: desc_html: Utilizatu per viste cù OpenGraph è l’API. Ricumandemu 1200x630px title: Vignetta di u servore @@ -456,37 +506,47 @@ co: desc_html: Vede a linea pubblica nant’a pagina d’accolta title: Vista di e linee title: Parametri di u situ + trendable_by_default: + desc_html: Ùn affetta micca quelli chì sò digià stati ricusati + title: Auturizà l'hashtag à esse in tindenze senza verificazione + trends: + desc_html: Mustrà à u pubblicu i hashtag chì sò stati digià verificati è chì sò in e tendenze avà + title: Tendenze di hashtag statuses: back_to_account: Ritornu à a pagina di u contu batch: delete: Toglie nsfw_off: Indicà cum’è micca sensibile nsfw_on: Indicà cum’è sensibile + deleted: Sguassatu failed_to_execute: Esecuzione impussibule media: title: Media no_media: Nisun media no_status_selected: I statuti ùn sò micca stati mudificati perchè manc'unu era selezziunatu - title: Statutu di u contu + title: Statuti di u contu with_media: Cù media - subscriptions: - callback_url: URL di richjama - confirmed: Cunfirmatu - expires_in: Spira in - last_delivery: Ultima arricata - title: WebSub - topic: Sughjettu tags: - accounts: Conti - hidden: Piattatu - hide: Piattà di l'annuariu + accounts_today: Usi unichi oghji + accounts_week: Usi unichi sta settimana + breakdown: Ditagli di l'usu d'oghji per origine + context: Cuntestu + directory: In l'annuariu + in_directory: "%{count} in l'annuariu" + last_active: Ultima attività + most_popular: Più pupulari + most_recent: Più ricente name: Hashtag + review: Statutu di verificazione + reviewed: Verificatu title: Hashtag - unhide: Mustrà in l'annuariu - visible: Visibile + trending_right_now: Tendenze avà + unique_uses_today: "%{count} pustendu oghji" + unreviewed: Micca verificatu + updated_msg: Parametri di i hashtag messi à ghjornu title: Amministrazione warning_presets: - add_new: Aghjustà nova + add_new: Aghjunghje delete: Sguassà edit: Cambià edit_preset: Cambià a preselezzione d'avertimentu @@ -499,11 +559,21 @@ co: body: "%{reporter} hà palisatu %{target}" body_remote: Qualch’unu da %{domain} hà palisatu %{target} subject: Novu signalamentu nant’à %{instance} (#%{id}) + new_trending_tag: + body: 'U hashtag #%{name} hè in e tendenze oghji, mà ùn hè micca verificatu. Ùn sarà micca mustratu à u pubblicu eccettu s''ellu hè auturizatu, o pudete ancu salvà u furmulariu cusì per ùn mai più avè à ne sente parlà .' + subject: Novu hashtag in attesa di rivista nant'à %{instance} (#%{name}) + aliases: + add_new: Creà un pseudonimu + created_msg: Novu pseudonimu creatu. Pudete avà inizià u trasferimentu da u vechju contu. + deleted_msg: U pseudonimu hè statu sguassatu. Ùn si puderà più migrà da questu contu à quellu. + hint_html: Per traslucà da un altru contu à questu, quì pudete creà un pseudonimu o "alias", riquisitu per trasferì l'abbunati da u vechju contu à u novu. St'azzione sola <strong>ùn face nunda</strong> è pò esse <strong>annullata senza prublemi</strong>. <strong>A migrazione hè principiata dapoi u vechju contu</strong>. + remove: Sguassà u pseudonimu appearance: advanced_web_interface: Interfaccia web avanzata advanced_web_interface_hint: 'S''è voi vulete fà usu di a larghezza sana di u vostru screnu, l''interfaccia web avanzata vi permette di cunfigurà parechje culonne sfarente per vede tutta l''infurmazione chì vulete vede in listessu tempu: Accolta, nutificazione, linea pubblica, è tutti l''hashtag è liste chì vulete.' animations_and_accessibility: Animazione è accessibilità confirmation_dialogs: Pop-up di cunfirmazione + discovery: Scuperta sensitive_content: Cuntinutu sensibile application_mailer: notification_preferences: Cambià e priferenze e-mail @@ -524,9 +594,13 @@ co: apply_for_account: Dumandà un'invitazione change_password: Chjave d’accessu checkbox_agreement_html: Sò d'accunsentu cù e <a href="%{rules_path}" target="_blank">regule di u servore</a> è i <a href="%{terms_path}" target="_blank">termini di u serviziu</a> - confirm_email: Cunfirmà l’e-mail + checkbox_agreement_without_rules_html: Accettu i <a href="%{terms_path}" target="_blank">termini di u serviziu</a> delete_account: Sguassà u contu delete_account_html: S’è voi vulete toglie u vostru contu <a href="%{path}">ghjè quì</a>. Duverete cunfirmà a vostra scelta. + description: + prefix_invited_by_user: "@%{name} v'invita à raghjunghje stu servore di Mastodon!" + prefix_sign_up: Arregistratevi nant'à Mastodon oghji! + suffix: Cù un contu, puderete siguità l'altri, pustà statuti è scambià missaghji cù l'utilizatori di tutti i servori Mastodon è ancu di più! didnt_get_confirmation: Ùn avete micca ricevutu l’istruzione di cunfirmazione? forgot_password: Chjave scurdata? invalid_reset_password_token: U ligame di riinizializazione di a chjave d’accessu hè spiratu o ùn hè micca validu. Pudete dumandà un'altru ligame. @@ -544,6 +618,16 @@ co: reset_password: Cambià a chjave d’accessu security: Sicurità set_new_password: Creà una nova chjave d’accessu + setup: + email_below_hint_html: S'è l'indirizzu e-mail quì sottu ùn hè micca currettu, pudete cambiallu quì è riceve un novu e-mail di cunfirmazione. + email_settings_hint_html: L'e-mail di cunfirmazione hè statu mandatu à l'indirizzu %{email}. S'ellu ùn hè micca currettu, pudete cambiallu in i parametri di u contu. + title: Stallazione + status: + account_status: Statutu di u contu + confirming: In attesa di a cumplezzione di a cunfirmazione di l'e-mail. + functional: U vostru contu hè uperaziunale. + pending: A vostra dumanda hè in attesa di rivista da a squadra di muderazione. Quessa pò piglià un certu tempu. Avete da riceve un'e-mail s'ella hè appruvata. + redirecting_to: U vostru contu hè inattivu perchè riindirizza versu %{acct}. trouble_logging_in: Difficultà per cunnettavi? authorize_follow: already_following: Site digià abbunatu·a à stu contu @@ -556,6 +640,11 @@ co: return: Vede u prufile di l’utilizatore web: Andà à l’interfaccia web title: Siguità %{acct} + challenge: + confirm: Cuntinuvà + hint_html: "<strong>Astuzia:</strong> Ùn avemu micca da dumandavvi stu codice per l'ore chì vene." + invalid_password: Chjave d'accessu micca curretta + prompt: Cunfirmà a chjave d'accessu per cuntinuvà datetime: distance_in_words: about_x_hours: "%{count}o" @@ -571,26 +660,33 @@ co: x_months: "%{count}Me" x_seconds: "%{count}s" deletes: - bad_password_msg: È nò! Sta chjave ùn hè curretta + challenge_not_passed: L'infurmazione entrata ùn era micca curretta confirm_password: Entrate a vostra chjave d’accessu attuale per verificà a vostra identità - description_html: U contu sarà deattivatu è u cuntenutu sarà sguassatu di manera <strong>permanente è irreversibile</strong>. Ùn sarà micca pussibule piglià stu cugnome torna per evità l’impusture. + confirm_username: Entrà u vostru cugnome per cunfirmà a prucedura proceed: Sguassà u contu success_msg: U vostru contu hè statu sguassatu - warning_html: Pudete esse sicuru·a solu chì u cuntenutu sarà sguassatu di stu servore. S’ellu hè statu spartutu in altrò, sarà forse sempre quallà . I servori scunettati è quelli ch'ùn sò più abbunati à e vostre pubblicazione ùn anu micca da mette à ghjornu e so database. - warning_title: Dispunibilità di i cuntenuti sparsi + warning: + before: 'Nanz''à cuntinuà , leghjete ste note attentamente:' + caches: U cuntinutu indè u cache di l'altri servori sarà forse cunservatu + data_removal: I vostri posti è dati saranu sguassati di manera permanente + email_change_html: Pudete <a href="%{path}">cambià u vostr'indirizzu e-mail</a> senza toglie u vostru contu + email_contact_html: S'ellu ùn hè sempre micca rimettu, pudete dumandà aiutu à <a href="mailto:%{email}">%{email}</a> + email_reconfirmation_html: S'e voi ùn ricevete micca l'e-mail di cunfirmazione, pudete <a href="%{path}">richiestà un'altru</a> + irreversible: Ùn puderete micca ricuperà o riattivà u vostru contu + more_details_html: Per più di ditagli, videte a <a href="%{terms_path}">pulitica di vita privata</a>. + username_available: U vostru cugnome riduvinterà dispunibule + username_unavailable: U vostru cugnome ùn sarà sempre micca dispunibule directories: directory: Annuariu di i prufili - enabled: Site inscrittu·a indè l'annuariu. - enabled_but_waiting: Avete sceltu d'esse inscrittu·a indè l'annuariu, mà ùn avete micca ancu u numeru minimale d'abbunati (%{min_followers}) per esse listatu·a. explanation: Scopre utilizatori à partesi di i so centri d'interessu explore_mastodon: Scopre à %{title} - how_to_enable: Ùn site micca ancu inscrittu·a indè l'annuariu. Pudete inscrive vi quì sottu. Utilizate qualchi hashtag indè a vostra biugrafia per esse listatu·a indè tag specifichi! - people: - one: "%{count} persona" - other: "%{count} persone" + domain_validator: + invalid_domain: ùn hè micca un nome di duminiu currettu errors: + '400': A richiesta mandata ùn era micca valida o curretta. '403': Ùn site micca auturizatu·a à vede sta pagina. '404': Sta pagina ùn esiste micca quì. + '406': A pagina ùn hè micca dispunivule in u furmatu riquisitu. '410': Sta pagina ùn esiste più quì. '422': content: C’hè statu un prublemu cù a verificazione di sicurità . Forse bluccate cookies? @@ -599,6 +695,7 @@ co: '500': content: Scusate, mà c’hè statu un prublemu cù u nostru servore. title: Sta pagina ùn hè curretta + '503': A pagina ùn hè micca stata servita per via di un prublemu timpuraneu di u servore. noscript_html: Mastodon nant’à u web hà bisognu di JavaScript per funziunà . Pudete ancu pruvà <a href="%{apps_path}">l’applicazione native</a> per a vostra piattaforma. existing_username_validator: not_found: ùn si pudeva micca truvà un'utilizatore lucale cù stu cugnome @@ -619,9 +716,10 @@ co: mutes: Piattate storage: I vostri media featured_tags: - add_new: Aghjustà novu + add_new: Aghjunghje errors: limit: Avete digià messu in mostra u numeru massimale di hashtag + hint_html: "<strong>Quale sò i hashtag in mostra?</strong> Sò messi in vista nant'à u vostru prufile pubblicu è permettenu à a ghjente di vede i vostri statuti ch'annu stu hashtag. Sò una bona manere di mustrà e vostre opere creative o i prughjetti à longu termine." filters: contexts: home: Accolta @@ -637,15 +735,17 @@ co: delete: Toglie title: Filtri new: - title: Aghjustà un novu filtru + title: Aghjunghje un novu filtru footer: developers: Sviluppatori more: Di più… resources: Risorze + trending_now: Tindenze d'avà generic: all: Tuttu changes_saved_msg: Cambiamenti salvati! copy: Cupià + no_batch_actions_available: Alcun'azzione di gruppu nant'à sta pagina order_by: Urdinà da save_changes: Salvà e mudificazione validation_errors: @@ -674,7 +774,7 @@ co: imports: modes: merge: Unisce - merge_long: Cunservà i dati esistenti è aghjustà i novi + merge_long: Cunservà i dati esistenti è aghjunghje i novi overwrite: Soprascrive overwrite_long: Rimpiazzà i dati esistenti cù i novi preface: Pudete impurtà certi dati, cumu e persone chì seguitate o bluccate nant’à u vostru contu, nant’à stu servore à partesi di fugliali creati nant’à un’altru. @@ -717,9 +817,34 @@ co: too_many: Ùn si pò micca aghjunghje più di 4 fugliali migrations: acct: cugnome@duminiu di u novu contu - currently_redirecting: 'U vostru prufile riindiriza tuttu versu à :' - proceed: Salvà - updated_msg: I paramettri di migrazione sò stati messi à ghjornu! + cancel: Annullà ridirezzione + cancel_explanation: L'annullazione di a ridirezzione hà da riattivà stu contu, mà ùn si puderà micca ricuperà l'abbunati chì sò digià stati trasferriti à l'altru contu. + cancelled_msg: Ridirezzione annullata. + errors: + already_moved: hè digià u contu induve avede traslucatu + missing_also_known_as: ùn fà micca riferenza à stu contu + move_to_self: ùn pò micca esse u contu attuale + not_found: ùn hè micca statu trovu + on_cooldown: Perioda di ricuperazione + followers_count: Abbunati à u mumentu di trasferimentu + incoming_migrations: Traslucà da un'altru contu + incoming_migrations_html: Per spustà da stu contu à un'altru, primu duvete <a href="%{path}">creà un pseudonimu di contu</a>. + moved_msg: Avà u vostru contu riindirizza versu %{acct} è i vostri abbunati sò in corsu di trasferimentu. + not_redirecting: U vostru contu ùn riindirizza micca ancu versu un'altru contu. + on_cooldown: Avede digià migratu u vostru contu. Sta funzionne sarà torna dispunibule in %{count} ghjorni. + past_migrations: Anziane migrazione + proceed_with_move: Trasferì l'abbunati + redirecting_to: U vostru contu riindirizza versu à %{acct}. + set_redirect: Creà ridirezzione + warning: + backreference_required: U novu contu deve prima esse cunfiguratu per fà rifirenza cù un pseudonimu à quessu contu + before: 'Nanz''à cuntinuà , leghjete ste note attentamente:' + cooldown: Dopu à a traslucazione, c'hè una perioda di ricuperazione in quella ùn puderete micca cambià torna di contu + disabled_account: U contu attuale ùn puderà più esse utilizatu dop'à st'azzione. Però, puderete accede à a spurtazione di dati o riattivà u contu. + followers: St'azzione hà da spiazzà tutti l'abbunati di u contu attuale nant'à u novu contu + only_redirect_html: Pudete ancu <a href="%{path}">mette solu una ridirezzione nant'à u vostru prufile</a>. + other_data: L'altri dati ùn saranu micca autumaticamente trasferiti + redirect: U prufile di u vostru contu attuale sarà messu à ghjornu cù una nutificazione di ridirezzione è sarà sclusu di e ricerche moderation: title: Muderazione notification_mailer: @@ -809,17 +934,13 @@ co: remote_interaction: favourite: proceed: Cuntinuà per favurisce - prompt: 'Vulete aghjustà stu statutu à i vostri favuriti:' + prompt: 'Vulete aghjunghje stu statutu à i vostri favuriti:' reblog: proceed: Cuntinuà per sparte prompt: 'Vulete sparte stu statutu:' reply: proceed: Cuntinuà per risponde prompt: 'Vulete risponde à stu statutu:' - remote_unfollow: - error: Errore - title: Titulu - unfollowed: Disabbunatu scheduled_statuses: over_daily_limit: Avete trapassatu a limita di %{limit} statuti planificati per stu ghjornu over_total_limit: Avete trapassatu a limita di %{limit} statuti planificati @@ -868,6 +989,7 @@ co: settings: account: Contu account_settings: Parametri di u contu + aliases: Pseudonimi di contu appearance: Apparenza authorized_apps: Applicazione auturizate back: Ritornu nant’à Mastodon @@ -908,6 +1030,9 @@ co: private: Ùn pudete micca puntarulà un statutu ch’ùn hè micca pubblicu reblog: Ùn pudete micca puntarulà una spartera poll: + total_people: + one: "%{count} persona" + other: "%{count} persone" total_votes: one: "%{count} votu" other: "%{count} voti" @@ -926,6 +1051,8 @@ co: pinned: Statutu puntarulatu reblogged: hà spartutu sensitive_content: Cuntenutu sensibile + tags: + does_not_match_previous_name: ùn currisponde micca à l'anzianu nome terms: body_html: | <h2>Politique de confidentialité</h2> @@ -1031,7 +1158,7 @@ co: recovery_codes: Codici di ricuperazione recovery_codes_regenerated: Codici di ricuperazione ricreati recovery_instructions_html: Pudete fà usu di i codici quì sottu per sempre avè accessu à u vostru contu s’ellu hè statu persu u vostru telefuninu. <strong>Guardateli in una piazza sicura</strong>. Per esempiu, stampati è cunservati cù altri ducumenti impurtanti. - setup: Installà + setup: Attivà wrong_code: U codice ùn hè micca currettu! Site sicuru che l’ora di u telefuninu è di u servore sò esatte? user_mailer: backup_ready: @@ -1043,7 +1170,9 @@ co: disable: Quandu u vostru contu hè ghjacciatu, i vostri dati stannu intatti, mà ùn pudete fà nunda fin'à ch'ellu sia sbluccatu. silence: Quandu u vostru contu hè limitatu, solu quelli chì sò digià abbunati à u vostru contu viderenu i vostri statuti nant'à quessu servore, è puderete esse esclusu·a di parechje liste pubbliche. Però, altri conti puderenu sempre seguitavi. suspend: U vostru contu hè statu suspesu, è tutti i vo statuti è fugliali media caricati sò stati sguassati di manera irreversibile di stu servore, è di i servori induve aviate abbunati. + get_in_touch: Pudete risponde à quest'e-mail per cuntattà a squadra di muderazione di %{instance}. review_server_policies: Leghje e pulitiche di u servore + statuses: 'Più pricisamente, per:' subject: disable: U vostru contu %{acct} hè statu ghjacciatu none: Avertimentu pè %{acct} diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 0735a86981afa95f6d4bdba9ea7a5f1c91ab653a..8a3669a6c1a8b1714dbf557ed3c0ccfb07c569aa 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -2,7 +2,7 @@ cs: about: about_hashtag_html: Tohle jsou veÅ™ejné tooty oznaÄené hashtagem <strong>#%{hashtag}</strong>. Pokud máte úÄet kdekoliv ve fedivesmÃru, můžete s nimi interagovat. - about_mastodon_html: Mastodon je sociálnà sÃÅ¥ založená na otevÅ™ených webových protokolech a svobodném, otevÅ™eném softwaru. Je decentralizovaná jako e-mail. + about_mastodon_html: 'Sociálnà sÃÅ¥ budoucnosti: žádné reklamy, žádné korporátnà sledovánÃ, etický design a decentralizace! S Mastodonem vlastnÃte svoje data!' about_this: O tomto serveru active_count_after: aktivnÃch active_footnote: MÄ›sÃÄnà aktivnà uživatelé (MAU) @@ -17,13 +17,12 @@ cs: contact_unavailable: Neuvedeno discover_users: Objevujte uživatele documentation: Dokumentace - extended_description_html: | - <h3>Dobré mÃsto pro pravidla</h3> - <p>RozÅ¡ÃÅ™ený popis jeÅ¡tÄ› nebyl nastaven.</p> federation_hint_html: S úÄtem na %{instance} můžete sledovat lidi na jakémkoliv serveru Mastodon a jiných službách. - generic_description: "%{domain} je jednÃm ze serverů v sÃti" get_apps: VyzkouÅ¡ejte mobilnà aplikaci hosted_on: Mastodon na adrese %{domain} + instance_actor_flash: | + Tento úÄet je virtuálnà aktér, který pÅ™edstavuje server samotný a ne jedotlivého uživatele. + PoužÃvá se pro úÄely federace a nesmà být blokován, pokud nechcete blokovat celý server. V tomto pÅ™ÃpadÄ› použijte doménovou blokaci. learn_more: Zjistit vÃce privacy_policy: Zásady soukromà see_whats_happening: PodÃvejte se, co se dÄ›je @@ -37,6 +36,14 @@ cs: status_count_before: Kteřà napsali tagline: Sledujte své přátele a objevujte nové terms: PodmÃnky použÃvánà + unavailable_content: Nedostupný obsah + unavailable_content_description: + domain: Server + reason: Důvod + rejecting_media: 'Mediálnà soubory z tohoto serveru nebudou zpracovány a nebudou zobrazeny žádné náhledy. Pro prohlédnutà médià bude tÅ™eba manuálnÄ› pÅ™ejÃt na druhý server:' + silenced: 'PÅ™ÃspÄ›vky z tÄ›chto serverů nebudou zobrazeny ve veÅ™ejných Äasových osách a konverzacÃch a nebudou generována oznámenà o interakcÃch uživatelů z toho serveru, pokud je nesledujete:' + suspended: 'Žádná data z tÄ›chto serverů nebudou zpracována, ukládána ani vyměňována, ÄÃmž bude znemožnÄ›na jakákoliv interakce Äi komunikace s uživateli z tÄ›chto serverů:' + unavailable_content_html: Mastodon vám obvykle dovoluje prohlÞet si obsah a komunikovat s uživateli z jakéhokoliv dalÅ¡Ãho serveru ve fedivesmÃru. Tohle jsou výjimky, které byly zavedeny na tomto konkrétnÃm serveru. user_count_after: few: uživatelé many: uživatelů @@ -46,6 +53,8 @@ cs: what_is_mastodon: Co je Mastodon? accounts: choices_html: 'Volby uživatele %{name}:' + endorsements_hint: Z webového rozhranà můžete podpoÅ™it lidi, které sledujete. Ti se poté zobrazà zde. + featured_tags_hint: Můžete vybrat konkrétnà hashtagy, které se zobrazà zde. follow: Sledovat followers: few: SledujÃcà @@ -59,6 +68,7 @@ cs: media: Média moved_html: "%{name} se pÅ™esunul/a na %{new_profile_link}:" network_hidden: Tato informace nenà k dispozici + never_active: Nikdy nothing_here: Tady nic nenÃ! people_followed_by: Lidé, které sleduje %{name} people_who_follow: Lidé, kteřà sledujà uživatele %{name} @@ -191,6 +201,7 @@ cs: username: Uživatelské jméno warn: Varovat web: Web + whitelisted: Na bÃlé listinÄ› action_logs: actions: assigned_to_self_report: "%{name} pÅ™idÄ›lil/a hlášenà %{target} sobÄ›" @@ -226,19 +237,24 @@ cs: deleted_status: "(smazaný toot)" title: Záznam auditu custom_emojis: + assign_category: PÅ™iÅ™adit kategorii by_domain: Doména copied_msg: MÃstnà kopie emoji byla úspěšnÄ› vytvoÅ™ena copy: KopÃrovat copy_failed_msg: Nebylo možné vytvoÅ™it mÃstnà kopii tohoto emoji + create_new_category: VytvoÅ™it novou kategorii created_msg: Emoji úspěšnÄ› vytvoÅ™eno! delete: Smazat destroyed_msg: Emoji úspěšnÄ› zniÄeno! disable: Zakázat + disabled: Zakázáno disabled_msg: Emoji bylo úspěšnÄ› zakázáno emoji: Emoji enable: Povolit + enabled: Povoleno enabled_msg: Emoji bylo úspěšnÄ› povoleno image_hint: PNG až do 50 KB + list: Uvést listed: Uvedeno new: title: PÅ™idat nové vlastnà emoji @@ -246,11 +262,14 @@ cs: shortcode: Zkratka shortcode_hint: Alespoň 2 znaky, pouze alfanumerické znaky a podtržÃtka title: Vlastnà emoji + uncategorized: NezaÅ™azená + unlist: Neuvést unlisted: Neuvedeno update_failed_msg: Nebylo možné aktualizovat toto emoji updated_msg: Emoji úspěšnÄ› aktualizováno! upload: Nahrát dashboard: + authorized_fetch_mode: ZabezpeÄený režim backlog: opoždÄ›né úlohy config: Konfigurace feature_deletions: Smazánà úÄtů @@ -258,10 +277,13 @@ cs: feature_profile_directory: Adresář profilů feature_registrations: Registrace feature_relay: Federovacà most + feature_spam_check: Antispam feature_timeline_preview: Náhled Äasové osy features: Vlastnosti hidden_service: Federace se skrytými službami open_reports: otevÅ™ená hlášenà + pending_tags: hashtagů Äeká na posouzenà + pending_users: uživatelů recent_users: Nedávnà uživatelé search: Fulltextové vyhledávánà single_user_mode: Režim jednoho uživatele @@ -273,11 +295,18 @@ cs: week_interactions: interakcà tento týden week_users_active: aktivnÃch tento týden week_users_new: uživatelů tento týden + whitelist_mode: Režim bÃlé listiny + domain_allows: + add_new: PÅ™idat doménu na bÃlou listinu + created_msg: Doména byla úspěšnÄ› pÅ™idána na bÃlou listinu + destroyed_msg: Doména byla odstranÄ›na z bÃlé listiny + undo: Odstranit z bÃlé listiny domain_blocks: add_new: PÅ™idat novou blokaci domény created_msg: Blokace domény se právÄ› vyÅ™izuje destroyed_msg: Blokace domény byla zruÅ¡ena domain: Doména + edit: Upravit doménovou blokaci existing_domain_block_html: Pro úÄet %{name} jste již nastavil/a pÅ™ÃsnÄ›jšà omezenÃ, musÃte jej nejdÅ™Ãve <a href="%{unblock_url}">odblokovat</a>. new: create: VytvoÅ™it blokaci @@ -288,6 +317,10 @@ cs: silence: UtiÅ¡it suspend: Pozastavit title: Nová doménová blokace + private_comment: Soukromý komentář + private_comment_hint: Komentář o tomto omezenà domény pro vnitÅ™nà použità moderátory. + public_comment: VeÅ™ejný komentář + public_comment_hint: Komentář o tomto omezenà domény pro obecnou veÅ™ejnost, pokud je povoleno zobrazovánà seznamu omezenà domén. reject_media: OdmÃtat mediálnà soubory reject_media_hint: Odstranà lokálnÄ› uložené mediálnà soubory a odmÃtne jejich stahovánà v budoucnosti. Nepodstatné pro pozastavenà reject_reports: OdmÃtat nahlášenà @@ -309,6 +342,7 @@ cs: title: ZruÅ¡it blokaci domény %{domain} undo: Odvolat undo: Odvolat blokaci domény + view: Zobrazit doménovou blokaci email_domain_blocks: add_new: PÅ™idat nový created_msg: E-mailová doména úspěšnÄ› pÅ™idána na Äernou listinu @@ -334,6 +368,8 @@ cs: all: VÅ¡echny limited: Omezené title: Moderovánà + private_comment: Soukromý komentář + public_comment: VeÅ™ejný komentář title: Federace total_blocked_by_us: Blokované námi total_followed_by_them: Sledované jimi @@ -363,6 +399,7 @@ cs: pending: ÄŒekám na souhlas mostu save_and_enable: Uložit a povolit setup: Nastavit pÅ™ipojenà k mostu + signatures_not_enabled: Mosty nebudou fungovat správnÄ›, dokud je povolen zabezpeÄený režim nebo režim bÃlé listiny status: Stav title: Mosty report_notes: @@ -411,11 +448,21 @@ cs: custom_css: desc_html: PozmÄ›nit vzhled pomocà šablony CSS naÄtené na každé stránce title: Vlastnà CSS + default_noindex: + desc_html: Ovlivňuje vÅ¡echny uživatele, kteřà toto nastavenà sami nezmÄ›nili + title: Odhlásit uživatele z indexovánà vyhledávaÄemi ve výchozÃm stavu + domain_blocks: + all: VÅ¡em + disabled: Nikomu + title: Zobrazit doménové blokace + users: PÅ™ihlášeným mÃstnÃm uživatelům + domain_blocks_rationale: + title: Zobrazit odůvodnÄ›nà hero: desc_html: Zobrazuje se na hlavnà stránce. DoporuÄuje se rozliÅ¡enà alespoň 600x100 px. Pokud toto nenà nastaveno, bude zobrazena miniatura serveru title: Hlavnà obrázek mascot: - desc_html: Zobrazuje se na hlavnà stránce. DoporuÄuje se rozliÅ¡enà alespoň 293x205 px. Pokud toto nenà nastaveno, bude zobrazen výchozà maskot + desc_html: Zobrazuje se na nÄ›kolika stránkách. DoporuÄuje se rozliÅ¡enà alespoň 293x205 px. Pokud toto nenà nastaveno, bude zobrazen výchozà maskot title: Obrázek maskota peers_api_enabled: desc_html: Domény, na které tento server narazil ve fedivesmÃru @@ -443,8 +490,8 @@ cs: open: Kdokoliv se může registrovat title: Režim registracà show_known_fediverse_at_about_page: - desc_html: Je-li tohle zapnuto, zobrazà se v náhledu tooty z celého známého fedivesmÃru. Jinak budou zobrazeny pouze mÃstnà tooty. - title: Zobrazit na náhledu Äasové osy celý známý fedivesmÃr + desc_html: Je-li tohle vypnuto, bude veÅ™ejná Äasová osa, na kterou odkazuje hlavnà stránka serveru, omezena pouze na mÃstnà obsah + title: Zobrazit na nepÅ™ihlášené stránce Äasové osy federovaný obsah show_staff_badge: desc_html: Zobrazit na stránce uživatele odznak Älena personálu title: Zobrazit odznak personálu @@ -455,25 +502,35 @@ cs: desc_html: Dobré mÃsto pro vaÅ¡e pravidla, pokyny a jiné vÄ›ci, které váš server odliÅ¡ujà od ostatnÃch. Lze použÃt HTML znaÄky title: Vlastnà rozÅ¡ÃÅ™ené informace site_short_description: - desc_html: Zobrazen v postrannÃm panelu a meta znaÄkách. PopiÅ¡te, co je Mastodon a dÃky Äemu je tento server zvláštnà v jediném odstavci. + desc_html: Zobrazen v postrannÃm panelu a meta znaÄkách. PopiÅ¡te, co je Mastodon a dÃky Äemu je tento server zvláštnÃ, v jediném odstavci. title: Krátký popis serveru site_terms: desc_html: Můžete si napsat vlastnà zásady soukromÃ, podmÃnky použÃvánà Äi jiné legality. Můžete použÃt HTML znaÄky title: Vlastnà podmÃnky použÃvánà site_title: Název serveru + spam_check_enabled: + desc_html: Mastodon může automaticky nahlaÅ¡ovat úÄty, které opakovanÄ› odesÃlajà nevyžádané zprávy. Mohou se najÃt Å¡patné shody. + title: Antispamová automatizace thumbnail: desc_html: PoužÃváno pro náhledy pÅ™es OpenGraph a API. DoporuÄuje se rozliÅ¡enà 1200x630px title: Miniatura serveru timeline_preview: - desc_html: Zobrazit na hlavnà stranÄ› veÅ™ejnou Äasovou osu - title: Náhled Äasové osy + desc_html: Zobrazit na hlavnà stranÄ› odkaz na veÅ™ejnou Äasovou osu a povolit pÅ™Ãstup na veÅ™ejnou Äasovou osu pomocà API bez autentizace + title: Povolit neautentizovaný pÅ™Ãstup k Äasové ose title: Nastavenà stránky + trendable_by_default: + desc_html: Ovlivňuje hashtagy, které nebyly dÅ™Ãve zakázány + title: Dovolit hashtagům zobrazit se na trendech bez pÅ™edchozÃho posouzenà + trends: + desc_html: VeÅ™ejnÄ› zobrazit pÅ™edtÃm schválené hashtagy, které jsou aktuálnÄ› populárnà + title: Populárnà hashtagy statuses: back_to_account: ZpÄ›t na stránku úÄtu batch: delete: Smazat nsfw_off: OznaÄit, že nenà citlivý nsfw_on: OznaÄit jako citlivý + deleted: Smazáno failed_to_execute: NepodaÅ™ilo se vykonat media: title: Média @@ -481,21 +538,24 @@ cs: no_status_selected: Nebyly zmÄ›nÄ›ny žádné tooty, neboÅ¥ žádné nebyly vybrány title: Tooty úÄtu with_media: S médii - subscriptions: - callback_url: ZpáteÄnà URL - confirmed: Potvrzeno - expires_in: Vypršà v - last_delivery: Poslednà doruÄenà - title: WebSub - topic: Téma tags: - accounts: ÚÄty - hidden: Skryté - hide: Skrýt z adresáře + accounts_today: JedineÄná použità dnes + accounts_week: JedineÄná použità tento týden + breakdown: PÅ™ehled dneÅ¡nÃho použÃvánà podle zdroje + context: Kontext + directory: V adresáři + in_directory: "%{count} v adresáři" + last_active: Naposledy aktivnà + most_popular: NejpopulárnÄ›jšà + most_recent: NejnovÄ›jšà name: Hashtag + review: Stav schválenà + reviewed: Schválen title: Hashtagy - unhide: Zobrazit v adresáři - visible: Viditelné + trending_right_now: Aktuálnà trendy + unique_uses_today: "%{count} dnes pÃÅ¡e" + unreviewed: Neposouzeno + updated_msg: Nastavenà hashtagu bylo úspěšnÄ› aktualizováno title: Administrace warning_presets: add_new: PÅ™idat nové @@ -511,11 +571,21 @@ cs: body: "%{reporter} nahlásil/a uživatele %{target}" body_remote: NÄ›kdo z %{domain} nahlásil uživatele %{target} subject: Nové nahlášenà pro %{instance} (#%{id}) + new_trending_tag: + body: 'Hashtag #%{name} je dnes populárnÃ, nebyl vÅ¡ak dÅ™Ãve schválen. Nebude zobrazen veÅ™ejnÄ›, pokud to nedovolÃte. Můžete také pouze uložit formulář tak, jak je, a nikdy o nÄ›m opÄ›t neslyÅ¡et.' + subject: Nový hashtag ke schválenà na %{instance} (#%{name}) + aliases: + add_new: VytvoÅ™it alias + created_msg: Nový alias byl úspěšnÄ› vytvoÅ™en. Nynà můžete zahájit pÅ™esun ze starého úÄtu. + deleted_msg: Alias byl úspěšnÄ› odstranÄ›n. PÅ™esun z tamtoho úÄtu na tento již nebude možný. + hint_html: Chcete-li se pÅ™esunout z jiného úÄtu na tento, můžete si zde vytvoÅ™it alias, který je vyžadován pÅ™edtÃm, než můžete pokraÄovat pÅ™esunem sledujÃcÃch ze starého úÄtu na tento. Tato akce sama o sobÄ› je <strong>neÅ¡kodná a vratná</strong>. <strong>PÅ™esun úÄtu se zahajuje ze starého úÄtu</strong>. + remove: Odpojit alias appearance: advanced_web_interface: PokroÄilé webové rozhranà advanced_web_interface_hint: 'Chcete-li využÃt celé Å¡ÃÅ™ky vašà obrazovky, dovolà vám pokroÄilé webové rozhranà nastavit si mnoho různých sloupců, takže můžete vidÄ›t ve stejnou chvÃli tolik informacÃ, kolik chcete: domovskou Äasovou osu, oznámenÃ, federovanou Äasovou osu a libovolný poÄet seznamů a hashtagů.' animations_and_accessibility: Animace a pÅ™Ãstupnost confirmation_dialogs: Potvrzovacà dialogy + discovery: Objevovánà sensitive_content: Citlivý obsah application_mailer: notification_preferences: ZmÄ›nit volby e-mailu @@ -536,9 +606,13 @@ cs: apply_for_account: Vyžádat si pozvánku change_password: Heslo checkbox_agreement_html: SouhlasÃm s <a href="%{rules_path}" target="_blank">pravidly serveru</a> a <a href="%{terms_path}" target="_blank">podmÃnkami použÃvánÃ</a> - confirm_email: Potvrdit e-mail + checkbox_agreement_without_rules_html: SouhlasÃm s <a href="%{terms_path}" target="_blank">podmÃnkami použÃvánÃ</a> delete_account: Odstranit úÄet delete_account_html: Chcete-li odstranit svůj úÄet, <a href="%{path}">pokraÄujte zde</a>. Budete požádán/a o potvrzenÃ. + description: + prefix_invited_by_user: "@%{name} vás zve, abyste se pÅ™idal/a na tento server Mastodon!" + prefix_sign_up: Registrujte se na Mastodonu již dnes! + suffix: S úÄtem budete moci sledovat lidi, psát pÅ™ÃspÄ›vky a vyměňovat si zprávy s uživateli z kteréhokoliv serveru Mastodon a dalÅ¡Ãch služeb! didnt_get_confirmation: Neobdržel/a jste pokyny pro potvrzenÃ? forgot_password: ZapomnÄ›l/a jste heslo? invalid_reset_password_token: Token pro obnovenà hesla je buÄ neplatný, nebo vyprÅ¡el. ProsÃm vyžádejte si nový. @@ -556,6 +630,16 @@ cs: reset_password: Obnovit heslo security: ZabezpeÄenà set_new_password: Nastavit nové heslo + setup: + email_below_hint_html: Pokud je nÞe uvedená e-mailová adresa nesprávná, můžete ji zmÄ›nit zde a obdržet nový potvrzovacà e-mail. + email_settings_hint_html: Potvrzovacà e-mail byl odeslán na %{email}. Pokud je tato adresa nesprávná, můžete ji zmÄ›nit v nastavenà úÄtu. + title: Nastavenà + status: + account_status: Stav úÄtu + confirming: ÄŒekám na dokonÄenà potvrzenà e-mailu. + functional: Váš úÄet je zcela funkÄnÃ. + pending: Váš požadavek Äeká na schválenà naÅ¡Ãm personálem. To může nÄ›jakou dobu trvat. Pokud bude váš požadavek schválen, obdržÃte e-mail. + redirecting_to: Váš úÄet je neaktivnÃ, protože právÄ› pÅ™esmÄ›rovává na úÄet %{acct}. trouble_logging_in: Problémy s pÅ™ihlaÅ¡ovánÃm? authorize_follow: already_following: Tento úÄet již sledujete @@ -568,6 +652,11 @@ cs: return: Zobrazit profil uživatele web: PÅ™ejÃt na web title: Sledovat uživatele %{acct} + challenge: + confirm: PokraÄovat + hint_html: "<strong>Tip:</strong> Po dobu hodiny vás nebudeme znovu žádat o heslo." + invalid_password: Neplatné heslo + prompt: PokraÄujte potvrzenÃm hesla datetime: distance_in_words: about_x_hours: "%{count} hod" @@ -583,28 +672,33 @@ cs: x_months: "%{count} mesÃců" x_seconds: "%{count} s" deletes: - bad_password_msg: Dobrý pokus, hackeÅ™i! Nesprávné heslo + challenge_not_passed: Informace, které jste zadal/a, nejsou správné confirm_password: Zadejte svoje souÄasné heslo pro ověřenà vašà identity - description_html: TÃmto <strong>trvale a nenávratnÄ›</strong> odstranÃte obsah z vaÅ¡eho úÄtu a deaktivujete ho. VaÅ¡e uživatelské jméno zůstane rezervované pro zabránÄ›nà budoucÃm napodobovánÃm. + confirm_username: ZadánÃm svého uživatelského jména potvrdÃte proces proceed: Odstranit úÄet success_msg: Váš úÄet byl úspěšnÄ› odstranÄ›n - warning_html: Pouze vymazánà obsahu z tohoto konkrétnÃho serveru je zaruÄeno. Obsah, který byl Å¡iroce sdÃlen, po sobÄ› pravdÄ›podobnÄ› zanechá stopy. U offline serverů a serverů, které vaÅ¡e aktualizace již neodebÃrajÃ, nebudou databáze aktualizovány. - warning_title: Dostupnost rozÅ¡ÃÅ™eného obsahu + warning: + before: 'PÅ™ed pokraÄovánÃm si prosÃm peÄlivÄ› pÅ™eÄtÄ›te tyto poznámky:' + caches: Obsah, který byl uložen do cache jiných serverů, nemusà být smazán + data_removal: VaÅ¡e pÅ™ÃspÄ›vky a dalšà data budou trvale smazána + email_change_html: Můžete si <a href="%{path}">zmÄ›nit svou e-mailovou adresu</a> bez smazánà úÄtu + email_contact_html: Pokud stále nepÅ™ijde, můžete požádat o pomoc zaslánÃm e-mailu na <a href="mailto:%{email}">%{email}</a> + email_reconfirmation_html: Pokud neobdržÃte potvrzovacà e-mail, můžete si ho <a href="%{path}">vyžádat znovu</a> + irreversible: Nebudete moci obnovit nebo znovu aktivovat váš úÄet + more_details_html: VÃce detailů najdete v <a href="%{terms_path}">zásadách soukromÃ</a>. + username_available: VaÅ¡e uživatelské jméno bude opÄ›t dostupné + username_unavailable: VaÅ¡e uživatelské jméno zůstane nedostupným directories: directory: Adresář profilů - enabled: AktuálnÄ› jste v adresáři uveden/a. - enabled_but_waiting: PÅ™ihlásil/a jste se k uvedenà v adresáři, ale jeÅ¡tÄ› nemáte minimálnà poÄet sledujÃcÃch (%{min_followers}) pro uvedenÃ. explanation: Objevujte uživatele podle jejich zájmů explore_mastodon: Prozkoumejte %{title} - how_to_enable: AktuálnÄ› nejste pÅ™ihlášen/a do adresáře. PÅ™ihlásit se můžete nÞe. Použijte ve svém popisu profilu hashtagy, abyste mohl/a být uveden/a pod konkrétnÃmi hashtagy! - people: - few: "%{count} lidé" - many: "%{count} lidÃ" - one: "%{count} ÄlovÄ›k" - other: "%{count} lidÃ" + domain_validator: + invalid_domain: nenà platné doménové jméno errors: + '400': Požadavek, který jste odeslal/a, byl neplatný nebo poÅ¡kozený. '403': Nemáte povolenà zobrazit tuto stránku. '404': Stránka, kterou hledáte, tu nenÃ. + '406': Tato stránka nenà dostupná v požadovaném formátu. '410': Stránka, kterou hledáte, tu již neexistuje. '422': content: BezpeÄnostnà ověřenà selhalo. Neblokujete cookies? @@ -613,6 +707,7 @@ cs: '500': content: Omlouváme se, ale nÄ›co se u nás pokazilo. title: Tato stránka nenà správná + '503': Stránku nelze naÄÃst kvůli doÄasnému selhánà serveru. noscript_html: Pro použità webové aplikace Mastodon prosÃm povolte JavaScript. Nebo zkuste jednu z <a href="%{apps_path}">nativnÃch aplikacÃ</a> pro Mastodon pro vaÅ¡i platformu. existing_username_validator: not_found: nelze najÃt mÃstnÃho uživatele s tÃmto uživatelským jménem @@ -636,6 +731,7 @@ cs: add_new: PÅ™idat nový errors: limit: Již jste zvýraznil/a maximálnà poÄet hashtagů + hint_html: "<strong>Co jsou zvýraznÄ›né hashtagy?</strong> Zobrazujà se prominentnÄ› na vaÅ¡em veÅ™ejném profilu a dovolujà lidem prohlÞet si vaÅ¡e veÅ™ejné pÅ™ÃspÄ›vky konkrétnÄ› pod tÄ›mi hashtagy. Je to skvÄ›lý nástroj pro sledovánà kreativnÃch dÄ›l nebo dlouhodobých projektů." filters: contexts: home: Domovská Äasová osa @@ -656,10 +752,12 @@ cs: developers: Vývojáři more: VÃce… resources: Zdroje + trending_now: Aktuálnà trendy generic: all: VÅ¡echny changes_saved_msg: ZmÄ›ny byly úspěšnÄ› uloženy! copy: KopÃrovat + no_batch_actions_available: Pro tuto stránku nejsou dostupny žádné souhrnné akce order_by: SeÅ™adit od save_changes: Uložit zmÄ›ny validation_errors: @@ -713,7 +811,7 @@ cs: '604800': 1 týden '86400': 1 den expires_in_prompt: Nikdy - generate: Vygenerovat + generate: Vygenerovat pozvánku invited_by: 'Byl/a jste pozván/a uživatelem:' max_uses: few: "%{count} použitÃ" @@ -734,10 +832,35 @@ cs: images_and_video: K tootu, který již obsahuje obrázky, nelze pÅ™ipojit video too_many: Nelze pÅ™ipojit vÃce než 4 soubory migrations: - acct: pÅ™ezdÃvka@doména nového úÄtu - currently_redirecting: 'Váš profil má nastaveno pÅ™esmÄ›rovánà na:' - proceed: Uložit - updated_msg: VaÅ¡e nastavenà pÅ™esunutà úÄtu bylo úspěšnÄ› aktualizováno! + acct: PÅ™esunuto na + cancel: ZruÅ¡it pÅ™esmÄ›rovánà + cancel_explanation: ZruÅ¡enÃm pÅ™esmÄ›rovánà znovu aktivujete svůj aktuálnà úÄet, ale nevrátà se vám sledujÃcÃ, kteřà byli pÅ™esmÄ›rováni na druhý úÄet. + cancelled_msg: PÅ™esmÄ›rovánà bylo úspěšnÄ› zruÅ¡eno. + errors: + already_moved: je stejný úÄet, na který jste se již pÅ™esunul/a + missing_also_known_as: neodkazuje na tento úÄet + move_to_self: nemůže být aktuálnà úÄet + not_found: nemohl být nalezen + on_cooldown: ProbÃhá obdobà odpoÄinku + followers_count: SledujÃcà v dobÄ› pÅ™esunu + incoming_migrations: PÅ™esun z jiného úÄtu + incoming_migrations_html: Chcete-li se pÅ™esunout z jiného úÄtu na tento, potÅ™ebujete si nejprve <a href="%{path}">vytvoÅ™it alias úÄtu</a>. + moved_msg: Váš úÄet nynà pÅ™esmÄ›rovává na úÄet %{acct} a vaÅ¡i sledujÃcà se na nÄ›j pÅ™esouvajÃ. + not_redirecting: Váš úÄet aktuálnÄ› nepÅ™esmÄ›rovává na žádný jiný úÄet. + on_cooldown: Nedávno jste pÅ™esunul/a svůj úÄet. Tato funkce bude opÄ›t dostupná za %{count} dnÃ. + past_migrations: PÅ™edchozà pÅ™esuny + proceed_with_move: PÅ™esunout sledujÃcà + redirecting_to: Váš úÄet pÅ™esmÄ›rovává na úÄet %{acct}. + set_redirect: Nastavit pÅ™esmÄ›rovánà + warning: + backreference_required: Nový úÄet musà být nejprve nastaven, aby odkazoval zpátky na tento + before: 'PÅ™ed pokraÄovánÃm si prosÃm peÄlivÄ› pÅ™eÄtÄ›te tyto poznámky:' + cooldown: Po pÅ™esunu nastane obdobà odpoÄinku, kdy se nebudete moci opÄ›t pÅ™esunout + disabled_account: Váš aktuálnà úÄet nebude poté zcela použitelný. Budete vÅ¡ak mÃt pÅ™Ãstup k datovým exportům a budete ho moci znovu aktivovat. + followers: Touto akcà pÅ™esunete vÅ¡echny sledujÃcà z aktuálnÃho úÄtu na nový úÄet + only_redirect_html: AlternativnÄ› můžete <a href="%{path}">nastavit pouze pÅ™esmÄ›rovánà na váš profil</a>. + other_data: Žádná dalšà data nebudou pÅ™esunuta automaticky + redirect: Profil vaÅ¡eho aktuálnÃho úÄtu bude aktualizován s oznámenÃm o pÅ™esmÄ›rovánà a bude vylouÄen z hledánà moderation: title: Moderovánà notification_mailer: @@ -838,10 +961,6 @@ cs: reply: proceed: PokraÄovat k odpovÄ›zenà prompt: 'Chcete odpovÄ›dÄ›t na tento toot:' - remote_unfollow: - error: Chyba - title: Nadpis - unfollowed: Už nesledujete scheduled_statuses: over_daily_limit: PÅ™ekroÄil/a jste limit %{limit} plánovaných tootů pro tento den over_total_limit: PÅ™ekroÄil/a jste limit %{limit} plánovaných tootů @@ -890,6 +1009,7 @@ cs: settings: account: ÚÄet account_settings: Nastavenà úÄtu + aliases: Aliasy úÄtů appearance: Vzhled authorized_apps: Autorizované aplikace back: ZpÄ›t na Mastodon @@ -901,12 +1021,14 @@ cs: identity_proofs: Důkazy identity import: Import import_and_export: Import a export - migrate: PÅ™esunutà úÄtu + migrate: PÅ™esun úÄtu notifications: Oznámenà preferences: PÅ™edvolby profile: Profil relationships: Sledovanà a sledujÃcà two_factor_authentication: Dvoufázové ověřovánà + spam_check: + spam_detected: Tohle je automatizované nahlášenÃ. Byl detekován spam. statuses: attached: description: 'PÅ™iloženo: %{attached}' @@ -936,6 +1058,11 @@ cs: private: Nelze pÅ™ipnout neveÅ™ejné tooty reblog: Nelze pÅ™ipnout boost poll: + total_people: + few: "%{count} lidé" + many: "%{count} lidÃ" + one: "%{count} ÄlovÄ›k" + other: "%{count} lidÃ" total_votes: few: "%{count} hlasy" many: "%{count} hlasů" @@ -956,6 +1083,8 @@ cs: pinned: PÅ™ipnutý toot reblogged: boostnul/a sensitive_content: Citlivý obsah + tags: + does_not_match_previous_name: se neshoduje s pÅ™edchozÃm názvem terms: body_html: | <h2>Zásady soukromÃ</h2> @@ -1073,7 +1202,9 @@ cs: disable: ZatÃmco je váš úÄet zmražen, zůstávajà data vaÅ¡eho úÄtu nedotÄená, ale nemůžete vykonávat žádné akce, dokud nebude odemÄen. silence: ZatÃmco je váš úÄet omezen, mohou vaÅ¡e tooty na tomto serveru vidÄ›t pouze lidé, kteřà váš již sledujÃ, a můžete být vylouÄen/a z různých veÅ™ejných výpisů. Ostatnà vás vÅ¡ak pořád mohou manuálnÄ› sledovat. suspend: Váš úÄet byl pozastaven a vÅ¡echny vaÅ¡e tooty a vaÅ¡e nahrané mediálnà soubory byly nenávratnÄ› odstranÄ›ny z tohoto serveru a serverů, na kterých jste mÄ›l/a sledujÃcÃ. + get_in_touch: Můžete odpovÄ›dÄ›t na tento e-mail a spojit se s personálem serveru %{instance}. review_server_policies: Posoudit politiku serveru + statuses: 'KonkrétnÄ› kvůli:' subject: disable: Váš úÄet %{acct} byl zmražen none: Varovánà pro uživatele %{acct} diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 080e89214bd9ffd44c8c87e0967e324724cf4ac9..202e5de6ca63bb51f30d914b0b5412e3a3aef1d8 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -17,13 +17,12 @@ cy: contact_unavailable: Ddim yn berthnasol discover_users: Darganfod defnyddwyr documentation: Dogfennaeth - extended_description_html: | - <h3>Lle da ar gyfer rheolau</h3> - <p>Nid yw'r disgrifiad estynedig wedi ei osod eto.</p> federation_hint_html: Gyda cyfrif ar %{instance}, gallwch dilyn pobl ar unrhyw gweinydd Mastodon, a thu hwnt. - generic_description: Mae %{domain} yn un gweinydd yn y rhwydwaith get_apps: Rhowch gynnig ar ap dyfeis symudol hosted_on: Mastodon wedi ei weinyddu ar %{domain} + instance_actor_flash: | + Mae'r cyfrif hwn yn actor rhithwir a ddefnyddir i gynrychioli'r gweinydd ei hun ac nid unrhyw ddefnyddiwr unigol. + Fe'i defnyddir at ddibenion ffederasiwn ac ni ddylid ei rwystro oni bai eich bod am rwystro'r achos cyfan, ac os felly dylech ddefnyddio bloc parth. learn_more: Dysu mwy privacy_policy: Polisi preifatrwydd see_whats_happening: Gweld beth sy'n digwydd @@ -39,6 +38,13 @@ cy: status_count_before: Ysgriffennwyd gan tagline: Dilyn ffrindiau a darganfod rhai newydd terms: Telerau gwasanaeth + unavailable_content: Cynnwys nad yw ar gael + unavailable_content_description: + reason: 'Rheswm:' + rejecting_media: Ni fydd ffeiliau cyfryngau o'r gweinydd hwn yn cael eu prosesu ac ni fydd unrhyw fawd yn cael eu harddangos, sy'n gofyn am glicio â llaw i'r gweinydd arall. + silenced: Ni fydd swyddi o'r gweinydd hwn yn ymddangos yn unman heblaw eich porthiant cartref os dilynwch yr awdur. + suspended: Ni fyddwch yn gallu dilyn unrhyw un o'r gweinydd hwn, ac ni fydd unrhyw ddata ohono'n cael ei brosesu na'i storio, ac ni chyfnewidir unrhyw ddata. + unavailable_content_html: Yn gyffredinol, mae Mastodon yn caniatáu ichi weld cynnwys gan unrhyw weinyddwr arall yn y ffederasiwn a rhyngweithio â hi. Dyma'r eithriadau a wnaed ar y gweinydd penodol hwn. user_count_after: few: defnyddwyr many: defnyddwyr @@ -50,6 +56,8 @@ cy: what_is_mastodon: Beth yw Mastodon? accounts: choices_html: 'Dewisiadau %{name}:' + endorsements_hint: Gallwch gymeradwyo pobl rydych chi'n eu dilyn o'r rhyngwyneb gwe, a byddan nhw'n ymddangos yma. + featured_tags_hint: Gallwch ychwanegu hashnodau penodol a fydd yn cael eu harddangos yma. follow: Dilynwch followers: few: Dilynwyr @@ -65,6 +73,7 @@ cy: media: Cyfryngau moved_html: 'Mae %{name} wedi symud i %{new_profile_link}:' network_hidden: Nid yw'r wybodaeth hyn ar gael + never_active: Peidiwch byth nothing_here: Does dim byd yma! people_followed_by: Pobl y mae %{name} yn ei ddilyn people_who_follow: Pobl sy'n dilyn %{name} @@ -199,6 +208,7 @@ cy: username: Enw defnyddiwr warn: Rhybuddio web: Gwe + whitelisted: Rhestredig wen action_logs: actions: assigned_to_self_report: Aseiniodd %{name} adroddiad %{target} i'w hunan @@ -234,10 +244,12 @@ cy: deleted_status: "(statws wedi ei ddileu)" title: Log archwilio custom_emojis: + assign_category: Neilltuo categori by_domain: Parth copied_msg: Llwyddwyd i greu copi lleol o'r emoji copy: Copïo copy_failed_msg: Methwyd i greu copi lleol o'r emoji hwnnw + create_new_category: Create new category created_msg: Llwyddwyd i greu emoji! delete: Dileu destroyed_msg: Llwyddwyd i ddinistrio emojo! @@ -254,11 +266,13 @@ cy: shortcode: Byrgod shortcode_hint: O leiaf 2 nodyn, dim ond nodau alffaniwmerig a tanlinellau title: Emoji unigryw + uncategorized: Heb gategori unlisted: Heb eu rhestru update_failed_msg: Methwyd a diweddaru'r emoji hwnnw updated_msg: Llwyddwyd i ddiweddaru'r emoji! upload: Uwchlwytho dashboard: + authorized_fetch_mode: Modd nôl awdurdodedig backlog: tasgau heb eu cwblhau config: Cyfluniad feature_deletions: Dileadau cyfrif @@ -266,10 +280,13 @@ cy: feature_profile_directory: Cyfeiriadur proffil feature_registrations: Cofrestriadau feature_relay: Relái ffederasiwn + feature_spam_check: Gwrth-sbam feature_timeline_preview: Rhagolwg o'r ffrwd features: Nodweddion hidden_service: Ffederasiwn a gwasanaethau cudd open_reports: adroddiadau agored + pending_tags: hashnodau yn aros am adolygiad + pending_users: defnyddwyr yn aros am adolygiad recent_users: Defnyddwyr diweddar search: Chwilio testun llawn single_user_mode: Modd un defnyddiwr @@ -281,11 +298,18 @@ cy: week_interactions: ymadweithiau yr wythnos hon week_users_active: gweithredol yr wythnos hon week_users_new: defnyddwyr yr wythnos hon + whitelist_mode: Modd rhestr wen + domain_allows: + add_new: Rhestrwch parth + created_msg: Rhestrwyd wen parth yn llwyddiannus + destroyed_msg: Mae parth wedi'i dynnu o'r rhestr wen + undo: Tynnwch o'r rhestr wen domain_blocks: add_new: Ychwanegu bloc parth newydd created_msg: Mae'r bloc parth nawr yn cael ei brosesu destroyed_msg: Mae'r bloc parth wedi ei ddadwneud domain: Parth + edit: Golygu bloc parth existing_domain_block_html: Rydych yn barod wedi gosod cyfyngau fwy llym ar %{name}, mae rhaid i chi ei <a href="%{unblock_url}">ddadblocio</a> yn gyntaf. new: create: Creu bloc @@ -296,6 +320,8 @@ cy: silence: Tawelwch suspend: Atal title: Blocio parth newydd + private_comment: Sylw preifat + public_comment: Sylw cyhoeddus reject_media: Gwrthod dogfennau cyfryngau reject_media_hint: Dileu dogfennau cyfryngau wedi eu cadw yn lleol ac yn gwrthod i lawrlwytho unrhyw rai yn y dyfodol. Amherthnasol i ataliadau reject_reports: Gwrthod adroddiadau @@ -319,6 +345,7 @@ cy: title: Dadwneud blocio parth ar gyfer %{domain} undo: Dadwneud undo: Dadwneud bloc parth + view: Gweld bloc parth email_domain_blocks: add_new: Ychwanegu created_msg: Llwyddwyd i ychwanegu parth e-bost i'r gosbrestr @@ -346,6 +373,8 @@ cy: all: Pob limited: Gyfyngedig title: Goruwchwyliad + private_comment: Sylw preifat + public_comment: Sylw cyhoeddus title: Ffederasiwn total_blocked_by_us: Wedi'i bloc gan ni total_followed_by_them: Yn dilyn ganynt @@ -423,6 +452,8 @@ cy: custom_css: desc_html: Addasu gwedd gyda CSS wedi lwytho ar bob tudalen title: CSS wedi'i addasu + domain_blocks: + all: I bawb hero: desc_html: Yn cael ei arddangos ar y dudadlen flaen. Awgrymir 600x100px oleia. Pan nad yw wedi ei osod, mae'n ymddangos fel mân-lun yr achos title: Delwedd arwr @@ -486,6 +517,7 @@ cy: delete: Dileu nsfw_off: Marcio fel nad yw'n sensitif nsfw_on: Marcio'n sensitif + deleted: Dilëwyd failed_to_execute: Methwyd a gweithredu media: title: Cyfryngau @@ -493,21 +525,14 @@ cy: no_status_selected: Ni newidwyd dim statws achos ni ddewiswyd dim un title: Statysau cyfrif with_media: A chyfryngau - subscriptions: - callback_url: URL galw-nôl - confirmed: Wedi'i gadarnhau - expires_in: Dod i ben ymhen - last_delivery: Danfoniad diwethaf - title: WebSub - topic: Pwnc tags: - accounts: Cyfrifon - hidden: Cudd - hide: Cuddio o gyfeiriadur + context: Cyd-destun + last_active: Yn weithredol ddiwethaf name: Hashnod + reviewed: Wedi'i adolygu title: Hashnodau - unhide: Dangoswch yn y cyfeiriadur - visible: Gweladwy + trending_right_now: Yn tueddu nawr + unreviewed: Heb ei adolygu title: Gweinyddiaeth warning_presets: add_new: Ychwanegu newydd @@ -548,7 +573,6 @@ cy: apply_for_account: Gofyn am wahoddiad change_password: Cyfrinair checkbox_agreement_html: Rydw i'n cytuno i'r <a href="%{rules_path}" target="_blank">rheolau'r gweinydd</a> a'r <a href="%{terms_path}" target="_blank">telerau gwasanaeth</a> - confirm_email: Cadarnhau e-bost delete_account: Dileu cyfrif delete_account_html: Os hoffech chi ddileu eich cyfrif, mae modd <a href="%{path}">parhau yma</a>. Bydd gofyn i chi gadarnhau. didnt_get_confirmation: Heb dderbyn cyfarwyddiadau cadarnhau? @@ -568,6 +592,14 @@ cy: reset_password: Ailosod cyfrinair security: Diogelwch set_new_password: Gosod cyfrinair newydd + setup: + title: Gosodiad + status: + account_status: Statws cyfrif + confirming: Aros i gadarnhad e-bost gael ei gwblhau. + functional: Mae eich cyfrif yn gwbl weithredol. + pending: Mae'ch cais yn aros i gael ei adolygu gan ein staff. Gall hyn gymryd cryn amser. Byddwch yn derbyn e-bost os caiff eich cais ei gymeradwyo. + redirecting_to: Mae eich cyfrif yn anactif oherwydd ei fod ar hyn o bryd yn ailgyfeirio i %{acct}. trouble_logging_in: Trafferdd mewngofnodi? authorize_follow: already_following: Yr ydych yn dilyn y cyfrif hwn yn barod @@ -580,6 +612,11 @@ cy: return: Dangos proffil y defnyddiwr web: I'r wê title: Dilyn %{acct} + challenge: + confirm: Parhau + hint_html: "<strong>Awgrym:</strong> Ni fyddwn yn gofyn i chi am eich cyfrinair eto am yr awr nesaf." + invalid_password: Cyfrinair annilys + prompt: Cadarnhewch gyfrinair i barhau datetime: distance_in_words: about_x_hours: "%{count}awr" @@ -595,30 +632,27 @@ cy: x_months: "%{count}mis" x_seconds: "%{count}eiliad" deletes: - bad_password_msg: Go dda, hacwyr! Cyfrinair anghywir + challenge_not_passed: Nid oedd y wybodaeth a nodoch yn gywir confirm_password: Mewnbynnwch eich cyfrinair presennol i gadarnhau mai chi sydd yno - description_html: Bydd hyn yn cael gwared ar gynnwys o'ch cyfrif <strong>am byth heb fodd i'w adfer</strong> ac yn diffodd y cyfrif. Caiff eich eich enw defnyddiwr ei gadw i atal unrhyw ddynwarediadau yn y dyfodol. + confirm_username: Rhowch eich enw defnyddiwr i gadarnhau'r weithdrefn proceed: Dileu cyfrif success_msg: Llwyddwyd i ddileu eich cyfrif - warning_html: Dim ond dileu cynnwys o'r achos hwn ellid bod yn sicr ei fod wedi ei ddileu. Mae cynnwys sydd wedi ei rannu'n eang yn debygol o adael olion. Ni fydd gweinyddwyr all-lein a gweinyddwyr sydd wedi dad-danysgrifio o'ch diwedderiadau ddim yn diweddaru eu cronfeydd data. - warning_title: Argaeledd cynnwys wedi'i rannu + warning: + before: 'Cyn bwrw ymlaen, darllenwch y nodiadau hyn yn ofalus:' + irreversible: Ni fyddwch yn gallu adfer nac ail-greu eich cyfrif + username_available: Bydd eich enw defnyddiwr ar gael eto + username_unavailable: Ni fydd eich enw defnyddiwr ar gael directories: directory: Cyfeiriadur proffil - enabled: Rydych chi wedi'ch rhestru yn y cyfeiriadur ar hyn o bryd. - enabled_but_waiting: Rydych wedi dewis i chi gael eich rhestru yn y cyfeiriadur, ond nid oes gennych y nifer lleiaf o ddilynwyr (%{min_followers}) i'w rhestru eto. explanation: Darganfod defnyddwyr yn seiliedig ar eu diddordebau explore_mastodon: Archwilio %{title} - how_to_enable: Ar hyn o bryd nid ydych chi wedi dewis y cyfeiriadur. Gallwch ddewis i mewn isod. Defnyddiwch hashnodau yn eich bio-destun i'w restru dan hashnodau penodol! - people: - few: "%{count} o bobl" - many: "%{count} o bobl" - one: "%{count} berson" - other: "%{count} o bobl" - two: "%{count} o bobl" - zero: "%{count} person" + domain_validator: + invalid_domain: ddim yn enw parth dilys errors: + '400': The request you submitted was invalid or malformed. '403': Nid oes gennych ganiatad i weld y dudalen hon. '404': Nid yw'r dudalen yr oeddech yn chwilio amdani'n bodoli. + '406': This page is not available in the requested format. '410': Nid yw'r dudalen yr oeddech yn chwilio amdani'n bodoli mwyach. '422': content: Methwyd i ddilysu diogelwch. A ydych chi'n blocio cwcîs? @@ -627,6 +661,7 @@ cy: '500': content: Mae'n ddrwg gennym ni, ond fe aeth rhywbeth o'i le ar ein rhan ni. title: Nid yw'r dudalen hon yn gywir + '503': The page could not be served due to a temporary server failure. noscript_html: I ddefnyddio ap gwe Mastodon, galluogwch JavaScript os gwlwch yn dda. Fel arall, gallwch drio un o'r <a href="%{apps_path}">apiau cynhenid</a> ar gyfer Mastodon ar eich platfform. existing_username_validator: not_found: ni ddarganfwyd defnyddiwr lleol gyda'r enw cyfrif hynny @@ -670,6 +705,7 @@ cy: developers: Datblygwyr more: Mwy… resources: Adnoddau + trending_now: Yn tueddu nawr generic: all: Popeth changes_saved_msg: Llwyddwyd i gadw y newidiadau! @@ -753,9 +789,12 @@ cy: too_many: Ni ellir ychwanegu mwy na 4 dogfen migrations: acct: enwdefnyddiwr@parth y cyfrif newydd - currently_redirecting: 'Mae eich proffil wedi ei osod i ailgyfeirio i:' - proceed: Cadw - updated_msg: Diweddarwyd gosodiad mudo eich cyfrif yn llwyddiannus! + cancel: Canslo ailgyfeirio + errors: + not_found: ni ellid dod o hyd iddo + past_migrations: Ymfudiadau yn y gorffennol + warning: + before: 'Cyn bwrw ymlaen, darllenwch y nodiadau hyn yn ofalus:' moderation: title: Goruwchwyliad notification_mailer: @@ -862,10 +901,6 @@ cy: reply: proceed: Ymlaen i ateb prompt: 'Hoffech ateb y tŵt hon:' - remote_unfollow: - error: Gwall - title: Teitl - unfollowed: Dad-ddilynwyd scheduled_statuses: over_daily_limit: Rydych wedi rhagori'r cyfwng o %{limit} o dŵtiau rhestredig ar y dydd hynny over_total_limit: Rydych wedi rhagori'r cyfwng o %{limit} o dŵtiau rhestredig @@ -914,6 +949,7 @@ cy: settings: account: Cyfrif account_settings: Gosodiadau'r cyfrif + aliases: Aliasau cyfrif appearance: Arddangosiad authorized_apps: Apiau awdurdodedig back: Yn ôl i Mastodon @@ -988,6 +1024,8 @@ cy: pinned: Tŵt wedi'i binio reblogged: hybwyd sensitive_content: Cynnwys sensitif + tags: + does_not_match_previous_name: ddim yn cyfateb i'r enw blaenorol terms: body_html: | <h2>Polisi Preifatrwydd</h2> @@ -1105,7 +1143,9 @@ cy: disable: Er bod eich cyfrif wedi'i rewi, mae eich data cyfrif yn parhau i fod yn gyfan, ond ni allwch chi berfformio unrhyw gamau nes ei ddatgloi. silence: Pan mae eich cyfrif yn gyfyngiedig, dim ond pobl sydd yn barod yn eich dilyn yn gweld eich tŵtiau ar y gweinydd hon, a efallai byddwch yn cael eich tynnu o restrau cyhoeddus. Er hyn, gall eraill eich dilyn chi wrth law. suspend: Mae eich cyfrif wedi cael ei wahardd, a mae gyd o'ch tŵtiau a'ch ffeiliau cyfrwng uwchlwythadwy wedi cael eu tynnu or gweinydd yn barhaol, ac o weinyddau ble yr oedd eich dilynwyr. + get_in_touch: Gallwch ymateb i'r e-bost hwn i gysylltu â staff %{instance}. review_server_policies: Adolygu polisïau'r gweinydd + statuses: 'Yn benodol, ar gyfer:' subject: disable: Mae'ch cyfrif %{acct} wedi'i rewi none: Rhybudd am %{acct} diff --git a/config/locales/da.yml b/config/locales/da.yml index da6ab1054514d89477b15549d8ae658ad00c291e..5e291154a80c2d376f522a063c119972cfd270db 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -4,23 +4,28 @@ da: about_hashtag_html: Disse er offentlige trut der indeholder tagget <strong>#%{hashtag}</strong>. Du kan interagere med dem hvis du har en konto hvor som helst i fediverset. about_mastodon_html: Mastodon er et socialt netværk der er baseret pÃ¥ Ã¥bne web protokoller og frit, open-source source software. Der er decentraliseret ligesom e-mail tjenester. about_this: Om + active_count_after: aktive administered_by: 'Administreret af:' + api: API apps: Apps til mobilen apps_platforms: Brug Mastodon pÃ¥ iOS, Android og andre platformer contact: Kontakt contact_missing: Ikke sat contact_unavailable: Ikke tilgængeligt + discover_users: Opdag brugere documentation: Dokumentation - extended_description_html: | - <h3>Et godt sted for regler</h3> - <p>Den udvidede beskrivelse er endnu ikke blevet opsat.</p> - generic_description: "%{domain} er en server i netværket" + get_apps: Prøv en mobil app hosted_on: Mostodon hostet pÃ¥ %{domain} learn_more: Lær mere privacy_policy: Privatlivspolitik + see_whats_happening: Se hvad der sker source_code: Kildekode + status_count_after: + one: status + other: statusser status_count_before: Som har skrevet terms: VilkÃ¥r for service + unavailable_content: Utilgængeligt indhold user_count_after: one: bruger other: brugere @@ -34,10 +39,12 @@ da: other: Følgere following: Følger joined: Tilmeldt den %{date} + last_active: sidst aktiv link_verified_on: Ejerskabet af dette link blev tjekket den %{date} media: Medier moved_html: "%{name} er flyttet til %{new_profile_link}:" network_hidden: Denne information er ikke tilgængelig + never_active: Aldrig nothing_here: Der er intet her! people_followed_by: Folk som %{name} følger people_who_follow: Folk der følger %{name} @@ -53,6 +60,7 @@ da: admin: Administrator bot: Robot moderator: Moderator + unavailable: Profil utilgængelig unfollow: Følg ikke længere admin: account_actions: @@ -63,6 +71,8 @@ da: delete: Slet destroyed_msg: Moderator notat succesfuldt destrueret! accounts: + approve: Godkend + approve_all: Godkend alle are_you_sure: Er du sikker? avatar: Profilbillede by_domain: Domæne @@ -76,6 +86,7 @@ da: confirm: Bekræft confirmed: Bekræftet confirming: Bekræfter + deleted: Slettet demote: Degrader disable: Deaktiver disable_two_factor_authentication: Deaktiver 2FA @@ -90,7 +101,9 @@ da: followers_url: Link til følgere follows: Følger inbox_url: Link til indbakke + invited_by: Inviteret af ip: IP-adresse + joined: Tilmeldt den location: all: Alle local: Lokalt @@ -100,7 +113,9 @@ da: media_attachments: Medie bilag memorialize: Omdan til et memoriam moderation: + active: Aktiv all: Alle + pending: Afventer silenced: Dæmpet suspended: Udelukket title: Moderasion @@ -116,6 +131,8 @@ da: public: Offentligt push_subscription_expires: PuSH abonnement udløber redownload: Opdater profil + reject: Afvis + reject_all: Afvis alle remove_avatar: Fjern profilbillede resend_confirmation: already_confirmed: Denne bruger er allerede blevet bekræftet @@ -147,11 +164,14 @@ da: undo_suspension: Fortryd udelukkelse unsubscribe: Abonner ikke længere username: Brugernavn + warn: Advar + whitelisted: Hvidlistet action_logs: actions: assigned_to_self_report: "%{name} tildelte anmeldelsen %{target} til sig selv" change_email_user: "%{name} ændrede email adressen for brugeren %{target}" confirm_user: "%{name} bekræftede %{target}s email adresse" + create_account_warning: "%{name} sendte en advarsel til %{target}" create_custom_emoji: "%{name} uploadede humørikonet %{target}" create_domain_block: "%{name} blokerede domænet %{target}" create_email_domain_block: "%{name} sortlistede email domænet %{target}" @@ -184,6 +204,7 @@ da: copied_msg: Succesfuldt oprettede en lokal kopi af humørikonet copy: Kopier copy_failed_msg: Kunne ikke oprette en lokal kopi af dette humørikon + create_new_category: Opret ny kategori created_msg: Humørikon succesfuldt oprettet! delete: Slet destroyed_msg: Emojo succesfuldt destrueret! @@ -209,6 +230,7 @@ da: config: Konfiguration feature_deletions: Konto sletninger feature_invites: Invitations links + feature_profile_directory: Profilliste feature_registrations: Registreringer feature_relay: Føderations relæ features: Funktioner @@ -224,6 +246,8 @@ da: week_interactions: interaktioner denne uge week_users_active: aktive denne uge week_users_new: brugere denne uge + domain_allows: + undo: Fjern fra hvidliste domain_blocks: add_new: Tilføj ny domain block created_msg: Domæne blokade bliver nu behandlet @@ -238,6 +262,8 @@ da: silence: Dæmp suspend: Udeluk title: Ny domæne blokering + private_comment: Privat kommentar + public_comment: Offentlig kommentar reject_media: Afvis medie filer reject_media_hint: Fjerner lokalt lagrede multimedie filer og nægter at hente nogen i fremtiden. Irrelevant for udelukkelser reject_reports: Afvis anmeldelser @@ -263,8 +289,18 @@ da: title: Email sortliste followers: back_to_account: Tilbage til konto + title: "%{acct}'s følgere" instances: + by_domain: Domæne + moderation: + all: Alle + limited: Begrænset + private_comment: Privat kommentar + public_comment: Offentlig kommentar title: Førderation + total_blocked_by_us: Blokeret af os + total_followed_by_them: Fulgt af dem + total_followed_by_us: Fulgt af os invites: deactivate_all: Deaktiver alle filter: @@ -286,6 +322,7 @@ da: pending: Venter pÃ¥ godkendelse fra relæet save_and_enable: Gem og aktiver setup: Opsæt en videresendelses forbindelse + status: Status title: Videresendelser report_notes: created_msg: Anmeldelse note blev oprettet! @@ -315,6 +352,7 @@ da: reported_by: Anmeldt af resolved: Løst resolved_msg: Anmeldelse er sat til at være løst! + status: Status title: Anmeldelser unassign: Utildel unresolved: Uløst @@ -332,6 +370,9 @@ da: custom_css: desc_html: Ændre udseendet med CSS indlæst pÃ¥ hver side title: Brugerdefineret CSS + domain_blocks: + all: Til alle + disabled: Til ingen hero: desc_html: Vist pÃ¥ forsiden. Mindst 600x100px anbefales. Hvis ikke sat, vil dette falde tilbage til billedet fra serveren title: Billede af helt @@ -351,6 +392,10 @@ da: min_invite_role: disabled: Ingen title: Tillad invitationer af + registrations_mode: + modes: + none: Ingen kan tilmelde sig + open: Alle kan tilmelde sig show_known_fediverse_at_about_page: desc_html: NÃ¥r slÃ¥et til, vil det vise trut fra hele det kendte fedivers pÃ¥ forhÃ¥ndsvisning. Ellers vil det kun vise lokale trut. title: Vis kendte fedivers pÃ¥ tidslinje forhÃ¥ndsvisning @@ -383,6 +428,7 @@ da: delete: Slet nsfw_off: Marker som værende ikke følsomt nsfw_on: Marker som værende følsomt + deleted: Slettet failed_to_execute: Udførelsen kunne ikke lade sig gøre media: title: Multimedier @@ -390,23 +436,24 @@ da: no_status_selected: Ingen statusser blev ændret eller ingen blev valgt title: Konto statusser with_media: Med multimedier - subscriptions: - callback_url: Callback-URL - confirmed: Bekræftet - expires_in: Udløber om - last_delivery: Sidste levering - title: Websub - topic: Emne tags: - accounts: Kontoer - hidden: Skjult + accounts_today: Unikke brug i dag + accounts_week: Unikke brug denne uge + context: Kontekst + warning_presets: + delete: Slet + edit: Rediger admin_mailer: new_report: body: "%{reporter} har anmeldt %{target}" body_remote: Nogen fra %{domain} har anmeldt %{target} subject: Ny anmeldelse for %{instance} (#%{id}) + appearance: + discovery: Opdagelse + sensitive_content: Følsomt indhold application_mailer: notification_preferences: Ændre email præferencer + salutation: "%{name}" settings: 'Ændre email præferencer: %{link}' view: 'Se:' view_profile: Se profil @@ -420,10 +467,12 @@ da: warning: Vær meget forsigtig med disse data. Del dem aldrig med nogen! your_token: Din adgangs token auth: + apply_for_account: Anmod om en invitation change_password: Kodeord - confirm_email: Bekræft email delete_account: Slet konto delete_account_html: Hvis du ønsker at slette din konto, kan du <a href="%{path}">gøre det her</a>. Du vil blive bedt om bekræftelse. + description: + prefix_sign_up: Tilmeld dig Mastodon i dag! didnt_get_confirmation: Har du endnu ikke modtaget instrukser for bekræftelse? forgot_password: Glemt dit kodeord? invalid_reset_password_token: Adgangskode nulstillings token er ugyldig eller udløbet. Anmod venligst om en ny. @@ -433,10 +482,14 @@ da: migrate_account_html: Hvis du ønsker at omdirigere denne konto til en anden, kan du <a href="%{path}">gøre det her</a>. or_log_in_with: Eller log in med register: Opret dig + registration_closed: "%{instance} accepterer ikke nye medlemmer" resend_confirmation: Gensend bekræftelses instrukser reset_password: Nulstil kodeord security: Sikkerhed set_new_password: Sæt et nyt kodeord + status: + account_status: Kontostatus + trouble_logging_in: Har du problemer med at logge pÃ¥? authorize_follow: already_following: Du følger allerede denne konto error: Der opstod desværre en fejl under søgningen af denne fjerne konto @@ -459,16 +512,20 @@ da: over_x_years: "%{count}Ã¥r" x_months: "%{count}md" deletes: - bad_password_msg: Godt forsøg, hackere! Forkert kodeord confirm_password: Indtast dit nuværende kodeord for at bekræfte din identitet - description_html: Dette vil <strong>permanent, uigenkaldeligt</strong> fjerne indhold fra din konto samt deaktivere den. Dit brugernavn vil forblive reserveret for at forhindre fremtidige efterligninger. proceed: Slet konto success_msg: Din konto er nu blevet slettet - warning_html: Kun sletning af indhold fra denne specifikke server er garanteret. Indhold der er blevet delt rundt omkring vil sandsynligvis efterlade spor. Offline servere og servere der ikke længere abonnerer pÃ¥ dine opdateringer vil ikke opdatere deres databaser. - warning_title: Tilgængelighed af delt indhold + warning: + username_available: Dit brugernavn vil blive tilgængeligt igen + username_unavailable: Dit brugernavn vil forblive utilgængeligt + directories: + directory: Profilliste + explore_mastodon: Uforsk %{title} errors: + '400': The request you submitted was invalid or malformed. '403': Du har ikke tilladelse til at se denne side. '404': Den side du leder efter findes ikke. + '406': This page is not available in the requested format. '410': Den side du leder efter findes ikke mere. '422': content: Sikkerhedsbekræftelse mislykkedes. Blokerer du cookies? @@ -477,7 +534,11 @@ da: '500': content: Beklager men der gik noget galt i vores ende. title: Siden er ikke korrekt + '503': The page could not be served due to a temporary server failure. noscript_html: For at bruge Mastodon web applikationen, aktiver JavaScript. Alternativt kan du prøve en af disse <a href="%{apps_path}">apps</a> til Mastodon for din platform. + existing_username_validator: + not_found: kunne ikke finde en lokal bruger med dette brugenavn + not_found_multiple: kunne ikke finde %{usernames} exports: archive_takeout: date: Dato @@ -487,7 +548,9 @@ da: request: Anmod om dit arkiv size: Størrelse blocks: Du blokerer + csv: CSV follows: Du følger + lists: Lister mutes: Du dæmper storage: Medie lager filters: @@ -510,14 +573,22 @@ da: developers: Udviklere more: Mere… resources: Ressourcer + trending_now: Hot lige nu generic: + all: Alle changes_saved_msg: Ændringerne blev gemt! copy: Kopier + order_by: Sorter efter save_changes: Gem ændringer validation_errors: one: Der er noget der ikke er helt som det bør være! Tag lige et kig pÃ¥ følgende fejl forneden other: Der er noget der ikke er helt som det bør være! Tag lige et kig pÃ¥ følgende %{count} fejl forneden + identity_proofs: + i_am_html: Jeg er %{username} pÃ¥ %{service}. + identity: Identitet imports: + modes: + overwrite: Overskriv preface: Du kan importere data du har eksporteret fra en anden server, sÃ¥ som en liste over folk du følger eller blokerer. success: Dine data blev succesfuldt uploaded og vil nu blive behandlet hurtigst muligt types: @@ -557,9 +628,6 @@ da: too_many: Kan ikke vedhæfte mere en 4 filer migrations: acct: username@domain af den nye konto - currently_redirecting: 'Din profil er sat til at henvise til:' - proceed: Gem - updated_msg: Dine konti migrærings indstillinger blev opdateret! moderation: title: Moderatering notification_mailer: @@ -608,20 +676,28 @@ da: older: Ældre prev: Forrige truncate: "...…" + polls: + errors: + duration_too_long: er for langt ude i fremtiden + duration_too_short: er for tidligy + expired: Denne afstemning er allerede afsluttet preferences: other: Andet + public_timelines: Offentlige tidslinjer + relationships: + last_active: Sidst aktiv + moved: Flyttet + mutual: Fælles + primary: Primær remote_follow: acct: Indtast dit brugernavn@domæne du vil handle fra missing_resource: Kunne ikke finde det pÃ¥krævede omdirigerings link for din konto no_account_html: Har du ikke en konto? Du kan <a href='%{sign_up_path}' target='_blank'>oprette dig her</a> proceed: Fortsæt for at følge prompt: 'Du er ved at følge:' - remote_unfollow: - error: Fejl - title: Titel - unfollowed: Følger ikke længere sessions: activity: Sidste aktivitet + browser: Browser browsers: alipay: Ali-pay blackberry: Blackberry OS @@ -643,11 +719,15 @@ da: current_session: Nuværrende session description: "%{browser} pÃ¥ %{platform}" explanation: Disse er de web browsere der pÃ¥ nuværende tidspunkt er logget ind pÃ¥ din Mastodon konto. + ip: IP platforms: adobe_air: Adobe air + android: Android blackberry: Blackberry OS chrome_os: Chromeos firefox_os: Firefox Os + ios: iOS + linux: Linux mac: Mac. other: ukendt platform windows: Microsoft windows @@ -657,6 +737,9 @@ da: revoke_success: Sessionen blev tilbagekaldt title: Sessioner settings: + account: Konto + account_settings: Kontoindstillinger + appearance: Udseende authorized_apps: Godkendte apps back: Tilbage til Mastodon delete: Sletning af konto @@ -664,9 +747,12 @@ da: edit_profile: Rediger profil export: Data eksportering import: Importer + import_and_export: Importer og eksporter migrate: Konto migrering notifications: Notifikationer preferences: Præferencer + profile: Profil + relationships: Følger og følgere two_factor_authentication: To-faktor godkendelse statuses: attached: @@ -674,6 +760,9 @@ da: image: one: "%{count} billede" other: "%{count} billeder" + video: + one: "%{count} video" + other: "%{count} videoer" boosted_from_html: Fremhævet fra %{acct_link} content_warning: 'Advarsel om indhold: %{warning}' disallowed_hashtags: @@ -687,6 +776,8 @@ da: ownership: Du kan ikke fastgøre en anden persons trut private: Ikke offentlige trut kan ikke blive fastgjort reblog: Fremhævede trut kan ikke fastgøres + poll: + vote: Stem show_more: Vis mere sign_in_to_participate: Log ind for at deltage i samtalen visibilities: @@ -728,6 +819,9 @@ da: explanation: Din anmodning for fuld backup af din Mastodon konto. Den er nu klar til at blive hentet! subject: Dit arkiv er klar til at blive hentet ned title: Udpluk af arkiv + warning: + title: + none: Advarsel welcome: edit_profile_action: Opsæt profil edit_profile_step: Du kan skræddersy din profil ved at uploade et profilbillede, overskrift, ændre dit visningsnavn og mere. Hvis du kunne tænke dig at gennemse nye følgere før de mÃ¥ følge dig, kan du lÃ¥se din konto. @@ -751,3 +845,5 @@ da: otp_lost_help_html: Hvis du har mistet adgang til begge, kan du fÃ¥ kontakt via %{email} seamless_external_login: Du er logget ind via en ekstern service, sÃ¥ er kodeord og e-mail indstillinger ikke tilgængelige. signed_in_as: 'Logget ind som:' + verification: + verification: Verificering diff --git a/config/locales/de.yml b/config/locales/de.yml index cfdaacab0657d17a66674eb585cc8fedae6e954a..77c66fb713c9d3a7221957e18424683904104d6e 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -5,7 +5,7 @@ de: about_mastodon_html: Mastodon ist ein soziales Netzwerk. Es basiert auf offenen Web-Protokollen und freier, quelloffener Software. Es ist dezentral (so wie E-Mail!). about_this: Über diesen Server active_count_after: aktiv - active_footnote: Monatlich Aktive Nutzer_innen (MAU) + active_footnote: Monatlich Aktive User (MAU) administered_by: 'Betrieben von:' api: API apps: Mobile Apps @@ -15,15 +15,14 @@ de: contact: Kontakt contact_missing: Nicht angegeben contact_unavailable: Nicht verfügbar - discover_users: Benutzer_innen entdecken + discover_users: Benutzer entdecken documentation: Dokumentation - extended_description_html: | - <h3>Ein hervorragender Ort für Regeln</h3> - <p>Die erweiterte Beschreibung wurde von dem Administrator noch nicht eingestellt.</p> - federation_hint_html: Mit einem Konto auf %{instance} wirst du in der Lage sein Nutzer_innen auf beliebigen Mastodon-Servern und darüber hinaus zu folgen. - generic_description: "%{domain} ist ein Server im Fediversum" + federation_hint_html: Mit einem Account auf %{instance} wirst du in der Lage sein Nutzern auf irgendeinem Mastodon-Server und darüber hinaus zu folgen. get_apps: Versuche eine mobile App hosted_on: Mastodon, gehostet auf %{domain} + instance_actor_flash: | + Dieses Konto ist ein virtueller Akteur, der den Server selbst und nicht einen einzelnen Benutzer repräsentiert. + Dieser wird für Föderationszwecke verwendet und sollte nicht blockiert werden, es sei denn du möchtest die gesamte Instanz blockieren. learn_more: Mehr erfahren privacy_policy: Datenschutzerklärung see_whats_happening: Finde heraus, was gerade in der Welt los ist @@ -35,6 +34,14 @@ de: status_count_before: mit tagline: Finde deine Freunde und entdecke neue terms: Nutzungsbedingungen + unavailable_content: Nicht verfügbarer Inhalt + unavailable_content_description: + domain: Server + reason: 'Grund:' + rejecting_media: Mediendateien dieses Servers werden nicht verarbeitet und keine Thumbnails werden angezeigt, was manuelles anklicken auf den anderen Server erfordert. + silenced: Beiträge von diesem Server werden nirgends angezeigt, außer in deiner Startseite, wenn du der Person folgst, die den Beitrag verfasst hat. + suspended: Du kannst niemanden von diesem Server folgen, und keine Daten werden verarbeitet oder gespeichert und keine Daten ausgetauscht. + unavailable_content_html: Mastodon erlaubt es dir generell, mit Inhalten zu interagieren, diese anzuzeigen und mit anderen Nutzern im Fediversum über Server hinweg zu interagieren. Dies sind die Ausnahmen, die auf diesem bestimmten Server gemacht wurden. user_count_after: one: Profil other: Profile @@ -42,10 +49,12 @@ de: what_is_mastodon: Was ist Mastodon? accounts: choices_html: "%{name} empfiehlt:" + endorsements_hint: Du kannst Personen, denen du über die Weboberfläche folgst, auswählen, und sie werden hier angezeigt. + featured_tags_hint: Du kannst spezifische Hashtags, die hier angezeigt werden, angeben. follow: Folgen followers: - one: Folger_innen - other: Folger_innen + one: Folgender + other: Folgende following: Folgt joined: Beigetreten am %{date} last_active: zuletzt aktiv @@ -53,6 +62,7 @@ de: media: Medien moved_html: "%{name} ist auf %{new_profile_link} umgezogen:" network_hidden: Diese Informationen sind nicht verfügbar + never_active: Nie nothing_here: Hier gibt es nichts! people_followed_by: Profile, denen %{name} folgt people_who_follow: Profile, die %{name} folgen @@ -108,8 +118,8 @@ de: enable: Freischalten enabled: Freigegeben feed_url: Feed-URL - followers: Folger_innen - followers_url: URL der Folger_innen + followers: Folgende + followers_url: URL des Folgenden follows: Folgt header: Titelbild inbox_url: Posteingangs-URL @@ -183,6 +193,7 @@ de: username: Profilname warn: Warnen web: Web + whitelisted: Auf der Whitelist action_logs: actions: assigned_to_self_report: "%{name} hat sich die Meldung %{target} selbst zugewiesen" @@ -218,19 +229,24 @@ de: deleted_status: "(gelöschter Beitrag)" title: Überprüfungsprotokoll custom_emojis: + assign_category: Kategorie zuweisen by_domain: Domain copied_msg: Eine lokale Kopie des Emojis wurde erstellt copy: Kopieren copy_failed_msg: Es konnte keine lokale Kopie des Emojis erstellt werden + create_new_category: Neue Kategorie erstellen created_msg: Emoji erstellt! delete: Löschen destroyed_msg: Emoji gelöscht! disable: Deaktivieren + disabled: Deaktiviert disabled_msg: Das Emoji wurde deaktiviert emoji: Emoji enable: Aktivieren + enabled: Aktiviert enabled_msg: Das Emoji wurde aktiviert image_hint: PNG bis zu 50 kB + list: Liste listed: Gelistet new: title: Eigenes Emoji hinzufügen @@ -238,11 +254,14 @@ de: shortcode: Kürzel shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche title: Eigene Emojis + uncategorized: Nicht kategorisiert + unlist: Nicht listen unlisted: Ungelistet update_failed_msg: Konnte dieses Emoji nicht aktualisieren updated_msg: Emoji erfolgreich aktualisiert! upload: Hochladen dashboard: + authorized_fetch_mode: Autorisierter Abruf-Modus backlog: Rückständige Jobs config: Konfiguration feature_deletions: Kontolöschung @@ -250,26 +269,36 @@ de: feature_profile_directory: Profilverzeichnis feature_registrations: Offene Anmeldung feature_relay: Föderationsrelais + feature_spam_check: Anti-Spam feature_timeline_preview: Zeitleistenvorschau features: Funktionen hidden_service: Föderation mit versteckten Diensten open_reports: Ausstehende Meldungen + pending_tags: Hashtags, die auf eine Überprüfung warten + pending_users: Benutzer, die auf eine Überprüfung warten recent_users: Neueste Nutzer search: Volltextsuche single_user_mode: Einzelnutzermodus software: Software space: Speicherverbrauch title: Übersicht - total_users: Benutzer_innen insgesamt + total_users: Benutzer insgesamt trends: Trends week_interactions: Interaktionen diese Woche week_users_active: Aktiv diese Woche - week_users_new: Benutzer_innen diese Woche + week_users_new: Benutzer diese Woche + whitelist_mode: Whitelist-Modus + domain_allows: + add_new: Whitelist-Domain + created_msg: Domain wurde erfolgreich zur Whitelist hinzugefügt + destroyed_msg: Domain wurde von der Whitelist entfernt + undo: Von der Whitelist entfernen domain_blocks: add_new: Neue Domainblockade hinzufügen created_msg: Die Domain-Blockade wird nun durchgeführt destroyed_msg: Die Domain-Blockade wurde rückgängig gemacht domain: Domain + edit: Domainblockade bearbeiten existing_domain_block_html: Es gibt schon eine Blockade für %{name}, diese muss erst <a href="%{unblock_url}">aufgehoben</a> werden. new: create: Blockade einrichten @@ -280,6 +309,10 @@ de: silence: Stummschaltung suspend: Sperre title: Neue Domain-Blockade + private_comment: Privater Kommentar + private_comment_hint: Kommentar zu dieser Domain-Beschränkung für die interne Nutzung durch die Moderatoren. + public_comment: Öffentlicher Kommentar + public_comment_hint: Kommentar zu dieser Domain-Beschränkung für die allgemeine Öffentlichkeit, wenn das Veröffentlichen der Blockliste aktiviert ist. reject_media: Mediendateien ablehnen reject_media_hint: Entfernt lokal gespeicherte Mediendateien und verhindert deren künftiges Herunterladen. Für Sperren irrelevant reject_reports: Meldungen ablehnen @@ -299,6 +332,7 @@ de: title: Domain-Blockade für %{domain} zurücknehmen undo: Zurücknehmen undo: Domainblockade zurücknehmen + view: Zeige Domain-Blockade email_domain_blocks: add_new: Neue hinzufügen created_msg: E-Mail-Domain-Blockade erfolgreich erstellt @@ -311,7 +345,7 @@ de: title: E-Mail-Domain-Blockade followers: back_to_account: Zurück zum Konto - title: "%{acct}'s Folger_innen" + title: "%{acct}'s Follower" instances: by_domain: Domain delivery_available: Zustellung funktioniert @@ -322,6 +356,8 @@ de: all: Alle limited: Beschränkt title: Moderation + private_comment: Privater Kommentar + public_comment: Öffentlicher Kommentar title: Föderation total_blocked_by_us: Von uns blockiert total_followed_by_them: Gefolgt von denen @@ -351,6 +387,7 @@ de: pending: Warte auf Zustimmung des Relays save_and_enable: Speichern und aktivieren setup: Relaisverbindung einrichten + signatures_not_enabled: Relais funktionieren nicht korrekt, während der sichere Modus oder der Whitelist-Modus aktiviert ist status: Zustand title: Relais report_notes: @@ -399,6 +436,16 @@ de: custom_css: desc_html: Verändere das Aussehen mit CSS, dass auf jeder Seite geladen wird title: Benutzerdefiniertes CSS + default_noindex: + desc_html: Beeinflusst alle Benutzer, die diese Einstellung nicht selbst geändert haben + title: Benutzer aus Suchmaschinen-Indizierung standardmäßig herausnehmen + domain_blocks: + all: An alle + disabled: An niemanden + title: Zeige Domain-Blockaden + users: Für angemeldete lokale Benutzer + domain_blocks_rationale: + title: Rationale anzeigen hero: desc_html: Wird auf der Startseite angezeigt. Mindestens 600x100px sind empfohlen. Wenn es nicht gesetzt wurde, wird das Server-Thumbnail dafür verwendet title: Bild für Einstiegsseite @@ -449,6 +496,9 @@ de: desc_html: Hier kannst du deine eigenen Geschäftsbedingungen, Datenschutzerklärung und anderes rechtlich Relevante eintragen. Du kannst HTML-Tags nutzen title: Benutzerdefinierte Geschäftsbedingungen site_title: Name des Servers + spam_check_enabled: + desc_html: Mastodon kann automatisch Server stummschalten und automatisch Konten melden basierend auf Maßnahmen wie die Erkennung von Konten, die wiederholt unerwünschte Nachrichten senden. Es können hierbei mögliche Probleme auftreten. + title: Anti-Spam thumbnail: desc_html: Wird für die Vorschau via OpenGraph und API verwendet. 1200×630 px wird empfohlen title: Vorschaubild des Servers @@ -456,12 +506,19 @@ de: desc_html: Auf der Einstiegsseite die öffentliche Zeitleiste anzeigen title: Zeitleisten-Vorschau title: Server-Einstellungen + trendable_by_default: + desc_html: Betroffene Hashtags, die bisher nicht gesperrt wurden + title: Hashtags ohne vorherige Überprüfung erlauben zu trenden + trends: + desc_html: Zuvor überprüfte Hashtags öffentlich anzeigen, die derzeit angesagt sind + title: Trendende Hashtags statuses: back_to_account: Zurück zum Konto batch: delete: Löschen nsfw_off: Als nicht heikel markieren nsfw_on: Als heikel markieren + deleted: Gelöscht failed_to_execute: Ausführen fehlgeschlagen media: title: Medien @@ -469,21 +526,24 @@ de: no_status_selected: Keine Beiträge wurden geändert, weil keine ausgewählt wurden title: Beiträge des Kontos with_media: Mit Medien - subscriptions: - callback_url: Callback-URL - confirmed: Bestätigt - expires_in: Verfällt in - last_delivery: Letzte Zustellung - title: WebSub - topic: Thema tags: - accounts: Konten - hidden: Versteckt - hide: Vom Profilverzeichnis verstecken + accounts_today: Einzigartige Nutzungen heute + accounts_week: Einzigartige Nutzung dieser Woche + breakdown: Heruntergebrochene Statistiken der heutigen Nutzung nach Quelle + context: Kontext + directory: Im Verzeichnis + in_directory: "%{count} im Verzeichnis" + last_active: Zuletzt aktiv + most_popular: Am beliebtesten + most_recent: Neuste name: Hashtag + review: Prüfstatus + reviewed: Überprüft title: Hashtags - unhide: Zeige in Verzeichnis - visible: Sichtbar + trending_right_now: Aktuell in den Trends + unique_uses_today: "%{count} Beiträge heute" + unreviewed: Nicht überprüft + updated_msg: Hashtageinstellungen wurden erfolgreich aktualisiert title: Administration warning_presets: add_new: Neu hinzufügen @@ -499,11 +559,21 @@ de: body: "%{reporter} hat %{target} gemeldet" body_remote: Jemand von %{domain} hat %{target} gemeldet subject: Neue Meldung auf %{instance} (#%{id}) + new_trending_tag: + body: 'Der Hashtag #%{name} ist heute am trenden, aber wurde vorher noch nicht überprüft. Er wird nicht öffentlich angezeigt, es sei denn du erlaubst es oder speicherst das Formular ab und vergisst es.' + subject: Neuer Hashtag zur Überprüfung auf %{instance} verfügbar (#%{name}) + aliases: + add_new: Alias erstellen + created_msg: Ein neuer Alias wurde erfolgreich erstellt. Du kannst nun den Wechsel vom alten Konto starten. + deleted_msg: Der Alias wurde erfolgreich entfernt. Aus diesem Konto zu diesem zu verschieben ist nicht mehr möglich. + hint_html: Wenn du von einem Konto zu einem anderem Konto wechseln möchtest, dann kannst du einen Alias erstellen, welcher benötigt wird bevor du deine Folgenden vom altem Account zu diesen migrierst. Die Aktion alleine ist <strong>harmlos und wiÂderÂrufÂlich</strong>. <strong>Die Kontenmigration wird vom altem Konto aus eingeleitet</strong>. + remove: Alle Aliase aufheben appearance: advanced_web_interface: Fortgeschrittene Benutzeroberfläche advanced_web_interface_hint: Wenn du mehr aus deiner Bildschirmbreite herausholen möchtest, erlaubt dir die fortgeschrittene Benutzeroberfläche viele unterschiedliche Spalten auf einmal zu sehen, wie z.B. deine Startseite, Benachrichtigungen, das gesamte bekannte Netz, deine Listen und beliebige Hashtags. animations_and_accessibility: Animationen und Barrierefreiheit confirmation_dialogs: Bestätigungsfenster + discovery: Entdecken sensitive_content: Heikle Inhalte application_mailer: notification_preferences: Ändere E-Mail-Einstellungen @@ -524,9 +594,13 @@ de: apply_for_account: Eine Einladung anfragen change_password: Passwort checkbox_agreement_html: Ich akzeptiere die <a href="%{rules_path}" target="_blank">Server-Regeln</a> und die <a href="%{terms_path}" target="_blank">Nutzungsbedingungen</a> - confirm_email: E-Mail bestätigen + checkbox_agreement_without_rules_html: Ich stimme den <a href="%{terms_path}" target="_blank">Nutzungsbedingungen</a> zu delete_account: Konto löschen delete_account_html: Falls du dein Konto löschen willst, kannst du <a href="%{path}">hier damit fortfahren</a>. Du wirst um Bestätigung gebeten werden. + description: + prefix_invited_by_user: "@%{name} lädt dich ein, diesem Server von Mastodon beizutreten!" + prefix_sign_up: Melde dich heute bei Mastodon an! + suffix: Mit einem Konto kannst du Leuten folgen, Updates veröffentlichen und Nachrichten mit Benutzern von jedem Mastodon-Server austauschen und mehr! didnt_get_confirmation: Keine Bestätigungs-Mail erhalten? forgot_password: Passwort vergessen? invalid_reset_password_token: Das Token zum Zurücksetzen des Passworts ist ungültig oder abgelaufen. Bitte fordere ein neues an. @@ -544,6 +618,16 @@ de: reset_password: Passwort zurücksetzen security: Sicherheit set_new_password: Neues Passwort setzen + setup: + email_below_hint_html: Wenn die unten stehende E-Mail-Adresse falsch ist, kannst du sie hier ändern und eine neue Bestätigungs-E-Mail erhalten. + email_settings_hint_html: Die Bestätigungs-E-Mail wurde an %{email} gesendet. Wenn diese E-Mail-Adresse nicht korrekt ist, kannst du sie in den Einstellungen ändern. + title: Konfiguration + status: + account_status: Kontostatus + confirming: Warte auf die Bestätigung der E-Mail. + functional: Dein Konto ist voll funktionsfähig. + pending: Deine Bewerbung wird von unseren Mitarbeitern noch überprüft. Dies kann einige Zeit dauern. Du erhältst eine E-Mail, wenn deine Bewerbung genehmigt wurde. + redirecting_to: Dein Konto ist inaktiv, da es derzeit zu %{acct} umgeleitet wird. trouble_logging_in: Schwierigkeiten beim Anmelden? authorize_follow: already_following: Du folgst diesem Konto bereits @@ -556,6 +640,11 @@ de: return: Zeige das Profil web: In der Benutzeroberfläche öffnen title: "%{acct} folgen" + challenge: + confirm: Fortfahren + hint_html: "<strong>Hinweis:</strong> Wir werden dich für die nächste Stunde nicht erneut nach deinem Passwort fragen." + invalid_password: Ungültiges Passwort + prompt: Gib dein Passwort ein um fortzufahren datetime: distance_in_words: about_x_hours: "%{count}h" @@ -571,26 +660,33 @@ de: x_months: "%{count}mo" x_seconds: "%{count}s" deletes: - bad_password_msg: Falsches Passwort + challenge_not_passed: Die eingegebenen Informationen waren nicht korrekt confirm_password: Gib dein derzeitiges Passwort ein, um deine Identität zu bestätigen - description_html: Hiermit wird <strong>dauerhaft und unwiederbringlich</strong> der Inhalt deines Kontos gelöscht und dein Konto deaktiviert. Dein Profilname wird reserviert, um künftige Imitationen zu verhindern. + confirm_username: Gib deinen Benutzernamen ein, um das Verfahren zu bestätigen proceed: Konto löschen success_msg: Dein Konto wurde erfolgreich gelöscht - warning_html: Wir können nur dafür garantieren, dass die Inhalte auf diesem einen Server gelöscht werden. Bei Inhalten, die weit verbreitet wurden, ist es wahrscheinlich, dass Spuren bleiben werden. Server, die offline sind oder keine Benachrichtigungen von deinem Konto mehr empfangen, werden ihre Datenbanken nicht bereinigen. - warning_title: Verfügbarkeit verstreuter Inhalte + warning: + before: 'Bevor du fortfährst, lese bitte diese Punkte sorgfältig durch:' + caches: Inhalte, die von anderen Servern zwischengespeichert wurden, können weiterhin bestehen + data_removal: Deine Beiträge und andere Daten werden dauerhaft entfernt + email_change_html: Du kannst <a href="%{path}">deine E-Mail-Adresse ändern</a>, ohne dein Konto zu löschen + email_contact_html: Wenn die Bestätigungs-E-Mail immer noch nicht ankam, kannst du eine E-Mail an <a href="mailto:%{email}">%{email}</a> senden, um weitere Hilfe zu erhalten + email_reconfirmation_html: Wenn du die Bestätigungs-E-Mail nicht erhalten hast, kannst du sie <a href="%{path}">erneut anfordern</a> + irreversible: Du kannst dein Konto nicht reaktivieren + more_details_html: Weitere Details findest du in der <a href="%{terms_path}">Datenschutzrichtlinie</a>. + username_available: Dein Benutzername wird wieder verfügbar + username_unavailable: Dein Benutzername bleibt nicht verfügbar directories: directory: Profilverzeichnis - enabled: Du bist gerade in dem Verzeichnis gelistet. - enabled_but_waiting: Du bist damit einverstanden im Verzeichnis aufgelistet zu werden, aber du hast noch nicht genug Folger_innen (%{min_followers}). - explanation: Entdecke Benutzer_innen basierend auf deren Interessen + explanation: Entdecke Benutzer basierend auf deren Interessen explore_mastodon: Entdecke %{title} - how_to_enable: Du hast dich gerade nicht dazu entschieden im Verzeichnis gelistet zu werden. Du kannst dich unten dafür eintragen. Benutze Hashtags in deiner Profilbeschreibung, um unter spezifischen Hashtags gelistet zu werden! - people: - one: "%{count} Person" - other: "%{count} Leute" + domain_validator: + invalid_domain: ist kein gültiger Domain-Name errors: + '400': Die Anfrage, die du gesendet hast, war ungültig oder fehlerhaft. '403': Dir fehlt die Befugnis, diese Seite sehen zu können. '404': Die Seite nach der du gesucht hast wurde nicht gefunden. + '406': Diese Seite ist im gewünschten Format nicht verfügbar. '410': Die Seite nach der du gesucht hast existiert hier nicht mehr. '422': content: Sicherheitsüberprüfung fehlgeschlagen. Blockierst du Cookies? @@ -599,6 +695,7 @@ de: '500': content: Bitte verzeih, etwas ist bei uns schief gegangen. title: Diese Seite ist kaputt + '503': Die Seite konnte wegen eines temporären Serverfehlers nicht angezeigt werden. noscript_html: Bitte aktiviere JavaScript, um die Mastodon-Web-Anwendung zu verwenden. Alternativ kannst du auch eine der <a href="%{apps_path}">nativen Mastodon-Anwendungen</a> für deine Plattform probieren. existing_username_validator: not_found: kann lokalen Benutzer nicht mit diesem Nuternamen finden @@ -622,6 +719,7 @@ de: add_new: Neu hinzufügen errors: limit: Du hast bereits die maximale Anzahl an empfohlenen Hashtags erreicht + hint_html: "<strong>Was sind empfohlene Hashtags?</strong> Sie werden in deinem öffentlichen Profil deutlich angezeigt und ermöglichen es den Menschen, deine öffentlichen Beiträge speziell unter diesen Hashtags zu durchsuchen. Sie sind ein großartiges Werkzeug, um kreative Werke oder langfristige Projekte zu verfolgen." filters: contexts: home: Startseite @@ -642,10 +740,12 @@ de: developers: Entwickler more: Mehr… resources: Ressourcen + trending_now: In den Trends generic: all: Alle changes_saved_msg: Änderungen gespeichert! copy: Kopieren + no_batch_actions_available: Keine Massenaktionen auf dieser Seite verfügbar order_by: Sortieren nach save_changes: Änderungen speichern validation_errors: @@ -717,9 +817,34 @@ de: too_many: Es können nicht mehr als 4 Dateien angehängt werden migrations: acct: benutzername@domain des neuen Kontos - currently_redirecting: 'Deine Profilweiterleitung wurde gesetzt auf:' - proceed: Speichern - updated_msg: Deine Konto-Migrationseinstellungen wurden erfolgreich aktualisiert! + cancel: Umleitung abbrechen + cancel_explanation: Das Abbrechen der Umleitung wird dein aktuelles Konto erneut aktivieren, aber keine Folgenden, die auf dieses Konto verschoben wurden, zurückholen. + cancelled_msg: Die Umleitung wurde erfolgreich abgebrochen. + errors: + already_moved: ist das gleiche Konto, zu dem du bereits umgezogen bist + missing_also_known_as: referenziert nicht zurück auf dieses Konto + move_to_self: darf nicht das aktuelles Konto sein + not_found: kann nicht gefunden werden + on_cooldown: Die Abklingzeit läuft gerade + followers_count: Folgende zur Zeit des Verschiebens + incoming_migrations: Ziehe von einem anderen Konto um + incoming_migrations_html: Um von einem anderen Konto zu diesem zu wechseln, musst du zuerst <a href="%{path}">einen Kontoalias</a> erstellen. + moved_msg: Dein Konto wird jetzt zu %{acct} weitergeleitet und deine Folgende werden verschoben. + not_redirecting: Dein Konto wird derzeit nicht auf ein anderes Konto weitergeleitet. + on_cooldown: Du hast dein Konto vor kurzem migriert. Diese Funktion wird in %{count} Tagen wieder verfügbar sein. + past_migrations: Vorherige Migrationen + proceed_with_move: Folgende verschieben + redirecting_to: Dein Konto wird zu %{acct} weitergeleitet. + set_redirect: Umleitung einrichten + warning: + backreference_required: Das neue Konto muss zuerst so konfiguriert werden, dass es auf das alte Konto referenziert + before: 'Bevor du fortfährst, lese bitte diese Hinweise sorgfältig durch:' + cooldown: Nach dem Migrieren wird es eine Abklingzeit geben, in der du das Konto nicht noch einmal migrieren kannst + disabled_account: Dein aktuelles Konto wird nachher nicht vollständig nutzbar sein. Du hast jedoch Zugriff auf den Datenexport sowie die Reaktivierung. + followers: Diese Aktion wird alle Folgende vom aktuellen Konto auf das neue Konto verschieben + only_redirect_html: Alternativ kannst du <a href="%{path}">nur eine Weiterleitung auf dein Profil</a> erstellen. + other_data: Keine anderen Daten werden automatisch verschoben + redirect: Das Profil deines aktuellen Kontos wird mit einer Weiterleitungsnachricht versehen und von Suchanfragen ausgeschlossen moderation: title: Moderation notification_mailer: @@ -816,10 +941,6 @@ de: reply: proceed: Fortfahren zum Antworten prompt: 'Du möchtest auf diesen Beitrag antworten:' - remote_unfollow: - error: Fehler - title: Titel - unfollowed: Entfolgt scheduled_statuses: over_daily_limit: Du hast das Limit für geplante Beiträge, dass %{limit} beträgt, für heute erreicht over_total_limit: Du hast das Limit für geplante Beiträge, dass %{limit} beträgt, erreicht @@ -868,6 +989,7 @@ de: settings: account: Konto account_settings: Konto & Sicherheit + aliases: Kontoaliase appearance: Aussehen authorized_apps: Autorisierte Anwendungen back: Zurück zu Mastodon @@ -883,8 +1005,10 @@ de: notifications: Benachrichtigungen preferences: Einstellungen profile: Profil - relationships: Folger_innen und Gefolgte + relationships: Folgende und Gefolgte two_factor_authentication: Zwei-Faktor-Auth + spam_check: + spam_detected: Dies ist ein automatisierter Bericht. Es wurde Spam erkannt. statuses: attached: description: 'Angehängt: %{attached}' @@ -908,6 +1032,9 @@ de: private: Du kannst nur öffentliche Beiträge anheften reblog: Du kannst keine geteilten Beiträge anheften poll: + total_people: + one: "%{count} Person" + other: "%{count} Personen" total_votes: one: "%{count} Stimme" other: "%{count} Stimmen" @@ -926,15 +1053,17 @@ de: pinned: Angehefteter Beitrag reblogged: teilte sensitive_content: Heikle Inhalte + tags: + does_not_match_previous_name: entspricht nicht dem vorherigen Namen terms: body_html: | <h2>Datenschutzerklärung</h2> <h3 id="collect">Welche Informationen sammeln wir?</h3> <ul> - <li><em>Grundlegende Kontoinformationen</em>: Wenn du dich auf diesem Server registrierst, wirst du darum gebeten, einen Benutzer:innen-Namen, eine E-Mail-Adresse und ein Passwort einzugeben. Du kannst auch zusätzliche Profilinformationen wie etwa einen Anzeigenamen oder eine Biografie eingeben und ein Profilbild oder ein Headerbild hochladen. Der Benutzer:innen-Name, der Anzeigename, die Biografie, das Profilbild und das Headerbild werden immer öffentlich angezeigt.</li> + <li><em>Grundlegende Kontoinformationen</em>: Wenn du dich auf diesem Server registrierst, wirst du darum gebeten, einen Benutzernamen, eine E-Mail-Adresse und ein Passwort einzugeben. Du kannst auch zusätzliche Profilinformationen wie etwa einen Anzeigenamen oder eine Biografie eingeben und ein Profilbild oder ein Headerbild hochladen. Der Benutzername, der Anzeigename, die Biografie, das Profilbild und das Headerbild werden immer öffentlich angezeigt.</li> <li><em>Beiträge, Folge- und andere öffentliche Informationen</em>: Die Liste der Leute, denen du folgst, wird öffentlich gezeigt, das gleiche gilt für deine Folgenden (Follower). Sobald du eine Nachricht übermittelst, wird das Datum und die Uhrzeit gemeinsam mit der Information, welche Anwendung du dafür verwendet hast, gespeichert. Nachricht können Medienanhänge enthalten, etwa Bilder und Videos. Öffentliche und ungelistete Beiträge sind öffentlich verfügbar. Sobald du einen Beitrag auf deinem Profil anpinnst, sind dies auch öffentlich verfügbare Informationen. Deine Beiträge werden an deine Folgenden ausgeliefert, was in manchen Fällen bedeutet, dass sie an andere Server ausgeliefert werden und dort Kopien gespeichert werden. Sobald du Beiträge löschst, wird dies ebenso an deine Follower ausgeliefert. Die Handlungen des Teilens und Favorisieren eines anderen Beitrages ist immer öffentlich.</li> - <li><em>Direkte und "Nur Folgende"-Beiträge</em>: Alle Beiträge werden auf dem Server gespeichert und verarbeitet. "Nur Folgende"-Beiträge werden an deine Folgenden und an Benutzer:innen, die du in ihnen erwähnst, ausgeliefert, direkte Beiträge nur an in ihnen erwähnte Benutzer:innen. In manchen Fällen bedeutet dass, dass sie an andere Server ausgeliefert werden und dort Kopien gespeichert werden. Wir bemühen uns nach bestem Wissen und Gewissen, den Zugriff auf diese Beiträge auf nur autorisierte Personen einzuschränken, jedoch könnten andere Server dabei scheitern. Deswegen ist es wichtig, die Server, zu denen deine Folgenden gehören, zu überprüfen. Du kannst eine Option in den Einstellungen umschalten, um neue Folgenden manuell anzunehmen oder abzuweisen. <em>Bitte beachte, dass die Betreiber des Server und jedes empfangenden Servers solche Nachrichten anschauen könnten</em> und dass Empfänger von diesen eine Bildschirmkopie erstellen könnten, sie kopieren oder anderweitig weiterverteilen könnten. <em>Teile nicht irgendwelche gefährlichen Informationen über Mastodon.</em></li> + <li><em>Direkte und "Nur Folgende"-Beiträge</em>: Alle Beiträge werden auf dem Server gespeichert und verarbeitet. "Nur Folgende"-Beiträge werden an deine Folgenden und an Benutzer, die du in ihnen erwähnst, ausgeliefert, direkte Beiträge nur an in ihnen erwähnte Benutzer. In manchen Fällen bedeutet dass, dass sie an andere Server ausgeliefert werden und dort Kopien gespeichert werden. Wir bemühen uns nach bestem Wissen und Gewissen, den Zugriff auf diese Beiträge auf nur autorisierte Personen einzuschränken, jedoch könnten andere Server dabei scheitern. Deswegen ist es wichtig, die Server, zu denen deine Folgenden gehören, zu überprüfen. Du kannst eine Option in den Einstellungen umschalten, um neue Folgenden manuell anzunehmen oder abzuweisen. <em>Bitte beachte, dass die Betreiber des Server und jedes empfangenden Servers solche Nachrichten anschauen könnten</em> und dass Empfänger von diesen eine Bildschirmkopie erstellen könnten, sie kopieren oder anderweitig weiterverteilen könnten. <em>Teile nicht irgendwelche gefährlichen Informationen über Mastodon.</em></li> <li><em>Internet Protocol-Adressen (IP-Adressen) und andere Metadaten</em>: Sobald du dich anmeldest, erfassen wir sowohl die IP-Adresse, von der aus du dich anmeldest, als auch den Namen deine Browseranwendung. Alle angemeldeten Sitzungen (Sessions) sind für deine Überprüfung und Widerruf in den Einstellungen verfügbar. Die letzte verwendete IP-Adresse wird bis zu 12 Monate lang gespeichert. Wir könnten auch Serverprotokoll behalten, welche die IP-Adresse von jeder Anfrage an unseren Server enthalten.</li> </ul> @@ -964,7 +1093,7 @@ de: <ul> <li>Serverprotokolle, die IP-Adressen von allen deinen Anfragen an diesen Server, falls solche Protokolle behalten werden, für nicht mehr als 90 Tage behalten.</li> - <li>registrierten Benutzer:innen zugeordnete IP-Adressen nicht länger als 12 Monate behalten.</li> + <li>registrierten Benutzer zugeordnete IP-Adressen nicht länger als 12 Monate behalten.</li> </ul> <p>Du kannst ein Archiv deines Inhalts anfordern und herunterladen, inkludierend deiner Beiträge, Medienanhänge, Profilbilder und Headerbilder.</p> @@ -1045,7 +1174,9 @@ de: disable: Solange dein Konto eingefroren ist, sind deine Benutzerdaten intakt; aber du kannst nichts tun, bis dein Konto entsperrt wurde. silence: Solange dein Konto limitiert ist, können nur die Leute, die dir bereits folgen, deine Beiträge auf dem Server sehen und es könnte sein, dass du von verschiedenen öffentlichen Listungen ausgeschlossen wirst. Andererseits können andere dir manuell folgen. suspend: Dein Konto wurde gesperrt und alle deine Beiträge und hochgeladenen Medien wurden unwiderruflich vom Server und anderen Servern, bei denen du Folgende hattest, gelöscht. + get_in_touch: Du kannst auf diese E-Mail antworten, um mit dem Personal von %{instance} in Kontakt zu treten. review_server_policies: Serverrichtlinien ansehen + statuses: 'Besonders für:' subject: disable: Dein Konto %{acct} wurde eingefroren none: Warnung für %{acct} diff --git a/config/locales/devise.ar.yml b/config/locales/devise.ar.yml index 366bd81b94b38a5190bdaa32a7202138c02bf65a..4662cd030b7ad8659cedd95de15ba642b1eb748a 100644 --- a/config/locales/devise.ar.yml +++ b/config/locales/devise.ar.yml @@ -45,6 +45,12 @@ ar: extra: إن لم تكن ØµØ§ØØ¨ هذا الطلب ØŒ ÙŠÙØ±Ø¬Ù‰ عدم إعارة الاهتمام لهذه الرسالة. ÙكلÙمَتÙÙƒ السرية تبقى هي Ù…ÙÙ† غير أي تعديل إلّا Ùˆ Ùقط إن قمت بالنقر على الرابط أعلاه قصد إنشاء كلمة سرية جديدة. subject: 'ماستدون: تعليمات استعادة كلمة المرور' title: إعادة تعيين كلمة السر + two_factor_disabled: + subject: 'ماستدون: نظام المصادقة بخطوتين Ù…ÙØ¹Ø·Ù‘Ù„' + title: إنّ 2FA معطّل + two_factor_enabled: + subject: 'ماستدون: تم ØªÙØ¹ÙŠÙ„ نظام المصادقة بخطوتين' + title: إنّ 2FA Ù†Ø´ÙØ· unlock_instructions: subject: 'ماستدون: تعليمات ÙÙƒ القÙÙ„' omniauth_callbacks: diff --git a/config/locales/devise.bn.yml b/config/locales/devise.bn.yml index 152c698290639f4688cdb5184962daafc705fc52..cb7179da6fccabd52906838ac6754646a5a0b665 100644 --- a/config/locales/devise.bn.yml +++ b/config/locales/devise.bn.yml @@ -1 +1,40 @@ +--- bn: + devise: + confirmations: + confirmed: আপনার ইমেইলটি সঠিকà¦à¦¾à¦¬à§‡ নিশà§à¦šà¦¿à¦¤ করা হয়েছে। + send_instructions: আপনি à¦à¦•টি ইমেইল পাবেন যেটাতে কিà¦à¦¾à¦¬à§‡ আপনার ইমেইলটি নিশà§à¦šà¦¿à¦¤ করতে হবে সেটা পাঠানো হবে। যদি না পান, অনà§à¦—à§à¦°à¦¹ করে আপনার সà§à¦ªà§à¦¯à¦¾à¦® ফোলà§à¦¡à¦¾à¦°à¦Ÿà¦¿ চেক করবেন। + send_paranoid_instructions: আমাদের ডাটাবেসে যদি আপনার ইমেইল থেকে থাকে, আপনার কাছে à¦à¦•টা ইমেইল পাঠানো হবে যেখানে কিà¦à¦¾à¦¬à§‡ আপনার ইমেইল নিশà§à¦šà¦¿à¦¤ করতে হবে লেখা থাকবে। যদি না পান, অনà§à¦—à§à¦°à¦¹ করে আপনার সà§à¦ªà§à¦¯à¦¾à¦® ফোলà§à¦¡à¦¾à¦°à¦Ÿà¦¿ চেক করবেন। + failure: + already_authenticated: আপনি ইতিপূরà§à¦¬à§‡ à¦à§‡à¦¤à¦°à§‡ ঢà§à¦•েছেন (আবার লাগবে না)। + inactive: আনার নিবনà§à¦§à¦¨à¦Ÿà¦¿ à¦à¦–নো চালৠকরা হয়নি। + invalid: à¦à§à¦² %{authentication_keys} বা পাসওয়ারà§à¦¡ । + last_attempt: আপনার আর à¦à¦•বার চেষà§à¦Ÿà¦¾ করার সà§à¦¯à§‹à¦• আছে, তারপর আপনার নিবনà§à¦§à¦¨à§‡ ঢোকার কà§à¦·à§‡à¦¤à§à¦°à§‡ তালা দেওয়া হবে। + locked: নিবনà§à¦§à¦¨à§‡ ঢোকার কà§à¦·à§‡à¦¤à§à¦°à§‡ তালা দেওয়া হয়েছে। + not_found_in_database: à¦à§à¦² %{authentication_keys} বা পাসওয়ারà§à¦¡à¥¤ + pending: আপনার নিবনà§à¦§à¦¨à¦Ÿà¦¿ à¦à¦–নো পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾à¦° জনà§à¦¯ অপেকà§à¦·à¦¾à§Ÿ আছে। + timeout: আপনার সেশনটির সময় শেষ হয়ে গেছে। অনà§à¦—à§à¦°à¦¹ করে আবার নিবনà§à¦§à¦¨à§‡ ঢà§à¦•ে চালাতে থাকেন। + unauthenticated: à¦à¦Ÿà¦¾ বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে আপনার আগে আপনার নিবনà§à¦§à¦¨à§‡ ঢà§à¦•তে হবে অথবা নিবনà§à¦§à¦¨ তৈরি করতে হবে। + unconfirmed: à¦à¦Ÿà¦¾ বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে আপনার আগে আপনার ইমেইলটি নিশà§à¦šà¦¿à¦¤ করতে হবে। + mailer: + confirmation_instructions: + action: ইমেইলটি নিশà§à¦šà¦¿à¦¤ করà§à¦¨ + action_with_app: নিশà§à¦šà¦¿à¦¤ করà§à¦¨ à¦à¦¬à¦‚ %{app} তে ফিরে যান + explanation: "%{host} তে à¦à¦‡ ইমেইল বà§à¦¯à¦¬à¦¹à¦¾à¦° করে নিবনà§à¦§à¦¨ করতে হবে। আর à¦à¦•টা কà§à¦²à¦¿à¦• করলেই à¦à¦Ÿà¦¾ চালৠহয়ে যাবে। যদি আপনি à¦à¦Ÿà¦¾ না পাঠিয়ে থাকেন, তাহলে অনà§à¦—à§à¦°à¦¹ করে à¦à¦‡ ইমেইলটি উপেকà§à¦·à¦¾ করà§à¦¨à¥¤" + password_change: + extra: আপনি নিজে যদি পাসওয়ারà§à¦¡à¦Ÿà¦¿ না বদলে থাকেন, খà§à¦¬ সমà§à¦à¦¬ অনà§à¦¯à¦•েও আপনার নিবনà§à¦§à¦¨à§‡ পà§à¦°à¦¬à§‡à¦¶ করে à¦à¦Ÿà¦¾ করেছে। অনà§à¦—à§à¦°à¦¹ করে যত দà§à¦°à§à¦¤ সমà§à¦à¦¬ আপনার পাসওয়ারà§à¦¡à¦Ÿà¦¿ বদলান অথবা যদি আপনি আপনার নিবনà§à¦§à¦¨à§‡ আর না ঢà§à¦•তে পারেন, à¦à¦‡ সারà§à¦à¦¾à¦°à§‡à¦° পরিচালককে জানান। + subject: 'মাসà§à¦Ÿà¦¾à¦¡à¦¨: পাসওয়ারà§à¦¡ বদলানো হয়েছে' + title: পাসওয়ারà§à¦¡ বদলানো হয়েছে + reconfirmation_instructions: + explanation: নতà§à¦¨ ইমেইলটি নিশà§à¦šà¦¿à¦¤ করà§à¦¨à¥¤ + extra: আপনি যদি à¦à¦Ÿà¦¾ না চেয়ে থাকেন, à¦à¦‡ ইমেইলটি উপেকà§à¦·à¦¾ করà§à¦¨à¥¤ উপরের লিংকটিতে না গেলে আপনার নিবনà§à¦§à¦¨à§‡à¦° সাথে যà§à¦•à§à¦¤ ইমেইল বদলাবে না। + subject: 'মাসà§à¦Ÿà¦¾à¦¡à¦¨: ইমেইল নিশà§à¦šà¦¿à¦¤ করà§à¦¨ %{instance} জনà§à¦¯' + title: আপনার ইমেইলটি নিশà§à¦šà¦¿à¦¤ করà§à¦¨ + reset_password_instructions: + action: পাসওয়ারà§à¦¡ বদলান + explanation: আপনি আপনার নিবনà§à¦§à¦¨à§‡à¦° জনà§à¦¯ নতà§à¦¨ পাসওয়ারà§à¦¡ চেয়েছেন। + extra: আপনি যদি à¦à¦Ÿà¦¾ না চেয়ে থাকেন, à¦à¦‡ ইমেইলটি উপেকà§à¦·à¦¾ করà§à¦¨à¥¤ উপরের লিংকটিতে না গেলে আপনার পাসওয়ারà§à¦¡ বদলাবে না। + subject: 'মাসà§à¦Ÿà¦¾à¦¡à¦¨: পাসওয়ারà§à¦¡ বদলানোর নিরà§à¦¦à§‡à¦¶à¦¨à¦¾' + title: পাসওয়ারà§à¦¡ বদলানো + registrations: + signed_up: সà§à¦¬à¦¾à¦—তম! আপনার নিবনà§à¦§à¦¨à¦Ÿà¦¿ সঠিকà¦à¦¾à¦¬à§‡ হয়েছে। diff --git a/config/locales/devise.br.yml b/config/locales/devise.br.yml new file mode 100644 index 0000000000000000000000000000000000000000..3fe043754fbe9f79248bace8f921a47c815fdc10 --- /dev/null +++ b/config/locales/devise.br.yml @@ -0,0 +1,33 @@ +--- +br: + devise: + failure: + inactive: N'eo ket gweredekaet ho kont c'hoazh. + invalid: "%{authentication_keys} pe ger-tremen diwiriek." + last_attempt: Un esae a chom deoc'h a-raok ma vefe prennet ho kont. + locked: Prennet eo ho kont. + not_found_in_database: "%{authentication_keys} pe ger-tremen diwiriek." + pending: O vezañ asantet eo ho kont. + mailer: + confirmation_instructions: + action: Gwiriekaat ar chomlec'h postel + action_with_app: Kadarnaat ha distroiñ da %{app} + title: Gwiriekaat ar chomlec'h postel + email_changed: + subject: 'Mastodoñ : Postel kemmet' + title: Chomlec'h postel nevez + password_change: + explanation: Kemmet eo bet ger-tremen ho kont. + subject: 'Mastodoñ : Ger-tremen kemmet' + title: Ger-tremen kemmet + reconfirmation_instructions: + explanation: Kadarnait ar chomlec'h nevez evit cheñch ho postel. + subject: 'Mastodoñ : Kadarnait ar postel evit %{instance}' + title: Gwiriekaat ar chomlec'h postel + reset_password_instructions: + action: Cheñch ar ger-tremen + explanation: Goulennet ho peus ur ger-tremen nevez evit ho kont. + passwords: + updated_not_active: Kemmet eo bet ho ker-tremen ent reizh. + registrations: + signed_up: Donemat ! Kevreet oc'h. diff --git a/config/locales/devise.ca.yml b/config/locales/devise.ca.yml index 7f2df1f995f83c20faa2eb4060a15c294794c81a..eb176c7bde4a59c324ba0e6ce863b5434113c73f 100644 --- a/config/locales/devise.ca.yml +++ b/config/locales/devise.ca.yml @@ -48,6 +48,18 @@ ca: extra: Si no ho has sol·licitat, ignora aquest correu electrònic. La teva contrasenya no canviarà fins que accedeixis a l'enllaç de dalt i creis un de nou. subject: 'Mastodon: Instruccions per a reiniciar contrassenya' title: Contrasenya restablerta + two_factor_disabled: + explanation: L´autenticació de dos factors pel teu compte ha estat desactivat. L'inici de sessió és ara possible utilitzant només l'adreça de correu electrònic i la contrasenya. + subject: 'Mastodon: autenticació de dos factors desactivada' + title: 2FA desactivat + two_factor_enabled: + explanation: L'autenticació de dos factors ha estat habilitada pel teu compte. Un token generat pel emparellat TOTP app serà requerit per a iniciar sessió. + subject: 'Mastodon: autenticació de dos factors activada' + title: 2FA activat + two_factor_recovery_codes_changed: + explanation: Els codis de recuperació anteriors han estat invalidats i s'han generat uns de nous. + subject: 'Mastodon: codis de recuperació de Dos factors regenerats' + title: 2FA codis de recuperació canviats unlock_instructions: subject: 'Mastodon: Instruccions per a desblocar' omniauth_callbacks: diff --git a/config/locales/devise.co.yml b/config/locales/devise.co.yml index 16481737f1e8e478a615d131a46ab4ae9b8ab4ee..c9511d14d14f245490072202ce8d84b63d0626c8 100644 --- a/config/locales/devise.co.yml +++ b/config/locales/devise.co.yml @@ -46,6 +46,18 @@ co: extra: S’ellu ùn era micca voi, ignurate stu missaghju. A chjave d’accessu ùn cambiarà micca s’è voi ùn cliccate micca u ligame. subject: 'Mastodon: Cambià a chjave d’accessu' title: Cambià a chjave + two_factor_disabled: + explanation: L'autentificazione à dui fattori per u vostru contu hè stata disattivata. A cunnessione hè avà pussibule cù l'usu solu di u vostru e-mail è di a chjave d'accessu. + subject: 'Mastodon: Autentificazione à dui fattori disattivata' + title: A2F disattivata + two_factor_enabled: + explanation: L'autentificazione à dui fattori hè stata attivata per u vostru contu. Un codice, o fiscia, generata da l'applicazione TOTP assuciata sarà dumandata per cunnettavvi. + subject: 'Mastodon: Autentificazione à dui fattori attivata' + title: A2F attivata + two_factor_recovery_codes_changed: + explanation: I codici di ricuperazione pricidenti ùn sò più validi è un novu inseme di codici hè statu creatu. + subject: 'Mastodon: Rigenerazione di i codici à dui fattori di ricuperazione' + title: Cambiamentu di i codici di ricuperazione d'A2F unlock_instructions: subject: 'Mastodon: Riapre u contu' omniauth_callbacks: diff --git a/config/locales/devise.cs.yml b/config/locales/devise.cs.yml index 94c41ed986e8f6db36a43eddf23599397408f6df..25be731d03b72e67bc372188dfe32992821ad21c 100644 --- a/config/locales/devise.cs.yml +++ b/config/locales/devise.cs.yml @@ -8,7 +8,7 @@ cs: failure: already_authenticated: Již jste pÅ™ihlášen/a. inactive: Váš úÄet jeÅ¡tÄ› nenà aktivován. - invalid: Neplatný %{authentication_keys} nebo heslo. + invalid: Neplatné %{authentication_keys} nebo heslo. last_attempt: Máte jeÅ¡tÄ› jeden pokus, než bude váš úÄet uzamÄen. locked: Váš úÄet je uzamÄen. not_found_in_database: Neplatné %{authentication_keys} nebo heslo. @@ -21,7 +21,7 @@ cs: action: Potvrdit e-mailovou adresu action_with_app: Potvrdit a navrátit se do %{app} explanation: S touto e-mailovou adresou jste si vytvoÅ™il/a úÄet na %{host}. K jeho aktivaci vám zbývá jedno kliknutÃ. Pokud jste to nebyl/a vy, prosÃm ignorujte tento e-mail. - explanation_when_pending: S touto e-mailovou adresou jste si vyžádal/a pozvánku na %{host}. Jakmile svou e-mailovou adresu potvrdÃte, posoudÃme váš poadavek. Do té doby se nemůžete pÅ™ihlásit. Pokud bude váš požadavek zamÃtnut, budou vaÅ¡e data odstranÄ›na, takže od vás nebude vyžadována žádná dalšà akce. Pokud jste to nebyl/a vy, prosÃm ignorujte tento e-mail. + explanation_when_pending: S touto e-mailovou adresou jste si vyžádal/a pozvánku na %{host}. Jakmile svou e-mailovou adresu potvrdÃte, posoudÃme váš poadavek. Můžete se pÅ™ihlásit, zmÄ›nit si své detaily Äi smazat svůj úÄet, ale do schválenà úÄtu nemáte pÅ™Ãstup ke vÄ›tÅ¡inÄ› funkcÃ. Pokud bude váš požadavek zamÃtnut, budou vaÅ¡e data odstranÄ›na, takže od vás nebude vyžadována žádná dalšà akce. Pokud jste to nebyl/a vy, prosÃm ignorujte tento e-mail. extra_html: ProsÃm podÃvejte se také na <a href="%{terms_path}">pravidla tohoto serveru</a> a <a href="%{policy_path}">naÅ¡e podmÃnky použÃvánÃ</a>. subject: 'Mastodon: Potvrzovacà instrukce pro %{instance}' title: PotvrÄte e-mailovou adresu @@ -46,6 +46,18 @@ cs: extra: Pokud jste tohle nevyžádal/a, prosÃm ignorujte tento e-mail. VaÅ¡e heslo nebude zmÄ›nÄ›no, dokud nepÅ™ejdete na výše uvedenou adresu a nevytvoÅ™Ãte si nové. subject: 'Mastodon: Instrukce pro obnovenà hesla' title: Obnovenà hesla + two_factor_disabled: + explanation: Dvoufázové ověřovánà bylo pr váš úÄet zakázáno. PÅ™ihlaÅ¡ovánà je nynà možné pouze pomocà e-mailové adresy a hesla. + subject: 'Mastodon: Dvoufázové ověřovánà zakázáno' + title: 2FA zakázáno + two_factor_enabled: + explanation: Dvoufázové ověřovánà bylo pr váš úÄet povoleno. Pro pÅ™ihlášenà bude vyžadován token vygenerovaný spárovanou TOTP aplikacÃ. + subject: 'Mastodon: Dvoufázové ověřovánà povoleno' + title: 2FA povoleno + two_factor_recovery_codes_changed: + explanation: PÅ™edchozà záložnà kódy byly zruÅ¡eny a byly vygenerovány nové. + subject: 'Mastodon: Dvoufázové záložnà kódy znovu vygenerovány' + title: Záložnà kódy pro 2FA zmÄ›nÄ›ny unlock_instructions: subject: 'Mastodon: Instrukce pro odemÄenà úÄtu' omniauth_callbacks: diff --git a/config/locales/devise.cy.yml b/config/locales/devise.cy.yml index 727c71464494442060156c825d2eae40719d77b0..e5366f8cdf09314f19630cd6481ff5e1cb7a0cab 100644 --- a/config/locales/devise.cy.yml +++ b/config/locales/devise.cy.yml @@ -46,6 +46,18 @@ cy: extra: Os na wnaethoch gais am hyn, anwybyddwch yr e-bost hwn os gwelwch yn dda. Ni fydd eich cyfrinair yn newid nes i chi fynd at y ddolen uchod a chreu un newydd. subject: 'Mastodon: Ailosod cyfarwyddiadau cyfrinair' title: Ailosod cyfrinair + two_factor_disabled: + explanation: Mae dilysu dau ffactor ar gyfer eich cyfrif wedi'i anablu. Mae mewngofnodi bellach yn bosibl gan ddefnyddio cyfeiriad e-bost a chyfrinair yn unig. + subject: 'Mastodon: Dilysu dau ffactor yn anabl' + title: Dilysu dau ffactor yn anabl + two_factor_enabled: + explanation: Mae dilysu dau ffactor wedi'i alluogi ar gyfer eich cyfrif. Bydd angen tocyn a gynhyrchir gan yr ap TOTP pâr i fewngofnodi. + subject: 'Mastodon: mae dilysu dau ffactor wedi''i alluogi' + title: Mae dilysu dau ffactor wedi'i alluogi + two_factor_recovery_codes_changed: + explanation: Mae'r codau adfer blaenorol wedi'u hannilysu a chynhyrchwyd rhai newydd. + subject: 'Mastodon: Mae codau adfer dau ffactor wedi''u hadfywio' + title: Newidiodd codau adfer 2FA unlock_instructions: subject: 'Mastodon: Cyfarwyddiadau datgloi' omniauth_callbacks: diff --git a/config/locales/devise.da.yml b/config/locales/devise.da.yml index 4816d6456b7f63c112e24237a4067487fce4e324..1b05d77c1d6e847c375f8558b3c9e0102eab9ac2 100644 --- a/config/locales/devise.da.yml +++ b/config/locales/devise.da.yml @@ -2,7 +2,7 @@ da: devise: confirmations: - confirmed: Din email adresse er blevet succesfuldt bekræftet. + confirmed: Din e-mail er nu bekræftet. send_instructions: Du vil modtage en mail med instrukser for hvordan du bekræfter din email adresse om fÃ¥ minutter. Tjek venligst din spam mappe hvis du ikke har modtaget denne email. send_paranoid_instructions: Hvis din email adresse allerede findes i vores database, vil du modtage en email med instrukser for hvordan du bekræfter din email adresse om fÃ¥ minutter. Tjek gerne din spam mappe hvis du ikke modtager denne email. failure: @@ -12,6 +12,7 @@ da: last_attempt: Du har et forsøg tilbage før din konto lÃ¥ses. locked: Din konto er lÃ¥st. not_found_in_database: Ugyldig %{authentication_keys} eller ugyldigt kodeord. + pending: Din konto er stadig under bedømmelse. timeout: Din session er udløbet. Log venligst ind igen for at fortsætte. unauthenticated: Du er nødt til at logge ind eller oprette dig for at fortsætte. unconfirmed: Du er nødt til at bekræfte din email adresse for at fortsætte. @@ -20,6 +21,7 @@ da: action: Bekræft email adresse action_with_app: Bekræft og vend tilbage til %{app} explanation: Du har oprettet en konto pÃ¥ %{host} med denne email adresse. Du er et klik fra at aktivere din konto. Hvis du ikke har oprettet dig, ignorer venligst denne email. + explanation_when_pending: Du har ansøgt om en invitation til %{host} med denne mailadresse. NÃ¥r du bekræfter din mailadresse vil vi bedømme din ansøgning. Indtil da kan du ikke logge ind. Din data slettes hvis din ansøgning bliver afvist sÃ¥ du behøver ikke foretage yderligere handlinger. Hvis dette ikke var dig kan du ignorere denne mail. extra_html: Tjek ogsÃ¥ <a href="%{terms_path}">reglerne for serveren</a> og <a href="%{policy_path}">vores betingelser</a>. subject: 'Mastodon: Bekræftelses instrukser for %{instance}' title: Bekræft email adresse @@ -60,6 +62,7 @@ da: signed_up: Velkommen! Du har nu tilmeldt dig. signed_up_but_inactive: Du har nu oprettet dig. Vi kunne dog ikke logge dig ind da din konto endnu ikke er aktiveret. signed_up_but_locked: Du har nu oprettet dig. Vi kunne dog ikke logge dig ind da din konto er lÃ¥st. + signed_up_but_pending: En besked med et bekræftelseslink er blevet sendt til din mailadresse. Vi vil behandle din ansøgning nÃ¥r du har trykket pÃ¥ dette link. Du fÃ¥r en meddelelse hvis din ansøgning bliver accepteret. signed_up_but_unconfirmed: En besked med et bekræftelses link er nu blevet sendt til din email adresse. Følg linket for at aktivere din konti. Tjek din spam mappe hvis du ikke har modtaget denne email. update_needs_confirmation: Du har succesfuldt opdateret din konto, men vi er nødt til at bekræfte din email adresse. Tjek venligst din email og følg bekræftelses linket for at bekræfte din nye email adresse. Tjek venligst din spam mappe hvis du ikke har modtaget denne email. updated: Din konto er nu blevet opdateret. diff --git a/config/locales/devise.de.yml b/config/locales/devise.de.yml index bd573dc3e9a50ea756d89036f2bb793451283cef..372090515f2152a75083fc762cd9326cf65d2351 100644 --- a/config/locales/devise.de.yml +++ b/config/locales/devise.de.yml @@ -46,6 +46,18 @@ de: extra: Wenn du diese Anfrage nicht gestellt hast, solltest du diese E-Mail ignorieren. Dein Passwort wird sich nicht ändern solange du den obigen Link anklickst und ein neues erstellst. subject: 'Mastodon: Passwort zurücksetzen' title: Passwort zurücksetzen + two_factor_disabled: + explanation: Zwei-Faktor-Authentifizierung für dein Konto wurde deaktiviert. Login ist jetzt nur mit E-Mail-Adresse und Passwort möglich. + subject: 'Mastodon: Zweiâ€Faktorâ€Authentifizierung deaktiviert' + title: 2FA deaktiviert + two_factor_enabled: + explanation: Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Ein Token, der von der gepaarten TOTP-App generiert wird, wird für den Login benötigt. + subject: 'Mastodon: Zweiâ€Faktorâ€Authentifizierung aktiviert' + title: 2FA aktiviert + two_factor_recovery_codes_changed: + explanation: Die vorherigen Wiederherstellungscodes wurden ungültig gemacht und es wurden neue generiert. + subject: 'Mastodon: Zwei-Faktor-Wiederherstellungscodes neu generiert' + title: 2FA Wiederherstellungscodes geändert unlock_instructions: subject: 'Mastodon: Konto entsperren' omniauth_callbacks: diff --git a/config/locales/devise.el.yml b/config/locales/devise.el.yml index 0b5c686368f7a17b7050a95a0e0cbd70ae3fefff..5dc7658c20f516d686cf03d0ff55acb08d0733b0 100644 --- a/config/locales/devise.el.yml +++ b/config/locales/devise.el.yml @@ -46,6 +46,18 @@ el: extra: Αν δεν ζήτησες ÎµÏƒÏ Î±Ï…Ï„Î® την αλλαγή, παÏακαλοÏμε αγνόησε αυτό το email. Το συνθηματικό σου δεν θα αλλάξει μÎχÏι να επισκεφτείς τον παÏαπάνω σÏνδεσμο και να δημιουÏγήσεις Îνα καινοÏÏιο. subject: 'Mastodon: Οδηγίες επαναφοÏάς συνθηματικοÏ' title: ΕπαναφοÏά ÏƒÏ…Î½Î¸Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï + two_factor_disabled: + explanation: Ο Îλεγχος ταυτότητας δυο παÏαγόντων (2FA) Îχει απενεÏγοποιηθεί για το λογαÏιασμό σου. Η σÏνδεση γίνεται απλά με το email και το συνθηματικό. + subject: 'Mastodon: ΑπενεÏγοποιήθηκε ο Îλεγχος ταυτότητας δÏο παÏαγόντων' + title: ΑπενεÏγοποιημÎνο 2FA + two_factor_enabled: + explanation: Έχει ενεÏγοποιηθεί η πιστοποίηση 2 παÏαγόντων για το λογαÏιασμό σου (2FA). Για να συνδεθείς θα απαιτηθεί πιστοποιημÎνο τεκμήÏιο από κάποια ζευγαÏωμÎνη εφαÏμογή. + subject: 'Mastodon: ΕνεÏγοποιήθηκε η πιστοποίηση 2 παÏαγόντων (2FA)' + title: Επαλήθευση δÏο βημάτων ενεÏγή + two_factor_recovery_codes_changed: + explanation: Οι Ï€ÏοηγοÏμενοι κωδικοί ανάκτησης ακυÏώθηκαν και δημιουÏγήθηκαν νÎοι. + subject: 'Mastodon: ΔημιουÏγήθηκαν νÎοι κωδικοί ανάκτησης' + title: Οι κωδικοί ανάκτησης 2FA άλλαξαν unlock_instructions: subject: 'Mastodon: Οδηγίες ξεκλειδώματος' omniauth_callbacks: diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 2930733c00cd1d076608e6cadef5c9c976d1b5bf..726d2426a77804095201b1ed845083c8d2d1ccec 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -21,7 +21,7 @@ en: action: Verify email address action_with_app: Confirm and return to %{app} explanation: You have created an account on %{host} with this email address. You are one click away from activating it. If this wasn't you, please ignore this email. - explanation_when_pending: You applied for an invite to %{host} with this email address. Once you confirm your e-mail address, we will review your application. You can't login until then. If your application is rejected, your data will be removed, so no further action will be required from you. If this wasn't you, please ignore this email. + explanation_when_pending: You applied for an invite to %{host} with this email address. Once you confirm your e-mail address, we will review your application. You can login to change your details or delete your account, but you cannot access most of the functions until your account is approved. If your application is rejected, your data will be removed, so no further action will be required from you. If this wasn't you, please ignore this email. extra_html: Please also check out <a href="%{terms_path}">the rules of the server</a> and <a href="%{policy_path}">our terms of service</a>. subject: 'Mastodon: Confirmation instructions for %{instance}' title: Verify email address @@ -46,6 +46,18 @@ en: extra: If you didn't request this, please ignore this email. Your password won't change until you access the link above and create a new one. subject: 'Mastodon: Reset password instructions' title: Password reset + two_factor_disabled: + explanation: Two-factor authentication for your account has been disabled. Login is now possible using only e-mail address and password. + subject: 'Mastodon: Two-factor authentication disabled' + title: 2FA disabled + two_factor_enabled: + explanation: Two-factor authentication has been enabled for your account. A token generated by the paired TOTP app will be required for login. + subject: 'Mastodon: Two-factor authentication enabled' + title: 2FA enabled + two_factor_recovery_codes_changed: + explanation: The previous recovery codes have been invalidated and new ones generated. + subject: 'Mastodon: Two-factor recovery codes re-generated' + title: 2FA recovery codes changed unlock_instructions: subject: 'Mastodon: Unlock instructions' omniauth_callbacks: diff --git a/config/locales/devise.eo.yml b/config/locales/devise.eo.yml index d7b7b2d6c67cbd1e2c29efb13ec8d3e01cbe3ad8..7c67ac8facc2ee3c2f2b04919970c65fd57b8c22 100644 --- a/config/locales/devise.eo.yml +++ b/config/locales/devise.eo.yml @@ -46,6 +46,10 @@ eo: extra: Se vi ne petis ĉi tion, bonvolu ignori ĉi tiun retmesaÄon. Via pasvorto ne ÅanÄiÄos se vi ne aliras la supran ligilon kaj kreas novan. subject: 'Mastodon: Instrukcioj por ÅanÄi pasvorton' title: Pasvorto restarigita + two_factor_disabled: + title: la du-etapa aÅtentigo estas malÅaltita + two_factor_enabled: + title: la du-etapa aÅtentigo estas Åaltita unlock_instructions: subject: 'Mastodon: Instrukcioj por malÅlosi' omniauth_callbacks: diff --git a/config/locales/devise.es-AR.yml b/config/locales/devise.es-AR.yml new file mode 100644 index 0000000000000000000000000000000000000000..bb229e8f5d10d39e5a3825c0f936b1e23ac3d01a --- /dev/null +++ b/config/locales/devise.es-AR.yml @@ -0,0 +1,98 @@ +--- +es-AR: + devise: + confirmations: + confirmed: Se confirmó exitosamente tu dirección de correo electrónico. + send_instructions: En unos minutos, vas a recibir un correo electrónico con instrucciones sobre cómo confirmar tu dirección de correo. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + send_paranoid_instructions: Si tu dirección de correo electrónico existe en nuestra base de datos, en unos minutos, vas a recibir un correo electrónico con instrucciones sobre cómo confirmar tu dirección de correo. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + failure: + already_authenticated: Ya iniciaste sesión. + inactive: Tu cuenta todavÃa no está activada. + invalid: "%{authentication_keys} o contraseña no válidas." + last_attempt: Tenés un intento más antes de que se bloquee tu cuenta. + locked: Se bloqueó tu cuenta. + not_found_in_database: "%{authentication_keys} o contraseña no válidas." + pending: Tu cuenta todavÃa está bajo revisión. + timeout: Venció tu sesión. Por favor, volvé a iniciar sesión para continuar. + unauthenticated: Necesitás iniciar sesión o registrarte antes de continuar. + unconfirmed: Tenés que confirmar tu dirección de correo electrónico antes de continuar. + mailer: + confirmation_instructions: + action: Verificar dirección de correo electrónico + action_with_app: Confirmar y volver a %{app} + explanation: Te creaste una cuenta en %{host} con esta dirección de correo electrónico. Estás a un clic de activarla. Si vos no te creaste ninguna cuenta acá, por favor, simplemente ignorá este mensaje. + explanation_when_pending: Pediste una invitación para %{host} con esta dirección de correo electrónico. Una vez que confirmés esta dirección de correo, revisaremos tu pedido. Podés iniciar sesión para cambiar tus detalles o eliminar tu cuenta, pero no vas a poder acceder a la mayorÃa de las funciones hasta que no se apruebe tu cuenta. Si tu pedido es rechazado, se eliminarán tus datos, por lo que no vas a necesitar hacer nada en especial. Si vos no pediste ninguna cuenta acá, por favor, simplemente ignorá este mensaje. + extra_html: Por favor, también leé <a href="%{terms_path}">las reglas del servidor</a> y <a href="%{policy_path}">nuestros términos del servicio</a>. + subject: 'Mastodon: instrucciones de confirmación para %{instance}' + title: Verificar dirección de correo electrónico + email_changed: + explanation: 'La dirección de correo electrónico de tu cuenta está siendo cambiada a:' + extra: Si no cambiaste tu correo electrónico, es probable que alguien más haya obtenido acceso a tu cuenta. Por favor, cambiá tu contraseña inmediatamente o contactá con el administrador del servidor si no podés ingresar a tu cuenta. + subject: 'Mastodon: correo electrónico cambiado' + title: Nueva dirección de correo electrónico + password_change: + explanation: Se cambió la contraseña de tu cuenta. + extra: Si no cambiaste tu contraseña, es probable que alguien más haya obtenido acceso a tu cuenta. Por favor, cambiá tu contraseña inmediatamente o contactá con el administrador del servidor si no podés ingresar a tu cuenta. + subject: 'Mastodon: contraseña cambiada' + title: Contraseña cambiada + reconfirmation_instructions: + explanation: Confirmá la nueva dirección para cambiar tu correo electrónico. + extra: Si no pediste este cambio, por favor, ignorá este mensaje. No se cambiará la dirección de correo electrónico de tu cuenta de Mastodon hasta que no accedas al enlace de arriba. + subject: 'Mastodon: confirmar correo electrónico para %{instance}' + title: Verifique dirección de correo electrónico + reset_password_instructions: + action: Cambiar contraseña + explanation: Pediste una nueva contraseña para tu cuenta. + extra: Si no pediste este cambio, por favor, ignorá este mensaje. No se cambiará la contraseña de tu cuenta de Mastodon hasta que no accedas al enlace de arriba y crees una nueva. + subject: 'Mastodon: instrucciones para cambiar la contraseña' + title: Cambiar contraseña + two_factor_disabled: + explanation: La autenticación de dos factores para tu cuenta está deshabilitada. Ahora el inicio de sesión sólo es posible usando la dirección de correo electrónico y la contraseña. + subject: 'Mastodon: autenticación de dos factores, deshabilitada' + title: 2FA deshabilitada + two_factor_enabled: + explanation: La autenticación de dos factores para tu cuenta está habilitada. Se requiere una clave generada por la aplicación TOTP asociada para iniciar sesión. + subject: 'Mastodon: autenticación de dos factores, habilitada' + title: 2FA habilitada + two_factor_recovery_codes_changed: + explanation: Los códigos anteriores de recuperación fueron invalidados y se generaron unos nuevos. + subject: 'Mastodon: códigos de recuperación de dos factores, regenerados' + title: Códigos de recuperación 2FA cambiados + unlock_instructions: + subject: 'Mastodon: instrucciones de desbloqueo' + omniauth_callbacks: + failure: 'No se te pudo autenticar desde %{kind} debido a esto: "%{reason}".' + success: Se autenticó exitosamente para la cuenta %{kind}. + passwords: + no_token: No podés acceder a esta página sin venir desde un correo electrónico destinado al cambio de contraseña. Si venÃs desde dicho mensaje, por favor, asegurate que usaste toda la dirección web ofrecida. + send_instructions: Si tu dirección de correo electrónico existe en nuestra base de datos, en unos minutos, vas a recibir un correo electrónico con un enlace para cambiar tu contraseña. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + send_paranoid_instructions: Si tu dirección de correo electrónico existe en nuestra base de datos, en unos minutos, vas a recibir un correo electrónico con un enlace para cambiar tu contraseña. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + updated: Se cambió existosamente tu contraseña. Ya iniciaste sesión. + updated_not_active: Se cambió exitosamente tu contraseña. + registrations: + destroyed: "¡Chauchas! Se canceló exitosamente tu cuenta. Esperamos verte pronto de nuevo." + signed_up: "¡Bienvenido! Te registraste exitosamente." + signed_up_but_inactive: Te registraste exitosamente. Sin embargo, no podés iniciar sesión porque tu cuenta todavÃa no está activada. + signed_up_but_locked: Te registraste exitosamente. Sin embargo, no podés iniciar sesión porque tu cuenta está bloqueada. + signed_up_but_pending: Se envió un correo electrónico a tu dirección de correo con un enlace de confirmación. Después que hagás clic en ese enlace, revisaremos tu pedido. Si sos aprobado, serás notificado. + signed_up_but_unconfirmed: Se envió un correo electrónico a tu dirección de correo con un enlace de confirmación. Por favor, seguà ese enlace para activar tu cuenta. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + update_needs_confirmation: Actualizaste tu cuenta exitosamente. Sin embargo, necesitamos verificar tu nueva dirección de correo electrónico. Por favor, revisá tu correo electrónico y seguà el enlace de confirmación. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + updated: Se actualizó exitosamente tu cuenta. + sessions: + already_signed_out: Cerraste sesión exitosamente. + signed_in: Iniciaste sesión exitosamente. + signed_out: Cerraste sesión exitosamente. + unlocks: + send_instructions: En unos minutos, vas a recibir un correo electrónico con instrucciones sobre cómo desbloquear tu cuenta. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + send_paranoid_instructions: Si tu cuenta existe en nuestra base de datos, en unos minutos vas a recibir un correo electrónico con instrucciones sobre cómo desbloquear tu cuenta. Si pasa el tiempo y no recibiste ningún mensaje, por favor, revisá tu carpeta de correo basura / no deseado / spam. + unlocked: Se desbloqueó tu cuenta exitosamente. Por favor, iniciá sesión para continuar. + errors: + messages: + already_confirmed: ya se confirmó; por favor, intentá iniciar sesión + confirmation_period_expired: necesita confirmarse dentro de %{period}; por favor, solicitá una nueva + expired: venció; por favor, solicitá una nueva + not_found: no se encontró + not_locked: no se bloqueó + not_saved: + one: '1 error prohibió que este %{resource} se guarde:' + other: "%{count} errores prohibieron que este %{resource} se guarde:" diff --git a/config/locales/devise.es.yml b/config/locales/devise.es.yml index 8210415f21ba4e9f9e9c94c4e857848b511d287b..80d4380923339e75cf59b583431585a61f02e8f8 100644 --- a/config/locales/devise.es.yml +++ b/config/locales/devise.es.yml @@ -46,6 +46,18 @@ es: extra: Si no solicitaste esto, por favor ignora este correo. Tu contraseña no cambiará hasta que tu accedas al vinculo arriba y crees una nueva. subject: 'Mastodon: Instrucciones para reiniciar contraseña' title: Reiniciar contraseña + two_factor_disabled: + explanation: La autenticación de dos factores para tu cuenta ha sido deshabilitada. Ahora puedes conectarte solamente usando la dirección de correo electrónico y la contraseña. + subject: 'Mastodon: La autenticación de dos factores está deshabilitada' + title: 2FA desactivada + two_factor_enabled: + explanation: La autenticación de dos factores para tu cuenta ha sido habilitada. Se requiere un token generado por la aplicación TOTP emparejada para ingresar. + subject: 'Mastodon: La autenticación de dos factores está habilitada' + title: 2FA activada + two_factor_recovery_codes_changed: + explanation: Los códigos de recuperación previos han sido invalidados y se generaron códigos nuevos. + subject: 'Mastodon: Los códigos de recuperación de dos factores fueron regenerados' + title: Códigos de recuperación 2FA cambiados unlock_instructions: subject: 'Mastodon: Instrucciones para desbloquear' omniauth_callbacks: diff --git a/config/locales/devise.et.yml b/config/locales/devise.et.yml new file mode 100644 index 0000000000000000000000000000000000000000..3c1b75f6cd75b80b978bc2f07f113dedee2ee4f6 --- /dev/null +++ b/config/locales/devise.et.yml @@ -0,0 +1,86 @@ +--- +et: + devise: + confirmations: + confirmed: Sinu e-postiaadress on edukalt kinnitatud. + send_instructions: Te saate paari minuti pärast e-kirja juhistega, kuidas oma e-posti aadressit kinnitada. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + send_paranoid_instructions: Kui Teie e-postiaadress eksisteerib meie andmebaasis, saate paari minuti pärast e-kirja juhistega, kuidas oma e-posti aadressit kinnitada. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + failure: + already_authenticated: Te olete juba sisse loginud. + inactive: Teie konto pole veel aktiveeritud. + invalid: Valed %{authentication_keys} või parool. + last_attempt: Teil on veel üks katse kuni teie konto on lukustatud. + locked: Sinu konto on lukustatud. + not_found_in_database: Valed %{authentication_keys} või parool. + pending: Teie konto on siiani läbivaatlusel. + timeout: Teie sessioon on aegunud. Jätkamiseks palun sisenege uuesti. + unauthenticated: Te peate sisenema või looma konto enne jätkamist. + unconfirmed: Te peate kinnitama oma e-postiaadressi enne jätkamist. + mailer: + confirmation_instructions: + action: Kinnita e-postiaadress + action_with_app: Kinnita ja naase %{app} + explanation: Te olete loonud konto %{host}'il selle e-postiaadressiga. Te olete ühe kliki kaugusel selle aktiveerimisest. Kui see polnud Teie, palun eirake seda kirja. + explanation_when_pending: Te esitasite soovi liituda %{host}-iga selle e-postiaadressiga. Siis, kui Te kinnitate oma e-posti aadressi, vaatame me Teie soovi üle. Te ei saa siseneda kuni selleni. Kui teie soov on tagasi lükatud, Teie andmed kustutatakse ning Te ei pea rohkem midagi tegema. Kui see ei olnud Teie, palun eirake seda kirja. + extra_html: Palun tutvu <a href="%{terms_path}">meie serveri reeglitega</a> ning <a href="%{policy_path}">meie kasutustingimustega</a>. + subject: 'Mastodon: %{instance} Kinnitamisjuhised' + title: Kinnita e-postiaadress + email_changed: + explanation: 'Teie konto e-postiaadress muudetakse:' + extra: Kui Te ei muutnud oma e-posti, on tõenäoline, et kellelgi on ligipääs Teie kontole. Palun muutke oma salasõna koheselt või võtke ühendust oma serveri administraatoriga, kui olete oma kontost välja lukustatud. + subject: 'Mastodon: E-post muudetud' + title: Uus e-postiaadress + password_change: + explanation: Teie konto parool on muudetud. + extra: Kui Te ei muutnud oma salasõna, on tõenäoline, et kellelgi on ligipääs Teie kontole. Palun muutke oma salasõna koheselt või võtke ühendust oma serveri administraatoriga, kui olete oma kontost välja lukustatud. + subject: 'Mastodon: Salasõna muudetud' + title: Salasõna muudetud + reconfirmation_instructions: + explanation: Kinnita oma uus aadress, et muuta oma e-posti. + extra: Kui see muudatus pole Teie poolt alustatud, palun eirake seda kirja. E-postiaadress sellele Mastodoni kontole ei muutu, kuni Te vajutate üleval asuvale lingile. + subject: 'Mastodon: Kinnitake e-post - %{instance}' + title: Kinnita e-postiaadress + reset_password_instructions: + action: Muuda salasõna + explanation: Te taotlesite oma kontole uut salasõna. + extra: Kui Te ei soovinud seda, palun eirake seda kirja. Teie salasõna ei muutu, kuni Te vajutate üleval olevale lingile ning loote uue. + subject: 'Mastodon: Salasõna lähtestamisjuhendid' + title: Salasõna lähtestamine + unlock_instructions: + subject: 'Mastodon: Lahti lukustamis juhendid' + omniauth_callbacks: + failure: Ei saanud Teid tuvastada %{kind} kaudu, kuna "%{reason}". + success: Tuvastamine %{kind} konto järgi õnnestus. + passwords: + no_token: Te ei saa sellele leheküljele ligi ilma tulemata salasõna lähtestamis e-kirjast. Kui Te tulete salasõna lähtestamis e-kirjast, palun olge kindel, et kasutasite tervet Teile antud URL-i. + send_instructions: Kui Teie e-postiaadress eksisteerib meie andmebaasis, saate paari minuti pärast e-kirja juhistega, kuidas oma salasõna taastada. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + send_paranoid_instructions: Kui Teie e-postiaadress eksisteerib meie andmebaasis, saate paari minuti pärast e-kirja juhistega, kuidas oma salasõna taastada. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + updated: Teie salasõna muutmine õnnestus. Te olete nüüd sisse loginud. + updated_not_active: Teie salasõna muutmine õnnestus. + registrations: + destroyed: Nägemist! Teie konto sulgemine õnnestus. Me loodame Teid varsti taas näha. + signed_up: Tere tulemast! Teie konto loomine õnnestus. + signed_up_but_inactive: Teie konto loodi edukalt, kuid me ei saanud Teid sisse logida, kuna teie konto pole veel aktiveeritud. + signed_up_but_locked: Teie konto loodi edukalt, kuid me ei saanud Teid sisse logida, kuna teie konto on lukustatud. + signed_up_but_pending: Kiri kinnituslingiga saadeti Teie e-postile. Pärast seda, kui te vajutate lingile, vaatame me Teie taotluse üle. Teid teavitatakse, kui see on vastu võetud. + signed_up_but_unconfirmed: Kiri kinnituslingiga saadeti Teie e-postile. Palun järgige linki, et aktiveerida oma konto. Palun kontrollige oma rämpsposti, kui Te ei saanud seda e-kirja. + update_needs_confirmation: Teie konto uuendamine õnnestus, kuid me peame Teie e-postiaadressit kinnitama. Palun kontrollige oma e-posti ning järgige linki, et kinnitada oma e-postiaadress. Palun kontrollige oma rämpsposti, kui Te ei saanud seda e-kirja. + updated: Teie konto uuendamine õnnestus. + sessions: + already_signed_out: Väljumine õnnestus. + signed_in: Sisenemine õnnestus. + signed_out: Väljumine õnnestus. + unlocks: + send_instructions: Te saate paari minuti pärast e-kirja juhistega, kuidas oma konto lukust lahti teha. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + send_paranoid_instructions: Kui Teie konto eksisteerib, saate Te paari minuti pärast e-kirja juhistega, kuidas see lukust lahti teha. Palun kontrollige oma rämpsposti kausta juhul, kui Te ei saanud seda e-kirja. + unlocked: Teie konto lukust lahti võtmine õnnestus. Jätkamiseks, palun logige sisse. + errors: + messages: + already_confirmed: oli juba kinnitatud, palun proovige sisse logida + confirmation_period_expired: peab olema kinnitatud ajavahemikus %{period} - palun taotlege uus + expired: on aegunud, palun taotlege uus + not_found: ei leitud + not_locked: ei olnud lukustatud + not_saved: + one: '1 viga takistas seda %{resource} salvestamast:' + other: "%{count} viga takistas seda %{resource} salvestamast:" diff --git a/config/locales/devise.eu.yml b/config/locales/devise.eu.yml index 3526f2ab5373a22dd59c1cbe2d827513587ef4a0..2160f224314869a6bf289b9e17adc5a6635f9706 100644 --- a/config/locales/devise.eu.yml +++ b/config/locales/devise.eu.yml @@ -46,6 +46,10 @@ eu: extra: Ez baduzu hau eskatu, mesedez ezikusi e-mail hau. Zure pasahitza ez da aldatuko goiko estekara sartu eta berri bat sortzen ez baduzu. subject: 'Mastodon: Pasahitza berrezartzeko argibideak' title: Pasahitza berrezartzea + two_factor_disabled: + title: 2FA desgaituta + two_factor_enabled: + title: 2FA gaituta unlock_instructions: subject: 'Mastodon: Desblokeatzeko argibideak' omniauth_callbacks: diff --git a/config/locales/devise.fa.yml b/config/locales/devise.fa.yml index 963572e6b47e12233c8dd0cb17bfb2ec1ac8824c..0954c8484b99601dddf92221d421aae6c318d49a 100644 --- a/config/locales/devise.fa.yml +++ b/config/locales/devise.fa.yml @@ -46,6 +46,18 @@ fa: extra: اگر شما چنین درخواستی نکردید، Ù„Ø·ÙØ§Ù‹ این ایمیل را نادیده بگیرید. تا زمانی Ú©Ù‡ شما پیوند بالا را باز نکنید Ùˆ رمز تازه‌ای نسازید، رمز شما عوض نخواهد شد. subject: 'ماستدون: راهنمایی برای بازنشانی رمز' title: بازنشانی رمز + two_factor_disabled: + explanation: ورود دومرØÙ„ه‌ای برای ØØ³Ø§Ø¨ شما ØºÛŒØ±ÙØ¹Ø§Ù„ شده است. از الان می‌توانید تنها با نشانی ایمیل Ùˆ رمز وارد ØØ³Ø§Ø¨ خود شوید. + subject: 'ماستدون: ورود دومرØÙ„ه‌ای ÙØ¹Ø§Ù„ نیست' + title: ورود دومرØÙ„ه‌ای ØºÛŒØ±ÙØ¹Ø§Ù„ + two_factor_enabled: + explanation: ورود دومرØÙ„ه‌ای برای ØØ³Ø§Ø¨ شما ÙØ¹Ø§Ù„ شده است. برای ورود به کدی نیاز خواهید داشت Ú©Ù‡ Ù†Ø±Ù…â€ŒØ§ÙØ²Ø§Ø± TOTP از پیش تنظیم شده برایتان می‌سازد. + subject: 'ماستدون: ورود دومرØÙ„ه‌ای ÙØ¹Ø§Ù„ است' + title: ورود دومرØÙ„ه‌ای ÙØ¹Ø§Ù„ + two_factor_recovery_codes_changed: + explanation: کدهای بازیابی قبلی نامعتبر شده‌اند Ùˆ کدهای تازه‌ای ساخته شده‌اند. + subject: 'ماستدون: کدهای بازیابی برای ورود دومرØÙ„ه‌ای دوباره ساخته شدند' + title: کدهای ورود دومرØÙ„ه‌ای تغییر کرد unlock_instructions: subject: 'ماستدون: راهنمایی برای بازکردن Ù‚ÙÙ„' omniauth_callbacks: diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml index 2d3c86c1fb9c5c6062c0ec0e5fa9b2522427f4ca..37ebdaa0721ad7efc55ed2e5a72d6540d27cc195 100644 --- a/config/locales/devise.fr.yml +++ b/config/locales/devise.fr.yml @@ -9,7 +9,7 @@ fr: already_authenticated: Vous êtes déjà connecté⋅e. inactive: Votre compte n’est pas encore activé. invalid: "%{authentication_keys} ou mot de passe invalide." - last_attempt: Vous avez droit à une tentative avant que votre compte ne soit verrouillé. + last_attempt: Vous avez droit à une dernière tentative avant que votre compte ne soit verrouillé. locked: Votre compte est verrouillé. not_found_in_database: "%{authentication_keys} ou mot de passe invalide." pending: Votre compte est toujours en cours d'approbation. @@ -21,7 +21,7 @@ fr: action: Vérifier l’adresse courriel action_with_app: Confirmer et retourner à %{app} explanation: Vous avez créé un compte sur %{host} avec cette adresse courriel. Vous êtes à un clic de l’activer. Si ce n’était pas vous, veuillez ignorer ce courriel. - explanation_when_pending: Vous avez demandé à vous inscrire à %{host} avec cette adresse courriel. Une fois que vous aurez confirmé cette adresse, nous étudierons votre demande. Vous ne pourrez pas vous connecté d'ici-là . Si votre demande est refusée, vos données seront supprimées du serveur, aucune action supplémentaire de votre part n'est donc requise. Si vous n'êtes pas à l'origine de cette demande, veuillez ignorer ce courriel. + explanation_when_pending: Vous avez demandé à vous inscrire à %{host} avec cette adresse de courriel. Une fois que vous aurez confirmé cette adresse, nous étudierons votre demande. Vous ne pourrez pas vous connecter d’ici-là . Si votre demande est refusée, vos données seront supprimées du serveur, aucune action supplémentaire de votre part n’est donc requise. Si vous n’êtes pas à l’origine de cette demande, veuillez ignorer ce message. extra_html: Merci de consultez également <a href="%{terms_path}">les règles du serveur</a> et <a href="%{policy_path}">nos conditions d’utilisation</a>. subject: 'Mastodon : Merci de confirmer votre inscription sur %{instance}' title: Vérifier l’adresse courriel @@ -46,6 +46,18 @@ fr: extra: Si vous ne l’avez pas demandé, veuillez ignorer ce courriel. Votre mot de passe ne changera pas tant que vous n’aurez pas cliqué sur le lien ci-dessus et que vous n’en aurez pas créé un nouveau. subject: 'Mastodon : Instructions pour changer votre mot de passe' title: Réinitialisation du mot de passe + two_factor_disabled: + explanation: L'authentification à deux facteurs pour votre compte a été désactivée. La connexion est maintenant possible en utilisant uniquement l'adresse courriel et le mot de passe. + subject: 'Mastodon : authentification à deux facteurs désactivée' + title: 2FA désactivée + two_factor_enabled: + explanation: L'authentification à deux facteurs a été activée pour votre compte. Un jeton généré par l'application appariée TOTP sera nécessaire pour vous connecter. + subject: 'Mastodon : authentification à deux facteurs activée' + title: A2F activée + two_factor_recovery_codes_changed: + explanation: Les codes de récupération précédents ont été invalidés et de nouveaux sont générés. + subject: 'Mastodon : codes de récupération à deux facteurs ré-générés' + title: Codes de récupération 2FA modifiés unlock_instructions: subject: 'Mastodon : Instructions pour déverrouiller votre compte' omniauth_callbacks: diff --git a/config/locales/devise.gl.yml b/config/locales/devise.gl.yml index 60a935a8a2f36788d57817fb1b042b83477df298..0ce335576b71416c8f26ead14cff9c68bfdb68ea 100644 --- a/config/locales/devise.gl.yml +++ b/config/locales/devise.gl.yml @@ -46,6 +46,18 @@ gl: extra: Si non fixo esta solicitude, por favor ignore este correo. O seu contrasinal non cambiará ate que acceda a ligazón superior e cree unha nova. subject: 'Mastodon: Instruccións para restablecer o contrasinal' title: Restablecer contrasinal + two_factor_disabled: + explanation: Desactivouse o segundo factor de autenticación para túa conta. Agora xa podes conectarte utilizando só o correo-e e contrasinal. + subject: 'Mastodon: Autenticación con dobre factor desactivada' + title: 2FA desactivada + two_factor_enabled: + explanation: A autenticación con dobre factor foi activada para a túa conta. Pedirase o testemuño xerado pola aplicación TOTP emparellada. + subject: 'Mastodon: Activouse o dobre factor de autenticación' + title: 2FA activado + two_factor_recovery_codes_changed: + explanation: Os códigos de recuperación anteriores quedan anulados e os novos foron creados. + subject: 'Mastodon: Código de recuperación do dobre factor rexenerados' + title: Códigos de recuperación 2FA cambiados unlock_instructions: subject: 'Mastodon: Instruccións para desbloquear' omniauth_callbacks: diff --git a/config/locales/devise.hu.yml b/config/locales/devise.hu.yml index e495cb5f0e7b84e6447387e1c0bfa17d78b8daa8..62888be74e597d4567159217596f946535fe3a66 100644 --- a/config/locales/devise.hu.yml +++ b/config/locales/devise.hu.yml @@ -46,6 +46,18 @@ hu: extra: Amennyiben nem te kezdeményezted a módosÃtást, kérjük tekintsd ezt az e-mailt tárgytalannak. A Mastodon fiókodhoz tartozó jelszavad változatlan marad mindaddig, amÃg újat nem hozol létre a fenti linkre kattintva. subject: 'Mastodon: Jelszó visszaállÃtási lépések' title: Jelszó visszaállÃtása + two_factor_disabled: + explanation: A fiókod kétlépcsÅ‘s hitelesÃtését letiltottuk. A bejelentkezés most már csak e-mail cÃmmel és jelszóval lehetséges. + subject: KétlépcsÅ‘s azonosÃtás letiltva + title: KétlépcsÅ‘s hitelesÃtés engedélyezve + two_factor_enabled: + explanation: KétlépcsÅ‘s hitelesÃtés engedélyezve van a fiókodban. Bejelentkezéshez a párosÃtott TOTP alkalmazás által létrehozott tokenre lesz szükség. + subject: KétlépcsÅ‘s azonosÃtás engedélyezve + title: KétlépcsÅ‘s hitelesÃtés engedélyezve + two_factor_recovery_codes_changed: + explanation: A korábbi helyreállÃtási kódokat letiltottuk, és újakat generáltunk. + subject: KétlépcsÅ‘s helyreállÃtási kódok újra létrejöttek + title: A kétlépcsÅ‘s kódok megváltozott unlock_instructions: subject: 'Mastodon: Feloldási lépések' omniauth_callbacks: diff --git a/config/locales/devise.id.yml b/config/locales/devise.id.yml index 5fa9020911358b2b165aac04723eba5ccf73413e..5b4e8af43da85803ea6898671182cd62e5f0e74b 100644 --- a/config/locales/devise.id.yml +++ b/config/locales/devise.id.yml @@ -12,16 +12,52 @@ id: last_attempt: Anda memiliki beberapa kali mencoba sebelum akun anda dikunci. locked: Akun anda dikunci. not_found_in_database: Ada %{authentication_keys} atau kata sandi yang tidak cocok. + pending: Akun Anda masih dalam peninjauan. timeout: Sesi anda telah berakhir. Silahkan coba masuk lagi. unauthenticated: Anda harus masuk atau mendaftar terlebih dahulu. unconfirmed: Anda harus mengkonfirmasi alamat email terlebih dahulu. mailer: confirmation_instructions: + action: Verifikasi alamat surel + action_with_app: Konfirmasi dan kembali ke %{app} + explanation: Anda membuat akun di %{host} dengan surel ini. Anda hanya perlu satu klik untuk mengaktifkannya. Jika ini bukan Anda, abaikan surel ini. + explanation_when_pending: Anda melamar undangan ke %{host} dengan surel ini. Saat Anda mengonfirmasi alamat surel Anda, kami akan meninjaunya. Anda dapat masuk untuk mengubah detail Anda atau menghapus akun Anda, tapi Anda tak dapat mengakses mayoritas fungsi sampai akun disetujui. Jika lamaran anda ditolak, data Anda akan dihapus, tak ada aksi lain yang dilakukan dari Anda. Jika ini bukan Anda, abaikan surel ini. + extra_html: Tolong cek juga <a href="%{terms_path}">peraturan server</a> dan <a href="%{policy_path}">ketentuan layanan kami</a>. subject: 'Mastodon: Petunjuk mengkonfirmasi untuk %{instance}' + title: Verifikasi alamat surel + email_changed: + explanation: 'Alamat surel akun Anda diubah menjadi:' + extra: Jika Anda tak mengganti surel Anda, mungkin seseorang telah mendapatkan akses ke Akun Anda. Mohon ubah kata sandi secepatnya atau hubungi admin server jika Anda dikunci dari akun Anda. + subject: 'Mastodon: Surel diganti' + title: Alamat surel baru password_change: + explanation: Kata sandi akun Anda telah diganti. + extra: Jika Anda tak mengubah kata sandi Anda, mungkin seseorang telah mendapatkan akses ke akun Anda. Mohon ubah kata sandi secepatnya atau hubungi admin server jika Anda dikunci dari akun Anda. subject: 'Mastodon: Kata sandi telah diubah' + title: Kata sandi diubah + reconfirmation_instructions: + explanation: Konfirmasi alamat baru untuk mengubah surel Anda. + extra: Jika perubahan ini tidak dimulai dari Anda, abaikan surel ini. Alamat surel untuk akun Mastodon tak berubah sampai Anda mengakses tautan di atas. + subject: 'Mastodon: Konfirmasi surel untuk %{instance}' + title: Verifikasi alamat surel reset_password_instructions: + action: Ubah kata sandi + explanation: Kata sandi baru yang diminta untuk akun Anda. + extra: Jika Anda tak meminta ini, abaikan surel ini. Kata sandi tak berubah sampai Anda mengakses tautan di atas dan membuatnya yang baru. subject: 'Mastodon: Petunjuk mereset kata sandi' + title: Reset kata sandi + two_factor_disabled: + explanation: Otentifikasi dua-faktor untuk akun Anda dimatikan. Kini Masuk dapat dilakukan hanya dengan alamat surel dan kata sandi. + subject: 'Mastodon: Otentifikasi dua-faktor dimatikan' + title: 2FA dimatikan + two_factor_enabled: + explanation: Otentifikasi dua-faktor telah dimatikan untuk Akun Anda. Token yang dibuat menggunakan aplikasi TOTP berpasangan akan diperlukan untuk masuk. + subject: 'Mastodon: Otentifikasi dua-faktor diaktifkan' + title: 2FA diaktifkan + two_factor_recovery_codes_changed: + explanation: Kode pemulihan sebelumnya telah dibatalkan dan yang baru telah dibuat. + subject: 'Mastodon: Kode pemulihan dua-faktor dibuat ulang' + title: Kode pemulihan 2FA diubah unlock_instructions: subject: 'Mastodon: Petunjuk membuka' omniauth_callbacks: @@ -38,6 +74,7 @@ id: signed_up: Selamat datang! Pendaftaran anda berhasil. signed_up_but_inactive: Anda berhasil melakukan pendaftaran. Tetapi kami tidak dapat memasukkan anda karena akun anda belum diaktifkan. signed_up_but_locked: Anda berhasil melakukan pendaftaran. Tetapi kami tidak dapat memasukkan anda karena akun anda dikunci. + signed_up_but_pending: Pesan dengan tautan konfirmasi telah dikirim ke alamat surel Anda. Setelah Anda mengklik tautan, kami akan meninjau lamaran Anda. Anda akan diberitahu jika diterima. signed_up_but_unconfirmed: Sebuah pesan berisi link konfirmasi telah dikirim ke alamat email anda. Silakan ikuti link tersebut untuk mengaktifkan akun anda. update_needs_confirmation: Akun anda telah berhasil diubah, tetapi kami harus memverifikasi alamat email anda yang baru. Mohon cek email anda dan ikuti link untuk mengkonfirmasi alamat email anda yang baru. updated: Akun anda berhasil diubah. diff --git a/config/locales/devise.it.yml b/config/locales/devise.it.yml index b603e12c682e89cf55689b98b3990e8345c3b0dd..dca5e3caa3d88b224b94353829dcdddb5638cd7f 100644 --- a/config/locales/devise.it.yml +++ b/config/locales/devise.it.yml @@ -46,6 +46,17 @@ it: extra: Se questo cambiamento non è stato chiesto da te, ignora questa email. La tua password non verrà cambiata finché non accedi tramite il link qui sopra e ne crei una nuova. subject: 'Mastodon: Istruzioni per il reset della password' title: Ripristino password + two_factor_disabled: + explanation: L'autenticazione a due fattori per il tuo account è stata disattivata. Il login è ora possibile utilizzando solo l'indirizzo e-mail e la password. + subject: 'Mastodon: Autenticazione a due fattori disattivata' + title: 2FA disabilitata + two_factor_enabled: + explanation: L'autenticazione a due fattori è stata attivata per il tuo account. Un token generato dall'app TOTP collegata sarà richiesto per il login. + subject: 'Mastodon: Autenticazione a due fattori attivata' + title: 2FA abilitata + two_factor_recovery_codes_changed: + subject: 'Mastodon: codici di recupero a due fattori ri-generati' + title: Codici di recupero 2FA modificati unlock_instructions: subject: 'Mastodon: Istruzioni di sblocco' omniauth_callbacks: diff --git a/config/locales/devise.ja.yml b/config/locales/devise.ja.yml index dc147be624ece8b887165f6d2d076d6d81bae1f1..5f32b23815b9bf2aa02a9b2009e61ef48a7a6d38 100644 --- a/config/locales/devise.ja.yml +++ b/config/locales/devise.ja.yml @@ -21,7 +21,7 @@ ja: action: メールアドレスã®ç¢ºèª action_with_app: 確èªã— %{app} ã«æˆ»ã‚‹ explanation: ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã§%{host}ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’作æˆã—ã¾ã—ãŸã€‚有効ã«ã™ã‚‹ã¾ã§ã‚ã¨ä¸€æ©ã§ã™ã€‚ã‚‚ã—心当ãŸã‚ŠãŒãªã„å ´åˆã€ç”³ã—訳ã‚りã¾ã›ã‚“ãŒã“ã®ãƒ¡ãƒ¼ãƒ«ã‚’無視ã—ã¦ãã ã•ã„。 - explanation_when_pending: ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã§%{host}ã¸ã®ç™»éŒ²ã‚’申請ã—ã¾ã—ãŸã€‚ã‚ãªãŸãŒãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’確èªã—ãŸã‚‰ã€ã‚µãƒ¼ãƒãƒ¼ç®¡ç†è€…ãŒç”³è«‹ã‚’審査ã—ã¾ã™ã€‚ãれã¾ã§ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“。申請ãŒå´ä¸‹ã•れãŸå ´åˆã€ã‚ãªãŸã®ãƒ‡ãƒ¼ã‚¿ã¯å‰Šé™¤ã•れã¾ã™ã®ã§ä»¥é™ã®æ“作ã¯å¿…è¦ã‚りã¾ã›ã‚“。もã—心当ãŸã‚ŠãŒãªã„å ´åˆã€ç”³ã—訳ã‚りã¾ã›ã‚“ãŒã“ã®ãƒ¡ãƒ¼ãƒ«ã‚’無視ã—ã¦ãã ã•ã„。 + explanation_when_pending: ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã§ %{host} ã¸ã®ç™»éŒ²ã‚’申請ã—ã¾ã—ãŸã€‚ã‚ãªãŸãŒãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’確èªã—ãŸã‚‰ã€ã‚µãƒ¼ãƒãƒ¼ç®¡ç†è€…ãŒç”³è«‹ã‚’審査ã—ã¾ã™ã€‚ãƒã‚°ã‚¤ãƒ³ã—ã¦ä¸€éƒ¨è¨å®šã‚’変更ã—ãŸã‚Šã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’削除ã§ãã¾ã™ãŒã€ã»ã¨ã‚“ã©ã®æ©Ÿèƒ½ã¯ç”³è«‹ãŒæ‰¿èªã•れるã¾ã§åˆ©ç”¨ã§ãã¾ã›ã‚“。申請ãŒå´ä¸‹ã•れãŸå ´åˆã€ã‚ãªãŸã®ãƒ‡ãƒ¼ã‚¿ã¯å‰Šé™¤ã•れã¾ã™ã®ã§ä»¥é™ã®æ“作ã¯å¿…è¦ã‚りã¾ã›ã‚“。もã—心当ãŸã‚ŠãŒãªã„å ´åˆã€ç”³ã—訳ã‚りã¾ã›ã‚“ãŒã“ã®ãƒ¡ãƒ¼ãƒ«ã‚’無視ã—ã¦ãã ã•ã„。 extra_html: ã¾ãŸ <a href="%{terms_path}">サーãƒãƒ¼ã®ãƒ«ãƒ¼ãƒ«</a> 㨠<a href="%{policy_path}">利用è¦ç´„</a> ã‚‚ãŠèªã¿ãã ã•ã„。 subject: 'Mastodon: メールアドレスã®ç¢ºèª %{instance}' title: メールアドレスã®ç¢ºèª @@ -46,6 +46,18 @@ ja: extra: ã“ã®è¦æ±‚ã«å¿ƒå½“ãŸã‚ŠãŒãªã„å ´åˆã€ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚’無視ã—ã¦ãã ã•ã„。上記リンク先ã«ã‚¢ã‚¯ã‚»ã‚¹ã—æ–°ã—ã„ã‚‚ã®ã‚’作æˆã™ã‚‹ã¾ã§ãƒ‘スワードã¯å¤‰æ›´ã•れã¾ã›ã‚“。 subject: 'Mastodon: パスワードå†ç™ºè¡Œ' title: パスワードå†ç™ºè¡Œ + two_factor_disabled: + explanation: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®äºŒæ®µéšŽèªè¨¼ãŒç„¡åŠ¹åŒ–ã•れã¾ã—ãŸã€‚メールã¨ãƒ‘スワードã®ã¿ã§ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã™ã€‚ + subject: 'Mastodon: 二段階èªè¨¼ãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ' + title: 二段階èªè¨¼ãŒç„¡åŠ¹åŒ–ã•れã¾ã—㟠+ two_factor_enabled: + explanation: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®äºŒæ®µéšŽèªè¨¼ãŒæœ‰åŠ¹åŒ–ã•れã¾ã—ãŸã€‚ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã«ã¯ TOTP アプリã§ç”Ÿæˆã•れãŸã‚³ãƒ¼ãƒ‰ãŒå¿…è¦ã§ã™ã€‚ + subject: 'Mastodon: 二段階èªè¨¼ãŒæœ‰åйã«ãªã‚Šã¾ã—ãŸ' + title: 二段階èªè¨¼ãŒæœ‰åŠ¹åŒ–ã•れã¾ã—㟠+ two_factor_recovery_codes_changed: + explanation: 以å‰ã®ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ãŒç„¡åŠ¹åŒ–ã•ã‚Œã€æ–°ã—ã„コードãŒç”Ÿæˆã•れã¾ã—ãŸã€‚ + subject: 'Mastodon: 二段階èªè¨¼ã®ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ãŒå†ç”Ÿæˆã•れã¾ã—ãŸ' + title: 二段階èªè¨¼ã®ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ãŒå¤‰æ›´ã•れã¾ã—㟠unlock_instructions: subject: 'Mastodon: アカウントã®ãƒãƒƒã‚¯ã®è§£é™¤' omniauth_callbacks: diff --git a/config/locales/devise.ko.yml b/config/locales/devise.ko.yml index f48531246e1127dd763cffa99a5101e1fd6d2976..89dd12f1d7f994a5c0362ddd58eef3edac66c58c 100644 --- a/config/locales/devise.ko.yml +++ b/config/locales/devise.ko.yml @@ -46,6 +46,18 @@ ko: extra: 만약 ë‹¹ì‹ ì´ ì‹œë„한 ê²ƒì´ ì•„ë‹ˆë¼ë©´ ì´ ë©”ì¼ì„ 무시해 주세요. 위 ë§í¬ë¥¼ í´ë¦í•´ 패스워드를 새로 ì„¤ì •í•˜ê¸° ì „ê¹Œì§€ëŠ” 패스워드가 바뀌지 않습니다. subject: 'ë§ˆìŠ¤í† ëˆ: 패스워드 ìž¬ì„¤ì • 방법' title: 패스워드 ìž¬ì„¤ì • + two_factor_disabled: + explanation: ë‹¹ì‹ ì˜ ê³„ì •ì— ì„¤ì •ëœ ì´ì¤‘ ì¸ì¦ì´ 비활성화 ë˜ì—ˆìŠµë‹ˆë‹¤. ì´ì œ ì´ë©”ì¼ê³¼ 암호만으로 로그ì¸ì´ 가능합니다. + subject: 'ë§ˆìŠ¤í† ëˆ: ì´ì¤‘ ì¸ì¦ 비활성화' + title: 2FA 비활성화 ë¨ + two_factor_enabled: + explanation: ë‹¹ì‹ ì˜ ê³„ì •ì— ì´ì¤‘ ì¸ì¦ì´ 활성화ë˜ì—ˆìŠµë‹ˆë‹¤. 로그ì¸ì‹œ 페어ë§ëœ T-OTP 앱ì—서 ìƒì„±ëœ í† í°ì´ 필요합니다. + subject: 'ë§ˆìŠ¤í† ëˆ: ì´ì¤‘ ì¸ì¦ 활성화' + title: 2FA 활성화 ë¨ + two_factor_recovery_codes_changed: + explanation: ì´ì „ 복구 코드가 무효화ë˜ê³ 새 코드가 ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤ + subject: 'ë§ˆìŠ¤í† ëˆ: ì´ì¤‘ ì¸ì¦ 복구 코드 재ìƒì„± ë¨' + title: 2FA 복구 코드 ë³€ê²½ë¨ unlock_instructions: subject: 'ë§ˆìŠ¤í† ëˆ: ìž ê¸ˆ í•´ì œ 방법' omniauth_callbacks: diff --git a/config/locales/devise.mk.yml b/config/locales/devise.mk.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b9144a988941222eba3d4c61c19a62a8b057db4 --- /dev/null +++ b/config/locales/devise.mk.yml @@ -0,0 +1 @@ +mk: diff --git a/config/locales/devise.nl.yml b/config/locales/devise.nl.yml index 51a95403fdee475c688d5e866aaabef6314f4809..3ab4d9f11711f8a90d20637b9ecef9d269dab6e2 100644 --- a/config/locales/devise.nl.yml +++ b/config/locales/devise.nl.yml @@ -46,6 +46,18 @@ nl: extra: Wanneer jij dit niet hebt aangevraagd, mag je deze e-mail negeren. Jouw wachtwoord wordt pas gewijzigd nadat je de link hierboven hebt aangeklikt en een nieuw wachtwoord aanmaakt. subject: 'Mastodon: Wachtwoord opnieuw instellen' title: Wachtwoord opnieuw instellen + two_factor_disabled: + explanation: Tweestapsverificatie voor jouw account is uitgeschakeld. Je kunt nu alleen inloggen met een e-mailadres en wachtwoord. + subject: 'Mastodon: Tweestapsverificatie uitgeschakeld' + title: Tweestapsverificatie uitgeschakeld + two_factor_enabled: + explanation: Tweestapsverificatie voor jouw account is ingeschakeld. Om te kunnen aanmelden is een door een tweestapsverificatie-app genereerde aanmeldcode nodig. + subject: 'Mastodon: Tweestapsverificatie ingeschakeld' + title: Tweestapsverificatie ingeschakeld + two_factor_recovery_codes_changed: + explanation: De vorige herstelcodes zijn ongeldig gemaakt en nieuwe zijn aangemaakt. + subject: 'Mastodon: Tweestaps-herstelcodes zijn opnieuw aangemaakt' + title: Herstelcodes tweestapsverificatie veranderd unlock_instructions: subject: 'Mastodon: Instructies om opschorten account ongedaan te maken' omniauth_callbacks: diff --git a/config/locales/devise.nn.yml b/config/locales/devise.nn.yml new file mode 100644 index 0000000000000000000000000000000000000000..777f4e600f65daa6a550f2cf0f04551a49cf4fcd --- /dev/null +++ b/config/locales/devise.nn.yml @@ -0,0 +1 @@ +nn: diff --git a/config/locales/devise.oc.yml b/config/locales/devise.oc.yml index 42be33f6b5b4c2de4dd03e503163fe6053bf355a..0fb259429a23bcc7fc6b2bba70a983f81f867ceb 100644 --- a/config/locales/devise.oc.yml +++ b/config/locales/devise.oc.yml @@ -46,6 +46,18 @@ oc: extra: S’avètz pas res demandat, fasquètz pas cas a aqueste corrièl. Vòstre senhal cambiarà pas se clicatz pas lo ligam e que ne causissètz pas un novèl. subject: Mastodon : consignas per reïnicializar lo senhal title: Reïnicializacion del senhal + two_factor_disabled: + explanation: L’autentificacion dos factors per vòstre compte es estada desactivada. La connexion es ara possibla solament amb l’adreça electronica e lo senhal. + subject: 'Mastodon : autentificacion dos factors desactivada' + title: 2FA desactivat + two_factor_enabled: + explanation: L’autentificacion dos factors es estada activada per vòstre compte. La connexion demandarà un geton generat per l’aplicacion TOTP associada. + subject: 'Mastodon : autentificacion dos factor activada' + title: 2FA activat + two_factor_recovery_codes_changed: + explanation: Los còdis de recuperacion precedents son ara invalids e de nòus son estats generats. + subject: 'Mastodon : còdis de recuperacion dos factors regenerats' + title: Còdis 2FA de recuperacion cambiats unlock_instructions: subject: Mastodon : consignas de desblocatge omniauth_callbacks: diff --git a/config/locales/devise.pt.yml b/config/locales/devise.pt-PT.yml similarity index 99% rename from config/locales/devise.pt.yml rename to config/locales/devise.pt-PT.yml index 9b44bbf00c6e6dadb06cf72c565e3270e9e78d15..7d3f8fc5523818cd8f6768883ebcc883b33d09a9 100644 --- a/config/locales/devise.pt.yml +++ b/config/locales/devise.pt-PT.yml @@ -1,5 +1,5 @@ --- -pt: +pt-PT: devise: confirmations: confirmed: O teu endereço de e-mail foi confirmado com sucesso. diff --git a/config/locales/devise.sk.yml b/config/locales/devise.sk.yml index 85de603d3e2068a4d159a0c31a96c05c3e7fd2b8..759d4874b7549b3294617866c871c837f6acd619 100644 --- a/config/locales/devise.sk.yml +++ b/config/locales/devise.sk.yml @@ -37,8 +37,8 @@ sk: reconfirmation_instructions: explanation: PotvrÄ novú emailovú adresu na ktorú chceÅ¡ zmeniÅ¥ svoj email. extra: Pokiaľ si túto akciu nevyžiadal/a, prosÃm ignoruj tento email. Emailová adresa pre tvoj Mastodon úÄet totiž nebude zmenená pokiaľ nepostúpiÅ¡ na adresu uvedenú vyššie. - subject: 'Mastodon: PotvrÄenie emailu pre %{instance}' - title: OveriÅ¥ emailovú adresu + subject: 'Mastodon: PotvrÄ email pre %{instance}' + title: Over emailovú adresu reset_password_instructions: action: Zmeň svoje heslo explanation: Vyžiadal/a si si nové heslo pre svoj úÄet. diff --git a/config/locales/devise.sl.yml b/config/locales/devise.sl.yml index 7d1e05fdf0f9dd9b48a0dfbb913ba6e5427e7a32..dee1b91253887392ab1d4dc4e6c0c53c25135711 100644 --- a/config/locales/devise.sl.yml +++ b/config/locales/devise.sl.yml @@ -6,7 +6,7 @@ sl: send_instructions: V nekaj minutah boste prejeli e-poÅ¡tno sporoÄilo z navodili za potrditev vaÅ¡ega e-poÅ¡tnega naslova. ÄŒe niste prejeli e-poÅ¡tnega sporoÄila, preverite mapo neželena poÅ¡ta. send_paranoid_instructions: ÄŒe vaÅ¡ e-poÅ¡tni naslov obstaja v naÅ¡i podatkovni bazi, boste v nekaj minutah prejeli e-poÅ¡tno sporoÄilo z navodili za potrditev vaÅ¡ega e-poÅ¡tnega naslova. ÄŒe niste prejeli e-poÅ¡tnega sporoÄila, preverite mapo neželena poÅ¡ta. failure: - already_authenticated: Prijavljeni ste že. + already_authenticated: Ste že prijavljeni. inactive: VaÅ¡ raÄun Å¡e ni aktiviran. invalid: Neveljavno %{authentication_keys} ali geslo. last_attempt: Pred zaklepom raÄuna imate Å¡e en poskus. @@ -45,9 +45,44 @@ sl: explanation: Zahtevali ste novo geslo za svoj raÄun. extra: ÄŒe tega niste zahtevali, prezrite to e-poÅ¡tno sporoÄilo. VaÅ¡e geslo se ne bo spremenilo, dokler ne kliknete na zgornjo povezavo in ustvarite novega. subject: 'Mastodon: Navodila za ponastavitev gesla' - title: Ponastavitev gesla + title: Ponastavi geslo unlock_instructions: subject: 'Mastodon: Odkleni navodila' omniauth_callbacks: failure: Overitev iz %{kind} ni možna zaradi "%{reason}". success: Overitev iz raÄuna %{kind} je bila uspeÅ¡na. + passwords: + no_token: Do te strani ne morete dostopati, ne da bi priÅ¡li iz e-poÅ¡tne za ponastavitev gesla. ÄŒe prihajate iz e-poÅ¡tne za ponastavitev gesla, se prepriÄajte, da ste uporabili celoten navedeni URL. + send_instructions: ÄŒe vaÅ¡ e-poÅ¡tni naslov obstaja v naÅ¡i bazi podatkov, boste v nekaj minutah na vaÅ¡ e-poÅ¡tni naslov prejeli povezavo za obnovitev gesla. ÄŒe niste prejeli e-poÅ¡te, preverite mapo z neželeno poÅ¡to. + send_paranoid_instructions: ÄŒe vaÅ¡ e-poÅ¡tni naslov obstaja v naÅ¡i bazi podatkov, boste v nekaj minutah na vaÅ¡ e-poÅ¡tni naslov prejeli povezavo za obnovitev gesla. ÄŒe niste prejeli e-poÅ¡te, preverite mapo z neželeno poÅ¡to. + updated: VaÅ¡e geslo je bilo uspeÅ¡no spremenjeno. Zdaj ste prijavljeni. + updated_not_active: VaÅ¡e geslo je bilo uspeÅ¡no spremenjeno. + registrations: + destroyed: Adijo! VaÅ¡ raÄun je bil uspeÅ¡no preklican. Upamo, da vas bomo kmalu spet videli. + signed_up: DobrodoÅ¡li! UspeÅ¡no ste se vpisali. + signed_up_but_inactive: UspeÅ¡no ste se vpisali. Vendar vas nismo mogli prijaviti, ker vaÅ¡ raÄun Å¡e ni aktiviran. + signed_up_but_locked: UspeÅ¡no ste se vpisali. Vendar vas nismo mogli prijaviti, ker je vaÅ¡ raÄun zaklenjen. + signed_up_but_pending: Na vaÅ¡ e-poÅ¡tni naslov je bilo poslano sporoÄilo s povezavo za potrditev. Ko kliknete na povezavo, bomo pregledali vaÅ¡o prijavo. ObveÅ¡Äeni boste, Äe bo odobren. + signed_up_but_unconfirmed: Na vaÅ¡ e-poÅ¡tni naslov je bilo poslano sporoÄilo s povezavo za potrditev. Sledite povezavi, da aktivirate svoj raÄun. ÄŒe niste prejeli te e-poÅ¡te, preverite mapo z neželeno poÅ¡to. + update_needs_confirmation: UspeÅ¡no ste posodobili raÄun, vendar moramo potrditi vaÅ¡ novi e-poÅ¡tni naslov. Preverite svojo e-poÅ¡to in sledite povezavi za potrditev, da potrdite nov e-poÅ¡tni naslov. ÄŒe niste prejeli te e-poÅ¡e, preverite mapo z neželeno poÅ¡to. + updated: VaÅ¡ raÄun je bil uspeÅ¡no posodobljen. + sessions: + already_signed_out: UspeÅ¡no ste se odjavili. + signed_in: UspeÅ¡no ste se prijavili. + signed_out: UspeÅ¡no ste se odjavili. + unlocks: + send_instructions: Prejeli boste e-poÅ¡to z navodili o tem, kako v nekaj minutah odklenete svoj raÄun. ÄŒe niste prejeli te e-poÅ¡te, preverite mapo z neželeno poÅ¡to. + send_paranoid_instructions: ÄŒe vaÅ¡ raÄun obstaja, boste prejeli e-poÅ¡to z navodili za njegovo odklepanje v nekaj minutah. ÄŒe niste prejeli te e-poÅ¡te, preverite mapo z neželeno poÅ¡to. + unlocked: VaÅ¡ raÄun je bil uspeÅ¡no odklenjen. ÄŒe želite nadaljevati, se prijavite. + errors: + messages: + already_confirmed: je bil potrjen, poskusite se prijaviti + confirmation_period_expired: mora biti potrjena v %{period}, zahtevajte novo + expired: je potekla, zahtevajte novo + not_found: ni najdeno + not_locked: ni bil zaklenjen + not_saved: + few: "%{count} napake so prepreÄile shranjevanje %{resource}:" + one: '1 napaka je prepreÄila shranjevanje %{resource}:' + other: "%{count} napak je prepreÄilo shranjevanje %{resource}:" + two: "%{count} napaki sta prepreÄili shranjevanje %{resource}:" diff --git a/config/locales/devise.th.yml b/config/locales/devise.th.yml index 8a9a65465ed5d6e9853b561ba376fb0c11626ba1..383c515138f9638d64d669b62130fb802e0a1313 100644 --- a/config/locales/devise.th.yml +++ b/config/locales/devise.th.yml @@ -9,7 +9,9 @@ th: already_authenticated: คุณได้ลงชื่à¸à¹€à¸‚้าà¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§ inactive: ยังไม่ได้เปิดใช้งานบัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณ invalid: "%{authentication_keys} หรืà¸à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹„ม่ถูà¸à¸•้à¸à¸‡" + locked: บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณถูà¸à¸¥à¹‡à¸à¸ not_found_in_database: "%{authentication_keys} หรืà¸à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹„ม่ถูà¸à¸•้à¸à¸‡" + pending: บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณà¸à¸³à¸¥à¸±à¸‡à¸–ูà¸à¸•รวจสà¸à¸š timeout: เซสชันขà¸à¸‡à¸„ุณหมดà¸à¸²à¸¢à¸¸à¹à¸¥à¹‰à¸§ โปรดลงชื่à¸à¹€à¸‚้าà¸à¸µà¸à¸„รั้งเพื่à¸à¸”ำเนินà¸à¸²à¸£à¸•่ภmailer: email_changed: @@ -19,10 +21,15 @@ th: title: เปลี่ยนรหัสผ่านà¹à¸¥à¹‰à¸§ reset_password_instructions: action: เปลี่ยนรหัสผ่าน + two_factor_disabled: + title: ปิดใช้งาน 2FA à¹à¸¥à¹‰à¸§ + two_factor_enabled: + title: เปิดใช้งาน 2FA à¹à¸¥à¹‰à¸§ passwords: send_instructions: If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes. send_paranoid_instructions: If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes. registrations: + signed_up_but_pending: ข้à¸à¸„วามที่ได้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ˆà¸°à¸–ูà¸à¸ªà¹ˆà¸‡à¹„ปยังà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸„ุณ หลังจาà¸à¸™à¸±à¹‰à¸™à¹ƒà¸«à¹‰à¸—ำà¸à¸²à¸£à¸„ลิà¸à¸¥à¸´à¸‡à¸à¹Œ เพื่à¸à¹ƒà¸«à¹‰à¸žà¸§à¸à¹€à¸£à¸²à¸•รวจสà¸à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¹€à¸ªà¸£à¹‡à¸ˆà¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸à¸¢ à¹à¸¥à¸°à¸«à¸¥à¸±à¸‡à¸ˆà¸²à¸à¸™à¸±à¹‰à¸™à¸„ุณจะได้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™ signed_up_but_unconfirmed: A message with a confirmation link has been sent to your email address. Please follow the link to activate your account. update_needs_confirmation: You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address. sessions: diff --git a/config/locales/devise.tr.yml b/config/locales/devise.tr.yml index 0a504dd67635900ff497a05acbeaef792c885d93..30cedc1fc92f3514f1efa5854d8cf51c54e8b105 100644 --- a/config/locales/devise.tr.yml +++ b/config/locales/devise.tr.yml @@ -8,8 +8,91 @@ tr: failure: already_authenticated: Zaten oturum açtınız. inactive: Hesabınız henüz etkinleÅŸtirilmedi. + invalid: Geçersiz %{authentication_keys} ya da ÅŸifre. last_attempt: Hesabınız kilitlenmeden önce bir kez daha denemeniz gerekir. locked: Hesabınız kilitli. + not_found_in_database: Geçersiz %{authentication_keys} ya da ÅŸifre. + pending: Hesabınız hala inceleniyor. + timeout: Oturum süreniz sona erdi. Lütfen devam etmek için tekrar giriÅŸ yapınız. + unauthenticated: Devam etmeden önce oturum açmanız veya kayıt olmanız gerek. + unconfirmed: Devam etmeden önce e-posta adresini onaylamanız gerekli. mailer: confirmation_instructions: action: E-posta adresinizi doÄŸrulayın + action_with_app: Onayla ve %{app}'a dön + explanation: Bu e-posta adresiyle %{host} bir hesap oluÅŸturdunuz. EtkinleÅŸtirmekten bir tık uzaktasınız. Bu siz deÄŸilseniz, lütfen bu e-postayı dikkate almayın. + explanation_when_pending: Bu e-posta adresiyle %{host} adresine bir davetiye için baÅŸvuru yaptınız. E-posta adresinizi onayladıktan sonra baÅŸvurunuzu inceleyeceÄŸiz. O zamana kadar giriÅŸ yapamazsınız. BaÅŸvurunuz reddedilirse, verileriniz silinecek, baÅŸka bir iÅŸlem yapmanız gerekmeyecek. Bu siz deÄŸilseniz, lütfen bu e-postayı dikkate almayın. + extra_html: Lütfen ayrıca <a href="%{terms_path}">sunucu kurallarını</a> ve <a href="%{policy_path}">hizmet ÅŸartlarımızı</a> inceleyin. + subject: 'Mastodon: %{instance} için onay talimatları' + title: E-posta adresini doÄŸrulayın + email_changed: + explanation: 'Hesabınızın e-posta adresi ÅŸu ÅŸekilde deÄŸiÅŸtirildi:' + extra: E-posta adresinizi deÄŸiÅŸtirmediyseniz, büyük olasılıkla birileri hesabınıza eriÅŸti. Lütfen derhal parolanızı deÄŸiÅŸtirin veya hesabınız kilitlendiyse sunucu yöneticisine baÅŸvurun. + subject: 'Mastodon: E-posta deÄŸiÅŸti' + title: Yeni e-posta adresi + password_change: + explanation: Hesabınızın parolası deÄŸiÅŸtirildi. + extra: Parolanızı deÄŸiÅŸtirmediyseniz, büyük olasılıkla birileri hesabınıza eriÅŸmiÅŸ olabilir. Lütfen derhal parolanızı deÄŸiÅŸtirin veya hesabınız kilitlendiyse sunucu yöneticisine baÅŸvurun. + subject: 'Mastodon: Parola deÄŸiÅŸtirildi' + title: Parola deÄŸiÅŸtirildi + reconfirmation_instructions: + explanation: E-postanızı deÄŸiÅŸtirmek için yeni adresi onaylayın. + extra: Bu deÄŸiÅŸiklik sizin tarafınızdan baÅŸlatılmadıysa, lütfen bu e-postayı dikkate almayın. Mastodon hesabının e-posta adresi, yukarıdaki baÄŸlantıya eriÅŸene kadar deÄŸiÅŸmez. + subject: 'Mastodon: %{instance} için e-postayı onayla' + title: E-posta adresinizi doÄŸrulayın + reset_password_instructions: + action: Parolayı deÄŸiÅŸtir + explanation: Hesabınız için yeni bir parola istediniz. + extra: Bunu siz yapmadıysanız, lütfen bu e-postayı dikkate almayın. Parolanız yukarıdaki baÄŸlantıya eriÅŸene ve yeni bir tane oluÅŸturuncaya kadar deÄŸiÅŸmez. + subject: 'Mastodon: Parola sıfırlama talimatları' + title: Parola sıfırlama + two_factor_disabled: + explanation: Hesabınız için iki-adımlı kimlik doÄŸrulama devre dışı bırakıldı. Åžimdi sadece e-posta adresi ve parola kullanarak giriÅŸ yapabilirsiniz. + subject: 'Mastodon: İki-adımlı kimlik doÄŸrulama devre dışı bırakıldı' + title: 2FA devre dışı bırakıldı + two_factor_enabled: + explanation: Hesabınız için iki-adımlı kimlik doÄŸrulama etkinleÅŸtirildi. GiriÅŸ yapmak için eÅŸleÅŸtirilmiÅŸ TOTP uygulaması tarafından oluÅŸturulan bir belirteç gereklidir. + subject: 'Mastodon: İki-adımlı kimlik doÄŸrulama etkinleÅŸtirildi' + title: 2FA etkinleÅŸtirildi + two_factor_recovery_codes_changed: + explanation: Önceki kurtarma kodları geçersiz kılındı ve yenileri oluÅŸturuldu. + subject: 'Mastodon: İki-adımlı kurtarma kodları yeniden oluÅŸturuldu' + title: 2FA kurtarma kodları deÄŸiÅŸtirildi + unlock_instructions: + subject: 'Mastodon: Engel kaldırma talimatları' + omniauth_callbacks: + failure: '%{kind}''den kimliÄŸiniz doÄŸrulanamadı çünkü "%{reason}".' + success: "%{kind} hesabından baÅŸarıyla kimlik doÄŸrulaması yapıldı." + passwords: + no_token: Bu sayfaya ÅŸifre sıfırlama e-postasından gelmeden eriÅŸemezsiniz. Åžifre sıfırlama e-postasından geliyorsanız lütfen saÄŸlanan tam URL'yi kullandığınızdan emin olun. + send_instructions: E-posta adresiniz veritabanımızda varsa, e-posta adresinize birkaç dakika içinde bir parola kurtarma baÄŸlantısı gönderilir. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + send_paranoid_instructions: E-posta adresiniz veritabanımızda varsa, e-posta adresinize birkaç dakika içinde bir parola kurtarma baÄŸlantısı gönderilir. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + updated: Parolanız baÅŸarıyla deÄŸiÅŸtirildi. Åžuan oturumunuz açıldı. + updated_not_active: Parolanız baÅŸarıyla deÄŸiÅŸtirildi. + registrations: + destroyed: Görüşürüz! hesabın baÅŸarıyla iptal edildi. Umarız seni sonra tekrar görürüz. + signed_up: HoÅŸ geldiniz! BaÅŸarılı bir ÅŸekilde oturum açtınız. + signed_up_but_inactive: BaÅŸarıyla kaydoldun. Ancak, seni içeri alamıyoruz çünkü hesabın henüz aktif deÄŸil. + signed_up_but_locked: BaÅŸarıyla kaydoldun. Ancak, seni içeri alamıyoruz çünkü hesabın kilitli. + signed_up_but_pending: Onay baÄŸlantısına sahip bir mesaj e-posta adresinize gönderildi. BaÄŸlantıyı tıkladıktan sonra baÅŸvurunuzu inceleyeceÄŸiz. Onaylanması durumunda size bilgi verilecektir. + signed_up_but_unconfirmed: Onay baÄŸlantısına sahip bir mesaj e-posta adresinize gönderildi. Lütfen hesabınızı etkinleÅŸtirmek için baÄŸlantıyı takip edin. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + update_needs_confirmation: Hesabınızı baÅŸarıyla güncellediniz, ancak yeni e-posta adresinizi doÄŸrulamamız gerekiyor. Lütfen e-postanızı kontrol edin ve yeni e-posta adresinizi onaylamak için onay baÄŸlantısını izleyin. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + updated: Hesabınız baÅŸarıyla güncellendi. + sessions: + already_signed_out: BaÅŸarıyla çıkış yapıldı. + signed_in: BaÅŸarıyla giriÅŸ yapıldı. + signed_out: BaÅŸarıyla çıkış yapıldı. + unlocks: + send_instructions: Hesabınızı birkaç dakika içinde nasıl açacağınıza iliÅŸkin talimatları içeren bir e-posta alacaksınız. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + send_paranoid_instructions: Hesabınız varsa, birkaç dakika içinde nasıl kilidini açacağınıza iliÅŸkin talimatları içeren bir e-posta alacaksınız. Bu e-postayı almadıysanız, lütfen spam klasörünüzü kontrol edin. + unlocked: Hesabınızın kilidi baÅŸarıyla açıldı. Devam etmek için lütfen oturum açın. + errors: + messages: + already_confirmed: zaten onaylanmış, lütfen tekrar oturum açmayı deneyin + confirmation_period_expired: "%{period} içinde onaylanması gerekli, lütfen yeni bir tane talep edin" + expired: süresi geçti, lütfen yeni bir tane talep edin + not_found: bulunamadı + not_locked: kilitlenmemiÅŸ + not_saved: + one: '1 hata bu %{resource} kaydedilmesini önledi:' + other: "%{count} hata bu %{resource} kaydedilmesini önledi:" diff --git a/config/locales/devise.uk.yml b/config/locales/devise.uk.yml index 6ec01b3d7cdd97fbebbe2339ab9e257e17b97369..e1eb2d1ae4333c4db15603171922334aee1d5c87 100644 --- a/config/locales/devise.uk.yml +++ b/config/locales/devise.uk.yml @@ -7,26 +7,53 @@ uk: send_paranoid_instructions: Якщо Ваша поштова Ñкринька Ñ–Ñнує в нашій базі даних, то Ви отримаєте лиÑта з інÑтрукціÑми щодо Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð’Ð°ÑˆÐ¾Ñ— адреÑи через декілька хвилин. failure: already_authenticated: Ви вже увійшли. - inactive: Ваш акаунт ще не активований. + inactive: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ‰Ðµ не активований. invalid: Ðеправильний %{authentication_keys} або пароль. - last_attempt: У Ð²Ð°Ñ Ñ” оÑÑ‚Ð°Ð½Ð½Ñ Ñпроба, піÑÐ»Ñ Ñкої вхід буде заблокований. - locked: Ваш акаунт заблокований. + last_attempt: У Ð²Ð°Ñ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ð»Ð°ÑÑŒ ще одна Ñпроба, піÑÐ»Ñ Ñкої ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ´Ðµ заблоковано. + locked: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾. not_found_in_database: Ðеправильний %{authentication_keys} або пароль. + pending: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ‰Ðµ перебуває на розглÑді. timeout: Ваша ÑеÑÑ–Ñ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð°Ð½Ð°. Будь лаÑка, зайдіть знову, щоб продовжити. unauthenticated: Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð’Ð°Ð¼ потрібно увійти або зареєÑтруватиÑÑ. unconfirmed: Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð’Ð°Ð¼ потрібно підтвердити Вашу поштову Ñкриньку. mailer: confirmation_instructions: + action: Підтвердити адреÑу електронної пошти + action_with_app: Підтвердити та повернутиÑÑ Ð´Ð¾ %{app} + explanation: Ви Ñтворили обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° %{host} з цією адреÑою електронної пошти, Ñ– зараз на відÑтані одного кліку від його активації. Якщо це були не ви, проігноруйте цього лиÑта, будь лаÑка. + extra_html: Також переглÑньте <a href="%{terms_path}">правила Ñерверу</a> та <a href="%{policy_path}">умови викориÑтаннÑ</a>. subject: 'Mastodon: ІнÑтрукції Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ %{instance}' + title: Підтвердити адреÑу електронної пошти + email_changed: + explanation: 'ÐдреÑа електронної пошти Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу змінюєтьÑÑ Ð½Ð°:' + extra: Якщо ви не змінювали Ñвою адреÑу електронної пошти, то хтоÑÑŒ вірогідно отримав доÑтуп до вашого облікового запиÑу. Будь лаÑка, негайно змініть Ñвій пароль або зв'ÑжітьÑÑ Ð· адмініÑтратором Ñерверу, Ñкщо ви не маєте доÑтупу до Ñвого облікового запиÑу. + subject: 'Mastodon: адреÑу електронної пошти змінено' + title: Ðова адреÑа електронної пошти password_change: + explanation: Пароль до вашого облікового запиÑу був змінений. + extra: Якщо ви не змінювали Ñвій пароль, то хтоÑÑŒ вірогідно отримав доÑтуп до вашого облікового запиÑу. Будь лаÑка, негайно змініть Ñвій пароль або зв'ÑжітьÑÑ Ð· адмініÑтратором Ñерверу, Ñкщо ви не маєте доÑтупу до Ñвого облікового запиÑу. subject: 'Mastodon: Ваш пароль змінений' + title: Пароль змінено + reconfirmation_instructions: + explanation: Підтвердіть нову адреÑу електронної пошти, щоб змінити поточну. + extra: Якщо Ñ†Ñ Ð·Ð¼Ñ–Ð½Ð° не була ініційована вами, проігноруйте цього лиÑта. ÐдреÑа електронної пошти Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу Mastodon не змінитьÑÑ, доки ви не перейдете за вищевказаним поÑиланнÑм. + subject: 'Mastodon: Підтвердіть електронну адреÑу Ð´Ð»Ñ %{instance}' + title: Підтвердіть адреÑу електронної пошти reset_password_instructions: + action: Змінити пароль + explanation: Ви запитали новий пароль Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу. + extra: Якщо ви не запитували зміну паролÑ, ігноруйте цей лиÑÑ‚. Ваш пароль не буде змінено, допоки ви не перейдете за поÑиланнÑм та не Ñтворите новий. subject: 'Mastodon: ІнÑтрукції Ð´Ð»Ñ ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ' + title: Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ + two_factor_disabled: + title: Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð° + two_factor_enabled: + title: Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð° unlock_instructions: subject: 'Mastodon: ІнÑтрукції Ð´Ð»Ñ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ' omniauth_callbacks: failure: Ðам не вдалоÑÑ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÑƒÐ²Ð°Ñ‚Ð¸ Ð’Ð°Ñ Ð· %{kind} через те, що "%{reason}". - success: УÑпішно аутентифіковано з акаунту %{kind}. + success: УÑпішно аутентифіковано з облікового запиÑу %{kind}. passwords: no_token: Ви не можете отримати доÑтуп до цієї Ñторінки без переходу за поÑиланнÑм з лиÑта з інÑтрукціÑми. Якщо ви дійÑно перейшли з цього лиÑта, переконайтеÑÑ, що ви перейшли за повним поÑиланнÑм. send_instructions: Ви отримаєте лиÑта з інÑтрукціÑми щодо зміни паролю через декілька хвилин. @@ -34,21 +61,22 @@ uk: updated: Ваш пароль було уÑпішно змінено. Вхід виконано. updated_not_active: Ваш пароль було уÑпішно змінено. registrations: - destroyed: До побаченнÑ! Ваш акаунт було уÑпішно видалено. СподіваємоÑÑŒ, що Ви Ñкоро повернетеÑÑ. + destroyed: До побаченнÑ! Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ уÑпішно видалено. СподіваємоÑÑŒ, Ви Ñкоро повернетеÑÑ. signed_up: ЛаÑкаво проÑимо! Ви були уÑпішно зареєÑтровані. - signed_up_but_inactive: Ви були уÑпішно зареєÑтровані, але ми не можемо авторизувати ваÑ, оÑкільки Ваш акаунт ще не активовано. - signed_up_but_locked: Ви були уÑпішно зареєÑтровані, аале ми не можемо авторизувати ваÑ, оÑкільки Ваш акаунт заблокований. - signed_up_but_unconfirmed: ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· поÑиланнÑм на Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ð¾ відправлено на Вашу поштову Ñкриньку. Будь лаÑка, перейдіть за поÑиланнÑм, щоб активувати Ваш акаунт. - update_needs_confirmation: Ви уÑпішно оновили Ваш акаунт, але нам потрібно підтвердити Вашу нову поштову адреÑу. Будь лаÑка, перевірте Вашу Ñкриньку та перейдіть за поÑиланнÑм, щоб активувати Вашу нову адреÑу. - updated: Ваш акаунт було уÑпішно оновлено. + signed_up_but_inactive: Ви були уÑпішно зареєÑтровані, але ми не можемо авторизувати ваÑ, оÑкільки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ‰Ðµ не активовано. + signed_up_but_locked: Ви були уÑпішно зареєÑтровані, але ми не можемо авторизувати ваÑ, оÑкільки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¸Ð¹. + signed_up_but_pending: ЛиÑÑ‚ з поÑиланнÑм Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ надіÑлано на вашу електронну пошту. Коли ви перейдете за цим поÑиланнÑм, ми розглÑнемо ваш запит. Ð’Ð°Ñ Ð±ÑƒÐ´Ðµ проінформовано, Ñкщо запит буде Ñхвалено. + signed_up_but_unconfirmed: ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· поÑиланнÑм на Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ð¾ відправлено на вашу адреÑу електронної пошти. Будь лаÑка, перейдіть за поÑиланнÑм, щоб активувати ваш обліковий запиÑ. Якщо ви не отримали цього лиÑта, перевірте теку зі Ñпамом у вашій Ñкринці. + update_needs_confirmation: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾, але необхідно підтвердити нову адреÑу електронної пошти. Будь лаÑка, перевірте Ñвою електронну Ñкриньку Ñ– перейдіть за поÑиланнÑм "Підтвердити", шоб завершити Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð°Ð´Ñ€ÐµÑи електронної пошти. Якщо ви не отримали цього лиÑта, перевірте теку зі Ñпамом у вашій Ñкринці. + updated: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ уÑпішно оновлено. sessions: already_signed_out: Ви вже вийшли з акаунту. signed_in: Ви уÑпішно увійшли в акаунт. signed_out: Ви уÑпішно вийшли з акаунту. unlocks: - send_instructions: Ви отримаєте лиÑта з інÑтрукціÑми щодо Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð’Ð°ÑˆÐ¾Ð³Ð¾ акаунту через декілька хвилин. - send_paranoid_instructions: Якщо Ваш акаунт Ñ–Ñнує, Ви отримаєте лиÑта з інÑтрукціÑми щодо Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð’Ð°ÑˆÐ¾Ð³Ð¾ акаунту через декілька хвилин. - unlocked: Ваш акаунт було уÑпішно активовано. Будь лаÑка, увійдіть, щоб продовжити. + send_instructions: За кілька хвилин ви отримаєте електронного лиÑта з інÑтрукціÑми щодо Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу. Якщо ви не отримали цього лиÑта, перевірте теку зі Ñпамом у вашій Ñкринці. + send_paranoid_instructions: Якщо ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ–Ñнує, за декілька хвилин ви отримаєте електронного лиÑта з інÑтрукціÑми щодо його розблокуваннÑ. Якщо ви не отримали цього лиÑта, перевірте теку зі Ñпамом у вашій Ñкринці. + unlocked: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ уÑпішно розблоковано. Будь лаÑка, увійдіть Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ. errors: messages: already_confirmed: вже був підтверджений, будь лаÑка, Ñпробуйте увійти @@ -57,7 +85,7 @@ uk: not_found: не знайдено not_locked: не був заблокований not_saved: - few: "%{count} помилок не дало цьому %{resource} зберегтиÑÑ:" + few: "%{count} помилки не дали цьому %{resource} зберегтиÑÑ:" many: "%{count} помилок не дало цьому %{resource} зберегтиÑÑ:" one: '1 помилка не дала цьому %{resource} зберегтиÑÑ:' other: "%{count} помилок не дало цьому %{resource} зберегтиÑÑ:" diff --git a/config/locales/devise.zh-CN.yml b/config/locales/devise.zh-CN.yml index 22fa130f60d49d54179b4b04abe36c399344eb3f..f9943238e3360e99ea6e804474b95eac20a4a702 100644 --- a/config/locales/devise.zh-CN.yml +++ b/config/locales/devise.zh-CN.yml @@ -58,7 +58,7 @@ zh-CN: updated: ä½ çš„å¯†ç 已修改æˆåŠŸï¼Œä½ çŽ°åœ¨å·²ç™»å½•ã€‚ updated_not_active: ä½ çš„å¯†ç 已修改æˆåŠŸã€‚ registrations: - destroyed: å†è§ï¼ä½ çš„å¸æˆ·å·²æˆåŠŸæ³¨é”€ã€‚æˆ‘ä»¬å¸Œæœ›å¾ˆå¿«å¯ä»¥å†è§åˆ°ä½ 。 + destroyed: å†è§ï¼ä½ çš„å¸æˆ·å·²æˆåŠŸé”€æ¯ã€‚我们希望很快å¯ä»¥å†è§åˆ°ä½ 。 signed_up: 欢迎ï¼ä½ 已注册æˆåŠŸã€‚ signed_up_but_inactive: ä½ å·²æ³¨å†Œï¼Œä½†å°šæœªæ¿€æ´»å¸æˆ·ã€‚ signed_up_but_locked: ä½ å·²æ³¨å†Œï¼Œä½†å¸æˆ·è¢«é”定了。 diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index cb989630eeb30aa1ad368ddb575f3e06503af050..895405f4a7151fb3a2ea94f0bd7ff7ac2fb0418b 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -46,6 +46,18 @@ zh-TW: extra: 若您並未請求,請忽略æ¤ä¿¡ä»¶ã€‚您的密碼在å˜å–上方連çµä¸¦å»ºç«‹æ–°é€£çµå‰ä¸æœƒè®Šæ›´ã€‚ subject: Mastodon:é‡è¨å¯†ç¢¼æŒ‡å¼• title: é‡è¨å¯†ç¢¼ + two_factor_disabled: + explanation: 您帳戶的兩æ¥é©Ÿé©—è‰å·²åœç”¨ã€‚ç¾åœ¨åªèƒ½ä½¿ç”¨é›»åä¿¡ç®±ä½å€åŠå¯†ç¢¼ç™»å…¥ã€‚ + subject: Mastodon:已åœç”¨å…©æ¥é©Ÿé©—è‰ + title: å·²åœç”¨ 2FA + two_factor_enabled: + explanation: 已尿‚¨çš„帳戶啟用兩æ¥é©Ÿé©—è‰ã€‚登入時將需è¦é…å°ä¹‹ TOTP æ‡‰ç”¨ç¨‹å¼æ‰€ç”¢ç”Ÿçš„ Token。 + subject: Mastodon:已啟用兩æ¥é©Ÿé©—è‰ + title: 已啟用 2FA + two_factor_recovery_codes_changed: + explanation: 上一次的復原碼已經失效,且已產生新的復原碼。 + subject: Mastodon:兩æ¥é©Ÿé©—è‰å¾©åŽŸç¢¼å·²ç¶“é‡æ–°ç”¢ç”Ÿ + title: 2FA 復原碼已變更 unlock_instructions: subject: Mastodon:帳戶解鎖指引 omniauth_callbacks: diff --git a/config/locales/doorkeeper.ar.yml b/config/locales/doorkeeper.ar.yml index 6f9e38f8b3c6e3c0a2b75ee0963bbcc1217f3c9c..51d8b76b434c2368a0c0f18fa4e1df170af2e917 100644 --- a/config/locales/doorkeeper.ar.yml +++ b/config/locales/doorkeeper.ar.yml @@ -113,6 +113,11 @@ ar: application: title: طلب ØªØµØ±ÙŠØ Ù…ÙØªÙˆØ OAuth scopes: + admin:read: قراءة ÙƒØ§ÙØ© البيانات على الخادم + admin:read:accounts: قراءة المعلومات Ø§Ù„ØØ³Ø§Ø³Ø© عن ÙƒØ§ÙØ© Ø§Ù„ØØ³Ø§Ø¨Ø§Øª + admin:write: تعديل ÙƒØ§ÙØ© البيانات على الخادم + admin:write:accounts: اتخاذ إجراءات إشرا٠على Ø§Ù„ØØ³Ø§Ø¨Ø§Øª + admin:write:reports: اتخاذ إجراءات إشرا٠على الإبلاغات follow: تعديل علاقات Ø§Ù„ØØ³Ø§Ø¨ push: تلقي إشعاراتك read: قراءة ÙƒØ§ÙØ© بيانات ØØ³Ø§Ø¨Ùƒ @@ -128,7 +133,7 @@ ar: read:search: Ø§Ù„Ø¨ØØ« مكانك read:statuses: رؤية ÙƒØ§ÙØ© المنشورات write: تغيير ÙƒØ§ÙØ© بيانات ØØ³Ø§Ø¨Ùƒ - write:accounts: تعديل ملÙÙƒ الشخصي + write:accounts: تعديل ØµÙØØªÙƒ التعريÙية write:blocks: ØØ¬Ø¨ Ø§Ù„ØØ³Ø§Ø¨Ø§Øª Ùˆ النطاقات write:favourites: الإعجاب بمنشورات write:filters: إنشاء عوامل تصÙية diff --git a/config/locales/doorkeeper.br.yml b/config/locales/doorkeeper.br.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7677c850c634b75b037ffd563bb1e1a6e36a5cf --- /dev/null +++ b/config/locales/doorkeeper.br.yml @@ -0,0 +1 @@ +br: diff --git a/config/locales/doorkeeper.cy.yml b/config/locales/doorkeeper.cy.yml index 19798c4d9f1198e52f23f8e5e34718927f46c1a2..e29043e86ffc3bfeadd16c03de03f584990ad98f 100644 --- a/config/locales/doorkeeper.cy.yml +++ b/config/locales/doorkeeper.cy.yml @@ -114,6 +114,12 @@ cy: application: title: Mae awdurdodiad OAuth yn ofynnol scopes: + admin:read: darllenwch yr holl ddata ar y serfiwr + admin:read:accounts: darllen gwybodaeth sensitif o'r holl gyfrifon + admin:read:reports: darllen gwybodaeth sensitif am bob adroddiad a chyfrifon yr adroddir amdanynt + admin:write: addasu pob data ar y serfiwr + admin:write:accounts: cyflawni camau cymedroli ar gyfrifon + admin:write:reports: cyflawni camau cymedroli ar adroddiadau follow: addasu perthnasau cyfrif push: derbyn eich hysbysiadau gwthiadwy read: darllen holl ddata eich cyfrif diff --git a/config/locales/doorkeeper.da.yml b/config/locales/doorkeeper.da.yml index b0f50a8931e8209fd93e01d95064ab3e38768882..4d2c95affed6b3acf725cfe5c0e2c535f6dd742f 100644 --- a/config/locales/doorkeeper.da.yml +++ b/config/locales/doorkeeper.da.yml @@ -72,6 +72,7 @@ da: index: application: Applikation created_at: Godkendt + date_format: "%Y-%m-%d %H:%M:%S" scopes: Omfang title: Dine godkendte applikationer errors: @@ -113,6 +114,12 @@ da: application: title: OAuth godkendelse pÃ¥krævet scopes: + admin:read: læs al data pÃ¥ serveren + admin:read:accounts: læs sensitiv information fra alle konti + admin:read:reports: læs sensitiv information fra alle anmeldelser og anmeldte konti + admin:write: redigér al data pÃ¥ serveren + admin:write:accounts: udfør modereringshandlinger pÃ¥ konti + admin:write:reports: udfør modereringshandlinger pÃ¥ anmeldelser follow: ændre din kontos forhold push: modtage dine push notifikationer read: læse alle din kontos data diff --git a/config/locales/doorkeeper.el.yml b/config/locales/doorkeeper.el.yml index c63688adeeef71868f704b45923f58db0878fc17..66bee0b0d835beec7beb3f38aa6951ff4712d08d 100644 --- a/config/locales/doorkeeper.el.yml +++ b/config/locales/doorkeeper.el.yml @@ -41,7 +41,7 @@ el: name: Όνομα new: ÎÎα εφαÏμογή scopes: ΕÏÏος εφαÏμογής - show: Εμφάνισε + show: Εμφάνιση title: Οι εφαÏμογÎÏ‚ σου new: title: ÎÎα εφαÏμογή diff --git a/config/locales/doorkeeper.eo.yml b/config/locales/doorkeeper.eo.yml index e80ba3236ae361737a0747bdd09cc9a7990ae76e..171e7d40464b46a4c9d6bba72490ecb8a1e834e5 100644 --- a/config/locales/doorkeeper.eo.yml +++ b/config/locales/doorkeeper.eo.yml @@ -114,6 +114,10 @@ eo: application: title: OAuth-a rajtigo bezonata scopes: + admin:read: legu ĉiujn datumojn en la servilo + admin:read:accounts: legas senteman informacion de ĉiuj kontoj + admin:read:reports: legas senteman informacion de ĉiuj raportoj kun raportis kontojn + admin:write: modifu ĉiujn datumojn en la servilo follow: ÅanÄi rilatojn al aliaj kontoj push: ricevi viajn puÅ-sciigojn read: legi ĉiujn datumojn de via konto diff --git a/config/locales/doorkeeper.es-AR.yml b/config/locales/doorkeeper.es-AR.yml new file mode 100644 index 0000000000000000000000000000000000000000..515d5c1ed90e1b9725f1a1648a82ee512d0575ea --- /dev/null +++ b/config/locales/doorkeeper.es-AR.yml @@ -0,0 +1 @@ +es-AR: diff --git a/config/locales/doorkeeper.es.yml b/config/locales/doorkeeper.es.yml index 752387d870d94ea205991cfbe72e0c1f7cb32b3f..1b03e33f2f086e82272a4829eab109ea89338920 100644 --- a/config/locales/doorkeeper.es.yml +++ b/config/locales/doorkeeper.es.yml @@ -114,7 +114,35 @@ es: application: title: OAuth autorización requerida scopes: + admin:read: leer todos los datos en el servidor + admin:read:accounts: leer información sensible de todas las cuentas + admin:read:reports: leer información sensible de todos los informes y cuentas reportadas + admin:write: modificar todos los datos en el servidor + admin:write:accounts: realizar acciones de moderación en cuentas + admin:write:reports: realizar acciones de moderación en informes follow: seguir, bloquear, desbloquear y dejar de seguir cuentas + push: recibir tus notificaciones push read: leer los datos de tu cuenta + read:accounts: ver información de cuentas + read:blocks: ver a quién has bloqueado + read:favourites: ver tus favoritos + read:filters: ver tus filtros + read:follows: ver a quién sigues + read:lists: ver tus listas + read:mutes: ver a quién has silenciado + read:notifications: ver tus notificaciones + read:reports: ver tus informes + read:search: buscar en su nombre + read:statuses: ver todos los estados write: publicar en tu nombre + write:accounts: modifica tu perfil write:blocks: bloquear cuentas y dominios + write:favourites: toots favoritos + write:filters: crear filtros + write:follows: seguir usuarios + write:lists: crear listas + write:media: subir archivos multimedia + write:mutes: silenciar usuarios y conversaciones + write:notifications: limpia tus notificaciones + write:reports: reportar a otras personas + write:statuses: publicar estados diff --git a/config/locales/doorkeeper.et.yml b/config/locales/doorkeeper.et.yml new file mode 100644 index 0000000000000000000000000000000000000000..f2a33ad0a85f859399509b4ab66ab2599529a04a --- /dev/null +++ b/config/locales/doorkeeper.et.yml @@ -0,0 +1,148 @@ +--- +et: + activerecord: + attributes: + doorkeeper/application: + name: Rakenduse nimi + redirect_uri: Suunamise URL + scopes: Ulatused + website: Rakenduse veebileht + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: ei tohi sisaldada fragmenti. + invalid_uri: peab olema õige URI. + relative_uri: peab olema absoluutne URI. + secured_uri: peab olema HTTPS/SSL URI. + doorkeeper: + applications: + buttons: + authorize: Autoriseeri + cancel: Katkesta + destroy: Hävita + edit: Redigeeri + submit: Saada + confirmations: + destroy: Olete kindel? + edit: + title: Redigeeri rakendust + form: + error: Oih! Kontrollige oma vormi võimalikest vigadest + help: + native_redirect_uri: Kasutage %{native_redirect_uri} kohalikeks testideks + redirect_uri: Kasuta ühte rida iga URI jaoks + scopes: Eralda ulatused tühikutega. Jäta tühjaks, et kasutada vaikimisi ulatusi. + index: + application: Rakendus + callback_url: Ümbersuunamise URL + delete: Kustuta + name: Nimi + new: Uus rakendus + scopes: Ulatused + show: Kuva + title: Teie rakendused + new: + title: Uus rakendus + show: + actions: Tegevused + application_id: Kliendi võti + callback_urls: Ümbersuunamise URLid + scopes: Ulatused + secret: Kliendi salasõna + title: 'Rakendus: %{name}' + authorizations: + buttons: + authorize: Autoriseeri + deny: Keeldu + error: + title: Ilmnes viga + new: + able_to: Sellel on võimalik + prompt: Rakendus %{client_name} soovib ligipääsu Teie kontole + title: Autoriseerimine vajalik + show: + title: Kopeeri see autoriseerimisvõti ja kleebi see rakendusse. + authorized_applications: + buttons: + revoke: Tühista + confirmations: + revoke: Olete kindel? + index: + application: Rakendus + created_at: Autoriseeritud + date_format: "%Y-%m-%d %H:%M:%S" + scopes: Ulatused + title: Autoriseeritud rakendused + errors: + messages: + access_denied: Ressursi omanik või autoriseerimisserver lükkas taotluse tagasi. + credential_flow_not_configured: Resource Owner Password Credentials vool ebaõnnestus, kuna Doorkeeper.configure.resource_owner_from_credentials ei ole seadistatud. + invalid_client: Kliendi autentimine ebaõnnestus, kuna tundmatu klient, puudulik autentimine või mitte toetatud autentimismeetod. + invalid_grant: Antud autoriseerimisluba on vale, aegunud, tagasi võetud, ei kattu kasutatud ümbersuunamise URLid või oli antud teisele kliendile. + invalid_redirect_uri: Antud ümbersuunamise URL ei ole õige. + invalid_request: Taotlusel puudub kohustuslik parameeter, sisaldab mitte toetatud parameetri väärtust või on kuidagi teisiti vale. + invalid_resource_owner: Antud ressursi omaniku andmed on valed või ressursi omanikku ei leitud + invalid_scope: Soovitud ulatus on vale, tundmatu või vigane. + invalid_token: + expired: Ligipääsuvõti on aegunud + revoked: Ligipääsuvõti on tagasi võetud + unknown: Ligipääsuvõti on vale + resource_owner_authenticator_not_configured: Ressursi omaniku leidmine ebaõnnestus, kuna Doorkeeper.configure.resource_owner_authenticator pole seadistatud. + server_error: Autoriseerimisserveril toimus ootamatu viga ning selle tulemusena ei läinud taotlus läbi. + temporarily_unavailable: Autoriseerimisserver ei ole hetkel võimeline taotlust vastu võtma, mis võib olla tingitud serveri ülekoormusest või hooldustöödest. + unauthorized_client: Klient ei ole lubatud seda taotlust vastu võtma kasutades seda meetodit. + unsupported_grant_type: Autoriseerimisloa tüüp ei ole toetatud antud serveri poolt. + unsupported_response_type: Autoriseerimisserver ei toeta seda vastuse tüüpi. + flash: + applications: + create: + notice: Rakendus loodud. + destroy: + notice: Rakendus kustutatud. + update: + notice: Rakendus uuendatud. + authorized_applications: + destroy: + notice: Rakendus tagasi lükatud. + layouts: + admin: + nav: + applications: Rakendused + oauth2_provider: OAuth2 pakkuja + application: + title: OAuth autoriseerimine vajalik + scopes: + admin:read: lugeda kõiki andmeid serveril + admin:read:accounts: lugeda privaatset informatsiooni kõikidel kontodel + admin:read:reports: lugeda privaatset informatsiooni kõikide teavituste ja teavitatud kasutajate kohta + admin:write: muuta kõiki andmeid serveril + admin:write:accounts: teostada moderaatori tegevusi kontodel + admin:write:reports: teostada moderaatori tegevusi teavitustel + follow: muuta kontode suhteid + push: saada Teie teateid + read: lugeda kogu Teie konto andmeid + read:accounts: näha konto informatsiooni + read:blocks: näha Teie blokeeringuid + read:favourites: näha Teie lemmikuid + read:filters: näha Teie filtreid + read:follows: näha Teie jälgimisi + read:lists: näha Teie nimekirju + read:mutes: näha Teie vaigistusi + read:notifications: näha Teie teateid + read:reports: näha Teie teavitusi + read:search: otsida Teie nimel + read:statuses: näha kõiki staatuseid + write: redigeerida kogu Teie konto andmeid + write:accounts: redigeerida Teie profiili + write:blocks: blokeerida kontosid ja domeene + write:favourites: lisada staatuseid lemmikuks + write:filters: luua filtreid + write:follows: jälgida inimesi + write:lists: luua nimekirju + write:media: üles laadida meediafaile + write:mutes: vaigista inimesi ja vestluseid + write:notifications: puhasta Teie teateid + write:reports: teavitada teistest inimestest + write:statuses: avaldada staatuseid diff --git a/config/locales/doorkeeper.eu.yml b/config/locales/doorkeeper.eu.yml index f98babae657e3d4160e2c6c65f193e5dcb4c3ee0..70e52e8ad90133319d265120ca203eb489720f05 100644 --- a/config/locales/doorkeeper.eu.yml +++ b/config/locales/doorkeeper.eu.yml @@ -5,7 +5,7 @@ eu: doorkeeper/application: name: Aplikazioaren izena redirect_uri: Birbideratu URIa - scopes: Esparruak + scopes: Irismena website: Aplikazioaren webgunea errors: models: @@ -33,14 +33,14 @@ eu: help: native_redirect_uri: Erabili %{native_redirect_uri} proba lokaletarako redirect_uri: Erabili lerro bat URI bakoitzeko - scopes: Banandu esparruak espazioekin. Laga hutsik lehenetsitako esparruak erabiltzeko. + scopes: Banandu irismenak espazioekin. Laga hutsik lehenetsitako irismenak erabiltzeko. index: application: Aplikazioa callback_url: Itzulera URLa delete: Ezabatu name: Izena new: Aplikazio berria - scopes: Esparruak + scopes: Irismena show: Erakutsi title: Zure aplikazioak new: @@ -49,7 +49,7 @@ eu: actions: Ekintzak application_id: Bezeroaren gakoa callback_urls: Itzulera URL-ak - scopes: Esparruak + scopes: Irismena secret: Bezeroaren sekretua title: 'Aplikazioa: %{name}' authorizations: @@ -73,7 +73,7 @@ eu: application: Aplikazioa created_at: Baimenduta date_format: "%Y-%m-%d %H:%M:%S" - scopes: Esparruak + scopes: Irismena title: Zuk baimendutako aplikazioak errors: messages: @@ -114,6 +114,12 @@ eu: application: title: OAuth autorizazioa behar da scopes: + admin:read: zerbitzariko datu guztiak irakurri + admin:read:accounts: kontu guztien informazio sentsiblea irakurri + admin:read:reports: salaketa guztietako eta salatutako kontu guztietako informazio sentsiblea irakurri + admin:write: zerbitzariko datu guztiak aldatu + admin:write:accounts: kontuetan moderazio ekintzak burutu + admin:write:reports: salaketetan moderazio ekintzak burutu follow: aldatu kontuaren erlazioak push: jaso push jakinarazpenak read: irakurri zure kontuko datu guztiak diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml index 3525617680de88b67556d1215f8f57b93224c537..09908b89aab2de8ae764d5e71a35a66fac7d90d3 100644 --- a/config/locales/doorkeeper.fr.yml +++ b/config/locales/doorkeeper.fr.yml @@ -114,6 +114,12 @@ fr: application: title: Autorisation OAuth requise scopes: + admin:read: lire toutes les données du serveur + admin:read:accounts: lire les informations sensibles de tous les comptes + admin:read:reports: lire les informations sensibles de tous les signalements et des comptes signalés + admin:write: modifier toutes les données sur le serveur + admin:write:accounts: effectuer des actions de modération sur les comptes + admin:write:reports: effectuer des actions de modération sur les singnalements follow: modifier les relations avec les comptes push: recevoir vos notifications read: lire toutes les données de votre compte diff --git a/config/locales/doorkeeper.hu.yml b/config/locales/doorkeeper.hu.yml index 122392864d91feec13706834e5342c1ff90c7557..92b4e6839bb7a7d4284c80cecb6cdab42bd9c2bf 100644 --- a/config/locales/doorkeeper.hu.yml +++ b/config/locales/doorkeeper.hu.yml @@ -114,6 +114,12 @@ hu: application: title: OAuth engedély szükséges scopes: + admin:read: szerver minden adatának olvasása + admin:read:accounts: minden érzékeny fiókadat olvasása + admin:read:reports: minden bejelentés és bejelentett fiók érzékeny adatainak olvasása + admin:write: szerver minden adatának változtatása + admin:write:accounts: moderációs műveletek végzése fiókokon + admin:write:reports: moderációs műveletek végzése bejelentéseken follow: fiókok követése, letiltása, tiltás feloldása és követés abbahagyása push: push értesÃtések fogadása read: fiókod adatainak olvasása diff --git a/config/locales/doorkeeper.id.yml b/config/locales/doorkeeper.id.yml index 3f9dee2ac80173912c0bf3876c915d15385ad6a2..08d2470f054061d4bfbadb16e411809910af1bfe 100644 --- a/config/locales/doorkeeper.id.yml +++ b/config/locales/doorkeeper.id.yml @@ -62,6 +62,8 @@ id: able_to: Mempunyai akses untuk prompt: Aplikasi %{client_name} meminta akses pada akun anda title: Izin diperlukan + show: + title: Salin kode autorisasi dan tempelkan pada aplikasi. authorized_applications: buttons: revoke: Cabut izin @@ -70,6 +72,7 @@ id: index: application: Aplikasi created_at: Diizinkan pada + date_format: "%d-%m-%Y %H:%M:%S" scopes: Scope title: Aplikasi yang anda izinkan errors: @@ -111,6 +114,35 @@ id: application: title: Otorisasi OAuth diperlukan scopes: + admin:read: baca semua data di server + admin:read:accounts: baca informasi sensitif semua akun + admin:read:reports: baca informasi sensitif semua laporan dan akun terlapor + admin:write: ubah semua data di server + admin:write:accounts: lakukan aksi moderasi akun + admin:write:reports: lakukan aksi moderasi laporan follow: mengikuti, blokir, menghapus blokir, dan berhenti mengikuti akun + push: terima notifikasi dorong read: membaca data pada akun anda + read:accounts: lihat informasi akun + read:blocks: lihat blokiran Anda + read:favourites: lihat favorit Anda + read:filters: lihat saringan Anda + read:follows: lihat yang Anda ikuti + read:lists: lihat daftar Anda + read:mutes: lihat daftar bisu Anda + read:notifications: lihat notifikasi Anda + read:reports: lihat laporan Anda + read:search: cari atas nama Anda + read:statuses: lihat semua status write: memposting sebagai anda + write:accounts: ubah profil Anda + write:blocks: blokir akun dan domain + write:favourites: status favorit + write:filters: buat saringan + write:follows: ikuti orang + write:lists: buat daftar + write:media: unggah berkas media + write:mutes: bisukan orang dan percakapan + write:notifications: hapus notifikasi Anda + write:reports: laporkan orang lain + write:statuses: terbitkan status diff --git a/config/locales/doorkeeper.ko.yml b/config/locales/doorkeeper.ko.yml index 76e725debc8b0b17aa755c8ec9d9d809f50af470..8337325e24579045c9c8bd0ac7a5f89bccf983bc 100644 --- a/config/locales/doorkeeper.ko.yml +++ b/config/locales/doorkeeper.ko.yml @@ -78,12 +78,23 @@ ko: errors: messages: access_denied: 리소스 ì†Œìœ ìž ë˜ëŠ” 권한 부여 서버가 ìš”ì²ì„ 거부했습니다. + credential_flow_not_configured: Doorkeeper.configure.resource_owner_from_credentialsì˜ ì„¤ì •ì´ ë˜ì–´ìžˆì§€ 않아 리소스 ì†Œìœ ìž íŒ¨ìŠ¤ì›Œë“œ ìžê²©ì¦ëª…ì´ ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤. + invalid_client: 알 수 없는 í´ë¼ì´ì–¸íЏì´ê¸° ë•Œë¬¸ì— í´ë¼ì´ì–¸íЏ ì¸ì¦ì´ 실패하였습니다, í´ë¼ì´ì–¸íЏ ìžê²©ì¦ëª…ì´ í¬í•¨ë˜ì§€ 않았거나, ì§€ì› ë˜ì§€ 않는 메소드입니다. + invalid_grant: ì œê³µëœ ê¶Œí•œ 부여가 잘못ë˜ê±°ë‚˜, 만료ë˜ì—ˆê±°ë‚˜, 취소ë˜ì—ˆê±°ë‚˜, 권한 부여 ìš”ì²ì— ì‚¬ìš©ëœ ë¦¬ë””ë ‰ì…˜ URIê°€ ì¼ì¹˜í•˜ì§€ 않거나, 다른 í´ë¼ì´ì–¸íŠ¸ì— ì§€ì •ë˜ì—ˆìŠµë‹ˆë‹¤. invalid_redirect_uri: ë¦¬ë””ë ‰ì…˜ URIê°€ 올바르지 않습니다 invalid_request: ìš”ì²ì— 필요한 매개변수가 없거나, ì§€ì› ë˜ì§€ 않는 매개변수가 있거나, 형ì‹ì´ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. + invalid_resource_owner: ì œê³µ ëœ ë¦¬ì†ŒìŠ¤ ì†Œìœ ìž ìžê²©ì¦ëª…ì´ ì˜¬ë°”ë¥´ì§€ 않거나 리소스 ì†Œìœ ìžë¥¼ ì°¾ì„ ìˆ˜ 없습니다 + invalid_scope: ìš”ì²í•œ 범위가 올바르지 않거나, 알 수 없거나, 잘못 ëœ í˜•ì‹ìž…니다. invalid_token: expired: 액세스 í† í°ì´ 만료ë˜ì—ˆìŠµë‹ˆë‹¤. revoked: 액세스 í† í°ì´ 취소ë˜ì—ˆìŠµë‹ˆë‹¤. unknown: 액세스 í† í°ì´ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. + resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticatorê°€ ì„¤ì •ë˜ì§€ 않아 리소스 ì†Œìœ ìž ì°¾ê¸°ê°€ 실패하였습니다. + server_error: 권한 부여 ì„œë²„ì— ì˜ˆê¸°ì¹˜ ì•Šì€ ìƒíƒœê°€ ë°œìƒí•˜ì—¬, ìš”ì²ì„ ìˆ˜í–‰í• ìˆ˜ 없습니다. + temporarily_unavailable: 권한 부여 서버가 ì¼ì‹œì ì¸ ê³¼ë¶€í•˜ ë˜ëŠ” ìœ ì§€ 관리로 ì¸í•´ ìš”ì²ì„ 현재 ì²˜ë¦¬í• ìˆ˜ 없습니다. + unauthorized_client: í´ë¼ì´ì–¸íŠ¸ê°€ ì´ ë°©ë²•ì„ ì‚¬ìš©í•˜ì—¬ 해당 ìš”ì²ì„ 실행하ë„ë¡ í—ˆìš©ë˜ì§€ 않았습니다. + unsupported_grant_type: 권한 부여 ìœ í˜•ì´ ê¶Œí•œ 부여 ì„œë²„ì— ì˜í•´ ì§€ì›ë˜ì§€ 않습니다. + unsupported_response_type: 권한 부여 서버가 ì´ ì‘답 ìœ í˜•ì„ ì§€ì›í•˜ì§€ 않습니다. flash: applications: create: @@ -103,6 +114,12 @@ ko: application: title: OAuth ì¸ì¦ì´ 필요합니다 scopes: + admin:read: ì„œë²„ì˜ ëª¨ë“ ë°ì´í„° ì½ê¸° + admin:read:accounts: ëª¨ë“ ê³„ì •ì˜ ë¯¼ê°í•œ ì •ë³´ ì½ê¸° + admin:read:reports: ì‹ ê³ ì™€ ì‹ ê³ ëœ ê³„ì •ì˜ ë¯¼ê°í•œ ì •ë³´ ì½ê¸° + admin:write: ì„œë²„ì˜ ëª¨ë“ ë°ì´í„° ìˆ˜ì • + admin:write:accounts: ê³„ì •ì— ëª¨ë”ë ˆì´ì…˜ 조치 취하기 + admin:write:reports: ì‹ ê³ ì— ëª¨ë”ë ˆì´ì…˜ 조치 취하기 follow: ê³„ì •ì˜ ê´€ê³„ë¥¼ ìˆ˜ì • push: 푸시 ì•Œë¦¼ì„ ë°›ê¸° read: ê³„ì •ì˜ ëª¨ë“ ë°ì´í„°ë¥¼ ì½ê¸° diff --git a/config/locales/doorkeeper.mk.yml b/config/locales/doorkeeper.mk.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b9144a988941222eba3d4c61c19a62a8b057db4 --- /dev/null +++ b/config/locales/doorkeeper.mk.yml @@ -0,0 +1 @@ +mk: diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml index aa37ea190ad38e4bfaef1bc39e46e812c98effd8..1fabfc1239276d6455c1411a18f2d24907950ca0 100644 --- a/config/locales/doorkeeper.nl.yml +++ b/config/locales/doorkeeper.nl.yml @@ -114,6 +114,12 @@ nl: application: title: OAuth-autorisatie vereist scopes: + admin:read: lees alle gegevens op de server + admin:read:accounts: lees gevoelige informatie van alle accounts + admin:read:reports: lees gevoelige informatie van alle rapportages en gerapporteerde accounts + admin:write: wijzig alle gegevens op de server + admin:write:accounts: moderatieacties op accounts uitvoeren + admin:write:reports: moderatieacties op rapportages uitvoeren follow: relaties tussen accounts bewerken push: ontvang jouw pushmeldingen read: alle gegevens van jouw account lezen diff --git a/config/locales/doorkeeper.nn.yml b/config/locales/doorkeeper.nn.yml new file mode 100644 index 0000000000000000000000000000000000000000..777f4e600f65daa6a550f2cf0f04551a49cf4fcd --- /dev/null +++ b/config/locales/doorkeeper.nn.yml @@ -0,0 +1 @@ +nn: diff --git a/config/locales/doorkeeper.oc.yml b/config/locales/doorkeeper.oc.yml index d97c2f600e57ee91829b004411d248ec232ee904..e715cc7d52d8da36cab5a54f5a6b6eb40df0abfa 100644 --- a/config/locales/doorkeeper.oc.yml +++ b/config/locales/doorkeeper.oc.yml @@ -114,6 +114,12 @@ oc: application: title: Cal una autorizacion OAuth scopes: + admin:read: lectura de totas las donadas del servidor + admin:read:accounts: lectura de las informacions sensiblas dels comptes + admin:read:reports: lectura de las informacions sensiblas dels senhalaments e dels comptes senhalats + admin:write: modificacion de las donadas del servidor + admin:write:accounts: realizacion d’accions de moderacion suls comptes + admin:write:reports: realizacion d’accions suls senhalaments follow: modificar las relacions del compte push: recebre vòstras notificacions push read: legir totas las donadas de vòstre compte diff --git a/config/locales/doorkeeper.pt.yml b/config/locales/doorkeeper.pt-PT.yml similarity index 99% rename from config/locales/doorkeeper.pt.yml rename to config/locales/doorkeeper.pt-PT.yml index f21e84d17f34338898398ed1641d966334b95850..42068e0a0b1b6b05fcf11a714b636afcb17c756a 100644 --- a/config/locales/doorkeeper.pt.yml +++ b/config/locales/doorkeeper.pt-PT.yml @@ -1,5 +1,5 @@ --- -pt: +pt-PT: activerecord: attributes: doorkeeper/application: diff --git a/config/locales/doorkeeper.ru.yml b/config/locales/doorkeeper.ru.yml index ebe90a18939bd76e909f7087e426834ce7749d8b..bd493f793b9b2ff256cd46015018d0c54e776717 100644 --- a/config/locales/doorkeeper.ru.yml +++ b/config/locales/doorkeeper.ru.yml @@ -114,6 +114,12 @@ ru: application: title: ТребуетÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ OAuth scopes: + admin:read: читать вÑе данные на Ñервере + admin:read:accounts: читать конфиденциальную информацию вÑех аккаунтов + admin:read:reports: читать конфиденциальную информацию о вÑех жалобах и аккаунтах Ñ Ð¶Ð°Ð»Ð¾Ð±Ð°Ð¼Ð¸ + admin:write: модифицировать вÑе данные на Ñервере + admin:write:accounts: производить модерацию аккаунтов + admin:write:reports: производить модерацию жалоб follow: подпиÑыватьÑÑ, отпиÑыватьÑÑ, блокировать и разблокировать аккаунты push: принимать push-ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð’Ð°ÑˆÐµÐ³Ð¾ аккаунта read: читать данные Вашего аккаунта diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml index f54eb6d48dca1f3e22cf3b1b5746c8a15694e2fb..a08a386f2c247ef44fdd22fa72134cb99febf1c6 100644 --- a/config/locales/doorkeeper.sk.yml +++ b/config/locales/doorkeeper.sk.yml @@ -19,34 +19,34 @@ sk: doorkeeper: applications: buttons: - authorize: OveriÅ¥ - cancel: ZruÅ¡iÅ¥ + authorize: Autorizuj + cancel: ZruÅ¡ destroy: ZniÄiÅ¥ - edit: UpraviÅ¥ - submit: PoslaÅ¥ + edit: Uprav + submit: PoÅ¡li confirmations: destroy: Si si istý/á? edit: - title: UpraviÅ¥ aplikáciu + title: Uprav aplikáciu form: - error: No teda! Pozrite formulár pre prÃpadné chyby + error: No teda! Skontroluj formulár pre prÃpadné chyby help: - native_redirect_uri: Použite %{native_redirect_uri} pre lokálne testy - redirect_uri: Iba jedna URI na riadok - scopes: Oprávnenia oddeľujte medzerami. Nechajte prázdne pre Å¡tandardné oprávnenia. + native_redirect_uri: Použi %{native_redirect_uri} pre lokálne testy + redirect_uri: Použi jeden riadok pre každú URI + scopes: Oprávnenia oddeľuj medzerami. Nechaj prázdne pre Å¡tandardné oprávnenia. index: application: Aplikácia callback_url: Návratová URL - delete: ZmazaÅ¥ + delete: Vymaž name: Názov new: Nová aplikácia scopes: Oprávnenia - show: UkázaÅ¥ - title: VaÅ¡e aplikácie + show: Ukáž + title: Tvoje aplikácie new: title: Nová aplikácia show: - actions: Akcie + actions: Úkony application_id: KÄ¾ÃºÄ klienta callback_urls: Návratové URL adresy scopes: Oprávnenia @@ -54,7 +54,7 @@ sk: title: 'Aplikácia: %{name}' authorizations: buttons: - authorize: OveriÅ¥ + authorize: Over deny: Zamietni error: title: Nastala chyba @@ -91,7 +91,7 @@ sk: resource_owner_authenticator_not_configured: Resource Owner zlyhal pretože Doorkeeper.configure.resource_owner_authenticator nebol nakonfigurovaný. server_error: Nastala neoÄakávaná chyba na autorizaÄnom serveri ktorá zabránila vykonaÅ¥ požiadavku. temporarily_unavailable: AutorizaÄný server Å¥a teraz nemôže obslúžiÅ¥, pretože prebieha údržba alebo je doÄasne preÅ¥ažený. - unauthorized_client: Klient nie je autorizovaný vykonaÅ¥ danú požiadavku takouto metódou. + unauthorized_client: Klient nie je autorizovaný vykonaÅ¥ danú požiadavku týmto spôsobom. unsupported_grant_type: Tento typ oprávnenia nie je podporovaný autorizaÄným serverom. unsupported_response_type: AutorizaÄný server nepodporuje typ tejto odpovede. flash: @@ -113,6 +113,12 @@ sk: application: title: Požadovaná OAuth autorizácia scopes: + admin:read: prezeraj vÅ¡etky dáta na serveri + admin:read:accounts: prezeraj chúlostivé informácie na vÅ¡etkých úÄtoch + admin:read:reports: ÄÃtaj chulostivé informácie o vÅ¡etkých hláseniach a nahlásených úÄtoch + admin:write: uprav vÅ¡etky dáta na serveri + admin:write:accounts: urob moderovacie úkony na úÄtoch + admin:write:reports: urob moderovacie úkony voÄi hláseniam follow: uprav vzÅ¥ahy svojho úÄtu push: dostávaj oboznámenia ohľadom tvojho úÄtu na obrazovku read: prezri si vÅ¡etky dáta ohľadom svojho úÄetu diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml index 4fd246eff2fb3b3375e9aca13d4e5502fb90d7fb..47b11b7fde8ae6937efa0e4ab559eb06be8e8094 100644 --- a/config/locales/doorkeeper.sv.yml +++ b/config/locales/doorkeeper.sv.yml @@ -113,7 +113,35 @@ sv: application: title: OAuth-behörighet krävs scopes: + admin:read: läs all data pÃ¥ servern + admin:read:accounts: läs känslig information frÃ¥n alla konton + admin:read:reports: läs känslig information frÃ¥n alla rapporter och rapporterade konton + admin:write: ändra all data pÃ¥ servern + admin:write:accounts: utför alla aktiviteter för moderering pÃ¥ konton + admin:write:reports: utför alla aktiviteter för moderering i rapporter follow: följa, blockera, ta bort blockerade och sluta följa konton push: ta emot push aviseringar för ditt konto read: läsa dina kontodata + read:accounts: se kontoinformation + read:blocks: se dina block + read:favourites: se dina favoriter + read:filters: se dina filter + read:follows: se vem du följer + read:lists: se dina listor + read:mutes: se dina tystningar + read:notifications: se dina notifieringar + read:reports: se dina rapporter + read:search: sök Ã¥ dina vägnar + read:statuses: se alla statusar write: posta Ã¥t dig + write:accounts: ändra din profil + write:blocks: blockera konton och domäner + write:favourites: favoritmarkera statusar + write:filters: skapa filter + write:follows: följ människor + write:lists: skapa listor + write:media: ladda upp mediafiler + write:mutes: tysta människor och konversationer + write:notifications: rensa dina notifieringar + write:reports: rapportera andra människor + write:statuses: publicera statusar diff --git a/config/locales/doorkeeper.tr.yml b/config/locales/doorkeeper.tr.yml index 686a018e0a6a6355dc8b4ae02347ab199e21dc77..120689a3a8009fe82e023bb6dbe2f4d8383a13bb 100644 --- a/config/locales/doorkeeper.tr.yml +++ b/config/locales/doorkeeper.tr.yml @@ -4,7 +4,18 @@ tr: attributes: doorkeeper/application: name: Uygulama adı + redirect_uri: Yönlendirme URI'si + scopes: Kapsamlar website: Uygulama web sitesi + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: parça içeremez. + invalid_uri: geçerli bir URI olmalıdır. + relative_uri: mutlak bir URI olmalıdır. + secured_uri: HTTPS/SSL URI olması gerekir. doorkeeper: applications: buttons: @@ -17,3 +28,121 @@ tr: destroy: Emin misiniz? edit: title: Uygulamayı düzenle + form: + error: Tüh! Muhtemel hatalar için formunuzu kontrol edin + help: + native_redirect_uri: Yerel testler için %{native_redirect_uri} kullanın + redirect_uri: URl başına bir satır kullanın + scopes: Kapsamları boÅŸluklarla ayırın. Varsayılan kapsamları kullanmak için boÅŸ bırakın. + index: + application: Uygulama + callback_url: Geri Dönüş URL + delete: Sil + name: İsim + new: Yeni uygulama + scopes: Kapsam + show: Göster + title: Uygulamalarınız + new: + title: Yeni uygulama + show: + actions: Eylemler + application_id: İstemci anahtarı + callback_urls: Callback URL'si + scopes: Kapsamlar + secret: İstemci anahtarı + title: 'Uygulama: %{name}' + authorizations: + buttons: + authorize: Yetkilendir + deny: Reddet + error: + title: Bir hata oluÅŸtu + new: + able_to: Åžunları yapabilecek + prompt: "%{client_name} uygulaması hesabınıza eriÅŸim istiyor" + title: Yetkilendirme gerekli + show: + title: Bu yetki kodunu kopyalayın ve uygulamaya yapıştırın. + authorized_applications: + buttons: + revoke: İptal + confirmations: + revoke: Emin misiniz? + index: + application: Uygulama + created_at: Yetkili + date_format: "%Y-%m-%d %H:%M:%S" + scopes: Kapsamlar + title: Yetkili uygulamalarınız + errors: + messages: + access_denied: Kaynak sahibi veya yetkilendirme sunucusu isteÄŸi reddetti. + credential_flow_not_configured: Kaynak Sahibi Åžifresinin Bilgi akışı Doorkeeper.configure.resource_owner_from_credentials bilgilerinin yapılandırılmamış olması nedeniyle baÅŸarısız oldu. + invalid_client: İstemcinin kimlik doÄŸrulaması bilinmeyen istemci, istemci kimlik doÄŸrulamasının dahil olmaması veya desteklenmeyen kimlik doÄŸrulama yöntemi nedeniyle baÅŸarısız oldu. + invalid_grant: SaÄŸlanan yetkilendirme izni geçersiz, süresi dolmuÅŸ, iptal edilmiÅŸ, yetkilendirme isteÄŸinde kullanılan yönlendirme URI'siyle eÅŸleÅŸmiyor veya baÅŸka bir müşteriye verilmiÅŸ. + invalid_redirect_uri: Dahil edilmiÅŸ yönlendirme Uri'si geçersiz. + invalid_request: İstekte gerekli bir parametre eksik, desteklenmeyen bir parametre deÄŸeri içeriyor veya baÅŸka türlü hatalı biçimlendirilmiÅŸ. + invalid_resource_owner: SaÄŸlanan kaynak sahibi kimlik bilgileri geçerli deÄŸil veya kaynak sahibi bulunamıyor + invalid_scope: İstenen kapsam geçersiz, bilinmeyen veya hatalı biçimlendirilmiÅŸ olabilir. + invalid_token: + expired: EriÅŸim belirtecinin süresi dolmuÅŸ + revoked: EriÅŸim belirteci iptal edildi + unknown: EriÅŸim belirteci geçersiz + resource_owner_authenticator_not_configured: Kaynak Sahibi yapılandırılmamış Doorkeeper.configure.resource_owner_authenticator nedeniyle baÅŸarısız oldu. + server_error: Yetkilendirme sunucusu, isteÄŸi yerine getirmesini engelleyen beklenmeyen bir koÅŸulla karşılaÅŸtı. + temporarily_unavailable: Yetkilendirme sunucusu ÅŸu anda sunucunun geçici bir aşırı yüklenmesi veya bakımı nedeniyle isteÄŸi yerine getiremiyor. + unauthorized_client: İstemci bu yöntemi kullanarak bu isteÄŸi gerçekleÅŸtirmek için yetkili deÄŸil. + unsupported_grant_type: Yetkilendirme izni türü, yetkilendirme sunucusu tarafından desteklenmiyor. + unsupported_response_type: Yetkilendirme sunucusu bu yanıt türünü desteklemiyor. + flash: + applications: + create: + notice: Uygulama oluÅŸturuldu. + destroy: + notice: Uygulama silindi. + update: + notice: Uygulama güncellendi. + authorized_applications: + destroy: + notice: Uygulama iptal edildi. + layouts: + admin: + nav: + applications: Uygulamalar + oauth2_provider: OAuth2 SaÄŸlayıcısı + application: + title: OAuth yetkilendirme gerekli + scopes: + admin:read: sunucudaki tüm verileri oku + admin:read:accounts: tüm hesapların hassas bilgilerini oku + admin:read:reports: tüm raporların ve raporlanan hesapların hassas bilgilerini oku + admin:write: sunucudaki tüm verileri deÄŸiÅŸtirin + admin:write:accounts: hesaplar üzerinde denetleme eylemleri gerçekleÅŸtirin + admin:write:reports: raporlar üzerinde denetleme eylemleri gerçekleÅŸtirin + follow: hesap iliÅŸkilerini deÄŸiÅŸtirin + push: anlık bildirimlerizi alın + read: hesabınızın tüm verilerini okuyun + read:accounts: hesap bilgilerini gör + read:blocks: engellemelerinizi görün + read:favourites: favorilerini gör + read:filters: filtrelerinizi görün + read:follows: izlerini gör + read:lists: listelerinizi görün + read:mutes: sessize aldıklarınızı görün + read:notifications: bildirimlerinizi görün + read:reports: ÅŸikayetlerinizi görün + read:search: kendi adınıza arayın + read:statuses: tüm durumları görün + write: hesabınızın tüm verilerini deÄŸiÅŸtirin + write:accounts: profilini deÄŸiÅŸtir + write:blocks: hesapları ve alan adlarını engelleyin + write:favourites: favori durumlar + write:filters: filtre oluÅŸtur + write:follows: insanları takip et + write:lists: liste oluÅŸtur + write:media: medya dosyalarını yükle + write:mutes: insanları ve konuÅŸmaları sustur + write:notifications: bildirimlerinizi temizleyin + write:reports: diÄŸer insanları bildir + write:statuses: durumları yayınlayın diff --git a/config/locales/doorkeeper.uk.yml b/config/locales/doorkeeper.uk.yml index 305a5c1d6f53052587c4fe157fbf4272dc3ed715..6e5dc1e426ac34094233d8a297fcb8da263f0850 100644 --- a/config/locales/doorkeeper.uk.yml +++ b/config/locales/doorkeeper.uk.yml @@ -3,7 +3,7 @@ uk: activerecord: attributes: doorkeeper/application: - name: Ім'Ñ + name: Ðазва додатку redirect_uri: URI Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ scopes: Рамки website: Веб-Ñайт додатку @@ -13,9 +13,9 @@ uk: attributes: redirect_uri: fragment_present: не може міÑтити фрагмент. - invalid_uri: повинен бути дійÑним URI. - relative_uri: повинен бути абÑолютним URI. - secured_uri: повинен бути HTTPS/SSL URI. + invalid_uri: має бути дійÑним URI. + relative_uri: має бути абÑолютним URI. + secured_uri: має бути URI HTTPS/SSL. doorkeeper: applications: buttons: @@ -35,9 +35,13 @@ uk: redirect_uri: ВикориÑтовуйте одну Ñтрічку на URI scopes: ВідділÑйте облаÑті видимоÑті пробілами. Залишайте порожніми, щоб викориÑтовувати облаÑті видимоÑті за промовчуваннÑм. index: + application: Додаток callback_url: URL зворотнього виклику + delete: Видалити name: Ðазва new: Ðовий додаток + scopes: ОблаÑті видимоÑті + show: Показати title: Ваші додатки new: title: Ðовий додаток @@ -56,8 +60,10 @@ uk: title: СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° new: able_to: Він зможе - prompt: Податок %{client_name} проÑить доÑтупу до вашого акаунту + prompt: Додаток %{client_name} запитує доÑтуп до вашого облікового запиÑу title: Ðеобхідна Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ + show: + title: Скопіюйте цей код авторизації та вÑтавте його у додаток. authorized_applications: buttons: revoke: Відкликати авторизацію @@ -78,7 +84,7 @@ uk: invalid_redirect_uri: Включений URI Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ Ñ” дійÑним. invalid_request: У запиті відÑутній обов'Ñзковий параметр, міÑтитьÑÑ Ð½ÐµÐ¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÑƒÐ²Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð°, або він Ñформований неправильно. invalid_resource_owner: Ðадані дані влаÑника реÑурÑу не Ñ” дійÑними, або влаÑника реÑурÑу неможливо знайти - invalid_scope: Запитуваний дозвыд недійÑний, невідомий, або неправильно Ñформований. + invalid_scope: Запитуваний дозвіл недійÑний, невідомий, або неправильно Ñформований. invalid_token: expired: Токен доÑтупу проÑтрочено revoked: Токен доÑтупу було відкликано @@ -108,6 +114,34 @@ uk: application: title: Ðеобхідна Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ OAuth scopes: - follow: підпиÑуватиÑÑ, відпиÑуватиÑÑ, блокувати та розблоковувати акаунти - read: Читати дані Вашого акаунта - write: Публікувати від Вашого імені + admin:read: читати вÑÑ– дані на Ñервері + admin:read:accounts: читати конфіденційну інформацію уÑÑ–Ñ… акаунтів + admin:read:reports: читати дражливу інформацію уÑÑ–Ñ… Ñкарг та облікових запиÑів зі Ñкаргами + admin:write: модифікувати вÑÑ– дані на Ñервері + admin:write:accounts: модерувати облікові запиÑи + admin:write:reports: модерувати Ñкарги + follow: змінювати ÑтоÑунки облікового запиÑу + push: отримувати Ваші Push-Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ + read: читати уÑÑ– дані вашого облікового запиÑу + read:accounts: бачити інформацію про облікові запиÑи + read:blocks: бачити Ваші Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ + read:favourites: бачити Ваші вподобані поÑти + read:filters: бачити Ваші фільтри + read:follows: бачити Ваші підпиÑки + read:lists: бачити Ваші ÑпиÑки + read:mutes: бачити ваші Ð·Ð°Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ + read:notifications: бачити Ваші ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ + read:reports: бачити Ваші Ñкарги + read:statuses: бачити вÑÑ– ÑтатуÑи + write: змінювати уÑÑ– дані вашого облікового запиÑу + write:accounts: змінювати ваш профіль + write:blocks: блокувати облікові запиÑи Ñ– домени + write:favourites: вподобані ÑтатуÑи + write:filters: Ñтворювати фільтри + write:follows: підпиÑуйтеÑÑŒ на людей + write:lists: Ñтворювайте ÑпиÑки + write:media: завантажити медіафайли + write:mutes: заглушити людей або беÑіди + write:notifications: очищувати Ваші ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ + write:reports: надіÑлати Ñкаргу про людей + write:statuses: публікувати ÑтатуÑи diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml index dd9337904acc1017dee5e19b7dc11a8b6f9292db..015d2c0ce1c9eb92ba89c488a3fa74089f7c9e07 100644 --- a/config/locales/doorkeeper.zh-CN.yml +++ b/config/locales/doorkeeper.zh-CN.yml @@ -72,6 +72,7 @@ zh-CN: index: application: 应用 created_at: æŽˆæƒæ—¶é—´ + date_format: "%H:%M:%S" scopes: æƒé™èŒƒå›´ title: 已授æƒçš„应用列表 errors: diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml index 41dd17264072aa3d507391c25d6f9a3722dc034a..0b2624aa9582b3bbf620e746c0bf0d11fa5e6a1d 100644 --- a/config/locales/doorkeeper.zh-TW.yml +++ b/config/locales/doorkeeper.zh-TW.yml @@ -3,9 +3,9 @@ zh-TW: activerecord: attributes: doorkeeper/application: - name: å稱 + name: 應用程å¼å稱 redirect_uri: 釿–°å°Žå‘ URI - scopes: 權é™ç¯„åœ + scopes: ç¯„åœ website: 應用程å¼ç¶²é errors: models: @@ -13,9 +13,9 @@ zh-TW: attributes: redirect_uri: fragment_present: ä¸èƒ½åŒ…å« fragment。 - invalid_uri: 必需有æ£ç¢ºçš„ URI. - relative_uri: å¿…éœ€ç‚ºçµ•å° URI. - secured_uri: 必需使用有 HTTPS/SSL åŠ å¯†çš„ URI. + invalid_uri: å¿…é ˆæ˜¯æ£ç¢ºçš„ URI。 + relative_uri: å¿…é ˆç‚ºçµ•å° URI。 + secured_uri: å¿…é ˆæ˜¯ HTTPS/SSL URI。 doorkeeper: applications: buttons: @@ -29,29 +29,29 @@ zh-TW: edit: title: ç·¨è¼¯æ‡‰ç”¨ç¨‹å¼ form: - error: 唉呦ï¼è«‹æª¢æŸ¥è¡¨å–®éŒ¯èª¤è¨Šæ¯ + error: 唉呦ï¼è«‹çœ‹çœ‹è¡¨å–®ä»¥æŽ’查錯誤 help: - native_redirect_uri: 請使用 %{native_redirect_uri} 作測試 + native_redirect_uri: 請使用 %{native_redirect_uri} 作本機測試 redirect_uri: æ¯è¡Œè¼¸å…¥ä¸€å€‹ URI - scopes: 請用åŠå½¢ç©ºæ ¼åˆ†é–‹æ¬Šé™ç¯„åœ (scope)。留空表示使用é è¨çš„æ¬Šé™ç¯„åœã€‚ + scopes: 請用åŠå½¢ç©ºæ ¼åˆ†é–‹ç¯„åœã€‚空白表示使用é è¨çš„範åœã€‚ index: application: æ‡‰ç”¨ç¨‹å¼ callback_url: å›žå‚³ç¶²å€ delete: 刪除 name: å稱 new: æ–°å¢žæ‡‰ç”¨ç¨‹å¼ - scopes: 權é™ç¯„åœ + scopes: ç¯„åœ show: 顯示 title: ä½ çš„æ‡‰ç”¨ç¨‹å¼ new: title: æ–°å¢žæ‡‰ç”¨ç¨‹å¼ show: actions: 動作 - application_id: æ‡‰ç”¨ç¨‹å¼ ID + application_id: 客戶端金鑰 callback_urls: å›žå‚³ç¶²å€ - scopes: 權é™ç¯„åœ - secret: 密碼 - title: 應用程å¼ï¸° %{name} + scopes: ç¯„åœ + secret: 客戶端密碼 + title: 應用程å¼ï¸°%{name} authorizations: buttons: authorize: 授權 @@ -59,61 +59,90 @@ zh-TW: error: title: 發生錯誤 new: - able_to: è¦æ±‚å–å¾—æ¬Šé™ - prompt: æ‡‰ç”¨ç¨‹å¼ %{client_name} è¦æ±‚å–å¾—æ‚¨å¸³è™Ÿçš„éƒ¨ä»½æ¬Šé™ + able_to: 這將å…許其作: + prompt: æ‡‰ç”¨ç¨‹å¼ %{client_name} è¦æ±‚å–得您帳號的å˜å–æ¬Šé™ title: éœ€è¦æŽˆæ¬Š show: title: è¤‡è£½æ¤æŽˆæ¬Šç¢¼ä¸¦è²¼ä¸Šåˆ°æ‡‰ç”¨ç¨‹å¼ä¸ã€‚ authorized_applications: buttons: - revoke: 撤銷授權 + revoke: 撤銷 confirmations: - revoke: æ‚¨ç¢ºå®šè¦æ’¤éŠ·é€™å€‹æŽˆæ¬Šï¼Ÿ + revoke: 確定撤銷? index: application: æ‡‰ç”¨ç¨‹å¼ - created_at: 授權時間 - scopes: 權é™ç¯„åœ + created_at: 授權於 + date_format: "%Y-%m-%d %H:%M:%S" + scopes: ç¯„åœ title: å·²æŽˆæ¬Šçš„æ‡‰ç”¨ç¨‹å¼ errors: messages: - access_denied: è³‡æºæ“有者或èªè‰ä¼ºæœå™¨ä¸æŽ¥å—請求。 - credential_flow_not_configured: è³‡æºæ“有者密碼èªè‰ç¨‹åºå¤±æ•—,由於 Doorkeeper.configure.resource_owner_from_credentials 沒有è¨å®šã€‚ - invalid_client: 客戶端驗è‰å¤±æ•—,å¯èƒ½æ˜¯æœªçŸ¥çš„客戶端程å¼ã€æœªåŒ…å«å®¢æˆ¶ç«¯é©—è‰ã€æˆ–ä½¿ç”¨äº†ä¸æ”¯æ´çš„èªè‰æ–¹æ³•。 + access_denied: è³‡æºæŒæœ‰è€…或授權伺æœå™¨æ‹’絕請求。 + credential_flow_not_configured: å› ç‚º Doorkeeper.configure.resource_owner_from_credentials 未è¨å®šï¼Œæ‰€ä»¥è³‡æºæŒæœ‰è€…密碼èªè‰ç¨‹åºå¤±æ•—。 + invalid_client: 客戶端驗è‰å¤±æ•—,å¯èƒ½æ˜¯å› 為未知的客戶端程å¼ã€æœªåŒ…å«å®¢æˆ¶ç«¯é©—è‰ã€æˆ–ä½¿ç”¨äº†ä¸æ”¯æ´çš„èªè‰æ–¹æ³•。 invalid_grant: æŽˆæ¬Šç”³è«‹ä¸æ£ç¢ºã€é€¾æœŸã€å·²è¢«å–消ã€èˆ‡æŽˆæ¬Šè«‹æ±‚å…§çš„é‡æ–°å°Žå‘ URI ä¸ç¬¦ã€æˆ–屬於別的客戶端程å¼ã€‚ - invalid_redirect_uri: 䏿£ç¢ºçš„釿–°å°Žå‘ç¶²å€ã€‚ - invalid_request: 請求缺少必è¦çš„åƒæ•¸ã€åŒ…å«ä¸æ”¯æ´çš„åƒæ•¸ã€æˆ–其他輸入錯誤。 - invalid_resource_owner: è³‡æºæ“æœ‰è€…çš„ç™»å…¥è³‡è¨ŠéŒ¯èª¤ã€æˆ–ç„¡æ³•æ‰¾åˆ°è©²è³‡æºæ“有者 - invalid_scope: 請求的權é™ç¯„åœç„¡æ•ˆã€æœªå®šç¾©ã€æˆ–輸入錯誤。 + invalid_redirect_uri: 包å«çš„釿–°å°Žå‘ URI æ˜¯ä¸æ£ç¢ºçš„。 + invalid_request: 請求缺少必è¦çš„åƒæ•¸ã€æœ‰ä¸æ”¯æ´çš„åƒæ•¸ã€æˆ–å…¶ä»–æ ¼å¼éŒ¯èª¤ã€‚ + invalid_resource_owner: è³‡æºæ“æœ‰è€…çš„ç™»å…¥è³‡è¨ŠéŒ¯èª¤ï¼Œæˆ–ç„¡æ³•æ‰¾åˆ°è©²è³‡æºæ“有者 + invalid_scope: 請求的範åœéŒ¯èª¤ã€æœªå®šç¾©ã€æˆ–æ ¼å¼éŒ¯èª¤ã€‚ invalid_token: - expired: access token å·²éŽæœŸ - revoked: access token å·²è¢«å–æ¶ˆ - unknown: access token 䏿£ç¢º - resource_owner_authenticator_not_configured: ç„¡æ³•æ‰¾åˆ°è³‡æºæ“有者,由於 Doorkeeper.configure.resource_owner_authenticator 沒有è¨å®šã€‚ + expired: å˜å–憑è‰å·²éŽæœŸ + revoked: å˜å–憑è‰å·²æ’¤éŠ· + unknown: å˜å–憑è‰ä¸æ£ç¢º + resource_owner_authenticator_not_configured: å› ç‚ºæœªè¨å®š Doorkeeper.configure.resource_owner_authenticatorï¼Œæ‰€ä»¥è³‡æºæŒæœ‰è€…尋找失敗。 server_error: èªè‰ä¼ºæœå™¨ç™¼ç”ŸæœªçŸ¥éŒ¯èª¤ã€‚ temporarily_unavailable: èªè‰ä¼ºæœå™¨æš«æ™‚無法使用。 - unauthorized_client: 客戶端程å¼ç„¡æ¬Šä½¿ç”¨æ¤æ–¹æ³•進行請求。 + unauthorized_client: å®¢æˆ¶ç«¯ç¨‹å¼æ²’有權é™ä½¿ç”¨æ¤æ–¹æ³•請求。 unsupported_grant_type: èªè‰ä¼ºæœå™¨ä¸æ”¯æ´é€™å€‹æŽˆæ¬Šé¡žåž‹ã€‚ unsupported_response_type: èªè‰ä¼ºæœå™¨ä¸æ”¯æ´é€™å€‹å›žæ‡‰é¡žåž‹ã€‚ flash: applications: create: - notice: 已新增應用程å¼ã€‚ + notice: 已建立應用程å¼ã€‚ destroy: notice: 已刪除應用程å¼ã€‚ update: notice: 已更新應用程å¼ã€‚ authorized_applications: destroy: - notice: å·²æ’¤éŠ·æ‡‰ç”¨ç¨‹å¼æŽˆæ¬Šã€‚ + notice: 已撤銷應用程å¼ã€‚ layouts: admin: nav: applications: æ‡‰ç”¨ç¨‹å¼ - oauth2_provider: OAuth2 供應者 + oauth2_provider: OAuth2 æä¾›è€… application: title: éœ€è¦ OAuth 授權 scopes: - follow: 關注ã€å°éŽ–ã€è§£é™¤å°éŽ–åŠå–消關注帳號 - push: æŽ¥æ”¶ä½ å¸³è™Ÿçš„æŽ¨é€é€šçŸ¥ - read: è®€å–æ‚¨çš„帳號資料 - write: 以您的å義發佈嘟文 + admin:read: 讀å–伺æœå™¨çš„æ‰€æœ‰è³‡æ–™ + admin:read:accounts: è®€å–æ‰€æœ‰å¸³æˆ¶çš„æ•æ„Ÿè³‡è¨Š + admin:read:reports: è®€å–æ‰€æœ‰å›žå ± / è¢«å›žå ±ä¹‹å¸³æˆ¶çš„æ•æ„Ÿè³‡è¨Š + admin:write: 修改伺æœå™¨çš„æ‰€æœ‰è³‡æ–™ + admin:write:accounts: å°å¸³æˆ¶é€²è¡Œä»²è£ç®¡ç†å‹•作 + admin:write:reports: å°å ±å‘Šé€²è¡Œä»²è£ç®¡ç†å‹•作 + follow: 修改帳戶關係 + push: 接收帳號的推é€é€šçŸ¥ + read: è®€å–æ‚¨æ‰€æœ‰çš„帳號資料 + read:accounts: 檢視帳戶資訊 + read:blocks: 檢視您的å°éŽ–åå–® + read:favourites: 檢視您的收è—é …ç›® + read:filters: æª¢è¦–æ‚¨çš„éŽæ¿¾æ¢ä»¶ + read:follows: 檢視您關注的人 + read:lists: 檢視您的åå–® + read:mutes: 檢視您éœéŸ³çš„人 + read:notifications: 檢視您的通知 + read:reports: 檢視您的檢舉 + read:search: ä»¥ä½ çš„èº«ä»½æœå°‹ + read:statuses: 檢視所有嘟文 + write: 修改您帳號的所有資料 + write:accounts: 修改您的個人檔案 + write:blocks: å°éŽ–å¸³æˆ¶åŠç«™å° + write:favourites: æ”¶è—嘟文 + write:filters: å»ºç«‹éŽæ¿¾æ¢ä»¶ + write:follows: 關注其他人 + write:lists: 建立åå–® + write:media: 上傳媒體檔案 + write:mutes: éœéŸ³ä½¿ç”¨è€…åŠå°è©± + write:notifications: 清除您的通知 + write:reports: 檢舉其他人 + write:statuses: 發布嘟文 diff --git a/config/locales/el.yml b/config/locales/el.yml index a08ec71416f5f88c0cc0faf0250cfb45ed858d7c..caaab41f870641bb899fb62f53e4051e27758fb2 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -10,20 +10,19 @@ el: api: API apps: ΕφαÏμογÎÏ‚ κινητών apps_platforms: ΧÏησιμοποίησε το Mastodon από το iOS, το Android και Î±Î»Î»Î¿Ï - browse_directory: ΞεφÏλλισε Îνα κατάλογο χÏηστών και φίλτÏαÏε ανά ενδιαφÎÏοντα - browse_public_posts: Κοίταξε μια ζωντανή Ïοή δημοσιεÏσεων στο Mastodon + browse_directory: ΞεφÏλλισε τον κατάλογο χÏηστών και ψάξε ανά ενδιαφÎÏοντα + browse_public_posts: ΞεφÏλλισε τη ζωντανή Ïοή του Mastodon contact: Επικοινωνία contact_missing: Δεν Îχει οÏιστεί contact_unavailable: Μ/Δ discover_users: Ανακάλυψε χÏήστες documentation: ΤεκμηÏίωση - extended_description_html: | - <h3>Ένα καλό σημείο για κανόνες</h3> - <p>Η αναλυτική πεÏιγÏαφή δεν Îχει ακόμα οÏιστεί</p> federation_hint_html: Με Îνα λογαÏιασμό στο %{instance} θα μποÏείς να ακολουθείς ανθÏώπους σε οποιοδήποτε κόμβο στο Mastodon αλλά και αλλοÏ. - generic_description: "%{domain} είναι Îνας εξυπηÏετητής στο δίκτυο" get_apps: Δοκίμασε μια εφαÏμογή ÎºÎ¹Î½Î·Ï„Î¿Ï hosted_on: Το Mastodon φιλοξενείται στο %{domain} + instance_actor_flash: | + Αυτός ο λογαÏιασμός είναι εικονικός και απεικονίζει τον κόμβο, όχι κάποιο συγκεκÏιμÎνο χÏήστη. + ΧÏησιμεÏει στη λειτουÏγία της ομοσπονδίας και δε θα Ï€ÏÎπει να αποκλειστεί, εκτός κι αν είναι επιθυμητός ο αποκλεισμός ολόκληÏου του κόμβου. Σε αυτή την πεÏίπτωση θα Ï€ÏÎπει να χÏησιμοποιηθεί η λειτουÏγία Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï Ï„Î¿Î¼Îα. learn_more: Μάθε πεÏισσότεÏα privacy_policy: Πολιτική αποÏÏήτου see_whats_happening: Μάθε τι συμβαίνει @@ -35,6 +34,14 @@ el: status_count_before: Που ÎγÏαψαν tagline: ΑκολοÏθησε τους γνωστοÏÏ‚ σου και ανακάλυψε νÎους ανθÏώπους terms: ÎŒÏοι χÏήσης + unavailable_content: Μη διαθÎσιμο + unavailable_content_description: + domain: Διακομιστής + reason: 'Αιτία:' + rejecting_media: 'Τα αÏχεία πολυμÎσων αυτών των διακομιστών δεν θα επεξεÏγάζονται, δεν θα αποθηκεÏονται και δεν θα εμφανίζεται η Ï€Ïοεπισκόπησή τους, απαιτώντας χειÏοκίνητη επιλογή μÎχÏι το αÏχικό αÏχείο:' + silenced: 'Οι δημοσιεÏσεις αυτών των διακομιστών θα είναι κÏυμμÎνες από τις δημόσιες ÏοÎÏ‚ και συζητήσεις, ενώ δεν θα δημιουÏγοÏνται ειδοποιήσεις για τις ενÎÏγειες των χÏηστών τους, εκτός κι αν τους ακολουθείς:' + suspended: 'ΚανÎνα δεδομÎνο δε θα επεξεÏγάζεται, δε θα αποθηκεÏεται και δε θα ανταλλάσσεται για αυτοÏÏ‚ τους διακομιστÎÏ‚, καθιστώντας οποιαδήποτε αλληλεπίδÏαση ή επικοινωνία με χÏήστες από αυτοÏÏ‚ τους διακομιστÎÏ‚ αδÏνατη:' + unavailable_content_html: Το Mastodon γενικά επιτÏÎπει να δεις πεÏιεχόμενο και να αλληλεπιδÏάσεις με χÏήστες από οποιονδήποτε διακομιστή στο fediverse. Εδώ είναι οι εξαιÏÎσεις που ισχÏουν σε αυτόν τον συγκεκÏιμÎνο διακομιστή. user_count_after: one: χÏήστης other: χÏήστες @@ -42,6 +49,8 @@ el: what_is_mastodon: Τι είναι το Mastodon; accounts: choices_html: 'ΕπιλογÎÏ‚ από %{name}:' + endorsements_hint: ΜποÏεις να εγκÏίνεις ανθÏώπους που ακολουθείς μÎσω της δικτυακής εφαÏμογής και αυτοί θα εμφανίζονται εδώ. + featured_tags_hint: ΜποÏείς να επιλÎξεις συγκεκÏιμÎνες ταμπÎλες που θα εμφανίζονται εδώ. follow: ΑκολοÏθησε followers: one: Ακόλουθος @@ -53,6 +62,7 @@ el: media: ΠολυμÎσα moved_html: 'Ο/Η %{name} μετακόμισε στο %{new_profile_link}:' network_hidden: Αυτή η πληÏοφοÏία δεν είναι διαθÎσιμη + never_active: ΠοτΠnothing_here: Δεν υπάÏχει τίποτα εδώ! people_followed_by: ΧÏήστες που ακολουθεί ο/η %{name} people_who_follow: ΧÏήστες που ακολουθοÏν τον/την %{name} @@ -183,6 +193,7 @@ el: username: Όνομα χÏήστη warn: Î Ïοειδοποίηση web: Διαδίκτυο + whitelisted: ΕγκεκÏιμÎνοι action_logs: actions: assigned_to_self_report: Ο/Η %{name} ανάθεσε την καταγγελία %{target} στον εαυτό του/της @@ -218,19 +229,24 @@ el: deleted_status: "(διαγÏαμμÎνη δημοσίευση)" title: ΑÏχείο ελÎγχου custom_emojis: + assign_category: ΚατηγοÏία by_domain: ΤομÎας copied_msg: Επιτυχής δημιουÏγία Ï„Î¿Ï€Î¹ÎºÎ¿Ï Î±Î½Ï„Î¯Î³Ïαφου του emoji copy: ΑντιγÏαφή copy_failed_msg: Αδυναμία δημιουÏγίας Ï„Î¿Ï€Î¹ÎºÎ¿Ï Î±Î½Ï„Î¯Î³Ïαφου Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… emoji + create_new_category: ÎÎα κατηγοÏία created_msg: Επιτυχής δημιουÏγία του emoji! delete: ΔιαγÏαφή destroyed_msg: Επιτυχής καταστÏοφή του emojo! disable: ΑπενεÏγοποίηση + disabled: ΑπενεÏγοποιημÎνα disabled_msg: Επιτυχής απενεÏγοποίηση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… emoji emoji: Emoji enable: ΕνεÏγοποίηση + enabled: ΕνεÏγοποιημÎνα enabled_msg: Επιτυχής ενεÏγοποίηση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… emoji image_hint: PNG Îως 50KB + list: Εμφάνιση listed: ΑναφεÏθÎντα new: title: Î Ïοσθήκη νÎου Ï€ÏοσαÏμοσμÎνου emoji @@ -238,11 +254,14 @@ el: shortcode: ΣÏντομος κωδικός shortcode_hint: Τουλάχιστον 2 χαÏακτήÏες, μόνο αλφαÏιθμητικοί και κάτω παÏλες title: Î ÏοσαÏμοσμÎνα emoji + uncategorized: ΧωÏίς κατηγοÏία + unlist: ΑπόκÏυψη unlisted: Μη καταχωÏημÎνα update_failed_msg: Αδυναμία ενημÎÏωσης του emoji updated_msg: Επιτυχής ενημÎÏωση του emoji! upload: ΑνÎβασμα dashboard: + authorized_fetch_mode: ΛειτουÏγία εγκεκÏιμÎνης ανάκτησης backlog: χÏονοκαθυστεÏημÎνες εÏγασίες config: ΔιαμόÏφωση feature_deletions: ΔιαγÏαφή λογαÏιασμών @@ -250,10 +269,13 @@ el: feature_profile_directory: Κατάλογος χÏηστών feature_registrations: ΕγγÏαφÎÏ‚ feature_relay: ΑνταποκÏιτής ομοσπονδίας + feature_spam_check: ΚαταπολÎμηση ανεπιθÏμητης επικοινωνίας feature_timeline_preview: Î Ïοεπισκόπιση Ïοής features: ΛειτουÏγίες hidden_service: Ομοσπονδία με κÏυμμÎνες υπηÏεσίες open_reports: ανοιχτÎÏ‚ καταγγελίες + pending_tags: ταμπÎλες Ï€Ïος ÎγκÏιση + pending_users: χÏήστες Ï€Ïος ÎγκÏιση recent_users: Î Ïόσφατοι χÏήστες search: Αναζήτηση πλήÏους κειμÎνου single_user_mode: ΛειτουÏγία μοναδιαίου χÏήστη @@ -265,11 +287,18 @@ el: week_interactions: αλληλεπιδÏάσεις αυτή την εβδομάδα week_users_active: ενεÏγοί αυτή την εβδομάδα week_users_new: χÏήστες αυτή την εβδομάδα + whitelist_mode: ΛειτουÏγία εγκÏίσεων + domain_allows: + add_new: ΈγκÏιση τομÎα + created_msg: Ο τομÎας εγκÏίθηκε με επιτυχία + destroyed_msg: Ο τομÎας δεν είναι πια εγκεκÏιμÎνος + undo: ΑφαίÏεση ÎγκÏισης domain_blocks: add_new: Î Ïοσθήκη νÎου created_msg: Ο αποκλεισμός τομÎα είναι υπό επεξεÏγασία destroyed_msg: Ο αποκλεισμός τομÎα άÏθηκε domain: ΤομÎας + edit: ΔιαχείÏηση αποκλεισμÎνου τομÎα existing_domain_block_html: Έχεις ήδη επιβάλλει αυστηÏότεÏους πεÏιοÏισμοÏÏ‚ στο %{name}, Ï€Ïώτα θα Ï€ÏÎπει να τους <a href="%{unblock_url}">αναιÏÎσεις</a>. new: create: ΔημιουÏγία Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï @@ -280,6 +309,10 @@ el: silence: Σίγαση suspend: Αναστολή title: Αποκλεισμός νÎου τομÎα + private_comment: Ιδιωτικό σχόλιο + private_comment_hint: Σχόλιο για τον πεÏιοÏισμό Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… τομÎα για εσωτεÏική χÏήση από τους διαχειÏιστÎÏ‚. + public_comment: Δημόσιο σχόλιο + public_comment_hint: Σχόλιο σχετικά με τον πεÏιοÏισμό Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… τομÎα Ï€Ïος το κοινό, αν η λειτουÏγία δημοσιοποίησης των πεÏιοÏισμÎνων τομÎων είναι ενεÏγοποιημÎνη. reject_media: ΑπόÏÏιψη πολυμÎσων reject_media_hint: ΑφαιÏεί τα τοπικά αποθηκευμÎνα αÏχεία πολυμÎσων και αποτÏÎπει τη λήψη άλλων στο μÎλλον. Δεν Îχει σημασία για τις αναστολÎÏ‚ reject_reports: ΑπόÏÏιψη καταγγελιών @@ -299,6 +332,7 @@ el: title: ΑναίÏεση Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï Î³Î¹Î± τον τομÎα %{domain} undo: ΑναίÏεση undo: ΑναίÏεση + view: Εμφάνιση Î±Ï€Î¿ÎºÎ»ÎµÎ¹ÏƒÎ¼Î¿Ï Ï„Î¿Î¼Îα email_domain_blocks: add_new: Î Ïόσθεση νÎου created_msg: Επιτυχής Ï€Ïόσθεση email τομÎα σε μαÏÏη λίστα @@ -322,6 +356,8 @@ el: all: Όλα limited: ΠεÏιοÏισμÎνα title: Διαμεσολάβηση + private_comment: Ιδιωτικό σχόλιο + public_comment: Δημόσιο σχόλιο title: Γνωστοί κόμβοι total_blocked_by_us: ΜπλοκάÏονται από εμάς total_followed_by_them: ΑκολουθοÏνται από εκείνους @@ -351,6 +387,7 @@ el: pending: ΠεÏιμÎνοντας την ÎγκÏιση του ανταποκÏιτή save_and_enable: Αποθήκευση και ενεÏγοποίηση setup: ÎŒÏισε μια σÏνδεση ανταπόκÏισης + signatures_not_enabled: Οι ανταποκÏιτÎÏ‚ δεν θα λειτουÏγοÏν σωστά όσο είναι ενεÏγοποιημÎνες οι επιλογÎÏ‚ ασφαλοÏÏ‚ λειτουÏγίας ή επιτÏεπόμενων συνδÎσεων status: Κατάσταση title: ΑνταποκÏιτÎÏ‚ report_notes: @@ -399,6 +436,16 @@ el: custom_css: desc_html: ΤÏοποποίηση της εμφάνισης μÎσω CSS που φοÏτώνεται σε κάθε σελίδα title: Î ÏοσαÏμοσμÎνο CSS + default_noindex: + desc_html: ΕπηÏεάζει όσους χÏήστες δεν Îχουν αλλάξει αυτή τη ÏÏθμιση + title: ΕξαίÏεση χÏηστών από τις μηχανÎÏ‚ αναζήτησης + domain_blocks: + all: Για όλους + disabled: Για κανÎναν + title: Εμφάνιση αποκλεισμÎνων τομÎων + users: Î Ïος συνδεδεμÎνους τοπικοÏÏ‚ χÏήστες + domain_blocks_rationale: + title: Εμφάνιση ÏƒÎºÎµÏ€Ï„Î¹ÎºÎ¿Ï hero: desc_html: Εμφανίζεται στην μπÏοστινή σελίδα. Συνίσταται τουλάχιστον 600x100px. Όταν λείπει, χÏησιμοποιείται η μικÏογÏαφία του κόμβου title: Εικόνα ήÏωα @@ -449,6 +496,9 @@ el: desc_html: ΜποÏείς να γÏάψεις τη δική σου πολιτική αποÏÏήτου, ÏŒÏους χÏήσης ή άλλους νομικοÏÏ‚ ÏŒÏους. ΜποÏείς να χÏησιμοποιήσεις HTML tags title: Î ÏοσαÏμοσμÎνοι ÏŒÏοι χÏήσης της υπηÏεσίας site_title: Όνομα κόμβου + spam_check_enabled: + desc_html: Το Mastodon μποÏεί να αποσιωπεί και να καταγγÎλει αυτόματα λογαÏιασμοÏÏ‚ βάσει της συμπεÏιφοÏάς τους όπως για παÏάδειγμα επαναλαμβανόμενη αποστολή ανεπιθÏμητων μηνυμάτων. ΜποÏεί να υπάÏξουν και λανθασμÎνες ανιχνεÏσεις. + title: ΚαταπολÎμηση ανεπιθÏμητης επικοινωνίας thumbnail: desc_html: ΧÏησιμοποιείται για Ï€Ïοεπισκοπήσεις μÎσω του OpenGraph και του API. Συστήνεται 1200x630px title: ΜικÏογÏαφία κόμβου @@ -456,12 +506,19 @@ el: desc_html: Εμφάνισε τη δημόσια Ïοή στην αÏχική σελίδα title: Î Ïοεπισκόπιση Ïοής title: Ρυθμίσεις ιστότοπου + trendable_by_default: + desc_html: ΕπηÏεάζει τις ταμπÎλες που δεν είχαν απαγοÏευτεί νωÏίτεÏα + title: ΕπÎÏ„Ïεψε στις ταμπÎλες να εμφανιστοÏν στις τάσεις χωÏίς Ï€ÏοηγοÏμενη ÎγκÏιση + trends: + desc_html: Δημόσια εμφάνιση ταμπελών που Îχουν ήδη εγκÏιθεί και είναι δημοφιλείς + title: Δημοφιλείς ταμπÎλες statuses: back_to_account: ΕπιστÏοφή στη σελίδα λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï batch: delete: ΔιαγÏαφή nsfw_off: Σημείωσε ως μη ευαίσθητο nsfw_on: Σημείωσε ως ευαίσθητο + deleted: ΔιαγÏαμμÎνα failed_to_execute: Αποτυχία εκτÎλεσης media: title: ΠολυμÎσα @@ -469,21 +526,24 @@ el: no_status_selected: Καμία δημοσίευση δεν άλλαξε Î±Ï†Î¿Ï ÎºÎ±Î¼Î¯Î± δεν ήταν επιλεγμÎνη title: Καταστάσεις λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï with_media: Με πολυμÎσα - subscriptions: - callback_url: URL επιστÏοφής (Callback) - confirmed: ΕπιβεβαιωμÎνες - expires_in: Λήγει σε - last_delivery: Τελευταία παÏάδοση - title: Î Ïωτόκολλο WebSub - topic: ΘÎμα tags: - accounts: ΛογαÏιασμοί - hidden: ΚÏυμμÎνες - hide: ΑπόκÏυψη από κατάλογο + accounts_today: ΜοναδικÎÏ‚ χÏήσεις ημÎÏας + accounts_week: ΜοναδικÎÏ‚ χÏήσεις εβδομάδας + breakdown: Ανάλυση σημεÏινής χÏήσης ανα πηγή + context: Συνάφεια + directory: Στον κατάλογο + in_directory: "%{count} στον κατάλογο" + last_active: Τελευταία δÏαστηÏιότητα + most_popular: ΔημοφιλÎστεÏες + most_recent: Πιο Ï€Ïόσφατες name: ΤαμπÎλα + review: Κατάσταση ÎγκÏισης + reviewed: ΕγκεκÏιμÎνες title: ΤαμπÎλες - unhide: Εμφάνιση σε κατάλογο - visible: Εμφανείς + trending_right_now: Δημοφιλείς αυτή τη στιγμή + unique_uses_today: "%{count} σημεÏινÎÏ‚ δημοσιεÏσεις" + unreviewed: ΕκκÏεμεί ÎγκÏιση + updated_msg: Οι Ïυθμίσεις των ταμπελών ενημεÏώθηκαν επιτυχώς title: ΔιαχείÏιση warning_presets: add_new: Î Ïόσθεση νÎου @@ -499,11 +559,20 @@ el: body: Ο/Η %{reporter} κατήγγειλε τον/την %{target} body_remote: Κάποιος/α από τον τομÎα %{domain} κατήγγειλε τον/την %{target} subject: ÎÎα καταγγελία για %{instance} (#%{id}) + new_trending_tag: + body: 'Η ταμπÎλα #%{name} είναι δημοφιλής σήμεÏα, αλλά δεν Îχει εγκÏιθεί μÎχÏι τώÏα. Δεν θα εμφανίζεται δημοσίως μÎχÏι να δοθεί ÎγκÏισή, αλλιώς αποθηκεÏστε τη φόÏμα ως Îχει για να μην την δείτε ξανά.' + subject: ÎÎα ταμπÎλα Ï€Ïος ÎγκÏιση στο %{instance} (#%{name}) + aliases: + add_new: ΔημιουÏγία ψευδώνυμου + created_msg: ΔημιουÏγήθηκε νÎο ψευδώνυμο. ΤώÏα μποÏείς να ξεκινήσεις τη μεταφοÏά από τον παλιό λογαÏιασμό. + deleted_msg: ΑφαιÏÎθηκε το ψευδώνυμο. Η μεταφοÏά από εκείνον τον λογαÏιασμό σε αυτόν εδώ δε θα είναι πλÎον δυνατή. + remove: ΑφαίÏεση ψευδώνυμου appearance: advanced_web_interface: Î ÏοηγμÎνη λειτουÏγία χÏήσης advanced_web_interface_hint: 'Αν θÎλεις να χÏησιμοποιήσεις ολόκληÏο το πλάτος της οθόνης σου, η Ï€ÏοηγμÎνη λειτουÏγία χÏήσης σου επιτÏÎπει να οÏίσεις πολλαπλÎÏ‚ κολώνες ώστε να βλÎπεις ταυτόχÏονα όση πληÏοφοÏία θÎλεις: Την αÏχική Ïοή, τις ειδοποιήσεις, την ομοσπονδιακή Ïοή και όσες λίστες και ταμπÎλες θÎλεις.' animations_and_accessibility: Κίνηση και Ï€Ïοσβασιμότητα confirmation_dialogs: ΕÏωτήσεις επιβεβαίωσης + discovery: ΕξεÏεÏνηση sensitive_content: Ευαίσθητο πεÏιεχόμενο application_mailer: notification_preferences: Αλλαγή Ï€Ïοτιμήσεων email @@ -524,9 +593,13 @@ el: apply_for_account: Αίτηση Ï€Ïόσκλησης change_password: Συνθηματικό checkbox_agreement_html: Συμφωνώ με τους <a href="%{rules_path}" target="_blank">κανονισμοÏÏ‚ του κόμβου</a> και <a href="%{terms_path}" target="_blank">τους ÏŒÏους χÏήσης</a> - confirm_email: Επιβεβαίωση email + checkbox_agreement_without_rules_html: Συμφωνώ με τους <a href="%{terms_path}" target="_blank">ÏŒÏους χÏήσης</a> delete_account: ΔιαγÏαφή email delete_account_html: Αν θÎλεις να διαγÏάψεις το λογαÏιασμό σου, μποÏείς <a href="%{path}">να συνεχίσεις εδώ</a>. Θα σου ζητηθεί επιβεβαίωση. + description: + prefix_invited_by_user: Ο/Η @%{name} σε Ï€Ïοσκαλεί να συνδεθείς με αυτό τον διακομιστή του Mastodon! + prefix_sign_up: Άνοιξε λογαÏιασμό στο Mastodon σήμεÏα! + suffix: Ανοίγοντας λογαÏιασμό θα μποÏείς να ακολουθείς άλλους, να ανεβάζεις ενημεÏώσεις και να ανταλλάζεις μηνÏματα με χÏήστες σε οποιοδήποτε διακομιστή Mastodon, καθώς και άλλα! didnt_get_confirmation: Δεν Îλαβες τις οδηγίες επιβεβαίωσης; forgot_password: ΞÎχασες το συνθηματικό σου; invalid_reset_password_token: Το διακÏιτικό επαναφοÏάς ÏƒÏ…Î½Î¸Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï ÎµÎ¯Î½Î±Î¹ άκυÏο ή ληγμÎνο. ΠαÏακαλώ αιτήσου νÎο. @@ -544,6 +617,15 @@ el: reset_password: ΕπαναφοÏά ÏƒÏ…Î½Î¸Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï security: Ασφάλεια set_new_password: ΟÏισμός νÎου ÏƒÏ…Î½Î¸Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï + setup: + email_below_hint_html: Αν η παÏακάτω διεÏθυνση email είναι λανθασμÎνη, μποÏείτε να την ενημεÏώσετε και να λάβετε νÎο email επιβεβαίωσης. + email_settings_hint_html: Το email επιβεβαίωσης στάλθηκε στο %{email}. Αν η διεÏθυνση αυτή δεν είναι σωστή, μποÏείτε να την ενημεÏώσετε στις Ïυθμίσεις λογαÏιασμοÏ. + title: Ρυθμίσεις + status: + account_status: Κατάσταση λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï + confirming: Αναμονή για ολοκλήÏωση επιβεβαίωσης του email. + functional: Ο λογαÏιασμός σου είναι πανÎτοιμος. + pending: Η εφαÏμογή σας εκκÏεμεί ÎγκÏισης, πιθανόν θα διαÏκÎσει κάποιο χÏόνο. Θα λάβετε email αν εγκÏιθεί. trouble_logging_in: Î Ïόβλημα σÏνδεσης; authorize_follow: already_following: Ήδη ακολουθείς αυτό το λογαÏιασμό @@ -556,6 +638,10 @@ el: return: Δείξε το Ï€Ïοφίλ του χÏήστη web: Πήγαινε στο δίκτυο title: ΑκολοÏθησε %{acct} + challenge: + confirm: ΣυνÎχεια + invalid_password: Μη ÎγκυÏο συνθηματικό + prompt: Επιβεβαίωση ÏƒÏ…Î½Î¸Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï Î³Î¹Î± συνÎχεια datetime: distance_in_words: about_x_hours: "%{count}ω" @@ -571,26 +657,33 @@ el: x_months: "%{count}μ" x_seconds: "%{count}δ" deletes: - bad_password_msg: Καλή Ï€Ïοσπάθεια χάκεÏÏ‚! Λάθος συνθηματικό + challenge_not_passed: Τα στοιχεία δεν ήταν σωστά confirm_password: ΓÏάψε το Ï„ÏÎχον συνθηματικό σου για να πιστοποιήσεις την ταυτότητά σου - description_html: Αυτό θα διαγÏάψει <strong>οÏιστικά και αμετάκλητα</strong> το πεÏιεχόμενο του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ¿Ï… και θα τον απενεÏγοποιήσει. Το όνομα χÏήστη θα παÏαμείνει δεσμευμÎνο για να αποφευχθοÏν μελλοντικÎÏ‚ πλαστοπÏοσωπίες. + confirm_username: ΓÏάψε το όνομα χÏήστη σου για επιβεβαίωση proceed: ΔιαγÏαφή λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï success_msg: Ο λογαÏιασμός σου διαγÏάφηκε με επιτυχία - warning_html: Μόνο η διαγÏαφή πεÏιεχομÎνου από αυτό τον συγκεκÏιμÎνο κόμβο είναι εγγυημÎνη. Το πεÏιεχόμενο που Îχει διαμοιÏαστεί ευÏÎως είναι πιθανό να αφήσει ίχνη. Όσοι διακομιστÎÏ‚ είναι εκτός σÏνδεσης και όσοι Îχουν διακόψει τη λήψη των ενημεÏώσεων του κόμβου σου, δε θα ενημεÏώσουν τις βάσεις δεδομÎνων τους. - warning_title: Διαθεσιμότητα ήδη διανεμημÎνου πεÏιεχομÎνου + warning: + before: 'Î Ïιν συνεχίσεις, παÏακαλοÏμε να διαβάσεις τις παÏακάτω σημειώσεις Ï€Ïοσεκτικά:' + caches: Οποίο πεÏιεχόμενο Îχει αποθηκευτεί Ï€ÏοσωÏινά σε άλλους διακομιστÎÏ‚ μποÏεί να παÏαμείνει + data_removal: Οι δημοσιεÏσεις σου και άλλα δεδομÎνα θα διαγÏαφοÏν οÏιστικά + email_change_html: ΜποÏείς να <a href="%{path}">αλλάξεις τη διεÏθυνση email σου</a> ΧωÏίς να διαγÏάψεις το λογαÏιασμό σου + email_contact_html: Αν και πάλι δεν εμφανιστεί, μποÏείς να στείλεις email Ï€Ïος <a href="mailto:%{email}">%{email}</a> για βοήθεια + email_reconfirmation_html: Αν δεν Îχεις λάβει το email επιβεβαίωσης, μποÏείς να το <a href="%{path}">ζητήσεις ξανά</a> + irreversible: Δεν θα μποÏείς να ανακτήσεις ή ενεÏγοποιήσεις ξανά το λογαÏιασμό σου + more_details_html: Για πεÏισσότεÏες πληÏοφοÏίες, δες την <a href="%{terms_path}">πολιτική αποÏÏήτου</a>. + username_available: Το όνομα χÏήστη σου θα γίνει ξανά διαθÎσιμο + username_unavailable: Το όνομα χÏήστη σου θα παÏαμείνει μη διαθÎσιμο directories: directory: Κατάλογος λογαÏιασμών - enabled: ΠεÏιλαμβάνεσαι στον κατάλογο. - enabled_but_waiting: Έχεις επιλÎξει να εμφανίζεσαι στον κατάλογο μεν, αλλά ακόμα δεν Îχεις τον ελάχιστο αÏιθμό ακόλουθων (%{min_followers}) που απαιτείται για να συμπεÏιληφθείς. explanation: Î’Ïες χÏήστες βάσει των ενδιαφεÏόντων τους explore_mastodon: ΕξεÏεÏνησε το %{title} - how_to_enable: Δεν Îχεις επιλÎξει να συμπεÏιληφθείς στον καταλογο. ΜποÏείς να επιλÎξεις παÏακάτω. ΧÏησιμοποίησε ταμπÎλες στο κείμενο του βιογÏÎ±Ï†Î¹ÎºÎ¿Ï ÏƒÎ¿Ï… για να εμφανίζεσαι κάτω από συγκεκÏιμÎνες ταμπÎλες! - people: - one: "%{count} άτομο" - other: "%{count} άτομα" + domain_validator: + invalid_domain: δεν είναι ÎγκυÏο όνομα τομÎα errors: + '400': Το αίτημα ήταν άκυÏο ή με λάθος μοÏφή. '403': Δεν Îχεις δικαίωμα Ï€Ïόσβασης σε αυτή τη σελίδα. '404': Η σελίδα που ψάχνεις δεν υπάÏχει. + '406': Η σελίδα αυτή δεν είναι διαθÎσιμη στην αιτοÏμενη μοÏφή. '410': Η σελίδα που Îψαχνες δεν υπάÏχει πια εδώ. '422': content: ΑπÎτυχε η επιβεβαίωση ασφαλείας. Μήπως μπλοκάÏεις τα cookies; @@ -599,6 +692,7 @@ el: '500': content: ΛυποÏμαστε, κάτι πήγε στÏαβά από τη δική μας μεÏιά. title: Η σελίδα αυτή δεν είναι σωστή + '503': Η σελίδα δε μπόÏεσε να εμφανιστεί λόγω Ï€ÏοσωÏÎ¹Î½Î¿Ï ÏƒÏ†Î¬Î»Î¼Î±Ï„Î¿Ï‚ του διακομιστή. noscript_html: Για να χÏησιμοποιήσετε τη δικτυακή εφαÏμογή του Mastodon, ενεÏγοποίησε την Javascript. Εναλλακτικά, δοκίμασε μια από τις <a href="%{apps_path}">εφαÏμογÎÏ‚</a> για το Mastodon στην πλατφόÏμα σου. existing_username_validator: not_found: δεν βÏÎθηκε τοπικός χÏήστης με αυτό το όνομα @@ -616,12 +710,13 @@ el: domain_blocks: ΜπλοκαÏίσματα κόμβων follows: Ακολουθείς lists: Λίστες - mutes: Αποσιωπάς + mutes: Αποσιωπήσεις storage: Αποθήκευση πολυμÎσων featured_tags: add_new: Î Ïοσθήκη νÎας errors: limit: Έχεις ήδη Ï€ÏοσθÎσει το μÎγιστο αÏιθμό ταμπελών + hint_html: "<strong>Τι είναι οι Ï€ÏοβεβλημÎνες ταμπÎλες;</strong> Î Ïοβάλλονται στο δημόσιο Ï€Ïοφίλ σου επιτÏÎποντας σε όποιον το βλÎπει να χαζÎψει τις δημοσιεÏσεις που τις χÏησιμοποιοÏν. Είναι ωÏαίος Ï„Ïόπος να παÏακολουθείς κάποια δημιουÏγία ή Îνα μακÏοπÏόθεσμο ÎÏγο." filters: contexts: home: ΑÏχική Ïοή @@ -642,12 +737,14 @@ el: developers: Ανάπτυξη more: ΠεÏισσότεÏα… resources: Î ÏŒÏοι + trending_now: Τάσεις generic: all: Όλα changes_saved_msg: Οι αλλαγÎÏ‚ αποθηκεÏτηκαν! copy: ΑντιγÏαφή + no_batch_actions_available: Δεν υπάÏχουν ομαδικÎÏ‚ ενÎÏγειες σε αυτή τη σελίδα order_by: Ταξινόμηση κατά - save_changes: Αποθήκευσε τις αλλαγÎÏ‚ + save_changes: Αποθήκευση αλλαγών validation_errors: one: Κάτι δεν είναι εντάξει ακόμα! Για κοίταξε το παÏακάτω σφάλμα other: Κάτι δεν είναι εντάξει ακόμα! Για κοίταξε τα παÏακάτω %{count} σφάλματα @@ -717,9 +814,26 @@ el: too_many: Δεν γίνεται να Ï€ÏοσθÎσεις πεÏισσότεÏα από 4 αÏχεία migrations: acct: ΌνομαΧÏήστη@ΤομÎας του νÎου λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï - currently_redirecting: 'Το Ï€Ïοφίλ σου Îχει Ïυθμιστεί να ανακατευθÏνει στο:' - proceed: Αποθήκευση - updated_msg: Οι Ïυθμίσεις μετακόμισης του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ¿Ï… ενημεÏώθηκαν! + cancel: ΑκÏÏωση ανακατεÏθυνσης + errors: + move_to_self: δεν επιτÏÎπεται να είναι ο Ï„ÏÎχων λογαÏιασμός + not_found: δεν βÏÎθηκε + on_cooldown: Είσαι σε πεÏίοδο Ï€ÏοσαÏμογής + followers_count: Ακόλουθοι τη στιγμή της μεταφοÏάς + incoming_migrations: ΜεταφοÏά από διαφοÏετικό λογαÏιασμό + past_migrations: Î ÏοηγοÏμενες μετακινήσεις + proceed_with_move: Μετακίνηση ακολοÏθων + redirecting_to: Ο λογαÏιασμός σου ανακατευθÏνει στο %{acct}. + set_redirect: ÎŒÏισε ανακατεÏθυνση + warning: + backreference_required: Θα Ï€ÏÎπει Ï€Ïώτα να Ïυθμιστεί μια αναφοÏά από τον νÎο λογαÏιασμό Ï€Ïος αυτόν + before: 'Î Ïιν συνεχίσεις, παÏακαλοÏμε διάβασε αυτÎÏ‚ τις σημειώσεις Ï€Ïοσεκτικά:' + cooldown: Άπαξ και μεταφεÏθείς υπάÏχει μια πεÏίοδος Ï€ÏοσαÏμογής κατά την οποία δε θα μποÏείς να μετακινηθείς ξανά + disabled_account: Ο Ï„ÏÎχων λογαÏιασμός σου δε θα είναι πλήÏως ενεÏγός μετά. Πάντως θα Îχεις Ï€Ïόσβαση στην εξαγωγή δεδομÎνων καθώς και στην επανενεÏγοποίηση. + followers: Αυτή η ενÎÏγεια θα μεταφÎÏει όλους τους ακόλουθοÏÏ‚ σου από τον Ï„ÏÎχοντα λογαÏιασμός στον νÎο λογαÏιασμό + only_redirect_html: Εναλλακτικά, μποÏείς <a href="%{path}">απλά να Ï€ÏοσθÎσεις μια ανακατατεÏθυνση στο Ï€Ïοφίλ σου</a>. + other_data: ΚανÎνα άλλο δεδομÎνο δε θα μεταφεÏθεί αυτόματα + redirect: Το Ï€Ïοφίλ του Ï„ÏÎχοντος λογαÏιασμό σου θα ενημεÏωθεί με μια σημείωση ανακατεÏθυνσης και θα εξαιÏεθεί από τα αποτελÎσματα αναζητήσεων moderation: title: Συντονισμός notification_mailer: @@ -783,7 +897,7 @@ el: too_few_options: Ï€ÏÎπει να Îχει πεÏισσότεÏες από μια επιλογÎÏ‚ too_many_options: δεν μποÏεί να Îχει πεÏισσότεÏες από %{max} επιλογÎÏ‚ preferences: - other: Άλλο + other: Άλλες posting_defaults: Î ÏοεπιλογÎÏ‚ δημοσίευσης public_timelines: Δημόσιες ÏοÎÏ‚ relationships: @@ -816,10 +930,6 @@ el: reply: proceed: ΣυνÎχισε για να απαντήσεις prompt: 'ΘÎλεις να απαντήσεις σε αυτό το τουτ:' - remote_unfollow: - error: Σφάλμα - title: Τίτλος - unfollowed: Σταμάτησες να ακολουθείς scheduled_statuses: over_daily_limit: Έχεις υπεÏβεί το ÏŒÏιο των %{limit} Ï€ÏογÏαμματισμÎνων τουτ για εκείνη τη μÎÏα over_total_limit: Έχεις υπεÏβεί το ÏŒÏιο των %{limit} Ï€ÏογÏαμματισμÎνων τουτ @@ -868,6 +978,7 @@ el: settings: account: ΛογαÏιασμός account_settings: Ρυθμίσεις λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï + aliases: Ψευδώνυμα λογαÏιασμών appearance: Εμφάνιση authorized_apps: ΕγκεκÏιμÎνες εφαÏμογÎÏ‚ back: Πίσω στο Mastodon @@ -908,6 +1019,9 @@ el: private: Τα μη δημόσια τουτ δεν καÏφιτσώνονται reblog: Οι Ï€Ïοωθήσεις δεν καÏφιτσώνονται poll: + total_people: + one: "%{count} άτομο" + other: "%{count} άτομα" total_votes: one: "%{count} ψήφος" other: "%{count} ψήφοι" @@ -926,6 +1040,8 @@ el: pinned: ΚαÏφιτσωμÎνο τουτ reblogged: Ï€ÏοωθημÎνο sensitive_content: Ευαίσθητο πεÏιεχόμενο + tags: + does_not_match_previous_name: δεν ταιÏιάζει με το Ï€ÏοηγοÏμενο όνομα terms: body_html: | <h2>Πολιτική ΑποÏÏήτου</h2> @@ -1043,7 +1159,9 @@ el: disable: Όσο ο λογαÏιασμός σου είναι παγωμÎνος, τα στοιχεία του παÏαμÎνουν άθικτα αλλά δεν μποÏείς να κανείς καμία ενÎÏγεια μÎχÏι να ξεκλειδωθείς. silence: Όσο ο λογαÏιασμός σου είναι πεÏιοÏισμÎνος, μόνο όσοι σε ακολουθοÏν ήδη θα βλÎπουν τα τουτ σου σε αυτό τον κόμβο ενώ μποÏεί να εξαιÏεθείς από διάφοÏες δημόσιες απαÏιθμήσεις. Πάντως, θα μποÏοÏν να σε ακολουθήσουν χειÏοκίνητα. suspend: Ο λογαÏιασμός σου αναστάλθηκε μόνιμα, όλα τα τουτ και τα ανεβασμÎνα πολυμÎσα σου διαγÏάφηκαν αμετάκλητα από αυτόν τον κόμβο και σε όσους άλλους είχες ακόλουθους. + get_in_touch: ΜποÏείς να απαντήσεις σε αυτό το email για να επικοινωνήσεις με το Ï€Ïοσωπικό του %{instance}. review_server_policies: ΑναθεώÏηση πολιτικής του κόμβου + statuses: 'ΣυγκεκÏιμÎνα, για:' subject: disable: Ο λογαÏιασμός σου %{acct} Îχει παγώσει none: Î Ïοειδοποίηση Ï€Ïος %{acct} diff --git a/config/locales/en.yml b/config/locales/en.yml index 6fe82a412e1b2b09b9c664cecbca279e7bd24a9e..faf1d0dd1570cad510dd3b58eee5c00f90f30637 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,7 +2,7 @@ en: about: about_hashtag_html: These are public toots tagged with <strong>#%{hashtag}</strong>. You can interact with them if you have an account anywhere in the fediverse. - about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail. + about_mastodon_html: 'The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!' about_this: About active_count_after: active active_footnote: Monthly Active Users (MAU) @@ -17,13 +17,12 @@ en: contact_unavailable: N/A discover_users: Discover users documentation: Documentation - extended_description_html: | - <h3>A good place for rules</h3> - <p>The extended description has not been set up yet.</p> federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond. - generic_description: "%{domain} is one server in the network" get_apps: Try a mobile app hosted_on: Mastodon hosted on %{domain} + instance_actor_flash: | + This account is a virtual actor used to represent the server itself and not any individual user. + It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block. learn_more: Learn more privacy_policy: Privacy policy see_whats_happening: See what's happening @@ -35,6 +34,14 @@ en: status_count_before: Who authored tagline: Follow friends and discover new ones terms: Terms of service + unavailable_content: Unavailable content + unavailable_content_description: + domain: Server + reason: Reason + rejecting_media: 'Media files from these servers will not be processed or stored, and and no thumbnails will be displayed, requiring manual click-through to the original file:' + silenced: 'Posts from these servers will be hidden in public timelines and conversations, and no notifications will be generated from their users'' interactions, unless you are following them:' + suspended: 'No data from these servers will be processed, stored or exchanged, making any interaction or communication with users from these servers impossible:' + unavailable_content_html: Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server. user_count_after: one: user other: users @@ -42,6 +49,8 @@ en: what_is_mastodon: What is Mastodon? accounts: choices_html: "%{name}'s choices:" + endorsements_hint: You can endorse people you follow from the web interface, and they will show up here. + featured_tags_hint: You can feature specific hashtags that will be displayed here. follow: Follow followers: one: Follower @@ -53,6 +62,7 @@ en: media: Media moved_html: "%{name} has moved to %{new_profile_link}:" network_hidden: This information is not available + never_active: Never nothing_here: There is nothing here! people_followed_by: People whom %{name} follows people_who_follow: People who follow %{name} @@ -183,6 +193,7 @@ en: username: Username warn: Warn web: Web + whitelisted: Whitelisted action_logs: actions: assigned_to_self_report: "%{name} assigned report %{target} to themselves" @@ -218,19 +229,24 @@ en: deleted_status: "(deleted status)" title: Audit log custom_emojis: + assign_category: Assign category by_domain: Domain copied_msg: Successfully created local copy of the emoji copy: Copy copy_failed_msg: Could not make a local copy of that emoji + create_new_category: Create new category created_msg: Emoji successfully created! delete: Delete destroyed_msg: Emojo successfully destroyed! disable: Disable + disabled: Disabled disabled_msg: Successfully disabled that emoji emoji: Emoji enable: Enable + enabled: Enabled enabled_msg: Successfully enabled that emoji image_hint: PNG up to 50KB + list: List listed: Listed new: title: Add new custom emoji @@ -238,11 +254,14 @@ en: shortcode: Shortcode shortcode_hint: At least 2 characters, only alphanumeric characters and underscores title: Custom emojis + uncategorized: Uncategorized + unlist: Unlist unlisted: Unlisted update_failed_msg: Could not update that emoji updated_msg: Emoji successfully updated! upload: Upload dashboard: + authorized_fetch_mode: Secure mode backlog: backlogged jobs config: Configuration feature_deletions: Account deletions @@ -250,10 +269,13 @@ en: feature_profile_directory: Profile directory feature_registrations: Registrations feature_relay: Federation relay + feature_spam_check: Anti-spam feature_timeline_preview: Timeline preview features: Features hidden_service: Federation with hidden services open_reports: open reports + pending_tags: hashtags waiting for review + pending_users: users waiting for review recent_users: Recent users search: Full-text search single_user_mode: Single user mode @@ -265,11 +287,18 @@ en: week_interactions: interactions this week week_users_active: active this week week_users_new: users this week + whitelist_mode: Whitelist mode + domain_allows: + add_new: Whitelist domain + created_msg: Domain has been successfully whitelisted + destroyed_msg: Domain has been removed from the whitelist + undo: Remove from whitelist domain_blocks: add_new: Add new domain block created_msg: Domain block is now being processed destroyed_msg: Domain block has been undone domain: Domain + edit: Edit domain block existing_domain_block_html: You have already imposed stricter limits on %{name}, you need to <a href="%{unblock_url}">unblock it</a> first. new: create: Create block @@ -280,6 +309,10 @@ en: silence: Silence suspend: Suspend title: New domain block + private_comment: Private comment + private_comment_hint: Comment about this domain limitation for internal use by the moderators. + public_comment: Public comment + public_comment_hint: Comment about this domain limitation for the general public, if advertising the list of domain limitations is enabled. reject_media: Reject media files reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions reject_reports: Reject reports @@ -299,6 +332,7 @@ en: title: Undo domain block for %{domain} undo: Undo undo: Undo domain block + view: View domain block email_domain_blocks: add_new: Add new created_msg: Successfully added e-mail domain to blacklist @@ -322,6 +356,8 @@ en: all: All limited: Limited title: Moderation + private_comment: Private comment + public_comment: Public comment title: Federation total_blocked_by_us: Blocked by us total_followed_by_them: Followed by them @@ -351,6 +387,7 @@ en: pending: Waiting for relay's approval save_and_enable: Save and enable setup: Setup a relay connection + signatures_not_enabled: Relays will not work correctly while secure mode or whitelist mode is enabled status: Status title: Relays report_notes: @@ -399,6 +436,16 @@ en: custom_css: desc_html: Modify the look with CSS loaded on every page title: Custom CSS + default_noindex: + desc_html: Affects all users who have not changed this setting themselves + title: Opt users out of search engine indexing by default + domain_blocks: + all: To everyone + disabled: To no one + title: Show domain blocks + users: To logged-in local users + domain_blocks_rationale: + title: Show rationale hero: desc_html: Displayed on the frontpage. At least 600x100px recommended. When not set, falls back to server thumbnail title: Hero image @@ -431,8 +478,8 @@ en: open: Anyone can sign up title: Registrations mode show_known_fediverse_at_about_page: - desc_html: When toggled, it will show toots from all the known fediverse on preview. Otherwise it will only show local toots. - title: Show known fediverse on timeline preview + desc_html: When disabled, restricts the public timeline linked from the landing page to showing only local content + title: Include federated content on unauthenticated public timeline page show_staff_badge: desc_html: Show a staff badge on a user page title: Show staff badge @@ -449,19 +496,29 @@ en: desc_html: You can write your own privacy policy, terms of service or other legalese. You can use HTML tags title: Custom terms of service site_title: Server name + spam_check_enabled: + desc_html: Mastodon can auto-report accounts that send repeated unsolicited messages. There may be false positives. + title: Anti-spam automation thumbnail: desc_html: Used for previews via OpenGraph and API. 1200x630px recommended title: Server thumbnail timeline_preview: - desc_html: Display public timeline on landing page - title: Timeline preview + desc_html: Display link to public timeline on landing page and allow API access to the public timeline without authentication + title: Allow unauthenticated access to public timeline title: Site settings + trendable_by_default: + desc_html: Affects hashtags that have not been previously disallowed + title: Allow hashtags to trend without prior review + trends: + desc_html: Publicly display previously reviewed hashtags that are currently trending + title: Trending hashtags statuses: back_to_account: Back to account page batch: delete: Delete nsfw_off: Mark as not sensitive nsfw_on: Mark as sensitive + deleted: Deleted failed_to_execute: Failed to execute media: title: Media @@ -469,21 +526,24 @@ en: no_status_selected: No statuses were changed as none were selected title: Account statuses with_media: With media - subscriptions: - callback_url: Callback URL - confirmed: Confirmed - expires_in: Expires in - last_delivery: Last delivery - title: WebSub - topic: Topic tags: - accounts: Accounts - hidden: Hidden - hide: Hide from directory + accounts_today: Unique uses today + accounts_week: Unique uses this week + breakdown: Breakdown of today's usage by source + context: Context + directory: In directory + in_directory: "%{count} in directory" + last_active: Last active + most_popular: Most popular + most_recent: Most recent name: Hashtag + review: Review status + reviewed: Reviewed title: Hashtags - unhide: Show in directory - visible: Visible + trending_right_now: Trending right now + unique_uses_today: "%{count} posting today" + unreviewed: Not reviewed + updated_msg: Hashtag settings updated successfully title: Administration warning_presets: add_new: Add new @@ -499,11 +559,21 @@ en: body: "%{reporter} has reported %{target}" body_remote: Someone from %{domain} has reported %{target} subject: New report for %{instance} (#%{id}) + new_trending_tag: + body: 'The hashtag #%{name} is trending today, but has not been previously reviewed. It will not be displayed publicly unless you allow it to, or just save the form as it is to never hear about it again.' + subject: New hashtag up for review on %{instance} (#%{name}) + aliases: + add_new: Create alias + created_msg: Successfully created a new alias. You can now initiate the move from the old account. + deleted_msg: Successfully remove the alias. Moving from that account to this one will no longer be possible. + hint_html: If you want to move from another account to this one, here you can create an alias, which is required before you can proceed with moving followers from the old account to this one. This action by itself is <strong>harmless and reversible</strong>. <strong>The account migration is initiated from the old account</strong>. + remove: Unlink alias appearance: advanced_web_interface: Advanced web interface advanced_web_interface_hint: 'If you want to make use of your entire screen width, the advanced web interface allows you to configure many different columns to see as much information at the same time as you want: Home, notifications, federated timeline, any number of lists and hashtags.' animations_and_accessibility: Animations and accessibility confirmation_dialogs: Confirmation dialogs + discovery: Discovery sensitive_content: Sensitive content application_mailer: notification_preferences: Change e-mail preferences @@ -524,9 +594,13 @@ en: apply_for_account: Request an invite change_password: Password checkbox_agreement_html: I agree to the <a href="%{rules_path}" target="_blank">server rules</a> and <a href="%{terms_path}" target="_blank">terms of service</a> - confirm_email: Confirm email + checkbox_agreement_without_rules_html: I agree to the <a href="%{terms_path}" target="_blank">terms of service</a> delete_account: Delete account delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation. + description: + prefix_invited_by_user: "@%{name} invites you to join this server of Mastodon!" + prefix_sign_up: Sign up on Mastodon today! + suffix: With an account, you will be able to follow people, post updates and exchange messages with users from any Mastodon server and more! didnt_get_confirmation: Didn't receive confirmation instructions? forgot_password: Forgot your password? invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one. @@ -544,6 +618,16 @@ en: reset_password: Reset password security: Security set_new_password: Set new password + setup: + email_below_hint_html: If the below e-mail address is incorrect, you can change it here and receive a new confirmation e-mail. + email_settings_hint_html: The confirmation e-mail was sent to %{email}. If that e-mail address is not correct, you can change it in account settings. + title: Setup + status: + account_status: Account status + confirming: Waiting for e-mail confirmation to be completed. + functional: Your account is fully operational. + pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. + redirecting_to: Your account is inactive because it is currently redirecting to %{acct}. trouble_logging_in: Trouble logging in? authorize_follow: already_following: You are already following this account @@ -556,6 +640,11 @@ en: return: Show the user's profile web: Go to web title: Follow %{acct} + challenge: + confirm: Continue + hint_html: "<strong>Tip:</strong> We won't ask you for your password again for the next hour." + invalid_password: Invalid password + prompt: Confirm password to continue datetime: distance_in_words: about_x_hours: "%{count}h" @@ -571,28 +660,33 @@ en: x_months: "%{count}mo" x_seconds: "%{count}s" deletes: - bad_password_msg: Nice try, hackers! Incorrect password + challenge_not_passed: The information you entered was not correct confirm_password: Enter your current password to verify your identity - description_html: This will <strong>permanently, irreversibly</strong> remove content from your account and deactivate it. Your username will remain reserved to prevent future impersonations. + confirm_username: Enter your username to confirm the procedure proceed: Delete account success_msg: Your account was successfully deleted - warning_html: Only deletion of content from this particular server is guaranteed. Content that has been widely shared is likely to leave traces. Offline servers and servers that have unsubscribed from your updates will not update their databases. - warning_title: Disseminated content availability + warning: + before: 'Before proceeding, please read these notes carefully:' + caches: Content that has been cached by other servers may persist + data_removal: Your posts and other data will be permanently removed + email_change_html: You can <a href="%{path}">change your e-mail address</a> without deleting your account + email_contact_html: If it still doesn't arrive, you can e-mail <a href="mailto:%{email}">%{email}</a> for help + email_reconfirmation_html: If you are not receiving the confirmation e-mail, you can <a href="%{path}">request it again</a> + irreversible: You will not be able to restore or reactivate your account + more_details_html: For more details, see the <a href="%{terms_path}">privacy policy</a>. + username_available: Your username will become available again + username_unavailable: Your username will remain unavailable directories: directory: Profile directory - enabled: You are currently listed in the directory. - enabled_but_waiting: You have opted-in to be listed in the directory, but you do not have the minimum number of followers (%{min_followers}) to be listed yet. explanation: Discover users based on their interests explore_mastodon: Explore %{title} - how_to_enable: You are not currently opted-in to the directory. You can opt-in below. Use hashtags in your bio text to be listed under specific hashtags! - people: - one: "%{count} person" - other: "%{count} people" domain_validator: invalid_domain: is not a valid domain name errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': content: Security verification failed. Are you blocking cookies? @@ -601,6 +695,7 @@ en: '500': content: We're sorry, but something went wrong on our end. title: This page is not correct + '503': The page could not be served due to a temporary server failure. noscript_html: To use the Mastodon web application, please enable JavaScript. Alternatively, try one of the <a href="%{apps_path}">native apps</a> for Mastodon for your platform. existing_username_validator: not_found: could not find a local user with that username @@ -624,6 +719,7 @@ en: add_new: Add new errors: limit: You have already featured the maximum amount of hashtags + hint_html: "<strong>What are featured hashtags?</strong> They are displayed prominently on your public profile and allow people to browse your public posts specifically under those hashtags. They are a great tool for keeping track of creative works or long-term projects." filters: contexts: home: Home timeline @@ -644,10 +740,12 @@ en: developers: Developers more: More… resources: Resources + trending_now: Trending now generic: all: All changes_saved_msg: Changes successfully saved! copy: Copy + no_batch_actions_available: No batch actions available on this page order_by: Order by save_changes: Save changes validation_errors: @@ -699,7 +797,7 @@ en: '604800': 1 week '86400': 1 day expires_in_prompt: Never - generate: Generate + generate: Generate invite link invited_by: 'You were invited by:' max_uses: one: 1 use @@ -718,10 +816,35 @@ en: images_and_video: Cannot attach a video to a status that already contains images too_many: Cannot attach more than 4 files migrations: - acct: username@domain of the new account - currently_redirecting: 'Your profile is set to redirect to:' - proceed: Save - updated_msg: Your account migration setting successfully updated! + acct: Moved to + cancel: Cancel redirect + cancel_explanation: Cancelling the redirect will re-activate your current account, but will not bring back followers that have been moved to that account. + cancelled_msg: Successfully cancelled the redirect. + errors: + already_moved: is the same account you have already moved to + missing_also_known_as: is not back-referencing this account + move_to_self: cannot be current account + not_found: could not be found + on_cooldown: You are on cooldown + followers_count: Followers at time of move + incoming_migrations: Moving from a different account + incoming_migrations_html: To move from another account to this one, first you need to <a href="%{path}">create an account alias</a>. + moved_msg: Your account is now redirecting to %{acct} and your followers are being moved over. + not_redirecting: Your account is not redirecting to any other account currently. + on_cooldown: You have recently migrated your account. This function will become available again in %{count} days. + past_migrations: Past migrations + proceed_with_move: Move followers + redirecting_to: Your account is redirecting to %{acct}. + set_redirect: Set redirect + warning: + backreference_required: The new account must first be configured to back-reference this one + before: 'Before proceeding, please read these notes carefully:' + cooldown: After moving there is a cooldown period during which you will not be able to move again + disabled_account: Your current account will not be fully usable afterwards. However, you will have access to data export as well as re-activation. + followers: This action will move all followers from the current account to the new account + only_redirect_html: Alternatively, you can <a href="%{path}">only put up a redirect on your profile</a>. + other_data: No other data will be moved automatically + redirect: Your current account's profile will be updated with a redirect notice and be excluded from searches moderation: title: Moderation notification_mailer: @@ -819,10 +942,6 @@ en: reply: proceed: Proceed to reply prompt: 'You want to reply to this toot:' - remote_unfollow: - error: Error - title: Title - unfollowed: Unfollowed scheduled_statuses: over_daily_limit: You have exceeded the limit of %{limit} scheduled toots for that day over_total_limit: You have exceeded the limit of %{limit} scheduled toots @@ -871,6 +990,7 @@ en: settings: account: Account account_settings: Account settings + aliases: Account aliases appearance: Appearance authorized_apps: Authorized apps back: Back to Mastodon @@ -888,6 +1008,8 @@ en: profile: Profile relationships: Follows and followers two_factor_authentication: Two-factor Auth + spam_check: + spam_detected: This is an automated report. Spam has been detected. statuses: attached: description: 'Attached: %{attached}' @@ -912,6 +1034,9 @@ en: private: Non-public toot cannot be pinned reblog: A boost cannot be pinned poll: + total_people: + one: "%{count} person" + other: "%{count} people" total_votes: one: "%{count} vote" other: "%{count} votes" @@ -930,6 +1055,8 @@ en: pinned: Pinned toot reblogged: boosted sensitive_content: Sensitive content + tags: + does_not_match_previous_name: does not match the previous name terms: body_html: | <h2>Privacy Policy</h2> @@ -1047,7 +1174,9 @@ en: disable: While your account is frozen, your account data remains intact, but you cannot perform any actions until it is unlocked. silence: While your account is limited, only people who are already following you will see your toots on this server, and you may be excluded from various public listings. However, others may still manually follow you. suspend: Your account has been suspended, and all of your toots and your uploaded media files have been irreversibly removed from this server, and servers where you had followers. + get_in_touch: You can reply to this e-mail to get in touch with the staff of %{instance}. review_server_policies: Review server policies + statuses: 'Specifically, for:' subject: disable: Your account %{acct} has been frozen none: Warning for %{acct} diff --git a/config/locales/eo.yml b/config/locales/eo.yml index c71b42fdd8161feef7ab8ab12f3ddb897fbff9a7..dd3ca1079b02eda3dcc5a17ffec2203df4040b05 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1,7 +1,7 @@ --- eo: about: - about_hashtag_html: Ĉi tiuj estas la publikaj fajfoj markitaj per <strong>#%{hashtag}</strong>. Vi povas interagi kun ili se vi havas konton ie ajn en la fediverse. + about_hashtag_html: Ĉi tiuj estas la publikaj mesaÄoj markitaj per <strong>#%{hashtag}</strong>. Vi povas interagi kun ili se vi havas konton ie ajn en la fediverse. about_mastodon_html: Mastodon estas socia reto bazita sur malfermitaj retaj protokoloj kaj sur libera malfermitkoda programo. Äœi estas sencentra kiel retmesaÄoj. about_this: Pri active_count_after: aktiva @@ -17,13 +17,12 @@ eo: contact_unavailable: Ne disponebla discover_users: Malkovri uzantojn documentation: Dokumentado - extended_description_html: | - <h3>Bona loko por reguloj</h3> - <p>La detala priskribo ne estis elektita.</p> federation_hint_html: Per konto ĉe %{instance}, vi povos sekvi homojn ĉe iu ajn Mastodon nodo kaj preter. - generic_description: "%{domain} estas unu servilo en la reto" get_apps: Provu telefonan aplikaĵon hosted_on: "%{domain} estas nodo de Mastodon" + instance_actor_flash: | + Ĉi tiu konto estas virtuala ulo uzata por reprezenti la servilon mem kaj ne iun apartan uzanton. + Äœi estas uzata por frataraj celoj kaj Äi ne devus esti blokita krom se vi volas bloki la tutan servilon, tiuokaze vi devus uzi domajnan blokadon. learn_more: Lerni pli privacy_policy: Privateca politiko see_whats_happening: Vidi kio okazas @@ -35,6 +34,10 @@ eo: status_count_before: Kie skribiÄis tagline: Sekvi amikojn kaj trovi novan onin terms: Uzkondiĉoj + unavailable_content: Nedisponebla enhavo + unavailable_content_description: + domain: Servilo + reason: 'Kialo:' user_count_after: one: uzanto other: uzantoj @@ -53,6 +56,7 @@ eo: media: AÅdovidaĵoj moved_html: "%{name} moviÄis al %{new_profile_link}:" network_hidden: Tiu informo ne estas disponebla + never_active: Neniam nothing_here: Estas nenio ĉi tie! people_followed_by: Sekvatoj de %{name} people_who_follow: Sekvantoj de %{name} @@ -174,6 +178,7 @@ eo: statuses: MesaÄoj subscribe: Aboni suspended: Haltigita + time_in_queue: Atendado en atendovico %{time} title: Kontoj unconfirmed_email: Nekonfirmita retadreso undo_silenced: Malfari kaÅon @@ -182,6 +187,7 @@ eo: username: Uzantnomo warn: Averti web: Reto + whitelisted: En la blanka listo action_logs: actions: assigned_to_self_report: "%{name} asignis signalon %{target} al si mem" @@ -225,11 +231,14 @@ eo: delete: Forigi destroyed_msg: EmoÄio sukcese forigita! disable: Malebligi + disabled: Malebligita disabled_msg: EmoÄio sukcese malebligita emoji: EmoÄio enable: Ebligi + enabled: Ebligita enabled_msg: Tiu emoÄio estis sukcese ebligita image_hint: PNG Äis 50KB + list: Listo listed: Listigita new: title: Aldoni novan propran emoÄion @@ -237,6 +246,7 @@ eo: shortcode: Mallonga kodo shortcode_hint: AlmenaÅ 2 signoj, nur literoj, ciferoj kaj substrekoj title: Propraj emoÄioj + unlist: Nelistigi unlisted: Nelistigita update_failed_msg: Äœisdatigi tiun emoÄion ne eblis updated_msg: EmoÄio sukcese Äisdatigita! @@ -249,6 +259,7 @@ eo: feature_profile_directory: Profilujo feature_registrations: RegistriÄoj feature_relay: Federacia ripetilo + feature_spam_check: KontraÅ-spamo feature_timeline_preview: Templinio antaÅvidi features: Funkcioj hidden_service: Federacio kun kaÅitaj servoj @@ -260,15 +271,23 @@ eo: space: Memorspaca uzado title: Kontrolpanelo total_users: uzantoj sume - trends: Furoroj + trends: Furoraĵoj week_interactions: interagoj tiusemajne week_users_active: aktivaj tiusemajne week_users_new: uzantoj tiusemajne + whitelist_mode: En la blanka listo + domain_allows: + add_new: En la blanka listo domajno + created_msg: Domajno sukcese blanklistigita + destroyed_msg: Domajno estis forigita de la blanklisto + undo: Forigi de la blanklisto domain_blocks: add_new: Aldoni novan created_msg: Domajna blokado en traktado destroyed_msg: Domajna blokado malfarita domain: Domajno + edit: Redakti domajna blokado + existing_domain_block_html: Vi jam trudis pli striktajn limojn al %{name}, vi devas <a href="%{unblock_url}">malbloki Äin</a> unue. new: create: Krei blokadon hint: La domajna blokado ne evitigos kreadon de novaj kontoj en la datumbazo, sed aplikos specifajn kontrolajn agojn sur ĉi tiujn kontojn aÅtomate kaj retroaktive. @@ -278,6 +297,8 @@ eo: silence: KaÅi suspend: Haltigi title: Nova domajna blokado + private_comment: Privata komento + public_comment: Publika komento reject_media: Malakcepti aÅdovidajn dosierojn reject_media_hint: Forigas aÅdovidaĵojn loke konservitajn kaj rifuzas alÅuti ajnan estonte. Senzorge pri haltigoj reject_reports: Malakcepti raportojn @@ -297,6 +318,7 @@ eo: title: Malfari domajnan blokadon por %{domain} undo: Malfari undo: Malfari + view: Vidi domajna blokado email_domain_blocks: add_new: Aldoni novan created_msg: Retadreso sukcese aldonita al la nigra listo @@ -320,6 +342,8 @@ eo: all: Ĉiuj limited: Limigita title: Kontrolo + private_comment: Privata komento + public_comment: Publika komento title: Federacio total_blocked_by_us: Blokitaj de ni total_followed_by_them: Sekvataj de ili @@ -334,6 +358,8 @@ eo: expired: Eksvalida title: Filtri title: Invitoj + pending_accounts: + title: Pritraktataj kontoj (%{count}) relays: add_new: Aldoni novan ripetilon delete: Forigi @@ -395,6 +421,11 @@ eo: custom_css: desc_html: ÅœanÄi la aspekton per CSS Åargita en ĉiu pago title: Propra CSS + domain_blocks: + all: Al ciuj + disabled: Al neniu + title: Vidi domajna blokado + users: Al ensalutintaj lokaj uzantoj hero: desc_html: Montrata en la ĉefpaÄo. AlmenaÅ 600x100px rekomendita. Kiam ne agordita, la bildeto de la servilo estos uzata title: Kapbildo @@ -445,6 +476,8 @@ eo: desc_html: Vi povas skribi vian propran privatecan politikon, viajn uzkondiĉojn aÅ aliajn leÄaĵojn. Vi povas uzi HTML-etikedojn title: Propraj uzkondiĉoj site_title: Nomo de la servilo + spam_check_enabled: + title: KontraÅ-spamo aÅtomatige thumbnail: desc_html: Uzata por antaÅvidoj per OpenGraph kaj per API. 1200x630px rekomendita title: Bildeto de la servilo @@ -452,12 +485,16 @@ eo: desc_html: Montri publikan templinion en komenca paÄo title: Tempolinia antaÅvido title: Retejaj agordoj + trends: + desc_html: Publike montri antaÅe kontrolitajn kradvortojn, kiuj nune furoras + title: Furoraj kradvortoj statuses: back_to_account: Reveni al konta paÄo batch: delete: Forigi - nsfw_off: Marki ne tikla + nsfw_off: Marki netikla nsfw_on: Marki tikla + deleted: Forigita failed_to_execute: Ekigo malsukcesa media: title: AÅdovidaĵoj @@ -465,21 +502,21 @@ eo: no_status_selected: Neniu mesaÄo estis ÅanÄita ĉar neniu estis elektita title: MesaÄoj de la konto with_media: Kun aÅdovidaĵoj - subscriptions: - callback_url: Revena URL - confirmed: Konfirmita - expires_in: EksvalidiÄas je - last_delivery: Lasta livero - title: WebSub - topic: Temo tags: - accounts: Kontoj - hidden: KaÅitaj - hide: KaÅi de la profilujo + context: Kunteksto + directory: En la adresaro + in_directory: "%{count} en adresaro" + last_active: Lasta aktiva + most_popular: La plej populara + most_recent: Plej lasta name: Kradvorto + review: La statuso de la recenzo + reviewed: Recenzis title: Kradvortoj - unhide: Montri en la profilujo - visible: Videblaj + trending_right_now: Nunaj furoraĵoj + unique_uses_today: "%{count} uzas hodiaÅ" + unreviewed: Ne recenzis + updated_msg: Kradvorto agordoj Äisdatigis sukcese title: Administrado warning_presets: add_new: Aldoni novan @@ -495,9 +532,20 @@ eo: body: "%{reporter} signalis %{target}" body_remote: Iu de %{domain} signalis %{target} subject: Nova signalo por %{instance} (#%{id}) + new_trending_tag: + body: 'La kradvorto #%{name} furoras hodiaÅ, sed ankoraÅ ne estis kontrolita. Äœi ne aperos publike sen via aprobo. Se vi ne volas tion, simple konservu la formularon tiel kiel.' + subject: Nova kradvorto kontrolebla en %{instance} (#%{name}) + aliases: + add_new: Krei alinomon + created_msg: Kreis novan alinomon sukcese. Vi povas inici la transloki el la malnovan konton nun. + deleted_msg: Forigis la alinomon sukcese. Transloki el tiu konto al ĉi tiu ne plu eblos. + remove: Malligili alinomon appearance: advanced_web_interface: Altnivela retpaÄa interfaco - confirmation_dialogs: Konfirmaj dialogoj + advanced_web_interface_hint: 'Se vi volas uzi la tutan larÄecon de via ekrano, la kompleksa reta interfaco permesas al vi agordi multajn malsamajn kolumnojn por vidi tiom da informoj kiom vi volas samtempe: Hejmo, sciigoj, fratara tempolinio, kaj ajna kvanto de listoj kaj kradvortoj.' + animations_and_accessibility: Animacioj kaj alirebleco + confirmation_dialogs: Konfirmaj fenestroj + discovery: Eltrovo sensitive_content: Tikla enhavo application_mailer: notification_preferences: ÅœanÄi retmesaÄajn preferojn @@ -518,9 +566,12 @@ eo: apply_for_account: Peti inviton change_password: Pasvorto checkbox_agreement_html: Mi samopinii al la <a href="%{rules_path}" target="_blank">Servo reguloj</a> kaj <a href="%{terms_path}" target="_blank">kondiĉo al servadon</a> - confirm_email: Konfirmi retadreson + checkbox_agreement_without_rules_html: Mi konsenti la <a href="%{terms_path}" target="_blank">reguloj de servado</a> delete_account: Forigi konton delete_account_html: Se vi deziras forigi vian konton, vi povas <a href="%{path}">fari tion ĉi tie</a>. Vi bezonos konfirmi vian peton. + description: + prefix_invited_by_user: "@%{name} invitigi vin aligiÄi ĉi tiu servilo de Mastodon!" + prefix_sign_up: RegistriÄi ĉe Mastodon hodiaÅ! didnt_get_confirmation: Ĉu vi ne ricevis la instrukciojn por konfirmi? forgot_password: Pasvorto forgesita? invalid_reset_password_token: Ä´etono por restarigi pasvorton nevalida aÅ eksvalida. Bonvolu peti novan. @@ -538,6 +589,11 @@ eo: reset_password: ÅœanÄi pasvorton security: Sekureco set_new_password: Elekti novan pasvorton + setup: + title: Agordi + status: + account_status: Statuso de la konto + functional: Via konto estas plene funkcianta. trouble_logging_in: Äœeni ensaluti? authorize_follow: already_following: Vi jam sekvas tiun konton @@ -550,6 +606,10 @@ eo: return: Montri la profilon de la uzanto web: Iri al reto title: Sekvi %{acct} + challenge: + confirm: DaÅrigi + invalid_password: Nevalida pasvorto + prompt: Konfirmi pasvorton por daÅrigi datetime: distance_in_words: about_x_hours: "%{count}h" @@ -565,26 +625,21 @@ eo: x_months: "%{count}mo" x_seconds: "%{count}s" deletes: - bad_password_msg: MalÄusta pasvorto confirm_password: Enmetu vian nunan pasvorton por konfirmi vian identecon - description_html: Tio <strong>porĉiame kaj neÅanÄeble</strong> forigos la enhavon de via konto kaj malaktivigos Äin. Via uzantnomo restos rezervita por eviti postajn trompojn pri identeco. + confirm_username: Enigi vian uzantnomon por konfirmi la procedo proceed: Forigi konton success_msg: Via konto estis sukcese forigita - warning_html: La forigo de la enhavo estas certa nur por ĉi tiu aparta servilo. Enhavo, kiu estis disvastigita verÅajne lasos spurojn. Eksterretaj serviloj kaj serviloj, kiuj ne abonas viajn Äisdatigojn ne Äisdatigos siajn datumbazojn. - warning_title: Disponebleco de disvastigita enhavo directories: directory: Profilujo - enabled: Vi estas listigata en la profilujo. - enabled_but_waiting: Vi elektis esti listigata en la profilujo, sed vi ankoraÅ ne havas la minimuman kvanton da sekvantoj (%{min_followers}) por esti listigata. explanation: Malkovru uzantojn per iliaj interesoj explore_mastodon: Esplori %{title} - how_to_enable: Vi ankoraÅ ne donis permeson listigi vin en la profilujo. Vi povas doni permeson ĉi-sube. Uzu kradvortojn en via biografia teksto por esti listigata sub specifaj kradvortoj! - people: - one: "%{count} persono" - other: "%{count} personoj" + domain_validator: + invalid_domain: ne estas valida domajna nomo errors: + '400': The request you submitted was invalid or malformed. '403': Vi ne havas la rajton por vidi ĉi tiun paÄon. '404': La paÄo ke kiun vi serĉas ne ekzistas ĉi tie. + '406': This page is not available in the requested format. '410': La paÄo, kiun vi serĉas, ne plu ekzistas ĉi tie. '422': content: Sekureca konfirmo malsukcesa. Ĉu vi blokas kuketojn? @@ -593,6 +648,7 @@ eo: '500': content: Ni bedaÅras, io malsukcesis niaflanke. title: Ĉi tiu paÄo ne estas Äusta + '503': The page could not be served due to a temporary server failure. noscript_html: |- Por uzi la retan aplikaĵon de Mastodon, bonvolu ebligi JavaScript. Alimaniere, provu unu el la <a href="%{apps_path}">operaciumaj aplikaĵoj</a> por Mastodon por via platformo. @@ -618,6 +674,7 @@ eo: add_new: Aldoni novan errors: limit: Vi jam elstarigis la maksimuman kvanton da kradvortoj + hint_html: "<strong>Kio estas la trajtaj kradvortoj?</strong> Ili bone videblas en via publika profilo kaj permesas al homoj trarigardi viajn publikajn mesaÄojn specife laÅ tiuj kradvortoj. Ili estas bonaj iloj por sekvi la evoluon de kreadaj laboroj aÅ longdaÅraj projektoj." filters: contexts: home: Hejma templinio @@ -638,6 +695,7 @@ eo: developers: Programistoj more: Pli… resources: Rimedoj + trending_now: Nunaj furoraĵoj generic: all: Ĉio changes_saved_msg: ÅœanÄoj sukcese konservitaj! @@ -648,7 +706,7 @@ eo: one: Io mise okazis! Bonvolu konsulti la suban erar-raporton other: Io mise okazis! Bonvolu konsulti la subajn %{count} erar-raportojn html_validator: - invalid_markup: 'havas malvalida HTML markado: %{error}' + invalid_markup: 'havas nevalidan HTML-markadon: %{error}' identity_proofs: active: Aktiva authorize: Jes, permesi @@ -713,9 +771,7 @@ eo: too_many: Aldoni pli ol 4 dosierojn ne eblas migrations: acct: uzantnomo@domajno de la nova konto - currently_redirecting: 'Via profilo alidirektos al:' - proceed: Konservi - updated_msg: Via agordo pri konta migrado estis sukcese Äisdatigita! + proceed_with_move: Translokigi sekvantoj moderation: title: Kontrolado notification_mailer: @@ -780,7 +836,10 @@ eo: too_many_options: ne povas enhavi pli da %{max} proponoj preferences: other: Aliaj aferoj + posting_defaults: AfiÅadoj defaÅltoj + public_timelines: Publikaj templinioj relationships: + activity: Konto aktiveco dormant: Dormanta last_active: Lasta aktiva most_recent: Plej lasta @@ -788,6 +847,9 @@ eo: mutual: Reciproka primary: Primara relationship: Rilato + remove_selected_domains: Forigi ĉiuj sekvantojn el la selektitajn domajnojn + remove_selected_followers: Forigi selektitajn sekvantojn + remove_selected_follows: Malsekvi selektitajn uzantojn status: Statuso de la konto remote_follow: acct: Enmetu vian uzantnomo@domajno de kie vi volas agi @@ -806,10 +868,6 @@ eo: reply: proceed: Konfirmi la respondon prompt: 'Vi volas respondi al ĉi tiu mesaÄo:' - remote_unfollow: - error: Eraro - title: Titolo - unfollowed: Ne plu sekvita scheduled_statuses: over_daily_limit: Vi transpasis la limigon al %{limit} samtage planitaj mesaÄoj over_total_limit: Vi transpasis la limigon al %{limit} planitaj mesaÄoj @@ -858,6 +916,7 @@ eo: settings: account: Konto account_settings: Agordoj de konto + aliases: Kontoj alinomoj appearance: Apero authorized_apps: Rajtigitaj aplikaĵoj back: Reveni al Mastodon @@ -953,6 +1012,7 @@ eo: silence: Dum via konto estas limigita, nur tiuj, kiuj jam sekvas vin, vidos viajn mesaÄojn en ĉi tiu servilo, kaj vi povus esti ekskludita de diversaj publikaj listoj. Tamen, aliaj ankoraÅ povas mane sekvi vin. suspend: Via konto estis haltigita, kaj ĉiuj el viaj mesaÄoj kaj alÅutitaj aÅdovidaj dosieroj estis nemalfareble forigitaj de ĉi tiu servilo, kaj de la serviloj, kie vi havis sekvantojn. review_server_policies: Superrigardi servilajn politikojn + statuses: 'Specife, per:' subject: disable: Via konto %{acct} estas frostigita none: Averto por %{acct} diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml new file mode 100644 index 0000000000000000000000000000000000000000..d70007d271b6da125d3da18840fbb4cf96aba5dc --- /dev/null +++ b/config/locales/es-AR.yml @@ -0,0 +1,402 @@ +--- +es-AR: + about: + about_hashtag_html: Estos son toots públicos etiquetados con <strong>#%{hashtag}</strong>. Si tenés una cuenta en cualquier parte del fediverso, podés interactuar con ellos. + about_mastodon_html: Mastodon es una red social basada en protocolos abiertos de la web y es software libre y de código abierto. Es descentralizada, como el correo electrónico. + about_this: Acerca de Mastodon + active_count_after: activo + active_footnote: Usuarios activos mensualmente (MAU) + administered_by: 'Administrado por:' + api: API + apps: Aplicaciones móviles + apps_platforms: Usá Mastodon desde iOS, Android y otras plataformas + browse_directory: Explorá el directorio de perfiles y filtrá por intereses + browse_public_posts: Explorá un flujo en tiempo real de toots públicos en Mastodon + contact: Contacto + contact_missing: No establecido + contact_unavailable: No disponible + discover_users: Descubrir usuarios + documentation: Documentación + federation_hint_html: Con una cuenta en %{instance} vas a poder seguir a gente de cualquier servidor de Mastodon y más allá. + get_apps: Probá una aplicación móvil + hosted_on: Mastodon alojado en %{domain} + instance_actor_flash: | + Esta cuenta es un actor virtual usado para representar al propio servidor y no a ningún usuario individual. + Se usa para fines federativos y no debe ser bloqueado a menos que quieras bloquear toda la instancia, en cuyo caso deberÃas usar un bloqueo de dominio. + learn_more: Aprendé más + privacy_policy: PolÃtica de privacidad + see_whats_happening: Esto es lo que está pasando ahora + server_stats: 'EstadÃsticas del servidor:' + source_code: Código fuente + status_count_after: + one: estado + other: estados + status_count_before: Que enviaron + tagline: Seguà a tus amigos y descubrà nueva gente + terms: Términos del servicio + unavailable_content: Contenido no disponible + unavailable_content_description: + domain: Servidor + reason: Razón + rejecting_media: 'Los archivos de medios de este servidor no van a ser procesados y no se mostrarán miniaturas, lo que requiere un clic manual hacia el archivo original:' + silenced: 'Los toots de estos servidores se ocultarán en las lÃneas temporales y conversaciones públicas, y no se generarán notificaciones de las interacciones de sus usuarios, a menos que los estés siguiendo:' + suspended: 'No se procesarán, almacenarán o intercambiarán datos de estos servidores, haciendo imposible cualquier interacción o comunicación con los usuarios de estos servidores:' + unavailable_content_html: Mastodon generalmente te permite ver contenido e interactuar con usuarios de cualquier otro servidor en el fediverso. Estas son las excepciones que se hicieron en este servidor en particular. + user_count_after: + one: usuario + other: usuarios + user_count_before: Hogar de + what_is_mastodon: "¿Qué es Mastodon?" + accounts: + choices_html: 'Recomendados de %{name}:' + endorsements_hint: Podés recomendar a gente que seguÃs desde la interface web, y van a aparecerán acá. + featured_tags_hint: Pdés destacar etiquetas especÃficas que se mostrarán acá. + follow: Seguir + followers: + one: Seguidor + other: Seguidores + following: Siguiendo + joined: Se unió en %{date} + last_active: última vez activo + link_verified_on: La propiedad de este enlace fue verificada el %{date} + media: Medios + moved_html: "%{name} se mudó a %{new_profile_link}:" + network_hidden: Esta información no está disponible + never_active: Nunca + nothing_here: "¡No hay nada acá!" + people_followed_by: "%{name} sigue a estas personas" + people_who_follow: Estas personas siguen a %{name} + pin_errors: + following: Ya tenés que estar siguiendo a la persona que querés recomendar + posts: + one: Toot + other: Toots + posts_tab_heading: Toots + posts_with_replies: Toots con respuestas + reserved_username: El nombre de usuario está reservado + roles: + admin: Administrador + bot: Bot + moderator: Moderador + unavailable: Perfil no disponible + unfollow: Dejar de seguir + admin: + account_actions: + action: Ejecutar acción + title: Ejecutar acción de moderación en %{acct} + account_moderation_notes: + create: Dejar nota + created_msg: "¡Nota de moderación creada exitosamente!" + delete: Eliminar + destroyed_msg: "¡Nota de moderación destruÃda exitosamente!" + accounts: + approve: Aprobar + approve_all: Aprobar todas + are_you_sure: "¿Estás seguro?" + avatar: Avatar + by_domain: Dominio + change_email: + changed_msg: "¡Correo electrónico de cuenta cambiado exitosamente!" + current_email: Correo electrónico actual + label: Cambiar correo electrónico + new_email: Nuevo correo electrónico + submit: Cambiar correo electrónico + title: Cambiar correo electrónico para %{username} + confirm: Confirmar + confirmed: Confirmado + confirming: Confirmando + deleted: Eliminado + demote: Bajar de nivel + disable: Deshabilitar + disable_two_factor_authentication: Deshabilitar 2FA + disabled: Deshabilitada + display_name: Nombre para mostrar + domain: Dominio + edit: Editar + email: Correo electrónico + email_status: Estado del correo + enable: Habilitar + enabled: Habilitada + feed_url: Dirección de la fuente web + followers: Seguidores + followers_url: Dirección web de los seguidores + header: Cabecera + inbox_url: Dirección web de la bandeja de entrada + invited_by: Invitado por + ip: Dirección IP + joined: Se unió en + location: + all: Todas + local: Local + remote: Remota + title: Ubicación + login_status: Estado del inicio de sesión + media_attachments: Adjuntos + memorialize: Convertir en recordatorio + moderation: + active: Activa + all: Todas + pending: Pendiente + silenced: Silenciados + suspended: Suspendidos + title: Moderación + moderation_notes: Notas de moderación + most_recent_activity: Actividad más reciente + most_recent_ip: Direcciones IP más recientes + no_account_selected: No se cambió ninguna cuenta ya que ninguna fue seleccionada + no_limits_imposed: Sin lÃmites impuestos + not_subscribed: No suscripto + outbox_url: Dirección web de la bandeja de salida + pending: Revisión pendiente + perform_full_suspension: Suspender + profile_url: Dirección web del perfil + promote: Promocionar + protocol: Protocolo + public: Pública + push_subscription_expires: La suscripción PuSH vence + redownload: Recargar perfil + reject: Rechazar + reject_all: Rechazar todas + remove_avatar: Quitar avatar + remove_header: Quitar cabecera + resend_confirmation: + already_confirmed: Este usuario ya está confirmado + send: Reenviar correo electrónico de confirmación + success: "¡Correo electrónico de confirmación enviado exitosamente!" + reset: Restablecer + reset_password: Cambiar contraseña + resubscribe: Resuscribir + role: Permisos + roles: + admin: Administrador + moderator: Moderador + staff: Equipo + user: Usuario + search: Buscar + shared_inbox_url: Dirección web de la bandeja de entrada compartida + show: + created_reports: Informes hechos + targeted_reports: Denunciado por otros + silence: Silenciar + silenced: Silenciadas + statuses: Estados + subscribe: Suscribirse + suspended: Suspendidas + title: Cuentas + unconfirmed_email: Correo electrónico sin confirmar + undo_silenced: Deshacer silenciado + undo_suspension: Deshacer suspensión + unsubscribe: Desuscribirse + username: Nombre de usuario + warn: Advertir + web: Web + whitelisted: Aprobadas + action_logs: + actions: + assigned_to_self_report: "%{name} se asignó la denuncia %{target} a sÃ" + change_email_user: "%{name} cambió la dirección de correo electrónico del usuario %{target}" + confirm_user: "%{name} confirmó la dirección de correo del usuario %{target}" + create_account_warning: "%{name} envió una advertencia a %{target}" + create_custom_emoji: "%{name} subió nuevo emoji %{target}" + create_domain_block: "%{name} bloqueó el dominio %{target}" + create_email_domain_block: "%{name} desaprobó el dominio de correo electrónico %{target}" + demote_user: "%{name} bajó de nivel al usuario %{target}" + destroy_custom_emoji: "%{name} destruyó el emoji %{target}" + destroy_domain_block: "%{name} desbloqueó el dominio %{target}" + destroy_email_domain_block: "%{name} aprobó el dominio de correo electrónico %{target}" + destroy_status: "%{name} eliminó el estado de %{target}" + disable_2fa_user: "%{name} deshabilitó el requerimiento de dos factores para el usuario %{target}" + disable_custom_emoji: "%{name} deshabilitó el emoji %{target}" + disable_user: "%{name} deshabilitó el inicio de sesión para el usuario %{target}" + enable_custom_emoji: "%{name} habilitó el emoji %{target}" + enable_user: "%{name} habilitó el inicio de sesión para el usuario %{target}" + memorialize_account: "%{name} convirtió la cuenta de %{target} en una página de recordatorio" + remove_avatar_user: "%{name} quitó el avatar de %{target}" + reopen_report: "%{name} reabrió la denuncia %{target}" + reset_password_user: "%{name} cambió la contraseña del usuario %{target}" + resolve_report: "%{name} resolvió la denuncia %{target}" + silence_account: "%{name} silenció la cuenta de %{target}" + suspend_account: "%{name} suspendió la cuenta de %{target}" + unassigned_report: "%{name} desasignó la denuncia %{target}" + unsilence_account: "%{name} quitó el silenciado de la cuenta de %{target}" + unsuspend_account: "%{name} quitó la suspensión de la cuenta de %{target}" + update_custom_emoji: "%{name} actualizó el emoji %{target}" + update_status: "%{name} actualizó el estado de %{target}" + deleted_status: "(estado borrado)" + title: Registro de auditorÃa + custom_emojis: + assign_category: Asignar categorÃa + by_domain: Dominio + copied_msg: Copia local del emoji creada exitosamente + copy: Copiar + copy_failed_msg: No se pudo realizar una copia local de ese emoji + create_new_category: Crear nueva categorÃa + created_msg: "¡Emoji creado exitosamente!" + delete: Eliminar + destroyed_msg: "¡Emoji destruido exitosamente!" + disable: Deshabilitar + disabled: Deshabilitado + disabled_msg: Se deshabilitó ese emoji exitosamente + emoji: Emoji + enable: Habilitar + enabled: Habilitado + enabled_msg: Se habilitó ese emoji exitosamente + image_hint: PNG de hasta 50KB + list: Lista + listed: Listados + new: + title: Agregar nuevo emoji personalizado + overwrite: Sobreescribir + shortcode: Código corto + shortcode_hint: Al menos 2 caracteres, sólo caracteres alfanuméricos y subguiones ("_") + title: Emojis personalizados + uncategorized: Sin categorÃa + unlist: No agregar a lista + unlisted: No listado + update_failed_msg: No se pudo actualizar ese emoji + updated_msg: "¡Emoji actualizado exitosamente!" + upload: Subir + dashboard: + authorized_fetch_mode: Modo seguro + backlog: trabajos registrados + config: Configuración + feature_deletions: Eliminaciones de cuenta + feature_invites: Enlaces de invitación + feature_profile_directory: Directorio de perfiles + feature_registrations: Registros + feature_relay: Relé de federación + feature_spam_check: Anti-spam + feature_timeline_preview: Previsualización de la lÃnea temporal + features: Funciones + hidden_service: Federación con servicios ocultos + open_reports: abrir denuncias + pending_tags: etiquetas esperando revisión + pending_users: usuarios esperando revisión + recent_users: Usuarios recientes + search: Búsqueda de texto completo + single_user_mode: Modo de usuario único + software: Software + space: Uso del espacio + title: Panel + total_users: usuarios en total + trends: Tendencias + week_interactions: interacciones esta semana + week_users_active: activos esta semana + week_users_new: usuarios esta semana + whitelist_mode: Modo de aprobación + domain_allows: + add_new: Aprobar dominio + created_msg: El dominio se aprobó exitosamente + destroyed_msg: El dominio no se aprobó + undo: No aprobado + domain_blocks: + add_new: Agregar nuevo bloquedo de dominio + created_msg: Ahora se está procesando el bloquedo de dominio + destroyed_msg: Se deshizo el bloqueo de dominio + domain: Dominio + edit: Editar bloqueo de dominio + existing_domain_block_html: Ya le aplicaste lÃmites más estrictos a %{name}, por lo que primero necesitás <a href="%{unblock_url}">desbloquearlo</a>. + new: + create: Crear bloqueo + hint: El bloqueo de dominio no va a prevenir la creación de toots de cuenta en la base de datos, pero se aplicarán métodos especÃficos de moderación en esas cuentas, retroactiva y automáticamente. + severity: + desc_html: "<strong>Silenciar</strong> hará que los toots de la cuenta sean invisibles a quienes no estén siguiendo esa cuenta. <strong>Suspender</strong> quitará todo el contenido, archivos de medio y datos de perfil de la cuenta. Usá <strong>Ninguno</strong> si simplemente querés rechazar archivos de medios." + noop: Ninguno + silence: Silenciar + suspend: Suspender + title: Nuevo bloquedo de dominio + private_comment: Comentario privado + private_comment_hint: Comentario sobre la limitación de este dominio, para uso interno de los moderadores. + public_comment: Comentario público + public_comment_hint: Comentario sobre la limitación de este dominio para el público en general, si está habilitada la publicación de lista de limitaciones de dominio. + reject_media: Rechazar archivos de medio + reject_media_hint: Quita los archivos de medio almacenados e impide la descarga en el futuro. Irrelevante para suspensiones. + reject_reports: Rechazar denuncias + reject_reports_hint: Ignora todas las denuncias que vengan de este dominio. Irrelevante para suspensiones. + rejecting_media: rechazando archivos de medio + rejecting_reports: rechazando denuncias + severity: + silence: silenciado + suspend: suspendido + show: + affected_accounts: + one: Una cuenta afectada en la base de datos + other: "%{count} cuentas afectadas en la base de datos" + retroactive: + silence: Quitar silenciado de las cuentas existentes afectadas de este dominio + suspend: Quitar suspensión de las cuentas existentes afectadas de este dominio + title: Deshacer el bloqueo de dominio para %{domain} + undo: Deshacer + undo: Deshacer bloqueo de dominio + view: Ver bloqueo de dominio + email_domain_blocks: + add_new: Agregar nuevo + created_msg: Se desaprobó dominio de correo electrónico exitosamente + delete: Eliminar + destroyed_msg: Se aprobó dominio de correo electrónico exitosamente + domain: Dominio + new: + create: Agregar dominio + title: Nueva desaprobación de correo electrónico + title: Desaprobación de correo electrónico + followers: + back_to_account: Volver a la cuenta + title: Seguidores de %{acct} + instances: + by_domain: Dominio + delivery_available: La entrega está disponible + known_accounts: + one: "%{count} cuenta conocida" + other: "%{count} cuentas conocidas" + moderation: + all: Todas + limited: Limitadas + title: Moderación + private_comment: Comentario privado + public_comment: Comentario público + title: Federación + total_blocked_by_us: Bloqueada por nosotros + total_followed_by_them: Seguidos por ellos + total_followed_by_us: Seguidos por nosotros + total_reported: Denuncias sobre ellos + total_storage: Adjuntos + invites: + deactivate_all: Desactivar todas + filter: + all: Todas + available: Disponibles + expired: Vencidas + title: Filtrar + title: Invitaciones + pending_accounts: + title: Cuentas pendientes (%{count}) + relays: + add_new: Agregar nuevo relé + delete: Eliminar + description_html: Un <strong>relé de federación</strong> es un servidor intermedio que intercambia grandes volúmenes de toots públicos entre servidores que se suscriben y publican en él. <strong>Puede ayudar a servidores chicos y medianos a descubrir contenido del fediverso</strong>, que de otra manera requerirÃa que los usuarios locales siguiesen manualmente a personas de servidores remotos. + disable: Deshabilitar + disabled: Deshabilitado + enable: Habilitar + enable_hint: Una vez habilitado, tu servidor se suscribirá a todos los toots públicos desde este relé, y comenzará a enviar los toots públicos de este servidor al relé. + enabled: Habilitado + inbox_url: Dirección web del relé + pending: Esperando aprobación del relé + save_and_enable: Guardar y habilitar + errors: + '400': The request you submitted was invalid or malformed. + '403': You don't have permission to view this page. + '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. + '410': The page you were looking for doesn't exist here anymore. + '422': + '429': Throttled + '500': + '503': The page could not be served due to a temporary server failure. + invites: + expires_in: + '1800': 30 minutes + '21600': 6 hours + '3600': 1 hour + '43200': 12 hours + '604800': 1 week + '86400': 1 day diff --git a/config/locales/es.yml b/config/locales/es.yml index 49765cd0a300477c95dee7608cd19951645cb9d9..8a194fdc17252405be083c67d7b8e9a072f2efe2 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1,25 +1,28 @@ --- es: about: - about_hashtag_html: Estos son toots públicos etiquetados con <strong>#%{hashtag}</strong>. Puedes interactuar con ellos si tienes una cuenta en cualquier parte del fediverso. - about_mastodon_html: Mastodon es un servidor de red social <em>libre y de código abierto</em>. Una alternativa <em>descentralizada</em> a plataformas comerciales, que evita el riesgo de que una única compañÃa monopolice tu comunicación. Cualquiera puede ejecutar Mastodon y participar sin problemas en la <em>red social</em>. - about_this: Acerca de esta instancia + about_hashtag_html: Estos son toots públicos etiquetados con <strong>#%{hashtag}</strong>. Puedes interactuar con ellos si tienes una cuenta en el fediverso. + about_mastodon_html: Mastodon es una red social basada en protocolos web abiertos y software libre y de código abierto. Está descentralizado como correo electrónico. + about_this: Información active_count_after: activo + active_footnote: Usuarios Activos Mensuales (UAM) administered_by: 'Administrado por:' api: API apps: Aplicaciones móviles + apps_platforms: Utiliza Mastodon desde iOS, Android y otras plataformas + browse_directory: Navega por el directorio de perfiles y filtra por intereses + browse_public_posts: Navega por un transmisión en vivo de publicaciones públicas en Mastodon contact: Contacto contact_missing: No especificado contact_unavailable: N/A discover_users: Descubrir usuarios documentation: Documentación - extended_description_html: | - <h3>Un buen lugar para las reglas</h3> - <p>La descripción extendida no se ha colocado aún.</p> federation_hint_html: Con una cuenta en %{instance} usted podrá seguir a las personas en cualquier servidor de Mastodon y más allá. - generic_description: "%{domain} es un servidor en la red" get_apps: Probar una aplicación móvil hosted_on: Mastodon hosteado en %{domain} + instance_actor_flash: | + Esta cuenta es un actor virtual usado para representar al servidor y no a ningún usuario individual. + Se usa para fines federativos y no debe ser bloqueado a menos que usted quiera bloquear toda la instancia, en cuyo caso se debe utilizar un bloque de dominio. learn_more: Aprende más privacy_policy: PolÃtica de privacidad see_whats_happening: Ver lo que está pasando @@ -31,6 +34,14 @@ es: status_count_before: Qué han escrito tagline: Seguir a amigos existentes y descubre nuevos terms: Condiciones de servicio + unavailable_content: Contenido no disponible + unavailable_content_description: + domain: Servidor + reason: 'Motivo:' + rejecting_media: Los archivos multimedia de este servidor no serán procesados y no se mostrarán miniaturas, lo que requiere un clic manual en el otro servidor. + silenced: Las publicaciones de este servidor no se mostrarán en ningún lugar salvo en el Inicio si sigues al autor. + suspended: No podrás seguir a nadie de este servidor, y ningún dato de este será procesado o almacenado, y no se intercambiarán datos. + unavailable_content_html: Mastodon generalmente le permite ver contenido e interactuar con usuarios de cualquier otro servidor en el fediverso. Estas son las excepciones que se han hecho en este servidor en particular. user_count_after: one: usuario other: usuarios @@ -38,6 +49,8 @@ es: what_is_mastodon: "¿Qué es Mastodon?" accounts: choices_html: 'Elecciones de %{name}:' + endorsements_hint: Puedes recomendar a gente que sigues desde la interfaz web, y aparecerán allÃ. + featured_tags_hint: Puede presentar hashtags especÃficos que se mostrarán aquÃ. follow: Seguir followers: one: Seguidor @@ -49,6 +62,7 @@ es: media: Multimedia moved_html: "%{name} se ha trasladado a %{new_profile_link}:" network_hidden: Esta información no está disponible + never_active: Nunca nothing_here: "¡No hay nada aquÃ!" people_followed_by: Usuarios a quien %{name} sigue people_who_follow: Usuarios que siguen a %{name} @@ -111,6 +125,7 @@ es: inbox_url: URL de la bandeja de entrada invited_by: Invitado por ip: IP + joined: Unido location: all: Todos local: Local @@ -178,6 +193,7 @@ es: username: Nombre de usuario warn: Adevertir web: Web + whitelisted: Añadido a la lista blanca action_logs: actions: assigned_to_self_report: "%{name} se ha asignado la denuncia %{target} a sà mismo" @@ -213,19 +229,24 @@ es: deleted_status: "(estado borrado)" title: Log de auditorÃa custom_emojis: + assign_category: Asignar categorÃa by_domain: Dominio copied_msg: Copia local del emoji creada con éxito copy: Copiar copy_failed_msg: No se pudo realizar una copia local de ese emoji + create_new_category: Crear una nueva categorÃa created_msg: "¡Emoji creado con éxito!" delete: Borrar destroyed_msg: "¡Emojo destruido con éxito!" disable: Deshabilitar + disabled: Desactivado disabled_msg: Se deshabilitó con éxito ese emoji emoji: Emoji enable: Habilitar + enabled: Activado enabled_msg: Se habilitó con éxito ese emoji image_hint: PNG de hasta 50KB + list: Lista listed: Listados new: title: Añadir nuevo emoji personalizado @@ -233,11 +254,14 @@ es: shortcode: Código de atajo shortcode_hint: Al menos 2 caracteres, solo caracteres alfanuméricos y guiones bajos title: Emojis personalizados + uncategorized: Sin clasificar + unlist: No listado unlisted: Sin listar update_failed_msg: No se pudo actualizar ese emoji updated_msg: "¡Emoji actualizado con éxito!" upload: Subir dashboard: + authorized_fetch_mode: Modo seguro backlog: trabajos de backlog config: Configuración feature_deletions: Borrados de cuenta @@ -245,9 +269,13 @@ es: feature_profile_directory: Directorio de perfil feature_registrations: Registros feature_relay: Relés de federación + feature_spam_check: Contra-spam + feature_timeline_preview: Vista previa de la lÃnea de tiempo features: CaracterÃsticas hidden_service: Federación con servicios ocultos open_reports: informes abiertos + pending_tags: hashtags esperando revisión + pending_users: usuarios esperando por revisión recent_users: Usuarios recientes search: Búsqueda por texto completo single_user_mode: Modo único usuario @@ -259,11 +287,18 @@ es: week_interactions: interacciones esta semana week_users_active: activo esta semana week_users_new: usuarios esta semana + whitelist_mode: En la lista blanca + domain_allows: + add_new: Añadir dominio a la lista blanca + created_msg: Dominio añadido a la lista blanca con éxito + destroyed_msg: Dominio quitado de la lista blanca con éxito + undo: Quitar de la lista blanca domain_blocks: add_new: Añadir nuevo created_msg: El bloque de dominio está siendo procesado destroyed_msg: El bloque de dominio se deshizo domain: Dominio + edit: Editar nuevo dominio bloqueado existing_domain_block_html: Ya ha impuesto lÃmites más estrictos a %{name}, necesita <a href="%{unblock_url}">desbloquearlo primero</a>. new: create: Crear bloque @@ -274,11 +309,16 @@ es: silence: Silenciar suspend: Suspender title: Nuevo bloque de dominio + private_comment: Comentario privado + private_comment_hint: Comentario sobre esta limitación de dominio para el uso interno por parte de los moderadores. + public_comment: Comentario público + public_comment_hint: Comentario sobre esta limitación de dominio para el público en general, si la publicidad de la lista de limitaciones de dominio está habilitada. reject_media: Rechazar archivos multimedia reject_media_hint: Remueve localmente archivos multimedia almacenados para descargar cualquiera en el futuro. Irrelevante para suspensiones reject_reports: Rechazar informes reject_reports_hint: Ignore todos los reportes de este dominio. Irrelevante para suspensiones rejecting_media: rechazar archivos multimedia + rejecting_reports: rechazando informes severity: silence: silenciado suspend: susependido @@ -292,6 +332,7 @@ es: title: Deshacer bloque de dominio para %{domain} undo: Deshacer undo: Deshacer + view: Ver dominio bloqueado email_domain_blocks: add_new: Añadir nuevo created_msg: Dominio de correo añadido a la lista negra con éxito @@ -304,16 +345,24 @@ es: title: Lista negra de correo followers: back_to_account: Volver a la cuenta + title: Seguidores de %{acct} instances: by_domain: Dominio + delivery_available: Entrega disponible + known_accounts: + one: "%{count} cuenta conocida" + other: "%{count} cuentas conocidas" moderation: all: Todos limited: Limitado title: Moderación + private_comment: Comentario privado + public_comment: Comentario público title: Instancias conocidas total_blocked_by_us: Bloqueado por nosotros total_followed_by_them: Seguidos por ellos total_followed_by_us: Seguido por nosotros + total_reported: Informes sobre ellas total_storage: Archivos multimedia invites: deactivate_all: Desactivar todos @@ -323,6 +372,8 @@ es: expired: Expiradas title: Filtrar title: Invitaciones + pending_accounts: + title: Cuentas pendientes (%{count}) relays: add_new: Añadir un nuevo relés delete: Borrar @@ -336,6 +387,7 @@ es: pending: Esperando la aprobación del relés save_and_enable: Guardar y conectar setup: Preparar una conexión de relés + signatures_not_enabled: Los relés no funcionarán correctamente mientras el modo seguro o el modo de lista blanca estén habilitados status: Estado title: Releses report_notes: @@ -384,6 +436,16 @@ es: custom_css: desc_html: Modificar el aspecto con CSS cargado en cada página title: CSS personalizado + default_noindex: + desc_html: Afecta a todos los usuarios que no han cambiado esta configuración por sà mismos + title: Optar por los usuarios fuera de la indexación en los motores de búsqueda por defecto + domain_blocks: + all: A todos + disabled: A nadie + title: Mostrar dominios bloqueados + users: Para los usuarios locales que han iniciado sesión + domain_blocks_rationale: + title: Mostrar la razón de ser hero: desc_html: Mostrado en la página principal. Recomendable al menos 600x100px. Por defecto se establece a la miniatura de la instancia title: Imagen de portada @@ -396,6 +458,9 @@ es: preview_sensitive_media: desc_html: Los enlaces de vistas previas en otras web mostrarán una miniatura incluso si el medio está marcado como contenido sensible title: Mostrar contenido sensible en previews de OpenGraph + profile_directory: + desc_html: Permitir que los usuarios puedan ser descubiertos + title: Habilitar directorio de perfiles registrations: closed_message: desc_html: Se muestra en la portada cuando los registros están cerrados. Puedes usar tags HTML @@ -408,8 +473,10 @@ es: title: Permitir invitaciones de registrations_mode: modes: + approved: Se requiere aprobación para registrarse none: Nadie puede registrarse open: Cualquiera puede registrarse + title: Modo de registros show_known_fediverse_at_about_page: desc_html: Cuando esté activado, se mostrarán toots de todo el fediverso conocido en la vista previa. En otro caso, se mostrarán solamente toots locales. title: Mostrar fediverso conocido en la vista previa de la historia @@ -429,6 +496,9 @@ es: desc_html: Puedes escribir tus propias polÃticas de privacidad, términos de servicio u otras legalidades. Puedes usar tags HTML title: Términos de servicio personalizados site_title: Nombre de instancia + spam_check_enabled: + desc_html: Mastodon puede silenciar y reportar cuentas automáticamente usando medidas como detectar cuentas que envÃan mensajes no solicitados repetidos. Puede que haya falsos positivos. + title: Contra-spam thumbnail: desc_html: Se usa para muestras con OpenGraph y APIs. Se recomienda 1200x630px title: Portada de instancia @@ -436,12 +506,19 @@ es: desc_html: Mostrar lÃnea de tiempo pública en la portada title: Previsualización title: Ajustes del sitio + trendable_by_default: + desc_html: Afecta a etiquetas que no han sido previamente rechazadas + title: Permitir que las etiquetas sean tendencia sin revisión previa + trends: + desc_html: Mostrar públicamente hashtags previamente revisados que son tendencia + title: Hashtags de tendencia statuses: back_to_account: Volver a la cuenta batch: delete: Eliminar nsfw_off: Marcar contenido como no sensible nsfw_on: Marcar contenido como sensible + deleted: Eliminado failed_to_execute: Falló al ejecutar media: title: Multimedia @@ -449,26 +526,58 @@ es: no_status_selected: No se cambió ningún estado al no seleccionar ninguno title: Estado de las cuentas with_media: Con multimedia - subscriptions: - callback_url: URL del callback - confirmed: Confirmado - expires_in: Expira en - last_delivery: Última entrega - topic: Tópico + tags: + accounts_today: Usos únicos de hoy + accounts_week: Usos únicos esta semana + breakdown: Desglose del consumo actual por fuentes + context: Contexto + directory: En el directorio + in_directory: "%{count} en el directorio" + last_active: Última actividad + most_popular: Más popular + most_recent: Más reciente + name: Hashtag + review: Estado de revisión + reviewed: Revisado + title: Etiquetas + trending_right_now: En tendencia ahora mismo + unique_uses_today: "%{count} publicando hoy" + unreviewed: No revisado + updated_msg: Hashtags actualizados exitosamente title: Administración warning_presets: add_new: Añadir nuevo delete: Borrar edit: Editar + edit_preset: Editar aviso predeterminado + title: Editar configuración predeterminada de avisos admin_mailer: new_pending_account: body: Los detalles de la nueva cuenta están abajos. Puedes aprobar o rechazar esta aplicación. + subject: Nueva cuenta para revisión en %{instance} (%{username}) new_report: body: "%{reporter} ha reportado a %{target}" body_remote: Alguien de %{domain} a reportado a %{target} subject: Nuevo reporte para la %{instance} (#%{id}) + new_trending_tag: + body: 'El hashtag #%{name} está en tendencia hoy, pero no ha sido revisado previamente. No se mostrará públicamente a menos que lo permita, o simplemente guarde el formulario como para no volver a ver esto.' + subject: Nuevo hashtag para revisión en %{instance} (#%{name}) + aliases: + add_new: Crear alias + created_msg: El nuevo alias se ha creado correctamente. Ahora puedes empezar el traslado desde la cuenta antigua. + deleted_msg: Elimina el alias correctamente. El traslado de esa cuenta a esta ya no será posible. + hint_html: Si quieres migrar de otra cuenta a esta, aquà puedes crear un alias, es necesario proceder antes de empezar a mover seguidores de la cuenta anterior a esta. Esta acción por sà misma es <strong>inofensiva y reversible</strong>. <strong>La migración de la cuenta se inicia desde la cuenta antigua</strong>. + remove: Desvincular alias + appearance: + advanced_web_interface: Interfaz web avanzada + advanced_web_interface_hint: 'Si desea utilizar todo el ancho de pantalla, la interfaz web avanzada le permite configurar varias columnas diferentes para ver tanta información al mismo tiempo como quiera: Inicio, notificaciones, lÃnea de tiempo federada, cualquier número de listas y etiquetas.' + animations_and_accessibility: Animaciones y accesibilidad + confirmation_dialogs: Diálogos de confirmación + discovery: Descubrir + sensitive_content: Contenido sensible application_mailer: notification_preferences: Cambiar preferencias de correo electrónico + salutation: "%{name}," settings: 'Cambiar preferencias de correo: %{link}' view: 'Vista:' view_profile: Ver perfil @@ -482,11 +591,16 @@ es: warning: Ten mucho cuidado con estos datos. ¡No los compartas con nadie! your_token: Tu token de acceso auth: + apply_for_account: Solicitar una invitación change_password: Contraseña checkbox_agreement_html: Acepto <a href="%{rules_path}" target="_blank">las reglas del servidor</a> y <a href="%{terms_path}" target="_blank">términos de servicio</a> - confirm_email: Confirmar email + checkbox_agreement_without_rules_html: Acepto los <a href="%{terms_path}" target="_blank">términos de servicio</a> delete_account: Borrar cuenta delete_account_html: Si desea eliminar su cuenta, puede <a href="%{path}">proceder aquÃ</a>. Será pedido de una confirmación. + description: + prefix_invited_by_user: "¡@%{name} te invita a unirte a este servidor de Mastodon!" + prefix_sign_up: "¡Únete a Mastodon hoy!" + suffix: "¡Con una cuenta podrás seguir a gente, publicar novedades e intercambiar mensajes con usuarios de cualquier servidor de Mastodon y más!" didnt_get_confirmation: "¿No recibió el correo de confirmación?" forgot_password: "¿Olvidaste tu contraseña?" invalid_reset_password_token: El token de reinicio de contraseña es inválido o expiró. Por favor pide uno nuevo. @@ -495,12 +609,26 @@ es: migrate_account: Mudarse a otra cuenta migrate_account_html: Si deseas redireccionar esta cuenta a otra distinta, puedes <a href="%{path}">configurarlo aquÃ</a>. or_log_in_with: O inicia sesión con + providers: + cas: CAS + saml: SAML register: Registrarse registration_closed: "%{instance} no está aceptando nuevos miembros" resend_confirmation: Volver a enviar el correo de confirmación reset_password: Restablecer contraseña security: Cambiar contraseña set_new_password: Establecer nueva contraseña + setup: + email_below_hint_html: Si la dirección de correo electrónico que aparece a continuación es incorrecta, se puede cambiarla aquà y recibir un nuevo correo electrónico de confirmación. + email_settings_hint_html: El correo electrónico de confirmación fue enviado a %{email}. Si esa dirección de correo electrónico no sea correcta, se puede cambiarla en la configuración de la cuenta. + title: Configuración + status: + account_status: Estado de la cuenta + confirming: Esperando confirmación de correo electrónico. + functional: Su cuenta está totalmente operativa. + pending: Su solicitud está pendiente de revisión por nuestros administradores. Eso puede tardar algún tiempo. Usted recibirá un correo electrónico si el solicitud sea aprobada. + redirecting_to: Tu cuenta se encuentra inactiva porque está siendo redirigida a %{acct}. + trouble_logging_in: "¿Problemas para iniciar sesión?" authorize_follow: already_following: Ya estás siguiendo a esta cuenta error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota @@ -512,29 +640,53 @@ es: return: Regresar al perfil del usuario web: Ir al sitio web title: Seguir a %{acct} + challenge: + confirm: Continuar + hint_html: "<strong>Tip:</strong> No volveremos a preguntarte por la contraseña durante la siguiente hora." + invalid_password: Contraseña incorrecta + prompt: Confirmar contraseña para seguir datetime: distance_in_words: + about_x_hours: "%{count}h" about_x_months: "%{count}m" + about_x_years: "%{count}a" + almost_x_years: "%{count}a" half_a_minute: Justo ahora + less_than_x_minutes: "%{count}m" less_than_x_seconds: Justo ahora + over_x_years: "%{count}a" + x_days: "%{count}d" + x_minutes: "%{count}m" x_months: "%{count}m" + x_seconds: "%{count}s" deletes: - bad_password_msg: "¡Buen intento, hackers! Contraseña incorrecta" + challenge_not_passed: Los datos introducidos son incorrectos confirm_password: Ingresa tu contraseña actual para demostrar tu identidad - description_html: Esto removerá el contenido de tu cuenta y la desactivará <strong>permanente e irrevesiblemente</strong>. Tu nombre de usuario quedará reservado para prevenir futuros robos de identidad. + confirm_username: Escribe tu nombre de usuario para confirmar proceed: Eliminar cuenta success_msg: Tu cuenta se eliminó con éxito - warning_html: Se garantiza únicamente la eliminación del contenido de esta instancia. El contenido que se haya compartido extensamente dejará sus huellas. Los servidores fuera de lÃnea y los que se hayan desuscrito de tus actualizaciones ya no actualizarán sus bases de datos. - warning_title: Disponibilidad diseminada del contenido + warning: + before: 'Antes de continuar, por favor lee con atención las siguientes notas:' + caches: El contenido que ha sido almacenado en caché por otros servidores puede persistir + data_removal: Tus publicaciones y el resto de datos se eliminarán definitivamente + email_change_html: Puedes <a href="%{path}"> cambiar tu dirección de correo electrónico</a> sin eliminar tu cuenta + email_contact_html: Si aún no te ha llegado, puedes escribir a <a href="mailto:%{email}">%{email}</a> para pedir ayuda + email_reconfirmation_html: Si no te ha llegado el correo de confirmación, puedes <a href="%{path}"> volver a solicitarlo</a> + irreversible: No podrás restaurar ni reactivar tu cuenta + more_details_html: Para más detalles, ver <a href="%{terms_path}"> la polÃtica de privacidad</a>. + username_available: Tu nombre de usuario volverá a estar disponible + username_unavailable: Tu nombre de usuario no estará disponible directories: + directory: Directorio de perfiles + explanation: Descubre usuarios según sus intereses explore_mastodon: Explorar %{title} - how_to_enable: Usted no está registrado por el directorio. Puede registrar por abajo. ¡Utilice hashtags en su bio para aparecer bajo hashtags especÃficos! - people: - one: "%{count} persona" - other: "%{count} personas" + domain_validator: + invalid_domain: no es un nombre de dominio válido errors: + '400': La solicitud que has enviado no es valida o estaba malformada. '403': No tienes permiso para acceder a esta página. '404': La página que estabas buscando no existe. + '406': Esta página no está disponible en el formato solicitado. '410': La página que estabas buscando no existe más. '422': content: Verificación de seguridad fallida. ¿Estás bloqueando algunas cookies? @@ -543,6 +695,7 @@ es: '500': content: Lo sentimos, algo ha funcionado mal por nuestra parte. title: Esta página no es correcta + '503': La página no se ha podido cargar debido a un fallo temporal del servidor. noscript_html: Para usar la aplicación web de Mastodon, por favor activa Javascript. Alternativamente, prueba alguna de las <a href="%{apps_path}">aplicaciones nativas</a> para Mastodon para tu plataforma. existing_username_validator: not_found: no pudo encontrar un usuario local con ese nombre de usuario @@ -557,6 +710,7 @@ es: size: Tamaño blocks: Personas que has bloqueado csv: CSV + domain_blocks: Bloqueos de dominios follows: Personas que sigues lists: Listas mutes: Tienes en silencio @@ -565,6 +719,7 @@ es: add_new: Añadir nuevo errors: limit: Ya has alcanzado la cantidad máxima de hashtags + hint_html: "<strong>¿Qué son las etiquetas destacadas?</strong> Se muestran de forma prominente en tu perfil público y permiten a los usuarios navegar por tus publicaciones públicas especÃficamente bajo esas etiquetas. Son una gran herramienta para hacer un seguimiento de trabajos creativos o proyectos a largo plazo." filters: contexts: home: Timeline propio @@ -585,15 +740,19 @@ es: developers: Desarrolladores more: Mas… resources: Recursos + trending_now: Tendencia ahora generic: all: Todos changes_saved_msg: "¡Cambios guardados con éxito!" copy: Copiar + no_batch_actions_available: No hay acciones por lotes disponibles en esta página order_by: Ordenar por save_changes: Guardar cambios validation_errors: one: "¡Algo no está bien! Por favor, revisa el error" other: "¡Algo no está bien! Por favor, revise %{count} errores más abajo" + html_validator: + invalid_markup: 'contiene código HTML no válido: %{error}' identity_proofs: active: Activo authorize: SÃ, autorizar @@ -603,18 +762,26 @@ es: keybase: invalid_token: Los tokens de Keybase son hashes de firmas y deben tener 66 caracteres hex verification_failed: Keybase no reconoce este token como una firma del usuario de Keybase %{kb_username}. Por favor, inténtelo de nuevo desde Keybase. + wrong_user: No se puede crear una prueba para %{proving} mientras se inicia sesión como %{current}. Inicia sesión como %{proving} e inténtalo de nuevo. + explanation_html: Aquà puedes conectar criptográficamente sus otras identidades, como un perfil de Keybase. Esto permite a otras personas enviarle mensajes encriptados y confiar en el contenido que les envÃas. + i_am_html: Soy %{username} en %{service}. identity: Identidad inactive: Inactivo + publicize_checkbox: 'Y tootee esto:' + publicize_toot: "¡Comprobado! Soy %{username} en %{service}: %{url}" status: Estado de la verificación view_proof: Ver prueba imports: modes: merge: Unir + merge_long: Mantener registros existentes y añadir nuevos overwrite: Sobrescribir + overwrite_long: Reemplazar registros actuales con los nuevos preface: Puedes importar ciertos datos, como todas las personas que estás siguiendo o bloqueando en tu cuenta en esta instancia, desde archivos exportados de otra instancia. success: Sus datos se han cargado correctamente y serán procesados en brevedad types: blocking: Lista de bloqueados + domain_blocking: Lista de dominios bloqueados following: Lista de seguidos muting: Lista de silenciados upload: Cargar @@ -650,9 +817,34 @@ es: too_many: No se pueden adjuntar más de 4 archivos migrations: acct: username@domain de la nueva cuenta - currently_redirecting: 'Tu perfil está redireccionado a:' - proceed: Guardar - updated_msg: "¡La configuración de migración de tu cuenta ha sido actualizada con éxito!" + cancel: Cancelar redireccionamiento + cancel_explanation: Al cancelar el redireccionamiento se reactivará tu cuenta actual, pero no recuperarás los seguidores que hayan sido trasladados a la otra cuenta. + cancelled_msg: El redireccionamiento se ha cancelado correctamente. + errors: + already_moved: es la misma cuenta a la que ya has migrado + missing_also_known_as: no está haciendo referencia a esta cuenta + move_to_self: no puede ser la cuenta actual + not_found: no se pudo encontrar + on_cooldown: Estás en tiempo de reutilización + followers_count: Seguidores al momento de migrar + incoming_migrations: Migrar de una cuenta diferente + incoming_migrations_html: Para migrar de otra cuenta a esta, primero necesitas <a href="%{path}">crear un alias de la cuenta</a>. + moved_msg: Tu cuenta ahora se está redirigiendo a %{acct} y tus seguidores se están migrando. + not_redirecting: Tu cuenta no se está redirigiendo a ninguna otra cuenta actualmente. + on_cooldown: Has migrado tu cuenta recientemente. Esta función estará disponible de nuevo en %{count} dÃas. + past_migrations: Migraciones pasadas + proceed_with_move: Migrar seguidores + redirecting_to: Tu cuenta se está redirigiendo a %{acct}. + set_redirect: Establecer redirección + warning: + backreference_required: La nueva cuenta debe ser configurada primero para hacer referencia a esta + before: 'Antes de continuar, por favor lee con atención las siguientes notas:' + cooldown: Después de migrar hay un perÃodo de espera durante el cual no podrás volver a migrar + disabled_account: Tu cuenta actual no será completamente utilizable después. Sin embargo, tendrás acceso a la exportación de datos asà como a la reactivación. + followers: Esta acción migrará a todos los seguidores de la cuenta actual a la nueva cuenta + only_redirect_html: Alternativamente, solo puedes <a href="%{path}">poner una redirección en tu perfil</a>. + other_data: No se moverán otros datos automáticamente + redirect: El perfil de tu cuenta actual se actualizará con un aviso de redirección y será excluido de las búsquedas moderation: title: Moderación notification_mailer: @@ -689,26 +881,70 @@ es: body: "%{name} ha retooteado tu estado:" subject: "%{name} ha retooteado tu estado" title: Nueva difusión + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: m + trillion: T pagination: newer: Más nuevo next: Próximo older: Más antiguo prev: Anterior + truncate: "…" + polls: + errors: + already_voted: Ya has votado en esta encuesta + duplicate_options: contiene elementos duplicados + duration_too_long: está demasiado lejos en el futuro + duration_too_short: es demasiado pronto + expired: La encuesta ya ha terminado + over_character_limit: no puede exceder %{max} caracteres cada uno + too_few_options: debe tener más de un elemento + too_many_options: no puede contener más de %{max} elementos preferences: other: Otros + posting_defaults: Configuración por defecto de publicaciones + public_timelines: LÃneas de tiempo públicas relationships: + activity: Actividad de la cuenta + dormant: Inactivo last_active: Última actividad most_recent: Más reciente + moved: Movido + mutual: Mutuo + primary: Principal + relationship: Relación + remove_selected_domains: Eliminar todos los seguidores de los dominios seleccionados + remove_selected_followers: Eliminar los seguidores seleccionados + remove_selected_follows: Dejar de seguir a los usuarios seleccionados + status: Estado de la cuenta remote_follow: acct: Ingesa tu usuario@dominio desde el que quieres seguir missing_resource: No se pudo encontrar la URL de redirección requerida para tu cuenta no_account_html: "¿No tienes una cuenta? Puedes <a href='%{sign_up_path}' target='_blank'>registrarte aqui</a>" proceed: Proceder a seguir prompt: 'Vas a seguir a:' - remote_unfollow: - error: Error - title: TÃtulo - unfollowed: Ha dejado de seguirse + reason_html: "¿<strong>¿Por qué es necesario este paso?</strong> <code>%{instance}</code> puede que no sea el servidor donde estás registrado, asà que necesitamos redirigirte primero a tu servidor de origen." + remote_interaction: + favourite: + proceed: Proceder a marcar como favorito + prompt: 'Quieres marcar como favorito este toot:' + reblog: + proceed: Proceder a retootear + prompt: 'Quieres retootear este toot:' + reply: + proceed: Proceder a responder + prompt: 'Quieres responder a este toot:' + scheduled_statuses: + over_daily_limit: Ha superado el lÃmite de %{limit} toots programados para ese dÃa + over_total_limit: Ha superado el lÃmite de %{limit} toots programados + too_soon: La fecha programada debe estar en el futuro sessions: activity: Última actividad browser: Navegador @@ -753,6 +989,7 @@ es: settings: account: Cuenta account_settings: Ajustes de la cuenta + aliases: Alias de la cuenta appearance: Apariencia authorized_apps: Aplicaciones autorizadas back: Volver al inicio @@ -761,11 +998,17 @@ es: edit_profile: Editar perfil export: Exportar información featured_tags: Hashtags destacados + identity_proofs: Pruebas de identidad import: Importar + import_and_export: Importar y exportar migrate: Migración de cuenta notifications: Notificaciones preferences: Preferencias + profile: Perfil + relationships: Siguiendo y seguidores two_factor_authentication: Autenticación de dos factores + spam_check: + spam_detected: Este es un informe automatizado. Se ha detectado Spam. statuses: attached: description: 'Adjunto: %{attached}' @@ -788,8 +1031,17 @@ es: ownership: El toot de alguien más no puede fijarse private: Los toots no-públicos no pueden fijarse reblog: Un boost no puede fijarse + poll: + total_people: + one: persona %{count} + other: "%{count} gente" + total_votes: + one: "%{count} voto" + other: "%{count} votos" + vote: Vota show_more: Mostrar más sign_in_to_participate: RegÃstrate para participar en la conversación + title: '%{name}: "%{quote}"' visibilities: private: Sólo mostrar a seguidores private_long: Solo mostrar a tus seguidores @@ -801,7 +1053,90 @@ es: pinned: Toot fijado reblogged: retooteado sensitive_content: Contenido sensible + tags: + does_not_match_previous_name: no coincide con el nombre anterior terms: + body_html: | + <h2>PolÃtica de Privacidad</h2> + <h3 id="collect">¿Qué información recogemos?</h3> + + <ul> + <li><em>Información básica sobre su cuenta</em>: Si se registra en este servidor, se le requerirá un nombre de usuario, una dirección de correo electrónico y una contraseña. Además puede incluir información adicional en el perfil como un nombre de perfil y una biografÃa, y subir una foto de perfil y una imagen de cabecera. El nombre de usuario, nombre de perfil, biografÃa, foto de perfil e imagen de cabecera siempre son visibles públicamente</li> + <li><em>Publicaciones, seguimiento y otra información pública</em>: La lista de gente a la que sigue es mostrada públicamente, al igual que sus seguidores. Cuando publica un mensaje, la fecha y hora es almacenada, asà como la aplicación desde la cual publicó el mensaje. Los mensajes pueden contener archivos adjuntos multimedia, como imágenes y vÃdeos. Las publicaciones públicas y no listadas están disponibles públicamente. Cuando destaca una entrada en su perfil, también es información disponible públicamente. Sus publicaciones son entregadas a sus seguidores, en algunos casos significa que son entregadas a diferentes servidores y las copias son almacenadas allÃ. Cuando elimina publicaciones, esto también se transfiere a sus seguidores. La acción de rebloguear o marcar como favorito otra publicación es siempre pública.</li> + <li><em>Publicaciones directas y sólo para seguidores</em>: Todos los mensajes se almacenan y procesan en el servidor. Los mensajes sólo para seguidores se entregan a los seguidores y usuarios que se mencionan en ellos, y los mensajes directos se entregan sólo a los usuarios que se mencionan en ellos. En algunos casos significa que se entregan a diferentes servidores y que las copias se almacenan allÃ. Hacemos un esfuerzo de buena fe para limitar el acceso a esas publicaciones sólo a las personas autorizadas, pero otros servidores pueden no hacerlo. Por lo tanto, es importante revisar los servidores a los que pertenecen sus seguidores. Puede cambiar una opción para aprobar y rechazar nuevos seguidores manualmente en la configuración <em>Por favor, tenga en cuenta que los operadores del servidor y de cualquier servidor receptor pueden ver dichos mensajes</em>, y que los destinatarios pueden capturarlos, copiarlos o volver a compartirlos de alguna otra manera. <em>No comparta ninguna información peligrosa en Mastodon.</em></li> + <li><em>Direcciones IP y otros metadatos</em>: Al iniciar sesión, registramos la dirección IP desde la que se ha iniciado sesión, asà como el nombre de la aplicación de su navegador. Todas las sesiones iniciadas están disponibles para su revisión y revocación en los ajustes. La última dirección IP utilizada se almacena hasta 12 meses. También podemos conservar los registros del servidor que incluyen la dirección IP de cada solicitud a nuestro servidor.</li> + </ul> + + <hr class="spacer" /> + + <h3 id="use">¿Para qué utilizamos su información?</h3> + + <p>Toda la información que obtenemos de usted puede ser utilizada de las siguientes maneras:</p> + + <ul> + <li>Para proporcionar la funcionalidad principal de Mastodon. Sólo puedes interactuar con el contenido de otras personas y publicar tu propio contenido cuando estés conectado. Por ejemplo, puedes seguir a otras personas para ver sus mensajes combinados en tu propia lÃnea de tiempo personalizada.</li> + <li>Para ayudar a la moderación de la comunidad, por ejemplo, comparando su dirección IP con otras conocidas para determinar la evasión de prohibiciones u otras violaciones.</li> + <li>La dirección de correo electrónico que nos proporcione podrá utilizarse para enviarle información, notificaciones sobre otras personas que interactúen con su contenido o para enviarle mensajes, asà como para responder a consultas y/u otras solicitudes o preguntas.</li> + </ul> + + <hr class="spacer" /> + + <h3 id="protect">¿Cómo protegemos su información?</h3> + + <p>Implementamos una variedad de medidas de seguridad para mantener la seguridad de su información personal cuando usted ingresa, envÃa o accede a su información personal. Entre otras cosas, la sesión de su navegador, asà como el tráfico entre sus aplicaciones y la API, están protegidos con SSL, y su contraseña está protegida mediante un algoritmo unidireccional fuerte. Puede habilitar la autenticación de dos factores para un acceso más seguro a su cuenta.</p> + + <hr class="spacer" /> + + <h3 id="data-retention">¿Cuál es nuestra polÃtica de retención de datos?</h3> + + <p>Haremos un esfuerzo de buena fe para:</p> + + <ul> + <li>Conservar los registros del servidor que contengan la dirección IP de todas las peticiones a este servidor, en la medida en que se mantengan dichos registros, no más de 90 dÃas.</li> + <li>Conservar las direcciones IP asociadas a los usuarios registrados no más de 12 meses.</li> + </ul> + + <p>Puede solicitar y descargar un archivo de su contenido, incluidos sus mensajes, archivos adjuntos multimedia, foto de perfil e imagen de cabecera.</p> + + <p>Usted puede borrar su cuenta de forma irreversible en cualquier momento.</p> + + <hr class="spacer"/> + + <h3 id="cookies">¿Utilizamos cookies?</h3> + + <p>SÃ. Las cookies son pequeños archivos que un sitio o su proveedor de servicios transfiere al disco duro de su ordenador a través de su navegador web (si usted lo permite). Estas cookies permiten al sitio reconocer su navegador y, si tiene una cuenta registrada, asociarla con su cuenta registrada.</p> + + <p>Utilizamos cookies para entender y guardar sus preferencias para futuras visitas.</p> + + <hr class="spacer" /> + + <h3 id="disclose">¿Revelamos alguna información a terceros?</h3> + + <p>No vendemos, comerciamos ni transferimos a terceros su información personal identificable. Esto no incluye a los terceros de confianza que nos asisten en la operación de nuestro sitio, en la realización de nuestros negocios o en la prestación de servicios, siempre y cuando dichas partes acuerden mantener la confidencialidad de esta información. También podemos divulgar su información cuando creamos que es apropiado para cumplir con la ley, hacer cumplir las polÃticas de nuestro sitio, o proteger nuestros u otros derechos, propiedad o seguridad.</p> + + <p>Su contenido público puede ser descargado por otros servidores de la red. Tus mensajes públicos y sólo para seguidores se envÃan a los servidores donde residen tus seguidores, y los mensajes directos se envÃan a los servidores de los destinatarios, en la medida en que dichos seguidores o destinatarios residan en un servidor diferente.</p> + + <p>Cuando usted autoriza a una aplicación a usar su cuenta, dependiendo del alcance de los permisos que usted apruebe, puede acceder a la información de su perfil público, su lista de seguimiento, sus seguidores, sus listas, todos sus mensajes y sus favoritos. Las aplicaciones nunca podrán acceder a su dirección de correo electrónico o contraseña.</p> + + <hr class="spacer" /> + + <h3 id="children">Uso del sitio por parte de los niños</h3> + + <p>Si este servidor está en la UE o en el EEE: Nuestro sitio, productos y servicios están dirigidos a personas mayores de 16 años. Si es menor de 16 años, según los requisitos de la GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation_Data_Protection_Regulation">General Data Protection Regulation</a>) no utilice este sitio.</p> + + <p>Si este servidor está en los EE.UU.: Nuestro sitio, productos y servicios están todos dirigidos a personas que tienen al menos 13 años de edad. Si usted es menor de 13 años, según los requisitos de COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) no utilice este sitio.</p> + + <p>Los requisitos legales pueden ser diferentes si este servidor está en otra jurisdicción.</p> + + <hr class="spacer" /> + + <h3 id="changes">Cambios en nuestra PolÃtica de Privacidad</h3> + + <p>Si decidimos cambiar nuestra polÃtica de privacidad, publicaremos esos cambios en esta página.</p> + + <p>Este documento es CC-BY-SA. Fue actualizado por última vez el 7 de marzo de 2018.</p> + + <p>Adaptado originalmente desde <a href="https://github.com/discourse/discourse">la polÃtica de privacidad de Discourse</a>.</p> title: Términos del Servicio y PolÃticas de Privacidad de %{instance} themes: contrast: Alto contraste @@ -810,6 +1145,7 @@ es: time: formats: default: "%d de %b del %Y, %H:%M" + month: "%b %Y" two_factor_authentication: code_hint: Ingresa el código generado por tu aplicación de autenticación para confirmar description_html: Si habilitas la <strong>autenticación de dos factores</strong>, se requerirá estar en posesión de su teléfono, lo que generará tokens para que usted pueda iniciar sesión. @@ -831,6 +1167,24 @@ es: explanation: Has solicitado una copia completa de tu cuenta de Mastodon. ¡Ya está preparada para descargar! subject: Tu archivo está preparado para descargar title: Descargar archivo + warning: + explanation: + disable: Mientras su cuenta esté congelada, la información de su cuenta permanecerá intacta, pero no puede realizar ninguna acción hasta que se desbloquee. + silence: Mientras su cuenta está limitada, sólo las personas que ya le están siguiendo verán sus toots en este servidor, y puede que se le excluya de varios listados públicos. Sin embargo, otros pueden seguirle manualmente. + suspend: Su cuenta ha sido suspendida, y todos tus toots y tus archivos multimedia subidos han sido irreversiblemente eliminados de este servidor, y de los servidores donde tenÃas seguidores. + get_in_touch: Puede responder a esta dirección de correo electrónico para ponerse en contacto con el personal de %{instance}. + review_server_policies: Revisar las polÃticas del servidor + statuses: 'EspecÃficamente, para:' + subject: + disable: Su cuenta %{acct} ha sido congelada + none: Advertencia para %{acct} + silence: Su cuenta %{acct} ha sido limitada + suspend: Su cuenta %{acct} ha sido suspendida + title: + disable: Cuenta congelada + none: Advertencia + silence: Cuenta limitada + suspend: Cuenta suspendida welcome: edit_profile_action: Configurar el perfil edit_profile_step: Puedes personalizar tu perfil subiendo un avatar, una cabecera, cambiando tu nombre de usuario y más cosas. Si quieres revisar a tus nuevos seguidores antes de que se les permita seguirte, puedes bloquear tu cuenta. @@ -846,6 +1200,7 @@ es: tip_following: Sigues a tus administradores de servidor por defecto. Para encontrar más gente interesante, revisa las lineas de tiempo local y federada. tip_local_timeline: La linea de tiempo local is una vista de la gente en %{instance}. Estos son tus vecinos inmediatos! tip_mobile_webapp: Si el navegador de tu dispositivo móvil ofrece agregar Mastodon a tu página de inicio, puedes recibir notificaciones. Actúa como una aplicación nativa en muchas formas! + tips: Consejos title: Te damos la bienvenida a bordo, %{name}! users: follow_limit_reached: No puedes seguir a más de %{limit} personas diff --git a/config/locales/et.yml b/config/locales/et.yml new file mode 100644 index 0000000000000000000000000000000000000000..6b814a23c8a220b934fd61a7b2bf0092c49a9d35 --- /dev/null +++ b/config/locales/et.yml @@ -0,0 +1,1000 @@ +--- +et: + about: + about_hashtag_html: Need on avalikud tuututused sildistatud sildiga <strong>#%{hashtag}</strong>. Te saate suhelda nendega, kui Teil on konto üks kõik kus terves fediversumis. + about_mastodon_html: Mastodon on sotsiaalvõrgustik, mis põhineb avatud protokollidel ja avatud lähtekoodiga tarkvaral. See on detsentraliseeritud nagu e-post. + about_this: Meist + active_count_after: aktiivne + active_footnote: Igakuiselt aktiivseid kasutajaid (MAU) + administered_by: 'Administraator:' + api: API + apps: Mobiilrakendused + apps_platforms: Kasuta Mastodoni iOS-is, Androidis ja teistel platvormidel + browse_directory: Sirvi profiilide kataloogi ja filtreeri huvide alusel + browse_public_posts: Sirvi reaalajas voogu avalikest postitustest Mastodonis + contact: Kontakt + contact_missing: Määramata + contact_unavailable: Pole saadaval + discover_users: Avasta kasutajaid + documentation: Dokumentatsioon + federation_hint_html: Kui Teil on kasutaja %{instance}-is, saate Te jälgida inimesi üks kõik millisel Mastodoni serveril ja kaugemalgi. + get_apps: Proovi mobiilirakendusi + hosted_on: Mastodon majutatud %{domain}-is + instance_actor_flash: | + See konto on virtuaalne näitleja, mis esindab tervet serverit ning mitte ühtegi kindlat isikut. + Seda kasutatakse föderatiivsetel põhjustel ning seda ei tohiks blokeerida, välja arvatud juhul, kui soovite blokeerida tervet serverit, kuid sellel juhul soovitame hoopis kasutada domeeni blokeerimist. + learn_more: Lisateave + privacy_policy: Privaatsuspoliitika + see_whats_happening: Vaata, mis toimub + server_stats: 'Serveri statistika:' + source_code: Lähtekood + status_count_after: + one: staatust + other: staatuseid + status_count_before: Kes omavad + tagline: Jälgi sõpru ja leia uusi + terms: Kasutustingimused + user_count_after: + one: kasutajale + other: kasutajale + user_count_before: Koduks + what_is_mastodon: Mis on Mastodon? + accounts: + choices_html: "%{name}-i valikud:" + follow: Jälgi + followers: + one: Jälgija + other: Jälgijaid + following: Jälgib + joined: Liitus %{date} + last_active: viimati aktiivne + link_verified_on: Selle lingi autorsust kontrolliti %{date} + media: Meedia + moved_html: "%{name} kolis %{new_profile_link}:" + network_hidden: Neid andmeid pole saadaval + nothing_here: Siin pole midagi! + people_followed_by: Inimesed, keda %{name} jälgib + people_who_follow: Inimesed, kes jälgivad kasutajat %{name} + pin_errors: + following: Te peate juba olema selle kasutaja jälgija, keda te heaks kiidate + posts: + one: Tuut + other: Tuututused + posts_tab_heading: Tuututused + posts_with_replies: Tuututused ja vastused + reserved_username: Kasutajanimi on reserveeritud + roles: + admin: Administraator + bot: Robot + moderator: Moderaator + unavailable: Profiil pole saadaval + unfollow: Lõpeta jälgimine + admin: + account_actions: + action: Täida tegevus + title: Rakenda moderaatori tegevus kasutajale %{acct} + account_moderation_notes: + create: Jäta teade + created_msg: Moderatsiooniteade edukalt koostatud! + delete: Kustuta + destroyed_msg: Moderatsiooniteade edukalt kustutatud! + accounts: + approve: Kinnita + approve_all: Kinnita kõik + are_you_sure: Olete kindel? + avatar: Profiilipilt + by_domain: Domeen + change_email: + changed_msg: Konto e-postiaadress edukalt muudetud! + current_email: Praegune e-postiaadress + label: Muuda e-posti aadressi + new_email: Uus е-posti aadress + submit: Muuda e-posti aadressi + title: Muuda e-postiaadressi kasutajale %{username} + confirm: Kinnita + confirmed: Kinnitatud + confirming: Kinnitamine + deleted: Kustutatud + demote: Alanda + disable: Keela + disable_two_factor_authentication: Keela 2FA + disabled: Keelatud + display_name: Kuvanimi + domain: Domeen + edit: Redigeeri + email: E-post + email_status: E-posti staatus + enable: Luba + enabled: Lubatud + feed_url: Voogu URL + followers: Jälgijad + followers_url: Jälgijate URL + follows: Jälgib + header: Päis + inbox_url: Sisendkausta URL + invited_by: Kutsuja + ip: IP + joined: Liitus + location: + all: Kõik + local: Kohalik + remote: Kaug + title: Asukoht + login_status: Sisselogimise olek + media_attachments: Lisatud meedia + memorialize: Tee memoriaaliks + moderation: + active: Aktiivne + all: Kõik + pending: Ootel + silenced: Vaigistatud + suspended: Peatatud + title: Moderatsioon + moderation_notes: Moderatsiooniteated + most_recent_activity: Kõige hiljutisem aktiivsus + most_recent_ip: Kõige hiljutisem IP + no_account_selected: Mitte ühtegi kontot muudeti sest midagi polnud valitud + no_limits_imposed: Mitte ühtegi limiiti kehtestatud + not_subscribed: Ei ole tellitud + outbox_url: Väljundkausta URL + pending: Ootab ülevaatamist + perform_full_suspension: Peata + profile_url: Profiili URL + promote: Edenda + protocol: Protokoll + public: Avalik + push_subscription_expires: PuSH tellimus aegub + redownload: Värskenda profiili + reject: Hülga + reject_all: Hülga kõik + remove_avatar: Kustuta profiilipilt + remove_header: Kustuta päis + resend_confirmation: + already_confirmed: See kasutaja on juba kinnitatud + send: Saada kinnituskiri uuesti + success: Kinnituskiri saadetud edukalt! + reset: Lähtesta + reset_password: Lähtesta salasõna + resubscribe: Telli taas + role: Õigused + roles: + admin: Administraator + moderator: Moderaator + staff: Personal + user: Kasutaja + salmon_url: Salmoni URL + search: Otsi + shared_inbox_url: Jagatud sisendkausta URL + show: + created_reports: Loodud teated + targeted_reports: Teiste poolt teatatud + silence: Vaigista + silenced: Vaigistatud + statuses: Staatuseid + subscribe: Telli + suspended: Peatatud + time_in_queue: Ootab järjekorras %{time} + title: Kontod + unconfirmed_email: Kinnitamata e-post + undo_silenced: Võta vaigistus tagasi + undo_suspension: Võta peatamine tagasi + unsubscribe: Tühista tellimus + username: Kasutajanimi + warn: Hoiata + web: Veeb + whitelisted: Lubatud + action_logs: + actions: + assigned_to_self_report: "%{name} määras teabe %{target} iseendale" + change_email_user: "%{name} muutis kasutaja %{target} e-postiaadressit" + confirm_user: "%{name} kinnitas kasutaja %{target} e-postiaadressi" + create_account_warning: "%{name} saatis kasutajale %{target} hoiatuse" + create_custom_emoji: "%{name} laadis üles uue emotikooni %{target}" + create_domain_block: "%{name} blokeeris domeeni %{target}" + create_email_domain_block: "%{name} lisas e-posti domeeni %{target} musta nimekirja" + demote_user: "%{name} alandas kasutaja %{target}" + destroy_custom_emoji: "%{name} kustutas emotikooni %{target}" + destroy_domain_block: "%{name} eemaldas blokeeringu domeenilt %{target}" + destroy_email_domain_block: "%{name} lisas e-posti domeeni %{target} lubatute nimekirja" + destroy_status: "%{name} eemaldas %{target} staatuse" + disable_2fa_user: "%{name} eemaldas kaheastmelise autentimise kohustuse kasutajalt %{target}" + disable_custom_emoji: "%{name} keelas emotikooni %{target}" + disable_user: "%{name} keelas sisselogimise kasutajal %{target}" + enable_custom_emoji: "%{name} lubas emotikooni %{target}" + enable_user: "%{name} lubas sisselogimise kasutajal %{target}" + memorialize_account: "%{name} muutis %{target}-i kasutaja memoriaaliks" + promote_user: "%{name} edendas kasutajat %{target}" + remove_avatar_user: "%{name} kustutas kasutaja %{target} profiilipildi" + reopen_report: "%{name} taasavas teate %{target}" + reset_password_user: "%{name} lähtestas parooli kasutajal %{target}" + resolve_report: "%{name} lahendas teate %{target}" + silence_account: "%{name} vaigistas %{target}-i kasutaja" + suspend_account: "%{name} peatas %{target}-i kasutaja" + unassigned_report: "%{name} eemaldas määratluse teatelt %{target}" + unsilence_account: "%{name} eemaldas vaigistuse %{target}-i kontolt" + unsuspend_account: "%{name} eemaldas peatamise %{target}-i kontolt" + update_custom_emoji: "%{name} uuendas emotikooni %{target}" + update_status: "%{name} uuendas kasutaja %{target} staatust" + deleted_status: "(kustutatud staatus)" + title: Auditilogi + custom_emojis: + by_domain: Domeen + copied_msg: Kohaliku koopia loomine emotikonist õnnestus + copy: Kopeeri + copy_failed_msg: Kohaliku koopia loomine sellest emotikonist ebaõnnestus + created_msg: Emotikoni loomine õnnestus! + delete: Kustuta + destroyed_msg: Emotikoni kustutamine õnnestus! + disable: Keela + disabled_msg: Selle emotikoni keelamine õnnestus + emoji: Emotikonid + enable: Luba + enabled_msg: Selle emotikoni lubamine õnnestus + image_hint: PNG kuni 50KB + listed: Nimekirjastatud + new: + title: Lisa uus emotikon + overwrite: Kirjuta üle + shortcode: Lühikood + shortcode_hint: Vähemalt 2 tähemärki, ainult tähted, numbrid ja alakriipsud + title: Emotikonid + unlisted: Kirjendamata + update_failed_msg: Ei saanud seda emotikoni uuendada + updated_msg: Emotikoni uuendamine õnnestus! + upload: Lae üles + dashboard: + authorized_fetch_mode: Autoriseeritud tõmberežiim + backlog: mahajäänud tööd + config: Konfiguratsioon + feature_deletions: Kontode kustutamised + feature_invites: Kutselingid + feature_profile_directory: Profiilikataloog + feature_registrations: Registreerimised + feature_relay: Föderatsiooni relee + feature_spam_check: Rämpsposti filter + feature_timeline_preview: Ajajoone eelvaade + features: Omadused + hidden_service: Föderatsioon peidetud teenustega + open_reports: ava teavitused + pending_tags: sildid ootamas ülevaadet + pending_users: kasutajad ootamas ülevaadet + recent_users: Hiljutised kasutajad + search: Täis teksti otsing + single_user_mode: Üksiku kasutaja režiim + software: Tarkvara + space: Kettakasutus + title: Töölaud + total_users: kokku kasutajaid + trends: Trendid + week_interactions: interaktsioone see nädal + week_users_active: aktiivne see nädal + week_users_new: kasutajaid see nädal + domain_allows: + add_new: Luba domeen + created_msg: Domeeni lubamine õnnestus + destroyed_msg: Domeen eemaldati lubatute nimekirjast + undo: Eemalda lubatute nimekirjast + domain_blocks: + add_new: Lisa uus domeeniblokeering + created_msg: Domeeni blokeeringut töödeldakse + destroyed_msg: Domeeniblokeering on tagasi võetud + domain: Domeen + existing_domain_block_html: Te olete juba lisanud domeenile %{name} piiranguid, palun <a href="%{unblock_url}">eemaldage blokeering</a> enne jätkamist. + new: + create: Loo blokeering + hint: Domeeniblokeering ei takista kontode lisamist andmebaasi, aga lisab nendele kontodele tagasiulatuvalt ja automaatselt erinevaid moderatsioonimeetodeid. + severity: + desc_html: |- + <strong>Vaigista</strong> teeb konto postitused nähtamatuks kõigile, kes teda ei jälgi. + <strong>Peata</strong> eemaldab kogu konto sisu, meedia ja profiiliandmed. + <strong>Ei midagi</strong> kui Te soovite lihtsalt keelata meediafailid. + noop: Ei midagi + silence: Vaigista + suspend: Peata + title: Uus domeeniblokeering + reject_media: Keela meediafailid + reject_media_hint: Kustutab kohalikult salvestatud meediafailid ja keeldub tulevikus rohkem allalaadimast. Ei puuduta peatamisi + reject_reports: Lükka teavitused tagasi + reject_reports_hint: Eira kõik teavitused sellelt domeenilt. Ei puuduta peatamisi + rejecting_media: keelan meediafaile + rejecting_reports: keelan teavitusi + severity: + silence: vaigistatud + suspend: peatatud + show: + affected_accounts: + one: Üks kasutaja andmebaasis mõjutatud + other: "%{count} kasutajat andmebaasis mõjutatud" + retroactive: + silence: Eemalda vaigistus mõjutatud kasutajatelt sellel domeenil + suspend: Lõpeta mõjutatud kasutajate peatamine sellel domeenil + title: Eemalda domeeniblokeering %{domain} + undo: Võta tagasi + undo: Võta tagasi domeeniblokeering + email_domain_blocks: + add_new: Lisa uus + created_msg: E-posti aadressi keelunimekirja lisamine õnnestus + delete: Kustuta + destroyed_msg: E-posti aadressi keelunimekirjast kustutamine õnnestus + domain: Domeen + new: + create: Lisa domeen + title: Uus e-posti keelunimekirja sisend + title: E-posti keelunimekiri + followers: + back_to_account: Tagasi minu kontole + title: "%{acct}-i jälgijad" + instances: + by_domain: Domeen + delivery_available: Üleandmine on saadaval + known_accounts: + one: "%{count} teadaolev kasutaja" + other: "%{count} teadaolevat kasutajat" + moderation: + all: Kõik + limited: Piiratud + title: Moderatsioon + title: Föderatsioon + total_blocked_by_us: Meie poolt blokeeritud + total_followed_by_them: Nende poolt jälgitud + total_followed_by_us: Meie poolt jälgitud + total_reported: Nende kohta teateid + total_storage: Lisatud meedia + invites: + deactivate_all: Peata kõik + filter: + all: Kõik + available: Saadaval + expired: Aegunud + title: Filter + title: Kutsed + pending_accounts: + title: Ootel olevad kasutajad (%{count}) + relays: + add_new: Lisa uus relee + delete: Kustuta + description_html: "<strong>Föderatsiooni relee</strong> on vahepealne server, mis vahetab suures koguses tuututusi serverite vahel, mis on selle tellijad ning mis sellele saadavad. <strong>See aitab väikestel ja keskmistel serveritel avastada sisu fediversumist</strong>, mis tavaliselt nõuab teisel serveril olevate inimeste jälgimist." + disable: Keela + disabled: Keelatud + enable: Luba + enable_hint: Kui lubatud, siis sinu server tellib kõik avalikud tuututused sellelt releelt, ning hakkab ka enda avalikke tuututusi sellele saatma. + enabled: Lubatud + inbox_url: Relee URL + pending: Ootab relee nõusolekut + save_and_enable: Salvesta ja luba + setup: Sea üles releeühendus + status: Staatus + title: Releed + report_notes: + created_msg: Teade edukalt koostatud! + destroyed_msg: Teade edukalt kustutatud! + reports: + account: + note: märkus + report: teavita + action_taken_by: Meetmeid kasutanud + are_you_sure: Olete kindel? + assign_to_self: Määra mulle + assigned: Määratud moderaator + comment: + none: Pole + created_at: Teavitatud + mark_as_resolved: Märgi lahendatuks + mark_as_unresolved: Märgi lahendamata + notes: + create: Lisa märkus + create_and_resolve: Lahenda märkusega + create_and_unresolve: Taasava märkusega + delete: Kustuta + placeholder: Kirjelda, mis on ette võetud või muid seotud uuendusi... + reopen: Taasava teavitus + report: 'Teavitus #%{id}' + reported_account: Teavitatud kontost + reported_by: Teavitatud + resolved: Lahendatud + resolved_msg: Teavituse lahendamine õnnestus! + status: Staatus + title: Teavitused + unassign: Eemalda määramine + unresolved: Lahendamata + updated_at: Uuendatud + settings: + activity_api_enabled: + desc_html: Kohalike postituste, aktiivsete kasutajate ja uute registreerimiste numbrid iganädalaste "ämbritena" + title: Avalda koondstatistikat selle kasutaja aktiivsusest + bootstrap_timeline_accounts: + desc_html: Eralda mitut kasutajanime komadega. Ainult kohalikud ja lukustamata kasutajate nimed töötavad. Kui tühi, on vaikesätteks kõik kohalikud administraatorid. + title: Vaikimisi jälgimised uutele kasutajatele + contact_information: + email: Äri e-post + username: Kontakt kasutajanimi + custom_css: + desc_html: Muuda kujundust CSSi abil, mis laetakse igal lehel + title: Kohandatud CSS + hero: + desc_html: Kuvatud kodulehel. Vähemalt 600x100px soovitatud. Kui pole seadistatud, kuvatakse serveri pisililt + title: Maskotipilt + mascot: + desc_html: Kuvatakse mitmel lehel. Vähemalt 293x205px soovitatud. Kui pole seadistatud, kuvatakse vaikimisi maskott + title: Maskotipilt + peers_api_enabled: + desc_html: Domeenid, mida see server on kohanud fediversumis + title: Avalda nimekiri avastatud serveritest + preview_sensitive_media: + desc_html: Lingi eelvaated teistel veebisaitidel kuvab pisipilti, isegi kui meedia on märgitud tundlikuks + title: Kuva tundlikku meediat OpenGraphi eelvaadetes + profile_directory: + desc_html: Luba kasutajate avastamine + title: Luba profiilikataloog + registrations: + closed_message: + desc_html: Kuvatud esilehel kui registreerimised on suletud. Te võite kasutada HTMLi silte + title: Suletud registreerimiste sõnum + deletion: + desc_html: Luba kasutajatel oma konto kustutada + title: Ava kontode kustutamine + min_invite_role: + disabled: Mitte keegi + title: Luba kutseid + registrations_mode: + modes: + approved: Kinnitus vajalik konto loomisel + none: Keegi ei saa kontoid luua + open: Kõik võivad kontoid luua + title: Registreerimisrežiim + show_known_fediverse_at_about_page: + desc_html: Kui lubatud, näitab kõiki teatud fediversumi tuututusi. Vastasel juhul näidatakse ainult kohalike tuututusi. + title: Näita teatud fediversumit ajajoone eelvaates + show_staff_badge: + desc_html: Näita personalimärki kasutaja profiilil + title: Näita personalimärki + site_description: + desc_html: Sissejuhatuslik lõik API kohta. Kirjelda, mis teeb selle Mastodoni serveri eriliseks ja ka muud tähtsat. Te saate kasutada HTMLi silte, peamiselt <code><a></code> ja <code><em></code>. + title: Serveri kirjeldus + site_description_extended: + desc_html: Hea koht käitumisreegliteks, reegliteks, suunisteks ja muuks, mis teevad Teie serveri eriliseks. Te saate kasutada HTML silte + title: Lisa informatsioon + site_short_description: + desc_html: Kuvatud küljeribal ja metasiltides. Kirjelda, mis on Mastodon ja mis on selles serveris erilist ühes lõigus. + title: Serveri lühikirjeldus + site_terms: + desc_html: Te saate kirjutada oma privaatsuspoliitika, kasutustingimused jm seaduslikku infot. Te saate kasutada HTMLi silte + title: Kasutustingimused + site_title: Serveri nimi + spam_check_enabled: + desc_html: Mastodon suudab automaatselt vaigistada ja teatada kasutajatest, kasutades erinevaid meetmeid, näiteks kui kasutaja saadab korduvalt ebasobivaid sõnumeid. Võib esineda ka valehäireid. + title: Rämpsposti filter + thumbnail: + desc_html: Kasutatud OpenGraph ja API eelvaadeteks. 1200x630px soovitatud + title: Serveri pisipilt + timeline_preview: + desc_html: Kuva avalikku ajajoont esilehel + title: Ajajoone eelvaade + title: Lehe seaded + statuses: + back_to_account: Tagasi konto lehele + batch: + delete: Kustuta + nsfw_off: Märgi kui mitte tundlik + nsfw_on: Märgi kui tundlik + failed_to_execute: Täitmine ebaõnnestus + media: + title: Meedia + no_media: Meedia puudub + no_status_selected: Mitte ühtegi staatust muudeti sest midagi polnud valitud + title: Konto staatused + with_media: Meediaga + tags: + title: Sildid + title: Administreerimine + warning_presets: + add_new: Lisa uus + delete: Kustuta + edit: Redigeeri + edit_preset: Redigeeri hoiatuse eelseadistust + title: Halda hoiatuste eelseadistusi + admin_mailer: + new_pending_account: + body: Uue konto üksikasjad on allpool. Te saate vastu võtta või tagasi lükata seda taotlust. + subject: Uus konto valmis ülevaatluseks serveril %{instance} (%{username}) + new_report: + body: "%{reporter} teavitas kasutajast %{target}" + body_remote: Keegi domeenist %{domain} teavitas kasutajast %{target} + subject: Uus teavitus %{instance}-ile (#%{id}) + appearance: + advanced_web_interface: Arenenud veebiliides + advanced_web_interface_hint: 'Kui soovite kasutada terve ekraani laiust, lubab arenenud veebiliides seadistada mitut erinevat veergu, et näha nii palju informatsiooni samal ajal kui võimalik: Kodu, teavitused, föderatsiooni ajajoon ning mis iganes arv nimekirju ja silte.' + animations_and_accessibility: Animatsioonid ja ligipääs + confirmation_dialogs: Kinnitusdialoogid + sensitive_content: Tundlik sisu + application_mailer: + notification_preferences: Muuda e-kirjade eelistusi + salutation: "%{name}," + settings: 'Muuda e-kirjade eelistusi: %{link}' + view: 'Vaade:' + view_profile: Vaata profiili + view_status: Vaata staatust + applications: + created: Rakenduse loomine õnnestus + destroyed: Rakenduse kustutamine õnnestus + invalid_url: Antud URL on vale + regenerate_token: Loo uus access token + token_regenerated: Access tokeni loomine õnnestus + warning: Olge nende andmetega ettevaatlikud. Ärge jagage neid kellegagi! + your_token: Sinu access token + auth: + apply_for_account: Taotle kutse + change_password: Salasõna + checkbox_agreement_html: Ma nõustun <a href="%{rules_path}" target="_blank">serveri reeglitega</a> ja <a href="%{terms_path}" target="_blank">kasutustingimustega</a> + checkbox_agreement_without_rules_html: Ma nõustun <a href="%{terms_path}" target="_blank">kasutustingimustega</a> + delete_account: Kustuta konto + delete_account_html: Kui Te soovite oma kontot kustutada, võite <a href="%{path}">jätkata siit</a>. Teilt küsitakse kinnitust. + didnt_get_confirmation: Ei saanud kinnituse juhendeid? + forgot_password: Unustasid oma salasõna? + invalid_reset_password_token: Salasõna lähtestusvõti on vale või aegunud. Palun taotle uus. + login: Logi sisse + logout: Logi välja + migrate_account: Koli teisele kasutajale + migrate_account_html: Kui Te soovite seda kontot ümber viia teisele, <a href="%{path}">saate teha seda siit</a>. + or_log_in_with: Või logi sisse koos + providers: + cas: CAS + saml: SAML + register: Loo konto + registration_closed: "%{instance} ei võta vastu uusi liikmeid" + resend_confirmation: Saada kinnitusjuhendid uuesti + reset_password: Lähtesta salasõna + security: Turvalisus + set_new_password: Määra uus salasõna + setup: + email_below_hint_html: Kui allolev e-posti aadress on vale, saate Te muuta seda siin ning Teile saadetakse uus kinnituskiri. + email_settings_hint_html: Kinnituskiri saadeti e-posti aadressile %{email}. Kui see aadress pole õige, saate Te muuta seda oma konto sätetest. + title: Seadistamine + status: + account_status: Konto olek + confirming: Ootan e-posti kinnitust. + pending: Teie taotlus ootab ülevaadet meie personali poolt. See võib võtta mõnda aega. Kui Teie taotlus on vastu võetud, saadetakse Teile e-kiri. + trouble_logging_in: Probleeme sisselogimisega? + authorize_follow: + already_following: Te juba jälgite seda kontot + error: Kahjuks ilmus viga kasutaja kaugserverist otsimisel + follow: Jälgi + follow_request: 'Te olete saatnud jälgimistaotluse kasutajale:' + following: 'Õnnestus! Te nüüd jälgite kasutajat:' + post_follow: + close: Või Te saate lihtsalt sulgeda seda akent. + return: Näita kasutaja profiili + web: Mine veebi + title: Jälgi %{acct} + datetime: + distance_in_words: + about_x_hours: "%{count}t" + about_x_months: "%{count}k" + about_x_years: "%{count}a" + almost_x_years: "%{count}a" + half_a_minute: Just praegu + less_than_x_minutes: "%{count}m" + less_than_x_seconds: Just praegu + over_x_years: "%{count}a" + x_days: "%{count}p" + x_minutes: "%{count}m" + x_months: "%{count}k" + x_seconds: "%{count}s" + deletes: + confirm_password: Sisesta oma praegune salasõna, et kinnitada oma identiteet + proceed: Kustuta konto + success_msg: Konto kustutamine õnnestus + directories: + directory: Profiilikataloog + explanation: Avasta kasutajaid nende huvide põhjal + explore_mastodon: Avasta %{title} + errors: + '400': The request you submitted was invalid or malformed. + '403': Sul puudub õigus seda lehte vaadata. + '404': Soovitud lehte ei leitud. + '406': This page is not available in the requested format. + '410': Soovitud lehekülge pole enam siin. + '422': + content: Turvalisuse kontroll ebaõnnestus. Kas Te blokeerite küpsiseid? + title: Turvalisuse kontroll ebaõnnestus + '429': Aeglustatud + '500': + content: Palume vabandust, midagi läks valesti meie pool. + title: See lehekülg pole õige + '503': The page could not be served due to a temporary server failure. + noscript_html: Et kasutada Mastodoni veebirakendust, palun lubage JavaScript. Alternatiivselt, proovige mõnda <a href="%{apps_path}">rakendust</a> Teie platvormile. + existing_username_validator: + not_found: ei suutnud leida kohalikku kasutajat selle kasutajanimega + not_found_multiple: ei suutnud leida %{usernames} + exports: + archive_takeout: + date: Kuupäev + download: Lae alla oma arhiiv + hint_html: Te saate taotleda arhiivi oma <strong>tuututustest ja üleslaetud meediast</strong>. Eksporditud andmed on ActivityPub-i formaadis, loetav vastava tarkvara poolt. Te saate taotleda arhiivi iga 7 päeva tagant. + in_progress: Loome Teie arhiivi... + request: Taotle oma arhiivi + size: Suurus + blocks: Teie blokeerite + csv: CSV + domain_blocks: Domeeni blokeeringud + follows: Teie jälgite + lists: Nimistud + mutes: Teie vaigistate + storage: Meedia hoidla + featured_tags: + add_new: Lisa uus + errors: + limit: Olete jõudnud maksimum lubatud siltide arvuni + filters: + contexts: + home: Kodu ajajoon + notifications: Teated + public: Avalikud ajajooned + thread: Vestlused + edit: + title: Muuda filtrit + errors: + invalid_context: Puudulik või vale kontekst + invalid_irreversible: Taastamatu filter töötab ainult kodu või teavituste kontekstis + index: + delete: Kustuta + title: Filterid + new: + title: Lisa uus filter + footer: + developers: Arendajad + more: Rohkem… + resources: Materjalid + generic: + all: Kõik + changes_saved_msg: Muudatuste salvestamine õnnestus! + copy: Kopeeri + order_by: Järjesta + save_changes: Salvesta muudatused + validation_errors: + one: Midagi pole ikka õigesti! Palun vaadake allolev veateade üle + other: Midagi pole ikka õigesti! Palun vaadake all olevad %{count} veateadet üle + html_validator: + invalid_markup: 'sisaldab valet HTMLi süntaksi: %{error}' + identity_proofs: + active: Aktiivne + authorize: Jah, luba + authorize_connection_prompt: Luba see krüptograafiline ühendus? + errors: + failed: Krüptograafiline ühendus ebaõnnestus. Palun proovi uuesti %{provider}-ist. + keybase: + invalid_token: Keybase võtmed on allkirjade hashid ja peavad olema 66 hex tähemärki + verification_failed: Keybase ei tunnista seda võtit kui kasutaja %{kb_username} omand. Palun proovige uuesti Keybasest. + wrong_user: Ei saa luua tõendit kontole %{proving} kui olete sisseloginud kui %{current}. Logige sisse kui %{proving} ja proovige uuesti. + explanation_html: Siin saate luua krüptograafilisi ühendusi oma teiste identiteetidega, nagu näiteks Keybase profiiliga. See võimaldab teistel kasutajatel saata Teile krüptitud sõnumeid ja usaldada sisu, mis Teie saadate neile. + i_am_html: Ma olen %{username} teenusel %{service}. + identity: Identiteet + inactive: Passiivne + publicize_checkbox: 'Ja tuututa seda:' + publicize_toot: 'See on tõestatud! Ma olen %{username} teenusel %{service}: %{url}' + status: Kinnituse staatus + view_proof: Vaata tõendit + imports: + modes: + merge: Lõimi + merge_long: Hoia olemasolevad andmed ja lisa uusi + overwrite: Kirjuta üle + overwrite_long: Vaheta praegused andmed uute vastu + preface: Te saate importida mis tahes andmeid, mis on eksporditud teisest serverist. Näiteks nimekiri inimestest, keda jälgid ja keda blokeerid. + success: Teie andmete üleslaadimine õnnestus ning neid töödeldakse + types: + blocking: Blokeeringute nimekiri + domain_blocking: Domeeniblokeeringute nimekiri + following: Jälgimiste nimekiri + muting: Vaigistuse nimekiri + upload: Lae üles + in_memoriam_html: Mälestamaks. + invites: + delete: Peata + expired: Aegunud + expires_in: + '1800': 30 minutit + '21600': 6 tundi + '3600': 1 tund + '43200': 12 tundi + '604800': 1 nädal + '86400': 1 päev + expires_in_prompt: Mitte kunagi + generate: Loo + invited_by: 'Teid kutsus:' + max_uses: + one: 1 kasutus + other: "%{count} kasutust" + max_uses_prompt: Piiranguteta + prompt: Loo ja jaga linke teistega, et lubada neil liituda selle serveriga + table: + expires_at: Aegub + uses: Kasutust + title: Kutsu inimesi + lists: + errors: + limit: Olete jõudnud maksimum lubatud nimekirjade arvuni + media_attachments: + validations: + images_and_video: Ei saa lisada video staatusele, millel on juba pildid + too_many: Ei saa lisada rohkem, kui 4 faili + migrations: + acct: uue konto kasutajanimi@domeen + moderation: + title: Moderatsioon + notification_mailer: + digest: + action: Vaata kõiki teateid + body: Siin on kiire ülevaade sellest, mis sõnumeid Te ei näinud pärast Teie viimast külastust %{since} + mention: "%{name} mainis sind postituses:" + new_followers_summary: + one: Ja veel, Te saite ühe uue jälgija kui Te olite eemal! Jee! + other: Ja veel, Te saite %{count} uut jälgijat kui Te olite eemal! Hämmastav! + subject: + one: "1 uus teavitus peale Teie eelmist külastust \U0001F418" + other: "%{count} uut teavitust peale Teie eelmist külastust \U0001F418" + title: Teie puudumisel... + favourite: + body: "%{name} lisas Teie staatuse lemmikutesse:" + subject: "%{name} märkis su staatuse lemmikuks" + title: Uus lemmik + follow: + body: "%{name} nüüd jälgib teid!" + subject: "%{name} nüüd jälgib teid" + title: Uus jälgija + follow_request: + action: Halda jälgimistaotlusi + body: "%{name} soovib Teid jälgida" + subject: 'Ootav jälgija: %{name}' + title: Uus jälgimistaotlus + mention: + action: Vasta + body: "%{name} mainis Teid:" + subject: "%{name} mainis Teid" + title: Uus mainimine + reblog: + body: "%{name} upitas Teie staatust:" + subject: "%{name} upitas su staatust" + title: Uus upitus + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: T + trillion: Tr + pagination: + newer: Uuemad + next: Järgmine + older: Vanemad + prev: Eelm + truncate: "…" + polls: + errors: + already_voted: Olete siin juba hääletanud + duplicate_options: sisaldab samu asju mitu korda + duration_too_long: kestab liiga kaua + duration_too_short: on liiga vara + expired: See küsitlus on juba lõppenud + over_character_limit: igaüks ei saa olla rohkem kui %{max} tähemärki + too_few_options: peab olema rohkem kui üks vastus + too_many_options: ei saa sisaldada rohkem kui %{max} vastust + preferences: + other: Muu + posting_defaults: Postitamise vaikesätted + public_timelines: Avalikud ajajooned + relationships: + activity: Konto tegevus + dormant: Seisev + last_active: Viimati aktiivne + most_recent: Viimased + moved: Kolinud + mutual: Ühine + primary: Peamine + relationship: Seos + remove_selected_domains: Eemalda kõik jälgijad valitud domeenidelt + remove_selected_followers: Eemalda valitud jälgijad + remove_selected_follows: Lõpeta valitud kasutajate jälgimine + status: Konto olek + remote_follow: + acct: Sisestage oma kasutajanimi@domeen, kust te soovite jälgida + missing_resource: Ei suutnud leida vajalikku suunamise URLi Teie konto jaoks + no_account_html: Teil pole veel kontot? Saate <a href='%{sign_up_path}' target='_blank'>luua ühe siit</a> + proceed: Jätka jälgimiseks + prompt: 'Te hakkate jälgima:' + reason_html: |- + <strong>Miks on see samm vajalik?</strong> + <code>%{instance}</code> ei pruugi olla server, kus asub Teie konto, nii et me peame Teid suunama oma kodu serverile. + remote_interaction: + favourite: + proceed: Jätka lemmikuks lisamiseks + prompt: 'Te soovite lisada seda tuututust lemmikutesse:' + reblog: + proceed: Jätka upitamiseks + prompt: 'Te soovite seda tuututust upitada:' + reply: + proceed: Jätka vastamiseks + prompt: 'Te soovite vastata sellele tuututusele:' + scheduled_statuses: + over_daily_limit: Te olete jõudnud maksimum lubatud ajastatud tuututuste arvuni %{limit} selle päeva kohta + over_total_limit: Te olete jõudnud maksimum lubatud ajastatud tuututuste arvuni %{limit} + too_soon: Ajastatud tuututuse kuupäev peab olema tukevikus + sessions: + activity: Viimane aktiivsus + browser: Veebilehitseja + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox + generic: Tundmatu veebilehitseja + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Praegune seanss + description: "%{browser} platvormil %{platform}" + explanation: Need on praegused veebilehitsejad, mis on sisse logitud sinu Mastodoni kontosse. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: tundmatu platvorm + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + revoke: Tühista + revoke_success: Seanssi tühistamine õnnestus + title: Seanssid + settings: + account: Konto + account_settings: Kontosätted + appearance: Välimus + authorized_apps: Lubatud rakendused + back: Tagasi Mastodoni + delete: Konto kustutamine + development: Arendus + edit_profile: Muuda profiili + export: Andmete eksportimine + featured_tags: Esile toodud sildid + identity_proofs: Isikutõendid + import: Impordi + import_and_export: Import / eksport + migrate: Konto migreerumine + notifications: Teated + preferences: Eelistused + profile: Profiil + relationships: Jälgitud ja jälgijad + two_factor_authentication: Kahesammuline autentimine + statuses: + attached: + description: 'Manused: %{attached}' + image: + one: "%{count} pilt" + other: "%{count} pilti" + video: + one: "%{count} video" + other: "%{count} videot" + boosted_from_html: Upitatud %{acct_link} + content_warning: 'Sisu hoiatus: %{warning}' + disallowed_hashtags: + one: 'sisaldab ebasobivat silti: %{tags}' + other: 'sisaldab ebasobivaid silte: %{tags}' + language_detection: Automaatselt tuvasta keel + open_in_web: Ava veebis + over_character_limit: tähtmärkide limiit %{max} ületatud + pin_errors: + limit: Te olete juba maksimum arvu lubatud tuututusi kinnitanud + ownership: Kellegi teise tuututust ei saa kinnitada + private: Privaatset tuututust ei saa kinnitada + reblog: Upitust ei saa kinnitada + poll: + total_votes: + one: "%{count} hääl" + other: "%{count} häält" + vote: Hääleta + show_more: Näita rohkem + sign_in_to_participate: Logi sisse, et liituda vestlusega + title: '%{name}: "%{quote}"' + visibilities: + private: Ainult jälgijatele + private_long: Näita ainult jälgijatele + public: Avalik + public_long: Kõik saavad näha + unlisted: Kirjendamata + unlisted_long: Kõik saavad näha, aga ei ole saadaval avalikes ajajoontes + stream_entries: + pinned: Kinnitatud tuut + reblogged: upitatud + sensitive_content: Tundlik sisu + terms: + title: "%{instance} Kasutustingimused ja Privaatsuspoliitika" + themes: + contrast: Mastodon (Kõrge kontrast) + default: Mastodon (Tume) + mastodon-light: Mastodon (Hele) + time: + formats: + default: "%d. %B, %Y. aastal, kell %H:%M" + month: "%B %Y" + two_factor_authentication: + code_hint: Sisesta kaheastmelise autentimise kood, mille lõi Teie autentimisrakendus, et jätkata + description_html: Kui Te aktiveerite <strong>kaheastmelise autentimise</strong>, siis sisselogimisel peab teil olema telefon, mis loob Teile koode sisenemiseks. + disable: Lülita välja + enable: Lülita sisse + enabled: Kaheastmeline autentimine on sisse lülitatud + enabled_success: Kaheastmeline autentimine on edukalt sisse lülitatud + generate_recovery_codes: Loo taastuskoodid + instructions_html: "<strong>Skaneeri see QR kood kasutades rakendust Google Authenticator või muu TOTP rakendus Teie telefonis</strong>. Nüüdsest alates loob see rakendus Teile koode, mida peate sisestama sisselogimisel." + lost_recovery_codes: Taastuskoodide abil on Teil võimalik sisse logida kontosse, kui Te kaotate oma telefoni. Kui Te kaotate oma taastuskoodid, saate need uuesti luua siin. Teie vanad taastuskoodid tehakse kehtetuks. + manual_instructions: 'Kui Te ei saa seda QR koodi skaneerida ning peate sisestama selle käsitsi, on siin tekstiline salavõti:' + recovery_codes: Tagavara taastuskoodid + recovery_codes_regenerated: Taastuskoodid edukalt taasloodud + recovery_instructions_html: Kui Te juhtute kunagi kaotama oma telefoni, saate kasutada ühte allpool olevatest taastuskoodidest, et saada ligipääsu oma kontole. <strong>Hoidke taastuskoodid turvaliselt</strong>. Näiteks võite Te need välja printida ning hoida need koos teiste tähtsate dokumentidega. + setup: Sätesta + wrong_code: Sisestatud kood on vale! Kas serveri aeg ja seadme aeg on õiged? + user_mailer: + backup_ready: + explanation: Te taotlesite varukoopia oma Mastodoni kontost. See on nüüd valmis allalaadimiseks! + subject: Teie arhiiv on allalaadimiseks valmis + title: Arhiivi väljavõte + warning: + explanation: + disable: Kuniks Teie konto on lukus, ei saa Te teha sellega ühtegi tegevust kuni see on lukust lahti tehtud. + silence: Kuniks Teie konto on limiteeritud, ainult need inimesed, kes Teid juba jälgivad, näevad Teie tuututusi sellel serveril ning Teid jäetakse välja avalikest nimekirjadest. Sellest hoolimata võivad inimesed Teid siiski jälgida. + suspend: Teie konto on peatatud ning kõik Teie tuututused ja üleslaetud meediafailid on jäädavalt kustutatud sellelt serverilt ning serveritelt, kus Teil oli jälgijad. + review_server_policies: Vaadake üle serveri eeskirju + subject: + disable: Teie konto %{acct} on lukustatud + none: Hoiatus kasutajale %{acct} + silence: Teie kontole %{acct} on kehtestatud limiidid + suspend: Teie konto %{acct} on peatatud + title: + disable: Konto lukustatud + none: Hoiatus + silence: Konto limiteeritud + suspend: Konto peatatud + welcome: + edit_profile_action: Sea üles profiil + edit_profile_step: Te saate oma profiili isikupärastada näiteks lisades profiilipildi, päise, muutes oma kuvanime ja muud. Kui Te soovite üle vaadata inimesi, kes Teid jälgida soovivad, saate lukustada oma konto. + explanation: Siin on mõned nõuanded, mis aitavad sul alustada + final_action: Alusa postitamist + final_step: 'Alusta postitamist! Isegi ilma jälgijateta näevad teised Teie avalikke postitusi, näiteks kohalikul ajajoonel ning siltidest. Te võite ennast tutvustada kasutades silti #introductions.' + full_handle: Sinu täisnimi + full_handle_hint: See on mida oma sõpradega jagada, et nad saaksid Teile sõnumeid saata ning Teid jälgida teiselt serverilt. + review_preferences_action: Muuda eelistusi + review_preferences_step: Kindlasti seadistage oma sätted Teie maitse järgi, näiteks e-kirju, mida soovite saada, või millist privaatsustaset Te soovite vaikimisi. Kui Teil pole merehaigust, võite Te näiteks lubada GIFide automaatse mängimise. + subject: Tere tulemast Mastodoni + tip_federated_timeline: Föderatiivne ajajoon on reaalajas voogvaade tervest Mastodoni võrgust. Aga see sisaldab ainult inimesi, keda su naabrid tellivad, niiet see pole täiuslik. + tip_following: Vaikimisi, Te jälgite ainult oma serveri administraator(eid). Et leida rohkem huvitavamaid inimesi, vaadake kohalikke ja föderatiivseid ajajooni. + tip_local_timeline: Kohalik ajajoon on reaalajas voogvaade inimestest, kes on serveris %{instance}. Need on Teie lähimad naabrid! + tip_mobile_webapp: Kui Teie mobiilne veebilehitseja pakub Teile lisada meid Teie avaekraanile, saate Te reaalajas teateid. See töötab nagu tavaline mobiilirakendus mitmel moel! + tips: Nõuanded + title: Tere tulemast pardale, %{name}! + users: + follow_limit_reached: Te ei saa jälgida rohkem kui %{limit} inimest + invalid_email: See e-posti aadress on vale + invalid_otp_token: Vale kaheastmelise autentimise kood + otp_lost_help_html: Kui Te kaotasite ligipääsu mõlemale, saate võtta ühendust %{email}-iga + seamless_external_login: Te olete sisse loginud läbi väljaspool asuva teenusega, niiet salasõna ja e-posti sätted pole saadaval. + signed_in_as: 'Sisse logitud kasutajana:' + verification: + explanation_html: 'Te saate <strong>kinnitada ennast oma linkide omanikena oma profiili metaandmetes</strong>. Et seda teha, peab Teie lingitud veebilehel olema link tagasi Teie Mastodoni profiilile. Tagasi saatval lingil <strong>peab</strong> olema <code>rel="me"</code> atribuut. Tekstisisu lingil ei loe. Siin on üks näide:' + verification: Kinnitamine diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 9b9c2c02719f731bbeb227af5a9af0233cea0575..1f2ac6644e35899b3575377cac5efc94e65fd2d2 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -4,7 +4,7 @@ eu: about_hashtag_html: Hauek <strong>#%{hashtag}</strong> traola duten toot publikoak dira. Fedibertsoko edozein kontu baduzu harremanetan jarri zaitezke. about_mastodon_html: Mastodon web protokolo ireki eta libreak darabiltzan gizarte sare bat da. E-mail sarea bezala deszentralizatua da. about_this: Honi buruz - active_count_after: aktiboa + active_count_after: aktibo active_footnote: Hilabeteko erabiltzaile aktiboak (HEA) administered_by: 'Administratzailea(k):' api: APIa @@ -17,13 +17,12 @@ eu: contact_unavailable: E/E discover_users: Aurkitu erabiltzaileak documentation: Dokumentazioa - extended_description_html: | - <h3>Arauentzako toki egoki bat</h3> - <p>Azalpen luzea ez da ezarri oraindik.</p> federation_hint_html: "%{instance} instantzian kontu bat izanda edozein Mastodon zerbitzariko jendea jarraitu ahal izango duzu, eta harago ere." - generic_description: "%{domain} sareko zerbitzari bat da" get_apps: Probatu mugikorrerako aplikazio bat hosted_on: Mastodon %{domain} domeinuan ostatatua + instance_actor_flash: 'Kontu hau zerbitzaria bera adierazten duen aktore birtual bat da, ez norbanako bat. Federaziorako erabiltzen da eta ez zenuke blokeatu behar instantzia osoa blokeatu nahi ez baduzu, kasu horretan domeinua blokeatzea egokia litzateke. + +' learn_more: Ikasi gehiago privacy_policy: Pribatutasun politika see_whats_happening: Ikusi zer gertatzen ari den @@ -35,6 +34,14 @@ eu: status_count_before: Hauek tagline: Jarraitu lagunak eta egin berriak terms: Erabilera baldintzak + unavailable_content: Eduki eskuraezina + unavailable_content_description: + domain: Zerbitzaria + reason: Arrazoia + rejecting_media: 'Zerbitzari hauetako multimedia fitxategiak ez dira prozesatuko ez gordeko, eta ez dira iruditxoak bistaratuko, jatorrizko irudira joan behar izango da klik eginez:' + silenced: 'Zerbitzari hauetako mezuak denbora-lerro eta elkarrizketa publikoetan ezkutatuko dira, eta bere erabiltzaileen interakzioek ez dute jakinarazpenik sortuko ez badituzu jarraitzen:' + suspended: 'Ez da zerbitzari hauetako daturik prozesatuko, gordeko, edo partekatuko, zerbitzari hauetako erabiltzaileekin komunikatzea ezinezkoa eginez:' + unavailable_content_html: Mastodonek orokorrean fedibertsoko beste zerbitzarietako erabiltzaileen edukia ikustea eta beraiekin aritzea ahalbidetzen dizu. Salbuespena egin da zerbitzari zehatz honekin. user_count_after: one: erabiltzaile other: erabiltzaile @@ -42,6 +49,8 @@ eu: what_is_mastodon: Zer da Mastodon? accounts: choices_html: "%{name}(r)en aukerak:" + endorsements_hint: Jarraitzen duzun jendea sustatu dezakezu web interfazearen bidez, eta hemen agertuko da. + featured_tags_hint: Hemen agertuko diren traolak nabarmendu ditzakezu. follow: Jarraitu followers: one: Jarraitzaile @@ -51,8 +60,9 @@ eu: last_active: azkenekoz aktiboa link_verified_on: 'Esteka honen jabetzaren egiaztaketa data: %{date}' media: Multimedia - moved_html: "%{name} hona lekualdatu da %{new_profile_link}:" + moved_html: "%{name} hona migratu da %{new_profile_link}:" network_hidden: Informazio hau ez dago eskuragarri + never_active: Inoiz ez nothing_here: Ez dago ezer hemen! people_followed_by: "%{name}(e)k jarraitzen dituenak" people_who_follow: "%{name} jarraitzen dutenak" @@ -61,8 +71,8 @@ eu: posts: one: Toot other: Toot - posts_tab_heading: Toot - posts_with_replies: Toot eta erantzunak + posts_tab_heading: Toot-ak + posts_with_replies: Toot-ak eta erantzunak reserved_username: Erabiltzaile-izena erreserbatuta dago roles: admin: Administratzailea @@ -183,6 +193,7 @@ eu: username: Erabiltzaile-izena warn: Abisatu web: Weba + whitelisted: Zerrenda zurian action_logs: actions: assigned_to_self_report: "%{name}(e)k %{target} salaketa bere buruari esleitu dio" @@ -218,19 +229,24 @@ eu: deleted_status: "(ezabatutako mezua)" title: Auditoria-egunkaria custom_emojis: + assign_category: Esleitu kategoria by_domain: Domeinua copied_msg: Ongi sortu da emoji-aren kopia lokala copy: Kopiatu copy_failed_msg: Ezin izan da emoji-aren kopia lokal bat sortu + create_new_category: Sortu kategoria berria created_msg: Emoji-a ongi sortu da! delete: Ezabatu destroyed_msg: Emoji-a ongi suntsitu da! disable: Desgaitu + disabled: Desgaituta disabled_msg: Emoji-a ongi desgaitu da emoji: Emojia enable: Gaitu + enabled: Gaituta enabled_msg: Emoji hori ongi gaitu da image_hint: PNG gehienez 50KB + list: Zerrendatu listed: Zerrendatua new: title: Gehitu emoji pertsonal berria @@ -238,11 +254,14 @@ eu: shortcode: Laster-kodea shortcode_hint: Gutxienez 2 karaktere, alfanumerikoak eta azpimarra besterik ez title: Emoji pertsonalak + uncategorized: Kategoriarik gabe + unlist: Kendu zerrendatik unlisted: Zerrendatu gabea update_failed_msg: Ezin izan da emoji hori eguneratu updated_msg: Emoji-a ongi eguneratu da! upload: Igo dashboard: + authorized_fetch_mode: Baimendutako eskuratze modua backlog: aurreikusitako lanak config: Konfigurazioa feature_deletions: Kontu ezabaketak @@ -250,10 +269,13 @@ eu: feature_profile_directory: Profil-direktorioa feature_registrations: Izen emateak feature_relay: Federazio haria + feature_spam_check: Anti-spam feature_timeline_preview: Denbora-lerroaren aurrebista features: Ezaugarriak hidden_service: Federazioa ezkutuko zerbitzuekin open_reports: salaketa irekiak + pending_tags: berrikusketaren zain dauden etiketak + pending_users: berrikusketaren zain dauden erabiltzaileak recent_users: Azken erabiltzaileak search: Testu osoko bilaketa single_user_mode: Erabiltzaile bakarreko modua @@ -265,11 +287,18 @@ eu: week_interactions: interakzio aste honetan week_users_active: aktibo aste honetan week_users_new: erabiltzaile aste honetan + whitelist_mode: Zerrenda zuria modua + domain_allows: + add_new: Sartu domeinua zerrenda zurian + created_msg: Domeinua ongi sartu da zerrenda zurian + destroyed_msg: Domeinua zerrenda zuritik kendu da + undo: Kendu zerrenda zuritik domain_blocks: add_new: Gehitu domeinuaren blokeo berria created_msg: Domeinuaren blokeoa orain prozesatzen ari da destroyed_msg: Domeinuaren blokeoa desegin da domain: Domeinua + edit: Editatu domeinu-blokeoa existing_domain_block_html: '%{name} domeinuan muga zorrotzagoak ezarri dituzu jada, aurretik <a href="%{unblock_url}">desblokeatu</a> beharko duzu.' new: create: Sortu blokeoa @@ -280,6 +309,10 @@ eu: silence: Isilarazi suspend: Kanporatu title: Domeinuaren blokeo berria + private_comment: Iruzkin pribatua + private_comment_hint: Domeinu hau mugatzeari buruzko iruzkina moderatzaileen barne erabilerarako. + public_comment: Iruzkin publikoa + public_comment_hint: Domeinu hau mugatzeari buruzko iruzkina publiko orokorrarentzat, domeinuak mugatzea iragartzea gaituta badago. reject_media: Ukatu multimedia fitxategiak reject_media_hint: Lokalki gordetako multimedia fitxategiak ezabatzen ditu eta etorkizunean fitxategi berriak deskargatzeari uko egingo dio. Ez du garrantzirik kanporaketetan reject_reports: Errefusatu salaketak @@ -299,6 +332,7 @@ eu: title: Desegin %{domain} domeinuko blokeoa undo: Desegin undo: Desegin domeinuaren blokeoa + view: Ikusi domeinuaren blokeoa email_domain_blocks: add_new: Gehitu berria created_msg: Ongi gehitu da e-mail helbidea domeinuen zerrenda beltzera @@ -322,6 +356,8 @@ eu: all: Denak limited: Mugatua title: Moderazioa + private_comment: Iruzkin pribatua + public_comment: Iruzkin publikoa title: Federazioa total_blocked_by_us: Guk blokeatuta total_followed_by_them: Haiek jarraitua @@ -399,6 +435,13 @@ eu: custom_css: desc_html: Aldatu itxura orri bakoitzean kargatutako CSS bidez title: CSS pertsonala + domain_blocks: + all: Guztiei + disabled: Inori ez + title: Erakutsi domeinu-blokeoak + users: Saioa hasita duten erabiltzaile lokalei + domain_blocks_rationale: + title: Erakutsi arrazoia hero: desc_html: Azaleko orrian bistaratua. Gutxienez 600x100px aholkatzen da. Ezartzen ez bada, zerbitzariaren irudia hartuko du title: Azaleko irudia @@ -431,7 +474,7 @@ eu: open: Edonork eman dezake izena title: Erregistratzeko modua show_known_fediverse_at_about_page: - desc_html: Txandakatzean, fedibertsu ezagun osoko toot-ak bistaratuko ditu aurrebistan. Bestela, toot lokalak besterik ez ditu erakutsiko. + desc_html: Txandakatzean, fedibertso ezagun osoko toot-ak bistaratuko ditu aurrebistan. Bestela, toot lokalak besterik ez ditu erakutsiko. title: Erakutsi fedibertsu ezagun osoko denbora-lerroa aurrebistan show_staff_badge: desc_html: Erakutsi langile banda erabiltzailearen orrian @@ -449,6 +492,9 @@ eu: desc_html: Zure pribatutasun politika, erabilera baldintzak eta bestelako testu legalak idatzi ditzakezu. HTML etiketak erabili ditzakezu title: Erabilera baldintza pertsonalizatuak site_title: Zerbitzariaren izena + spam_check_enabled: + desc_html: Mastodonek automatikoki isildu eta salatu ditzake kontuak neurriei jarraituz, esaterako eskatu gabeko mezuak behin eta berriro bidaltzen dituzten kontuak antzemanez. Positibo faltsuak gertatu daitezke. + title: Anti-spam thumbnail: desc_html: Aurrebistetarako erabilia OpenGraph eta API bidez. 1200x630px aholkatzen da title: Zerbitzariaren iruditxoa @@ -456,12 +502,16 @@ eu: desc_html: Bistaratu denbora-lerro publikoa hasiera orrian title: Denbora-lerroaren aurrebista title: Gunearen ezarpenak + trends: + desc_html: Erakutsi publikoki orain joeran dauden aurretik errebisatutako traolak + title: Traolak joeran statuses: back_to_account: Atzera kontuaren orrira batch: delete: Ezabatu nsfw_off: Markatu ez hunkigarri gisa nsfw_on: Markatu hunkigarri gisa + deleted: Ezabatuta failed_to_execute: Ezin izan da burutu media: title: Multimedia @@ -469,21 +519,23 @@ eu: no_status_selected: Ez da mezurik aldatu ez delako mezurik aukeratu title: Kontuaren mezuak with_media: Multimediarekin - subscriptions: - callback_url: Itzulera URL-a - confirmed: Berretsita - expires_in: Iraungitzea - last_delivery: Azken bidalketa - title: WebSub - topic: Mintzagaia tags: - accounts: Kontuak - hidden: Ezkutatuta - hide: Ezkutatu direktoriotik + accounts_today: Erabilera bakanak gaur + accounts_week: Erabilera bakanak aste honetan + context: Testuingurua + directory: Direktorioan + in_directory: "%{count} direktorioan" + last_active: Azkenekoz aktiboa + most_popular: Erabilienak + most_recent: Azkenak name: Traola + review: Berrikusketaren egoera + reviewed: Berrikusita title: Traolak - unhide: Erakutsi direktorioan - visible: Ikusgai + trending_right_now: Joera orain + unique_uses_today: "%{count} idazten gaur" + unreviewed: Berrikusi gabe + updated_msg: Traola-ezarpenak ongi eguneratu dira title: Administrazioa warning_presets: add_new: Gehitu berria @@ -499,11 +551,20 @@ eu: body: "%{reporter}(e)k %{target} salatu du" body_remote: "%{domain} domeinuko norbaitek %{target} salatu du" subject: Salaketa berria %{instance} instantzian (#%{id}) + new_trending_tag: + subject: Traola berria errebisatzeko %{instance} instantzian (#%{name}) + aliases: + add_new: Sortu ezizena + created_msg: Ongi sortu da ezizena. Orain kontu zaharretik migratzen hasi zaitezke. + deleted_msg: Ongi kendu da ezizena. Orain ezin izango da aurreko kontutik hona migratu. + hint_html: Beste kontu batetik hona migratu nahi baduzu, hemen ezizen bat sortu dezakezu, hau beharrezkoa da kontu zaharreko jarraitzaileak hona ekartzeko. Ekintza hau berez <strong>kaltegabea eta desegingarria</strong> da. <strong>Kontuaren migrazioa kontu zaharretik abiatzen da</strong>. + remove: Deslotu ezizena appearance: advanced_web_interface: Web interfaze aurreratua advanced_web_interface_hint: 'Pantaila bere zabalera osoan erabili nahi baduzu, web interfaze aurreratuak hainbat zutabe desberdin konfiguratzea ahalbidetzen dizu, aldi berean nahi beste informazio ikusteko: Hasiera, jakinarazpenak, federatutako denbora-lerroa, edo nahi beste zerrenda eta traola.' animations_and_accessibility: Animazioak eta irisgarritasuna confirmation_dialogs: Berrespen dialogoak + discovery: Aurkitzea sensitive_content: Eduki hunkigarria application_mailer: notification_preferences: Aldatu e-mail hobespenak @@ -524,15 +585,19 @@ eu: apply_for_account: Eskatu gonbidapen bat change_password: Pasahitza checkbox_agreement_html: <a href="%{rules_path}" target="_blank">Zerbitzariaren arauak</a> eta <a href="%{terms_path}" target="_blank">erabilera baldintzak</a> onartzen ditut - confirm_email: Berretsi e-mail helbidea + checkbox_agreement_without_rules_html: <a href="%{terms_path}" target="_blank">Erabilera baldintzak</a> onartzen ditut delete_account: Ezabatu kontua delete_account_html: Kontua ezabatu nahi baduzu, <a href="%{path}">jarraitu hemen</a>. Berrestea eskatuko zaizu. + description: + prefix_invited_by_user: "@%{name} erabiltzaileak Mastodon zerbitzari honetara elkartzera gonbidatzen zaitu!" + prefix_sign_up: Eman izena Mastodon-en! + suffix: Kontu bat baduzu, jendea jarraitu ahal izango duzu, mezuak bidali eta Mastodon zein kanpoko zerbitzarietako erabiltzaileekin elkarrizketan aritu! didnt_get_confirmation: Ez dituzu berresteko argibideak jaso? forgot_password: Pasahitza ahaztu duzu? invalid_reset_password_token: Pasahitza berrezartzeko token-a baliogabea da edo iraungitu du. Eskatu beste bat. login: Hasi saioa logout: Amaitu saioa - migrate_account: Lekualdatu beste kontu batera + migrate_account: Migratu beste kontu batera migrate_account_html: Kontu hau beste batera birbideratu nahi baduzu, <a href="%{path}">hemen konfiguratu</a> dezakezu. or_log_in_with: Edo hasi saioa honekin providers: @@ -544,6 +609,16 @@ eu: reset_password: Berrezarri pasahitza security: Segurtasuna set_new_password: Ezarri pasahitza berria + setup: + email_below_hint_html: Beheko e-mail helbidea okerra bada, hemen aldatu dezakezu eta baieztapen e-mail berria jaso. + email_settings_hint_html: Baieztamen e-maila %{email} helbidera bidali da. E-mail helbide hori zuzena ez bada, kontuaren ezarpenetan aldatu dezakezu. + title: Ezarpena + status: + account_status: Kontuaren egoera + confirming: E-mail baieztapena osatu bitartean zain. + functional: Zure kontua guztiz erabilgarri dago. + pending: Zure eskaera gainbegiratzeko dago oraindik. Honek denbora behar lezake. Zure eskaera onartzen bada e-mail bat jasoko duzu. + redirecting_to: Zure kontua ez dago aktibo orain %{acct} kontura birbideratzen duelako. trouble_logging_in: Arazoak saioa hasteko? authorize_follow: already_following: Kontu hau aurretik jarraitzen duzu @@ -556,6 +631,11 @@ eu: return: Erakutsi erabiltzailearen profila web: Joan webera title: Jarraitu %{acct} + challenge: + confirm: Jarraitu + hint_html: "<strong>Oharra:</strong> Ez dizugu pasahitza berriro eskatuko ordu batez." + invalid_password: Pasahitz baliogabea + prompt: Berretsi pasahitza jarraitzeko datetime: distance_in_words: about_x_hours: "%{count}o" @@ -571,26 +651,29 @@ eu: x_months: "%{count} hilabete" x_seconds: "%{count}s" deletes: - bad_password_msg: Saiakera ona hacker! Pasahitz okerra + challenge_not_passed: Sartu duzun informazioa ez da zuzena confirm_password: Sartu zure oraingo pasahitza zure identitatea baieztatzeko - description_html: Honek <strong>behin betirako eta atzera egiteko aukera gabe</strong> zure kontuko edukia kendu eta hau desaktibatuko du. Zure erabiltzaile-izena erreserbatuko da etorkizunean inork zure itxurak ez egiteko. + confirm_username: Sartu zure erabiltzaile-izena prozedura berresteko proceed: Ezabatu kontua success_msg: Zure kontua ongi ezabatu da - warning_html: Zerbitzari honetako edukiak ezabatzea besterik ezin da bermatu. Asko partekatu den edukiaren arrastoak geratzea izan liteke. Deskonektatuta dauden zerbitzariak edo zure eguneraketetatik harpidetza kendu duten zerbitzariek ez dituzte beraien datu-baseak eguneratuko. - warning_title: Sakabanatutako edukiaren eskuragarritasuna + warning: + before: 'Jarraitu aurretik, irakurri adi ohar hauek:' + caches: Beste zerbitzariek cachean duten edukia mantentzea gerta daiteke + data_removal: Zure mezuak eta beste datuak behin betiko ezabatuko dira + more_details_html: Xehetasun gehiagorako, ikusi <a href="%{terms_path}">pribatutasun politika</a>. + username_available: Zure erabiltzaile-izena berriro eskuragarri egongo da + username_unavailable: Zure erabiltzaile-izena ez da eskuragarri egongo directories: directory: Profilen direktorioa - enabled: Direktorioan zerrendatuta zaude orain. - enabled_but_waiting: Direktorioan zerrendatuta izatea aukeratu duzu, baina ez duzu oraindik gutxieneko jarraitzaile kopurua (%{min_followers}) zerrendan agertzeko. explanation: Deskubritu erabiltzaileak interesen arabera explore_mastodon: Esploratu %{title} - how_to_enable: Ez duzu aukeratu direktorioan zerrendatua izatea aukeratu. Behean aukeratu dezakezu. Erabili traolak zure biografiaren testuan traola zehatzetan agertzeko! - people: - one: pertsona %{count} - other: "%{count} pertsona" + domain_validator: + invalid_domain: ez da domeinu izen baliogarria errors: + '400': Bidali duzun eskaria baliogabea da edo gaizki osatua dago. '403': Ez duzu orri hau ikusteko baimenik. '404': Bilatu duzun orria ez dago hemen. + '406': Orri hau ez dago eskatutako formatuan eskuragarri. '410': Bilatu duzun orria ez dago hemen jada. '422': content: Segurtasun egiaztaketak huts egin du. Cookie-ak blokeatzen dituzu? @@ -599,6 +682,7 @@ eu: '500': content: Sentitzen dugu, zerbait okerra gertatu da gure aldean. title: Orri hau ez da zuzena + '503': The page could not be served due to a temporary server failure. noscript_html: Mastodon web aplikazioa erabiltzeko, gaitu JavaScript. Bestela, probatu Mastodon plataformarako <a href="%{apps_path}">aplikazio natibo</a>ren bat. existing_username_validator: not_found: ezin izan da izen hori duen kide lokalik aurkitu @@ -642,6 +726,7 @@ eu: developers: Garatzaileak more: Gehiago… resources: Baliabideak + trending_now: Joera orain generic: all: Denak changes_saved_msg: Aldaketak ongi gorde dira! @@ -717,9 +802,33 @@ eu: too_many: Ezin dira 4 fitxategi baino gehiago erantsi migrations: acct: Kontu berriaren erabiltzaile@domeinua - currently_redirecting: 'Zure profila hona birbideratzeko ezarri da:' - proceed: Gorde - updated_msg: Kontuaren migrazio-ezarpenak ongi eguneratu dira! + cancel: Ezeztatu birbideratzea + cancel_explanation: Birbideratzea ezezteak zure kontu hau berriro aktibatuko du, baina ez ditu atzera ekarriko bestera aldatu diren jarraitzaileak. + cancelled_msg: Ongi ezeztatu da birbideratzea. + errors: + already_moved: kontu honetara migratu duzu dagoeneko + missing_also_known_as: ez dio kontu honi erreferentzia egiten + move_to_self: ezin da oraingo kontua izan + not_found: ezin izan da aurkitu + on_cooldown: Egonaldian zaude + followers_count: Jarraitzaileak migrazioaren unean + incoming_migrations: Beste kontu batetik migratuz + incoming_migrations_html: Beste kontu batetik hona migratzeko, aurretik <a href="%{path}">kontuaren ezizen bat sortu</a> behar duzu. + moved_msg: Zure kontuak orain %{acct} kontura birbideratzen du, eta zure jarraitzaileak hara migratuak izaten ari dira. + not_redirecting: Zure kontuak ez du orain beste konturen batera birbideratzen. + on_cooldown: Duela gutxi migratu duzu. Funtzio hau %{count} egun barru egongo da berriro eskuragarri. + past_migrations: Aurreko migrazioak + proceed_with_move: Mugitu jarraitzaileak + redirecting_to: 'Zure kontuak hona birbideratzen du: %{acct}.' + set_redirect: Ezarri birbideratzea + warning: + backreference_required: Kontu berria honi erreferentzia egiteko konfiguratu behar da + before: 'Jarraitu aurretik, irakurri adi ohar hauek:' + cooldown: Migratu eta gero egonaldi tarte bat egongo da eta bitartean ezin izango duzu berriro migratu + followers: Ekintza honek jarraitzaile guztiak eramango ditu uneko kontutik kontu berrira + only_redirect_html: Bestela, <a href="%{path}">zure profilean birbideratze soil bat jarri</a> dezakezu. + other_data: Ez da beste daturik migratuko automatikoki + redirect: Zure uneko kontuaren profila eguneratuko da birbideratze ohar batekin eta bilaketetatik kenduko da moderation: title: Moderazioa notification_mailer: @@ -783,7 +892,7 @@ eu: too_few_options: elementu bat baino gehiago izan behar du too_many_options: ezin ditu %{max} elementu baino gehiago izan preferences: - other: Beste bat + other: Denetarik posting_defaults: Bidalketarako lehenetsitakoak public_timelines: Denbora-lerro publikoak relationships: @@ -816,10 +925,6 @@ eu: reply: proceed: Ekin erantzuteari prompt: 'Toot honi erantzun nahi diozu:' - remote_unfollow: - error: Errorea - title: Izenburua - unfollowed: Jarraitzeari utzita scheduled_statuses: over_daily_limit: Egun horretarako programatutako toot kopuruaren muga gainditu duzu (%{limit}) over_total_limit: Programatutako toot kopuruaren muga gainditu duzu (%{limit}) @@ -868,6 +973,7 @@ eu: settings: account: Kontua account_settings: Kontuaren ezarpenak + aliases: Kontuaren ezizenak appearance: Itxura authorized_apps: Baimendutako aplikazioak back: Itzuli Mastodon-era @@ -908,6 +1014,9 @@ eu: private: Ezin dira publikoak ez diren toot-ak finkatu reblog: Bultzada bat ezin da finkatu poll: + total_people: + one: pertsona %{count} + other: "%{count} pertsona" total_votes: one: Boto %{count} other: "%{count} boto" @@ -926,6 +1035,8 @@ eu: pinned: Finkatutako toot-a reblogged: "(r)en bultzada" sensitive_content: 'Kontuz: Eduki hunkigarria' + tags: + does_not_match_previous_name: ez dator aurreko izenarekin bat terms: body_html: | <h2>Pribatutasun politika</h2> @@ -1044,6 +1155,7 @@ eu: silence: Zure kontua murriztua dagoen bitartean, jada zu jarraitzen zaituztenak besterik ez dituzte zure Toot-ak ikusiko zerbitzari honetan, eta agian zerrenda publikoetatik kenduko zaizu. Hala ere besteek oraindik zu jarraitu zaitzakete. suspend: Zure kontua kanporatua izan da, zure toot guztiak eta multimedia fitxategiak behin betiko ezabatu dira zerbitzari honetatik, eta zure jarraitzaileen zerbitzarietatik. review_server_policies: Berrikusi zerbitzariko politikak + statuses: 'Zehazki, honentzat:' subject: disable: Zure %{acct} kontua izoztu da none: "%{acct} konturako abisua" diff --git a/config/locales/fa.yml b/config/locales/fa.yml index d37dbdeb49ecf34693635cac02e2f24e716d12d6..ebac705d804820c33b9f4e8312b92673d3d6458e 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -2,7 +2,7 @@ fa: about: about_hashtag_html: این‌ها نوشته‌های عمومی هستند Ú©Ù‡ برچسب (هشتگ) <strong>#%{hashtag}</strong> را دارند. اگر شما روی هر سروری ØØ³Ø§Ø¨ داشته باشید می‌توانید به این نوشته‌ها واکنش نشان دهید. - about_mastodon_html: ماستدون (Mastodon) یک شبکهٔ اجتماعی است Ú©Ù‡ بر اساس پروتکل‌های آزاد وب Ùˆ Ù†Ø±Ù…â€ŒØ§ÙØ²Ø§Ø±Ù‡Ø§ÛŒ آزاد Ùˆ کدباز ساخته شده است. این شبکه مانند ایمیل غیرمتمرکز است. + about_mastodon_html: 'شبکهٔ اجتماعی آینده: بدون تبلیغات، بدون شنود از طر٠شرکت‌ها، طراØÛŒ اخلاق‌مدار، Ùˆ معماری غیرمتمرکز! با ماستدون ØµØ§ØØ¨ داده‌های خودتان باشید!' about_this: درباره active_count_after: ÙØ¹Ø§Ù„ active_footnote: کاربران ÙØ¹Ø§Ù„ در ماه گذشته @@ -10,20 +10,19 @@ fa: api: رابط برنامه‌نویسی کاربردی apps: اپ‌های موبایل apps_platforms: ماستدون را در iOSØŒ اندروید، Ùˆ سایر سیستم‌ها داشته باشید - browse_directory: در Ùهرست گزیدهٔ کاربران این سرور چرخی بزنید Ùˆ کاربران را بر اساس علاقه‌مندی‌هایشان پیدا کنید + browse_directory: کاربران این سرور را بر اساس علاقه‌مندی‌هایشان پیدا کنید browse_public_posts: Ùهرست Ù„ØØ¸Ù‡â€ŒØ§ÛŒ نوشته‌های عمومی در ماستدون را ببینید contact: تماس contact_missing: تعیین نشده contact_unavailable: موجود نیست discover_users: ÛŒØ§ÙØªÙ† کاربران documentation: مستندات - extended_description_html: | - <h3>جای خوبی برای قانون‌ها</h3> - <p>ØªÙˆØ¶ÛŒØØ§Øª تکمیلی نوشته نشده است.</p> federation_hint_html: با داشتن ØØ³Ø§Ø¨ روی %{instance} می‌توانید کاربران همهٔ سرورهای دیگر ماستدون (Ùˆ سایر شبکه‌های سازگار با آن) را Ù¾ÛŒ بگیرید. - generic_description: "%{domain} یک سرور روی شبکه است" get_apps: یک اپ موبایل را Ø§Ù…ØªØØ§Ù† کنید hosted_on: ماستدون، میزبانی‌شده روی %{domain} + instance_actor_flash: | + این ØØ³Ø§Ø¨ یک بازیگر مجازی برای نمایندگی از این سرور است Ùˆ متعلق به هیچ کاربری نیست. + این ØØ³Ø§Ø¨ برای ارتباط میان‌سروری به کار می‌رود Ùˆ نباید مسدود شود، مگر این Ú©Ù‡ شما بخواهید Ú©Ù„ سرور را مسدود کنید، Ú©Ù‡ در آن صورت باید از راه مسدودسازی دامین پیش بروید. learn_more: بیشتر بدانید privacy_policy: سیاست رازداری see_whats_happening: ببینید Ú†Ù‡ خبر است @@ -35,6 +34,14 @@ fa: status_count_before: Ú©Ù‡ در کنار هم tagline: با دوستان خود در ارتباط باشید Ùˆ دوستان تازه پیدا کنید terms: شرایط کاربری + unavailable_content: Ù…ØØªÙˆØ§ÛŒ ناموجود + unavailable_content_description: + domain: سرور + reason: 'دلیل:' + rejecting_media: تصاویر ÙØ±Ø³ØªØ§Ø¯Ù‡ شده از سمت این سرور پردازش نخواهد شد Ùˆ هیچ تصویر Ú©ÙˆÚ†Ú©ÛŒ از آن‌ها در این‌جا نمایش نخواهد ÛŒØ§ÙØªØŒ Ùˆ آن‌ها را باید مستقیماً در آن سرور ببینید. + silenced: هیچ کدام از نوشته‌ها از طر٠این سرور این‌جا نمایش نخواهند ÛŒØ§ÙØª مگر در Ùهرست پیگیری‌ها شما، اگر نویسنده‌اش را Ù¾ÛŒ بگیرید. + suspended: شما نمی‌توانید هیچ کدام از کاربرهای این سرور را Ù¾ÛŒ بگیرید، Ùˆ هیچ داده‌ای از طر٠این سرور پردازش یا ذخیره یا مبادله نخواهد شد. + unavailable_content_html: ماستدون در ØØ§Ù„ت Ú©Ù„ÛŒ اجازه می‌دهد Ú©Ù‡ شما همهٔ مطالب Ùˆ کاربران در سرورهای دیگر را نیز ببینید Ùˆ با آن‌ها برهم‌کنش داشته باشید. Ùهرست زیر ولی استثناهای این ارتباط است Ú©Ù‡ به طور خاص روی این سرور اعمال شده‌اند. user_count_after: one: کاربر other: کاربر @@ -42,6 +49,8 @@ fa: what_is_mastodon: ماستدون چیست؟ accounts: choices_html: 'انتخاب‌های %{name}:' + endorsements_hint: شما می‌توانید از Ù…ØÛŒØ· وب ماستدون، کسانی را Ú©Ù‡ Ù¾ÛŒ می‌گیرید به دیگران هم پیشنهاد دهید تا این‌جا نشان داده شوند. + featured_tags_hint: شما می‌توانید برچسب‌های دلخواه خود را پیشنهاد دهید Ú©Ù‡ این‌جا نمایش خواهند ÛŒØ§ÙØª. follow: Ù¾ÛŒ بگیرید followers: one: پیگیر @@ -53,6 +62,7 @@ fa: media: عکس Ùˆ ویدیو moved_html: "%{name} ØØ³Ø§Ø¨ خود را به %{new_profile_link} منتقل کرده است:" network_hidden: این اطلاعات در دسترس نیست + never_active: هرگز nothing_here: این‌جا چیزی نیست! people_followed_by: کسانی Ú©Ù‡ %{name} Ù¾ÛŒ می‌گیرد people_who_follow: کسانی Ú©Ù‡ %{name} را Ù¾ÛŒ می‌گیرند @@ -183,6 +193,7 @@ fa: username: نام کاربری warn: هشدار web: وب + whitelisted: Ùهرست مجاز action_logs: actions: assigned_to_self_report: "%{name} رسیدگی به گزارش %{target} را به عهده Ú¯Ø±ÙØª" @@ -218,19 +229,24 @@ fa: deleted_status: "(بوق پاک‌شده)" title: سیاههٔ بازرسی custom_emojis: + assign_category: تعیین دسته by_domain: دامین copied_msg: نسخهٔ Ù…ØÙ„ÛŒ Ø´Ú©Ù„Ú© با موÙقیت ساخته شد copy: نسخه‌برداری copy_failed_msg: نشد Ú©Ù‡ نسخهٔ Ù…ØÙ„ÛŒ این Ø´Ú©Ù„Ú© ساخته شود + create_new_category: ساختن دستهٔ تازه created_msg: این Ø´Ú©Ù„Ú© با موÙقیت ساخته شد! delete: پاک کردن destroyed_msg: این Ø´Ú©Ù„Ú© با موÙقیت پاک شد! disable: ØºÛŒØ±ÙØ¹Ø§Ù„‌سازی + disabled: ØºÛŒØ±ÙØ¹Ø§Ù„ disabled_msg: این Ø´Ú©Ù„Ú© با موÙقیت ØºÛŒØ±ÙØ¹Ø§Ù„ شد emoji: Ø´Ú©Ù„Ú© enable: ÙØ¹Ø§Ù„‌سازی + enabled: ÙØ¹Ø§Ù„ enabled_msg: این Ø´Ú©Ù„Ú© با موÙقیت ÙØ¹Ø§Ù„ شد image_hint: پروندهٔ PNG ØØ¯Ø§Ú©Ø«Ø± 50KB + list: Ùهرست listed: Ùهرست‌شده new: title: Ø§ÙØ²ÙˆØ¯Ù† Ø´Ú©Ù„Ú© Ø³ÙØ§Ø±Ø´ÛŒ @@ -238,11 +254,14 @@ fa: shortcode: کد کوتاه shortcode_hint: دست‌کم Û² نویسه Ùˆ تنها شامل ØØ±ÙˆÙØŒ اعداد Ùˆ زیرخط title: شکلک‌های Ø³ÙØ§Ø±Ø´ÛŒ + uncategorized: دسته‌بندی نشده + unlist: ناÙهرست unlisted: Ùهرست‌نشده update_failed_msg: این Ø´Ú©Ù„Ú© نتوانست به‌روز شود updated_msg: Ø´Ú©Ù„Ú© با موÙقیت به‌روز شد! upload: بارگذاری dashboard: + authorized_fetch_mode: ØØ§Ù„ت Ø¯Ø±ÛŒØ§ÙØª مجازشده backlog: کارهای باقیمانده config: پیکربندی feature_deletions: ØØ³Ø§Ø¨â€ŒÙ‡Ø§ÛŒ ØØ°Ù‌شده @@ -250,10 +269,13 @@ fa: feature_profile_directory: Ùهرست گزیدهٔ کاربران feature_registrations: ثبت‌نام‌ها feature_relay: رله + feature_spam_check: ضدهرزنامه feature_timeline_preview: پیش‌نمایش نوشته‌ها features: ویژگی‌ها hidden_service: ارتباط میان‌سروری با سرویس‌های Ù†Ù‡ÙØªÙ‡ open_reports: گزارش‌های ÙØ¹Ø§Ù„ + pending_tags: برچسب منتظر بازبینی + pending_users: کاربران منتظر بازبینی recent_users: کاربران تازه search: جستجوی متنی single_user_mode: ØØ§Ù„ت تک‌کاربره @@ -261,15 +283,22 @@ fa: space: ÙØ¶Ø§ÛŒ مصرÙ‌شده title: ابزارهای مدیریت total_users: شمار کاربران - trends: هشتگ‌های پرکاربرد + trends: برچسب‌های پرکاربرد week_interactions: ÙØ¹Ø§Ù„یت‌ها در این Ù‡ÙØªÙ‡ week_users_active: کاربران ÙØ¹Ø§Ù„ Ù‡ÙØªÙ‡Ù” اخیر week_users_new: کاربران Ù‡ÙØªÙ‡Ù” اخیر + whitelist_mode: ØØ§Ù„ت Ùهرست مجاز + domain_allows: + add_new: دامین مجازشده + created_msg: این دامین با موÙقیت مجاز شد + destroyed_msg: دامین از ØØ§Ù„ت مجاز خارج شد + undo: برداشتن از Ùهرست مجازها domain_blocks: add_new: Ø§ÙØ²ÙˆØ¯Ù† مسدودسازی دامین تازه created_msg: مسدودکردن دامین در ØØ§Ù„ انجام است destroyed_msg: مسدودکردن دامین واگردانده شد domain: دامین + edit: ویرایش مسدودسازی دامین existing_domain_block_html: شما پیش‌تر Ù…ØØ¯ÙˆØ¯ÛŒØªâ€ŒÙ‡Ø§ÛŒ سخت‌تری روی %{name} اعمال کرده‌اید، Ùˆ باید نخست <a href="%{unblock_url}">مسدودسازی را لغو کنید</a>. new: create: مسدودسازی @@ -280,6 +309,10 @@ fa: silence: بی‌صداکردن suspend: معلق‌کردن title: مسدودسازی دامین دیگر + private_comment: یادداشت خصوصی + private_comment_hint: یادداشتی دربارهٔ Ù…ØØ¯ÙˆØ¯ÛŒØª روی این دامین برای سایر ناظمان. + public_comment: یادداشت عمومی + public_comment_hint: یادداشتی دربارهٔ Ù…ØØ¯ÙˆØ¯ÛŒØª روی این دامین برای عموم، در صورتی Ú©Ù‡ Ùهرست دامین‌های Ù…ØØ¯ÙˆØ¯ شده منتشر شود. reject_media: Ù†Ù¾Ø°ÛŒØ±ÙØªÙ† پرونده‌های تصویری reject_media_hint: تصویرهای ذخیره‌شده در این‌جا را پاک می‌کند Ùˆ جلوی Ø¯Ø±ÛŒØ§ÙØª تصویرها را در آینده می‌گیرد. بی‌تأثیر برای معلق‌شده‌ها reject_reports: Ù†Ù¾Ø°ÛŒØ±ÙØªÙ† گزارش‌ها @@ -299,6 +332,7 @@ fa: title: واگردانی مسدودسازی دامنه برای %{domain} undo: واگردانی undo: واگردانی مسدودسازی دامین + view: دیدن مسدودسازی دامنه email_domain_blocks: add_new: Ø§ÙØ²ÙˆØ¯Ù† تازه created_msg: مسدودسازی دامین ایمیل با موÙقیت ساخته شد @@ -322,6 +356,8 @@ fa: all: همه limited: Ù…ØØ¯ÙˆØ¯ title: مدیریت + private_comment: یادداشت خصوصی + public_comment: یادداشت عمومی title: ارتباط میان‌سروری total_blocked_by_us: مسدودشده از طر٠ما total_followed_by_them: ما را Ù¾ÛŒ می‌گیرند @@ -351,6 +387,7 @@ fa: pending: در انتظار پذیرش رله save_and_enable: ذخیره Ùˆ ÙØ¹Ø§Ù„‌سازی setup: پیوستن به رله‌ها + signatures_not_enabled: وقتی ØØ§Ù„ت امن یا ØØ§Ù„ت Ùهرست سÙید ÙØ¹Ø§Ù„ باشد رله‌ها به درستی کار نخواهند کرد status: وضعیت title: رله‌ها report_notes: @@ -399,6 +436,16 @@ fa: custom_css: desc_html: ظاهر ماستدون را با CSS-ای Ú©Ù‡ در همهٔ ØµÙØÙ‡â€ŒÙ‡Ø§ جاسازی می‌شود تغییر دهید title: سبک CSS Ø³ÙØ§Ø±Ø´ÛŒ + default_noindex: + desc_html: روی همهٔ کاربرانی Ú©Ù‡ این تنظیم را خودشان تغییر نداده‌اند تأثیر می‌گذارد + title: درخواست Ù¾ÛŒØ´â€ŒÙØ±Ø¶ از طر٠کاربران برای ظاهر نشدن در نتایج موتورهای جستجوگر + domain_blocks: + all: برای همه + disabled: برای هیچ‌کدام + title: نمایش دامین‌های مسدودشده + users: برای کاربران Ù…ØÙ„ÛŒ واردشده + domain_blocks_rationale: + title: دیدن دلیل hero: desc_html: در ØµÙØÙ‡Ù” آغازین نمایش می‌یابد. دست‌کم ۶۰۰×۱۰۰ پیکسل توصیه می‌شود. اگر تعیین نشود، با تصویر بندانگشتی سرور جایگزین خواهد شد title: تصویر سربرگ @@ -449,6 +496,9 @@ fa: desc_html: می‌توانید سیاست رازداری، شرایط Ø§Ø³ØªÙØ§Ø¯Ù‡ØŒ یا سایر مسائل قانونی را به دلخواه خود بنویسید. تگ‌های HTML هم مجاز است title: شرایط Ø§Ø³ØªÙØ§Ø¯Ù‡Ù” Ø³ÙØ§Ø±Ø´ÛŒ site_title: نام سرور + spam_check_enabled: + desc_html: ماستدون می‌تواند ØØ³Ø§Ø¨â€ŒÙ‡Ø§ را به طور خودکار بی‌صدا کند یا گزارش دهد. این کار بر اساس سنجه‌هایی از قبیل شناسایی پیغام‌های ناخواستهٔ تکراری انجام می‌شود Ùˆ ممکن است گاهی اشتباه باشد. + title: ضدهرزنامه thumbnail: desc_html: برای دیدن با OpenGraph Ùˆ رابط برنامه‌نویسی. ÙˆØ¶ÙˆØ Ù¾ÛŒØ´Ù†Ù‡Ø§Ø¯ÛŒ ۱۲۰۰×۶۳۰ پیکسل title: تصویر Ú©ÙˆÚ†Ú© سرور @@ -456,12 +506,19 @@ fa: desc_html: نوشته‌های عمومی این سرور را در ØµÙØÙ‡Ù” آغازین نشان دهید title: پیش‌نمایش نوشته‌ها title: تنظیمات سایت + trendable_by_default: + desc_html: روی برچسب‌هایی Ú©Ù‡ پیش از این ممنوع نشده‌اند تأثیر می‌گذارد + title: بگذارید Ú©Ù‡ برچسب‌های Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± بدون بازبینی قبلی نمایش داده شوند + trends: + desc_html: برچسب‌های عمومی Ú©Ù‡ پیش‌تر بازبینی شده‌اند Ùˆ هم‌اینک Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø±Ù†Ø¯ + title: برچسب‌های Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± statuses: back_to_account: بازگشت به ØµÙØÙ‡Ù” ØØ³Ø§Ø¨ batch: delete: پاک‌کردن nsfw_off: علامت‌زدن به عنوان ØºÛŒØ±ØØ³Ø§Ø³ nsfw_on: علامت‌زدن به عنوان ØØ³Ø§Ø³ + deleted: پاک‌شده failed_to_execute: اجرا نشد media: title: رسانه @@ -469,21 +526,24 @@ fa: no_status_selected: هیچ بوقی تغییری نکرد زیرا هیچ‌کدام از آن‌ها انتخاب نشده بودند title: نوشته‌های ØØ³Ø§Ø¨ with_media: دارای عکس یا ویدیو - subscriptions: - callback_url: نشانی Callback - confirmed: تأییدشده - expires_in: مهلت انقضا - last_delivery: آخرین ارسال - title: WebSub - topic: موضوع tags: - accounts: ØØ³Ø§Ø¨â€ŒÙ‡Ø§ - hidden: پنهان‌شده - hide: در Ùهرست گزیدهٔ کاربران نشان نده + accounts_today: کاربرد یکتا در امروز + accounts_week: کاربرد یکتا در این Ù‡ÙØªÙ‡ + breakdown: کاربردهای امروز به تÙکیک منبع + context: زمینه + directory: در Ùهرست + in_directory: "%{count} در Ùهرست" + last_active: آخرین ÙØ¹Ø§Ù„یت + most_popular: Ù…ØØ¨ÙˆØ¨â€ŒØªØ±ÛŒÙ† + most_recent: تازه‌ترین name: برچسب + review: وضعیت بازبینی + reviewed: بازبینی شده title: برچسب‌ها - unhide: نمایش در Ùهرست گزیدهٔ کاربران - visible: نمایان + trending_right_now: Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø±Ù‡Ø§ÛŒ کنونی + unique_uses_today: "%{count} امروز منتشر شده" + unreviewed: بازبینی نشده + updated_msg: تنظیمات برچسب‌ها با موÙقیت به‌روز شد title: مدیریت سرور warning_presets: add_new: Ø§ÙØ²ÙˆØ¯Ù† تازه @@ -499,11 +559,21 @@ fa: body: کاربر %{reporter} کاربر %{target} را گزارش داد body_remote: کسی از %{domain} گزارش %{target} را ÙØ±Ø³ØªØ§Ø¯Ù‡ subject: گزارش تازه‌ای برای %{instance} (#%{id}) + new_trending_tag: + body: 'برچسب #%{name} امروز Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± است، ولی تا ØØ§Ù„ا بازبینی نشده. تا وقتی Ú©Ù‡ شما اجازه نداده‌اید، این برچسب به طور عمومی نمایش داده نخواهد شد. اگر ÙØ±Ù… را به Ø´Ú©Ù„ ÙØ¹Ù„ÛŒ ذخیره کنید، هیچ وقت چیزی دربارهٔ این برچسب نخواهید دید.' + subject: برچسب تازه‌ای در %{instance} نیازمند بررسی است (#%{name}) + aliases: + add_new: ساختن نام مستعار + created_msg: نام مستعار تازه با موÙقیت ساخته شد. الان می‌توانید انتقال از ØØ³Ø§Ø¨ قدیمی را آغاز کنید. + deleted_msg: نام مستعار با موÙقیت ØØ°Ù شد. انتقال از آن ØØ³Ø§Ø¨ به ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ دیگر ممکن نیست. + hint_html: اگر می‌خواهید از ØØ³Ø§Ø¨ دیگری به این ØØ³Ø§Ø¨ منتقل شوید، این‌جا می‌توانید یک نام مستعار بسازید Ú©Ù‡ برای انتقال از ØØ³Ø§Ø¨ قدیمی به این ØØ³Ø§Ø¨ لازم است. این کار به تنهایی <strong>بی‌ضرر Ùˆ قابل بازگشت</strong> است. <strong>ÙØ±Ø§ÛŒÙ†Ø¯ انتقال ØØ³Ø§Ø¨ از ØØ³Ø§Ø¨ قدیمی آغاز خواهد شد</strong>. + remove: ØØ°Ù ارتباط نام مستعار appearance: advanced_web_interface: رابط کاربری Ù¾ÛŒØ´Ø±ÙØªÙ‡ advanced_web_interface_hint: 'اگر می‌خواهید همهٔ ÙØ¶Ø§ÛŒ نمایشگر خود را به کار ببرید، می‌توانید به Ú©Ù…Ú© رابط کاربری Ù¾ÛŒØ´Ø±ÙØªÙ‡ ستون‌های گوناگونی داشته باشید تا در یک نگاه همهٔ اطلاعاتی را Ú©Ù‡ می‌خواهید ببینید: نوشته‌های دیگران، اعلان‌ها، Ùهرست نوشته‌های همه‌جا، Ùˆ هر تعداد Ùهرست Ùˆ برچسب Ú©Ù‡ بخواهید.' animations_and_accessibility: پویانمایی‌های Ùˆ دسترسی‌پذیری confirmation_dialogs: پیغام‌های تأیید + discovery: کاوش sensitive_content: Ù…ØØªÙˆØ§ÛŒ ØØ³Ø§Ø³ application_mailer: notification_preferences: تغییر ØªØ±Ø¬ÛŒØØ§Øª ایمیل @@ -524,9 +594,13 @@ fa: apply_for_account: درخواست دعوت‌نامه change_password: رمز checkbox_agreement_html: من <a href="%{rules_path}" target="_blank">قانون‌های این سرور</a> Ùˆ <a href="%{terms_path}" target="_blank">شرایط کاربری</a> را می‌پذیرم - confirm_email: تأیید ایمیل + checkbox_agreement_without_rules_html: من با <a href="%{terms_path}" target="_blank">شرایط Ø§Ø³ØªÙØ§Ø¯Ù‡</a> مواÙقم delete_account: پاک‌کردن ØØ³Ø§Ø¨ delete_account_html: اگر می‌خواهید ØØ³Ø§Ø¨ خود را پاک کنید، از <a href="%{path}">این‌جا</a> پیش بروید. از شما درخواست تأیید خواهد شد. + description: + prefix_invited_by_user: "@%{name} شما را به عضویت در این سرور ماستدون دعوت کرده است!" + prefix_sign_up: همین امروز عضو ماستدون شوید! + suffix: با داشتن ØØ³Ø§Ø¨ می‌توانید دیگران را Ù¾ÛŒ بگیرید، نوشته‌های تازه منتشر کنید، Ùˆ با کاربران دیگر از هر سرور ماستدون دیگری Ùˆ ØØªÛŒ سرورهای دیگر در ارتباط باشید! didnt_get_confirmation: راهنمایی برای تأیید را Ø¯Ø±ÛŒØ§ÙØª نکردید؟ forgot_password: رمزتان را Ú¯Ù… کرده‌اید؟ invalid_reset_password_token: کد بازنشانی رمز نامعتبر یا منقضی شده است. Ù„Ø·ÙØ§Ù‹ کد دیگری درخواست کنید. @@ -544,6 +618,16 @@ fa: reset_password: بازنشانی رمز security: امنیت set_new_password: تعیین رمز تازه + setup: + email_below_hint_html: اگر نشانی ایمیل زیر نادرست است، می‌توانید آن را تغییر دهید Ùˆ ایمیل تأیید دوباره‌ای Ø¯Ø±ÛŒØ§ÙØª کنید. + email_settings_hint_html: ایمیل تأیید به %{email} ÙØ±Ø³ØªØ§Ø¯Ù‡ شد. اگر این نشانی ایمیل درست نیست، می‌توانید از تنظیمات ØØ³Ø§Ø¨ آن را تغییر دهید. + title: راه اندازی + status: + account_status: وضعیت ØØ³Ø§Ø¨ + confirming: در ØØ§Ù„ انتظار برای کامل شدن تأیید ایمیل. + functional: ØØ³Ø§Ø¨ شما قابل Ø§Ø³ØªÙØ§Ø¯Ù‡ است. + pending: درخواست شما منتظر تأیید مسئولان سایت است Ùˆ این ÙØ±Ø§ÛŒÙ†Ø¯ ممکن است Ú©Ù…ÛŒ طول بکشد. اگر درخواست شما Ù¾Ø°ÛŒØ±ÙØªÙ‡ شود به شما ایمیلی ÙØ±Ø³ØªØ§Ø¯Ù‡ خواهد شد. + redirecting_to: ØØ³Ø§Ø¨ شما ØºÛŒØ±ÙØ¹Ø§Ù„ است زیرا هم‌اکنون به %{acct} منتقل شده است. trouble_logging_in: برای ورود مشکلی دارید؟ authorize_follow: already_following: شما همین الان هم این ØØ³Ø§Ø¨ را پی‌می‌گیرید @@ -556,6 +640,11 @@ fa: return: نمایهٔ این کاربر را نشان بده web: Ø±ÙØªÙ† به وب title: پیگیری %{acct} + challenge: + confirm: ادامه + hint_html: "<strong>نکته:</strong> ما در یک ساعت آینده رمزتان را از شما نخواهیم پرسید." + invalid_password: رمز نامعتبر + prompt: برای ادامه رمزتان را تأیید کنید datetime: distance_in_words: about_x_hours: "%{count} ساعت" @@ -571,26 +660,33 @@ fa: x_months: "%{count} ماه" x_seconds: "%{count} ثانیه" deletes: - bad_password_msg: هکر گرامی، رمزی Ú©Ù‡ وارد کردید اشتباه است Ø›) + challenge_not_passed: اطلاعاتی Ú©Ù‡ وارد کردید اشتباه بود confirm_password: رمز ÙØ¹Ù„ÛŒ خود را وارد کنید تا معلوم شود Ú©Ù‡ خود شمایید - description_html: این کار همهٔ Ù…ØØªÙˆØ§ÛŒ ØØ³Ø§Ø¨ شما را <strong>برای همیشه Ùˆ به‌طور بازگشت‌ناپذیری</strong> پاک کرده Ùˆ ØØ³Ø§Ø¨ را ØºÛŒØ±ÙØ¹Ø§Ù„ می‌کند. نام کاربری شما برای جلوگیری از جعل هویت Ø§ØØªÙ…الی در آینده از دسترس خارج خواهد شد. + confirm_username: برای تأیید این ÙØ±Ø§ÛŒÙ†Ø¯ نام کاربری خود را وارد کنید proceed: پاک‌کردن ØØ³Ø§Ø¨ success_msg: ØØ³Ø§Ø¨ شما با موÙقیت پاک شد - warning_html: تنها پاک‌شدن Ù…ØØªÙˆØ§ÛŒ ØØ³Ø§Ø¨ در این سرور خاص تضمین می‌شود. Ù…ØØªÙˆØ§ÛŒÛŒ Ú©Ù‡ به گستردگی هم‌رسانی شده باشد ممکن است ردش همچنان باقی بماند. سرورهای Ø¢Ùلاین یا سرورهایی Ú©Ù‡ دیگر مشترک شما نیستند پایگاه‌های دادهٔ خود را به‌روز نخواهند کرد. - warning_title: دسترس‌پذیری Ù…ØØªÙˆØ§ÛŒ هم‌رسان‌شده + warning: + before: 'پیش از ادامه،‌ Ù„Ø·ÙØ§Ù‹ نکته‌های زیر را به دقت بخوانید:' + caches: Ù…ØØªÙˆØ§Ù‡Ø§ÛŒÛŒ Ú©Ù‡ سرورهای دیگر ذخیره کرده‌اند شاید همچنان باقی بمانند + data_removal: نوشته‌ها Ùˆ داده‌های شما برای همیشه پاک خواهند شد + email_change_html: شما می‌توانید بدون پاک کردن ØØ³Ø§Ø¨ <a href="%{path}">نشانی ایمیل خود را تغییر دهید</a> + email_contact_html: اگر ایمیل همچنان نرسیده، برای درخواست Ú©Ù…Ú© به <a href="mailto:%{email}">%{email}</a> پیغام دهید + email_reconfirmation_html: اگر ایمیل تأیید به دستتان نرسیده، می‌توانید <a href="%{path}">یک بار دیگر برایش درخواست بدهید</a> + irreversible: شما نخواهید توانست ØØ³Ø§Ø¨ خود را بازیابی یا ÙØ¹Ø§Ù„‌سازی کنید + more_details_html: برای اطلاعات بیشتر <a href="%{terms_path}">سیاست رازداری</a> را ببینید. + username_available: نام کاربری شما دوباره در دسترس خواهد بود + username_unavailable: نام کاربری شما برای دیگران غیرقابل دسترس خواهد ماند directories: directory: Ùهرست گزیدهٔ کاربران - enabled: شما هم‌اینک در Ùهرست گزیدهٔ کاربران نمایش می‌یابید. - enabled_but_waiting: شما می‌خواهید در Ùهرست گزیدهٔ کاربران این سرور باشید، ولی تعداد پیگیران شما هنوز به مقدار لازم (%{min_followers}) نرسیده است. explanation: کاربران این سرور را بر اساس علاقه‌مندی‌هایشان پیدا کنید explore_mastodon: گشت Ùˆ گذار در %{title} - how_to_enable: شما هنوز در Ùهرست گزیدهٔ کاربران این سرور نشان داده نمی‌شوید. این‌جا می‌توانید انتخابش کنید. اگر در بخش معرÙÛŒ خود در نمایه‌تان برچسب (هشتگ) داشته باشد، نام شما هم برای آن هشتگ‌ها Ùهرست می‌شود! - people: - one: "%{count} Ù†ÙØ±" - other: "%{count} Ù†ÙØ±" + domain_validator: + invalid_domain: نام دامین معتبر نیست errors: + '400': درخواستی Ú©Ù‡ ÙØ±Ø³ØªØ§Ø¯ÛŒØ¯ نامعتبر یا اشتباه بود. '403': شما اجازهٔ دیدن این ØµÙØÙ‡ را ندارید. '404': ØµÙØÙ‡â€ŒØ§ÛŒ Ú©Ù‡ به دنبالش هستید این‌جا نیست. + '406': این ØµÙØÙ‡ در قالبی Ú©Ù‡ درخواست کرده‌اید موجود نیست. '410': ØµÙØÙ‡â€ŒØ§ÛŒ Ú©Ù‡ به دنبالش بودید دیگر این‌جا وجود ندارد. '422': content: تأیید امنیتی انجام نشد. آیا مرورگر شما کوکی‌ها را مسدود می‌کند؟ @@ -599,6 +695,7 @@ fa: '500': content: شرمنده، یک چیزی از سمت ما اشتباه شده. title: این ØµÙØÙ‡ درست نیست + '503': این ØµÙØÙ‡ به خاطر مشکل موقت سرور در دسترس نیست. noscript_html: برای Ø§Ø³ØªÙØ§Ø¯Ù‡ از نسخهٔ ØªØØª وب ماستدون، Ù„Ø·ÙØ§Ù‹ جاوااسکریپت را ÙØ¹Ø§Ù„ کنید. یا به جایش می‌توانید <a href="%{apps_path}">یک اپ ماستدون</a> را به‌کار ببرید. existing_username_validator: not_found: کاربری در این سرور با این نام کاربری پیدا نشد @@ -622,6 +719,7 @@ fa: add_new: Ø§ÙØ²ÙˆØ¯Ù† تازه errors: limit: شما بیشترین تعداد مجاز برچسب‌ها را دارید + hint_html: "<strong>برچسب‌های برگزیده چیستند؟</strong> این برچسب‌ها (هشتگ‌ها) به طور واضØÛŒ روی نمایهٔ عمومی شما نمایش می‌یابند Ùˆ دیگران می‌توانند نوشته‌های شما را ØªØØª هر کدام از این برچسب‌ها مرور کنند. این یک روش بسیار خوب برای دسته‌بندی آثار خلاقانه یا پروژه‌های بلندمدت شماست." filters: contexts: home: خانه @@ -642,10 +740,12 @@ fa: developers: برنامه‌نویسان more: بیشتر… resources: منابع + trending_now: Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± generic: all: همه changes_saved_msg: تغییرات با موÙقیت ذخیره شدند! copy: رونوشت + no_batch_actions_available: هیچ کار گروهی‌ای در این ØµÙØÙ‡ موجود نیست order_by: مرتب‌سازی save_changes: ذخیرهٔ تغییرات validation_errors: @@ -717,9 +817,34 @@ fa: too_many: نمی‌توان بیشتر از Û´ تصویر بارگذاری کرد migrations: acct: username@domain ØØ³Ø§Ø¨ تازه - currently_redirecting: 'نمایهٔ شما منتقل می‌شود به:' - proceed: ذخیره - updated_msg: تنظیمات نقل مکان ØØ³Ø§Ø¨ شما با موÙقیت به‌روز شد! + cancel: لغو انتقال + cancel_explanation: با لغو انتقال، ØØ³Ø§Ø¨ شما دوباره ÙØ¹Ø§Ù„ می‌شود، ولی این کار پیگیران شما را Ú©Ù‡ به ØØ³Ø§Ø¨ دیگر منتقل شده‌اند برنمی‌گرداند. + cancelled_msg: انتقال ØØ³Ø§Ø¨ با موÙقیت لغو شد. + errors: + already_moved: این همان ØØ³Ø§Ø¨ÛŒ است Ú©Ù‡ به آن منتقل شده‌اید + missing_also_known_as: به ØØ³Ø§Ø¨ شما اشاره نمی‌کند + move_to_self: نمی‌تواند ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ شما باشد + not_found: چنین ØØ³Ø§Ø¨ÛŒ پیدا نشد + on_cooldown: شما باید صبر کنید + followers_count: شمار پیگیران در زمان انتقال + incoming_migrations: انتقال از یک ØØ³Ø§Ø¨ دیگر + incoming_migrations_html: برای انتقال از یک ØØ³Ø§Ø¨ دیگر به این ØØ³Ø§Ø¨ØŒ شما باید نخست <a href="%{path}">یک نام مستعار بسازید</a>. + moved_msg: ØØ³Ø§Ø¨ شما هم‌اینک به %{acct} منتقل شده است Ùˆ پیگیران شما در ØØ§Ù„ انتقال به آن‌جا هستند. + not_redirecting: ØØ³Ø§Ø¨ شما هم‌اینک به هیچ ØØ³Ø§Ø¨ دیگری منتقل نشده است یا اشاره نمی‌کند. + on_cooldown: شما به تازگی ØØ³Ø§Ø¨ خود را منتقل کرده‌اید. این ویژگی برای شما دوباره در %{count} روز ÙØ¹Ø§Ù„ خواهد شد. + past_migrations: انتقال‌های پیشین + proceed_with_move: انتقال پیگیران + redirecting_to: ØØ³Ø§Ø¨ شما هم‌اینک به %{acct} منتقل شده است. + set_redirect: اشاره به ØØ³Ø§Ø¨ دیگر + warning: + backreference_required: ØØ³Ø§Ø¨ تازهٔ شما نخست باید تنظیم شود تا به این ØØ³Ø§Ø¨ اشاره کند + before: 'پیش از ادامه،‌ Ù„Ø·ÙØ§Ù‹ نکته‌های زیر را به دقت بخوانید:' + cooldown: پس از انتقال ØØ³Ø§Ø¨ØŒ یک دورهٔ انتظار وجود دارد Ú©Ù‡ در این مدت نخواهید توانست دوباره ØØ³Ø§Ø¨ØªØ§Ù† را منتقل کنید + disabled_account: ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ شما پس از این کار دیگر قابل Ø§Ø³ØªÙØ§Ø¯Ù‡ نخواهد بود. شما Ùقط خواهید توانست داده‌های خود را بیرون ببرید یا ØØ³Ø§Ø¨ را دوباره ÙØ¹Ø§Ù„ کنید. + followers: این کار همهٔ پیگیران شما را از ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ به ØØ³Ø§Ø¨ تازه منتقل خواهد کرد + only_redirect_html: شما همچنین می‌توانید ØØ³Ø§Ø¨ خود را <a href="%{path}">به یک ØØ³Ø§Ø¨ دیگر اشاره دهید</a>. + other_data: هیچ دادهٔ دیگری خودبه‌خود منتقل نخواهد شد + redirect: نمایهٔ ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ شما به ØØ³Ø§Ø¨ تازه اشاره خواهد کرد Ùˆ خودش در نتیجهٔ جستجوها ظاهر نخواهد شد moderation: title: مدیریت کاربران notification_mailer: @@ -816,10 +941,6 @@ fa: reply: proceed: به سمت پاسخ‌دادن prompt: 'شما می‌خواهید به این بوق پاسخ دهید:' - remote_unfollow: - error: خطا - title: عنوان - unfollowed: پایان پیگیری scheduled_statuses: over_daily_limit: شما از ØØ¯ مجاز %{limit} بوق زمان‌بندی‌شده در آن روز ÙØ±Ø§ØªØ± Ø±ÙØªÙ‡â€ŒØ§ÛŒØ¯ over_total_limit: شما از ØØ¯ مجاز %{limit} بوق زمان‌بندی‌شده ÙØ±Ø§ØªØ± Ø±ÙØªÙ‡â€ŒØ§ÛŒØ¯ @@ -868,6 +989,7 @@ fa: settings: account: ØØ³Ø§Ø¨ account_settings: تنظیمات ØØ³Ø§Ø¨ + aliases: نام‌های مستعار appearance: نما authorized_apps: برنامه‌های مجاز back: بازگشت به ماستدون @@ -885,6 +1007,8 @@ fa: profile: نمایه relationships: پیگیری‌ها Ùˆ پیگیران two_factor_authentication: ورود دومرØÙ„ه‌ای + spam_check: + spam_detected: این یک گزارش خودکار برای تشخیص هرزنامه است. statuses: attached: description: 'پیوست‌شده: %{attached}' @@ -908,6 +1032,9 @@ fa: private: نوشته‌های غیرعمومی را نمی‌توان ثابت کرد reblog: بازبوق‌ها را نمی‌توان ثابت کرد poll: + total_people: + one: "%{count} Ù†ÙØ±" + other: "%{count} Ù†ÙØ±" total_votes: one: "%{count} رأی" other: "%{count} رأی" @@ -926,6 +1053,8 @@ fa: pinned: نوشته‌های ثابت reblogged: بازبوقید sensitive_content: Ù…ØØªÙˆØ§ÛŒ ØØ³Ø§Ø³ + tags: + does_not_match_previous_name: با نام پیشین مطابق نیست terms: body_html: | <h2>سیاست رازداری</h2> @@ -1043,7 +1172,9 @@ fa: disable: تا وقتی ØØ³Ø§Ø¨ شما متوق٠باشد، داده‌های شما دست‌نخورده باقی می‌مانند، ولی تا وقتی Ú©Ù‡ ØØ³Ø§Ø¨ØªØ§Ù† باز نشده، نمی‌توانید هیچ کاری با آن بکنید. silence: تا وقتی ØØ³Ø§Ø¨ شما Ù…ØØ¯ÙˆØ¯ باشد، تنها کسانی Ú©Ù‡ از قبل پیگیر شما بودند نوشته‌های شما در این سرور را می‌بینند Ùˆ شما در Ùهرست‌های عمومی دیده نمی‌شوید. ولی دیگران همچنان می‌توانند به دلخواه خودشان پیگیر شما شوند. suspend: ØØ³Ø§Ø¨ شما معلق شده است، Ùˆ همهٔ نوشته‌ها Ùˆ رسانه‌های تصویری شما به طور بازگشت‌ناپذیری پاک شده‌اند؛ Ú†Ù‡ از این سرور Ùˆ Ú†Ù‡ از سرورهای دیگری Ú©Ù‡ از آن‌ها پیگیر داشتید. + get_in_touch: با پاسخ به این ایمیل می‌توانید با دست‌اندرکاران %{instance} در تماس باشید. review_server_policies: مرور سیاست‌های این سرور + statuses: 'به طور خاص برای:' subject: disable: ØØ³Ø§Ø¨ %{acct} شما متوق٠شده است none: هشدار برای %{acct} @@ -1059,7 +1190,7 @@ fa: edit_profile_step: 'شما می‌توانید نمایهٔ خود را به دلخواه خود تغییر دهید: می‌توانید تصویر نمایه، تصویر پس‌زمینه، نام، Ùˆ چیزهای دیگری را تعیین کنید. اگر بخواهید، می‌توانید ØØ³Ø§Ø¨ خود را خصوصی کنید تا Ùقط کسانی Ú©Ù‡ شما اجازه می‌دهید بتوانند پیگیر ØØ³Ø§Ø¨ شما شوند.' explanation: نکته‌هایی Ú©Ù‡ برای آغاز کار به شما Ú©Ù…Ú© می‌کنند final_action: چیزی منتشر کنید - final_step: 'چیزی بنویسید! ØØªÛŒ اگر الان کسی پیگیر شما نباشد، دیگران نوشته‌های عمومی شما را می‌بینند، مثلاً در Ùهرست نوشته‌های Ù…ØÙ„ÛŒ Ùˆ در هشتگ‌ها. شاید بخواهید با هشتگ #آشنایی خودتان را معرÙÛŒ کنید.' + final_step: 'چیزی بنویسید! ØØªÛŒ اگر الان کسی پیگیر شما نباشد، دیگران نوشته‌های عمومی شما را می‌بینند، مثلاً در Ùهرست نوشته‌های Ù…ØÙ„ÛŒ Ùˆ در برچسب (هشتگ)ها. شاید بخواهید با برچسب #معرÙÛŒ خودتان را معرÙÛŒ کنید.' full_handle: نام کاربری کامل شما full_handle_hint: این چیزی است Ú©Ù‡ باید به دوستان خود بگویید تا بتوانند به شما پیغام Ø¨ÙØ±Ø³ØªÙ†Ø¯ یا از سرورهای دیگر پیگیر شما شوند. review_preferences_action: تغییر ØªØ±Ø¬ÛŒØØ§Øª diff --git a/config/locales/fi.yml b/config/locales/fi.yml index e0dc0f756e5912420bf5243aa4c761aaa13d27b2..500c2408f29a2df2c9ac34b4c0ec502cc80b5186 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -10,10 +10,6 @@ fi: contact_missing: Ei asetettu contact_unavailable: Ei saatavilla documentation: Dokumentaatio - extended_description_html: | - <h3>Hyvä paikka säännöille</h3> - <p>Pidempää kuvausta ei ole vielä laadittu.</p> - generic_description: "%{domain} on yksi verkostoon kuuluvista palvelimista" hosted_on: Mastodon palvelimella %{domain} learn_more: Lisätietoja privacy_policy: Tietosuojaseloste @@ -330,12 +326,6 @@ fi: no_media: Ei mediaa title: Tilin tilat with_media: Sisältää mediaa - subscriptions: - callback_url: Paluu-URL - confirmed: Vahvistettu - expires_in: Vanhenee - last_delivery: Viimeisin toimitus - topic: Aihe title: Ylläpito admin_mailer: new_report: @@ -358,7 +348,6 @@ fi: your_token: Pääsytunnus auth: change_password: Salasana - confirm_email: Vahvista sähköpostiosoite delete_account: Poista tili delete_account_html: Jos haluat poistaa tilisi, <a href="%{path}">paina tästä</a>. Poisto on vahvistettava. didnt_get_confirmation: Etkö saanut vahvistusohjeita? @@ -400,16 +389,14 @@ fi: x_months: "%{count} kk" x_seconds: "%{count} s" deletes: - bad_password_msg: Hyvä yritys, hakkerit! Väärä salasana confirm_password: Tunnistaudu syöttämällä nykyinen salasanasi - description_html: Tämä poistaa <strong>pysyvästi ja peruuttamattomasti</strong> kaiken tilisi sisällön ja poistaa tilin käytöstä. Käyttäjänimesi pysyy varattuna, jotta identiteettiäsi ei myöhemmin varasteta. proceed: Poista tili success_msg: Tilin poisto onnistui - warning_html: Sisällön poistaminen taataan vain tämän instanssin osalta. Jos sisältöä on jaettu paljon, siitä todennäköisesti jää jälkiä. Palvelimet, joihin ei saada yhteyttä tai jotka ovat lopettaneet päivitystesi tilaamisen, eivät päivitä tietokantojaan. - warning_title: Sisällön saatavuustieto levitetty errors: + '400': The request you submitted was invalid or malformed. '403': Sinulla ei ole lupaa nähdä tätä sivua. '404': Etsimääsi sivua ei ole olemassa. + '406': This page is not available in the requested format. '410': Etsimääsi sivua ei ole enää olemassa. '422': content: Turvallisuusvahvistus epäonnistui. Oletko estänyt evästeet? @@ -418,6 +405,7 @@ fi: '500': content: Valitettavasti jokin meni pieleen meidän päässämme. title: Sivu ei ole oikein + '503': The page could not be served due to a temporary server failure. noscript_html: Mastodon-selainsovelluksen käyttöön vaaditaan JavaScript. Voit vaihtoehtoisesti kokeilla jotakin omalle käyttöjärjestelmällesi tehtyä Mastodon<a href="%{apps_path}">sovellusta</a>. exports: archive_takeout: @@ -476,9 +464,6 @@ fi: too_many: Tiedostoja voi liittää enintään 4 migrations: acct: uuden tilin käyttäjätunnus@verkkotunnus - currently_redirecting: 'Profiiliisi on asetettu uudelleenohjaus:' - proceed: Tallenna - updated_msg: Tilinsiirtoasetusten päivitys onnistui! moderation: title: Moderointi notification_mailer: @@ -536,8 +521,6 @@ fi: missing_resource: Vaadittavaa uudelleenohjaus-URL:ää tiliisi ei löytynyt proceed: Siirry seuraamaan prompt: 'Olet aikeissa seurata:' - remote_unfollow: - error: Virhe sessions: activity: Viimeisin toiminta browser: Selain diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 5c15ab6a4282870b9a29a07d68884187df335823..f44a0893f38854eba22d53372586cfbe75325b1a 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2,7 +2,7 @@ fr: about: about_hashtag_html: Figurent ci-dessous les pouets tagués avec <strong>#%{hashtag}</strong>. Vous pouvez interagir avec eux si vous avez un compte n’importe où dans le Fediverse. - about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé. + about_mastodon_html: Mastodon est un réseau social utilisant des protocoles Web ouverts et des logiciels libres. Tout comme le courriel, il est décentralisé. about_this: À propos active_count_after: actif·ve·s active_footnote: Utilisateur·rice·s actif·ve·s mensuels (MAU) @@ -17,13 +17,12 @@ fr: contact_unavailable: Non disponible discover_users: Découvrez des utilisateur·rice·s documentation: Documentation - extended_description_html: | - <h3>Un bon endroit pour les règles</h3> - <p>La description étendue n’a pas été remplie.</p> federation_hint_html: Avec un compte sur %{instance}, vous pourrez suivre les gens sur n’importe quel serveur Mastodon et au-delà . - generic_description: "%{domain} est seulement un serveur du réseau" get_apps: Essayez une application mobile hosted_on: Serveur Mastodon hébergée par %{domain} + instance_actor_flash: | + Ce compte est un acteur virtuel utilisé pour représenter le serveur lui-même et non un utilisateur individuel. + Il est utilisé à des fins de fédération et ne doit pas être bloqué à moins que vous ne vouliez bloquer l’instance entière, dans ce cas vous devriez utiliser un bloqueur de domaine. learn_more: En savoir plus privacy_policy: Politique de vie privée see_whats_happening: Voir ce qui se passe @@ -35,6 +34,14 @@ fr: status_count_before: Ayant publié tagline: Suivez vos ami·e·s et découvrez en de nouveaux·elles terms: Conditions d’utilisation + unavailable_content: Contenu non disponible + unavailable_content_description: + domain: Serveur + reason: 'Motif :' + rejecting_media: Les fichiers média de ce serveur ne seront pas traités et aucune miniature ne sera affichée, nécessitant un clic manuel vers l'autre serveur. + silenced: Les messages de ce serveur ne s'afficheront nulle part sauf votre flux personnel si vous suivez l'auteur. + suspended: Vous ne pourrez suivre personne de ce serveur, aucune donnée ne sera traitée ou stockée et aucune donnée ne sera échangée. + unavailable_content_html: Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateurs de n'importe quel autre serveur dans le fédiverse. Voici les exceptions qui ont été faites sur ce serveur en particulier. user_count_after: one: utilisateur other: utilisateurs @@ -42,6 +49,8 @@ fr: what_is_mastodon: Qu’est-ce que Mastodon ? accounts: choices_html: "%{name} recommande :" + endorsements_hint: Vous pouvez soutenir les personnes que vous suivez depuis l’interface web, et elles apparaîtront ici. + featured_tags_hint: Vous pouvez recommander des hashtags spécifiques qui seront affichés ici. follow: Suivre followers: one: Abonné·e @@ -53,6 +62,7 @@ fr: media: Médias moved_html: "%{name} a changé de compte pour %{new_profile_link} :" network_hidden: Cette information n’est pas disponible + never_active: Jamais nothing_here: Rien à voir ici ! people_followed_by: Personnes suivies par %{name} people_who_follow: Personnes qui suivent %{name} @@ -183,6 +193,7 @@ fr: username: Nom d’utilisateurâ‹…ice warn: Avertissement web: Web + whitelisted: Sur liste blanche action_logs: actions: assigned_to_self_report: "%{name} s’est assigné·e le signalement de %{target}" @@ -218,19 +229,24 @@ fr: deleted_status: "(statut supprimé)" title: Journal d’audit custom_emojis: + assign_category: Attribuer une catégorie by_domain: Domaine copied_msg: Copie locale de l’émoji créée avec succès ! copy: Copier copy_failed_msg: Impossible de faire une copie locale de cet émoji + create_new_category: Créer une nouvelle catégorie created_msg: Émoji créé avec succès ! delete: Supprimer destroyed_msg: Émoji supprimé avec succès ! disable: Désactiver + disabled: Désactivé disabled_msg: Émoji désactivé avec succès ! emoji: Émoji enable: Activer + enabled: Activé enabled_msg: Émoji activé avec succès image_hint: PNG de moins de 50 Ko + list: Liste listed: Listé new: title: Ajouter un nouvel émoji personnalisé @@ -238,11 +254,14 @@ fr: shortcode: Raccourci shortcode_hint: Au moins deux caractères, seulement des caractères alphanumériques ou des tirets bas title: Émojis personnalisés + uncategorized: Non catégorisé + unlist: Délisté unlisted: Délisté update_failed_msg: N’a pas pu mettre à jour cet émoji updated_msg: Émoji mis à jour avec succès ! upload: Téléverser dashboard: + authorized_fetch_mode: Mode de récupération autorisé backlog: tâches en attente config: Configuration feature_deletions: Suppressions de comptes @@ -250,10 +269,13 @@ fr: feature_profile_directory: Annuaire des profils feature_registrations: Inscriptions feature_relay: Relais de fédération + feature_spam_check: Anti-spam feature_timeline_preview: Aperçu du fil public features: Fonctionnalités hidden_service: Fédération avec des services cachés open_reports: signalements non résolus + pending_tags: hashtags en attente d’approbation + pending_users: utilisateur·rice·s en attente d’approbation recent_users: Utilisateur·rice·s récent·e·s search: Recherche plein texte single_user_mode: Mode utilisateur·ice unique @@ -265,11 +287,18 @@ fr: week_interactions: interactions cette semaine week_users_active: actif·ve·s cette semaine week_users_new: utilisateur·rice·s cette semaine + whitelist_mode: Mode liste blanche + domain_allows: + add_new: Mettre le domaine sur liste sur blanche + created_msg: Ce domaine a été ajouté à la liste blanche avec succès + destroyed_msg: Le domaine a été supprimé de la liste blanche + undo: Supprimer de la liste blanche domain_blocks: add_new: Ajouter un nouveau bloqueur de domaine created_msg: Le blocage de domaine est désormais activé destroyed_msg: Le blocage de domaine a été désactivé domain: Domaine + edit: Modifier le bloqueur de domaine existing_domain_block_html: Vous avez déjà imposé des limites plus strictes à %{name}, vous devez d’abord le <a href="%{unblock_url}">débloquer</a>. new: create: Créer le blocage @@ -280,6 +309,10 @@ fr: silence: Masqué suspend: Suspendre title: Nouveau blocage de domaine + private_comment: Commentaire privé + private_comment_hint: Commenter sur cette limitation de domaine pour informer les modérateurs internes. + public_comment: Commentaire public + public_comment_hint: Commentaire sur cette limitation de domaine pour le grand public, si la visibilité publique de la liste des limitations de domaine est activée. reject_media: Fichiers média rejetés reject_media_hint: Supprime localement les fichiers média stockés et refuse d’en télécharger ultérieurement. Ne concerne pas les suspensions reject_reports: Rejeter les signalements @@ -299,6 +332,7 @@ fr: title: Annuler le blocage de domaine pour %{domain} undo: Annuler undo: Annuler le bloqueur de domaine + view: Afficher les bloqueurs de domaines email_domain_blocks: add_new: Ajouter created_msg: Le blocage de domaine de courriel est désormais activé @@ -322,6 +356,8 @@ fr: all: Tout limited: Limité title: Modération + private_comment: Commentaire privé + public_comment: Commentaire public title: Fédération total_blocked_by_us: Bloqués par nous total_followed_by_them: Suivi par eux @@ -341,7 +377,7 @@ fr: relays: add_new: Ajouter un nouveau relais delete: Effacer - description_html: Un <strong>relai de fédération</strong> est un serveur intermédiaire qui échange de grandes quantités de pouets entre les serveurs qui publient dessus et ceux qui y sont abonnés. <strong>Il peut aider les petits et moyen serveurs à découvrir du contenu sur le fediverse</strong>, ce qui normalement nécessiterait que les membres locaux suivent des gens inscrits sur des serveurs distants. + description_html: Un <strong>relai de fédération</strong> est un serveur intermédiaire qui échange de grandes quantités de pouets publics entre les serveurs qui publient dessus et ceux qui y sont abonnés. <strong>Il peut aider les petits et moyen serveurs à découvrir du contenu sur le fediverse</strong>, ce qui normalement nécessiterait que les membres locaux suivent des gens inscrits sur des serveurs distants. disable: Désactiver disabled: Désactivé enable: Activé @@ -351,6 +387,7 @@ fr: pending: En attente de l’approbation du relai save_and_enable: Sauvegarder et activer setup: Paramétrer une connexion de relais + signatures_not_enabled: Les relais ne fonctionneront pas correctement lorsque le mode sécurisé ou le mode liste blanche est activé status: Statut title: Relais report_notes: @@ -399,6 +436,16 @@ fr: custom_css: desc_html: Modifier l’apparence avec une CSS chargée sur chaque page title: CSS personnalisé + default_noindex: + desc_html: Affecte tous les utilisateurs qui n'ont pas changé eux-mêmes ce paramètre + title: Opter pour le retrait de l'indexation des moteurs de recherche par défaut + domain_blocks: + all: À tout le monde + disabled: À personne + title: Afficher les bloqueurs de domaines + users: Pour les utilisateurs locaux connectés + domain_blocks_rationale: + title: Voir le raisonnement hero: desc_html: Affichée sur la page d’accueil. Au moins 600x100px recommandé. Lorsqu’elle n’est pas définie, se rabat sur la vignette du serveur title: Image d’en-tête @@ -449,6 +496,9 @@ fr: desc_html: Affichée sur la page des conditions d’utilisation du site<br>Vous pouvez utiliser des balises HTML title: Politique de confidentialité site_title: Nom du serveur + spam_check_enabled: + desc_html: Mastodon peut auto-silencer et signaler des comptes qui envoient des messages répétés non sollicités. Il peut y avoir de faux positifs. + title: Automatisation anti-spam thumbnail: desc_html: Utilisée pour les prévisualisations via OpenGraph et l’API. 1200x630px recommandé title: Vignette du serveur @@ -456,12 +506,19 @@ fr: desc_html: Afficher le fil public sur la page d’accueil title: Prévisualisation du fil global title: Paramètres du serveur + trendable_by_default: + desc_html: Affecte les hashtags qui n'ont pas été précédemment non autorisés + title: Autoriser les hashtags à apparaître dans les tendances sans examen préalable + trends: + desc_html: Afficher publiquement les hashtags approuvés qui sont populaires en ce moment + title: Hashtags populaires statuses: back_to_account: Retour à la page du compte batch: delete: Supprimer nsfw_off: Marquer comme non-sensible nsfw_on: Marquer comme sensible + deleted: Supprimé failed_to_execute: Erreur d’exécution media: title: Médias @@ -469,21 +526,24 @@ fr: no_status_selected: Aucun statut n’a été modifié car aucun n’a été sélectionné title: État du compte with_media: avec médias - subscriptions: - callback_url: URL de rappel - confirmed: Confirmé - expires_in: Expire dans - last_delivery: Dernière livraison - title: WebSub - topic: Sujet tags: - accounts: Comptes - hidden: Masqué - hide: Masquer dans l’annuaire - name: Hashtag + accounts_today: Utilisations uniques aujourd'hui + accounts_week: Utilisation unique cette semaine + breakdown: Répartition de l’utilisation actuelle par source + context: Contexte + directory: Dans le répertoire + in_directory: "%{count} dans le répertoire" + last_active: Dernière activité + most_popular: Plus populaire + most_recent: Plus récent + name: Mot-clé + review: État de traitement + reviewed: Traité title: Hashtags - unhide: Afficher dans l’annuaire - visible: Visible + trending_right_now: Populaire en ce moment + unique_uses_today: "%{count} posts aujourd'hui" + unreviewed: Non traité + updated_msg: Paramètres Hashtag mis à jour avec succès title: Administration warning_presets: add_new: Ajouter un nouveau @@ -499,11 +559,21 @@ fr: body: "%{reporter} a signalé %{target}" body_remote: Quelqu’un de %{domain} a signalé %{target} subject: Nouveau signalement sur %{instance} (#%{id}) + new_trending_tag: + body: 'Le hashtag #%{name} est populaire aujourd’hui, mais il n’a pas été approuvé. Il ne sera pas affiché publiquement à moins que vous l’autorisiez, ou sauvegardiez simplement ce formulaire tel quel pour ne plus jamais en entendre parler.' + subject: Nouveau hashtag en attente de traitement sur %{instance} (#%{name}) + aliases: + add_new: Créer un alias + created_msg: Un nouvel alias a été créé avec succès. Vous pouvez maintenant lancer le déplacement depuis l'ancien compte. + deleted_msg: Suppression réussie de l'alias. Le déménagement de ce compte vers celui-ci ne sera plus possible. + hint_html: Si vous voulez déménager d’un autre compte vers celui-ci, vous pouvez créer ici un alias, qui est nécessaire avant de pouvoir migrer les abonné·e·s de l’ancien compte vers celui-ci. Cette action en soi est <strong>inoffensive et réversible</strong>. <strong>La migration du compte est initiée à partir de l’ancien compte</strong>. + remove: Détacher l'alias appearance: advanced_web_interface: Interface web avancée advanced_web_interface_hint: 'Si vous voulez utiliser toute la largeur de votre écran, l’interface web avancée vous permet de configurer plusieurs colonnes différentes pour voir autant d’informations que vous le souhaitez en même temps : Accueil, notifications, fil public fédéré, un nombre illimité de listes et hashtags.' animations_and_accessibility: Animations et accessibilité confirmation_dialogs: Dialogues de confirmation + discovery: Découverte sensitive_content: Contenu sensible application_mailer: notification_preferences: Modifier les préférences de courriel @@ -524,15 +594,19 @@ fr: apply_for_account: Demander une invitation change_password: Mot de passe checkbox_agreement_html: J’accepte les <a href="%{rules_path}" target="_blank">règles du serveur</a> et les <a href="%{terms_path}" target="_blank">conditions de service</a> - confirm_email: Confirmer mon adresse mail + checkbox_agreement_without_rules_html: J’accepte les <a href="%{terms_path}" target="_blank">conditions d’utilisation</a> delete_account: Supprimer le compte delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action. + description: + prefix_invited_by_user: "@%{name} vous invite à rejoindre ce serveur Mastodon !" + prefix_sign_up: Inscrivez-vous aujourd’hui sur Mastodon ! + suffix: Avec un compte, vous pourrez suivre des gens, publier des statuts et échanger des messages avec les utilisateurs de n'importe quel serveur Mastodon et bien plus ! didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? forgot_password: Mot de passe oublié ? invalid_reset_password_token: Le lien de réinitialisation du mot de passe est invalide ou a expiré. Merci de réessayer. login: Se connecter logout: Se déconnecter - migrate_account: Déplacer vers un compte différent + migrate_account: Déménager vers un compte différent migrate_account_html: Si vous voulez rediriger ce compte vers un autre, vous pouvez le <a href="%{path}">configurer ici</a>. or_log_in_with: Ou authentifiez-vous avec providers: @@ -544,6 +618,16 @@ fr: reset_password: Réinitialiser le mot de passe security: Sécurité set_new_password: Définir le nouveau mot de passe + setup: + email_below_hint_html: Si l’adresse e-mail ci-dessous est incorrecte, vous pouvez la modifier ici et recevoir un nouveau courriel de confirmation. + email_settings_hint_html: L’e-mail de confirmation a été envoyé à %{email}. Si cette adresse e-mail n’est pas correcte, vous pouvez la modifier dans les paramètres du compte. + title: Configuration + status: + account_status: Statut du compte + confirming: En attente de la confirmation par e-mail à compléter. + functional: Votre compte est entièrement opérationnel. + pending: Votre demande est en attente d'examen par notre personnel. Cela peut prendre un certain temps. Vous recevrez un courriel si votre demande est approuvée. + redirecting_to: Votre compte est inactif car il est actuellement redirigé vers %{acct}. trouble_logging_in: Vous avez un problème pour vous connecter ? authorize_follow: already_following: Vous suivez déjà ce compte @@ -556,6 +640,11 @@ fr: return: Afficher le profil de l’utilisateurâ‹…ice web: Retour à l’interface web title: Suivre %{acct} + challenge: + confirm: Continuer + hint_html: "<strong>Astuce :</strong> Nous ne vous demanderons plus votre mot de passe pour la prochaine heure." + invalid_password: Mot de passe invalide + prompt: Confirmez votre mot de passe pour continuer datetime: distance_in_words: about_x_hours: "%{count} h" @@ -571,26 +660,33 @@ fr: x_months: "%{count} mois" x_seconds: "%{count} s" deletes: - bad_password_msg: Bien essayé ! Mot de passe incorrect + challenge_not_passed: Les renseignements que vous avez entrés n'étaient pas exacts confirm_password: Entrez votre mot de passe pour vérifier votre identité - description_html: Cela va supprimer votre compte et le désactiver de manière <strong>permanente et irréversible</strong>. Votre nom d’utilisateurâ‹…ice restera réservé afin d’éviter la confusion. + confirm_username: Entrez votre nom d'utilisateur pour confirmer la procédure proceed: Supprimer compte success_msg: Votre compte a été supprimé avec succès - warning_html: Seule la suppression du contenu depuis ce serveur est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-ligne ainsi que ceux n’étant plus abonnées à vos publications ne mettront pas leur base de données à jour. - warning_title: Disponibilité du contenu disséminé + warning: + before: 'Avant de procéder, veuillez lire attentivement ces notes :' + caches: Le contenu mis en cache par d'autres serveurs peut persister + data_removal: Vos messages et autres données seront définitivement supprimés + email_change_html: Vous pouvez <a href="%{path}">modifier votre adresse courriel</a> sans supprimer votre compte + email_contact_html: S'il n'arrive toujours pas, vous pouvez envoyer un courriel à <a href="mailto:%{email}">%{email}</a> pour de l'aide + email_reconfirmation_html: Si vous ne recevez pas le courriel de confirmation, vous pouvez le <a href="%{path}">demander à nouveau</a> + irreversible: Vous ne pourrez pas restaurer ou réactiver votre compte + more_details_html: Pour plus de détails, voir la <a href="%{terms_path}">politique de confidentialité</a>. + username_available: Votre nom d’utilisateur sera à nouveau disponible + username_unavailable: Votre nom d’utilisateur restera indisponible directories: directory: Annuaire des profils - enabled: Vous êtes actuellement listé dans l’annuaire. - enabled_but_waiting: Vous avez choisi d’être listé dans l’annuaire, mais vous n’avez pas encore le nombre minimum de suiveurs (%{min_followers}) pour y être inscrit. explanation: Découvrir des utilisateurs en se basant sur leurs centres d’intérêt explore_mastodon: Explorer %{title} - how_to_enable: Vous n’êtes pas encore inscrit dans l’annuaire. Vous pouvez vous inscrire ci-dessous. Utilisez des hashtags dans votre texte biographique pour être listé sous des hashtags spécifiques ! - people: - one: "%{count} personne" - other: "%{count} personne" + domain_validator: + invalid_domain: n’est pas un nom de domaine valide errors: + '400': La demande que vous avez soumise est invalide ou mal formée. '403': Vous n’avez pas accès à cette page. '404': La page que vous recherchez n’existe pas. + '406': Cette page n'est pas disponible au format demandé. '410': La page que vous recherchez n’existe plus. '422': content: Vérification de sécurité échouée. Bloquez-vous les cookies ? @@ -599,6 +695,7 @@ fr: '500': content: Nous sommes désolé·e·s, mais quelque chose s’est mal passé de notre côté. title: Cette page n’est pas correcte + '503': La page n'a pas pu être servie en raison d'une défaillance temporaire du serveur. noscript_html: Pour utiliser Mastodon, veuillez activer JavaScript. Sinon, essayez l’une des <a href="%{apps_path}">applications natives</a> pour Mastodon pour votre plate-forme. existing_username_validator: not_found: n’a pas trouvé d’utilisateur·rice local·e avec ce nom @@ -621,7 +718,8 @@ fr: featured_tags: add_new: Ajouter un nouvel hashtag errors: - limit: Vous avez déjà mis en avant le nombre maximum de hashtags + limit: Vous avez déjà recommandé le nombre maximum de hashtags + hint_html: "<strong>Que sont les hashtags vedettes ?</strong> Ils sont affichés avec emphase sur votre flux d'actualités publique et permettent aux gens de parcourir vos messages publics spécifiquement sous ces hashtags. Ils sont un excellent outil pour garder trace des Å“uvres créatives ou des projets à long terme." filters: contexts: home: Accueil @@ -642,10 +740,12 @@ fr: developers: Développeurs more: Davantage… resources: Ressources + trending_now: Tendance en ce moment generic: all: Tous changes_saved_msg: Les modifications ont été enregistrées avec succès ! copy: Copier + no_batch_actions_available: Aucune action par lots disponible sur cette page order_by: Classer par save_changes: Enregistrer les modifications validation_errors: @@ -717,9 +817,34 @@ fr: too_many: Impossible de joindre plus de 4 fichiers migrations: acct: profil@domaine du nouveau compte - currently_redirecting: 'Votre profil va être redirigé vers :' - proceed: Enregistrer - updated_msg: Les paramètres de votre migration de compte ont été mis à jour avec succès ! + cancel: Annuler la redirection + cancel_explanation: Annuler la redirection réactivera votre compte courant, mais ne rapportera pas les abonné·e·s qui ont été déplacé·e·s sur ce compte. + cancelled_msg: Suppression de la redirection réussie. + errors: + already_moved: est le même compte que vous avez déjà déplacé vers + missing_also_known_as: ne référence pas rétroactivement ce compte + move_to_self: ne peut pas être un compte courant + not_found: n'a pas été trouvé + on_cooldown: Vous êtes en refroidissement + followers_count: Abonnés au moment du déplacement + incoming_migrations: Déplacement d'un compte différent + incoming_migrations_html: Pour passer d'un autre compte à celui-ci, vous devez d'abord <a href="%{path}">créer un alias de compte</a>. + moved_msg: Votre compte est maintenant redirigé vers %{acct} et vos abonnés sont déplacés. + not_redirecting: Votre compte n'est pas redirigé vers un autre compte actuellement. + on_cooldown: Vous avez récemment migré votre compte. Cette fonction sera à nouveau disponible dans %{count} jours. + past_migrations: Migrations passées + proceed_with_move: Migrer les abonné·e·s + redirecting_to: Votre compte est redirigé vers %{acct}. + set_redirect: Définir redirection + warning: + backreference_required: Le nouveau compte doit d'abord être configuré pour faire référence à celui-ci + before: 'Avant de procéder, veuillez lire attentivement ces notes :' + cooldown: Après le déménagement, il y a une période de gel pendant laquelle vous ne pourrez plus re-déménager + disabled_account: Votre compte actuel ne sera pas entièrement utilisable par la suite. Cependant, vous aurez accès à l'exportation de données et à la ré-activation. + followers: Cette action va déménager tou·te·s les abonné·e·s du compte actuel vers le nouveau compte + only_redirect_html: Alternativement, vous pouvez <a href="%{path}">seulement appliquer une redirection sur votre profil</a>. + other_data: Aucune autre donnée ne sera déplacée automatiquement + redirect: Le profil de votre compte actuel sera mis à jour avec un avis de redirection et sera exclu des recherches moderation: title: Modération notification_mailer: @@ -816,10 +941,6 @@ fr: reply: proceed: Confirmer la réponse prompt: 'Vous souhaitez répondre à ce pouet :' - remote_unfollow: - error: Erreur - title: Titre - unfollowed: Non-suivi scheduled_statuses: over_daily_limit: Vous avez dépassé la limite de %{limit} pouets planifiés pour ce jour over_total_limit: Vous avez dépassé la limite de %{limit} pouets planifiés @@ -829,7 +950,7 @@ fr: browser: Navigateur browsers: alipay: Alipay - blackberry: Blackberry + blackberry: BlackBerry chrome: Chrome edge: Microsoft Edge electron: Electron @@ -843,7 +964,7 @@ fr: phantom_js: PhantomJS qq: QQ Browser safari: Safari - uc_browser: UCBrowser + uc_browser: UC Browser weibo: Weibo current_session: Session courante description: "%{browser} sur %{platform}" @@ -852,8 +973,8 @@ fr: platforms: adobe_air: Adobe Air android: Android - blackberry: Blackberry - chrome_os: ChromeOS + blackberry: BlackBerry + chrome_os: Chrome OS firefox_os: Firefox OS ios: iOS linux: Linux @@ -868,6 +989,7 @@ fr: settings: account: Compte account_settings: Paramètres du compte + aliases: Alias du compte appearance: Apparence authorized_apps: Applications autorisées back: Retour vers Mastodon @@ -875,7 +997,7 @@ fr: development: Développement edit_profile: Modifier le profil export: Export de données - featured_tags: Hashtags mis en avant + featured_tags: Hashtags recommandés identity_proofs: Preuves d’identité import: Import de données import_and_export: Import et export @@ -885,6 +1007,8 @@ fr: profile: Profil relationships: Abonnements et abonné·e·s two_factor_authentication: Identification à deux facteurs + spam_check: + spam_detected: Ceci est un rapport automatisé. Du spam a été détecté. statuses: attached: description: 'Attaché : %{attached}' @@ -908,6 +1032,9 @@ fr: private: Les statuts non-publics ne peuvent pas être épinglés reblog: Un partage ne peut pas être épinglé poll: + total_people: + one: "%{count} personnes" + other: "%{count} personnes" total_votes: one: "%{count} vote" other: "%{count} votes" @@ -926,6 +1053,8 @@ fr: pinned: Pouet épinglé reblogged: a partagé sensitive_content: Contenu sensible + tags: + does_not_match_previous_name: ne correspond pas au nom précédent terms: body_html: | <h2>Politique de confidentialité</h2> @@ -1005,7 +1134,7 @@ fr: <p>Dans le cas où nous déciderions de changer notre politique de confidentialité, nous posterons les modifications sur cette page.</p> - <p>Ce document est publié sous lincence CC-BY-SA. Il a été mis à jours pour la dernière fois le 7 mars 2018.</p> + <p>Ce document est publié sous licence CC-BY-SA. Il a été mis à jour pour la dernière fois le 7 mars 2018.</p> <p>Originellement adapté de la <a href="https://github.com/discourse/discourse">politique de confidentialité de Discourse</a>.</p> title: "%{instance} Conditions d’utilisation et politique de confidentialité" @@ -1043,7 +1172,9 @@ fr: disable: Lorsque votre compte est gelé, les données de votre compte demeurent intactes, mais vous ne pouvez effectuer aucune action jusqu’à ce qu’il soit débloqué. silence: Lorsque votre compte est limité, seulement les utilisateurs qui vous suivent déjà verront vos pouets sur ce serveur, et vous pourriez être exclu de plusieurs listes publiques. Néanmoins, d’autres utilisateurs peuvent vous suivre manuellement. suspend: Votre compte a été suspendu, et tous vos pouets et vos fichiers multimédia téléversés ont été supprimés irréversiblement de ce serveur, et des serveurs où vous aviez des abonné⋅eâ‹…s. + get_in_touch: Vous pouvez répondre à cette adresse pour entrer en contact avec l’équipe de %{instance}. review_server_policies: Passer en revue les politiques du serveur + statuses: 'Spécialement, pour :' subject: disable: Votre compte %{acct} a été gelé none: Avertissement pour %{acct} diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 79ef993e2edab37d2bbf385f5c1860833a32151c..f325757520fc38133199d5da87b6b33bc3643c91 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1,9 +1,9 @@ --- gl: about: - about_hashtag_html: Estas son mensaxes públicas etiquetadas con <strong>#%{hashtag}</strong>. Pode interactuar con elas si ten unha conta nalgures do fediverso. + about_hashtag_html: Estas son mensaxes públicas etiquetadas con <strong>#%{hashtag}</strong>. Podes interactuar con elas se tes unha conta nalgures do fediverso. about_mastodon_html: Mastodon é unha rede social que se basea en protocolos web abertos e libres, software de código aberto. É descentralizada como o correo electrónico. - about_this: Sobre + about_this: Acerca de active_count_after: activo active_footnote: Usuarias Activas no Mes (UAM) administered_by: 'Administrada por:' @@ -17,13 +17,12 @@ gl: contact_unavailable: N/A discover_users: Descubra usuarias documentation: Documentación - extended_description_html: | - <h3>Un bo lugar para regras</h3> - <p>A descrición extendida aÃnda non se proporcionou.</p> federation_hint_html: Con unha conta en %{instance} poderá seguir a outras persoas en calquera dos servidores Mastodon e incluso máis. - generic_description: "%{domain} é un servidor na rede" get_apps: Probe cunha app móbil hosted_on: Mastodon aloxado en %{domain} + instance_actor_flash: 'Esta conta é un actor virtual utilizado para representar ao servidor e non a unha usuaria individual. UtilÃzase para propósitos de federación e non deberÃa estar bloqueada a menos que queira bloquear a toda a instancia, en tal caso deberÃa utilizar o bloqueo do dominio. + +' learn_more: Coñeza máis privacy_policy: PolÃtica de intimidade see_whats_happening: Mire o que acontece @@ -35,6 +34,14 @@ gl: status_count_before: Que publicaron tagline: Siga as amizades e faga outras novas terms: Termos do servizo + unavailable_content: Contido non dispoñible + unavailable_content_description: + domain: Servidor + reason: 'Razón:' + rejecting_media: Os ficheiros de medios de este servidor non se procesarán e non se mostrarán miniaturas, precisando solicitarse manualmente ao outro servidor. + silenced: As publicacións desde este servidor non se mostrarán en ningún lugar excepto no Inicio se segues ao autor. + suspended: Non poderás seguir a ninguén desde este servidor, e non se procesarán nin se gardarán datos que procedan del, e non se intercambiarán datos. + unavailable_content_html: Normalmente Mastodon permÃteche ver contidos de outros servidores do fediverso e interactuar coas súas usuarias. Estas son as excepcións que se estableceron en este servidor particular. user_count_after: one: usuaria other: usuarias @@ -42,6 +49,8 @@ gl: what_is_mastodon: Qué é Mastodon? accounts: choices_html: 'Eleccións de %{name}:' + endorsements_hint: Desde a interface web pode recomendar xente que segue, e aparecerán aquÃ. + featured_tags_hint: Pode destacar determinadas etiquetas que se mostrarán aquÃ. follow: Seguir followers: one: Seguidora @@ -53,6 +62,7 @@ gl: media: Medios moved_html: "%{name} mudouse a %{new_profile_link}:" network_hidden: A información non está dispoñible + never_active: Nunca nothing_here: Nada por aquÃ! people_followed_by: Personas que segue %{name} people_who_follow: Personas que seguen a %{name} @@ -183,6 +193,7 @@ gl: username: Nome de usuaria warn: Aviso web: Web + whitelisted: Lista branca action_logs: actions: assigned_to_self_report: "%{name} asignou o informe %{target} a ela misma" @@ -218,19 +229,24 @@ gl: deleted_status: "(estado eliminado)" title: Rexistro de auditorÃa custom_emojis: + assign_category: Asignar categorÃa by_domain: Dominio copied_msg: Creouse con éxito unha copia local dos emoji copy: Copiar copy_failed_msg: Non se puido facer copia local de ese emoji + create_new_category: Crear nova categorÃa created_msg: Creou o emoji con satisfactoriamente! delete: Eliminar destroyed_msg: Emojo destruÃdo satisfactoriamente! disable: Deshabilitar + disabled: Desactivado disabled_msg: Deshabilitouse correctamente ese emoji emoji: Emoji enable: Habilitar + enabled: Activado enabled_msg: Habilitouse correctamente ese emoji image_hint: PNG ate 50KB + list: A lista listed: Listado new: title: Engadir novo emoji persoalizado @@ -238,11 +254,14 @@ gl: shortcode: Código corto shortcode_hint: Cando menos 2 caracteres, só caracteres alfanuméricos e subliñados title: Emojis persoalizados + uncategorized: Sen categorizar + unlist: Sacar da lista unlisted: Non listado update_failed_msg: Non se puido actualizar ese emoji updated_msg: Actualizouse correctamente o emoji! upload: Subir dashboard: + authorized_fetch_mode: Modo de obtención autorizado backlog: traballos respaldados config: Axustes feature_deletions: Borrado de contas @@ -250,10 +269,13 @@ gl: feature_profile_directory: Directorio do perfil feature_registrations: Rexistros feature_relay: Repetidores de federación + feature_spam_check: Anti-spam feature_timeline_preview: Vista previa da TL features: CaracterÃsticas hidden_service: Federación con servizos ocultos open_reports: informes abertos + pending_tags: etiquetas agardando revisión + pending_users: usuarias agardando revisión recent_users: Usuarias recentes search: Busca de texto completo single_user_mode: Modo de usuario individual @@ -265,11 +287,18 @@ gl: week_interactions: interaccións en esta semana week_users_active: activas estas semana week_users_new: usuarias esta semana + whitelist_mode: Modo de lista branca + domain_allows: + add_new: Dominio en lista branca + created_msg: Engadeu o dominio a lista branca + destroyed_msg: Quitou o dominio da lista branca + undo: Eliminar da lista branca domain_blocks: add_new: Engadir novo bloqueo de dominio created_msg: Estase a procesar o bloqueo do dominio destroyed_msg: Desfixose a acción de bloqueo de dominio domain: Dominio + edit: Editar bloqueo de dominio existing_domain_block_html: Xa estableceu lÃmites estrictos para %{name}, precisa <a href="%{unblock_url}">desbloqueala</a> primeiro. new: create: Crear bloque @@ -280,6 +309,10 @@ gl: silence: Silenciar suspend: Suspender title: Novo bloqueo de dominio + private_comment: Comentario privado + private_comment_hint: Comentar entre moderadores para uso interno as limitacións de este dominio. + public_comment: Comentario público + public_comment_hint: Comentar sobre as limitacións de este dominio para o interese público, se o aviso da lista de dominios limitados está activado. reject_media: Rexeitar ficheiros de medios reject_media_hint: Eliminar ficheiros de medios almacenados localmente e rexeita descargalos no futuro. Irrelevante para as suspensións reject_reports: Rexeitar informes @@ -299,6 +332,7 @@ gl: title: Desfacer o bloqueo de dominio para %{domain} undo: Desfacer undo: Desfacer bloqueo de dominio + view: Ver dominios bloqueados email_domain_blocks: add_new: Engadir novo created_msg: Engadeuse correctamente o dominio de email a lista negra @@ -322,6 +356,8 @@ gl: all: Todo limited: Limitado title: Moderación + private_comment: Comentario privado + public_comment: Comentario público title: Federación total_blocked_by_us: Bloqueado por nós total_followed_by_them: Seguidas por eles @@ -351,6 +387,7 @@ gl: pending: Agardando polo permiso do repetidor save_and_enable: Gardar e activar setup: Configurar a conexión ao repetidor + signatures_not_enabled: Os repetidores non funcionarán correctamente se o modo seguro ou lista branca están activados status: Estado title: Repetidores report_notes: @@ -399,6 +436,16 @@ gl: custom_css: desc_html: Modificar o aspecto con CSS cargado en cada páxina title: CSS persoalizado + default_noindex: + desc_html: Aféctalle a todas as usuarias que non cambiaron os axustes elas mesmas + title: Por omisión exclúe as usuarias do indexado por servidores de busca + domain_blocks: + all: Para todas + disabled: Para ninguén + title: Mostrar dominios bloqueados + users: Para usuarias locais conectadas + domain_blocks_rationale: + title: Mostrar razón hero: desc_html: Mostrado na portada. Recoméndase 600x100px como mÃnimo. Se non se establece, mostrará a imaxe por omisión do servidor title: Imáxe Heróe @@ -449,6 +496,9 @@ gl: desc_html: Pode escribir a súa propia polÃtica de intimidade, termos de servizo ou aclaracións legais. Pode utilizar etiquetas HTML title: Termos de servizo persoalizados site_title: Nome do servidor + spam_check_enabled: + desc_html: Mastodon pode silenciar e informar automáticamente sobre contas baseándose en medidas como detectar contas que envÃan mensaxes non solicitadas de xeito repetido. PoderÃa haber falsos positivos. + title: Anti-spam thumbnail: desc_html: Utilizado para vistas previsas vÃa OpenGraph e API. Recoméndase 1200x630px title: Icona do servidor @@ -456,12 +506,19 @@ gl: desc_html: Mostrar liña de tempo pública na páxina de inicio title: vista previa da liña temporal title: Axustes do sitio + trendable_by_default: + desc_html: Afecta a etiquetas que non foron rexeitadas previamente + title: Permite as etiquetas ser tendencia sen revisión previa + trends: + desc_html: Mostrar públicamente etiquetas previamente revisadas que actualmente son tendencia + title: Etiquetas Tendencia statuses: back_to_account: Voltar a páxina da conta batch: delete: Eliminar nsfw_off: Marcar como non sensible nsfw_on: Marcar como sensible + deleted: Eliminado failed_to_execute: Fallou a execución media: title: Medios @@ -469,21 +526,24 @@ gl: no_status_selected: Non se cambiou ningún estado xa que ningún foi seleccionado title: Estados da conta with_media: con medios - subscriptions: - callback_url: URL de chamada - confirmed: Confirmado - expires_in: Caduca en - last_delivery: Última entrega - title: WebSub - topic: Asunto tags: - accounts: Contas - hidden: Ocultas - hide: Ocultar do directorio + accounts_today: Usos únicos hoxe + accounts_week: Usos únicos esta semana + breakdown: Consumo do uso diario por fonte + context: Contexto + directory: No directorio + in_directory: "%{count} no directorio" + last_active: Úlimo activo + most_popular: Máis popular + most_recent: Máis recente name: Etiqueta + review: Estado de revisión + reviewed: Revisado title: Etiquetas - unhide: Mostrar en directorio - visible: Visible + trending_right_now: Agora como Tendencia + unique_uses_today: "%{count} publicando hoxe" + unreviewed: Sen revisar + updated_msg: Actualizaronse os axustes das etiquetas title: Administración warning_presets: add_new: Engadir novo @@ -499,11 +559,21 @@ gl: body: "%{reporter} informou sobre %{target}" body_remote: Alguén desde %{domain} informou sobre %{target} subject: Novo informe sobre %{instance} (#%{id}) + new_trending_tag: + body: 'A etiqueta #%{name} é tendencia hoxe, pero non foi previamente revisada. Non se mostrará publicamente a menos que vostede o permita, ou garde o formulario para facer que non se lle consulte de novo.' + subject: Unha nova etiqueta que revisar en %{instance} (#%{name}) + aliases: + add_new: Crear alcume + created_msg: Creou un novo alcume correctamente. Pode iniciar o movemento desde a conta antiga. + deleted_msg: Eliminou correctamente o alias. Xa non será posible mover desde esa conta a esta. + hint_html: Se quere mudarse desde outra conta a esta nova, aquà pode crear un alcume, que é requerido antes de poder proceder a mover os seguidores da conta antiga a esta nova. Esta acción por si mesma é <strong>inocua e reversible</strong>. <strong>A migración da conta inÃciase desde a conta antiga</strong>. + remove: Desligar alcume appearance: advanced_web_interface: Interface web avanzada advanced_web_interface_hint: Se quere utilizar todo o ancho da súa pantalla, a interface web avanzada permÃtelle configurar diferentes columnas para ver tanta información como desexe. Inicio, notificacións, liña temporal federada, calquera número de listas e etiquetas. animations_and_accessibility: Animacións e accesibilidade confirmation_dialogs: Diálogos de confirmación + discovery: Descubrir sensitive_content: Contido sensible application_mailer: notification_preferences: Cambiar os axustes de correo-e @@ -524,9 +594,13 @@ gl: apply_for_account: Solicite un convite change_password: Contrasinal checkbox_agreement_html: Acepto as <a href="%{rules_path}" target="_blank">regras do servidor</a> e os <a href="%{terms_path}" target="_blank">termos do servizo</a> - confirm_email: Confirmar correo-e + checkbox_agreement_without_rules_html: Acepto os <a href="%{terms_path}" target="_blank">termos do servizo</a> delete_account: Eliminar conta delete_account_html: Se desexa eliminar a súa conta, pode <a href="%{path}">facelo aquÃ</a>. Pediráselle confirmación. + description: + prefix_invited_by_user: "@%{name} convÃdate a que te unas a este servidor Mastodon!" + prefix_sign_up: RexÃstrate agora en Mastodon! + suffix: Ao abrir unha conta, poderás seguir a xente, actualizacións das publicacións e intercambiar mensaxes coas usuarias de calquera servidor de Mastodon e moito máis! didnt_get_confirmation: Non recibeu as instruccións de confirmación? forgot_password: Esqueceu o contrasinal? invalid_reset_password_token: O testemuño para restablecer o contrasinal non é válido ou caducou. Por favor solicite un novo. @@ -544,6 +618,16 @@ gl: reset_password: Restablecer contrasinal security: Seguridade set_new_password: Establecer novo contrasinal + setup: + email_below_hint_html: Se o enderezo inferior non é correcto, pode cambialo aquà e recibir un correo de confirmación. + email_settings_hint_html: Enviouse un correo de confirmación a %{email}. Se o enderezo non é correcto pode cambialo nos axustes da conta. + title: Axustes + status: + account_status: Estado da conta + confirming: Agardando a confirmación do correo enviado. + functional: A súa conta está totalmente operativa. + pending: A súa aplicación está pendente de revisión. PoderÃanos levar algún tempo. Recibirá un correo se a aplicación está aprobada. + redirecting_to: A túa conta está inactiva porque está redirixida a %{acct}. trouble_logging_in: Problemas para conectar? authorize_follow: already_following: Xa está a seguir esta conta @@ -556,6 +640,11 @@ gl: return: Mostrar o perfil da usuaria web: Ir a web title: Seguir %{acct} + challenge: + confirm: Continuar + hint_html: "<strong>Nota:</strong> Non che pediremos o contrasinal na seguinte hora." + invalid_password: Contrasinal incorrecto + prompt: Confirma o contrasinal para continuar datetime: distance_in_words: about_x_hours: "%{count}h" @@ -571,26 +660,33 @@ gl: x_months: "%{count}mes" x_seconds: "%{count}s" deletes: - bad_password_msg: Bo intento, hackers! Contrasinal incorrecto + challenge_not_passed: A información introducida non é correcta confirm_password: Introduza o seu contrasinal para verificar a súa identidade - description_html: Esto eliminará de xeito <strong>permanente e irreversible</strong> o contido da súa conta e será desactivada. O seu nome de usuaria permanecerá reservado para evitar futuras confusións de identidades. + confirm_username: Introduce o nome de usuaria para confirmar o procedemento proceed: Eliminar conta success_msg: A súa conta eliminouse correctamente - warning_html: Só se garantiza a eliminación de contido de este servidor. O contido que foi compartido con outras instancias é probable que deixe rastros. O servidores fora de liña e servidores que se desuscribiron das súas actualizacións non actualizarán as súas bases de datos. - warning_title: Dispoñibilidade do contido espallado + warning: + before: 'Antes de seguir, por favor lé estas notas con atención:' + caches: O contido almacenado en outros servidores poderÃa persistir + data_removal: As túas publicacións e outros datos serán permanentemente borrados + email_change_html: Podes <a href="%{path}">cambiar o enderezo de correo</a> sen eliminar a conta + email_contact_html: Se non o recibes, podes escribir a <a href="mailto:%{email}">%{email}</a> pedindo axuda + email_reconfirmation_html: Se non recibes o correo de confirmación, podes <a href="%{path}">solicitalo de novo</a> + irreversible: Non poderás restaurar ou reactivar a conta + more_details_html: Para máis detalles, mira a <a href="%{terms_path}">polÃtica de intimidade</a>. + username_available: O nome de usuario estará dispoñible novamente + username_unavailable: O nome de usuario non estará dispoñible directories: directory: Directorio de perfil - enabled: Vostede está actualmente na lista do directorio. - enabled_but_waiting: Vostede optou por ser incluÃda no directorio, mais por agora non ten o número mÃnimo de seguidoras (%{min_followers}) para aparecer. explanation: Descubra usuarias según o seu interese explore_mastodon: Explorar %{title} - how_to_enable: Actualmente non solicitou ser incluÃda no directorio, pode facelo abaixo. Utilice etiquetas no texto de biografÃa para ser incluÃda baixo etiquetas especÃficas! - people: - one: "%{count} persoa" - other: "%{count} persoas" + domain_validator: + invalid_domain: non é un nome de dominio válido errors: + '400': A solicitude que enviou non é válida ou ten formato incorrecto. '403': Non ten permiso para ver esta páxina. '404': A páxina que está a buscar non está aquÃ. + '406': Esta páxina non está dispoñible no formato solicitado. '410': A páxina que estaba a buscar xa non existe. '422': content: Fallou a verificación de seguridade. Está bloqueando as cookies? @@ -599,6 +695,7 @@ gl: '500': content: SentÃmolo, pero algo do noso lado falloou. title: Esta páxina non é correcta + '503': A páxina non se puido servir debido a un fallo temporal no servidor. noscript_html: Para utilizar a aplicación web de Mastodon debe habilitar JavaScript. De xeito alternativo, intente unha das <a href="%{apps_path}">apps nativas</a> para Mastodon da súa plataforma. existing_username_validator: not_found: non se atopou unha usuaria local con ese alcume @@ -622,6 +719,7 @@ gl: add_new: Engadir novo errors: limit: Xa acadou o número máximo de etiquetas + hint_html: "<strong>¿Qué son as etiquetas destacadas?</strong> Móstranse destacadas no seu perfil público e permÃtenlle a outras persoas ver os seus toots públicos nos que as utilizou. Son unha ferramenta moi útil para facer seguimento de traballos creativos e proxectos a longo prazo." filters: contexts: home: Liña temporal inicial @@ -642,10 +740,12 @@ gl: developers: Desenvolvedoras more: Máis… resources: Recursos + trending_now: Tendencia agora generic: all: Todo changes_saved_msg: Cambios gardados correctamente!! copy: Copiar + no_batch_actions_available: Non hai accións en pila dispoñibles nesta páxina order_by: Ordenar por save_changes: Gardar cambios validation_errors: @@ -717,9 +817,34 @@ gl: too_many: Non pode anexar máis de 4 ficheiros migrations: acct: nomeusuaria@dominio da nova conta - currently_redirecting: 'O seu perfil está listo para redirixir a:' - proceed: Gardar - updated_msg: O axuste de migración da conta actualizouse correctamente! + cancel: Cancelar a redirección + cancel_explanation: Ao cancelar a redirección reactivarás a conta actual, pero non poderás traer de volta os seguidores que se moveron a esa conta. + cancelled_msg: Cancelouse a redirección. + errors: + already_moved: é a mesma conta a que xa te moveches + missing_also_known_as: non está referenciando hacia esta conta + move_to_self: non pode ser a conta actual + not_found: non se atopou + on_cooldown: Estas no perÃodo de calma + followers_count: Seguidoras no momento da migración + incoming_migrations: Movendo desde unha conta diferente + incoming_migrations_html: Para migrar doutra conta cara esta, primeiro debes <a href="%{path}">crear un alias da conta</a>. + moved_msg: A túa conta está redirixindo agora a %{acct} e os teus seguidores movéronse alÃ. + not_redirecting: Neste momento a túa conta non está redirixindo cara a ningunha outra. + on_cooldown: Migraches recentemente a conta. Esta función estará dispoñible de novo en %{count} dÃas. + past_migrations: Migracións pasadas + proceed_with_move: Mover seguidoras + redirecting_to: A conta está redirixindo cara a %{acct}. + set_redirect: Establecer redirección + warning: + backreference_required: Tes que configurar primeiro a nova conta para referenciar hacia esta + before: 'Antes de seguir, por favor lé estas notas con atención:' + cooldown: Tras a migración existe un perÃodo de calma durante o cal non poderás voltar a migrar de novo + disabled_account: Tras o cambio a túa conta actual non será totalmente usable, pero terás acceso a exportar os datos e tamén a reactivación. + followers: Esta acción moverá todas as túas seguidoras desde a conta actual a nova conta + only_redirect_html: De xeito alternativo, podes <a href="%{path}">simplemente por unha redirección no perfil</a>. + other_data: Non se moverán outros datos de xeito automático + redirect: O perfil da túa conta actualizarase cun aviso de redirección e será excluÃdo das buscas moderation: title: Moderación notification_mailer: @@ -816,10 +941,6 @@ gl: reply: proceed: Respostar prompt: 'Vostede quere respostar a este toot:' - remote_unfollow: - error: Fallo - title: TÃtulo - unfollowed: Deixou de seguir scheduled_statuses: over_daily_limit: Excedeu o lÃmite de %{limit} toots programados para ese dÃa over_total_limit: Excedeu o lÃmite de %{limit} toots programados @@ -868,6 +989,7 @@ gl: settings: account: Conta account_settings: Axustes da conta + aliases: Alcumes da conta appearance: Aparencia authorized_apps: Apps autorizadas back: Voltar a Mastodon @@ -884,7 +1006,7 @@ gl: preferences: Preferencias profile: Perfil relationships: Seguindo e seguidoras - two_factor_authentication: Validar Doble Factor + two_factor_authentication: Validar Dobre Factor statuses: attached: description: 'Axenado: %{attached}' @@ -908,6 +1030,9 @@ gl: private: As mensaxes non-públicas non poden ser fixadas reblog: Non se poden fixar as mensaxes promovidas poll: + total_people: + one: "%{count} persoa" + other: "%{count} persoas" total_votes: one: "%{count} voto" other: "%{count} votos" @@ -926,6 +1051,8 @@ gl: pinned: Mensaxe fixada reblogged: promovida sensitive_content: Contido sensible + tags: + does_not_match_previous_name: non concorda co nome anterior terms: body_html: | <h2>Intimidade</h2> @@ -1019,11 +1146,11 @@ gl: month: "%b %Y" two_factor_authentication: code_hint: Introducir o código xerado polo seu aplicativo de autenticación para confirmar - description_html: Si habilita a <strong>autenticación de doble factor</strong>, a conexión pediralle estar en posesión do seu teléfono, que xerará testemuños para poder entrar. + description_html: Se activa a <strong>autenticación de dobre factor</strong>, a conexión pediralle estar en posesión do seu teléfono, que creará testemuños para poder entrar. disable: Deshabilitar enable: Habilitar - enabled: A autenticación de doble-factor está habilitada - enabled_success: Habilitouse con éxito a autenticación de doble-factor + enabled: A autenticación de dobre-factor está activada + enabled_success: Activouse con éxito a autenticación de dobre-factor generate_recovery_codes: Xerar códigos de recuperación instructions_html: "<strong>Escanee este código QR en Google Authenticator ou aplicativo TOTP similar no seu teléfono</strong>. Desde agora, este aplicativo xerará testemuños que vostede deberá introducir ao conectarse." lost_recovery_codes: Os códigos de recuperación permÃtenlle recuperar o acceso a súa conta si perde o teléfono. Si perde os códigos de recuperación, pode restauralos aquÃ. Os seus códigos de recuperación anteriores serán invalidados. @@ -1043,7 +1170,9 @@ gl: disable: Cando a súa conta está conxelada, os datos permanecen intactos, pero non pode levar a fin accións ate que se desbloquea. silence: Mentras a conta está limitada, só a xente que actualmente a segue verá os seus toots en este servidor, e vostede poderÃa estar excluÃda de varias listas públicas. Porén, outras persoas poderÃana seguila de xeito manual. suspend: A súa conta foi suspendida, e todos os seus toots e medios subidos foron eliminados de este servidor de xeito irreversible, e dos servidores onde tivese seguidoras. + get_in_touch: Pode responder a este correo para contactar coa administración de %{instance}. review_server_policies: Revisar polÃticas do servidor + statuses: 'En concreto, para:' subject: disable: A súa conta %{acct} foi conxelada none: Aviso para %{acct} @@ -1074,7 +1203,7 @@ gl: users: follow_limit_reached: Non pode seguir a máis de %{limit} persoas invalid_email: O enderezo de correo non é válido - invalid_otp_token: Código de doble-factor non válido + invalid_otp_token: O código do segundo factor non é válido otp_lost_help_html: Si perde o acceso a ambos, pode contactar con %{email} seamless_external_login: Está conectado a través de un servizo externo, polo que os axustes de contrasinal e correo-e non están dispoñibles. signed_in_as: 'Rexistrada como:' diff --git a/config/locales/he.yml b/config/locales/he.yml index 5e50f738df81679c1b18cf72ef4d8e771cc6f593..50db571dccd45726875bb077bd7b1897ad301866 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -9,10 +9,6 @@ he: contact_missing: ×œ×œ× ×”×’×“×¨×” contact_unavailable: ×œ× ×¨×œ×•×•× ×˜×™/חסר documentation: תיעוד - extended_description_html: | - <h3>×ž×§×•× ×˜×•×‘ לכללי×</h3> - <p>התי×ור המורחב ×˜×¨× ×”×•×’×“×¨.</p> - generic_description: "%{domain} ×”×•× ×©×¨×ª ×חד בתוך הרשת" hosted_on: מסטודון שיושב בכתובת %{domain} learn_more: מידע × ×•×¡×£ source_code: קוד מקור @@ -177,13 +173,6 @@ he: title: תי×ור ×תר מורחב site_title: כותרת ×”×תר title: הגדרות ×תר - subscriptions: - callback_url: קישורית Callback - confirmed: מ×ושר - expires_in: פג תוקף ב- - last_delivery: משלוח ×חרון - title: ×ž× ×•×™ WebSub - topic: × ×•×©× title: × ×™×”×•×œ application_mailer: settings: '×©×™× ×•×™ הגדרות דו×"ל: %{link}' @@ -219,14 +208,17 @@ he: x_months: "%{count} חודשי×" x_seconds: "%{count} ×©× ×™×•×ª" errors: + '400': The request you submitted was invalid or malformed. '403': חסרות לך הרש×ות לצפיה בעמוד ×–×”. '404': הדף המבוקש ×œ× ×§×™×™×. + '406': This page is not available in the requested format. '410': הדף המבוקש כבר ×œ× ×§×™×™×. '422': content: בדיקת ×בטחה × ×›×©×œ×”. החסמת עוגיותיך ×ž×¤× ×™× ×•? title: בדיקת בטיחות × ×›×©×œ×” '429': ×”×•×—× ×§ '500': + '503': The page could not be served due to a temporary server failure. exports: blocks: רשימת חסימות follows: רשימת × ×¢×§×‘×™× diff --git a/config/locales/hr.yml b/config/locales/hr.yml index a4fe6205538722a87471e3240a28fc58bdce3afe..67d83525b3d80ef26994a78431f1c9e71a13c421 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -44,12 +44,15 @@ hr: x_months: "%{count}mj" x_seconds: "%{count}sek" errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. exports: blocks: Blokirao si follows: SlijediÅ¡ diff --git a/config/locales/hu.yml b/config/locales/hu.yml index d771b96832fd3df682c49e2aaf33eeb958a5e4e3..af7048ebb72ec5c93e0fd5d133fb15c3055dbdce 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -10,20 +10,19 @@ hu: api: API apps: Mobil appok apps_platforms: Használd a Mastodont iOS-rÅ‘l, Androidról vagy más platformról - browse_directory: Böngészd a profil adatbázist és szűrj érdeklÅ‘dési kör szerint + browse_directory: Böngészd a profilokat és szűrj érdeklÅ‘dési körre browse_public_posts: Nézz bele a Mastodon élÅ‘ adatfolyamába contact: Kapcsolat contact_missing: Nincs megadva contact_unavailable: N/A discover_users: Találj meg másokat documentation: Dokumentáció - extended_description_html: | - <h3>Ez itt a szabályzat helye</h3> - <p>Még nem állÃtottál be bÅ‘vebb leÃrást.</p> federation_hint_html: Egy %{instance} fiókkal bármely más Mastodon szerveren vagy a föderációban lévÅ‘ felhasználót követni tudsz. - generic_description: "%{domain} csak egy a számtalan szerver közül a föderációban" get_apps: Próbálj ki egy mobil appot hosted_on: "%{domain} Mastodon szerver" + instance_actor_flash: 'Ez a fiók egy virtuális szereplÅ‘, mely magát a szervert reprezentálja, nem egy felhasználót. Ez a föderáció támogatására készült, ezért nem szabad blokkolni, hacsak egy teljes szervert nem akarsz kitiltani, amire persze a domain blokkolása jobb megoldás. + +' learn_more: Tudj meg többet privacy_policy: Adatvédelmi szabályzat see_whats_happening: Nézd, mi történik @@ -35,6 +34,14 @@ hu: status_count_before: eddig tagline: Kövess barátokat és találj újakat terms: Felhasználási feltételek + unavailable_content: A tartalom nem elérhetÅ‘ + unavailable_content_description: + domain: Szerver + reason: 'Indok:' + rejecting_media: A szerverrÅ‘l származó médiafájlok nem kerülnek feldolgozásra, és nem jelennek meg miniatűrök, amelyek kézi átkattintást igényelnek a másik szerverre. + silenced: A szerver hozzászólásai csak a saját hÃrvonalon jelennek meg, ha követik a szerzÅ‘t. + suspended: Nem fogsz tudni követni senkit ebbÅ‘l a szerverbÅ‘l, és nem kerül feldolgozásra vagy tárolásra a tÅ‘le származó adat, és nincs adatcsere. + unavailable_content_html: A Mastodon általában mindenféle tartalomcserét és interakciót lehetÅ‘vé tesz bármelyik szerverrel a fediverzumban. Ezek azok a kivételek, melyek a mi szerverünkön érvényben vannak. user_count_after: one: felhasználónk other: felhasználónk @@ -42,6 +49,8 @@ hu: what_is_mastodon: Mi a Mastodon? accounts: choices_html: "%{name} választásai:" + endorsements_hint: A webes felületen jóváhagyhatod a követett embereket, és itt jelennek meg. + featured_tags_hint: Szerepeltethetsz bizonyos hashtageket, melyek itt jelennek majd meg. follow: Követés followers: one: KövetÅ‘ @@ -53,6 +62,7 @@ hu: media: Média moved_html: "%{name} ide költözött: %{new_profile_link}" network_hidden: Ez az információ nem elérhetÅ‘ + never_active: Soha nothing_here: Nincs itt semmi! people_followed_by: "%{name} követettjei" people_who_follow: "%{name} követÅ‘i" @@ -183,6 +193,7 @@ hu: username: Felhasználónév warn: Figyelmeztetés web: Web + whitelisted: EngedélyezÅ‘-listán action_logs: actions: assigned_to_self_report: "%{name} a %{target} bejelentést magához rendelte" @@ -218,19 +229,24 @@ hu: deleted_status: "(törölt tülk)" title: Audit napló custom_emojis: + assign_category: Kategóriák by_domain: Domain copied_msg: Sikeresen létrehoztuk az emoji helyi másolatát copy: Másolás copy_failed_msg: Emoji helyi másolatának létrehozása sikertelen + create_new_category: Új kategória létrehozása created_msg: Emoji létrehozva! delete: Törlés destroyed_msg: Emoji törlése sikeres! disable: Letiltás + disabled: Letiltva disabled_msg: Emoji letiltva emoji: Emoji enable: Engedélyezés + enabled: Engedélyezve enabled_msg: Emoji engedélyezve image_hint: PNG (maximális méret 50KB) + list: Listázás listed: Listázva new: title: Új egyedi emoji hozzáadása @@ -238,11 +254,14 @@ hu: shortcode: RövidÃtés shortcode_hint: Legalább két karakter, csak betűk, számok és alsóvonás title: Egyedi emojik + uncategorized: Nem kategorizált + unlist: Elrejtés a listáról unlisted: Nincs listázva update_failed_msg: Nem sikerült frissÃteni az emojit updated_msg: Emoji sikeresen frissÃtve! upload: Feltöltés dashboard: + authorized_fetch_mode: ÃttöltÅ‘ üzemmód engedélyezve backlog: hátralévÅ‘ feladatok config: BeállÃtások feature_deletions: Fióktörlések @@ -250,10 +269,13 @@ hu: feature_profile_directory: Profil adatbázis feature_registrations: Regisztráció feature_relay: Föderációs relé + feature_spam_check: Anti-spam feature_timeline_preview: IdÅ‘vonal betekintÅ‘ features: Funkciók hidden_service: Föderáció rejtett szolgáltatásokkal open_reports: nyitott bejelentések + pending_tags: hashtag engedélyezésre vár + pending_users: felhasználó engedélyezésre vár recent_users: Legutóbbi felhasználók search: Keresés teljes szövegben single_user_mode: Egyfelhasználós mód @@ -265,11 +287,18 @@ hu: week_interactions: interakció ezen a héten week_users_active: aktÃv ezen a héten week_users_new: felhasználó ezen a héten + whitelist_mode: EngedélyezÅ‘-lista mód + domain_allows: + add_new: Domain engedélyezése + created_msg: A domain rákerült az engedélyezÅ‘-listára + destroyed_msg: A domain lekerült az engedélyezÅ‘-listáról + undo: EltávolÃtás az engedélyezÅ‘-listáról domain_blocks: add_new: Új tiltott domain hozzáadása created_msg: A domain-tiltás feldolgozása folyamatban destroyed_msg: A domain tiltása feloldva domain: Domain + edit: Domain tiltás szerkesztése existing_domain_block_html: A %{name} domainen már szorosabb korlátokat állÃtottál be, elÅ‘ször <a href="%{unblock_url}">oldd fel a tiltást</a>. new: create: Tiltás létrehozása @@ -280,6 +309,10 @@ hu: silence: NémÃtás suspend: Felfüggesztés title: Új domain tiltása + private_comment: Privát megjegyzés + private_comment_hint: Megjegyzés domain tiltásával kapcsolatban belsÅ‘ használatra, a többi moderátor részére. + public_comment: Nyilvános megjegyzés + public_comment_hint: Megjegyzés domain tiltásával kapcsolatban a nyilvánosság számára, ha a domainek tiltólistája egyébként látható. reject_media: Médiafájlok elutasÃtása reject_media_hint: EltávolÃtja a helyben tárolt médiafájlokat és a továbbiakban letiltja az új médiafájlok letöltését. Felfüggesztett fiókok esetében irreleváns opció reject_reports: Bejelentések elutasÃtása @@ -299,6 +332,7 @@ hu: title: "%{domain} domain tiltásának feloldása" undo: Visszavonás undo: Domain tiltásának visszavonása + view: Domain tiltásának megtekintése email_domain_blocks: add_new: Új hozzáadása created_msg: E-mail domain sikeresen hozzáadva a feketelistához @@ -322,6 +356,8 @@ hu: all: Mind limited: Korlátozott title: Moderáció + private_comment: Privát megjegyzés + public_comment: Nyilvános megjegyzés title: Föderáció total_blocked_by_us: Ãltalunk letiltott total_followed_by_them: Ãltaluk követett @@ -351,6 +387,7 @@ hu: pending: Várakozás a relé jóváhagyására save_and_enable: Mentés és engedélyezés setup: Relé kapcsolat felállÃtása + signatures_not_enabled: A relék nem fognak jól működni, amÃg a biztonságos mód vagy az engedélyezÅ‘ listás mód aktÃv status: Ãllapot title: Relék report_notes: @@ -399,6 +436,16 @@ hu: custom_css: desc_html: Változtasd meg a kinézetet ebben a CSS-ben, mely minden oldalon be fog töltÅ‘dni title: Egyedi CSS + default_noindex: + desc_html: Olyan felhasználókat érinti, akik nem módosÃtották ezt a beállÃtást + title: Alapértelmezésként ne indexeljék a keresÅ‘k a felhasználóinkat + domain_blocks: + all: Mindenkinek + disabled: Senkinek + title: Domain tiltások megjelenitése + users: Bejelentkezett helyi felhasználóknak + domain_blocks_rationale: + title: Mutasd meg az indokolást hero: desc_html: A kezdÅ‘oldalon látszik. Legalább 600x100px méret javasolt. Ha nincs beállÃtva, a szerver bélyegképet használjuk title: HÅ‘si kép @@ -449,6 +496,9 @@ hu: desc_html: MegÃrhatod saját adatkezelési szabályzatodat, felhasználási feltételeidet vagy más hasonló jellegű dokumentumodat. HTML-tageket is használhatsz title: Egyedi felhasználási feltételek site_title: A szerver neve + spam_check_enabled: + desc_html: A Mastodon automatikusan elnémÃthatja és bejelentheti azokat a fiókokat, akik rendszeresen kéretlen üzeneteket küldenek. Persze lehetnek tévedések is. + title: Automatikus anti-spam thumbnail: desc_html: OpenGraph-os és API-s elÅ‘nézetekben használjuk. Ajánlott mérete 1200x630 pixel title: A szerver bélyegképe @@ -456,12 +506,16 @@ hu: desc_html: Nyilvános idÅ‘vonal megjelenÃtése a fÅ‘oldalon title: IdÅ‘vonal elÅ‘nézete title: Webhely beállÃtásai + trends: + desc_html: ElÅ‘zetesen engedélyezett és most trendi hashtagek nyilvános mutatása + title: Trendi hashtagek statuses: back_to_account: Vissza a fiók oldalára batch: delete: Törlés nsfw_off: SzenzitÃv megjelölés törlése nsfw_on: Megjelölés szenzitÃv tartalomként + deleted: Törölve failed_to_execute: Végrehajtás sikertelen media: title: Média @@ -469,21 +523,24 @@ hu: no_status_selected: Nem változtattunk meg semmit, mert semmi sem volt kiválasztva title: Felhasználó tülkjei with_media: Médiafájlokkal - subscriptions: - callback_url: Callback URL - confirmed: MegerÅ‘sÃtve - expires_in: Elévül - last_delivery: Utolsó kézbesÃtés - title: WebSub - topic: Téma tags: - accounts: Fiókok - hidden: Rejtett - hide: Ne jelenjen meg a profilok adatbázisában - name: Hashtag + accounts_today: Egyedi használat a mai napon + accounts_week: Egyedi használat ezen a héten + breakdown: Mai használat bontása forrás szerint + context: Környezet + directory: Katalógusban + in_directory: "%{count} a katalógusban" + last_active: Utoljára aktÃv + most_popular: Legnépszerűbb + most_recent: Legutóbbi + name: CÃmke + review: Engedélyezés állapota + reviewed: Engedélyezett title: Hashtagek - unhide: Jelenjen meg a profilok adatbázisában - visible: Látható + trending_right_now: Most trendi + unique_uses_today: "%{count} mai tülkölés" + unreviewed: Még nem engedélyezett + updated_msg: A hashtag beállÃtásokat sikeresen frissÃtettük title: Karbantartás warning_presets: add_new: Új hozzáadása @@ -499,11 +556,21 @@ hu: body: "%{reporter} jelentette: %{target}" body_remote: Valaki a %{domain} domainrÅ‘l jelentette %{target} subject: 'Új jelentés az alábbi szerveren: %{instance} (#%{id})' + new_trending_tag: + body: 'A #%{name} hashtag trendi a mai napon, de még nem engedélyeztük eddig. Nem mutatjuk meg nyilvánosan, hacsak nem engedélyezed. Csak simán mentsd az űrlapot, ha soha többé nem akarsz errÅ‘l a hashtagrÅ‘l hallani.' + subject: Új hashtag (#%{name}) engedélyezésre vár a %{instance} szerveren + aliases: + add_new: Alias készÃtése + created_msg: Elkészült az új aliasod. Most már elkezdheted a költöztetést a régi fiókból. + deleted_msg: Sikeresen eltávolÃtottad az aliast. A fiókról erre a fiókra való áttérés már nem lehetséges. + hint_html: Ha másik fiókról kÃvánsz átlépni erre a fiókra, itt létrehozhatsz egy aliast, amelyre szükség van, mielÅ‘tt folytathatod a követÅ‘k áthelyezését a régi fiókból erre. Ez az áthelyezés önmagában <strong>ártalmatlan és visszafordÃtható</strong> folyamat. <strong>A fiók áttelepÃtését a régi fiókból indul el. </strong> + remove: Alias szétkapcsolása appearance: advanced_web_interface: Haladó webes felület advanced_web_interface_hint: 'Ha szeretnéd, a teljes képernyÅ‘szélességet felhasználhatod. A haladó webes felülettel különbözÅ‘ oszlopokat állÃthatsz be, hogy egyszerre annyi infót láthass, amennyit csak akarsz: Saját idÅ‘vonal, értesÃtések, föderációs idÅ‘vonal, bármennyi lista vagy hashtag.' animations_and_accessibility: Animáció és akadálymentesÃtés confirmation_dialogs: MegerÅ‘sÃtÅ‘ párbeszédablakok + discovery: Felfedezés sensitive_content: SzenzitÃv tartalom application_mailer: notification_preferences: E-mail beállÃtások módosÃtása @@ -524,9 +591,13 @@ hu: apply_for_account: MeghÃvó kérése change_password: Jelszó checkbox_agreement_html: Egyetértek a <a href="%{rules_path}" target="_blank">szerver szabályaival</a> és a <a href="%{terms_path}" target="_blank">felhasználási feltételekkel</a> - confirm_email: E-mail megerÅ‘sÃtése + checkbox_agreement_without_rules_html: Egyetértek a <a href="%{terms_path}" target="_blank">felhasználási feltételekkel</a> delete_account: Felhasználói fiók törlése delete_account_html: Felhasználói fiókod törléséhez <a href="%{path}">kattints ide</a>. A rendszer újbóli megerÅ‘sÃtést fog kérni. + description: + prefix_invited_by_user: "@%{name} meghÃv téged, hogy csatlakozz erre a Mastodon szerverre!" + prefix_sign_up: Regisztrláj még ma a Mastodonra! + suffix: Egy fiókkal követhetsz másokat, tülkölhetsz, eszmét cserélhetsz más Mastodon szerverek felhasználóival! didnt_get_confirmation: Nem kaptad meg a megerÅ‘sÃtési lépéseket? forgot_password: Elfelejtetted a jelszavad? invalid_reset_password_token: A jelszó-visszaállÃtási kulcs nem megfelelÅ‘ vagy lejárt. Kérlek generálj egy újat. @@ -544,6 +615,16 @@ hu: reset_password: Jelszó visszaállÃtása security: Biztonság set_new_password: Új jelszó beállÃtása + setup: + email_below_hint_html: Ha az alábbi e-mail cÃm nem megfelelÅ‘, itt megváltoztathatod és kaphatsz egy új igazoló e-mailt. + email_settings_hint_html: A visszaigazoló e-mailt elküldtük ide %{email}. Ha az e-mail cÃm nem megfelelÅ‘, megváltoztathatod a fiókod beállÃtásainál. + title: BeállÃtás + status: + account_status: Fiók állapota + confirming: Várakozás a visszaigazolásra. + functional: A fiókod teljesen működÅ‘képes. + pending: A jelentkezésed engedélyezésre vár. Ez eltarthat egy ideig. Kapsz egy e-mailt, ha az elbÃrálás megtörtént. + redirecting_to: A fiókod inaktÃv, mert jelenleg ide %{acct} van átirányÃtva. trouble_logging_in: Problémád van a bejelentkezéssel? authorize_follow: already_following: Már követed ezt a felhasználót @@ -556,6 +637,11 @@ hu: return: Visszatérés a felhasználó profiloldalára web: Megtekintés a weben title: "%{acct} követése" + challenge: + confirm: Folytatás + hint_html: "<strong>Hasznos:</strong> Nem fogjuk megint a jelszavadat kérdezni a következÅ‘ órában." + invalid_password: Érvénytelen jelszó + prompt: Add meg a jelszót a folytatáshoz datetime: distance_in_words: about_x_hours: "%{count}ó" @@ -571,26 +657,33 @@ hu: x_months: "%{count}h" x_seconds: "%{count}mp" deletes: - bad_password_msg: Haha, hekker! Helytelen jelszó + challenge_not_passed: A beÃrt információ helytelen confirm_password: Személyazonosságod megerÅ‘sÃtéséhez Ãrd be a jelenlegi jelszavad - description_html: Ezzel <strong>véglegesen és visszafordÃthatatlanul</strong> törlöd minden tartalmadat és deaktiválod a fiókodat. A felhasználónevedet megtartjuk, hogy megakadályozzuk a neveddel történÅ‘ jövÅ‘beni visszaélések lehetÅ‘ségét. + confirm_username: Add meg a felhasználói nevedet a jóváhagyáshoz proceed: Felhasználói fiók törlése success_msg: Felhasználói fiókod sikeresen töröltük - warning_html: Csak azt tudjuk garantálni, hogy az általad létrehozott tartalmat errÅ‘l a szerverrÅ‘l töröljük. Ha egyes tartalmaidat sokan megosztották, valószÃnűleg marad nyomuk a megosztások miatt. Nem fogjuk tudni frissÃteni azon szerverek adatbázisát, amelyek nem kapcsolódnak a föderációhoz vagy amelyek leiratkoztak a tülkjeidrÅ‘l. - warning_title: Szórt tartalmak elérése + warning: + before: 'MielÅ‘tt továbbmész, kérlek olvasd el ezt alaposan:' + caches: Más szerverek által cache-elt tartalmak még megmaradhatnak + data_removal: A tülkjeid és minden más adatod véglegesen törlÅ‘dni fog + email_change_html: <a href="%{path}">Megváltoztathatod az email cÃmed</a> a fiókod törlése nélkül + email_contact_html: Ha még mindig nem érkezik meg, emailezhetsz ide <a href="mailto:%{email}">%{email}</a> segÃtségért + email_reconfirmation_html: Ha nem kaptad meg a megerÅ‘sÃtÅ‘ emailt, <a href="%{path}">itt újrakérheted</a> + irreversible: Nem fogod tudni visszaállÃtani vagy újraaktiválni a fiókodat + more_details_html: A részletekért nézd meg az <a href="%{terms_path}">adatvédelmi szabályzatot</a>. + username_available: A fiókod ismét elérhetÅ‘vé válik + username_unavailable: A fiókod elérhetetlen marad directories: directory: Profilok - enabled: Szerepelsz a profil adatbázisban. - enabled_but_waiting: Engedélyezted, hogy szerepelj a profil adatbázisban, de még nincs elegendÅ‘ követÅ‘d (%{min_followers}) ehhez. explanation: Találj másokra érdeklÅ‘désük alapján explore_mastodon: "%{title} felfedezése" - how_to_enable: Nem engedélyezted a profil adatbázisban való megjelenésed. Engedélyezheted alább. Használj hashtageket az életrajzodban, hogy az ezekhez tartozó listákba bekerülj! - people: - one: "%{count} ember" - other: "%{count} ember" + domain_validator: + invalid_domain: nem egy valódi domain név errors: + '400': A küldött kérés érvénytelen vagy hibás volt. '403': Nincs jogosultságod az oldal megtekintéséhez. '404': Az általad keresett oldal nem található. + '406': Ez az oldal a kért formátumban nem áll rendelkezésre. '410': Az általad keresett oldal már nem létezik. '422': content: MegerÅ‘sÃtés sikertelen. Nem tiltottad le esetleg a sütiket? @@ -599,6 +692,7 @@ hu: '500': content: Sajnáljuk, valami hiba történt a mi oldalunkon. title: Az oldal nem megfelelÅ‘ + '503': Az oldalt nem tudjuk megmutatni átmeneti szerverprobléma miatt. noscript_html: A Mastodon webalkalmazás használatához engedélyezned kell a JavaScriptet. A másik megoldás, hogy kipróbálsz egy platformodnak megfelelÅ‘ <a href="%{apps_path}">alkalmazást</a>. existing_username_validator: not_found: ezzel a névvel nem találtunk helyi felhasználót @@ -622,6 +716,7 @@ hu: add_new: Új hozzáadása errors: limit: Már kiemelted a maximálisan engedélyezett számú hashtaget + hint_html: "<strong>Mik a kiemelt hashtagek?</strong> Ezek állandóan megjelennek a nyilvános profilodon és lehetÅ‘vé teszik, hogy mások kifejezetten az ezekhez tartozó tülkjeidet böngésszék. Jó eszköz ez kreatÃv munkák vagy hosszútávú projektek nyomonkövetésére." filters: contexts: home: Saját idÅ‘vonal @@ -642,10 +737,12 @@ hu: developers: FejlesztÅ‘knek more: Többet… resources: Segédanyagok + trending_now: Most trendi generic: all: Mind changes_saved_msg: A változásokat elmentettük! copy: Másolás + no_batch_actions_available: Ezen az oldalon nem elérhetÅ‘ek kötegelt műveletek order_by: Rendezés save_changes: Változások mentése validation_errors: @@ -717,9 +814,34 @@ hu: too_many: Maximum négy fájlt csatolhatsz a tülkhöz migrations: acct: Az új fiók felhasznalonev@domain formátumban - currently_redirecting: 'A profilod az alábbi fiókra van átirányÃtva:' - proceed: Mentés - updated_msg: Fiókod átirányÃtási beállÃtásait sikeresen mentettük! + cancel: ÃtirányÃtás törlése + cancel_explanation: Az átirányÃtás törlése reaktiválja a fiókodat, de nem fogja visszahozni azokat a követÅ‘idet, akik közben a másik fiókhoz kerültek át. + cancelled_msg: Az átirányÃtást sikeresen töröltük. + errors: + already_moved: ugyanaz a fiók, ahová már elköltöztél + missing_also_known_as: nem hivatkozza vissza ezt a fiókot + move_to_self: nem lehet az aktuális fiók + not_found: nem található + on_cooldown: Még tart a türelmi idÅ‘ + followers_count: KövetÅ‘ a költözéskor + incoming_migrations: Más fiókból költözÅ‘ + incoming_migrations_html: Ahhoz, hogy egy másik fiókból ebbe költözz, elÅ‘ször <a href="%{path}">hozz létre egy fiók aliast</a>. + moved_msg: A fiókod mostantól ide %{acct} irányÃt át, a követÅ‘idet átköltöztetjük. + not_redirecting: A fiókod nincs átirányÃtva jelenleg sehová sem. + on_cooldown: Nemrég költöztetted a fiókod. Ez a funkció %{count} nap múlva lesz megint elérhetÅ‘. + past_migrations: Eddigi költözések + proceed_with_move: KövetÅ‘k átköltöztetése + redirecting_to: A fiókod át van irányÃtva ide %{acct}. + set_redirect: ÃtirányÃtás beállÃtása + warning: + backreference_required: Az új fiókot elÅ‘ször be kell úgy állÃtani, hogy ezt visszahivatkozza + before: 'MielÅ‘tt továbbmész, olvasd el ezeket kérlek figyelmesen:' + cooldown: A költözés után van egy türelmi idÅ‘, mely alatt nem tudsz majd újra költözni + disabled_account: A jelenlegi fiókod nem lesz teljesen használható ezután. Viszont elérhetÅ‘ lesz majd az adatexport funkció, valamint a reaktiválás is. + followers: Ez a művelet az összes követÅ‘det a jelenlegi fiókról az újra fogja költöztetni + only_redirect_html: Az is lehetséges, hogy <a href="%{path}">csak átirányÃtást raksz a profilodra</a>. + other_data: Más adatot nem fogunk automatikusan mozgatni + redirect: A jelenlegi fiókod profiljára átirányÃtásról szóló figyelmeztetést rakunk, valamint már nem fogjuk mutatni a keresésekben moderation: title: Moderáció notification_mailer: @@ -816,10 +938,6 @@ hu: reply: proceed: Válaszadás prompt: 'Erre a tülkre szeretnél válaszolni:' - remote_unfollow: - error: Hiba - title: CÃm - unfollowed: Nem követett scheduled_statuses: over_daily_limit: Túllépted az idÅ‘zÃtett tülkökre vonatkozó napi limitet (%{limit}) over_total_limit: Túllépted az idÅ‘zÃtett tülkökre vonatkozó limitet (%{limit}) @@ -868,6 +986,7 @@ hu: settings: account: Fiók account_settings: Fiók beállÃtások + aliases: Fiók aliasok appearance: Megjelenés authorized_apps: Jóváhagyott alkalmazások back: Vissza a Mastodonhoz @@ -908,6 +1027,9 @@ hu: private: Csak nyilvános tülköt tűzhetsz ki reblog: Megtolt tülköt nem tudsz kitűzni poll: + total_people: + one: "%{count} személy" + other: "%{count} személy" total_votes: one: "%{count} szavazat" other: "%{count} szavazat" @@ -924,25 +1046,27 @@ hu: unlisted_long: Mindenki látja, de a nyilvános idÅ‘vonalakon nem jelenik meg stream_entries: pinned: Kitűzött tülk - reblogged: megtolt + reblogged: megtolta sensitive_content: SzenzitÃv tartalom + tags: + does_not_match_previous_name: nem illeszkedik az elÅ‘zÅ‘ névvel terms: body_html: | <h2>Adatvédelmi nyilatkozat</h2> <h3 id="collect">Milyen adatokat gyűjtünk?</h3> <ul> - <li><em>AlapvetÅ‘ fiókadatok</em>: Ha regisztrálsz ezen a szerveren, kérhetünk tÅ‘led felhasználói nevet, e-mail cÃmet és jelszót is. Megadhatsz magadról egyéb profil információt, mint megjelenÃtendÅ‘ név, bemutatkozás, feltölthetsz profilképet, háttérképet. A felhasználói neved, megjelenÃtendÅ‘ neved, bemutatkozásod, profil képed és háttér képed mindig nyilvánosak mindenki számára.</li> - <li><em>Tülkök (posztok), követések, más nyilvános adatok</em>: Az általad követett emberek listája nyilvános. Ugyanez igaz a te követÅ‘idre is. Ha küldesz egy üzenetet, ennek az idejét eltároljuk azzal az alkalmazással együtt, melybÅ‘l az üzenetet küldted. Az üzenetek tartalmazhatnak média csatolmányt, képeket, videókat. A nyilvános tülkök (posztok) bárki számára elérhetÅ‘ek. Ha egy tülköt kiemelsz a profilodon, az is nyilvánossá válik. Amikor a tülkjeidet a követÅ‘idnek továbbÃtjuk, a poszt más szerverekre is kerülhet, melyeken Ãgy másolatok képzÅ‘dhetnek. Ha törölsz tülköket, ez is továbbÃtódik a követÅ‘id felé. A megtolás (reblog) és kedvencnek jelölés művelete is mindig nyilvános.</li> - <li><em>Közvetlen üzenetek és csak követÅ‘knek szánt tülkök</em>: Minden tülk a szerveren tárolódik. A csak követÅ‘knek szánt tülköket a követÅ‘idnek és az ezekben megemlÃtetteknek továbbÃtjuk, mÃg a közvetlen üzeneteket kizárólag az ebben megemlÃtettek kapják. Néhány esetben ez azt jelenti, hogy ezek más szerverekre is továbbÃtódnak, Ãgy ott másolatok keletkezhetnek. Jóhiszeműen feltételezzük, hogy más szerverek is hasonlóan járnak el, mikor ezeket az üzeneteket csak az arra jogosultaknak mutatják meg. Ugyanakkor ez nem feltétlenül igaz. Ezért érdemes megnézni azokat a szervereket, melyeken követÅ‘id vannak. Be tudod állÃtani, hogy minden követési kérelmet jóvá kelljen hagynod. <em>Tartsd észben, hogy a szerver üzemeltetÅ‘i láthatják az üzeneteket</em>, illetve a fogadók képernyÅ‘képet, másolatot készÃthetnek belÅ‘lük, vagy újraoszthatják Å‘ket.<em>Ne ossz meg veszélyes információt a Mastodon hálózaton!</em></li> - <li><em>IP cÃmek és egyéb metaadatok</em>: Bejelentkezéskor letároljuk a használt böngészÅ‘det és IP cÃmedet. Mindent rögzÃtett munkamenet elérhetÅ‘ és visszavonható a beállÃtások között. A legutolsó IP cÃmet maximum 12 hónapig tárolunk. Egyéb szerver logokat is megtarthatunk, melyek HTTP kérésenként is tárolhatják az IP cÃmedet.</li> + <li><em>AlapvetÅ‘ fiókadatok</em>: Ha regisztrálsz ezen a szerveren, kérhetünk tÅ‘led felhasználói nevet, e-mail cÃmet és jelszót is. Megadhatsz magadról egyéb profil információt, megjelenÃtendÅ‘ nevet, bemutatkozást, feltölthetsz profilképet, háttérképet. A felhasználói neved, megjelenÃtendÅ‘ neved, bemutatkozásod, profil képed és háttér képed mindig nyilvánosak mindenki számára.</li> + <li><em>Tülkök (posztok), követések, más nyilvános adatok</em>: Az általad követett emberek listája nyilvános. Ugyanez igaz a te követÅ‘idre is. Ha küldesz egy üzenetet, ennek az idejét eltároljuk azzal az alkalmazással együtt, melybÅ‘l az üzenetet küldted. Az üzenetek tartalmazhatnak média csatolmányt, képeket, videókat. A nyilvános tülkök (posztok) bárki számára elérhetÅ‘ek. Ha egy tülköt kiemelsz a profilodon, az is nyilvánossá válik. Amikor a tülkjeidet a követÅ‘idnek továbbÃtjuk, a poszt más szerverekre is átkerülhet, melyeken Ãgy másolatok képzÅ‘dhetnek. Ha törölsz tülköket, ez is továbbÃtódik a követÅ‘id felé. A megtolás (reblog) és kedvencnek jelölés művelete is mindig nyilvános.</li> + <li><em>Közvetlen üzenetek és csak követÅ‘knek szánt tülkök</em>: Minden tülk a szerveren tárolódik. A csak követÅ‘knek szánt tülköket a követÅ‘idnek és az ezekben megemlÃtetteknek továbbÃtjuk, mÃg a közvetlen üzeneteket kizárólag az ebben megemlÃtettek kapják. Néhány esetben ez azt jelenti, hogy ezek más szerverekre is továbbÃtódnak, Ãgy ott másolatok keletkezhetnek. Jóhiszeműen feltételezzük, hogy más szerverek is hasonlóan járnak el, mikor ezeket az üzeneteket csak az arra jogosultaknak mutatják meg. Ugyanakkor ez nem feltétlenül igaz. Érdemes ezért megvizsgálni azokat a szervereket, melyeken követÅ‘id vannak. Be tudod állÃtani, hogy minden követési kérelmet jóvá kelljen hagynod. <em>Tartsd észben, hogy a szerver üzemeltetÅ‘i láthatják az üzeneteket</em>, illetve a fogadók képernyÅ‘képet, másolatot készÃthetnek belÅ‘lük, vagy újraoszthatják Å‘ket. <em>Ne ossz meg veszélyes információt a Mastodon hálózaton!</em></li> + <li><em>IP cÃmek és egyéb metaadatok</em>: Bejelentkezéskor letároljuk a használt böngészÅ‘det és IP cÃmedet. Minden rögzÃtett munkamenet elérhetÅ‘ és visszavonható a beállÃtások között. Az utoljára rögzÃtett IP cÃmet maximum 12 hónapig tároljuk. Egyéb szerver logokat is megtarthatunk, melyek HTTP kérésenként is tárolhatják az IP cÃmedet.</li> </ul> <hr class="spacer" /> <h3 id="use">Mire használjuk az adataidat?</h3> - <p>Bármely tÅ‘led begyűjtött adatot a következÅ‘ célokra használhatjuk:</p> + <p>Bármely tÅ‘led begyűjtött adatot a következÅ‘ célokra használhatjuk fel:</p> <ul> <li>Mastodon alapfunkcióinak biztosÃtása: Csak akkor léphetsz kapcsolatba másokkal, ha be vagy jelentkezve. Pl. követhetsz másokat a saját, személyre szabott idÅ‘vonaladon.</li> @@ -960,14 +1084,14 @@ hu: <h3 id="data-retention">Mik az adatmegÅ‘rzési szabályaink?</h3> - <p>Jóhiszeműen járunk el, hogy:</p> + <p>Mindent megteszünk, hogy:</p> <ul> - <li>A szerver logokat, melyek kérésenként tartalmazzák a felhasználó IP cÃmét maximum 90 napig tartjuk meg.</li> - <li>A regisztrált felhasználók IP cÃmeikkel összekötÅ‘ adatokat maximum 12 hónapig tartjuk meg.</li> + <li>A szerver logokat, melyek kérésenként tartalmazzák a felhasználó IP cÃmét maximum 90 napig tartsuk meg.</li> + <li>A regisztrált felhasználókat IP cÃmeikkel összekötÅ‘ adatokat maximum 12 hónapig tartsuk meg.</li> </ul> - <p>Kérhetsz archÃvot minden tárolt adatodról, tülkjeidrÅ‘l, média fájljaidról, profil- és háttér képedrÅ‘l.</p> + <p>Kérhetsz mentést minden tárolt adatodról, tülködrÅ‘l, média fájlodról, profil- és háttér képedrÅ‘l.</p> <p>Bármikor visszaállÃthatatlanul le is törölheted a fiókodat.</p> @@ -983,7 +1107,7 @@ hu: <h3 id="disclose">Ãtadunk bármilyen adatot harmadik személynek?</h3> - <p>Az azonosÃtásodra alkalmazható adatokat nem adjuk el, nem kereskedünk vele, nem adjuk át külsÅ‘ szereplÅ‘nek. Ez nem foglalja magába azon harmadik személyeket, aki az üzemeltetésben, felhasználók kiszolgálásban és a tevékenységünkben segÃtenek, de csak addig, amÃg Å‘k is elfogadják, hogy ezeket az adatokat bizalmasan kezelik. Akkor is átadhatjuk ezeket az adatokat, ha erre hitünk szerint törvény kötelez minket, ha betartatjuk az oldalunk szabályzatát vagy megvédjük a saját vagy mások személyiségi jogait, tulajdonát, biztonságát.</p> + <p>Az azonosÃtásodra alkalmazható adatokat nem adjuk el, nem kereskedünk vele, nem adjuk át külsÅ‘ szereplÅ‘nek. Ez nem foglalja magában azon harmadik személyeket, aki az üzemeltetésben, felhasználók kiszolgálásban és a tevékenységünkben segÃtenek, de csak addig, amÃg Å‘k is elfogadják, hogy ezeket az adatokat bizalmasan kezelik. Akkor is átadhatjuk ezeket az adatokat, ha erre hitünk szerint törvény kötelez minket, ha betartatjuk az oldalunk szabályzatát vagy megvédjük a saját vagy mások személyiségi jogait, tulajdonát, biztonságát.</p> <p>A nyilvános tartalmaidat más hálózatban lévÅ‘ szerverek letölthetik. A nyilvános és csak követÅ‘knek szánt tülkjeid olyan szerverekre is elküldÅ‘dnek, melyeken követÅ‘id vannak. A közvetlen üzenetek is átkerülnek a cÃmzettek szervereire, ha Å‘k más szerveren regisztráltak.</p> @@ -993,9 +1117,9 @@ hu: <h3 id="children">Az oldal gyerekek általi használata</h3> - <p>Ha ez a szerver az EU-ban vagy EEA-ban van: Az oldalunk, szolgáltatásaink és termékeink mind 16 éven felülieket céloznak. Ha 16 évnél fiatalabb vagy, a GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) értelmében kérlek ne használd ezt az oldalt!</p> + <p>Ha ez a szerver az EU-ban vagy EEA-ban található: Az oldalunk, szolgáltatásaink és termékeink mind 16 éven felülieket céloznak. Ha 16 évnél fiatalabb vagy, a GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) értelmében kérlek ne használd ezt az oldalt!</p> - <p>Ha ez a szerver az USA-ban van: Az oldalunk, szolgáltatásaink és termékeink mind 13 éven felülieket céloznak. Ha 13 évnél fiatalabb vagy, a COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) értelmében kérlek ne használd ezt az oldalt!</p> + <p>Ha ez a szerver az USA-ban található: Az oldalunk, szolgáltatásaink és termékeink mind 13 éven felülieket céloznak. Ha 13 évnél fiatalabb vagy, a COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) értelmében kérlek ne használd ezt az oldalt!</p> <p>A jogi elÅ‘Ãrások különbözhetnek ettÅ‘l a világ egyéb tájain.</p> @@ -1043,7 +1167,9 @@ hu: disable: A fiókod befagyasztott állapotban megtartja minden adatát, de feloldásig nem csinálhatsz vele semmit. silence: A fiókod korlátozott állapotában csak a követÅ‘id láthatják a tülkjeidet, valamint nem kerülsz rá nyilvános idÅ‘vonalakra. Ugyanakkor mások manuálisan még követhetnek. suspend: A fiókodat felfüggesztették, Ãgy minden tülköd és feltöltött fájlod menthetetlenül elveszett errÅ‘l a szerverrÅ‘l és minden olyanról is, ahol voltak követÅ‘id. + get_in_touch: Válaszolhatsz erre az emailre, hogy kapcsolatba lépj a %{instance} csapatával. review_server_policies: Szerver szabályzat átnézése + statuses: 'Különösen hozzá:' subject: disable: A fiókodat %{acct} befagyasztották none: Figyelmeztetés a %{acct} fióknak diff --git a/config/locales/hy.yml b/config/locales/hy.yml index 86645a57464f0d8490abb7e5e436711065f3733a..203351893d9aa9ce0a6872dc30ac1146c3d69309 100644 --- a/config/locales/hy.yml +++ b/config/locales/hy.yml @@ -1,12 +1,26 @@ --- hy: + about: + active_count_after: Õ¡Õ¯Õ¿Õ«Õ¾ + see_whats_happening: ÕÕ¥Õ½ Õ«Õ¶Õ¹ Õ¡ Õ¯Õ¡Õ¿Õ¡Ö€Õ¾Õ¸Ö‚Õ´ + status_count_after: + one: Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ½ + other: Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ½ + what_is_mastodon: Ô»ÕžÕ¶Õ¹ Õ§ Õ„Õ¡Õ½Õ¿Õ¸Õ¤Õ¸Õ¶Õ¨ + accounts: + media: Õ„Õ¥Õ¤Õ«Õ¡ + auth: + login: Õ„Õ¿Õ¶Õ¥Õ¬ errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. invites: expires_in: '1800': 30 minutes @@ -15,3 +29,7 @@ hy: '43200': 12 hours '604800': 1 week '86400': 1 day + sessions: + platforms: + android: Ô±Õ¶Õ¤Ö€Õ¸Õ«Õ¤ + linux: Ô¼Õ«Õ¶Õ¸Ö‚Ö„Õ½ diff --git a/config/locales/id.yml b/config/locales/id.yml index 43721b19b9f68255ff9c2a9fc8dab9a7cf111b3f..572769a5ae994fddcb5b7cad1dd97f96d65a8725 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -4,29 +4,51 @@ id: about_hashtag_html: Ini adalah toot public yang ditandai dengan <strong>#%{hashtag}</strong>. Anda bisa berinteraksi dengan mereka jika anda memiliki akun dimanapun di fediverse. about_mastodon_html: Mastodon adalah sebuah jejaring sosial <em>terbuka, open-source</em. Sebuah alternatif <em>desentralisasi</em> dari platform komersial, menjauhkan anda resiko dari sebuah perusahaan yang memonopoli komunikasi anda. Pilih server yang anda percayai — apapun yang anda pilih, anda tetap dapat berinteraksi dengan semua orang. Semua orang dapat menjalankan server Mastodon sendiri dan berpartisipasi dalam <em>jejaring sosial</em> dengan mudah. about_this: Tentang server ini + active_count_after: aktif + active_footnote: Pengguna Aktif Bulanan (PAB) administered_by: 'Dikelola oleh:' + api: API apps: Aplikasi hp + apps_platforms: Gunakan Mastodon dari iOS, Android, dan platform lain + browse_directory: Jelajahi direktori profil dan saring sesuai minat + browse_public_posts: Jelajahi siaran langsung pos publik di Mastodon contact: Kontak contact_missing: Belum diset contact_unavailable: Tidak Tersedia + discover_users: Temukan pengguna documentation: Dokumentasi - extended_description_html: | - <h3>Tempat yang baik untuk peraturan</h3> - <p>Deskripsi lainnya belum diset.</p> - generic_description: "%{domain} adalah satu server dalam jaringan" + federation_hint_html: Dengan akun di %{instance} Anda dapat mengikuti orang di server Mastodon mana pun dan di luarnya. + get_apps: Coba aplikasi mobile hosted_on: Mastodon dihosting di %{domain} + instance_actor_flash: 'Akun ini adalah aktor virtual yang dipakai untuk merepresentasikan server, bukan pengguna individu. Ini dipakai untuk tujuan federasi dan jangan diblokir kecuali Anda ingin memblokir seluruh instansi, yang seharusnya Anda pakai blokir domain. + +' learn_more: Pelajari selengkapnya privacy_policy: Kebijakan Privasi + see_whats_happening: Lihat apa yang sedang terjadi + server_stats: 'Statistik server:' source_code: Kode sumber status_count_after: other: status status_count_before: Yang telah menulis + tagline: Ikuti teman dan temukan yang baru terms: Kebijakan layanan + unavailable_content: Konten tak tersedia + unavailable_content_description: + domain: Server + reason: Alasan + rejecting_media: 'Berkas media dari server ini tak akan diproses dan disimpan, dan tak akan ada gambar kecil yang ditampilkan, perlu klik manual utk menuju berkas asli:' + silenced: 'Pos dari server ini akan disembunyikan dari linimasa publik dan percakapan, dan takkan ada notifikasi yang dibuat dari interaksi pengguna mereka, kecuali Anda mengikuti mereka:' + suspended: 'Takkan ada data yang diproses, disimpan, dan ditukarkan dari server ini, sehingga interaksi atau komunikasi dengan pengguna dari server ini tak mungkin dilakukan:' + unavailable_content_html: Mastodon umumnya mengizinkan Anda untuk melihat konten dan berinteraksi dengan pengguna dari server lain di fediverse. Ini adalah pengecualian yang dibuat untuk beberapa server. user_count_after: other: pengguna user_count_before: Tempat bernaung bagi what_is_mastodon: Apa itu Mastodon? accounts: + choices_html: 'Pilihan %{name}:' + endorsements_hint: Anda dapat mempromosikan orang yang Anda ikuti lewat antar muka web, dan mereka akan muncul di sini. + featured_tags_hint: Anda dapat mengunggulkan tagar tertentu yang akan ditampilkan di sini. follow: Ikuti followers: other: Pengikut @@ -34,8 +56,10 @@ id: joined: Bergabung pada %{date} last_active: terakhir aktif link_verified_on: Kepemilikan tautan ini telah dicek pada %{date} + media: Media moved_html: "%{name} telah pindah ke %{new_profile_link}:" network_hidden: Informasi ini tidak tersedia + never_active: Tak pernah nothing_here: Tidak ada apapun disini! people_followed_by: Orang yang diikuti %{name} people_who_follow: Orang-orang yang mengikuti %{name} @@ -47,7 +71,10 @@ id: posts_with_replies: Toot dan balasan reserved_username: Nama pengguna telah dipesan roles: + admin: Admin + bot: Bot moderator: Moderator + unavailable: Profil tidak tersedia unfollow: Berhenti mengikuti admin: account_actions: @@ -59,7 +86,11 @@ id: delete: Hapus destroyed_msg: Catatan moderasi berhasil dihapus! accounts: + approve: Terima + approve_all: Terima semua are_you_sure: Anda yakin? + avatar: Avatar + by_domain: Domian change_email: changed_msg: Email akun ini berhasil diubah! current_email: Email saat ini @@ -71,10 +102,12 @@ id: confirmed: Dikonfirmasi confirming: Mengkonfirmasi deleted: Terhapus + demote: Turunkan disable: Nonaktifkan disable_two_factor_authentication: Nonaktifkan 2FA disabled: Dinonaktifkan display_name: Nama + domain: Domain edit: Ubah email: E-mail email_status: Status Email @@ -84,12 +117,15 @@ id: followers: Pengikut followers_url: URL pengikut follows: Mengikut + header: Tajuk inbox_url: URL Kotak masuk invited_by: Diundang oleh + ip: IP joined: Bergabung location: all: Semua local: Lokal + remote: Remot title: Lokasi login_status: Status login media_attachments: Lampiran media @@ -97,15 +133,18 @@ id: moderation: active: Aktif all: Semua + pending: Tertunda silenced: Didiamkan suspended: Disuspen title: Moderasi moderation_notes: Catatan moderasi most_recent_activity: Aktivitas terbaru most_recent_ip: IP terbaru + no_account_selected: Tak ada akun yang diubah sebab tak ada yang dipilih no_limits_imposed: Tidak ada batasan not_subscribed: Tidak berlangganan outbox_url: URL Kotak keluar + pending: Tinjauan tertunda perform_full_suspension: Lakukan suspen penuh profile_url: URL profil promote: Promosikan @@ -113,20 +152,26 @@ id: public: Publik push_subscription_expires: Langganan PuSH telah kadaluarsa redownload: Muat ulang profil + reject: Tolak + reject_all: Tolak semua remove_avatar: Hapus avatar remove_header: Hapus header resend_confirmation: already_confirmed: Pengguna ini sudah dikonfirmasi send: Kirim ulang email konfirmasi success: Email konfirmasi berhasil dikirim! + reset: Reset reset_password: Reset kata sandi resubscribe: Langganan ulang role: Hak akses roles: + admin: Administrator + moderator: Moderator staff: Staf user: Pengguna salmon_url: URL Salmon search: Cari + shared_inbox_url: URL kotak masuk bersama show: created_reports: Laporan yang dibuat oleh akun ini targeted_reports: Laporan yang dibuat tentang akun ini @@ -135,6 +180,7 @@ id: statuses: Status subscribe: Langganan suspended: Disuspen + time_in_queue: Menunggu dalam antrean %{time} title: Akun unconfirmed_email: Email belum dikonfirmasi undo_silenced: Undo mendiamkan @@ -142,20 +188,131 @@ id: unsubscribe: Berhenti langganan username: Nama pengguna warn: Beri Peringatan + web: Web + whitelisted: Masuk daftar putih + action_logs: + actions: + assigned_to_self_report: "%{name} menugaskan laporan %{target} kpd dirinya sendiri" + change_email_user: "%{name} mengubah alamat surel pengguna %{target}" + confirm_user: "%{name} mengonfirmasi alamat surel pengguna %{target}" + create_account_warning: "%{name} mengirim peringatan untuk %{target}" + create_custom_emoji: "%{name} mengunggah emoji baru %{target}" + create_domain_block: "%{name} memblokir domain %{target}" + create_email_domain_block: "%{name} memasukkan ke daftar hitam domain surel %{target}" + demote_user: "%{name} menurunkan pengguna %{target}" + destroy_custom_emoji: "%{name} menghapus emoji %{target}" + destroy_domain_block: "%{name} membuka blokir domain %{target}" + destroy_email_domain_block: "%{name} memasukkan ke daftar putih surel domain %{target}" + disable_2fa_user: "%{name} mematikan syarat dua faktor utk pengguna %{target}" + disable_custom_emoji: "%{name} mematikan emoji %{target}" + disable_user: "%{name} mematikan login untuk pengguna %{target}" + enable_custom_emoji: "%{name} mengaktifkan emoji %{target}" + enable_user: "%{name} mengaktifkan login untuk pengguna %{target}" + memorialize_account: "%{name} mengubah akun %{target} jadi halaman memorial" + promote_user: "%{name} mempromosikan pengguna %{target}" + remove_avatar_user: "%{name} menghapus avatar %{target}" + reopen_report: "%{name} membuka ulang laporan %{target}" + reset_password_user: "%{name} mereset kata sandi pengguna %{target}" + resolve_report: "%{name} menyelesaikan laporan %{target}" + silence_account: "%{name} membungkam akun %{target}" + suspend_account: "%{name} menangguhkan akun %{target}" + unassigned_report: "%{name} tidak menugaskan laporan %{target}" + unsilence_account: "%{name} menghapus bungkaman akun %{target}" + unsuspend_account: "%{name} menghapus penangguhan akun %{target}" + update_custom_emoji: "%{name} memperbarui emoji %{target}" + deleted_status: "(status dihapus)" + title: Log audit + custom_emojis: + assign_category: Beri kategori + by_domain: Domain + copied_msg: Pembuatan salinan lokal emoji berhasil + copy: Salin + copy_failed_msg: Tidak dapat membuat salinan lokal emoji + create_new_category: Buat kategori baru + created_msg: Emoji berhasil dibuat! + delete: Hapus + destroyed_msg: Emoji berhasil dihapus! + disable: Matikan + disabled: Dinonaktifkan + disabled_msg: Emoji berhasil dinonaktifkan + emoji: Emoji + enable: Aktifkan + enabled: Diaktifkan + enabled_msg: Emoji berhasil diaktifkan + image_hint: PNG hingga 50KB + list: Daftar + listed: Terdaftar + new: + title: Tambah emoji kustom baru + overwrite: Timpa + shortcode: Kode pendek + shortcode_hint: Sedikitnya 2 karakter, hanya karakter alfanumerik dan garis bawah + title: Emoji kustom + uncategorized: Tak terkategorikan + unlist: Tak terdaftar + unlisted: Tak terdaftar + update_failed_msg: Tak dapat memperbarui emoji + updated_msg: Emoji berhasil diperbarui! + upload: Unggah + dashboard: + authorized_fetch_mode: Mode aman + backlog: pekerjaan di-backlog + config: Konfigurasi + feature_deletions: Penghapusan akun + feature_invites: Tautan undangan + feature_profile_directory: Direktori profil + feature_registrations: Registrasi + feature_relay: Relai federasi + feature_spam_check: Anti-spam + feature_timeline_preview: Pratinjau linimasa + features: Fitur + hidden_service: Federasi dengan layanan tersembunyi + open_reports: buka laporan + pending_tags: tagar menunggu ditinjau + pending_users: pengguna menunggu ditinjau + recent_users: Pengguna terbaru + search: Pencarian teks lengkap + single_user_mode: Mode pengguna tunggal + software: Perangkat lunak + space: Penggunaan ruang + title: Dasbor + total_users: total pengguna + trends: Tren + week_interactions: interaksi minggu ini + week_users_active: aktif minggu ini + week_users_new: pengguna minggu ini + whitelist_mode: Mode daftar putih + domain_allows: + add_new: Daftar putihkan domain + created_msg: Domain berhasil masuk daftar putih + destroyed_msg: Domain dihapus dari daftar putih + undo: Hapus dari daftar putih domain_blocks: add_new: Tambah created_msg: Pemblokiran domain sedang diproses destroyed_msg: Pemblokiran domain telah dibatalkan + domain: Domain + edit: Edit blok domain + existing_domain_block_html: Anda telah menerapkan batasan yang lebih ketat pada %{name}, Anda harus <a href="%{unblock_url}">membuka blokirnya</a> lebih dulu. new: create: Buat pemblokiran hint: Pemblokiran domain tidak akan menghentikan pembuatan akun dalam database, tapi kami akan memberikan moderasi otomatis pada akun-akun tersebut. severity: desc_html: "<strong>Pendiaman</strong> akan membuat semua postingan tidak dapat dilihat oleh semua orang yang tidak mengikutinya. <strong>Suspen</strong> akan menghapus semua konten, media, dan profil dari akun yang bersangkutan." + noop: Tidak ada silence: Pendiaman suspend: Suspen title: Pemblokiran domain baru + private_comment: Komentar pribadi + public_comment: Komentar publik reject_media: Tolak berkas media reject_media_hint: Hapus file media yang tersimpan dan menolak semua unduhan nantinya. Tidak terpengaruh dengan suspen + reject_reports: Tolak laporan + reject_reports_hint: Abaikan semua laporan dari domain ini. Tidak relevan untuk penangguhan + rejecting_media: tolak berkas media + severity: + silence: dibungkam + suspend: ditangguhkan show: affected_accounts: other: "%{count} akun dalam database terpengaruh" @@ -163,22 +320,111 @@ id: silence: Hapus pendiaman terhadap akun pada domain ini suspend: Hapus suspen terhadap akun pada domain ini title: Hapus pemblokiran domain %{domain} + undo: Urungkan + undo: Urungkan blokir domain + view: Lihat blokir domain + email_domain_blocks: + add_new: Tambah baru + created_msg: Berhasil menambahkan domain surel ke daftar hitam + delete: Hapus + destroyed_msg: Berhasil menghapus domain surel dari daftar hitam + domain: Domain + new: + create: Tambah domain + title: Entri daftar hitam surel baru + title: Daftar hitam surel + followers: + back_to_account: Kembali Ke Akun + title: Pengikut %{acct} instances: + by_domain: Domain + delivery_available: Pengiriman tersedia + moderation: + all: Semua + limited: Terbatas + title: Moderasi + private_comment: Komentar pribadi + public_comment: Komentar publik title: Server yang diketahui + total_blocked_by_us: Yang kita blokir + total_followed_by_them: Diikuti mereka + total_followed_by_us: Diikuti kita + total_storage: Lampiran media + invites: + deactivate_all: Nonaktifkan semua + filter: + all: Semua + available: Tersedia + expired: Kedaluwarsa + title: Saring + title: Undang + pending_accounts: + title: Akun tertunda (%{count}) + relays: + add_new: Tambah relai baru + delete: Hapus + disable: Matikan + disabled: Dimatikan + enable: Aktifkan + enable_hint: Saat diaktifkan, server Anda akan melanggan semua toot publik dari relai ini, dan akan mengirim toot publik server ini ke sana. + enabled: Diaktifkan + inbox_url: URL Relai + pending: Menunggu persetujuan relai + save_and_enable: Simpan dan aktifkan + setup: Atur koneksi relai + signatures_not_enabled: Relai tak akan bekerja dengan benar saat mode aman atau mode daftar putih diaktifkan + status: Status + title: Relai + report_notes: + created_msg: Catatan laporan berhasil dibuat! + destroyed_msg: Catatan laporan berhasil dihapus! reports: + account: + note: catatan + report: lapor + action_taken_by: Aksi dilakukan oleh + are_you_sure: Apakah Anda yakin? + assign_to_self: Tugaskan kpd saya + assigned: Moderator tertugas comment: none: Tidak ada + created_at: Dilaporkan mark_as_resolved: Tandai telah diseleseikan + mark_as_unresolved: Tandai belum terselesaikan + notes: + create: Tambah catatan + create_and_resolve: Diselesaikan dengan catatan + create_and_unresolve: Dibuka kembali dengan catatan + delete: Hapus + placeholder: Jelaskan aksi yang telah dilakukan, atau pembaruan lain yang berhubungan... + reopen: Buka lagi laporan report: 'Laporkan #%{id}' reported_account: Akun yang dilaporkan reported_by: Dilaporkan oleh resolved: Terseleseikan + resolved_msg: Laporan berhasil diselesaikan! + status: Status title: Laporan unresolved: Belum Terseleseikan + updated_at: Diperbarui settings: + activity_api_enabled: + desc_html: Hitung status yang dipos scr lokal, pengguna aktif, dan registrasi baru dlm keranjang bulanan + title: Terbitkan statistik keseluruhan tentang aktivitas pengguna + bootstrap_timeline_accounts: + desc_html: Pisahkan nama pengguna dengan koma. Hanya akun lokal dan tak terkunci yang akan bekerja. Isi bawaan jika kosong adalah semua admin lokal. + title: Ikuti scr bawaan untuk pengguna baru contact_information: email: Masukkan alamat email username: Masukkan nama pengguna + custom_css: + desc_html: Ubah tampilan dengan CSS yang dimuat di setiap halaman + title: CSS Kustom + default_noindex: + title: Singkirkan pengguna dari pengindeksan mesin pencari scr bawaan + domain_blocks: + all: Kepada semua orang + title: Lihat blokir domain registrations: closed_message: desc_html: Ditampilkan pada halaman depan saat pendaftaran ditutup<br>Anda bisa menggunakan tag HTML @@ -191,12 +437,51 @@ id: title: Deskripsi situs tambahan site_title: Judul Situs title: Pengaturan situs - subscriptions: - confirmed: Dikonfirmasi - expires_in: Kadaluarsa dalam - last_delivery: Terakhir dikirim - topic: Topik + statuses: + batch: + nsfw_off: Tandai sebagai tak sensitif + nsfw_on: Tandai sebagai sensitif + deleted: Dihapus + failed_to_execute: Gagal mengeksekusi + media: + title: Media + no_media: Tanpa media + no_status_selected: Tak ada status yang berubah karena tak ada yang dipilih + title: Status akun + with_media: Dengan media + tags: + accounts_today: Penggunaan unik hari ini + accounts_week: Penggunaan unik minggu ini + breakdown: Rinci penggunaan hari ini berdasar sumber + context: Konteks + directory: Di direktori + in_directory: "%{count} di direktori" + last_active: Terakhir aktif + most_popular: Paling populer + most_recent: Terkini + name: Tagar + review: Tinjau status + reviewed: Ditinjau + title: Tagar + trending_right_now: Sedang tren sekarang + unique_uses_today: "%{count} memposkan hari ini" + unreviewed: Tak tertinjau + updated_msg: Pembaruan pengaturan tagar berhasil title: Administrasi + warning_presets: + add_new: Tambah baru + delete: Hapus + edit: Sunting + edit_preset: Sunting preset peringatan + title: Kelola preset peringatan + admin_mailer: + new_pending_account: + body: Detail akun baru di bawah. Anda dapat menyetujui atau menolak lamaran ini. + subject: Akun baru muncul untuk ditinjau di %{instance} (%{username}) + new_report: + body: "%{reporter} telah melaporkan %{target}" + body_remote: Seseorang dari %{domain} telah melaporkan %{target} + subject: Laporan baru untuk %{instance} (#%{id}) application_mailer: settings: 'Ubah pilihan email: %{link}' view: 'Tampilan:' @@ -231,14 +516,17 @@ id: x_months: "%{count}bln" x_seconds: "%{count}dtk" errors: + '400': The request you submitted was invalid or malformed. '403': Anda tidak mempunyai izin untuk melihat halaman ini. '404': Halaman yang anda cari tidak ditemukan + '406': This page is not available in the requested format. '410': Halaman yang anda cari sudah tidak dapat ditemukan lagi. '422': content: Verifikasi keamanan gagal. Apa anda memblokir cookie? title: Verifikasi keamanan gagal '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. exports: blocks: Anda blokir follows: Anda ikuti diff --git a/config/locales/io.yml b/config/locales/io.yml index 559bf0f534dcb71eb6c69c9418fd0ff29d0d9c6a..2b3e5069173f29841aaba51144cc9c59d88f7bb2 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -73,12 +73,15 @@ io: half_a_minute: Jus less_than_x_seconds: Jus errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': La pagino quan tu serchas ne existas. + '406': This page is not available in the requested format. '410': La pagino quan tu serchas ne plus existas. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. exports: blocks: Tu blokusas follows: Tu sequas diff --git a/config/locales/it.yml b/config/locales/it.yml index 6dfe212d1496abc4b94d73243d27b1364e764337..f35c717a6ded7c456f3c2af45e222ac093d709d0 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -8,7 +8,7 @@ it: active_footnote: Utenti Attivi Mensili (MAU) administered_by: 'Amministrato da:' api: API - apps: Applicazioni Mobile + apps: Applicazioni per dispositivi mobili apps_platforms: Usa Mastodon da iOS, Android e altre piattaforme browse_directory: Sfoglia la directory dei profili e filtra per interessi browse_public_posts: Sfoglia il flusso in tempo reale di post pubblici su Mastodon @@ -17,13 +17,12 @@ it: contact_unavailable: N/D discover_users: Scopri utenti documentation: Documentazione - extended_description_html: | - <h3>Un buon posto per le regole</h3> - <p>La descrizione estesa non è ancora stata preparata.</p> federation_hint_html: Con un account su %{instance} sarai in grado di seguire persone su qualsiasi server Mastodon e oltre. - generic_description: "%{domain} è un server nella rete" get_apps: Prova un'app per smartphone hosted_on: Mastodon ospitato su %{domain} + instance_actor_flash: | + Questo account è un attore virtuale utilizzato per rappresentare il server stesso e non un particolare utente. + È utilizzato per scopi di federazione e non dovrebbe essere bloccato a meno che non si voglia bloccare l'intera istanza: in questo caso si dovrebbe utilizzare un blocco di dominio. learn_more: Scopri altro privacy_policy: Politica della privacy see_whats_happening: Guarda cosa succede @@ -35,6 +34,10 @@ it: status_count_before: Che hanno pubblicato tagline: Segui amici e trovane di nuovi terms: Termini di Servizio + unavailable_content: Contenuto non disponibile + unavailable_content_description: + reason: 'Motivo:' + rejecting_media: I file multimediali di questo server non saranno elaborati e non verranno visualizzate miniature, che richiedono clic manuale sull'altro server. user_count_after: one: utente other: utenti @@ -42,6 +45,7 @@ it: what_is_mastodon: Che cos'è Mastodon? accounts: choices_html: 'Suggerimenti da %{name}:' + featured_tags_hint: Puoi mettere in evidenza determinati hashtag che verranno visualizzati qui. follow: Segui followers: one: Seguace @@ -51,8 +55,9 @@ it: last_active: ultima attività link_verified_on: La proprietà di questo link è stata controllata il %{date} media: Media - moved_html: "%{name} è stato spostato su %{new_profile_link}:" + moved_html: "%{name} si è spostato su %{new_profile_link}:" network_hidden: Questa informazione non e' disponibile + never_active: Mai nothing_here: Qui non c'è nulla! people_followed_by: Persone seguite da %{name} people_who_follow: Persone che seguono %{name} @@ -63,7 +68,7 @@ it: other: Toot posts_tab_heading: Toot posts_with_replies: Toot e risposte - reserved_username: Il nome utente è gia stato preso + reserved_username: Questo nome utente è già stato preso roles: admin: Amministratore bot: Bot @@ -87,7 +92,7 @@ it: by_domain: Dominio change_email: changed_msg: Account email cambiato con successo! - current_email: Email corrente + current_email: Email attuale label: Cambia email new_email: Nuova email submit: Cambia email @@ -98,7 +103,7 @@ it: deleted: Cancellato demote: Declassa disable: Disabilita - disable_two_factor_authentication: Disabilita 2FA + disable_two_factor_authentication: Disabilita l'autenticazione a due fattori disabled: Disabilitato display_name: Nome visualizzato domain: Dominio @@ -115,7 +120,7 @@ it: inbox_url: URL inbox invited_by: Invitato da ip: IP - joined: Unito + joined: Iscritto da location: all: Tutto local: Locale @@ -133,7 +138,7 @@ it: title: Moderazione moderation_notes: Note di moderazione most_recent_activity: Attività più recenti - most_recent_ip: IP più recenti + most_recent_ip: IP più recente no_account_selected: Nessun account è stato modificato visto che non ne è stato selezionato nessuno no_limits_imposed: Nessun limite imposto not_subscribed: Non sottoscritto @@ -183,6 +188,7 @@ it: username: Nome utente warn: Avverti web: Web + whitelisted: Nella whitelist action_logs: actions: assigned_to_self_report: "%{name} ha assegnato il rapporto %{target} a se stesso" @@ -218,10 +224,12 @@ it: deleted_status: "(stato cancellato)" title: Registro di controllo custom_emojis: + assign_category: Assegna categoria by_domain: Dominio copied_msg: Creata con successo una copia locale dell'emoji copy: Copia copy_failed_msg: Impossibile creare una copia locale di questo emoji + create_new_category: Crea nuova categoria created_msg: Emoji creato con successo! delete: Elimina destroyed_msg: Emoji distrutto con successo! @@ -229,6 +237,7 @@ it: disabled_msg: Questa emoji è stata disabilitata con successo emoji: Emoji enable: Abilita + enabled: Abilitato enabled_msg: Questa emoji è stata abilitata con successo image_hint: PNG fino a 50 KB listed: Elencato @@ -238,11 +247,13 @@ it: shortcode: Scorciatoia shortcode_hint: Almeno due caratteri, solo caratteri alfanumerici e trattino basso title: Emoji personalizzate + uncategorized: Nessuna categoria unlisted: Non elencato update_failed_msg: Impossibile aggiornare questa emojii updated_msg: Emoji aggiornata con successo! upload: Carica dashboard: + authorized_fetch_mode: Modalità sicura backlog: lavori arretrati config: Configurazione feature_deletions: Cancellazioni di account @@ -250,10 +261,13 @@ it: feature_profile_directory: Directory dei profili feature_registrations: Registrazioni feature_relay: Ripetitore di federazione + feature_spam_check: Anti-spam feature_timeline_preview: Anteprima timeline features: Funzionalità hidden_service: Federazione con servizi nascosti open_reports: apri report + pending_tags: hashtag in attesa di essere esaminati + pending_users: utenti in attesa di essere esaminati recent_users: Utenti Recenti search: Ricerca testo intero single_user_mode: Modalita utente singolo @@ -265,11 +279,18 @@ it: week_interactions: interazioni per questa settimana week_users_active: attivi questa settimana week_users_new: utenti questa settimana + whitelist_mode: Modalità whitelist + domain_allows: + add_new: Dominio nella whitelist + created_msg: Il dominio è stato inserito nella whitelist + destroyed_msg: Il dominio è stato eliminato dalla whitelist + undo: Elimina dalla whitelist domain_blocks: add_new: Aggiungi nuovo created_msg: Il blocco del dominio sta venendo processato destroyed_msg: Il blocco del dominio è stato rimosso domain: Dominio + edit: Modifica blocco di dominio existing_domain_block_html: Hai già impostato limitazioni più stringenti su %{name}, dovresti <a href="%{unblock_url}">sbloccare</a> prima. new: create: Crea blocco @@ -280,6 +301,10 @@ it: silence: Silenzia suspend: Sospendi title: Nuovo blocco dominio + private_comment: Commento privato + private_comment_hint: Commento su questa limitazione di dominio per uso interno da parte dei moderatori. + public_comment: Commento pubblico + public_comment_hint: Commento pubblico su questa limitazione di dominio, se l'elenco delle limitazioni di dominio è pubblico. reject_media: Rifiuta file media reject_media_hint: Rimuovi i file media salvati in locale e blocca i download futuri. Irrilevante per le sospensioni reject_reports: Respingi rapporti @@ -299,6 +324,7 @@ it: title: Annulla il blocco del dominio per %{domain} undo: Annulla undo: Annulla + view: Visualizza blocco di dominio email_domain_blocks: add_new: Aggiungi nuovo created_msg: Dominio email aggiunto con successo alla lista nera @@ -322,6 +348,8 @@ it: all: Tutto limited: Limitato title: Moderazione + private_comment: Commento privato + public_comment: Commento pubblico title: Istanze conosciute total_blocked_by_us: Bloccato da noi total_followed_by_them: Seguito da loro @@ -399,6 +427,13 @@ it: custom_css: desc_html: Modifica l'aspetto con il CSS caricato in ogni pagina title: CSS personalizzato + domain_blocks: + all: A tutti + disabled: A nessuno + title: Mostra blocchi di dominio + users: Agli utenti locali connessi + domain_blocks_rationale: + title: Mostra motivazione hero: desc_html: Mostrata nella pagina iniziale. Almeno 600x100 px consigliati. Se non impostata, sarà usato il thumbnail del server title: Immagine dell'eroe @@ -449,6 +484,9 @@ it: desc_html: Potete scrivere la vostra politica sulla privacy, condizioni del servizio o altre informazioni legali. Potete usare tag HTML title: Termini di servizio personalizzati site_title: Nome del server + spam_check_enabled: + desc_html: Mastodon può silenziare e segnalare automaticamente account che inviano ripetutamente messaggi non richiesti. Potrebbero esserci falsi positivi. + title: Automazione anti-spam thumbnail: desc_html: Usato per anteprime tramite OpenGraph e API. 1200x630px consigliati title: Thumbnail del server @@ -456,12 +494,16 @@ it: desc_html: Mostra la timeline pubblica sulla pagina iniziale title: Anteprima timeline title: Impostazioni sito + trends: + desc_html: Visualizza pubblicamente gli hashtag precedentemente esaminati che sono attualmente in tendenza + title: Hashtag di tendenza statuses: back_to_account: Torna alla pagina dell'account batch: delete: Elimina nsfw_off: Segna come non sensibile nsfw_on: Segna come sensibile + deleted: Cancellato failed_to_execute: Impossibile eseguire media: title: Media @@ -469,21 +511,22 @@ it: no_status_selected: Nessun status è stato modificato perché nessuno era stato selezionato title: Gli status dell'account with_media: con media - subscriptions: - callback_url: URL Callback - confirmed: Confermato - expires_in: Scade in - last_delivery: Ultima distribuzione - title: WebSub - topic: Argomento tags: - accounts: Account - hidden: Nascosto - hide: Nascondi dalla directory - name: Etichetta + accounts_today: Usi unici oggi + accounts_week: Usi unici questa settimana + breakdown: Suddivisione dell'utilizzo di oggi per fonte + context: Contesto + directory: Nella directory + in_directory: "%{count} nella directory" + last_active: Ultima attività + most_popular: Più popolari + most_recent: Più recenti + name: Hashtag + reviewed: Controllato title: Hashtag - unhide: Mostra nella directory - visible: Visibile + trending_right_now: Di tendenza ora + unreviewed: Non controllato + updated_msg: Impostazioni degli hashtag aggiornate title: Amministrazione warning_presets: add_new: Aggiungi nuovo @@ -499,6 +542,11 @@ it: body: "%{reporter} ha segnalato %{target}" body_remote: Qualcuno da %{domain} ha segnalato %{target} subject: Nuova segnalazione per %{instance} (#%{id}) + new_trending_tag: + body: 'L''hashtag #%{name} oggi è di tendenza, ma non è stato mai controllato. Non sarà visualizzato pubblicamente se non lo permetti; se salvi il form senza modifiche non lo vedrai mai più.' + subject: Nuovo hashtag pronto per essere controllato su %{instance} (%{name}) + aliases: + add_new: Crea alias appearance: advanced_web_interface: Interfaccia web avanzata advanced_web_interface_hint: |- @@ -526,7 +574,7 @@ it: apply_for_account: Richiedi un invito change_password: Password checkbox_agreement_html: Sono d'accordo con le <a href="%{rules_path}" target="_blank">regole del server</a> ed i <a href="%{terms_path}" target="_blank">termini di servizio</a> - confirm_email: Conferma email + checkbox_agreement_without_rules_html: Accetto i <a href="%{terms_path}" target="_blank">termini di servizio</a> delete_account: Elimina account delete_account_html: Se desideri cancellare il tuo account, puoi <a href="%{path}">farlo qui</a>. Ti sarà chiesta conferma. didnt_get_confirmation: Non hai ricevuto le istruzioni di conferma? @@ -546,6 +594,14 @@ it: reset_password: Resetta la password security: Credenziali set_new_password: Imposta una nuova password + setup: + email_below_hint_html: Se l'indirizzo e-mail sottostante non è corretto, puoi cambiarlo qui e ricevere una nuova e-mail di conferma. + email_settings_hint_html: L'email di conferma è stata inviata a %{email}. Se l'indirizzo e-mail non è corretto, puoi modificarlo nelle impostazioni dell'account. + status: + account_status: Stato dell'account + confirming: In attesa che la conferma e-mail sia completata. + functional: Il tuo account è pienamente operativo. + pending: La tua richiesta è in attesa di esame da parte del nostro staff. Potrebbe richiedere un po' di tempo. Riceverai una e-mail se la richiesta è approvata. trouble_logging_in: Problemi di accesso? authorize_follow: already_following: Stai già seguendo questo account @@ -558,6 +614,11 @@ it: return: Mostra il profilo dell'utente web: Vai al web title: Segui %{acct} + challenge: + confirm: Continua + hint_html: "<strong>Suggerimento:</strong> Non ti chiederemo di nuovo la tua password per la prossima ora." + invalid_password: Password non valida + prompt: Conferma la tua password per continuare datetime: distance_in_words: about_x_hours: "%{count} ore" @@ -573,26 +634,24 @@ it: x_months: "%{count} mesi" x_seconds: "%{count} secondi" deletes: - bad_password_msg: Ci avete provato, hacker! Password errata + challenge_not_passed: Le informazioni che hai inserito non sono corrette confirm_password: Inserisci la tua password attuale per verificare la tua identità - description_html: Questa azione eliminerà <strong>in modo permanente e irreversibile</strong> tutto il contenuto del tuo account e lo disattiverà . Il tuo nome utente resterà riservato per prevenire che qualcuno in futuro assuma la tua identità . + confirm_username: Inserisci il tuo nome utente per confermare la procedura proceed: Cancella l'account success_msg: Il tuo account è stato cancellato - warning_html: È garantita la cancellazione del contenuto solo da questo server. I contenuti che sono stati ampiamente condivisi probabilmente lasceranno delle tracce. I server offline e quelli che non ricevono più i tuoi aggiornamenti non aggiorneranno i loro database. - warning_title: Disponibilità di contenuto diffuso + warning: + before: 'Prima di procedere, per favore leggi attentamente queste note:' directories: directory: Directory dei profili - enabled: Attualmente sei elencato nella directory. - enabled_but_waiting: Hai scelto di essere elencato nella directory, ma non hai ancora il numero minimo di seguaci (%{min_followers}) per comparire. explanation: Scopri utenti in base ai loro interessi explore_mastodon: Esplora %{title} - how_to_enable: Attualmente non hai scelto di comparire nella directory. Puoi farlo qui sotto. Se vuoi comparire sotto determinati hashtag, usali nel testo della tua biografia. - people: - one: "%{count} persona" - other: "%{count} persone" + domain_validator: + invalid_domain: non è un nome di dominio valido errors: + '400': The request you submitted was invalid or malformed. '403': Non sei autorizzato a visualizzare questa pagina. '404': La pagina che stavi cercando non esiste. + '406': This page is not available in the requested format. '410': La pagina che stavi cercando qui non esiste più. '422': content: Verifica di sicurezza non riuscita. Stai bloccando i cookies? @@ -601,6 +660,7 @@ it: '500': content: Siamo spiacenti, ma qualcosa non ha funzionato dal nostro lato. title: Questa pagina non è corretta + '503': The page could not be served due to a temporary server failure. noscript_html: Per usare l'interfaccia web di Mastodon dovi abilitare JavaScript. In alternativa puoi provare una delle <a href="%{apps_path}">app native</a> per Mastodon per la tua piattaforma. existing_username_validator: not_found: impossibile trovare un utente locale con quel nome utente @@ -624,6 +684,7 @@ it: add_new: Aggiungi nuovo errors: limit: Hai già messo in evidenza il numero massimo di hashtag + hint_html: "<strong>Cosa sono gli hashtag in evidenza?</strong> Sono visualizzati in evidenza sul tuo profilo pubblico e permettono alle persone di visualizzare i tuoi post pubblici marcati con questi hashtag. Sono un grande strumento per tenere traccia di opere creative o progetti a lungo termine." filters: contexts: home: Timeline home @@ -644,6 +705,7 @@ it: developers: Sviluppatori more: Altro… resources: Risorse + trending_now: Di tendenza ora generic: all: Tutto changes_saved_msg: Modifiche effettuate con successo! @@ -719,9 +781,6 @@ it: too_many: Impossibile allegare più di 4 file migrations: acct: utente@dominio del nuovo account - currently_redirecting: 'Il tuo profilo sarà ridirezionato a:' - proceed: Salva - updated_msg: L'impostazione per la migrazione dell'account è sta aggiornata! moderation: title: Moderazione notification_mailer: @@ -818,10 +877,6 @@ it: reply: proceed: Continua per rispondere prompt: 'Vuoi rispondere a questo toot:' - remote_unfollow: - error: Errore - title: Titolo - unfollowed: Non più seguito scheduled_statuses: over_daily_limit: Hai superato il limite di %{limit} toot programmati per questo giorno over_total_limit: Hai superato il limite di %{limit} toot programmati @@ -885,7 +940,7 @@ it: notifications: Notifiche preferences: Preferenze profile: Profilo - relationships: Follows and followers + relationships: Follows e followers two_factor_authentication: Autenticazione a due fattori statuses: attached: @@ -928,6 +983,8 @@ it: pinned: Toot fissato in cima reblogged: condiviso sensitive_content: Materiale sensibile + tags: + does_not_match_previous_name: non corrisponde al nome precedente terms: title: "%{instance} Termini di servizio e politica della privacy" themes: @@ -964,7 +1021,9 @@ it: disable: Mentre il tuo account è congelato, i tuoi dati dell'account rimangono intatti, ma non potrai eseguire nessuna azione fintanto che non viene sbloccato. silence: Mentre il tuo account è limitato, solo le persone che già ti seguono possono vedere i tuoi toot su questo server, e potresti essere escluso da vari elenchi pubblici. Comunque, altri possono manualmente seguirti. suspend: Il tuo account è stato sospeso, e tutti i tuoi toot ed i tuoi file media caricati sono stati irreversibilmente rimossi da questo server, e dai server dove avevi dei seguaci. + get_in_touch: Puoi rispondere a questa email per entrare in contatto con lo staff di %{instance}. review_server_policies: Rivedi regole del server + statuses: 'Nello specifico, per:' subject: disable: Il tuo account %{acct} è stato congelato none: Avviso per %{acct} diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ca640d07d1f42546867ac77070a85a1dc56728ae..3ff226efe1017adac07fbb47d97b98fd537018e8 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -17,13 +17,12 @@ ja: contact_unavailable: N/A discover_users: ユーザーを見ã¤ã‘ã‚‹ documentation: ドã‚ュメント - extended_description_html: | - <h3>ルールを書ãã®ã«é©ã—ãŸå ´æ‰€</h3> - <p>詳細説明ãŒè¨å®šã•れã¦ã„ã¾ã›ã‚“。</p> federation_hint_html: "%{instance} ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã²ã¨ã¤ã§ã©ã‚“ãªMastodon互æ›ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚‚フォãƒãƒ¼ã§ãã‚‹ã§ã—ょã†ã€‚" - generic_description: "%{domain} ã¯ã€Mastodon サーãƒãƒ¼ã®ä¸€ã¤ã§ã™" get_apps: モãƒã‚¤ãƒ«ã‚¢ãƒ—リを試㙠hosted_on: Mastodon hosted on %{domain} + instance_actor_flash: 'ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã‚µãƒ¼ãƒãƒ¼ãã®ã‚‚ã®ã‚’示ã™ä»®æƒ³çš„ãªã‚‚ã®ã§ã€ç‰¹å®šã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’示ã™ã‚‚ã®ã§ã¯ã‚りã¾ã›ã‚“。ã“れã¯ã‚µãƒ¼ãƒãƒ¼ã®é€£åˆã®ãŸã‚ã«ä½¿ç”¨ã•れã¾ã™ã€‚サーãƒãƒ¼å…¨ä½“をブãƒãƒƒã‚¯ã™ã‚‹ã¨ãã¯ã€ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ブãƒãƒƒã‚¯ã›ãšã«ã€ãƒ‰ãƒ¡ã‚¤ãƒ³ãƒ–ãƒãƒƒã‚¯ã‚’使用ã—ã¦ãã ã•ã„。 + +' learn_more: ã‚‚ã£ã¨è©³ã—ã privacy_policy: プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ see_whats_happening: やりã¨ã‚Šã‚’見ã¦ã¿ã‚‹ @@ -34,12 +33,22 @@ ja: status_count_before: トゥート数 tagline: Follow friends and discover new ones terms: 利用è¦ç´„ + unavailable_content: 制é™ä¸ã®ã‚µãƒ¼ãƒãƒ¼ + unavailable_content_description: + domain: サーãƒãƒ¼ + reason: 制é™ç†ç”± + rejecting_media: 'ã“れらã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®ãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ァイルã¯å‡¦ç†ã•れãšã€ä¿å˜ã‚„変æ›ã‚‚ã•れã¾ã›ã‚“。サムãƒã‚¤ãƒ«ã‚‚表示ã•れã¾ã›ã‚“。表示ã™ã‚‹ã«ã¯ã‚¯ãƒªãƒƒã‚¯ã—ã¦ãã®ã‚µãƒ¼ãƒãƒ¼ã«ç›´æŽ¥ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™:' + silenced: 'ã“れらã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®æŠ•稿ã¯å…¬é–‹ã‚¿ã‚¤ãƒ ラインã¨ä¼šè©±ã‹ã‚‰éš ã•れã¾ã™ã€‚ã¾ãŸè©²å½“ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®é€šçŸ¥ã¯ç›¸æ‰‹ã‚’フォãƒãƒ¼ã—ã¦ã„ã‚‹å ´åˆã‚’除ã表示ã•れã¾ã›ã‚“:' + suspended: 'ã“れらã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã®ãƒ‡ãƒ¼ã‚¿ã¯å‡¦ç†ã•れãšã€ä¿å˜ã‚„変æ›ã‚‚ã•れã¾ã›ã‚“。該当ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®äº¤æµã‚‚ã§ãã¾ã›ã‚“:' + unavailable_content_html: 通常 Mastodon ã§ã¯é€£åˆå…ˆã®ã©ã‚“ãªã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã§ã‚‚やりã¨ã‚Šã§ãã¾ã™ã€‚ãŸã ã—æ¬¡ã®ã‚µãƒ¼ãƒãƒ¼ã«ã¯ä¾‹å¤–ãŒè¨å®šã•れã¦ã„ã¾ã™ã€‚ user_count_after: other: 人 user_count_before: ユーザー数 what_is_mastodon: Mastodon ã¨ã¯ï¼Ÿ accounts: choices_html: "%{name} ã«ã‚ˆã‚‹ãŠã™ã™ã‚:" + endorsements_hint: ã‚ãªãŸãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ã‚‹ä¸ã§ãŠã™ã™ã‚ã—ãŸã„人をã“ã“ã§ç´¹ä»‹ã§ãã¾ã™ã€‚ + featured_tags_hint: 特定ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’ã“ã“ã«è¡¨ç¤ºã§ãã¾ã™ã€‚ follow: フォãƒãƒ¼ followers: other: フォãƒãƒ¯ãƒ¼ @@ -50,6 +59,7 @@ ja: media: メディア moved_html: "%{name} ã•ã‚“ã¯å¼•ã£è¶Šã—ã¾ã—㟠%{new_profile_link}:" network_hidden: ã“ã®æƒ…å ±ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“ + never_active: 活動ãªã— nothing_here: 何もã‚りã¾ã›ã‚“ï¼ people_followed_by: "%{name} ã•ã‚“ãŒãƒ•ã‚©ãƒãƒ¼ä¸ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ" people_who_follow: "%{name} ã•んをフォãƒãƒ¼ä¸ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ" @@ -179,6 +189,7 @@ ja: username: ユーザーå warn: è¦å‘Š web: Web + whitelisted: ホワイトリストã«ç™»éŒ²æ¸ˆã¿ action_logs: actions: assigned_to_self_report: "%{name} ã•ã‚“ãŒé€šå ± %{target} ã‚’è‡ªèº«ã®æ‹…当ã«å‰²ã‚Šå½“ã¦ã¾ã—ãŸ" @@ -214,31 +225,39 @@ ja: deleted_status: "(削除済)" title: æ“ä½œå±¥æ´ custom_emojis: + assign_category: カテゴリーを割り当㦠by_domain: ドメイン copied_msg: 絵文å—ã®ã‚³ãƒ”ーをãƒãƒ¼ã‚«ãƒ«ã«ä½œæˆã—ã¾ã—㟠copy: コピー copy_failed_msg: 絵文å—ã®ã‚³ãƒ”ーをãƒãƒ¼ã‚«ãƒ«ã«ä½œæˆã§ãã¾ã›ã‚“ã§ã—㟠+ create_new_category: ã‚«ãƒ†ã‚´ãƒªãƒ¼ã‚’ä½œæˆ created_msg: 絵文å—ã®è¿½åŠ ã«æˆåŠŸã—ã¾ã—ãŸï¼ delete: 削除 destroyed_msg: 絵文å—ã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸï¼ disable: 無効化 + disabled: 無効 disabled_msg: 絵文å—を無効化ã—ã¾ã—㟠emoji: çµµæ–‡å— enable: 有効化 + enabled: 有効 enabled_msg: 絵文å—を有効化ã—ã¾ã—㟠image_hint: 50KBã¾ã§ã®PNGç”»åƒã‚’利用ã§ãã¾ã™ - listed: åŽè¼‰ + list: 表示 + listed: 表示 new: title: æ–°è¦ã‚«ã‚¹ã‚¿ãƒ 絵文å—ã®è¿½åŠ overwrite: 上書ã shortcode: ショートコード shortcode_hint: 2æ–‡å—以上ã®åŠè§’英数å—ã¨ã‚¢ãƒ³ãƒ€ãƒ¼ãƒãƒ¼ã®ã¿åˆ©ç”¨ã§ãã¾ã™ title: ã‚«ã‚¹ã‚¿ãƒ çµµæ–‡å— - unlisted: 未åŽè¼‰ + uncategorized: 未分類 + unlist: éžè¡¨ç¤º + unlisted: éžè¡¨ç¤º update_failed_msg: 絵文å—ã‚’æ›´æ–°ã§ãã¾ã›ã‚“ã§ã—㟠updated_msg: 絵文å—ã®æ›´æ–°ã«æˆåŠŸã—ã¾ã—ãŸï¼ upload: アップãƒãƒ¼ãƒ‰ dashboard: + authorized_fetch_mode: ã‚»ã‚ュアモード backlog: 未処ç†ã®ã‚¸ãƒ§ãƒ– config: æ§‹æˆ feature_deletions: アカウント削除 @@ -246,10 +265,13 @@ ja: feature_profile_directory: ディレクトリ feature_registrations: æ–°è¦ç™»éŒ² feature_relay: 連åˆãƒªãƒ¬ãƒ¼ + feature_spam_check: ã‚¹ãƒ‘ãƒ å¯¾ç– feature_timeline_preview: タイムラインプレビュー features: 機能 hidden_service: 秘匿サービスã¨ã®é€£åˆ open_reports: 未解決ã®é€šå ± + pending_tags: 審査待ã¡ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + pending_users: 承èªå¾…ã¡ã®äººæ•° recent_users: 最近登録ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ search: 全文検索 single_user_mode: シングルユーザーモード @@ -261,11 +283,18 @@ ja: week_interactions: 今週交æµã®ã‚ã£ãŸå›žæ•° week_users_active: 今週活動ã—ãŸäººæ•° week_users_new: 今週登録ã—ãŸäººæ•° + whitelist_mode: ホワイトリストモード + domain_allows: + add_new: ホワイトリストã«è¿½åŠ + created_msg: ドメインã®ãƒ›ãƒ¯ã‚¤ãƒˆãƒªã‚¹ãƒˆã¸ã®ç™»éŒ²ãŒå®Œäº†ã—ã¾ã—㟠+ destroyed_msg: ドメインをホワイトリストã‹ã‚‰å‰Šé™¤ã—ã¾ã—㟠+ undo: ホワイトリストã‹ã‚‰å‰Šé™¤ domain_blocks: add_new: ドメインブãƒãƒƒã‚¯ã‚’è¿½åŠ created_msg: ドメインブãƒãƒƒã‚¯å‡¦ç†ã‚’完了ã—ã¾ã—㟠destroyed_msg: ドメインブãƒãƒƒã‚¯ã‚’外ã—ã¾ã—㟠domain: ドメイン + edit: ドメインブãƒãƒƒã‚¯ã‚’編集 existing_domain_block_html: æ—¢ã«%{name}ã«å¯¾ã—ã¦ã€ã‚ˆã‚Šå޳ã—ã„制é™ã‚’課ã—ã¦ã„ã¾ã™ã€‚å…ˆã«<a href="%{unblock_url}">ãã®åˆ¶é™ã‚’解除</a>ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ new: create: ブãƒãƒƒã‚¯ã‚’ä½œæˆ @@ -276,6 +305,10 @@ ja: silence: サイレンス suspend: åœæ¢ title: æ–°è¦ãƒ‰ãƒ¡ã‚¤ãƒ³ãƒ–ãƒãƒƒã‚¯ + private_comment: コメント (éžå…¬é–‹) + private_comment_hint: ã“ã®ã‚³ãƒ¡ãƒ³ãƒˆã¯åŒã˜ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã‚‚閲覧ã§ãã¾ã™ã€‚ + public_comment: コメント (公開) + public_comment_hint: ドメインブãƒãƒƒã‚¯ã®å…¬é–‹ã‚’有効ã«ã—ã¦ã„ã‚‹å ´åˆã€ã“ã®ã‚³ãƒ¡ãƒ³ãƒˆã‚‚公開ã•れã¾ã™ã€‚ reject_media: ãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ã‚¡ã‚¤ãƒ«ã‚’æ‹’å¦ reject_media_hint: ãƒãƒ¼ã‚«ãƒ«ã«ä¿å˜ã•れãŸãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ァイルを削除ã—ã€ä»Šå¾Œã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ã‚’æ‹’å¦ã—ã¾ã™ã€‚åœæ¢ã¨ã¯ç„¡é–¢ä¿‚ã§ã™ reject_reports: é€šå ±ã‚’æ‹’å¦ @@ -294,6 +327,7 @@ ja: title: "%{domain}ã®ãƒ‰ãƒ¡ã‚¤ãƒ³ãƒ–ãƒãƒƒã‚¯ã‚’戻ã™" undo: å…ƒã«æˆ»ã™ undo: ドメインブãƒãƒƒã‚¯ã‚’戻㙠+ view: ドメインブãƒãƒƒã‚¯ã‚’表示 email_domain_blocks: add_new: æ–°è¦è¿½åŠ created_msg: ブラックリストã«è¿½åŠ ã—ã¾ã—㟠@@ -316,6 +350,8 @@ ja: all: ã™ã¹ã¦ limited: 制é™ã‚り title: モデレーション + private_comment: コメント (éžå…¬é–‹) + public_comment: コメント (公開) title: 既知ã®ã‚µãƒ¼ãƒãƒ¼ total_blocked_by_us: ブãƒãƒƒã‚¯åˆè¨ˆ total_followed_by_them: 被フォãƒãƒ¼åˆè¨ˆ @@ -345,6 +381,7 @@ ja: pending: リレーサーãƒãƒ¼ã®æ‰¿èªå¾…ã¡ã§ã™ save_and_enable: ä¿å˜ã—ã¦æœ‰åйã«ã™ã‚‹ setup: リレー接続をè¨å®šã™ã‚‹ + signatures_not_enabled: ã‚»ã‚ュアモードã¾ãŸã¯ãƒ›ãƒ¯ã‚¤ãƒˆãƒªã‚¹ãƒˆãƒ¢ãƒ¼ãƒ‰ãŒæœ‰åйã®å ´åˆã€ãƒªãƒ¬ãƒ¼ã¯æ£å¸¸ã«å‹•作ã—ã¾ã›ã‚“ status: ステータス title: リレー report_notes: @@ -393,6 +430,16 @@ ja: custom_css: desc_html: 全ページã«é©ç”¨ã•れるCSSã®ç·¨é›† title: カスタムCSS + default_noindex: + desc_html: ã“ã®è¨å®šã‚’変更ã—ã¦ã„ãªã„全ユーザーã«å½±éŸ¿ã—ã¾ã™ + title: ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§æ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³ã«ã‚ˆã‚‹ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’æ‹’å¦ã™ã‚‹ + domain_blocks: + all: 誰ã«ã§ã‚‚è¨±å¯ + disabled: 誰ã«ã‚‚許å¯ã—ãªã„ + title: ドメインブãƒãƒƒã‚¯ã‚’表示 + users: ãƒã‚°ã‚¤ãƒ³æ¸ˆã¿ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¿è¨±å¯ + domain_blocks_rationale: + title: コメントを表示 hero: desc_html: フãƒãƒ³ãƒˆãƒšãƒ¼ã‚¸ã«è¡¨ç¤ºã•れã¾ã™ã€‚サイズã¯600x100px以上推奨ã§ã™ã€‚未è¨å®šã®å ´åˆã€æ¨™æº–ã®ã‚µãƒ ãƒã‚¤ãƒ«ãŒä½¿ç”¨ã•れã¾ã™ title: ヒーãƒãƒ¼ã‚¤ãƒ¡ãƒ¼ã‚¸ @@ -416,17 +463,17 @@ ja: desc_html: 誰ã§ã‚‚自分ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’削除ã§ãるよã†ã«ã—ã¾ã™ title: アカウント削除をå—ã‘付ã‘ã‚‹ min_invite_role: - disabled: 誰も許å¯ã—ãªã„ + disabled: 誰ã«ã‚‚許å¯ã—ãªã„ title: 招待ã®ä½œæˆã‚’è¨±å¯ registrations_mode: modes: approved: 登録ã«ã¯æ‰¿èªãŒå¿…è¦ - none: 誰も許å¯ã—ãªã„ + none: 誰ã«ã‚‚許å¯ã—ãªã„ open: 誰ã§ã‚‚ç™»éŒ²å¯ title: æ–°è¦ç™»éŒ² show_known_fediverse_at_about_page: - desc_html: ãƒã‚§ãƒƒã‚¯ã‚’入れるã¨ãƒ—ãƒ¬ãƒ“ãƒ¥ãƒ¼æ¬„ã«æ—¢çŸ¥ã®é€£åˆå…ˆå…¨ã¦ã®ãƒˆã‚¥ãƒ¼ãƒˆã‚’表示ã—ã¾ã™ã€‚外ã™ã¨ãƒãƒ¼ã‚«ãƒ«ã®ãƒˆã‚¥ãƒ¼ãƒˆã ã‘表示ã—ã¾ã™ã€‚ - title: タイムラインプレビューã«é€£åˆã‚¿ã‚¤ãƒ ラインを表示ã™ã‚‹ + desc_html: ãƒã‚§ãƒƒã‚¯ã‚’外ã™ã¨ã€ãƒ©ãƒ³ãƒ‡ã‚£ãƒ³ã‚°ãƒšãƒ¼ã‚¸ã‹ã‚‰ãƒªãƒ³ã‚¯ã•れãŸå…¬é–‹ã‚¿ã‚¤ãƒ ラインã«ãƒãƒ¼ã‚«ãƒ«ã®å…¬é–‹ãƒˆã‚¥ãƒ¼ãƒˆã®ã¿è¡¨ç¤ºã—ã¾ã™ã€‚ + title: 公開タイムラインã«é€£åˆå…ˆã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„も表示ã™ã‚‹ show_staff_badge: desc_html: ユーザーページã«ã‚¹ã‚¿ãƒƒãƒ•ã®ãƒãƒƒã‚¸ã‚’表示ã—ã¾ã™ title: スタッフãƒãƒƒã‚¸ã‚’表示ã™ã‚‹ @@ -443,19 +490,29 @@ ja: desc_html: 独自ã®ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ã‚„利用è¦ç´„ã€ãã®ä»–ã®æ³•çš„æ ¹æ‹ ã‚’è¨˜è¿°ã§ãã¾ã™ã€‚HTMLã‚¿ã‚°ãŒä½¿ãˆã¾ã™ title: カスタム利用è¦ç´„ site_title: サーãƒãƒ¼ã®åå‰ + spam_check_enabled: + desc_html: 迷惑ãªãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’繰り返ã—é€ä¿¡ã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’自動ã§é€šå ±ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚誤検知をå«ã‚€å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ + title: スパム対ç–を有効ã«ã™ã‚‹ thumbnail: desc_html: OpenGraphã¨APIã«ã‚ˆã‚‹ãƒ—レビューã«ä½¿ç”¨ã•れã¾ã™ã€‚サイズã¯1200×630px推奨ã§ã™ title: サーãƒãƒ¼ã®ã‚µãƒ ãƒã‚¤ãƒ« timeline_preview: - desc_html: ランディングページã«å…¬é–‹ã‚¿ã‚¤ãƒ ラインを表示ã—ã¾ã™ - title: タイムラインプレビュー + desc_html: ランディングページã«å…¬é–‹ã‚¿ã‚¤ãƒ ラインã¸ã®ãƒªãƒ³ã‚¯ã‚’表示ã—ã€èªè¨¼ãªã—ã§ã®å…¬é–‹ã‚¿ã‚¤ãƒ ラインã¸ã® API アクセスを許å¯ã—ã¾ã™ + title: 公開タイムラインã¸ã®æœªèªè¨¼ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹ title: サイトè¨å®š + trendable_by_default: + desc_html: 表示を拒å¦ã—ã¦ã„ãªã„ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã«å½±éŸ¿ã—ã¾ã™ + title: 審査å‰ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã®ãƒˆãƒ¬ãƒ³ãƒ‰ã¸ã®è¡¨ç¤ºã‚’許å¯ã™ã‚‹ + trends: + desc_html: ç¾åœ¨ãƒˆãƒ¬ãƒ³ãƒ‰ã«ãªã£ã¦ã„ã‚‹æ‰¿èªæ¸ˆã¿ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’公開ã—ã¾ã™ + title: トレンドタグを有効ã«ã™ã‚‹ statuses: back_to_account: ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãƒšãƒ¼ã‚¸ã«æˆ»ã‚‹ batch: delete: 削除 nsfw_off: 閲覧注æ„ã‚’ã¯ãšã™ nsfw_on: 閲覧注æ„ã«ã™ã‚‹ + deleted: 削除済㿠failed_to_execute: 実行ã«å¤±æ•—ã—ã¾ã—㟠media: title: メディア @@ -463,21 +520,24 @@ ja: no_status_selected: ä½•ã‚‚é¸æŠžã•れã¦ã„ãªã„ãŸã‚ã€å¤‰æ›´ã•れã¦ã„ã¾ã›ã‚“ title: トゥート一覧 with_media: メディアã‚り - subscriptions: - callback_url: コールãƒãƒƒã‚¯URL - confirmed: ç¢ºèªæ¸ˆã¿ - expires_in: æœŸé™ - last_delivery: 最終é…é€ - title: WebSub - topic: トピック tags: - accounts: アカウント - hidden: éžè¡¨ç¤º - hide: ディレクトリã‹ã‚‰éš ã™ + accounts_today: 本日使用ã—ãŸäººæ•° + accounts_week: 今週使用ã—ãŸäººæ•° + breakdown: ç›´è¿‘ã®ã‚µãƒ¼ãƒãƒ¼åˆ¥ä½¿ç”¨çŠ¶æ³ + context: 表示先 + directory: ディレクトリã«ä½¿ç”¨ + in_directory: "%{count} 人ãŒãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ä½¿ç”¨" + last_active: 最近使ã‚れãŸé † + most_popular: ä½¿ç”¨é »åº¦é † + most_recent: æ–°ç€é † name: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + review: å¯©æŸ»çŠ¶æ³ + reviewed: 審査済㿠title: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° - unhide: ディレクトリã«è¡¨ç¤ºã™ã‚‹ - visible: 表示 + trending_right_now: ç¾åœ¨ã®ãƒˆãƒ¬ãƒ³ãƒ‰ + unique_uses_today: 本日 %{count} 人ãŒãƒˆã‚¥ãƒ¼ãƒˆã«ä½¿ç”¨ + unreviewed: 未審査 + updated_msg: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°è¨å®šãŒæ›´æ–°ã•れã¾ã—㟠title: ç®¡ç† warning_presets: add_new: è¿½åŠ @@ -493,11 +553,21 @@ ja: body: "%{reporter} ㌠%{target} ã‚’é€šå ±ã—ã¾ã—ãŸ" body_remote: "%{domain} ã®èª°ã‹ãŒ %{target} ã‚’é€šå ±ã—ã¾ã—ãŸ" subject: "%{instance} ã®æ–°ã—ã„é€šå ± (#%{id})" + new_trending_tag: + body: 'ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° #%{name} ãŒæœ¬æ—¥ã®ãƒˆãƒ¬ãƒ³ãƒ‰ã«ãªã£ã¦ã„ã¾ã™ãŒã€å¯©æŸ»ãŒã¾ã 行ã‚れã¦ã„ãªã„ãŸã‚トレンドタグã«ã¯è¡¨ç¤ºã•れã¦ã„ã¾ã›ã‚“。一度許å¯ã™ã‚Œã°æ¬¡å›žã‹ã‚‰ã“ã®æ“作ã¯ä¸è¦ã§ã™ã€‚' + subject: "%{instance} ã§æ–°ã—ã„ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° (#%{name}) ãŒå¯©æŸ»å¾…ã¡ã§ã™" + aliases: + add_new: ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’ä½œæˆ + created_msg: エイリアスを作æˆã—ã¾ã—ãŸã€‚ã“れã§ä»¥å‰ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰å¼•ã£è¶Šã—ã‚’é–‹å§‹ã§ãã¾ã™ã€‚ + deleted_msg: エイリアスを削除ã—ã¾ã—ãŸã€‚指定ã•れã¦ã„ãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã¯å¼•ã£è¶Šã—ã§ããªããªã‚Šã¾ã™ã€‚ + hint_html: ä»–ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã‚’引ãç¶™ã„ã§å¼•ã£è¶Šã—ãŸã„å ´åˆã€ã“ã“ã§ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’作æˆã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ã€‚エイリアス自体ã¯<strong>無害ã§ã€å–り消ã™</strong>ã“ã¨ãŒã§ãã¾ã™ã€‚<strong>引ã£è¶Šã—ã¯ä»¥å‰ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå´ã‹ã‚‰é–‹å§‹ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™</strong>。 + remove: エイリアスを削除 appearance: advanced_web_interface: 上級者å‘ã‘ UI advanced_web_interface_hint: ディスプレイを幅ã„ã£ã±ã„ã¾ã§æ´»ç”¨ã—ãŸã„å ´åˆã€ä¸Šç´šè€…å‘ã‘ UI ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚ホームã€é€šçŸ¥ã€é€£åˆã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã€æ›´ã«ã¯ãƒªã‚¹ãƒˆã‚„ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ãªã©ã€æ§˜ã€…ãªç•°ãªã‚‹ã‚«ãƒ©ãƒ ã‹ã‚‰æœ›ã‚€é™ã‚Šã®æƒ…å ±ã‚’ä¸€åº¦ã«å—ã‘å–れるよã†ãªè¨å®šãŒå¯èƒ½ã«ãªã‚Šã¾ã™ã€‚ animations_and_accessibility: アニメーションã¨ã‚¢ã‚¯ã‚»ã‚·ãƒ“リティー confirmation_dialogs: 確èªãƒ€ã‚¤ã‚¢ãƒã‚° + discovery: 見ã¤ã‘ã‚‹ sensitive_content: 閲覧注æ„コンテンツ application_mailer: notification_preferences: メールè¨å®šã®å¤‰æ›´ @@ -518,9 +588,13 @@ ja: apply_for_account: 登録を申請ã™ã‚‹ change_password: パスワード checkbox_agreement_html: <a href="%{rules_path}" target="_blank">サーãƒãƒ¼ã®ãƒ«ãƒ¼ãƒ«</a> 㨠<a href="%{terms_path}" target="_blank">プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼</a> ã«åŒæ„ã—ã¾ã™ - confirm_email: メールアドレスã®ç¢ºèª + checkbox_agreement_without_rules_html: <a href="%{terms_path}" target="_blank">利用è¦ç´„</a> ã«åŒæ„ã—ã¾ã™ delete_account: アカウントã®å‰Šé™¤ delete_account_html: アカウントを削除ã—ãŸã„å ´åˆã€<a href="%{path}">ã“ã¡ã‚‰</a> ã‹ã‚‰æ‰‹ç¶šããŒè¡Œãˆã¾ã™ã€‚削除ã™ã‚‹å‰ã«ã€ç¢ºèªç”»é¢ãŒã‚りã¾ã™ã€‚ + description: + prefix_invited_by_user: "@%{name} ãŒã‚ãªãŸã‚’ã“ã® Mastodon サーãƒãƒ¼ã«æ‹›å¾…ã—ã¾ã—ãŸ" + prefix_sign_up: 今ã™ã Mastodon ã‚’å§‹ã‚よã†ï¼ + suffix: アカウントãŒã‚れã°ã€ã©ã‚“㪠Mastodon 互æ›ã‚µãƒ¼ãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚‚フォãƒãƒ¼ã—ãŸã‚Šãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’やりå–りã§ãるよã†ã«ãªã‚Šã¾ã™ï¼ didnt_get_confirmation: 確èªãƒ¡ãƒ¼ãƒ«ã‚’å—ä¿¡ã§ãã¾ã›ã‚“ã‹ï¼Ÿ forgot_password: パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿ invalid_reset_password_token: ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ãƒªã‚»ãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³ãŒæ£ã—ããªã„ã‹æœŸé™åˆ‡ã‚Œã§ã™ã€‚ã‚‚ã†ä¸€åº¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã—ã¦ãã ã•ã„。 @@ -538,6 +612,16 @@ ja: reset_password: パスワードをå†ç™ºè¡Œ security: ã‚»ã‚ュリティ set_new_password: æ–°ã—ã„パスワード + setup: + email_below_hint_html: 下記ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒé–“é•ã£ã¦ã„ã‚‹å ´åˆã€ã“ã“ã§å¤‰æ›´ã™ã‚‹ã“ã¨ã§æ–°ãŸã«ç¢ºèªãƒ¡ãƒ¼ãƒ«ã‚’å—ä¿¡ã§ãã¾ã™ã€‚ + email_settings_hint_html: 確èªç”¨ã®ãƒ¡ãƒ¼ãƒ«ã‚’ %{email} ã«é€ä¿¡ã—ã¾ã—ãŸã€‚ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒæ£ã—ããªã„å ´åˆã€ä»¥ä¸‹ã‚ˆã‚Šå¤‰æ›´ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + title: セットアップ + status: + account_status: アカウントã®çŠ¶æ…‹ + confirming: メールアドレスã®ç¢ºèªãŒå®Œäº†ã™ã‚‹ã®ã‚’å¾…ã£ã¦ã„ã¾ã™ã€‚ + functional: アカウントã¯å®Œå…¨ã«æ©Ÿèƒ½ã—ã¦ã„ã¾ã™ã€‚ + pending: ã‚ãªãŸã®ç”³è«‹ã¯ç¾åœ¨ã‚µãƒ¼ãƒãƒ¼ç®¡ç†è€…ã«ã‚ˆã‚‹å¯©æŸ»å¾…ã¡ã§ã™ã€‚ã“れã«ã¯ã—ã°ã‚‰ãã‹ã‹ã‚Šã¾ã™ã€‚ç”³è«‹ãŒæ‰¿èªã•れるã¨ãƒ¡ãƒ¼ãƒ«ãŒå±Šãã¾ã™ã€‚ + redirecting_to: アカウント㯠%{acct} ã«å¼•ã£è¶Šã—è¨å®šã•れã¦ã„ã‚‹ãŸã‚éžã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ãªã£ã¦ã„ã¾ã™ã€‚ trouble_logging_in: ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“ã‹ï¼Ÿ authorize_follow: already_following: ã‚ãªãŸã¯æ—¢ã«ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’フォãƒãƒ¼ã—ã¦ã„ã¾ã™ @@ -550,6 +634,11 @@ ja: return: ユーザーã®ãƒ—ãƒãƒ•ィールを見る web: Web ã‚’é–‹ã title: "%{acct} をフォãƒãƒ¼" + challenge: + confirm: ç¶šã‘ã‚‹ + hint_html: 以後 1 時間ã¯ãƒ‘スワードã®å†å…¥åŠ›ã‚’æ±‚ã‚ã¾ã›ã‚“ + invalid_password: パスワードãŒé–“é•ã£ã¦ã„ã¾ã™ + prompt: 続行ã™ã‚‹ã«ã¯ãƒ‘スワードを入力ã—ã¦ãã ã•ã„ datetime: distance_in_words: about_x_hours: "%{count}時間" @@ -565,25 +654,33 @@ ja: x_months: "%{count}月" x_seconds: "%{count}ç§’" deletes: - bad_password_msg: パスワードãŒé•ã„ã¾ã™ + challenge_not_passed: 入力ã•ã‚ŒãŸæƒ…å ±ã¯æ£ã—ãã‚りã¾ã›ã‚“ confirm_password: 本人確èªã®ãŸã‚ã€ç¾åœ¨ã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„ - description_html: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å«ã¾ã‚Œã‚‹ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯å…¨ã¦å‰Šé™¤ã•れã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ç„¡åŠ¹åŒ–ã•れã¾ã™ã€‚ã“ã‚Œã¯æ’ä¹…çš„ãªã‚‚ã®ã§ã€<strong>å–り消ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“</strong>。ãªã‚Šã™ã¾ã—を防ããŸã‚ã«ã€åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼åã§å†åº¦ç™»éŒ²ã™ã‚‹ã“ã¨ã¯ã§ããªããªã‚Šã¾ã™ã€‚ + confirm_username: 確èªã®ãŸã‚ユーザーåを入力ã—ã¦ãã ã•ã„ proceed: アカウントを削除ã™ã‚‹ success_msg: ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯æ£å¸¸ã«å‰Šé™¤ã•れã¾ã—㟠- warning_html: 削除ãŒä¿è¨¼ã•れるã®ã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ä¸Šã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã¿ã§ã™ã€‚ä»–ã®ã‚µãƒ¼ãƒãƒ¼ç‰ã€å¤–部ã«åºƒã共有ã•れãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã«ã¤ã„ã¦ã¯ç—•è·¡ãŒæ®‹ã‚‹ã“ã¨ãŒã‚りã¾ã™ã€‚ã¾ãŸã€ç¾åœ¨æŽ¥ç¶šã§ããªã„サーãƒãƒ¼ã‚„ã€ã‚ãªãŸã®æ›´æ–°ã‚’å—ã‘å–らãªããªã£ãŸã‚µãƒ¼ãƒãƒ¼ã«å¯¾ã—ã¦ã¯ã€å‰Šé™¤ã¯åæ˜ ã•れã¾ã›ã‚“。 - warning_title: 共有ã•れãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã«ã¤ã„㦠+ warning: + before: '続行ã™ã‚‹å‰ã«ã€æ¬¡ã®ç‚¹ã‚’å†åº¦ç¢ºèªã—ã¦ãã ã•ã„:' + caches: ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã«ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã‚ャッシュãŒãšã£ã¨æ®‹ã‚‹å ´åˆãŒã‚りã¾ã™ + data_removal: ã‚ãªãŸã®æŠ•稿やãã®ä»–ã®ãƒ‡ãƒ¼ã‚¿ã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰å®Œå…¨ã«å‰Šé™¤ã•れã¾ã™ + email_change_html: アカウントを削除ã—ãªãã¦ã‚‚<a href="%{path}">メールアドレスを変更</a>ã§ãã¾ã™ + email_contact_html: ãれã§ã‚‚届ã‹ãªã„å ´åˆã€<a href="mailto:%{email}">%{email}</a> ã¾ã§ãƒ¡ãƒ¼ãƒ«ã§å•ã„åˆã‚ã›ã¦ãã ã•ã„ + email_reconfirmation_html: 確èªã®ãƒ¡ãƒ¼ãƒ«ãŒå±Šã‹ãªã„å ´åˆã€<a href="%{path}">ã‚‚ã†ä¸€åº¦ç”³è«‹</a>ã§ãã¾ã™ã€‚ + irreversible: ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’å…ƒã«æˆ»ã—ãŸã‚Šå¾©æ´»ã•ã›ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ + more_details_html: 詳ã—ãã¯<a href="%{terms_path}">プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼</a>ã‚’ã”覧ãã ã•ã„。 + username_available: ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯å†åˆ©ç”¨ã§ãるよã†ã«ãªã‚Šã¾ã™ + username_unavailable: ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯å¼•ãç¶šã利用ã§ãã¾ã›ã‚“ directories: directory: ディレクトリ - enabled: ã‚ãªãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«æŽ²è¼‰ã•れã¦ã„ã¾ã™ã€‚ - enabled_but_waiting: ã‚ãªãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¸ã®æŽ²è¼‰ã‚’é¸æŠžã—ã¾ã—ãŸãŒã€æŽ²è¼‰ã«å¿…è¦ãªæœ€å°ãƒ•ã‚©ãƒãƒ¯ãƒ¼æ•° (%{min_followers} 人) を満ãŸã—ã¦ã„ã¾ã›ã‚“。 explanation: 関心を軸ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’発見ã—よㆠexplore_mastodon: "%{title}を探索" - how_to_enable: ã‚ãªãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¸ã®æŽ²è¼‰ã‚’é¸æŠžã—ã¦ã„ã¾ã›ã‚“。下記ã‹ã‚‰é¸æŠžã§ãã¾ã™ã€‚ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚«ãƒ©ãƒ ã«æŽ²è¼‰ã™ã‚‹ã«ã¯ãƒ—ãƒãƒ•ィール文ã«ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã‚’使用ã—ã¦ãã ã•ã„。 - people: - other: "%{count} 人" + domain_validator: + invalid_domain: ã¯ç„¡åйãªãƒ‰ãƒ¡ã‚¤ãƒ³åã§ã™ errors: + '400': é€ä¿¡ã•れãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ç„¡åйã§ã‚ã‚‹ã‹ã€ã¾ãŸã¯ä¸æ£ãªãƒ•ォーマットã§ã™ã€‚ '403': ã“ã®ãƒšãƒ¼ã‚¸ã‚’表示ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“。 '404': ãŠæŽ¢ã—ã®ãƒšãƒ¼ã‚¸ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ + '406': ã“ã®ãƒšãƒ¼ã‚¸ã¯è¦æ±‚ã•れãŸå½¢å¼ã§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。 '410': ãŠæŽ¢ã—ã®ãƒšãƒ¼ã‚¸ã¯ã‚‚ã†å˜åœ¨ã—ã¾ã›ã‚“。 '422': content: ã‚»ã‚ュリティèªè¨¼ã«å¤±æ•—ã—ã¾ã—ãŸã€‚Cookieをブãƒãƒƒã‚¯ã—ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ @@ -592,6 +689,7 @@ ja: '500': content: ã‚‚ã†ã—ã‚ã‘ã‚りã¾ã›ã‚“ãŒã€ãªã«ã‹ãŒé–“é•ã£ã¦ã„ã¾ã™ã€‚ title: ã“ã®ãƒšãƒ¼ã‚¸ã¯æ£ã—ãã‚りã¾ã›ã‚“ + '503': 一時的ãªã‚µãƒ¼ãƒãƒ¼éšœå®³ã®ãŸã‚利用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。 noscript_html: Mastodonã®ã‚¦ã‚§ãƒ–アプリケーションを利用ã™ã‚‹å ´åˆã¯JavaScriptを有効ã«ã—ã¦ãã ã•ã„。ã¾ãŸã¯ã‚ãªãŸã®ãƒ—ラットフォームå‘ã‘ã®<a href="%{apps_path}">Mastodonãƒã‚¤ãƒ†ã‚£ãƒ–アプリ</a>を探ã™ã“ã¨ãŒã§ãã¾ã™ã€‚ existing_username_validator: not_found: ãã®ã‚ˆã†ãªãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ãƒãƒ¼ã‚«ãƒ«ã«è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠@@ -615,6 +713,7 @@ ja: add_new: è¿½åŠ errors: limit: 注目ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã®ä¸Šé™ã«é”ã—ã¾ã—㟠+ hint_html: "<strong>注目ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã¨ã¯ï¼Ÿ</strong>プãƒãƒ•ィールページã«ç›®ç«‹ã¤å½¢ã§è¡¨ç¤ºã•れã€ãã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã®ã¤ã„ãŸã‚ãªãŸã®å…¬é–‹æŠ•稿ã ã‘を抽出ã—ã¦é–²è¦§ã§ãるよã†ã«ã—ã¾ã™ã€‚クリエイティブãªä»•事や長期的ãªãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆã‚’追ã†ã®ã«å„ªã‚ŒãŸæ©Ÿèƒ½ã§ã™ã€‚" filters: contexts: home: ホームタイムライン @@ -635,10 +734,12 @@ ja: developers: 開発者å‘ã‘ more: ã•らã«â€¦ resources: リソース + trending_now: トレンドタグ generic: all: ã™ã¹ã¦ changes_saved_msg: æ£å¸¸ã«å¤‰æ›´ã•れã¾ã—ãŸï¼ copy: コピー + no_batch_actions_available: ã“ã®ãƒšãƒ¼ã‚¸ã«ä¸€æ‹¬æ“作ã¯ã‚りã¾ã›ã‚“ order_by: 並ã³é † save_changes: 変更をä¿å˜ validation_errors: @@ -689,7 +790,7 @@ ja: '604800': 1 週間 '86400': 1 æ—¥ expires_in_prompt: ç„¡æœŸé™ - generate: ä½œæˆ + generate: æ‹›å¾…ãƒªãƒ³ã‚¯ã‚’ä½œæˆ invited_by: '次ã®äººã«æ‹›å¾…ã•れã¾ã—ãŸ:' max_uses: other: "%{count}" @@ -708,9 +809,34 @@ ja: too_many: è¿½åŠ ã§ãるファイルã¯4ã¤ã¾ã§ã§ã™ migrations: acct: 引ã£è¶Šã—先㮠ユーザーå@ドメイン - currently_redirecting: 'ã‚ãªãŸã®ãƒ—ãƒãƒ•ィールã¯å¼•ã£è¶Šã—å…ˆãŒè¨å®šã•れã¦ã„ã¾ã™:' - proceed: ä¿å˜ - updated_msg: アカウントã®å¼•ã£è¶Šã—è¨å®šã‚’æ›´æ–°ã—ã¾ã—ãŸï¼ + cancel: 引ã£è¶Šã—ã‚’å–り消㙠+ cancel_explanation: 引ã£è¶Šã—ã‚’å–り消ã™ã¨ç¾åœ¨ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒå†åº¦æœ‰åŠ¹åŒ–ã•れã¾ã™ãŒã€å¼•ãç¶™ãŒã‚ŒãŸãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯æˆ»ã•れã¾ã›ã‚“。 + cancelled_msg: 引ã£è¶Šã—è¨å®šã‚’å–り消ã—ã¾ã—ãŸã€‚ + errors: + already_moved: ã¯æ—¢ã«å¼•ã£è¶Šã—ãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¨åŒã˜ã§ã™ + missing_also_known_as: ã¯ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã®è¨å®šãŒã•れã¦ã„ã¾ã›ã‚“ + move_to_self: åŒã˜ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + not_found: 見ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠+ on_cooldown: クールダウン期間ä¸ã§ã™ + followers_count: 引ãç¶™ãŒã‚Œã‚‹ãƒ•ã‚©ãƒãƒ¯ãƒ¼ + incoming_migrations: 別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰å¼•ã£è¶Šã™ + incoming_migrations_html: 別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¼•ã£è¶Šã™ã«ã¯ã€ã¾ãš<a href="%{path}">アカウントエイリアスを作æˆ</a>ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ + moved_msg: アカウント㯠%{acct} ã«å¼•ã£è¶Šã—è¨å®šã•れã¦ãŠã‚Šã€ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã¯å¼•ã£è¶Šã—å…ˆã«å¼•ãç¶™ãŒã‚Œã¦ã„ã¾ã™ã€‚ + not_redirecting: アカウントã¯ç¾åœ¨å¼•ã£è¶Šã—è¨å®šã•れã¦ã„ã¾ã›ã‚“。 + on_cooldown: ã‚ãªãŸã¯æœ€è¿‘アカウントを引ã£è¶Šã—ã¾ã—ãŸã€‚ã“ã®æ©Ÿèƒ½ã¯ %{count} 日後ã«å†åº¦åˆ©ç”¨ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚ + past_migrations: éŽåŽ»ã®å¼•ã£è¶Šã— + proceed_with_move: フォãƒãƒ¯ãƒ¼ã‚’引ãç¶™ã + redirecting_to: アカウント㯠%{acct} ã«å¼•ã£è¶Šã—è¨å®šã•れã¦ã„ã¾ã™ã€‚ + set_redirect: 引ã£è¶Šã—ã‚’è¨å®š + warning: + backreference_required: ã¾ãšã¯å¼•ã£è¶Šã—å…ˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«å¯¾ã—エイリアスを作æˆã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ + before: '続行ã™ã‚‹å‰ã«ã€æ¬¡ã®ç‚¹ã‚’å†åº¦ç¢ºèªã—ã¦ãã ã•ã„:' + cooldown: 引ã£è¶Šã—後ã¯ã‚¯ãƒ¼ãƒ«ãƒ€ã‚¦ãƒ³æœŸé–“ãŒã‚りã¾ã™ã€‚ãã®é–“å†åº¦å¼•ã£è¶Šã™ã“ã¨ã¯ã§ãã¾ã›ã‚“ + disabled_account: 引ã£è¶Šã—ãŸå¾Œã¯ãƒ‡ãƒ¼ã‚¿ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã¨å†æœ‰åŠ¹åŒ–ã‚’é™¤ãã»ã¨ã‚“ã©ã®æ©Ÿèƒ½ãŒåˆ©ç”¨ã§ããªããªã‚Šã¾ã™ + followers: ã™ã¹ã¦ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã‚’ç¾åœ¨ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰æ–°ã—ã„アカウントã«å¼•ãç¶™ãŽã¾ã™ + only_redirect_html: ã¾ãŸã¯ã€<a href="%{path}">フォãƒãƒ¯ãƒ¼ã‚’残ã—ãŸã¾ã¾å¼•ã£è¶Šã™</a>ã“ã¨ã‚‚ã§ãã¾ã™ã€‚ + other_data: ãã®ä»–ã®ãƒ‡ãƒ¼ã‚¿ã¯è‡ªå‹•çš„ã«å¼•ãç¶™ãŒã‚Œã¾ã›ã‚“ + redirect: プãƒãƒ•ィールã«å¼•ã£è¶Šã—済ã¿ã®é€šçŸ¥ãŒè¡¨ç¤ºã•ã‚Œã€æ¤œç´¢çµæžœã‹ã‚‰é™¤å¤–ã•れã¾ã™ moderation: title: モデレーション notification_mailer: @@ -805,10 +931,6 @@ ja: reply: proceed: 返信ã™ã‚‹ prompt: '返信ã—よã†ã¨ã—ã¦ã„ã¾ã™:' - remote_unfollow: - error: エラー - title: タイトル - unfollowed: フォãƒãƒ¼è§£é™¤ã—ã¾ã—㟠scheduled_statuses: over_daily_limit: ãã®æ—¥äºˆç´„ã§ãる投稿数 %{limit} ã‚’è¶…ãˆã¦ã„ã¾ã™ over_total_limit: 予約ã§ãる投稿数 %{limit} ã‚’è¶…ãˆã¦ã„ã¾ã™ @@ -856,7 +978,8 @@ ja: title: セッション settings: account: アカウント - account_settings: ã‚»ã‚ュリティ + account_settings: アカウントè¨å®š + aliases: アカウントエイリアス appearance: 外観 authorized_apps: èªè¨¼æ¸ˆã¿ã‚¢ãƒ—リ back: Mastodon ã«æˆ»ã‚‹ @@ -874,6 +997,8 @@ ja: profile: プãƒãƒ•ィール relationships: フォãƒãƒ¼ãƒ»ãƒ•ã‚©ãƒãƒ¯ãƒ¼ two_factor_authentication: 二段階èªè¨¼ + spam_check: + spam_detected: ã“れã¯è‡ªå‹•çš„ã«ä½œæˆã•れãŸé€šå ±ã§ã™ã€‚ã‚¹ãƒ‘ãƒ ãŒæ¤œå‡ºã•れã¦ã„ã¾ã™ã€‚ statuses: attached: description: '添付: %{attached}' @@ -894,6 +1019,8 @@ ja: private: éžå…¬é–‹ã®ãƒˆã‚¥ãƒ¼ãƒˆã‚’固定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ reblog: ブーストを固定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ poll: + total_people: + other: "%{count} 人" total_votes: other: "%{count}票" vote: 投票 @@ -911,6 +1038,8 @@ ja: pinned: 固定ã•れãŸãƒˆã‚¥ãƒ¼ãƒˆ reblogged: ã•ã‚“ãŒãƒ–ースト sensitive_content: é–²è¦§æ³¨æ„ + tags: + does_not_match_previous_name: 以å‰ã®åå‰ã¨ä¸€è‡´ã—ã¾ã›ã‚“ terms: body_html: | <h2>プライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼</h2> @@ -1003,7 +1132,7 @@ ja: default: "%Yå¹´%m月%dæ—¥ %H:%M" month: "%Yå¹´ %b" two_factor_authentication: - code_hint: 確èªã™ã‚‹ã«ã¯èªè¨¼ã‚¢ãƒ—リã§è¡¨ç¤ºã•れãŸã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¦ãã ã•ã„ + code_hint: 続行ã™ã‚‹ã«ã¯èªè¨¼ã‚¢ãƒ—リã§è¡¨ç¤ºã•れãŸã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¦ãã ã•ã„ description_html: "<strong>二段階èªè¨¼</strong>を有効ã«ã™ã‚‹ã¨ãƒã‚°ã‚¤ãƒ³æ™‚ã€èªè¨¼ã‚¢ãƒ—リã‹ã‚‰ã‚³ãƒ¼ãƒ‰ã‚’入力ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" disable: 無効 enable: 有効 @@ -1028,7 +1157,9 @@ ja: disable: アカウントãŒå‡çµã•れã¦ã„ã‚‹é–“ã€ãƒ‡ãƒ¼ã‚¿ã¯ãã®ã¾ã¾æ®‹ã‚Šã¾ã™ãŒã€å‡çµãŒè§£é™¤ã•れるã¾ã§ã¯ä½•ã®æ“作もã§ãã¾ã›ã‚“。 silence: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯åˆ¶é™ã•れã¦ã„ã¾ã™ãŒã€ã‚ãªãŸã‚’フォãƒãƒ¼ã—ã¦ã„るユーザーã®ã¿ã€ã“ã®ã‚µãƒ¼ãƒãƒ¼ä¸Šã®æŠ•稿を見るã“ã¨ãŒã§ãã¾ã™ã€‚ãã—ã¦ã‚ãªãŸã¯æ§˜ã€…ãªå…¬é–‹ãƒªã‚¹ãƒˆã‹ã‚‰é™¤å¤–ã•れるã‹ã‚‚ã—れã¾ã›ã‚“。ãŸã ã—ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ‰‹å‹•ã§ã‚ãªãŸã‚’フォãƒãƒ¼ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ suspend: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯åœæ¢ã•れã¦ã„ã¾ã™ã€‚ã‚ãªãŸã®æŠ•稿ã¨ã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã•れãŸãƒ¡ãƒ‡ã‚£ã‚¢ãƒ•ァイルã¯ã€ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¨ã‚ãªãŸã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ãŒå‚åŠ ã—ã¦ã„ãŸã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰å®Œå…¨ã«å‰Šé™¤ã•れã¾ã—ãŸã€‚ + get_in_touch: ã“ã®ãƒ¡ãƒ¼ãƒ«ã«è¿”ä¿¡ã™ã‚‹ã“ã¨ã§ %{instance} ã®ã‚¹ã‚¿ãƒƒãƒ•ã¨é€£çµ¡ã‚’å–ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ review_server_policies: サーãƒãƒ¼ã®ãƒãƒªã‚·ãƒ¼ã‚’ç¢ºèª + statuses: 'ç‰¹ã«æ¬¡ã®ãƒˆã‚¥ãƒ¼ãƒˆ:' subject: disable: ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ %{acct} ã¯å‡çµã•れã¦ã„ã¾ã™ none: "%{acct} ã«å¯¾ã™ã‚‹è¦å‘Š" diff --git a/config/locales/ka.yml b/config/locales/ka.yml index 53057d8603312cd6fa368577bfe041c572c5c482..0ab1f2d69da1adc37222057e15bb3708c599f454 100644 --- a/config/locales/ka.yml +++ b/config/locales/ka.yml @@ -11,10 +11,6 @@ ka: contact_missing: áƒáƒ áƒáƒ დáƒáƒ§áƒ”ნებული contact_unavailable: მიუწ. documentation: დáƒáƒ™áƒ£áƒ›áƒ”ნტáƒáƒªáƒ˜áƒ - extended_description_html: | - <h3>კáƒáƒ გი áƒáƒ“გილი წესებისთვის</h3> - <p>გáƒáƒœáƒ•რცáƒáƒ‘ილი áƒáƒ¦áƒ¬áƒ”რილáƒáƒ‘რჯერáƒáƒ შექმნილáƒ.</p> - generic_description: "%{domain} ერთი სერვერირქსელში" hosted_on: მáƒáƒ¡áƒ¢áƒáƒ“áƒáƒœáƒ¡ მáƒáƒ¡áƒžáƒ˜áƒœáƒ«áƒšáƒáƒ‘ს %{domain} learn_more: გáƒáƒ˜áƒ’ე მეტი privacy_policy: კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ‘ის პáƒáƒšáƒ˜áƒ¢áƒ˜áƒ™áƒ @@ -368,13 +364,6 @@ ka: no_status_selected: სáƒáƒ¢áƒ£áƒ¡áƒ”ბი áƒáƒ შეცვლილáƒ, რáƒáƒ“გáƒáƒœ áƒáƒ ცერთი áƒáƒ მáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒšáƒ title: áƒáƒœáƒ’áƒáƒ იშის სტáƒáƒ¢áƒ£áƒ¡áƒ”ბი with_media: მედიით - subscriptions: - callback_url: ქáƒáƒšáƒ‘ექ ურლ - confirmed: დáƒáƒ›áƒáƒ¬áƒ›áƒ“რ- expires_in: ვáƒáƒ“რგáƒáƒ¡áƒ“ის - last_delivery: ბáƒáƒšáƒ მიღებრ- title: ვებ-სáƒáƒ‘ი - topic: სáƒáƒ—áƒáƒ£áƒ ი title: áƒáƒ“მინისტრáƒáƒªáƒ˜áƒ admin_mailer: new_report: @@ -397,7 +386,6 @@ ka: your_token: თქვენი წვდáƒáƒ›áƒ˜áƒ¡ ტáƒáƒ™áƒ”ნი auth: change_password: პáƒáƒ áƒáƒšáƒ˜ - confirm_email: ელ-ფáƒáƒ¡áƒ¢áƒ˜áƒ¡ დáƒáƒ›áƒáƒ¬áƒ›áƒ”ბრdelete_account: áƒáƒœáƒ’áƒáƒ იშის გáƒáƒ£áƒ¥áƒ›áƒ”ბრdelete_account_html: თუ გსურთ გáƒáƒáƒ£áƒ¥áƒ›áƒáƒ— თქვენი áƒáƒœáƒ’áƒáƒ იში, შეგიძლიáƒáƒ— <a href="%{path}">გáƒáƒáƒ’რძელáƒáƒ— áƒáƒ¥</a>. სáƒáƒáƒ˜áƒ რიქნებრდáƒáƒ›áƒáƒ¬áƒ›áƒ”ბáƒ. didnt_get_confirmation: áƒáƒ მáƒáƒ’სვლიáƒáƒ— დáƒáƒ›áƒáƒ¬áƒ›áƒ”ბის ინსტრუქციები? @@ -442,16 +430,14 @@ ka: x_months: "%{count}თვე" x_seconds: "%{count}წმ" deletes: - bad_password_msg: კáƒáƒ გáƒáƒ“ სცáƒáƒ“ეთ, ჰáƒáƒ™áƒ”რებáƒ! áƒáƒ áƒáƒ¡áƒ¬áƒáƒ ი პáƒáƒ áƒáƒšáƒ˜ confirm_password: იდენტáƒáƒ‘ის დáƒáƒ¡áƒáƒ›áƒáƒ¬áƒ›áƒ”ბლáƒáƒ“ შეიყვáƒáƒœáƒ”თ მიმდინáƒáƒ ე პáƒáƒ áƒáƒšáƒ˜ - description_html: ეს <strong>სáƒáƒ›áƒ£áƒ“áƒáƒ›áƒáƒ“, დáƒáƒ£áƒ‘რუნებლáƒáƒ“</strong> გáƒáƒáƒ£áƒ¥áƒ›áƒ”ბს კáƒáƒœáƒ¢áƒ”ნტს თქვენი áƒáƒœáƒ’áƒáƒ იშიდáƒáƒœ დრმáƒáƒáƒ®áƒ“ენს მის დეáƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒªáƒ˜áƒáƒ¡. მáƒáƒ›áƒ®áƒ›áƒáƒ ებლის სáƒáƒ®áƒ”ლი კი, სáƒáƒ›áƒáƒ›áƒáƒ•ლრიმპერსáƒáƒœáƒáƒªáƒ˜áƒ”ბის შესáƒáƒ©áƒ”რებლáƒáƒ“, გáƒáƒ®áƒ“ებრრეზერვირებული. proceed: áƒáƒœáƒ’áƒáƒ იშის გáƒáƒ£áƒ¥áƒ›áƒ”ბრsuccess_msg: თქვენი áƒáƒœáƒ’áƒáƒ იში წáƒáƒ მáƒáƒ¢áƒ”ბით გáƒáƒ£áƒ¥áƒ›áƒ“რ- warning_html: მáƒáƒªáƒ£áƒšáƒáƒ‘ის გáƒáƒ£áƒ¥áƒ›áƒ”ბრგáƒáƒ áƒáƒœáƒ¢áƒ˜áƒ ებულირმხáƒáƒšáƒáƒ“ áƒáƒ› ინსტáƒáƒœáƒªáƒ˜áƒáƒ–ე. კáƒáƒœáƒ¢áƒ”ნტი რáƒáƒ›áƒ”ლიც ფáƒáƒ თრმáƒáƒ¡áƒ¨áƒ¢áƒáƒ‘ით გáƒáƒ–იáƒáƒ დრუფრრდáƒáƒ¢áƒáƒ•ებს კვáƒáƒšáƒ¡. áƒáƒ¤áƒšáƒáƒ˜áƒœ სერვერები დრსერვერები, რáƒáƒ›áƒšáƒ”ბმáƒáƒª შეწყვიტეს თქვენი გáƒáƒœáƒáƒ®áƒšáƒ”ბების გáƒáƒ›áƒáƒ¬áƒ”რრáƒáƒ გáƒáƒœáƒáƒáƒ®áƒšáƒ”ბენ მáƒáƒœáƒáƒªáƒ”მთრბáƒáƒ–ებს. - warning_title: წვდáƒáƒ›áƒ გáƒáƒ•რცელებულ კáƒáƒœáƒ¢áƒ”ნტზე errors: + '400': The request you submitted was invalid or malformed. '403': áƒáƒ› გვერდის ხილვის უფლებრáƒáƒ გáƒáƒ¥áƒ•თ. '404': გვერდი რáƒáƒ›áƒ”ლსáƒáƒª ეძებთ áƒáƒ áƒáƒ სებáƒáƒ‘ს. + '406': This page is not available in the requested format. '410': გვერდი რáƒáƒ›áƒ”ლსáƒáƒª ეძებდით áƒáƒ¦áƒáƒ áƒáƒ სებáƒáƒ‘ს. '422': content: უსáƒáƒ¤áƒ თხáƒáƒ”ბის ვერიფიკáƒáƒªáƒ˜áƒ ვერმáƒáƒ®áƒ”რხდáƒ. ბლáƒáƒ™áƒáƒ•თ ქუქის? @@ -460,6 +446,7 @@ ka: '500': content: ბáƒáƒ“იში, ჩვენ მხáƒáƒ ეს რáƒáƒ¦áƒáƒª áƒáƒ იáƒ. title: გვერდი áƒáƒ áƒáƒ სწáƒáƒ ი + '503': The page could not be served due to a temporary server failure. noscript_html: მáƒáƒ¡áƒ¢áƒáƒ“áƒáƒœ ვებ-áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ˜áƒ¡ გáƒáƒ›áƒáƒ§áƒ”ნებისთვის, გთხáƒáƒ•თ ჩáƒáƒ თáƒáƒ— ჯáƒáƒ•áƒáƒ¡áƒ™áƒ იპტი. სხვრშემთხვევáƒáƒ¨áƒ˜, მáƒáƒ¡áƒ¢áƒáƒ“áƒáƒœáƒ˜áƒ¡ თქვენი პáƒáƒ¢áƒ¤áƒáƒ მისთვის სცáƒáƒ“ეთ გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნáƒáƒ— ერთ-ერთი <a href="%{apps_path}">მშáƒáƒ‘ლიური áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ</a>. exports: archive_takeout: @@ -540,9 +527,6 @@ ka: too_many: თáƒáƒœ ვერდáƒáƒ£áƒ თáƒáƒ•თ 4 ფáƒáƒ˜áƒšáƒ–ე მეტს migrations: acct: username@domain áƒáƒ®áƒáƒšáƒ˜ áƒáƒœáƒ’áƒáƒ იშის - currently_redirecting: 'თქვენი პრáƒáƒ¤áƒ˜áƒšáƒ˜ გáƒáƒ›áƒáƒ თულირმáƒáƒáƒ®áƒ“ინáƒáƒ¡ გáƒáƒ“áƒáƒ›áƒ˜áƒ¡áƒáƒ›áƒáƒ თებრმისáƒáƒ›áƒáƒ თზე:' - proceed: შენáƒáƒ®áƒ•რ- updated_msg: თქვენი áƒáƒœáƒ’áƒáƒ იშის მიგრáƒáƒªáƒ˜áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრები წáƒáƒ მáƒáƒ¢áƒ”ებით დáƒáƒ›áƒáƒ®áƒ¡áƒáƒ•რდáƒ! moderation: title: მáƒáƒ“ერáƒáƒªáƒ˜áƒ notification_mailer: @@ -601,10 +585,6 @@ ka: no_account_html: áƒáƒ გáƒáƒ¥áƒ•თ áƒáƒœáƒ’áƒáƒ იში? შეგიძლიáƒáƒ— <a href='%{sign_up_path}' target='_blank'>დáƒáƒ ეგისტრირდეთ áƒáƒ¥</a> proceed: გáƒáƒáƒ’რძელეთ გáƒáƒ¡áƒáƒ§áƒáƒšáƒáƒ“ prompt: 'თქვენ გáƒáƒ°áƒ§áƒ•ებით:' - remote_unfollow: - error: შეცდáƒáƒ›áƒ - title: სáƒáƒ—áƒáƒ£áƒ ი - unfollowed: დáƒáƒ“ევნების შეწყვეტრsessions: activity: ბáƒáƒšáƒ áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რbrowser: ბრáƒáƒ£áƒ–ერი diff --git a/config/locales/kk.yml b/config/locales/kk.yml index c6212c378fd33925b54416da0fb61617cd34d726..49cc18a4cce8c043418c9248ad4e3f1ebdea64eb 100644 --- a/config/locales/kk.yml +++ b/config/locales/kk.yml @@ -10,10 +10,6 @@ kk: contact_missing: Бапталмаған contact_unavailable: БелгіÑіз documentation: Құжаттама - extended_description_html: | - <h3>Ережелерге арналған жақÑÑ‹ орын</h3> - <p>Әлі ештеңе жазылмапты</p> - generic_description: "%{domain} желідегі Ñерверлердің бірі" hosted_on: Mastodon орнатылған %{domain} доменінде learn_more: Көбірек білу privacy_policy: ҚұпиÑлылық ÑаÑÑаты @@ -436,21 +432,8 @@ kk: no_status_selected: Бірде-бір ÑÑ‚Ð°Ñ‚ÑƒÑ Ó©Ð·Ð³ÐµÑ€Ð³ÐµÐ½ жоқ, Ñебебі ештеңе таңдалмады title: Ðккаунт ÑтатуÑтары with_media: Медиамен - subscriptions: - callback_url: Callbаck URL - confirmed: Confirmеd - expires_in: Expirеs in - last_delivery: Last dеlivery - title: WеbSub - topic: Tоpic tags: - accounts: Accоunts - hidden: Hiddеn - hide: Hidе from directory - name: Hаshtag title: Hashtаgs - unhide: Shоw in directory - visible: Visiblе title: Administrаtion warning_presets: add_new: Add nеw @@ -479,7 +462,6 @@ kk: your_token: Your access tokеn auth: change_password: ҚұпиÑÑөз - confirm_email: Еmаil құптау delete_account: Ðккаунт өшіру delete_account_html: Ðккаунтыңызды жойғыңыз келÑе, <a href="%{path}">мына жерді</a> баÑыңыз. Сізден раÑтау Ñұралатын болады. didnt_get_confirmation: РаÑтау хаты келмеді ме? @@ -524,26 +506,18 @@ kk: x_months: "%{count}ай" x_seconds: "%{count}Ñек" deletes: - bad_password_msg: Болмады ма, хакер бала? ҚұпиÑÑөз қате confirm_password: Қазіргі құпиÑÑөзіңізді жазыңыз - description_html: This will <strong>permanently, irreversibly</strong> remove content from your account аnd deactivate it. Your username will remain reserved to prevent future impersonations. proceed: Ðккаунт өшіру success_msg: Ðккаунтыңыз Ñәтті өшірілді - warning_html: Only deletion of content from this particular server is guaranteed. Content that has been widely sharеd is likely to leave traces. Offline servers and servers that have unsubscribed from your updates will not update their databases. - warning_title: Бөлінген мазмұнның қол жетімділігі directories: directory: Профильдер каталогы - enabled: Каталогтағы тізімге ендіңіз. - enabled_but_waiting: Каталогта көрінгіңіз келетінін түÑінеміз, бірақ ол үшін кем дегенде (%{min_followers}) оқырманыңыз болуы қажет. explanation: Қолданушыларды қызығушылықтарына қарай реттеу explore_mastodon: "%{title} шарлау" - how_to_enable: Сіз қазіргі уақытта каталогқа қоÑылмағанÑыз. Төменде қоÑылуға болады. Ðрнайы био мәтініндегі Ñ…Ñштегтерді қолданыңыз! - people: - one: "%{count} адам" - other: "%{count} адам" errors: + '400': The request you submitted was invalid or malformed. '403': Бұны көру үшін Ñізде Ñ€Ò±Ò›Ñат жоқ. '404': Сіз іздеген бет бұл жерде ÐµÐ¼ÐµÑ ÐµÐºÐµÐ½. + '406': This page is not available in the requested format. '410': Сіз іздеген бет қазір жоқ екен. '422': content: ҚауіпÑіздік раÑтауы қате. кукилерді блоктағанÑыз ба? @@ -552,6 +526,7 @@ kk: '500': content: КешірерÑіз, бірақ қазір бір қате пайда болып тұр. title: Бұл бет Ð´Ò±Ñ€Ñ‹Ñ ÐµÐ¼ÐµÑ ÐµÐºÐµÐ½ + '503': The page could not be served due to a temporary server failure. noscript_html: Mastodon веб қоÑымшаÑын қолдану үшін, JavaScript қоÑыңыз. Болмай жатÑа, <a href="%{apps_path}">мына қоÑымшаларды</a> қоÑып көріңіз, Mastodon қолдану үшін. exports: archive_takeout: @@ -645,9 +620,6 @@ kk: too_many: 4 файлдан артық қоÑылмайды migrations: acct: жаңа аккаунт үшін username@domain - currently_redirecting: 'Профиліңіз көшіріледі:' - proceed: Сақтау - updated_msg: Ðккаунт көшіруіңіз Ñәтті аÑқталды! moderation: title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ñ notification_mailer: @@ -726,10 +698,6 @@ kk: reply: proceed: Жауап жазу prompt: 'Сіз мына жазбаға жауап жазаÑыз:' - remote_unfollow: - error: Қате - title: Тақырыбы - unfollowed: Жазылудан Ð±Ð°Ñ Ñ‚Ð°Ñ€Ñ‚Ñ‹Ð»Ð´Ñ‹ scheduled_statuses: over_daily_limit: Сіз бір күндік %{limit} жазба лимитін тауыÑтыңыз over_total_limit: Сіз %{limit} жазба лимитін тауыÑтыңыз diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3f14d5df64e70359bde3ca8ff560c9a8ed8aac11..bae3d69bcac35674e724d21cc36b85686bd8110f 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1,7 +1,7 @@ --- ko: about: - about_hashtag_html: "<strong>#%{hashtag}</strong> ë¼ëŠ” 해시태그가 ë¶™ì€ ê³µê°œ 툿 입니다. ê°™ì€ ì—°í•©ì— ì†í•œ ìž„ì˜ì˜ ì¸ìŠ¤í„´ìŠ¤ì— ê³„ì •ì„ ìƒì„±í•˜ë©´ ë‹¹ì‹ ë„ ëŒ€í™”ì— ì°¸ì—¬í• ìˆ˜ 있습니다." + about_hashtag_html: "<strong>#%{hashtag}</strong> 해시태그가 ë¶™ì€ ê³µê°œ 툿 입니다. ê°™ì€ ì—°í•©ì— ì†í•œ ìž„ì˜ì˜ ì¸ìŠ¤í„´ìŠ¤ì— ê³„ì •ì„ ìƒì„±í•˜ë©´ ë‹¹ì‹ ë„ ëŒ€í™”ì— ì°¸ì—¬í• ìˆ˜ 있습니다." about_mastodon_html: ë§ˆìŠ¤í† ëˆì€ <em>오픈 소스 기반ì˜</em> 소셜 ë„¤íŠ¸ì›Œí¬ ì„œë¹„ìŠ¤ 입니다. ìƒìš© 플랫í¼ì˜ 대체로서 <em>분산형 구조</em>를 채íƒí•´, ì—¬ëŸ¬ë¶„ì˜ ëŒ€í™”ê°€ 한 íšŒì‚¬ì— ë…ì ë˜ëŠ” ê²ƒì„ ë°©ì§€í•©ë‹ˆë‹¤. ì‹ ë¢°í• ìˆ˜ 있는 ì¸ìŠ¤í„´ìŠ¤ë¥¼ ì„ íƒí•˜ì„¸ìš” — ì–´ë–¤ ì¸ìŠ¤í„´ìŠ¤ë¥¼ ê³ ë¥´ë”ë¼ë„, ëˆ„êµ¬ì™€ë„ ëŒ€í™”í• ìˆ˜ 있습니다. 누구나 ìžì‹ ë§Œì˜ ë§ˆìŠ¤í† ëˆ ì¸ìŠ¤í„´ìŠ¤ë¥¼ 만들 수 있으며, 아주 매ë„럽게 <em>소셜 네트워í¬</em>ì— ì°¸ê°€í• ìˆ˜ 있습니다. about_this: ì´ ì¸ìŠ¤í„´ìŠ¤ì— ëŒ€í•´ì„œ active_count_after: 활성 ì‚¬ìš©ìž @@ -17,13 +17,12 @@ ko: contact_unavailable: ì—†ìŒ discover_users: ìœ ì € 발견하기 documentation: 문서 - extended_description_html: | - <h3>ë£°ì„ ìž‘ì„±í•˜ëŠ” 장소</h3> - <p>ì•„ì§ ì„¤ëª…ì´ ìž‘ì„±ë˜ì§€ 않았습니다.</p> federation_hint_html: "%{instance}ì— ê³„ì •ì„ ë§Œë“œëŠ” 것으로 ëª¨ë“ ë§ˆìŠ¤í† ëˆ ì„œë²„, ê·¸ë¦¬ê³ í˜¸í™˜ ë˜ëŠ” ëª¨ë“ ì„œë²„ì˜ ì‚¬ìš©ìžë¥¼ 팔로우 í• ìˆ˜ 있습니다." - generic_description: "%{domain} ì€ ë„¤íŠ¸ì›Œí¬ì— 있는 한 서버입니다" get_apps: ëª¨ë°”ì¼ ì•± 사용해 보기 hosted_on: "%{domain}ì—서 호스팅 ë˜ëŠ” ë§ˆìŠ¤í† ëˆ" + instance_actor_flash: | + ì´ ê³„ì •ì€ ê°€ìƒì˜ actor로서 ê°œì¸ ìœ ì €ê°€ 아닌 서버 ìžì²´ë¥¼ 나타냅니다. + ì´ê²ƒì€ 페ë”ë ˆì´ì…˜ì„ 목ì 으로 사용 ë˜ë©° ì¸ìŠ¤í„´ìŠ¤ ì „ì²´ë¥¼ ì°¨ë‹¨í•˜ë ¤ 하지 않는 ì´ìƒ 차단하지 않아야 합니다, ê·¸ 경우ì—는 ë„ë©”ì¸ ì°¨ë‹¨ì„ ì‚¬ìš©í•˜ì„¸ìš”. learn_more: ìžì„¸ížˆ privacy_policy: ê°œì¸ì •ë³´ ì •ì±… see_whats_happening: 무슨 ì¼ì´ ì¼ì–´ë‚˜ëŠ” ì§€ 보기 @@ -34,12 +33,22 @@ ko: status_count_before: 툿 수 tagline: ì¹œêµ¬ë“¤ì„ íŒ”ë¡œìš° í•˜ê³ ìƒˆë¡œìš´ ì‚¬ëžŒë“¤ë„ ë§Œë‚˜ê¸° terms: ì´ìš©ì•½ê´€ + unavailable_content: ì´ìš© 불가능한 컨í…ì¸ + unavailable_content_description: + domain: 서버 + reason: ì´ìœ + rejecting_media: ì´ ì„œë²„ì˜ ë¯¸ë””ì–´ 파ì¼ë“¤ì€ 처리ë˜ì§€ ì•Šê³ ì¸ë„¤ì¼ë˜í•œ ë³´ì´ì§€ 않게 ë©ë‹ˆë‹¤. 수ë™ìœ¼ë¡œ í´ë¦í•˜ì—¬ 해당 서버로 가게 ë©ë‹ˆë‹¤. + silenced: ì´ ì„œë²„ì˜ ê²Œì‹œë¬¼ì€ ìž‘ì„±ìžë¥¼ 팔로우 한 경우ì—ë§Œ 홈 í”¼ë“œì— ë‚˜íƒ€ë‚˜ë©° ì´ë¥¼ ì œì™¸í•œ ì–´ë””ì—ë„ ë‚˜íƒ€ë‚˜ì§€ 않습니다. + suspended: ì´ ì„œë²„ì˜ ì•„ë¬´ë„ íŒ”ë¡œìš° í• ìˆ˜ 없으며, ì–´ë–¤ ë°ì´í„°ë„ 처리ë˜ê±°ë‚˜ ì €ìž¥ ë˜ì§€ ì•Šê³ ë°ì´í„°ê°€ êµí™˜ ë˜ì§€ë„ 않습니다. + unavailable_content_html: ë§ˆìŠ¤í† ëˆì€ ì¼ë°˜ì 으로 ì—°í•©ìš°ì£¼ì— ìžˆëŠ” ì–´ë–¤ ì„œë²„ì˜ ìœ ì €ì™€ë„ ê²Œì‹œë¬¼ì„ ë³´ê³ ì‘ë‹µì„ í• ìˆ˜ 있ë„ë¡ í—ˆìš©í•©ë‹ˆë‹¤. ë‹¤ìŒ í•ëª©ë“¤ì€ íŠ¹ì •í•œ ì„œë²„ì— ëŒ€í•´ 만들어 ì§„ 예외사í•입니다. user_count_after: other: 명 user_count_before: ì‚¬ìš©ìž ìˆ˜ what_is_mastodon: ë§ˆìŠ¤í† ëˆì´ëž€? accounts: choices_html: "%{name}ì˜ ì¶”ì²œ:" + endorsements_hint: ë‚´ê°€ 팔로우 í•˜ê³ ìžˆëŠ” ì‚¬ëžŒë“¤ì„ ì—¬ê¸°ì— ì¶”ì²œ í• ìˆ˜ 있습니다. + featured_tags_hint: íŠ¹ì •í•œ í•´ì‹œíƒœê·¸ë“¤ì„ ì—¬ê¸°ì— í‘œì‹œë˜ë„ë¡ í• ìˆ˜ 있습니다. follow: 팔로우 followers: other: 팔로워 @@ -50,6 +59,7 @@ ko: media: 미디어 moved_html: "%{name}ì€ %{new_profile_link}으로 ì´ë™ë˜ì—ˆìŠµë‹ˆë‹¤:" network_hidden: ì´ ì •ë³´ëŠ” ì‚¬ìš©í• ìˆ˜ 없습니다 + never_active: ì—†ìŒ nothing_here: 아무 ê²ƒë„ ì—†ìŠµë‹ˆë‹¤! people_followed_by: "%{name} ë‹˜ì´ íŒ”ë¡œìš° ì¤‘ì¸ ê³„ì •" people_who_follow: "%{name} ë‹˜ì„ íŒ”ë¡œìš° ì¤‘ì¸ ê³„ì •" @@ -179,6 +189,7 @@ ko: username: ì•„ì´ë”” warn: ê²½ê³ web: 웹 + whitelisted: 허용 ëª©ë¡ action_logs: actions: assigned_to_self_report: "%{name}ì´ ë¦¬í¬íЏ %{target}ì„ ìžì‹ ì—게 í• ë‹¹í–ˆìŠµë‹ˆë‹¤" @@ -214,19 +225,24 @@ ko: deleted_status: "(ì‚ì œë¨)" title: ê°ì‚¬ ê¸°ë¡ custom_emojis: + assign_category: 분류 ì§€ì • by_domain: ë„ë©”ì¸ copied_msg: 성공ì 으로 ì—ëª¨ì§€ì˜ ë¡œì»¬ ë³µì‚¬ë³¸ì„ ìƒì„±í–ˆìŠµë‹ˆë‹¤ copy: 복사 copy_failed_msg: ì—ëª¨ì§€ì˜ ë¡œì»¬ ë³µì‚¬ë³¸ì„ ë§Œë“œëŠ” ë° ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤ + create_new_category: 분류 ìƒì„± created_msg: ì—모지가 성공ì 으로 ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤! delete: ì‚ì œ destroyed_msg: ì—모지가 성공ì 으로 ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤! disable: 비활성화 + disabled: 비활성 disabled_msg: 성공ì 으로 비활성화하였습니다 emoji: ì—모지 enable: 활성화 + enabled: í™œì„±ë¨ enabled_msg: 성공ì 으로 활성화하였습니다 image_hint: 50KB ì´í•˜ì˜ PNG + list: ëª©ë¡ listed: 목ë¡ì— 실림 new: title: 새 커스텀 ì—모지 추가 @@ -234,11 +250,14 @@ ko: shortcode: ì§§ì€ ì½”ë“œ shortcode_hint: 최소 2글ìž, ì˜ë¬¸ìž, 숫ìž, _ë§Œ 사용 가능 title: 커스텀 ì—모지 + uncategorized: 분류ë˜ì§€ ì•ŠìŒ + unlist: 목ë¡ì—서 ì œê±° unlisted: 목ë¡ì— ì—†ìŒ update_failed_msg: ì—모지를 ì—…ë°ì´íЏ í• ìˆ˜ 없습니다 updated_msg: ì—모지가 성공ì 으로 ì—…ë°ì´íЏ ë˜ì—ˆìŠµë‹ˆë‹¤! upload: 업로드 dashboard: + authorized_fetch_mode: ì¸ì¦ ëœ íŽ˜ì¹˜ 모드 backlog: 미처리 ëœ ìž‘ì—… config: ì„¤ì • feature_deletions: ê³„ì • ì‚ì œ @@ -246,10 +265,13 @@ ko: feature_profile_directory: 프로필 ë””ë ‰í† ë¦¬ feature_registrations: 가입 feature_relay: ì—°í•© ë¦´ë ˆì´ + feature_spam_check: 안티 스팸 feature_timeline_preview: 타임ë¼ì¸ 미리보기 features: 기능 hidden_service: ížˆë“ ì„œë¹„ìŠ¤ì™€ì˜ ì—°í•© open_reports: 미해결 ì‹ ê³ + pending_tags: 심사를 기다리는 해시태그 + pending_users: 심사를 기다리는 ìœ ì € recent_users: 최근 가입 한 ìœ ì € search: ì „ë¬¸ 검색 single_user_mode: 싱글 ìœ ì € 모드 @@ -261,11 +283,18 @@ ko: week_interactions: ì´ë²ˆ ì£¼ì˜ ìƒí˜¸ìž‘ìš© week_users_active: ì´ë²ˆ ì£¼ì˜ í™œì„± ì‚¬ìš©ìž week_users_new: ì´ë²ˆ ì£¼ì˜ ì‹ ê·œ ìœ ì € + whitelist_mode: í™”ì´íŠ¸ë¦¬ìŠ¤íŠ¸ 모드 + domain_allows: + add_new: 허용 ëœ ë„ë©”ì¸ + created_msg: ë„ë©”ì¸ì´ 성공ì 으로 허용 목ë¡ì— 추가ë˜ì—ˆìŠµë‹ˆë‹¤ + destroyed_msg: ë„ë©”ì¸ì´ 허용 목ë¡ì—서 ì œê±°ë˜ì—ˆìŠµë‹ˆë‹¤ + undo: 허용 목ë¡ì—서 ì œì™¸ domain_blocks: add_new: ë„ë©”ì¸ ì°¨ë‹¨ 추가하기 created_msg: ë„ë©”ì¸ ì°¨ë‹¨ 처리를 완료했습니다 destroyed_msg: ë„ë©”ì¸ ì°¨ë‹¨ì´ í•´ì œë˜ì—ˆìŠµë‹ˆë‹¤ domain: ë„ë©”ì¸ + edit: ë„ë©”ì¸ ì°¨ë‹¨ ìˆ˜ì • existing_domain_block_html: ì´ë¯¸ %{name}ì— ëŒ€í•œ ë” ê°•ë ¥í•œ ì œí•œì´ ê±¸ë ¤ 있습니다, <a href="%{unblock_url}">차단 í•´ì œ</a>를 ë¨¼ì € 해야 합니다. new: create: 차단 추가 @@ -278,6 +307,10 @@ ko: silence: 침묵 suspend: ì •ì§€ title: 새로운 ë„ë©”ì¸ ì°¨ë‹¨ + private_comment: 비공개 ì£¼ì„ + private_comment_hint: ì´ ë„ë©”ì¸ ì œí•œì— ëŒ€í•œ 주ì„ì€ ëª¨ë”ë ˆì´í„°ë¥¼ 위해 ë‚´ë¶€ì 으로 사용 ë©ë‹ˆë‹¤. + public_comment: 공개 ì£¼ì„ + public_comment_hint: ì´ ë„ë©”ì¸ ì œí•œì— ëŒ€í•œ 공개ì ì¸ ì£¼ì„, ë„ë©”ì¸ ì œí•œ 공개를 활성화 한 경우 보여집니다. reject_media: 미디어 íŒŒì¼ ê±°ë¶€í•˜ê¸° reject_media_hint: ë¡œì»¬ì— ì €ìž¥ëœ ë¯¸ë””ì–´ 파ì¼ì„ ì‚ì œí•˜ê³ , ì´í›„ë¡œë„ ë‹¤ìš´ë¡œë“œë¥¼ 거부합니다. ì •ì§€ì™€ëŠ” 관계 없습니다 reject_reports: ì‹ ê³ ê±°ë¶€ @@ -296,6 +329,7 @@ ko: title: "%{domain}ì˜ ë„ë©”ì¸ ì°¨ë‹¨ì„ í•´ì œ" undo: 실행 취소 undo: ë„ë©”ì¸ ì°¨ë‹¨ 취소 + view: ë„ë©”ì¸ ì°¨ë‹¨ 보기 email_domain_blocks: add_new: 새로 추가 created_msg: ì´ë©”ì¼ ë„ë©”ì¸ ì°¨ë‹¨ ê·œì¹™ì„ ìƒì„±í–ˆìŠµë‹ˆë‹¤ @@ -318,6 +352,8 @@ ko: all: ëª¨ë‘ limited: ì œí•œë¨ title: 모ë”ë ˆì´ì…˜ + private_comment: 비공개 ì£¼ì„ + public_comment: 공개 ì£¼ì„ title: ì—°í•© total_blocked_by_us: 우리ì—게 차단 ë¨ total_followed_by_them: 우리를 팔로우 @@ -347,6 +383,7 @@ ko: pending: ë¦´ë ˆì´ì˜ ìŠ¹ì¸ ëŒ€ê¸°ì¤‘ save_and_enable: ì €ìž¥í•˜ê³ í™œì„±í™” setup: ë¦´ë ˆì´ ì—°ê²° ì„¤ì • + signatures_not_enabled: 시í어모드나 í™”ì´íŠ¸ë¦¬ìŠ¤íŠ¸ëª¨ë“œë¥¼ ì‚¬ìš©í•˜ê³ ìžˆë‹¤ë©´ ë¦´ë ˆì´ëŠ” ì œëŒ€ë¡œ ë™ìž‘하지 ì•Šì„ ê²ƒìž…ë‹ˆë‹¤ status: ìƒíƒœ title: ë¦´ë ˆì´ report_notes: @@ -395,6 +432,16 @@ ko: custom_css: desc_html: ëª¨ë“ íŽ˜ì´ì§€ì— ì ìš©í• CSS title: 커스텀 CSS + default_noindex: + desc_html: ì´ ì„¤ì •ì„ ë°”ê¾¸ì§€ ì•Šì€ ëª¨ë“ ìœ ì €ë“¤ì—게 ì ìš© ë©ë‹ˆë‹¤ + title: ìœ ì €ë“¤ì´ ê¸°ë³¸ì 으로 ê²€ìƒ‰ì—”ì§„ì— ì¸ë±ì‹± ë˜ì§€ 않ë„ë¡ í•©ë‹ˆë‹¤ + domain_blocks: + all: 모ë‘ì—게 + disabled: 아무ì—ê²Œë„ ì•ˆ 함 + title: ë„ë©”ì¸ ì°¨ë‹¨ 보여주기 + users: ë¡œê·¸ì¸ í•œ ìœ ì €ì—게 + domain_blocks_rationale: + title: ì‚¬ìœ ë³´ì—¬ì£¼ê¸° hero: desc_html: í”„ë¡ íŠ¸íŽ˜ì´ì§€ì— 표시 ë©ë‹ˆë‹¤. 최소 600x100í”½ì…€ì„ ê¶Œìž¥í•©ë‹ˆë‹¤. 만약 ì„¤ì •ë˜ì§€ 않았다면, ì„œë²„ì˜ ì¸ë„¤ì¼ì´ 사용 ë©ë‹ˆë‹¤ title: 히어로 ì´ë¯¸ì§€ @@ -415,7 +462,7 @@ ko: desc_html: ì‹ ê·œ 등ë¡ì„ 받지 ì•Šì„ ë•Œ í”„ë¡ íŠ¸ 페ì´ì§€ì— 표시ë©ë‹ˆë‹¤. HTML 태그를 ì‚¬ìš©í• ìˆ˜ 있습니다 title: ì‹ ê·œ ë“±ë¡ ì •ì§€ 시 메시지 deletion: - desc_html: ìœ ì €ê°€ ìžì‹ ì˜ ê³„ì •ì„ ì‚ì œí• ìˆ˜ 있ë„ë¡ ì„¤ì •í•©ë‹ˆë‹¤ + desc_html: ìœ ì €ê°€ ìžì‹ ì˜ ê³„ì •ì„ ì‚ì œí• ìˆ˜ 있ë„ë¡ í—ˆìš©í•©ë‹ˆë‹¤ title: ê³„ì • ì‚ì œë¥¼ 허가함 min_invite_role: disabled: ì•„ë¬´ë„ ëª» 하게 @@ -445,6 +492,9 @@ ko: desc_html: ë‹¹ì‹ ì€ ë…ìžì ì¸ ê°œì¸ì •ë³´ 취급 방침ì´ë‚˜ ì´ìš©ì•½ê´€, ê·¸ ì™¸ì˜ ë²•ì 근거를 ìž‘ì„±í• ìˆ˜ 있습니다. HTML태그를 ì‚¬ìš©í• ìˆ˜ 있습니다 title: 커스텀 서비스 ì´ìš© 약관 site_title: 서버 ì´ë¦„ + spam_check_enabled: + desc_html: ë§ˆìŠ¤í† ëˆì€ ë°˜ë³µëœ ë©”ì‹œì§€ ë“±ì˜ ì¸¡ì •ê°’ì— ë”°ë¼ ìžë™ìœ¼ë¡œ ê³„ì •ì„ ì¹¨ë¬µ, ì‹ ê³ í• ìˆ˜ 있습니다. 위양성(False-positive)ì´ ì¡´ìž¬í• ìˆ˜ 있습니다. + title: 안티 스팸 thumbnail: desc_html: OpenGraph와 APIì˜ ë¯¸ë¦¬ë³´ê¸°ë¡œ 사용 ë©ë‹ˆë‹¤. 1200x630pxì„ ê¶Œìž¥í•©ë‹ˆë‹¤ title: 서버 ì¸ë„¤ì¼ @@ -452,12 +502,19 @@ ko: desc_html: 랜딩 페ì´ì§€ì— 공개 타임ë¼ì¸ì„ 표시합니다 title: 타임ë¼ì¸ 프리뷰 title: 사ì´íЏ ì„¤ì • + trendable_by_default: + desc_html: ì´ì „ì— ë¹„í—ˆìš© ë˜ì§€ ì•Šì€ í•´ì‹œíƒœê·¸ë“¤ì— ì˜í–¥ì„ 미칩니다 + title: 해시태그가 ì‚¬ì „ 리뷰 ì—†ì´ íŠ¸ë Œë“œì— ì˜¬ë¼ê°ˆ 수 있ë„ë¡ í—ˆìš© + trends: + desc_html: 리뷰를 거친 해시태그를 ìœ í–‰í•˜ëŠ” í•´ì‹œíƒœê·¸ì— ê³µê°œì 으로 ë³´ì—¬ì¤ë‹ˆë‹¤ + title: ìœ í–‰í•˜ëŠ” 해시태그 statuses: back_to_account: ê³„ì •ìœ¼ë¡œ ëŒì•„가기 batch: delete: ì‚ì œ nsfw_off: NSFW ë„기 nsfw_on: NSFW 켜기 + deleted: ì‚ì œë¨ failed_to_execute: ì‹¤í–‰ì„ ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤ media: title: 미디어 @@ -465,21 +522,24 @@ ko: no_status_selected: 아무 ê²ƒë„ ì„ íƒ ë˜ì§€ 않아 아무 ê²ƒë„ ë°”ë€Œì§€ 않았습니다 title: ê³„ì • 툿 with_media: 미디어 ìžˆìŒ - subscriptions: - callback_url: 콜백 URL - confirmed: 확ì¸ë¨ - expires_in: 기한 - last_delivery: 최종 발송 - title: WebSub - topic: í† í”½ tags: - accounts: ê³„ì •ë“¤ - hidden: ìˆ¨ê²¨ì§ - hide: ë””ë ‰í† ë¦¬ì—서 숨기기 + accounts_today: ì˜¤ëŠ˜ì˜ ìˆœ ì‚¬ìš©ìž + accounts_week: ê¸ˆì£¼ì˜ ìˆœ ì‚¬ìš©ìž + breakdown: 소스별 ì˜¤ëŠ˜ì˜ ì‚¬ìš©ëŸ‰ ë¶„ì„ + context: 문맥 + directory: ë””ë ‰í† ë¦¬ì— ìžˆìŒ + in_directory: ë””ë ‰í† ë¦¬ì— %{count}ê°œ ìžˆìŒ + last_active: 최근 í™œë™ + most_popular: ìµœê³ ì¸ê¸° + most_recent: ìµœì‹ name: 해시태그 + review: 심사 ìƒíƒœ + reviewed: 심사 ë¨ title: 해시태그 - unhide: ë””ë ‰í† ë¦¬ì— í‘œì‹œ - visible: ë³´ì—¬ì§ + trending_right_now: 지금 ìœ í–‰ 중 + unique_uses_today: 오늘 %{count}ëª…ì´ í¬ìŠ¤íŒ… + unreviewed: 심사 ë˜ì§€ ì•ŠìŒ + updated_msg: 해시태그 ì„¤ì •ì´ ì„±ê³µì 으로 ê°±ì‹ ë˜ì—ˆìŠµë‹ˆë‹¤ title: 관리 warning_presets: add_new: 새로 추가 @@ -495,11 +555,21 @@ ko: body: "%{reporter} ê°€ %{target} 를 ì‹ ê³ í–ˆìŠµë‹ˆë‹¤" body_remote: "%{domain}ì˜ ëˆ„êµ°ê°€ê°€ %{target}ì„ ì‹ ê³ í–ˆìŠµë‹ˆë‹¤" subject: "%{instance} ì— ìƒˆ ì‹ ê³ ë“±ë¡ë¨ (#%{id})" + new_trending_tag: + body: '오늘 #%{name} 해시태그가 ìœ í–‰í•˜ê³ ìžˆìŠµë‹ˆë‹¤, 하지만 심사 ëœ ì ì´ ì—†ìŠµë‹ˆë‹¤. 허용하지 않는 한 공개ì 으로 나타나지 않습니다. ë˜ëŠ” 그냥 ì €ìž¥ì„ ëˆŒëŸ¬ ë”ì´ìƒ 나타나지 않게 í• ìˆ˜ 있습니다.' + subject: 새 해시태그가 %{instance}ì—서 심사 대기 중입니다(#%{name}) + aliases: + add_new: ë³„ì¹ ë§Œë“¤ê¸° + created_msg: 새 별ì¹ì´ 성공ì 으로 만들어졌습니다. ì´ì œ 기존 ê³„ì •ì—서 ì´ì£¼ë¥¼ ì‹œìž‘í• ìˆ˜ 있습니다. + deleted_msg: 성공ì 으로 별ì¹ì„ ì‚ì œí–ˆìŠµë‹ˆë‹¤. 해당 ê³„ì •ì—서 ì´ ê³„ì •ìœ¼ë¡œì˜ ì´ì£¼ëŠ” ë”ì´ìƒ 가능하지 않습니다. + hint_html: 다른 ê³„ì •ì—서 ì´ ê³„ì •ìœ¼ë¡œ 옮기길 ì›í•˜ëŠ” 경우, 여기ì—서 별ì¹ì„ 만들 수 있습니다, 기존 ê³„ì •ì˜ íŒ”ë¡œì›Œë¥¼ ì´ìª½ìœ¼ë¡œ ì˜®ê¸°ê³ ì‹¶ì€ ê²½ìš° 필요한 ê³¼ì •ìž…ë‹ˆë‹¤. ì´ í–‰ë™ ìžì²´ëŠ” <strong>í•´ë¡ì§€ ì•Šê³ ë˜ëŒë¦¬ê¸°ê°€ 가능합니다</strong>.<strong>ê³„ì • ì´ì£¼ëŠ” ì´ì „ ê³„ì •ì—서 착수하게 ë©ë‹ˆë‹¤</strong> + remove: ë³„ì¹ ì—°ê²° ëŠê¸° appearance: advanced_web_interface: ê³ ê¸‰ 웹 ì¸í„°íŽ˜ì´ìФ advanced_web_interface_hint: 'í™”ë©´ì˜ ê°€ë¡œíì„ ê°€ë“ ì±„ìš°ê³ ì‹¶ë‹¤ë©´, ê³ ê¸‰ 웹 ì¸í„°íŽ˜ì´ìŠ¤ëŠ” 한 ë²ˆì— ì—¬ëŸ¬ ì •ë³´ë¥¼ ë³¼ 수 있ë„ë¡ ì—¬ëŸ¬ ì»¬ëŸ¼ì„ ì„¤ì •í• ìˆ˜ 있ë„ë¡ í•©ë‹ˆë‹¤: 홈, 알림, 연합타임ë¼ì¸, 리스트, 해시태그 등' animations_and_accessibility: ì• ë‹ˆë©”ì´ì…˜ê³¼ ì ‘ê·¼ì„± confirmation_dialogs: í™•ì¸ ëŒ€í™”ìƒìž + discovery: 발견하기 sensitive_content: 민ê°í•œ ë‚´ìš© application_mailer: notification_preferences: ë©”ì¼ ì„¤ì • 변경 @@ -520,9 +590,13 @@ ko: apply_for_account: 가입 ìš”ì²í•˜ê¸° change_password: 패스워드 checkbox_agreement_html: <a href="%{rules_path}" target="_blank">서버 규칙</a>ê³¼ <a href="%{terms_path}" target="_blank">ì´ìš©ì•½ê´€</a>ì— ë™ì˜í•©ë‹ˆë‹¤ - confirm_email: í™•ì¸ ë©”ì¼ ìŠ¹ì¸ + checkbox_agreement_without_rules_html: <a href="%{terms_path}" target="_blank">ì´ìš© 약관</a>ì— ë™ì˜í•©ë‹ˆë‹¤ delete_account: ê³„ì • ì‚ì œ delete_account_html: ê³„ì •ì„ ì‚ì œí•˜ê³ ì‹¶ì€ ê²½ìš°, <a href="%{path}">여기서</a> ì‚ì œí• ìˆ˜ 있습니다. ì‚ì œ ì „ í™•ì¸ í™”ë©´ì´ í‘œì‹œë©ë‹ˆë‹¤. + description: + prefix_invited_by_user: "@%{name} ë‹˜ì´ ë‹¹ì‹ ì„ ì´ ë§ˆìŠ¤í† ëˆ ì„œë²„ë¡œ 초대했습니다!" + prefix_sign_up: ë§ˆìŠ¤í† ëˆì— 가입하세요! + suffix: ê³„ì • 하나로 ì‚¬ëžŒë“¤ì„ íŒ”ë¡œìš° í•˜ê³ , ê²Œì‹œë¬¼ì„ ìž‘ì„±í•˜ë©° ë§ˆìŠ¤í† ëˆì„ í¬í•¨í•œ 다른 ì–´ë–¤ ì„œë²„ì˜ ìœ ì €ì™€ë„ ë©”ì‹œì§€ë¥¼ ì£¼ê³ ë°›ì„ ìˆ˜ 있습니다! didnt_get_confirmation: í™•ì¸ ë©”ì¼ì„ 받지 못하셨습니까? forgot_password: 비밀번호를 잊어버리셨습니까? invalid_reset_password_token: 암호 리셋 í† í°ì´ 올바르지 못하거나 ê¸°ê°„ì´ ë§Œë£Œë˜ì—ˆìŠµë‹ˆë‹¤. 다시 ìš”ì²í•´ì£¼ì„¸ìš”. @@ -540,6 +614,16 @@ ko: reset_password: 암호 ìž¬ì„¤ì • security: 보안 set_new_password: 새 암호 + setup: + email_below_hint_html: ì•„ëž˜ì˜ ì´ë©”ì¼ ê³„ì •ì´ ì˜¬ë°”ë¥´ì§€ ì•Šì„ ê²½ìš°, 여기서 ë³€ê²½í•˜ê³ ìƒˆ í™•ì¸ ë©”ì¼ì„ ë°›ì„ ìˆ˜ 있습니다. + email_settings_hint_html: í™•ì¸ ë©”ì¼ì´ %{email}로 보내졌습니다. ì´ë©”ì¼ ì£¼ì†Œê°€ 올바르지 ì•Šì€ ê²½ìš°, ê³„ì • ì„¤ì •ì—서 변경하세요. + title: ì„¤ì • + status: + account_status: ê³„ì • ìƒíƒœ + confirming: ì´ë©”ì¼ í™•ì¸ ê³¼ì •ì´ ì™„ë£Œë˜ê¸°ë¥¼ 기다리는 중. + functional: ê³„ì •ì´ ì™„ë²½ì´ ìž‘ë™í•©ë‹ˆë‹¤. + pending: ë‹¹ì‹ ì˜ ê°€ìž… ì‹ ì²ì€ ìŠ¤íƒœí”„ì˜ ê²€ì‚¬ë¥¼ 위해 대기중입니다. ì´ê²ƒì€ ì‹œê°„ì´ ë‹¤ì†Œ 소요ë©ë‹ˆë‹¤. 가입 ì‹ ì²ì´ ìŠ¹ì¸ ë 경우 ì´ë©”ì¼ì„ 받게 ë©ë‹ˆë‹¤. + redirecting_to: ê³„ì •ì´ %{acct}로 리다ì´ë ‰íЏ 중ì´ê¸° ë•Œë¬¸ì— ë¹„í™œì„± ìƒíƒœìž…니다. trouble_logging_in: ë¡œê·¸ì¸ í•˜ëŠ”ë° ë¬¸ì œê°€ 있나요? authorize_follow: already_following: ì´ë¯¸ ì´ ê³„ì •ì„ íŒ”ë¡œìš° í•˜ê³ ìžˆìŠµë‹ˆë‹¤ @@ -552,6 +636,11 @@ ko: return: ìœ ì € 프로필 보기 web: 웹으로 가기 title: "%{acct} 를 팔로우" + challenge: + confirm: ê³„ì† + hint_html: "<strong>íŒ:</strong> 한 시간 ë™ì•ˆ 다시 암호를 묻지 ì•Šì„ ê²ƒìž…ë‹ˆë‹¤." + invalid_password: ìž˜ëª»ëœ ì•”í˜¸ + prompt: 계ì†í•˜ë ¤ë©´ 암호 í™•ì¸ datetime: distance_in_words: about_x_hours: "%{count}시간" @@ -567,25 +656,33 @@ ko: x_months: "%{count}ì›”" x_seconds: "%{count}ì´ˆ" deletes: - bad_password_msg: 비밀번호가 올바르지 않습니다 + challenge_not_passed: ìž…ë ¥í•œ ì •ë³´ê°€ 올바르지 않습니다 confirm_password: ë³¸ì¸ í™•ì¸ì„ 위해 현재 사용 ì¤‘ì¸ ì•”í˜¸ë¥¼ ìž…ë ¥í•´ 주ì‹ì‹œì˜¤ - description_html: ê³„ì •ì— ì—…ë¡œë“œëœ ëª¨ë“ ì»¨í…ì¸ ê°€ ì‚ì œë˜ë©°, ê³„ì •ì€ ë¹„í™œì„±í™” ë©ë‹ˆë‹¤. ì´ê²ƒì€ ì˜êµ¬ì 으로 ì´ë£¨ì–´ì§€ëŠ” 것ì´ë¯€ë¡œ <strong>ë˜ëŒë¦´ 수 없습니다</strong>. ì‚¬ì¹ í–‰ìœ„ë¥¼ 방지하기 위해 ê°™ì€ ì•„ì´ë””로 다시 등ë¡í•˜ëŠ” ê²ƒì€ ë¶ˆê°€ëŠ¥í•©ë‹ˆë‹¤. + confirm_username: ì ˆì°¨ë¥¼ ì§„í–‰í•˜ë ¤ë©´ ë‹¹ì‹ ì˜ ì‚¬ìš©ìžëª…ì„ ìž…ë ¥í•˜ì„¸ìš” proceed: ê³„ì • ì‚ì œ success_msg: ê³„ì •ì´ ì„±ê³µì 으로 ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤ - warning_html: ì‚ì œê°€ 보장ë˜ëŠ” ê²ƒì€ ì´ ì„œë²„ ìƒì—ì„œì˜ ì»¨í…ì¸ ì— í•œí•©ë‹ˆë‹¤. 타 서버 등, ì™¸ë¶€ì— ë©€ë¦¬ ê³µìœ ëœ ì»¨í…ì¸ ëŠ” í”ì ì´ ë‚¨ì•„ ì‚ì œë˜ì§€ 않는 ê²½ìš°ë„ ìžˆìŠµë‹ˆë‹¤. ê·¸ë¦¬ê³ í˜„ìž¬ ì ‘ì†ì´ 불가능한 서버나, ì—…ë°ì´íŠ¸ë¥¼ 받지 않게 ëœ ì„œë²„ì— ëŒ€í•´ì„œëŠ” ì‚ì œê°€ ë°˜ì˜ë˜ì§€ ì•Šì„ ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. - warning_title: ê³µìœ ëœ ì»¨í…ì¸ ì— ëŒ€í•´ì„œ + warning: + before: '진행하기 ì „, 주ì˜ì‚¬í•ì„ ê¼¼ê¼¼ížˆ ì½ì–´ë³´ì„¸ìš”:' + caches: 다른 ì„œë²„ì— ìºì‹± ëœ ì •ë³´ë“¤ì€ ë‚¨ì•„ìžˆì„ ìˆ˜ 있습니다 + data_removal: ë‹¹ì‹ ì˜ ê²Œì‹œë¬¼ê³¼ 다른 ì •ë³´ë“¤ì€ ì˜êµ¬ì 으로 ì‚ì œ ë©ë‹ˆë‹¤ + email_change_html: ê³„ì •ì„ ì§€ìš°ì§€ ì•Šê³ ë„ <a href="%{path}">ì´ë©”ì¼ ì£¼ì†Œë¥¼ ìˆ˜ì •í• ìˆ˜ 있습니다</a> + email_contact_html: ì•„ì§ ë„착하지 않았다면, <a href="mailto:%{email}">%{email}</a>ì— ë©”ì¼ì„ ë³´ë‚´ ë„ì›€ì„ ìš”ì²í• 수 있습니다 + email_reconfirmation_html: ì•„ì§ í™•ì¸ ë©”ì¼ì´ ë„착하지 ì•Šì€ ê²½ìš°, <a href="%{path}">다시 ìš”ì²í• 수 있습니다</a> + irreversible: ê³„ì •ì„ ë³µêµ¬í•˜ê±°ë‚˜ 다시 ì‚¬ìš©í• ìˆ˜ 없게 ë©ë‹ˆë‹¤ + more_details_html: ë” ìžì„¸í•œ ì •ë³´ëŠ”, <a href="%{terms_path}">ê°œì¸ì •ë³´ ì •ì±…</a>ì„ ì°¸ê³ í•˜ì„¸ìš”. + username_available: ë‹¹ì‹ ì˜ ê³„ì •ëª…ì€ ë‹¤ì‹œ ì‚¬ìš©í• ìˆ˜ 있게 ë©ë‹ˆë‹¤ + username_unavailable: ë‹¹ì‹ ì˜ ê³„ì •ëª…ì€ ì•žìœ¼ë¡œ ì‚¬ìš©í• ìˆ˜ 없습니다 directories: directory: 프로필 ë””ë ‰í† ë¦¬ - enabled: ë‹¹ì‹ ì€ ë””ë ‰í„°ë¦¬ì— í‘œì‹œ ë˜ê³ 있습니다. - enabled_but_waiting: ë””ë ‰í„°ë¦¬ì— í‘œì‹œ ë˜ê¸° 위해서는 수ë™ìœ¼ë¡œ 참여해야 합니다, 하지만 ë””ë ‰í„°ë¦¬ì— í‘œì‹œ ë˜ê¸° 위한 최소 팔로워(%{min_followers})ìˆ˜ì— ë¯¸ì¹˜ì§€ 못했습니다. explanation: ê´€ì‹¬ì‚¬ì— ëŒ€í•œ ìœ ì €ë“¤ì„ ë°œê²¬í•©ë‹ˆë‹¤ explore_mastodon: "%{title} íƒì‚¬í•˜ê¸°" - how_to_enable: ì•„ì§ ë””ë ‰í„°ë¦¬ì— ì°¸ì—¬í•˜ì§€ 않았습니다. 아래ì—서 ì°¸ì—¬í• ìˆ˜ 있습니다. ë°”ì´ì˜¤ í…ìŠ¤íŠ¸ì— í•´ì‹œíƒœê·¸ë¥¼ 사용해 íŠ¹ì • 해시태그 ë””ë ‰í„°ë¦¬ì— í‘œì‹œ ë 수 있습니다! - people: - other: "%{count}명" + domain_validator: + invalid_domain: 올바른 ë„ë©”ì¸ ë„¤ìž„ì´ ì•„ë‹™ë‹ˆë‹¤ errors: + '400': ì œì¶œí•œ ìš”ì²ì´ 올바르지 않습니다. '403': ì´ íŽ˜ì´ì§€ë¥¼ í‘œì‹œí• ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤. '404': ë‹¹ì‹ ì´ ì°¾ìœ¼ë ¤ëŠ” 페ì´ì§€ëŠ” 존재하지 않습니다. + '406': ì´ íŽ˜ì´ì§€ëŠ” ìš”ì²í•œ ìžë£Œí˜•으로 ì œê³µë˜ì§€ 않습니다. '410': ë‹¹ì‹ ì´ ë³´ë ¤ëŠ” 페ì´ì§€ëŠ” ë”ì´ìƒ ì—¬ê¸°ì— ì¡´ìž¬í•˜ì§€ 않습니다. '422': content: 보안 ì¸ì¦ì— 실패했습니다. ì¿ í‚¤ë¥¼ ì°¨ë‹¨í•˜ê³ ìžˆì§„ 않습니까? @@ -594,6 +691,7 @@ ko: '500': content: 죄송합니다, ë”ê°€ 잘못 ë˜ì—ˆìŠµë‹ˆë‹¤. title: ì´ íŽ˜ì´ì§€ëŠ” 잘못ë˜ì—ˆìŠµë‹ˆë‹¤ + '503': ì´ íŽ˜ì´ì§€ëŠ” 임시ì ì¸ ì„œë²„ ë¬¸ì œë¡œ ì¸í•´ ì œê³µ ë 수 없습니다. noscript_html: ë§ˆìŠ¤í† ëˆì„ 사용하기 위해서는 ìžë°”스í¬ë¦½íŠ¸ë¥¼ 켜 주ì‹ì‹œì˜¤. 아니면 <a href="%{apps_path}">네ì´í‹°ë¸Œ 앱</a> 중 하나를 ì‚¬ìš©í• ìˆ˜ 있습니다. existing_username_validator: not_found: 해당 ìœ ì €ë„¤ìž„ì— ëŒ€í•œ 로컬 ìœ ì €ë¥¼ ì°¾ì„ ìˆ˜ 없습니다 @@ -617,6 +715,7 @@ ko: add_new: 추가 errors: limit: ì´ë¯¸ 추천 í•´ì‹œíƒœê·¸ì˜ ê°œìˆ˜ê°€ 최대입니다 + hint_html: "<strong>추천 해시태그가 무엇ì´ì£ ?</strong> ë‹¹ì‹ ì˜ ê³µê°œ 프로필 페ì´ì§€ì— ëˆˆì— ë„게 표현 ë˜ë©° ì‚¬ëžŒë“¤ì´ ê·¸ 해시태그를 í¬í•¨í•œ ë‹¹ì‹ ì˜ ê¸€ì„ ì°¾ì•„ ë³¼ 수 있ë„ë¡ í•©ë‹ˆë‹¤. 창작활ë™ì´ë‚˜ 긴 ê¸°ê°„ì„ ê°€ì§€ëŠ” 프로ì 트를 ì‰ ë”°ë¼ê°€ê¸°ì— ì¢‹ì€ ë„구입니다." filters: contexts: home: 홈 타임ë¼ì¸ @@ -637,10 +736,12 @@ ko: developers: ê°œë°œìž more: ë” ë³´ê¸°â€¦ resources: 리소스 + trending_now: 지금 ìœ í–‰ì¤‘ generic: all: ëª¨ë‘ changes_saved_msg: ì •ìƒì 으로 변경ë˜ì—ˆìŠµë‹ˆë‹¤! copy: 복사 + no_batch_actions_available: ì´ íŽ˜ì´ì§€ì—서 ìˆ˜í–‰í• ìˆ˜ 있는 ì¼ê´„ìž‘ì—…ì´ ì—†ìŠµë‹ˆë‹¤ order_by: 순서 save_changes: 변경 사í•ì„ ì €ìž¥ validation_errors: @@ -710,9 +811,34 @@ ko: too_many: 최대 4개까지 ì²¨ë¶€í• ìˆ˜ 있습니다 migrations: acct: 새 ê³„ì •ì˜ username@domain - currently_redirecting: 'ë‹¹ì‹ ì˜ í”„ë¡œíŒŒì¼ì€ 여기로 ë¦¬ë””ë ‰ì…˜ ë©ë‹ˆë‹¤:' - proceed: ì €ìž¥ - updated_msg: ê³„ì • ì´ë™ ì„¤ì •ì´ ì €ìž¥ë˜ì—ˆìŠµë‹ˆë‹¤! + cancel: ë¦¬ë””ë ‰ì…˜ 취소 + cancel_explanation: 리다ì´ë ‰íŠ¸ë¥¼ 취소하면 현재 ê³„ì •ì´ ë‹¤ì‹œ 활성화 ë©ë‹ˆë‹¤, 새 ê³„ì •ìœ¼ë¡œ ì´ë™í•œ 팔로워를 ë˜ì°¾ì„ 수는 없습니다. + cancelled_msg: 리다ì´ë ‰íŠ¸ë¥¼ 성공ì 으로 취소했습니다. + errors: + already_moved: ì´ë¯¸ ì´ë™í•œ ê³„ì •ê³¼ ë™ì¼í•©ë‹ˆë‹¤ + missing_also_known_as: ì´ ê³„ì •ì„ ì—으로 참조하지 ì•Šê³ ìžˆìŠµë‹ˆë‹¤ + move_to_self: 현재 ê³„ì •ì€ ì‚¬ìš©í• ìˆ˜ 없습니다 + not_found: ì°¾ì„ ìˆ˜ 없습니다 + on_cooldown: 쿨다운 기간입니다 + followers_count: ì´ì£¼ ë 팔로워들 + incoming_migrations: 다른 ê³„ì •ìœ¼ë¡œë¶€í„° 옮기기 + incoming_migrations_html: 다른 ê³„ì •ì„ ì´ ê³„ì •ìœ¼ë¡œ ì´ì£¼í•˜ê³ ì‹¶ì€ ê²½ìš°, ë¨¼ì € <a href="%{path}">ê³„ì • 별ì¹ì„ 만들어야 합니다</a>. + moved_msg: ë‹¹ì‹ ì˜ ê³„ì •ì€ %{acct}로 리다ì´ë ‰íЏ ë˜ê³ 있으며 íŒ”ë¡œì›Œë“¤ì€ ì´ì£¼ ë 것입니다. + not_redirecting: 현재 ì´ ê³„ì •ì€ ì–´ë””ë¡œë„ ë¦¬ë‹¤ì´ë ‰íЏ ë˜ê³ 있지 않습니다. + on_cooldown: ë‹¹ì‹ ì€ ìµœê·¼ì— ì´ë¯¸ ê³„ì •ì„ ì´ë™í–ˆìŠµë‹ˆë‹¤. ì´ ê¸°ëŠ¥ì€ %{count}ì¼ í›„ì— ë‹¤ì‹œ ì´ìš© 가능합니다. + past_migrations: 과거 ì´ì£¼ + proceed_with_move: 팔로워 ì´ë™ + redirecting_to: ë‹¹ì‹ ì˜ ê³„ì •ì€ %{acct} 로 리다ì´ë ‰íЏë©ë‹ˆë‹¤. + set_redirect: ë¦¬ë””ë ‰ì…˜ ì„¤ì • + warning: + backreference_required: 새 ê³„ì •ì€ ì´ ê³„ì •ìœ¼ë¡œ ì—참조를 하ë„ë¡ ì„¤ì •ë˜ì–´ 있어야 합니다 + before: '진행하기 ì „, 주ì˜ì‚¬í•ì„ ê¼¼ê¼¼ížˆ ì½ì–´ë³´ì„¸ìš”:' + cooldown: ì´ì£¼ ë’¤ì—는 새로운 ì´ì£¼ë¥¼ 하지 못하는 쿨다운 ê¸°ê°„ì´ ì¡´ìž¬í•©ë‹ˆë‹¤ + disabled_account: ì´ ê³„ì •ì€ ì™„ì „í•œ ì‚¬ìš©ì´ ë¶ˆê°€ëŠ¥í•˜ê²Œ ë©ë‹ˆë‹¤. 하지만, ë°ì´í„° 내보내기나 재활성화를 위해 ì ‘ê·¼í• ìˆ˜ 있습니다. + followers: ì´ í–‰ë™ì€ 현재 ê³„ì •ì˜ ëª¨ë“ íŒ”ë¡œì›Œë¥¼ 새 ê³„ì •ìœ¼ë¡œ ì´ë™ì‹œí‚µë‹ˆë‹¤ + only_redirect_html: ëŒ€ì‹ , <a href="%{path}">í”„ë¡œí•„ì— ë¦¬ë””ë ‰ì…˜ë§Œ 표시</a>í• ìˆ˜ 있습니다. + other_data: 다른 ì–´ë–¤ ë°ì´í„°ë„ ìžë™ì 으로 옮겨지지 ì•Šì„ ê²ƒìž…ë‹ˆë‹¤ + redirect: 현재 ê³„ì • í”„ë¡œí•„ì€ ë¦¬ë‹¤ì´ë ‰íЏ 알림과 함께 ì—…ë°ì´íЏ ë˜ë©° 검색ì—서 ì œì™¸ ë©ë‹ˆë‹¤ moderation: title: 모ë”ë ˆì´ì…˜ notification_mailer: @@ -807,10 +933,6 @@ ko: reply: proceed: 답장 ì§„í–‰ prompt: 'ì´ íˆ¿ì— ë‹µìž¥ì„ í•˜ë ¤ 합니다:' - remote_unfollow: - error: ì—러 - title: 타ì´í‹€ - unfollowed: ì–¸íŒ”ë¡œìš°ë¨ scheduled_statuses: over_daily_limit: ê·¸ ë‚ ì§œì— ëŒ€í•œ %{limit}ê°œì˜ ì˜ˆì•½ 툿 ì œí•œì„ ì´ˆê³¼í•©ë‹ˆë‹¤ over_total_limit: 예약 툿 ì œí•œ %{limit}ì„ ì´ˆê³¼í•©ë‹ˆë‹¤ @@ -859,6 +981,7 @@ ko: settings: account: ê³„ì • account_settings: ê³„ì • ì„¤ì • + aliases: ê³„ì • 별명 appearance: 외관 authorized_apps: ì¸ì¦ëœ ì• í”Œë¦¬ì¼€ì´ì…˜ back: ëŒì•„가기 @@ -896,6 +1019,8 @@ ko: private: 비공개 íˆ¿ì€ ê³ ì •ë 수 없습니다 reblog: 부스트는 ê³ ì •ë 수 없습니다 poll: + total_people: + other: "%{count}명" total_votes: other: "%{count}명 투표함" vote: 투표 @@ -913,6 +1038,8 @@ ko: pinned: ê³ ì •ëœ íˆ¿ reblogged: ë‹˜ì´ ë¶€ìŠ¤íŠ¸ 했습니다 sensitive_content: 민ê°í•œ 컨í…ì¸ + tags: + does_not_match_previous_name: ì´ì „ ì´ë¦„ê³¼ ë§žì§€ 않습니다 terms: body_html: | <h2>ê°œì¸ì •ë³´ ì •ì±…</h2> @@ -1030,7 +1157,9 @@ ko: disable: ë‹¹ì‹ ì˜ ê³„ì •ì´ ë™ê²° ëœ ë™ì•ˆ ë‹¹ì‹ ì˜ ê³„ì •ì€ ìœ ì§€ ë©ë‹ˆë‹¤. 하지만 ìž ê¸ˆì´ í’€ë¦´ 때까지 ë‹¹ì‹ ì€ ì•„ë¬´ ê²ƒë„ í• ìˆ˜ 없습니다. silence: ë‹¹ì‹ ì˜ ê³„ì •ì´ ì œí•œ ëœ ë™ì•ˆì—” ë‹¹ì‹ ì˜ íŒ”ë¡œì›Œ ì´ì™¸ì—” íˆ¿ì„ ë°›ì•„ ë³¼ 수 ì—†ê³ ê³µê°œ 리스팅ì—서 ì œì™¸ ë©ë‹ˆë‹¤. 하지만 다른 ì‚¬ëžŒë“¤ì€ ì—¬ì „ížˆ ë‹¹ì‹ ì„ íŒ”ë¡œìš° 가능합니다. suspend: ë‹¹ì‹ ì˜ ê³„ì •ì€ ì •ì§€ ë˜ì—ˆìœ¼ë©°, ëª¨ë“ íˆ¿ê³¼ 업로드 한 미디어가 서버ì—서 ì‚ì œ ë˜ì–´ ë˜ëŒë¦´ 수 없습니다. + get_in_touch: ì´ ë©”ì¼ì— 대해 답장해서 %{instance}ì˜ ìŠ¤íƒœí”„ì™€ ì—°ë½ í• ìˆ˜ 있습니다. review_server_policies: 서버 ì •ì±… ê²€í† í•˜ê¸° + statuses: '구체ì 으로:' subject: disable: ë‹¹ì‹ ì˜ ê³„ì • %{acct}ê°€ ë™ê²° ë˜ì—ˆìŠµë‹ˆë‹¤ none: "%{acct}ì—ê²Œì˜ ê²½ê³ " diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 2cf0b7c42bfd9b1acc055b856cc5fa760701c771..7a09bee594b90dccdbdaed1f51d3522ca5281b05 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -9,10 +9,6 @@ lt: contact: Kontaktai contact_missing: Nenustatyta documentation: Dokumentacija - extended_description_html: | - <h3>TaisyklÄ—s</h3> - <p>Ilgas apraÅ¡ymas dar nÄ—ra sudartyas</p> - generic_description: "%{domain} yra vienas serveris tinkle" hosted_on: Mastodon palaikomas naudojantis %{domain} talpinimu learn_more: Daugiau privacy_policy: Privatumo Politika @@ -419,21 +415,8 @@ lt: no_status_selected: Jokie statusai nebuvo pakeisti, nes niekas nepasirinkta title: Paskyros statusai with_media: Su medija - subscriptions: - callback_url: AtgalinÄ— URL - confirmed: Patvirtinta - expires_in: Pasibaigia - last_delivery: Paskutinis pristatymas - title: WebSub protokolas - topic: Tema tags: - accounts: Paskyros - hidden: PaslÄ—pti - hide: PaslÄ—pti iÅ¡ direktorijos - name: Saitažodis(#) title: Saitažodžiai(#) - unhide: Rodyti direktorijoje - visible: Matomas title: Administracija warning_presets: add_new: PridÄ—ti naujÄ… @@ -462,7 +445,6 @@ lt: your_token: JÅ«sų prieigos žetonas auth: change_password: Slaptažodis - confirm_email: Patvirtinti el paÅ¡tÄ… delete_account: IÅ¡trinti paskyrÄ… delete_account_html: Jeigu norite iÅ¡trinti savo paskyrÄ…, galite eiti <a href="%{path}">Äia</a>. JÅ«sų praÅ¡ys patvirtinti pasirinkimÄ…. didnt_get_confirmation: Negavote patvirtinimo instrukcijų? @@ -504,23 +486,18 @@ lt: x_months: "%{count}mÄ—n" x_seconds: "%{count}sek" deletes: - bad_password_msg: Geras bandymas, programiÅ¡iau! Neteisingas slaptažodis confirm_password: Kad patvirtintumÄ—te savo tapatybÄ™, įveskite dabartini slaptažodį - description_html: Tai <strong>be sugrąžinimo, visam laikui</strong> panaikins visa turini iÅ¡ JÅ«sų paskyros ir deaktyvuos jÄ…. JÅ«sų vartotojo vardas paliks rezervuotas, kad iÅ¡vengtumÄ—me tapatybÄ—s pavagimo ateityje. proceed: IÅ¡trinti paskyrÄ… success_msg: JÅ«sų paskyra sÄ—kmingai iÅ¡trinta - warning_html: Tiktai panaikinimas turinio iÅ¡ Å¡io serverio garantuotas. Turinys, kuris buvo vieÅ¡ai prieinamas ir dalinamas kituose serveriuose paliks pÄ—dsakus. Serveriai, kurie neseka jÅ«sų, kurie nÄ—ra tinkle, nepakeis savo duomenų sistemos. - warning_title: Platinamo turinio prieinamumas directories: directory: Profilio direktorija - enabled: JÅ«s esate rodomas Å¡ioje direktorijoje. - enabled_but_waiting: JÅ«s pasirinkote bÅ«ti įtrauktas į direktorija, bet jÅ«s neturite minimalaus sekÄ—jų skaiÄiaus (%{min_followers}), kad bÅ«tumÄ—te rodomas. explanation: Raskite vartotojus, remiantis tuo, kuo jie domisi explore_mastodon: NarÅ¡yti %{title} - how_to_enable: JÅ«s nesate prisijungÄ™s prie Å¡ios direktorijos. Galite prisijungti žemiau. Naudokite saitažodžius savo biografiniame tekste, kad bÅ«tumÄ—te rastas naudojantis specifinius saitažodžius! errors: + '400': The request you submitted was invalid or malformed. '403': JÅ«s neturie prieigos matyti Å¡iam puslapiui. '404': Puslapis nerastas. + '406': This page is not available in the requested format. '410': Puslapis neegzistuoja. '422': content: Apsaugos patvirtinmas klaidingas. Ar jÅ«s blokuojate sausainius? @@ -529,6 +506,7 @@ lt: '500': content: AtsipraÅ¡ome, taÄiau mÅ«sų pusÄ—je įvyko klaida. title: Netinkamas puslapis + '503': The page could not be served due to a temporary server failure. noscript_html: Kad naudotumÄ—tÄ—s Mastodon web aplikacija, praÅ¡ome įsijungti JavaScript. Alternatyviai, pabandykite viena iÅ¡ <a href="%{apps_path}">vietinių aplikacijų</a> Mastodon savo platformai. exports: archive_takeout: @@ -615,9 +593,6 @@ lt: too_many: Negalima pridÄ—ti daugiau nei 4 failų migrations: acct: slapyvardis@domenas naujam vartotojui - currently_redirecting: 'JÅ«sų profilis nustatytas nukreipimui į:' - proceed: IÅ¡saugoti - updated_msg: JÅ«sų paskyros migracijos nustatymai sÄ—kmingai pakeisti! moderation: title: Moderacija notification_mailer: @@ -672,10 +647,6 @@ lt: reply: proceed: Atsakyti prompt: 'JÅ«s norite atsakyti Å¡iam toot''ui:' - remote_unfollow: - error: Klaida - title: Pavadinimas - unfollowed: Nebesekama scheduled_statuses: over_daily_limit: JÅ«s pasieketÄ— limitÄ… (%{limit}) galimų toot'ų per dienÄ… over_total_limit: JÅ«s pasieketÄ— %{limit} limitÄ… galimų toot'ų diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 971450a89bae8bd48cf8ae69fec86bb9e5f4dc33..f6625dd41f385314f766b63aaaa7dba272d96740 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1,12 +1,15 @@ --- lv: errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. invites: expires_in: '1800': 30 minutes diff --git a/config/locales/mk.yml b/config/locales/mk.yml new file mode 100644 index 0000000000000000000000000000000000000000..acb18fb5a00af60406909358b4962f8eff2990db --- /dev/null +++ b/config/locales/mk.yml @@ -0,0 +1,20 @@ +--- +mk: + errors: + '400': The request you submitted was invalid or malformed. + '403': You don't have permission to view this page. + '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. + '410': The page you were looking for doesn't exist here anymore. + '422': + '429': Throttled + '500': + '503': The page could not be served due to a temporary server failure. + invites: + expires_in: + '1800': 30 minutes + '21600': 6 hours + '3600': 1 hour + '43200': 12 hours + '604800': 1 week + '86400': 1 day diff --git a/config/locales/ms.yml b/config/locales/ms.yml index 3597ccd15fd46d85a60d4aac6099b0950f18a784..d824aed60c3ed70bb2ff9b55a474a5c1b4e67eeb 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -10,10 +10,6 @@ ms: contact_missing: Tidak ditetapkan contact_unavailable: Tidak tersedia documentation: Pendokumenan - extended_description_html: | - <h3>Tempat sesuai untuk peraturan</h3> - <p>Kenyataan penuh masih belum ditetapkan.</p> - generic_description: "%{domain} ialah salah sebuah pelayan dalam rangkaian Mastodon" hosted_on: Mastodon dihoskan di %{domain} learn_more: Ketahui lebih lanjut privacy_policy: Polisi privasi @@ -297,12 +293,15 @@ ms: create_and_resolve: Selesaikan dengan nota placeholder: Terangkan tindakan apa yang telah diambil, atau sebarang kemas kini lain yang berkaitan... errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. exports: archive_takeout: in_progress: Mengkompil arkib anda... diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 78be7872d8fb028be74bf90f56efc4eab0199269..2c9e42b33c371a96be0e460b92c7703f4c647641 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -17,13 +17,12 @@ nl: contact_unavailable: n.v.t discover_users: Gebruikers ontdekken documentation: Documentatie - extended_description_html: | - <h3>Een goede plek voor richtlijnen</h3> - <p>De uitgebreide omschrijving is nog niet ingevuld.</p> federation_hint_html: Met een account op %{instance} ben je in staat om mensen die zich op andere Mastodonservers (en op andere plekken) bevinden te volgen. - generic_description: "%{domain} is een server in het Mastodonnetwerk" get_apps: Mobiele apps hosted_on: Mastodon op %{domain} + instance_actor_flash: 'Dit account is een virtuel actor die wordt gebruikt om de server zelf te vertegenwoordigen en is geen individuele gebruiker. Het wordt voor federatiedoeleinden gebruikt en moet niet worden geblokkeerd, tenzij je de hele server wil blokkeren. In zo''n geval dien je echter een domeinblokkade te gebruiken. + +' learn_more: Meer leren privacy_policy: Privacybeleid see_whats_happening: Kijk wat er aan de hand is @@ -35,6 +34,14 @@ nl: status_count_before: Zij schreven tagline: Vrienden volgen en nieuwe ontdekken terms: Gebruiksvoorwaarden + unavailable_content: Niet beschikbare inhoud + unavailable_content_description: + domain: Server + reason: 'Reden:' + rejecting_media: Mediabestanden van deze server worden niet verwerkt en er worden geen thumbnails getoond. Je moet handmatig naar deze server doorklikken om de mediabestanden te kunnen bekijken. + silenced: Toots van deze server worden nergens weergegeven, behalve op jouw eigen starttijdlijn wanneer je het account volgt. + suspended: Je bent niet in staat om iemand van deze server te volgen, en er worden geen gegevens van deze server verwerkt of opgeslagen, en met deze server uitgewisseld. + unavailable_content_html: Met Mastodon kun je in het algemeen berichten bekijken van en communiceren met gebruikers van elke andere server in de fediverse. Dit zijn de uitzonderingen die door deze server zijn gemaakt en expliciet alleen hier gelden. user_count_after: one: gebruiker other: gebruikers @@ -42,6 +49,8 @@ nl: what_is_mastodon: Wat is Mastodon? accounts: choices_html: 'Aanbevelingen van %{name}:' + endorsements_hint: Je kunt mensen die je volgt in de webomgeving aanbevelen, waarna ze dan hier zullen verschijnen. + featured_tags_hint: Je kunt specifieke hashtags uitlichten, waarna ze dan hier zullen verschijnen. follow: Volgen followers: one: Volger @@ -53,6 +62,7 @@ nl: media: Media moved_html: "%{name} is verhuisd naar %{new_profile_link}:" network_hidden: Deze informatie is niet beschikbaar + never_active: Nooit nothing_here: Hier is niets! people_followed_by: Mensen die %{name} volgen people_who_follow: Mensen die %{name} volgen @@ -183,6 +193,7 @@ nl: username: Gebruikersnaam warn: Waarschuwen web: Webapp + whitelisted: Op de witte lijst action_logs: actions: assigned_to_self_report: "%{name} heeft rapportage %{target} aan zichzelf toegewezen" @@ -218,19 +229,24 @@ nl: deleted_status: "(verwijderde toot}" title: Auditlog custom_emojis: + assign_category: Categorie toewijzen by_domain: Domein copied_msg: Lokale kopie van emoji maken geslaagd copy: Kopiëren copy_failed_msg: Kan geen lokale kopie van deze emoji maken + create_new_category: Nieuwe categorie toevoegen created_msg: Aanmaken van emoji geslaagd! delete: Verwijderen destroyed_msg: Verwijderen van emoji geslaagd! disable: Uitschakelen + disabled: Uitgeschakeld disabled_msg: Uitschakelen van deze emoji geslaagd emoji: Emoji enable: Inschakelen + enabled: Ingeschakeld enabled_msg: Inschakelen van deze emoji geslaagd image_hint: PNG van max. 50KB + list: In lijst listed: Weergegeven new: title: Lokale emoji toevoegen @@ -238,11 +254,14 @@ nl: shortcode: Verkorte code shortcode_hint: Tenminste 2 tekens (alleen alfanumeriek en underscores) title: Lokale emoji’s + uncategorized: Niet gecategoriseerd + unlist: Niet in lijst unlisted: Niet weergegeven update_failed_msg: Deze emoji kon niet worden bijgewerkt updated_msg: Bijwerken van emoji is geslaagd! upload: Uploaden dashboard: + authorized_fetch_mode: Veilige modus backlog: achterstallige taken config: Configuratie feature_deletions: Verwijderen van account @@ -250,10 +269,13 @@ nl: feature_profile_directory: Gebruikersgids feature_registrations: Registraties feature_relay: Federatierelay + feature_spam_check: Anti-spam feature_timeline_preview: Voorvertoning van tijdlijn features: Functies hidden_service: Federatie met verborgen diensten open_reports: onopgeloste rapportages + pending_tags: hashtags die op een beoordeling wachten + pending_users: gebruikers die op een beoordeling wachten recent_users: Recente gebruikers search: In volledige tekst zoeken single_user_mode: Modus voor één gebruiker @@ -265,11 +287,18 @@ nl: week_interactions: interacties deze week week_users_active: actieve gebruikers deze week week_users_new: nieuwe gebruikers deze week + whitelist_mode: Modus voor de witte lijst + domain_allows: + add_new: Domein voor de witte lijst + created_msg: Het domein is succesvol aan de witte lijst toegevoegd + destroyed_msg: Het domein is van de witte lijst verwijderd + undo: Van de witte lijst verwijderen domain_blocks: add_new: Nieuwe domeinblokkade toevoegen created_msg: Domeinblokkade wordt nu verwerkt destroyed_msg: Domeinblokkade is ongedaan gemaakt domain: Domein + edit: Domeinblokkade bewerken existing_domain_block_html: Jij hebt al strengere beperkingen opgelegd aan %{name}, je moet het domein eerst <a href="%{unblock_url}">deblokkeren</a>. new: create: Blokkade aanmaken @@ -280,6 +309,10 @@ nl: silence: Negeren suspend: Opschorten title: Nieuwe domeinblokkade + private_comment: Privé-opmerking + private_comment_hint: Opmerking over deze domeinbeperking voor intern gebruik door de moderatoren. + public_comment: Openbare opmerking + public_comment_hint: Opmerking over deze domeinbeperking voor het grote publiek, voor wanneer de openbare lijst met domeinbeperkingen is ingeschakeld. reject_media: Mediabestanden weigeren reject_media_hint: Verwijderd lokaal opgeslagen mediabestanden en weigert deze in de toekomst te downloaden. Irrelevant voor opgeschorte domeinen reject_reports: Rapportages weigeren @@ -299,6 +332,7 @@ nl: title: Domeinblokkade voor %{domain} ongedaan maken undo: Ongedaan maken undo: domeinblokkade ongedaan maken + view: Domeinblokkade bekijken email_domain_blocks: add_new: Nieuwe toevoegen created_msg: Blokkeren e-maildomein geslaagd @@ -322,6 +356,8 @@ nl: all: Alles limited: Beperkt title: Moderatie + private_comment: Privé-opmerking + public_comment: Openbare opmerking title: Andere domeinen total_blocked_by_us: Door ons geblokkeerd total_followed_by_them: Door hun gevolgd @@ -351,6 +387,7 @@ nl: pending: Aan het wachten op toestemming van de relayserver save_and_enable: Opslaan en inschakelen setup: Een verbinding met een relayserver maken + signatures_not_enabled: Federatierelays werken niet goed wanneer de veilige modus of de witte lijstmodus is ingeschakeld status: Status title: Relayservers report_notes: @@ -399,6 +436,16 @@ nl: custom_css: desc_html: Het uiterlijk van deze server met CSS aanpassen title: Aangepaste CSS + default_noindex: + desc_html: Heeft invloed op alle gebruikers die deze instelling niet zelf hebben veranderd + title: Toots van gebruikers standaard niet door zoekmachines laten indexeren + domain_blocks: + all: Naar iedereen + disabled: Naar niemand + title: Domeinblokkades tonen + users: Naar ingelogde lokale gebruikers + domain_blocks_rationale: + title: Motivering tonen hero: desc_html: Wordt op de voorpagina getoond. Tenminste 600x100px aanbevolen. Wanneer dit niet is ingesteld wordt de thumbnail van de Mastodonserver getoond title: Hero-afbeelding @@ -431,8 +478,8 @@ nl: open: Iedereen kan zich registreren title: Registratiemodus show_known_fediverse_at_about_page: - desc_html: Wanneer ingeschakeld wordt de globale tijdlijn op de voorpagina getoond en wanneer uitgeschakeld de lokale tijdljn. - title: De globale tijdlijn op de voorpagina tonen + desc_html: Wanneer ingeschakeld wordt de globale tijdlijn op de voorpagina getoond en wanneer uitgeschakeld de lokale tijdlijn + title: De globale tijdlijn op de openbare tijdlijnpagina tonen show_staff_badge: desc_html: Medewerkersbadge op profielpagina tonen title: Medewerkersbadge tonen @@ -449,19 +496,26 @@ nl: desc_html: Je kan hier jouw eigen privacybeleid, gebruiksvoorwaarden en ander juridisch jargon kwijt. Je kan HTML gebruiken title: Aangepaste gebruiksvoorwaarden site_title: Naam Mastodonserver + spam_check_enabled: + desc_html: Mastodon kan accounts die herhaaldelijk ongevraagde berichten versturen automatisch negeren of rapporteren. Het is mogelijk dat er foutpositieven tussen zitten. + title: Automatische spambestrijding thumbnail: desc_html: Gebruikt als voorvertoning voor OpenGraph en de API. 1200x630px aanbevolen title: Thumbnail Mastodonserver timeline_preview: - desc_html: Toon een openbare tijdlijn op de voorpagina - title: Tijdlijn op de voorpagina tonen + desc_html: Toon een link naar de openbare tijdlijnpagina op de voorpagina en geef de API zonder in te loggen toegang tot de openbare tijdlijn + title: Toegang tot de openbare tijdlijn zonder in te loggen toestaan title: Server-instellingen + trends: + desc_html: Eerder beoordeelde hashtags die op dit moment trending zijn openbaar tonen + title: Trending hashtags statuses: back_to_account: Terug naar accountpagina batch: delete: Verwijderen nsfw_off: Als niet gevoelig markeren nsfw_on: Als gevoelig markeren + deleted: Verwijderd failed_to_execute: Uitvoeren mislukt media: title: Media @@ -469,21 +523,24 @@ nl: no_status_selected: Er werden geen toots gewijzigd, omdat er geen enkele werd geselecteerd title: Toots van account with_media: Met media - subscriptions: - callback_url: Callback-URL - confirmed: Bevestigd - expires_in: Verloopt over - last_delivery: Laatste bezorging - title: WebSub - topic: Account tags: - accounts: Accounts - hidden: Verborgen - hide: Niet in gebruikersgids tonen + accounts_today: Aantal verschillende keren vandaag gebruikt + accounts_week: Aantal verschillende keren deze week gebruikt + breakdown: Uitsplitsing van het gebruik van vandaag naar bron + context: Context + directory: In de gebruikersgids + in_directory: "%{count} keer in de gebruikersgids" + last_active: Laatst actief + most_popular: Meest populair + most_recent: Meest recent name: Hashtag + review: Status beoordelen + reviewed: Beoordeeld title: Hashtags - unhide: In gebruikersgids tonen - visible: Zichtbaar + trending_right_now: Op dit moment trending + unique_uses_today: "%{count} keer vandaag gebruikt" + unreviewed: Niet beoordeeld + updated_msg: Instellingen hashtag succesvol bijgewerkt title: Beheer warning_presets: add_new: Nieuwe toevoegen @@ -499,11 +556,21 @@ nl: body: "%{reporter} heeft %{target} gerapporteerd" body_remote: Iemand van %{domain} heeft %{target} gerapporteerd subject: Nieuwe rapportage op %{instance} (#%{id}) + new_trending_tag: + body: 'De hashtag #%{name} is vandaag trending, maar is nog niet beoordeeld. Het wordt niet in het openbaar getoond alvorens je de hashtag goedkeurt. Je kunt ook het formulier zoals het nu is opslaan, waarna je er niks meer over zult horen.' + subject: Nieuwe hashtag te beoordelen op %{instance} (#%{name}) + aliases: + add_new: Alias aanmaken + created_msg: Succesvol een nieuwe alias aangemaakt. Je kunt nu met de verhuizing vanaf het oude account beginnen. + deleted_msg: De alias is succesvol verwijderd. Verhuizen vanaf dat account naar dit account is niet meer mogelijk. + hint_html: Wanneer je vanaf een ander account naar dit account wilt verhuizen, kun je hier een alias aanmaken. Dit is nodig voordat je verder kunt gaan met het verhuizen van volgers van het oude naar dit nieuwe account. Deze actie is op zich <strong>ongevaarlijk en omkeerbaar</strong>. <strong>De accountmigratie wordt gestart vanaf het oude account</strong>. + remove: Alias ontkoppelen appearance: advanced_web_interface: Geavanceerde webomgeving advanced_web_interface_hint: 'Wanneer je van de hele schermbreedte gebruik wilt maken, stelt de geavanceerde webomgeving je in staat om meerdere verschillende kolommen te configureren. Hiermee kun je zoveel mogelijk informatie op hetzelfde moment bekijken, zoals: Start, meldingen, de globale tijdlijn, meerdere lijsten en hashtags.' animations_and_accessibility: Animaties en toegankelijkheid confirmation_dialogs: Bevestigingen + discovery: Ontdekken sensitive_content: Gevoelige inhoud application_mailer: notification_preferences: E-mailvoorkeuren wijzigen @@ -524,9 +591,13 @@ nl: apply_for_account: Een uitnodiging aanvragen change_password: Wachtwoord checkbox_agreement_html: Ik ga akkoord met de <a href="%{rules_path}" target="_blank">regels van deze server</a> en de <a href="%{terms_path}" target="_blank">gebruiksvoorwaarden</a> - confirm_email: E-mail bevestigen + checkbox_agreement_without_rules_html: Ik ga akkoord met de <a href="%{terms_path}" target="_blank">gebruiksvoorwaarden</a> delete_account: Account verwijderen delete_account_html: Wanneer je jouw account graag wilt verwijderen, kun je dat <a href="%{path}">hier doen</a>. We vragen jou daar om een bevestiging. + description: + prefix_invited_by_user: "@%{name} nodigt je hierbij uit om een account aan te maken op deze Mastodonserver!" + prefix_sign_up: Registreer je vandaag nog op Mastodon! + suffix: Met een account ben je in staat om mensen te volgen, berichten te plaatsen en uit te wisselen met mensen die zich op andere Mastodonservers bevinden en meer! didnt_get_confirmation: Geen bevestigingsinstructies ontvangen? forgot_password: Wachtwoord vergeten? invalid_reset_password_token: De code om jouw wachtwoord opnieuw in te stellen is verlopen. Vraag een nieuwe aan. @@ -544,6 +615,16 @@ nl: reset_password: Wachtwoord opnieuw instellen security: Beveiliging set_new_password: Nieuw wachtwoord instellen + setup: + email_below_hint_html: Wanneer onderstaand e-mailadres niet klopt, kun je dat hier veranderen. Je ontvangt dan hierna een bevestigingsmail. + email_settings_hint_html: De bevestigingsmail is verzonden naar %{email}. Wanneer dat e-mailadres niet klopt, kun je dat veranderen in je accountinstellingen. + title: Instellen + status: + account_status: Accountstatus + confirming: Aan het wachten totdat de e-mail is bevestigd. + functional: Jouw account is volledig operationeel. + pending: Jouw aanvraag moet nog worden beoordeeld door een van onze medewerkers. Dit kan misschien eventjes duren. Je ontvangt een e-mail wanneer jouw aanvraag is goedgekeurd. + redirecting_to: Jouw account is inactief omdat het momenteel wordt doorverwezen naar %{acct}. trouble_logging_in: Problemen met inloggen? authorize_follow: already_following: Je volgt dit account al @@ -556,6 +637,11 @@ nl: return: Profiel van deze gebruiker tonen web: Ga naar de webapp title: Volg %{acct} + challenge: + confirm: Doorgaan + hint_html: "<strong>Tip:</strong> We vragen jou het komende uur niet meer naar jouw wachtwoord." + invalid_password: Ongeldig wachtwoord + prompt: Bevestig wachtwoord om door te gaan datetime: distance_in_words: about_x_hours: "%{count}u" @@ -571,26 +657,33 @@ nl: x_months: "%{count}ma" x_seconds: "%{count}s" deletes: - bad_password_msg: Goed geprobeerd hackers! Ongeldig wachtwoord + challenge_not_passed: De informatie die u hebt ingevoerd is ongeldig confirm_password: Voer jouw huidige wachtwoord in om jouw identiteit te bevestigen - description_html: Hierdoor worden alle gegevens van jouw account <strong>permanent, onomkeerbaar</strong> verwijderd en wordt deze gedeactiveerd. Om toekomstige identiteitsdiefstal te voorkomen, kan op deze server jouw gebruikersnaam niet meer gebruikt worden. + confirm_username: Voer uw gebruikersnaam in om de procedure te bevestigen proceed: Account verwijderen success_msg: Jouw account is succesvol verwijderd - warning_html: We kunnen alleen garanderen dat jouw gegevens op deze server worden verwijderd. Berichten (toots), incl. media, die veel zijn gedeeld laten mogelijk sporen achter. Offline servers en servers die niet meer op jouw updates zijn geabonneerd zullen niet hun databases updaten. - warning_title: Verwijdering gegevens op andere servers + warning: + before: 'Lees deze tekst zorgvuldig voordat je verder gaat:' + caches: Toots en media die op andere servers zijn opgeslagen kunnen daar achterblijven + data_removal: Jouw toots en andere gegevens worden permanent verwijderd + email_change_html: Je kunt <a href="%{path}">je e-mailadres wijzigen</a> zonder dat je jouw account hoeft te verwijderen + email_contact_html: Wanneer het nog steeds niet aankomt, kun je voor hulp e-mailen naar <a href="mailto:%{email}">%{email}</a> + email_reconfirmation_html: Wanneer je de bevestigingsmail niet hebt ontvangen, kun je deze <a href="%{path}">opnieuw aanvragen</a> + irreversible: Je zult niet in staat zijn om jouw account te herstellen of te deactiveren + more_details_html: Zie het <a href="%{terms_path}">privacybeleid</a> voor meer informatie. + username_available: Jouw gebruikersnaam zal weer beschikbaar komen + username_unavailable: Jouw gebruikersnaam zal onbeschikbaar blijven directories: directory: Gebruikersgids - enabled: Je staat momenteel in de gebruikersgids vermeldt. - enabled_but_waiting: Je hebt er voor gekozen om in de gebruikersgids te worden vermeldt, maar je hebt daarvoor nog niet het minimaal aantal volgers (%{min_followers}). explanation: Ontdek gebruikers aan de hand van hun interesses explore_mastodon: "%{title} verkennen" - how_to_enable: Je geeft momenteel geen toestemming om in de gebruikersgids te worden vermeldt. Je kunt hieronder toestemming geven. Gebruik hashtags in de tekst van jouw bio, om onder bepaalde hashtags te worden vermeldt! - people: - one: "%{count} gebruikers" - other: "%{count} gebruikers" + domain_validator: + invalid_domain: is een ongeldige domeinnaam errors: + '400': De aanvraag die je hebt ingediend was ongeldig of foutief. '403': Jij hebt geen toestemming om deze pagina te bekijken. '404': De pagina waarnaar jij op zoek bent bestaat niet. + '406': Deze pagina is niet beschikbaar in het opgevraagde formaat. '410': De pagina waarnaar jij op zoek bent bestaat niet meer. '422': content: Veiligheidsverificatie mislukt. Blokkeer je toevallig cookies? @@ -599,6 +692,7 @@ nl: '500': content: Het spijt ons, er is aan onze kant iets fout gegaan. title: Er is iets mis + '503': De pagina kon door een tijdelijke serverstoring niet worden geladen. noscript_html: Schakel JavaScript in om de webapp van Mastodon te kunnen gebruiken. Als alternatief kan je een <a href="%{apps_path}">Mastodon-app</a> zoeken voor jouw platform. existing_username_validator: not_found: Kon geen lokale gebruiker met die gebruikersnaam vinden @@ -622,6 +716,7 @@ nl: add_new: Nieuwe toevoegen errors: limit: Je hebt al het maximaal aantal hashtags uitgelicht + hint_html: "<strong>Wat zijn uitgelichte hashtags?</strong> Deze worden prominent op jouw openbare profiel getoond en stelt mensen in staat om jouw openbare toots per hashtag te bekijken. Het zijn een goed hulpmiddel om creatieve werkzaamheden of langetermijnprojecten bij te houden." filters: contexts: home: Starttijdlijn @@ -642,10 +737,12 @@ nl: developers: Ontwikkelaars more: Meer… resources: Hulpmiddelen + trending_now: Trends generic: all: Alles changes_saved_msg: Wijzigingen succesvol opgeslagen! copy: Kopiëren + no_batch_actions_available: Geen batchacties op deze pagina beschikbaar order_by: Sorteer op save_changes: Wijzigingen opslaan validation_errors: @@ -697,7 +794,7 @@ nl: '604800': 1 week '86400': 1 dag expires_in_prompt: Nooit - generate: Genereren + generate: Uitnodigingslink genereren invited_by: 'Jij bent uitgenodigd door:' max_uses: one: 1 keer @@ -716,10 +813,35 @@ nl: images_and_video: Een video kan niet aan een toot met afbeeldingen worden gekoppeld too_many: Er kunnen niet meer dan 4 afbeeldingen toegevoegd worden migrations: - acct: gebruikersnaam@domein van het nieuwe account - currently_redirecting: 'Jouw profiel wordt nu doorverwezen naar:' - proceed: Opslaan - updated_msg: Jouw accountmigratie-instelling is succesvol bijgewerkt! + acct: Verhuisd naar + cancel: Doorverwijzing annuleren + cancel_explanation: Het annuleren van de doorverwijzing zal jouw huidige account opnieuw activeren, maar brengt geen volgers terug die naar het andere account zijn verhuisd. + cancelled_msg: De doorverwijzing is succesvol geannuleerd. + errors: + already_moved: is hetzelfde account waarnaar je al naar toe bent verhuisd + missing_also_known_as: verwijst niet terug naar dit account + move_to_self: kan niet het huidige account zijn + not_found: kon niet worden gevonden + on_cooldown: Jouw laatste migratie is nog te kort geleden + followers_count: Volgers op het moment van verhuizing + incoming_migrations: Verhuizen vanaf een ander account + incoming_migrations_html: Om vanaf een ander account naar dit account te verhuizen, dien je eerst <a href="%{path}">een accountalias aan te maken</a>. + moved_msg: Jouw account wordt nu naar %{acct} doorverwezen en jouw volgers worden verhuisd. + not_redirecting: Jouw account wordt momenteel niet naar een ander account doorverwezen. + on_cooldown: Je hebt recentelijk jouw account verhuisd. Deze mogelijkheid is weer beschikbaar over %{count} dagen. + past_migrations: Vorige migraties + proceed_with_move: Volgers verhuizen + redirecting_to: Jouw account wordt nu naar %{acct} doorverwezen. + set_redirect: Doorverwijzing instellen + warning: + backreference_required: Het nieuwe account moet eerst worden ingesteld om naar dit account te kunnen terugverwijzen + before: 'Lees eerst goed deze tekst, alvorens verder te gaan:' + cooldown: Na de verhuizing kun je tijdelijk niet opnieuw verhuizen + disabled_account: Jouw huidige account is hierna niet meer volledig bruikbaar. Je hebt echter wel toegang tot het exporteren van je gegevens en tot het opnieuw activeren van je account. + followers: Deze actie verhuisd alle volgers vanaf het huidige account naar het nieuwe account + only_redirect_html: Je kunt als alternatief ook <a href="%{path}">alleen de doorverwijzing op je profiel zetten</a>. + other_data: Geen andere gegevens worden automatisch verhuisd + redirect: Jouw huidige accountprofiel wordt bijgewerkt met een doorverwijzingsmelding en wordt uitgesloten van zoekresultaten moderation: title: Moderatie notification_mailer: @@ -816,10 +938,6 @@ nl: reply: proceed: Doorgaan met reageren prompt: 'Je wilt op de volgende toot reageren:' - remote_unfollow: - error: Fout - title: Titel - unfollowed: Ontvolgd scheduled_statuses: over_daily_limit: Je hebt de limiet van %{limit} in te plannen toots voor die dag overschreden over_total_limit: Je hebt de limiet van %{limit} in te plannen toots overschreden @@ -868,6 +986,7 @@ nl: settings: account: Account account_settings: Accountinstellingen + aliases: Accountaliassen appearance: Uiterlijk authorized_apps: Geautoriseerde apps back: Terug naar Mastodon @@ -908,6 +1027,9 @@ nl: private: Alleen openbare toots kunnen worden vastgezet reblog: Een boost kan niet worden vastgezet poll: + total_people: + one: "%{count} persoon" + other: "%{count} personen" total_votes: one: "%{count} stem" other: "%{count} stemmen" @@ -926,6 +1048,8 @@ nl: pinned: Vastgemaakte toot reblogged: boostte sensitive_content: Gevoelige inhoud + tags: + does_not_match_previous_name: komt niet overeen met de vorige naam terms: body_html: | <h2>Privacy Policy</h2> @@ -1043,7 +1167,9 @@ nl: disable: Zolang jouw account is bevroren blijven jouw accountgegevens intact, maar kun je geen handelingen uitvoeren totdat het account is vrijgegeven. silence: Zolang jouw account wordt beperkt, kunnen alleen mensen die jou al volgen jouw toots op deze server zien. Tevens ben je niet zichtbaar in meldingen, gesprekken en op openbare tijdlijnen. Anderen kunnen je echter wel handmatig volgen. suspend: Jouw account is opgeschort. Jouw toots en geüploade media zijn onomkeerbaar van deze server verwijderd, en ook o.a. van de servers waar jij volgers had. + get_in_touch: Je kunt deze e-mail beantwoorden om in contact te komen met de medewerkers van %{instance}. review_server_policies: Serverbeleid bekijken + statuses: 'Met name voor:' subject: disable: Jouw account %{acct} is bevroren none: Waarschuwing voor %{acct} diff --git a/config/locales/nn.yml b/config/locales/nn.yml new file mode 100644 index 0000000000000000000000000000000000000000..a1b61d6e7db4a8e6cc2c7c85eedf4c5a8315db75 --- /dev/null +++ b/config/locales/nn.yml @@ -0,0 +1,20 @@ +--- +nn: + errors: + '400': The request you submitted was invalid or malformed. + '403': You don't have permission to view this page. + '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. + '410': The page you were looking for doesn't exist here anymore. + '422': + '429': Throttled + '500': + '503': The page could not be served due to a temporary server failure. + invites: + expires_in: + '1800': 30 minutes + '21600': 6 hours + '3600': 1 hour + '43200': 12 hours + '604800': 1 week + '86400': 1 day diff --git a/config/locales/no.yml b/config/locales/no.yml index d21dda6fba0f4b4d39131d49fa6054d67606ba22..fa40975a79856508897977d310cd22f734f7a958 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -7,10 +7,6 @@ contact: Kontakt contact_missing: Ikke innstilt contact_unavailable: Ikke tilgjengelig - extended_description_html: | - <h3>En god plassering for regler</h3> - <p>En utvidet beskrivelse er ikke satt opp ennÃ¥.</p> - generic_description: "%{domain} er en tjener i nettverket" hosted_on: Mastodon driftet pÃ¥ %{domain} learn_more: Lær mer source_code: Kildekode @@ -267,12 +263,6 @@ no_media: Ingen media title: Kontostatuser with_media: Med media - subscriptions: - callback_url: Callback-URL - confirmed: Bekreftet - expires_in: Utløper om - last_delivery: Siste levering - topic: Emne title: Administrasjon admin_mailer: new_report: @@ -332,16 +322,14 @@ x_months: "%{count} mnd" x_seconds: "%{count} sek" deletes: - bad_password_msg: Godt forsøk, hacker! Feil passord confirm_password: Skriv inn ditt passord for Ã¥ verifisere din identitet - description_html: Dette vil <strong>permanent, irreversibelt</strong> fjerne innhold fra din konto og deaktivere den. Ditt brukernavn vil forbli reservert for Ã¥ forhindre fremtidige etterligninger. proceed: Slett konto success_msg: Din konto ble slettet - warning_html: Kun sletting av innhold fra denne instansen er garantert. Innhold som har blitt delt i stor utstrekning vil sannsynligvis legge igjen spor. Tjenere som er frakoblet og tjenere som ikke abbonerer pÃ¥ dine oppdatereringer vil ikke oppdatere sine databaser. - warning_title: Tilgjengelighet av spredt innhold errors: + '400': The request you submitted was invalid or malformed. '403': Du har ikke tillatelse til Ã¥ vise denne siden. '404': Siden du leter etter finnes ikke. + '406': This page is not available in the requested format. '410': Siden du leter etter finnes ikke lenger. '422': content: Sikkerhetsverifisering feilet. Blokkerer du informasjonskapsler? @@ -350,6 +338,7 @@ '500': content: Beklager men noe gikk galt ved vÃ¥r ende. title: Denne siden er ikke korrekt + '503': The page could not be served due to a temporary server failure. noscript_html: For Ã¥ bruke Mastodon webapplikasjon mÃ¥ du aktivere JavaScript. Alternativt kan du forsøke en av de mange <a href="%{apps_path}">integrerte appene</a> for Mastodon til din plattform. exports: blocks: Du blokkerer @@ -401,9 +390,6 @@ too_many: Kan ikke legge ved mer enn 4 filer migrations: acct: brukernavn@domene til den nye kontoen - currently_redirecting: 'Din profil er omdirigert til:' - proceed: Lagre - updated_msg: Dine innstillinger for kontomigrering er oppdatert! moderation: title: Moderasjon notification_mailer: diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 785caa4eccab9eccd276cc66d7aefa34fedeee7a..bd19401ed107cdd0bcb87c36adebb6eb1f56ee07 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -7,6 +7,7 @@ oc: active_count_after: actius active_footnote: Utilizaire actius per mes (UAM) administered_by: 'Administrat per :' + api: API apps: Aplicacions per mobil apps_platforms: Utilizatz Mastodon d‘iOS, Android o d’autras plataforma estant browse_directory: Navigatz per l’annuari de perfil e filtratz segon çò qu’aimatz @@ -16,10 +17,7 @@ oc: contact_unavailable: Pas disponible discover_users: Descobrissètz de nòvas personas documentation: Documentacion - extended_description_html: | - <h3>Una bona plaça per las règlas</h3> - <p>La descripcion longa es pas estada causida pel moment.</p> - generic_description: "%{domain} es un dels servidors del malhum" + federation_hint_html: Amb un compte sus %{instance} poiretz sègre de personas de qualque siasque servidor Mastodon e encara mai. get_apps: Ensajatz una aplicacion mobil hosted_on: Mastodon albergat sus %{domain} learn_more: Ne saber mai @@ -31,7 +29,11 @@ oc: one: estatut other: estatuts status_count_before: qu’an escrich + tagline: Seguètz d’amics e trobatz-ne de nòus terms: Condicions d’utilizacion + unavailable_content: Contengut pas disponible + unavailable_content_description: + reason: 'Motiu :' user_count_after: one: utilizaire other: utilizaires @@ -50,6 +52,7 @@ oc: media: Mèdias moved_html: "%{name} a mudat a %{new_profile_link} :" network_hidden: Aquesta informacion es pas disponibla + never_active: Jamai nothing_here: I a pas res aquÃ ! people_followed_by: Lo monde que %{name} sèc people_who_follow: Lo monde que sègon %{name} @@ -62,8 +65,10 @@ oc: posts_with_replies: Tuts e responsas reserved_username: Aqueste nom d’utilizaire es reservat roles: + admin: Admin bot: Robòt moderator: Moderador + unavailable: Perfil indisponible unfollow: Quitar de sègre admin: account_actions: @@ -76,7 +81,9 @@ oc: destroyed_msg: Nòta de moderacion ben suprimida ! accounts: approve: Aprovar + approve_all: O validar tot are_you_sure: Sètz segur ? + avatar: Avatar by_domain: Domeni change_email: changed_msg: Adreça corrèctament cambiada ! @@ -107,6 +114,7 @@ oc: header: Bandièra inbox_url: URL de recepcion invited_by: Convidat per + ip: IP joined: Venguèt location: all: Totes @@ -126,6 +134,7 @@ oc: moderation_notes: Nòtas de moderacion most_recent_activity: Activitat mai recenta most_recent_ip: IP mai recenta + no_account_selected: Cap de compte pas cambiat estant que cap èra pas seleccionat no_limits_imposed: Cap de limit impausat not_subscribed: Pas seguidor outbox_url: URL Outbox @@ -134,8 +143,11 @@ oc: profile_url: URL del perfil promote: Promòure protocol: Protocòl + public: Public push_subscription_expires: Fin de l’abonament PuSH redownload: Actualizar lo perfil + reject: Regetar + reject_all: O regetar tot remove_avatar: Supriir l’avatar remove_header: Levar la bandièra resend_confirmation: @@ -145,7 +157,9 @@ oc: reset: Reïnicializar reset_password: Reïnicializar lo senhal resubscribe: Se tornar abonar + role: Autorizacions roles: + admin: Administrator moderator: Moderador staff: Personnal user: Uitlizaire @@ -160,6 +174,7 @@ oc: statuses: Estatuts subscribe: S’abonar suspended: Suspendut + time_in_queue: En espèra a la fila %{time} title: Comptes unconfirmed_email: Adreça pas confirmada undo_silenced: Levar lo silenci @@ -167,6 +182,8 @@ oc: unsubscribe: Se desabonar username: Nom d’utilizaire warn: Avisar + web: Web + whitelisted: Mes en lista blanca action_logs: actions: assigned_to_self_report: "%{name} s’assignèt lo rapòrt %{target}" @@ -202,18 +219,23 @@ oc: deleted_status: "(estatut suprimit)" title: Audit dels jornals custom_emojis: + assign_category: Atribuir una categoria by_domain: Domeni copied_msg: Còpia locala de l’emoji ben creada copy: Copiar copy_failed_msg: Fracà s de la còpia locala de l’emoji + create_new_category: Crear una nòva categoria created_msg: Emoji ben creat ! delete: Suprimir destroyed_msg: Emoji ben suprimit ! disable: Desactivar disabled_msg: Aqueste emoji es ben desactivat + emoji: Emoji enable: Activar + enabled: Activat enabled_msg: Aqueste emoji es ben activat image_hint: PNG cap a 50Ko + list: Listar listed: Listat new: title: Ajustar un nòu emoji personal @@ -221,11 +243,14 @@ oc: shortcode: Acorchi shortcode_hint: Almens 2 caractèrs, solament alfanumerics e jonhent bas title: Emojis personals + uncategorized: Sens categoria + unlist: Listar pas unlisted: Pas listat update_failed_msg: Mesa a jorn de l’emoji fracasada updated_msg: Emoji ben mes a jorn ! upload: Enviar dashboard: + authorized_fetch_mode: Mòde recuperacion autorizat backlog: Accions en retard config: Configuracion feature_deletions: Supressions de comptes @@ -233,9 +258,13 @@ oc: feature_profile_directory: Annuari de perfils feature_registrations: Inscripcions feature_relay: Relai de federacion + feature_spam_check: Anti-spam + feature_timeline_preview: Apercebut del flux d’actualitats features: Foncionalitats hidden_service: Federacion amb servicis amagats open_reports: Senhalaments dobèrts + pending_tags: etiquetas en espèra de validacion + pending_users: utilizaires en espèra de validacion recent_users: Utilizaires recents search: Recèrca tèxte complèt single_user_mode: Mòde sol utilizaire @@ -247,11 +276,19 @@ oc: week_interactions: interaccions aquesta setmana week_users_active: actius aquesta setmana week_users_new: utilizaires aquesta setmana + whitelist_mode: Mòde lista blanca + domain_allows: + add_new: Plaçar en lista blanca + created_msg: Lo domeni es corrèctament estat plaçat en lista blanca + destroyed_msg: Lo domeni es corrèctament estat levat de la lista blanca + undo: Levar de la lista blanca domain_blocks: add_new: Ajustar un novèl blocatge de domeni created_msg: Domeni blocat es a èsser tractat destroyed_msg: Lo blocatge del domeni es estat levat domain: Domeni + edit: Modificar lo blocatge de domeni + existing_domain_block_html: Impausèretz ja de limitas mai estrictas per %{name}, vos cal lo <a href="%{unblock_url}">desblocar</a>d’en primièr. new: create: Crear blocatge hint: Lo blocatge empacharà pas la creacion de compte dins la basa de donadas, mai aplicarà la moderacion sus aquestes comptes. @@ -261,6 +298,9 @@ oc: silence: Silenci suspend: Suspendre title: Nòu blocatge domeni + private_comment: Comentari privat + private_comment_hint: Comentari tocant la limitacion d’aqueste domeni per un usatge intèrn pels moderators. + public_comment: Comentari public reject_media: Regetar los fichièrs mèdias reject_media_hint: Lèva los fichièrs gardats localament e regèta las demandas de telecargament dins lo futur. ServÃs pas a res per las suspensions reject_reports: Regetar los senhalaments @@ -280,6 +320,7 @@ oc: title: Restablir lo blocatge de domeni de %{domain} undo: Restablir undo: Restablir + view: Veire lo blocatge de domeni email_domain_blocks: add_new: Ajustar created_msg: Blocatge del domeni de corrièl ben plaçat @@ -303,6 +344,8 @@ oc: all: Totas limited: Limitat title: Moderacion + private_comment: Comentari privat + public_comment: Comentari public title: Federacion total_blocked_by_us: Avèm blocat total_followed_by_them: Sègon @@ -317,6 +360,8 @@ oc: expired: Expirats title: Filtre title: Convits + pending_accounts: + title: Comptes en espèra (%{count}) relays: add_new: Ajustar un nòu relai delete: Suprimir @@ -330,6 +375,7 @@ oc: pending: En espèra d’aprovacion del relai save_and_enable: Salvar e activar setup: Configurar una connexion relai + signatures_not_enabled: Los relais foncionarà n pas coma cal se lo mòde segur o lista blanca es activat status: Estatut title: Relais report_notes: @@ -378,6 +424,15 @@ oc: custom_css: desc_html: Modificar l’estil amb una fuèlha CSS cargada sus cada pagina title: CSS personalizada + default_noindex: + desc_html: Tòca totes los utilizaires qu’an pas cambiat lo paramètre + domain_blocks: + all: A tot lo monde + disabled: A degun + title: Mostrar los blocatges de domeni + users: Als utilizaires locals connectats + domain_blocks_rationale: + title: Mostrar lo rasonament hero: desc_html: Mostrat en primièra pagina. Almens 600x100px recomandat. S’es pas configurat l’imatge del servidor serà mostrat title: Imatge de l’eròi @@ -405,7 +460,9 @@ oc: title: Autorizat amb invitacions registrations_mode: modes: + approved: Validacion necessà ria per s’inscriure none: Degun pòt pas se marcar + open: Tot lo monde se pòt marcar title: Mòdes d’inscripcion show_known_fediverse_at_about_page: desc_html: Un còp activat mostrarà los tuts de totes los fediverse dins l’apercebut. Autrament mostrarà pas que los tuts locals. @@ -426,6 +483,8 @@ oc: desc_html: Afichada sus la pagina de las condicions d’utilizacion<br>Podètz utilizar de balisas HTML title: Politica de confidencialitat del site site_title: TÃtol del servidor + spam_check_enabled: + title: Anti-spam thumbnail: desc_html: ServÃs pels apercebuts via OpenGraph e las API. Talha de 1200x630px recomandada title: Miniatura del servidor @@ -433,12 +492,15 @@ oc: desc_html: Mostrar lo flux public sus la pagina d’acuèlh title: Apercebut flux public title: Paramètres del site + trends: + title: Etiquetas tendéncia statuses: back_to_account: Tornar a la pagina Compte batch: delete: Suprimir nsfw_off: Marcar coma pas sensible nsfw_on: Marcar coma sensible + deleted: Suprimits failed_to_execute: Fracà s media: title: Mèdia @@ -446,19 +508,23 @@ oc: no_status_selected: Cap d’estatut pas cambiat estant que cap èra pas seleccionat title: Estatuts del compte with_media: Amb mèdia - subscriptions: - callback_url: URL de rapèl - confirmed: Confirmat - expires_in: S’acaba dins - last_delivery: Darrièra distribucion - topic: Subjècte tags: - accounts: Comptes - hidden: Amagat - hide: Amagar dins l’annuari + accounts_today: Utilizacions unicas uèi + accounts_week: Utilizacions unicas aquesta setmana + context: Contèxt + directory: A l’annuari + in_directory: "%{count} a l’annuari" + last_active: Darrièra activitat + most_popular: Mai popularas + most_recent: Mai recentas name: Etiqueta + review: Repassar l’estatut + reviewed: Repassadas title: Etiquetas - unhide: Aparéisser dins l’annuari + trending_right_now: Actualament en tendéncia + unique_uses_today: "%{count} publicacions uèi" + unreviewed: Pas repassadas + updated_msg: Paramètres d’etiquetas corrèctament actualizats title: Administracion warning_presets: add_new: N’ajustar un nòu @@ -467,12 +533,23 @@ oc: edit_preset: Modificar lo tèxt predefinit d’avertiment title: Gerir los tèxtes predefinits admin_mailer: + new_pending_account: + body: Los detalhs del nòu compte son çai-jos. Podètz validar o regetar aquesta demanda. + subject: Nòu compte per repassar sus %{instance} (%{username}) new_report: body: "%{reporter} a senhalat %{target}" body_remote: Qualqu’un de %{domain} senhalèt %{target} subject: Novèl senhalament per %{instance} (#%{id}) + appearance: + advanced_web_interface: Interfà cia web avançada + advanced_web_interface_hint: 'Se volètz utilizar la nautor complèta de l’ecran, l’interfà cia web avançada vos permet de configurar diferentas colomnas per mostrar tan d’informacions que volètz : Acuèlh, notificacions, flux d’actualitat, e d’autras listas e etiquetas.' + animations_and_accessibility: Animacion e accessibilitat + confirmation_dialogs: Fenèstras de confirmacion + discovery: Descobèrta + sensitive_content: Contengut sensible application_mailer: notification_preferences: Cambiar las preferéncias de corrièl + salutation: "%{name}," settings: 'Cambiar las preferéncias de corrièl : %{link}' view: 'Veire :' view_profile: Veire lo perfil @@ -489,9 +566,12 @@ oc: apply_for_account: Demandar una invitacion change_password: Senhal checkbox_agreement_html: Accepti las <a href="%{rules_path}" target="_blank">règlas del servidor</a> e <a href="%{terms_path}" target="_blank">los tèrmes del servici</a> - confirm_email: Confirmar lo corrièl + checkbox_agreement_without_rules_html: Soi d’acòrdi amb las <a href="%{terms_path}" target="_blank">condicions d’utilizacion</a> delete_account: Suprimir lo compte delete_account_html: Se volètz suprimir vòstre compte, podètz <a href="%{path}">o far aquÃ</a>. Vos demandarem que confirmetz. + description: + prefix_invited_by_user: "@%{name} vos convida a rejónher aqueste servidor Mastodon !" + prefix_sign_up: Marcatz-vos a Mostodon uèi ! didnt_get_confirmation: Avètz pas recebut las instruccions de confirmacion ? forgot_password: Senhal oblidat ? invalid_reset_password_token: Lo geton de reïnicializacion es invalid o acabat. Tornatz demandar un geton se vos plai. @@ -500,12 +580,20 @@ oc: migrate_account: Mudar endacòm mai migrate_account_html: Se volètz mandar los visitors d’aqueste compte a un autre, podètz<a href="%{path}"> o configurar aquÃ</a>. or_log_in_with: O autentificatz-vos amb + providers: + cas: CAS + saml: SAML register: Se marcar registration_closed: "%{instance} accepta pas de nòus membres" resend_confirmation: Tornar mandar las instruccions de confirmacion reset_password: Reïnicializar lo senhal security: Seguretat set_new_password: Picar un nòu senhal + setup: + title: Configuracion + status: + account_status: Estat del compte + functional: Vòstre compte es complètament foncional. trouble_logging_in: Problèmas de connexion ? authorize_follow: already_following: Seguètz ja aqueste compte @@ -518,6 +606,11 @@ oc: return: Veire lo perfil a la persona web: Tornar a l’interfà cia Web title: Sègre %{acct} + challenge: + confirm: Contunhar + hint_html: "<strong>Asutúcia :</strong> vos demandarem pas vòstre senhal de nòu d’aquà unas oras." + invalid_password: Senhal invalid + prompt: Confirmatz lo senhal per dire de contunhar datetime: distance_in_words: about_x_hours: "%{count} h" @@ -531,27 +624,31 @@ oc: x_days: "%{count} jorns" x_minutes: "%{count} min" x_months: "%{count} meses" + x_seconds: "%{count}s" deletes: - bad_password_msg: Ben ensajat pirata ! Senhal incorrècte + challenge_not_passed: Las informacions qu’avètz fornidas son pas corrèctas confirm_password: Picatz vòstre senhal actual per verificar vòstra identitat - description_html: Aquò suprimirà <strong>definitivament e sens possibilitat de retorn</strong> lo contengut de vòstre compte e lo desactivarà . Lo nom d’utilizaire serà gardat per evitar una futura impostura. + confirm_username: Picatz vòstre nom d’utilizaire per confirmar la procedura proceed: Suprimir lo compte success_msg: Compte ben suprimit - warning_html: La supression del contengut d’aqueste servidor es sola assegurada. Lo contengut fòrça partejat daissarà probablament de traças. Los servidors fòra-linha e los que vos sègon pas mai aurà n pas la mesa a jorn de lor basa de donada. - warning_title: Disponibilitat del contengut difusat + warning: + caches: Lo contengut en cache suls autres servidors pòt demorar + email_change_html: Podètz <a href="%{path}">cambiar vòstra adreça electronia</a>sens suprimir vòstre compte + irreversible: Poiretz pas restaurar o reactivar vòstre compte + more_details_html: Per mai d’informacion, vejatz la <a href="%{terms_path}">politica de confidencialitat</a>. + username_available: Vòstre nom d’utilizaire serà disponible de nòu + username_unavailable: Vòstre nom d’utilizaire demorarà pas disponible directories: directory: Annuari de perfils - enabled: Sètz actualament listat dins l'annuari. - enabled_but_waiting: Avètz causit d'èsser listat dins l'annuari mas avètz pas encara lo nombre minimum de seguidors (%{min_followers}) per i èsser listat. explanation: Trobar d’utilizaires segon lor interèsses explore_mastodon: Explorar %{title} - how_to_enable: Sètz pas actualament listat dins l’annuari. Podètz cambiar aquò çai-jos. Utilizatz d'etiquetas dins vòstre tèxt de bio per èsser listat amb d’etiquetas especificas ! - people: - one: "%{count} persona" - other: "%{count} personas" + domain_validator: + invalid_domain: es pas un nom de domeni valid errors: + '400': The request you submitted was invalid or malformed. '403': Avètz pas l’autorizacion de veire aquesta pagina. '404': La pagina que cercatz existÃs pas aquÃ. + '406': La pagina es pas disponibla dins lo format demandat. '410': La pagina que cercatz existÃs pas mai aquÃ. '422': content: Verificacion de seguretat fracassada. Blocatz los cookies ? @@ -560,16 +657,21 @@ oc: '500': content: Un quicomet a pas foncionat coma caliá. title: Aquesta pagina es pas corrècta + '503': The page could not be served due to a temporary server failure. noscript_html: Per utilizar l’aplicacion web de Mastodon, mercés d’activar JavaScript. O podètz utilizar <a href="%{apps_path}">una aplicacion</a> per vòstra plataforma coma alernativa. + existing_username_validator: + not_found: impossible de trobar un utilizaire local amb aqueste nom d’utilizaire + not_found_multiple: impossible de trobar %{usernames} exports: archive_takeout: date: Data download: Telecargar vòstre archiu - hint_html: Podètz demandar un archiu de vòstres <strong>tuts e mèdias enviats</strong>. Las donadas exportadas serà n al format ActivityPub, ligible pels logicials compatibles. Podètz demandar un archiu cada 7 jorns. - in_progress: Complilacion de vòstre archiu... + hint_html: Podètz demandar un archiu de vòstres <strong>tuts e mèdias enviats</strong>. Las donadas exportadas serà n al format ActivityPub, legible pels logicials compatibles. Podètz demandar un archiu cada 7 jorns. + in_progress: Compilacion de vòstre archiu... request: Demandar vòstre archiu size: Talha blocks: Personas que blocatz + csv: CSV domain_blocks: Blocatge de domenis follows: Personas que seguètz lists: Listas @@ -599,20 +701,37 @@ oc: developers: Desvolopaires more: Mai… resources: Ressorsas + trending_now: Tendéncia del moment generic: all: Tot changes_saved_msg: Cambiaments ben realizats ! copy: Copiar + no_batch_actions_available: Cap d’accion de massa pas disponibla sus aquesta pagina + order_by: Triar per save_changes: Salvar los cambiaments validation_errors: one: I a quicòm que truca ! Mercés de corregir l’error çai-jos other: I a quicòm que truca ! Mercés de corregir las %{count} errors çai-jos + html_validator: + invalid_markup: 'conten un balisatge HTML invalid : %{error}' identity_proofs: + active: Actiu authorize: Ã’c, autorizar authorize_connection_prompt: Autorizar aquesta connexion criptografica ? + errors: + failed: La connexion criptografica a fracassat. Ensajatz tornamai de %{provider} estant. + keybase: + invalid_token: Los getons Keybase son de hashes de signaturas e devon èsser de caractèrs 66 hex + verification_failed: Keybase reconeis pas aqueste geton coma signatura de l’utilizaire Keybase %{kb_username}. Ensajatz tornamai de Keybase estant. + wrong_user: Creacion impossibla de la pròva per %{proving} en estant connectat coma %{current}. Connectatz-vos coma %{proving} e ensajatz tornamai. + explanation_html: Aquà podètz connectar d’un biais criptografic vòstras identitats, coma un perfil Keybase. Aquò permet al monde de vos enviar de messatges chifrats e fisar al contengut que lor enviatz. i_am_html: Soi %{username} a %{service}. identity: Identitat + inactive: Inactiu + publicize_checkbox: 'E enviatz lo tut seguent :' + publicize_toot: 'Es provat ! Soi %{username} de %{service} : %{url}' status: Estatut de verificacion + view_proof: Veire la pròva imports: modes: merge: Fondre @@ -659,9 +778,20 @@ oc: too_many: Se pòt pas ajustar mai de 4 fichièrs migrations: acct: nomutilizaire@domeni del nòu compte - currently_redirecting: 'Vòstre perfil es parametrat per mandar a :' - proceed: Enregistrar - updated_msg: Vòstre paramètre de migracion es ben estat mes a jorn ! + cancel: Anullar la redireccion + cancelled_msg: Redireccion corrèctament anullada. + errors: + move_to_self: pòt pas èsser lo compte actual + not_found: impossible de trobar + incoming_migrations: Mudar d’un compte diferent + moved_msg: Vòstre compte manda ara a %{acct} e vòstres seguidors son desplaçats. + not_redirecting: Vòstre compte manda pas enlòc pel moment. + past_migrations: Migracions passadas + proceed_with_move: Desplaçar los seguidors + redirecting_to: Vòstre compte manda a %{acct}. + warning: + before: 'Abans de contunhar, volgatz legir aquestas nòtas amb atencion :' + other_data: Cap d’autra donada serà desplaçada automaticament moderation: title: Moderacion notification_mailer: @@ -698,11 +828,22 @@ oc: body: "%{name} a tornat partejar vòstre estatut :" subject: "%{name} a tornat partejar vòstre estatut" title: Novèl partatge + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T pagination: newer: Mai recents next: Seguent older: Mai ancians prev: Precedent + truncate: "…" polls: errors: already_voted: Avètz ja votat per aqueste sondatge @@ -715,13 +856,20 @@ oc: too_many_options: pòt pas contenir mai de %{max} opcions preferences: other: Autre + posting_defaults: Valors per defaut de las publicacions + public_timelines: Fluxes d’actualitats publics relationships: activity: Activitat del compte dormant: Inactiu + last_active: Darrièra activitat + most_recent: Mai recenta moved: Mudat mutual: Mutuala primary: Pirmà ria relationship: Relacion + remove_selected_domains: Levar totes los seguidors dels domenis seleccionats + remove_selected_followers: Levar los seguidors seleccionats + remove_selected_follows: Quitar de sègre las personas seleccionadas status: Estat del compte remote_follow: acct: Picatz vòstre utilizaire@domeni que que volètz utilizar per sègre aqueste utilizaire @@ -740,9 +888,6 @@ oc: reply: proceed: Contunhar per respondre prompt: 'Volètz respondre a aqueste tut :' - remote_unfollow: - title: TÃtol - unfollowed: Pas mai seguit scheduled_statuses: over_daily_limit: Avètz passat la limita de %{limit} tuts programats per aquel jorn over_total_limit: Avètz passat la limita de %{limit} tuts programats @@ -751,15 +896,47 @@ oc: activity: Darrièra activitat browser: Navigator browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox generic: Navigator desconegut + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo current_session: Session en cors description: "%{browser} sus %{platform}" explanation: Aquà los navigators connectats a vòstre compte Mastodon. + ip: IP platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac other: plataforma desconeguda + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone revoke: Revocar revoke_success: Session ben revocada + title: Sessions settings: + account: Compte + account_settings: Paramètres de compte + appearance: Aparéncia authorized_apps: Aplicacions autorizadas back: Tornar a Mastodon delete: Supression de compte @@ -767,10 +944,13 @@ oc: edit_profile: Modificar lo perfil export: Exportar de donadas featured_tags: Etiquetas en avant + identity_proofs: Pròvas d’identitat import: Importar de donadas + import_and_export: Import e export migrate: Migracion de compte notifications: Notificacions preferences: Preferéncias + profile: Perfil relationships: Abonaments e seguidors two_factor_authentication: Autentificacion en dos temps statuses: @@ -806,6 +986,7 @@ oc: visibilities: private: Seguidors solament private_long: Mostrar pas qu’als seguidors + public: Public public_long: Tot lo monde pòt veire unlisted: Pas listat unlisted_long: Tot lo monde pòt veire mai serà pas visible sul flux public @@ -813,6 +994,8 @@ oc: pinned: Tut penjat reblogged: a partejat sensitive_content: Contengut sensible + tags: + does_not_match_previous_name: correspond pas al nom precedent terms: body_html: | <h2>Politica de confidencialitat</h2> @@ -905,6 +1088,7 @@ oc: time: formats: default: Lo %d %b de %Y a %Ho%M + month: "%b de %Y" two_factor_authentication: code_hint: Picatz lo còdi generat per vòstra aplicacion d’autentificacion per confirmar description_html: S’activatz <strong> l’autentificacion two-factor</strong>, vos caldrà vòstre mobil per vos connectar perque generarà un geton per vos daissar dintrar. @@ -931,7 +1115,9 @@ oc: disable: Quand vòstre compte es gelat, las donadas d’aqueste demòran senceras, mas podètz pas realizar cap d’accion fins que siá desblocat. silence: Del temps que vòstre compte es limitat, solament lo monde que vos sègon veirà n vòstres tuts sus aqueste servidor, e poiriatz èsser tirat de mantunas listas publicas. Pasmens, d’autres vos pòdon sègre manualament. suspend: Vòstre compte es suspendut e totes vòstres tuts e fichièrs enviats son estats suprimits sens retorn possible d’aqueste servidor e los de vòstres seguidors. + get_in_touch: Podètz respondre a aqueste corrièl per contactar la còla de %{instance}. review_server_policies: Repassar las politicas del servidor + statuses: 'Especificament per :' subject: disable: Vòstre compte %{acct} es gelat none: Avertiment per %{acct} diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 78db4c6723440066a59675099917c0318409a41c..3c24a18caca7d0acdb01982ee46e4bd256600f07 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -17,11 +17,7 @@ pl: contact_unavailable: Nie dotyczy discover_users: Odkrywaj użytkowników documentation: Dokumentacja - extended_description_html: | - <h3>Dobre miejsce na zasady użytkowania</h3> - <p>Nie ustawiono jeszcze szczegółowego opisu</p> federation_hint_html: Z kontem na %{instance}, możesz Å›ledzić użytkowników każdego serwera Mastodona i nie tylko. - generic_description: "%{domain} jest jednym z serwerów sieci" get_apps: Spróbuj aplikacji mobilnej hosted_on: Mastodon uruchomiony na %{domain} learn_more: Dowiedz siÄ™ wiÄ™cej @@ -52,7 +48,7 @@ pl: many: Å›ledzÄ…cych one: Å›ledzÄ…cy other: ÅšledzÄ…cych - following: Åšledzonych + following: Å›ledzonych joined: DołączyÅ‚(a) %{date} last_active: ostatnio aktywny(-a) link_verified_on: WÅ‚asność tego odnoÅ›nika zostaÅ‚a sprawdzona %{date} @@ -481,21 +477,8 @@ pl: no_status_selected: Å»aden wpis nie zostaÅ‚ zmieniony, bo żaden nie zostaÅ‚ wybrany title: Wpisy konta with_media: Z zawartoÅ›ciÄ… multimedialnÄ… - subscriptions: - callback_url: URL zwrotny - confirmed: Potwierdzone - expires_in: Wygasa - last_delivery: Ostatnio dorÄ™czono - title: WebSub - topic: Temat tags: - accounts: Konta - hidden: Ukryte - hide: Ukryj w katalogu - name: Hasztag title: Hashtagi - unhide: Pokazuj w katalogu - visible: Widoczne title: Administracja warning_presets: add_new: Dodaj nowy @@ -536,7 +519,6 @@ pl: apply_for_account: PoproÅ› o zaproszenie change_password: HasÅ‚o checkbox_agreement_html: Zgadzam siÄ™ z <a href="%{rules_path}" target="_blank">reguÅ‚ami serwera</a> i <a href="%{terms_path}" target="_blank">zasadami korzystania z usÅ‚ugi</a> - confirm_email: Potwierdź adres e-mail delete_account: UsuniÄ™cie konta delete_account_html: Jeżeli chcesz usunąć konto, <a href="%{path}">przejdź tutaj</a>. Otrzymasz proÅ›bÄ™ o potwierdzenie. didnt_get_confirmation: Nie otrzymaÅ‚eÅ›(-aÅ›) instrukcji weryfikacji? @@ -583,28 +565,18 @@ pl: x_months: "%{count} miesiÄ™cy" x_seconds: "%{count}s" deletes: - bad_password_msg: NiezÅ‚a próba, hakerze! Wprowadzono nieprawidÅ‚owe hasÅ‚o confirm_password: Wprowadź aktualne hasÅ‚o, aby potwierdzić tożsamość - description_html: Ta opcja usunie <strong>bezpowrotnie i nieodwracalnie</strong> całą zawartość konta i zdezaktywuje je. Twoja nazwa użytkownika pozostanie zarezerwowana, aby zapobiec nadużyciom. proceed: UsuÅ„ konto success_msg: Twoje konto zostaÅ‚o pomyÅ›lnie usuniÄ™te - warning_html: Możemy usunąć zawartość jedynie w obrÄ™bie tego serwera. Zawartość udostÄ™pniona publicznie pozostawia trwaÅ‚e Å›lady. Serwery niepodłączone do sieci bÄ…dź nieÅ›ledzÄ…ce Twoich aktualizacji mogÄ… zachować Twoje dane. - warning_title: DostÄ™pność usuniÄ™tej zawartoÅ›ci directories: directory: Katalog profilów - enabled: JesteÅ› obecnie zapisany(-a) do katalogu - enabled_but_waiting: JesteÅ› zapisany(-a) do katalogu, ale jeszcze nie Å›ledzi CiÄ™ wystarczajÄ…ca liczba osób (%{min_followers}), aby siÄ™ tam pojawić. explanation: Poznaj profile na podstawie zainteresowaÅ„ explore_mastodon: Odkrywaj %{title} - how_to_enable: Nie jesteÅ› obecnie zapisany(-a) do katalogu. Poniżej możesz zapisać siÄ™. Użyj hashtagów w swoim opisie, aby zostać wyÅ›wietlonym pod okreÅ›lonymi hashtagami! - people: - few: "%{count} osoby" - many: "%{count} osób" - one: "%{count} osoba" - other: "%{count} osób" errors: + '400': The request you submitted was invalid or malformed. '403': Nie masz uprawnieÅ„, aby wyÅ›wietlić tÄ™ stronÄ™. '404': Strona, którÄ… próbujesz odwiedzić, nie istnieje. + '406': This page is not available in the requested format. '410': Strona, którÄ… próbujesz odwiedzić, przestaÅ‚a istnieć. '422': content: Sprawdzanie bezpieczeÅ„stwa nie powiodÅ‚o siÄ™. Czy blokujesz pliki cookie? @@ -613,6 +585,7 @@ pl: '500': content: Przepraszamy, coÅ› poszÅ‚o nie tak, po naszej stronie. title: Ta strona jest nieprawidÅ‚owa + '503': The page could not be served due to a temporary server failure. noscript_html: Aby korzystać z aplikacji Mastodon, włącz JavaScript. Możesz też skorzystać z jednej z <a href="%{apps_path}">natywnych aplikacji</a> obsÅ‚ugujÄ…cej Twoje urzÄ…dzenie. existing_username_validator: not_found: nie znaleziono lokalnego użytkownika o tej nazwie @@ -735,9 +708,6 @@ pl: too_many: Nie możesz załączyć wiÄ™cej niż 4 plików migrations: acct: nazwa@domena nowego konta - currently_redirecting: 'Obecnie Twoje konto przekierowuje do:' - proceed: Zapisz - updated_msg: PomyÅ›lnie zaktualizowano ustawienia migracji Twojego konta! moderation: title: Moderacja notification_mailer: @@ -838,10 +808,6 @@ pl: reply: proceed: Przejdź do dodawania odpowiedzi prompt: 'Chcesz odpowiedzieć na ten wpis:' - remote_unfollow: - error: Błąd - title: TytuÅ‚ - unfollowed: PrzestaÅ‚eÅ›(-aÅ›) Å›ledzić scheduled_statuses: over_daily_limit: PrzekroczyÅ‚eÅ›(-aÅ›) limit %{limit} zaplanowanych wpisów na ten dzieÅ„ over_total_limit: PrzekroczyÅ‚eÅ›(-aÅ›) limit %{limit} zaplanowanych wpisów @@ -1023,9 +989,9 @@ pl: <h3 id="children">Korzystanie ze strony przez dzieci</h3> - <p>Jeżeli serwer znajduje siÄ™ w UE lub w EOG: Ta strona, produkty i usÅ‚ugi sÄ… przeznaczone dla osób, które ukoÅ„czyÅ‚y 16 lat. Jeżeli nie ukoÅ„czyÅ‚eÅ›(-aÅ›) 16 roku życia, zgodnie z wymogami COPPA (<a href="https://pl.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Prawo o Ochronie PrywatnoÅ›ci Dzieci w Internecie</a>), nie używaj tej strony.</p> + <p>Jeżeli serwer znajduje siÄ™ w UE lub w EOG: Ta strona, produkty i usÅ‚ugi sÄ… przeznaczone dla osób, które ukoÅ„czyÅ‚y 16 lat. Jeżeli nie ukoÅ„czyÅ‚eÅ›(-aÅ›) 16 roku życia, zgodnie z wymogami RODO (<a href="https://pl.wikipedia.org/wiki/Ogólne_rozporzÄ…dzenie_o_ochronie_danych">Ogólne rozporzÄ…dzenie o ochronie danych</a>), nie używaj tej strony.</p> - <p>Jeżeli serwer znajduje siÄ™ w USA: Ta strona, produkty i usÅ‚ugi sÄ… przeznaczone dla osób, które ukoÅ„czyÅ‚y 13 lat. Jeżeli nie ukoÅ„czyÅ‚eÅ›(-aÅ›) 13 roku życia, zgodnie z wymogami RODO (<a href="https://pl.wikipedia.org/wiki/Ogólne_rozporzÄ…dzenie_o_ochronie_danych">Ogólne rozporzÄ…dzenie o ochronie danych</a>), nie używaj tej strony.</p> + <p>Jeżeli serwer znajduje siÄ™ w USA: Ta strona, produkty i usÅ‚ugi sÄ… przeznaczone dla osób, które ukoÅ„czyÅ‚y 13 lat. Jeżeli nie ukoÅ„czyÅ‚eÅ›(-aÅ›) 13 roku życia, zgodnie z wymogami COPPA (<a href="https://pl.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Prawo o Ochronie PrywatnoÅ›ci Dzieci w Internecie</a>), nie używaj tej strony.</p> <p>Wymogi mogÄ… być inne, jeżeli serwer znajduje siÄ™ w innym kraju.</p> diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 2f339b1edc0eec7a1e9224fccf932238007f34bd..c21d98063c7e46a1a920e50cd5699b8fb8a682ec 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -7,6 +7,7 @@ pt-BR: active_count_after: Ativo active_footnote: Usuários ativos mensais (UAM) administered_by: 'Administrado por:' + api: API apps: Apps apps_platforms: Use o Mastodon a partir de iOS, Android e outras plataformas browse_directory: Navegue pelo diretório de perfis e filtre por interesses @@ -16,11 +17,7 @@ pt-BR: contact_unavailable: Não disponÃvel discover_users: Descubra usuários documentation: Documentação - extended_description_html: | - <h3>Um bom lugar para regras</h3> - <p>A descrição da instância ainda não foi feita.</p> federation_hint_html: Com uma conta em %{instance} você vai poder seguir pessoas em qualquer servidor Mastodon ou outros do fediverso. - generic_description: "%{domain} é um servidor na rede" get_apps: Experimente um aplicativo hosted_on: Mastodon hospedado em %{domain} learn_more: Saiba mais @@ -31,6 +28,11 @@ pt-BR: status_count_before: Autores de tagline: Siga amigos e encontre novos terms: Termos de serviço + unavailable_content: Conteúdo indisponÃvel + unavailable_content_description: + reason: 'Motivo:' + suspended: Você não será capaz de seguir ninguém deste servidor, e nenhum dado dele será processado ou armazenado, e nenhum dado trocado. + unavailable_content_html: Mastodon geralmente permite que você veja o conteúdo e interaja com usuários de qualquer outro servidor no fediverso. Estas são as exceções deste servidor especÃfico. user_count_after: one: usuário other: usuários @@ -38,6 +40,7 @@ pt-BR: what_is_mastodon: O que é Mastodon? accounts: choices_html: 'Escolhas de %{name}:' + featured_tags_hint: Você pode destacar hashtags especÃficas que serão exibidas aqui. follow: Seguir followers: one: Seguidor @@ -49,11 +52,16 @@ pt-BR: media: MÃdia moved_html: "%{name} se mudou para %{new_profile_link}:" network_hidden: Esta informação não está disponÃvel + never_active: Nunca nothing_here: Não há nada aqui! people_followed_by: Pessoas que %{name} segue people_who_follow: Pessoas que seguem %{name} pin_errors: following: Você tem que estar seguindo a pessoa que você quer sugerir + posts: + one: Toot + other: Toots + posts_tab_heading: Toots posts_with_replies: Toots e respostas reserved_username: Este usuário está reservado roles: @@ -75,6 +83,7 @@ pt-BR: approve: Aprovar approve_all: Aprovar tudo are_you_sure: Você tem certeza? + avatar: Imagem de Perfil by_domain: DomÃnio change_email: changed_msg: E-mail da conta modificado com sucesso! @@ -105,9 +114,11 @@ pt-BR: header: Cabeçalho inbox_url: URL da caixa de entrada invited_by: Convidado por + ip: IP joined: Se cadastrou location: all: Todos + local: Local remote: Remoto title: Localização login_status: Situação de login @@ -163,6 +174,7 @@ pt-BR: statuses: Postagens subscribe: Inscrever-se suspended: Suspenso + time_in_queue: Esperando na fila por %{time} title: Contas unconfirmed_email: E-mail não confirmado undo_silenced: Retirar silenciamento @@ -170,6 +182,7 @@ pt-BR: unsubscribe: Desinscrever-se username: Nome de usuário warn: Notificar + web: Web action_logs: actions: assigned_to_self_report: "%{name} designou a denúncia %{target} para si" @@ -205,15 +218,18 @@ pt-BR: deleted_status: "(status deletado)" title: Auditar relatório custom_emojis: + assign_category: Designar Categoria by_domain: DomÃnio copied_msg: Cópia local do emoji criada com sucesso copy: Copiar copy_failed_msg: Não foi possÃvel criar uma cópia local deste emoji + create_new_category: Criar nova categoria created_msg: Emoji criado com sucesso! delete: Excluir destroyed_msg: Emoji deletado com sucesso! disable: Desabilitar disabled_msg: Emoji desabilitado com sucesso + emoji: Emoji enable: Habilitar enabled_msg: Emoji habilitado com sucesso image_hint: PNG de até 50KB @@ -224,6 +240,7 @@ pt-BR: shortcode: Atalho shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e underscores title: Emojis customizados + uncategorized: Não categorizado unlisted: Não listado update_failed_msg: Não foi possÃvel atualizar esse emoji updated_msg: Emoji atualizado com sucesso! @@ -236,13 +253,17 @@ pt-BR: feature_profile_directory: Diretório de perfis feature_registrations: Cadastros feature_relay: Repetidor da federação + feature_spam_check: Anti-spam feature_timeline_preview: pré-visualização da timeline features: Funcionalidades hidden_service: Federação com serviços onion open_reports: Denúncias em aberto + pending_tags: hashtags aguardando revisão + pending_users: usuários aguardando revisão recent_users: Usuários recentes search: Pesquisa em texto single_user_mode: Modo de usuário único + software: Software space: Uso de espaço em disco title: Painel de controle total_users: usuários no total @@ -255,6 +276,8 @@ pt-BR: created_msg: Bloqueio de domÃnio está sendo processado destroyed_msg: Bloqueio de domÃnio desfeito domain: DomÃnio + edit: Editar bloqueio de domÃnio + existing_domain_block_html: Você já impôs limites mais restritivos a %{name}, é necessário primeiro <a href="%{unblock_url}">desbloqueá-lo</a>. new: create: Criar bloqueio hint: O bloqueio de domÃnio não prevenirá a criação de entradas de contas na base de dados, mas vai reatroativa e automaticamente aplicar métodos especÃficos de moderação nestas contas. @@ -264,6 +287,8 @@ pt-BR: silence: Silêncio suspend: Suspensão title: Novo bloqueio de domÃnio + private_comment: Comentário privado + public_comment: Comentário público reject_media: Rejeitar arquivos de mÃdia reject_media_hint: Remove arquivos de mÃdia armazenados localmente e recusa quaisquer outros no futuro. Irrelevante para suspensões reject_reports: Rejeitar denúncias @@ -283,6 +308,7 @@ pt-BR: title: Retirar bloqueio de domÃnio de %{domain} undo: Retirar undo: Retirar bloqueio de domÃnio + view: Ver domÃnios bloqueados email_domain_blocks: add_new: Adicionar novo created_msg: Bloqueio de domÃnio de e-mail criado com sucesso @@ -306,6 +332,8 @@ pt-BR: all: Todas limited: Limitado title: Moderação + private_comment: Comentário privado + public_comment: Comentário público title: Federação total_blocked_by_us: Bloqueado por nós total_followed_by_them: Seguidos por eles @@ -381,6 +409,15 @@ pt-BR: custom_css: desc_html: Modificar o visual com CSS que é carregado em todas as páginas title: CSS customizado + default_noindex: + desc_html: Afeta qualquer usuário que não tenha modificado esta configuração manualmente + domain_blocks: + all: Para todo mundo + disabled: Para ninguém + title: Mostrar domÃnios bloqueados + users: Para usuários locais logados + domain_blocks_rationale: + title: Visualizar justificativa hero: desc_html: Aparece na página inicial. Ao menos 600x100px é recomendado. Se não estiver definido, o thumbnail da instância é usado no lugar title: Imagem de capa @@ -431,6 +468,8 @@ pt-BR: desc_html: Você pode escrever a sua própria polÃtica de privacidade, termos de serviço, entre outras coisas. Você pode usar tags HTML title: Termos de serviço customizados site_title: Nome da instância + spam_check_enabled: + title: Automação anti-spam thumbnail: desc_html: Usada para prévias via OpenGraph e API. Recomenda-se 1200x630px title: Miniatura da instância @@ -438,12 +477,15 @@ pt-BR: desc_html: Exibir a timeline pública na página inicial title: Prévia da timeline title: Configurações do site + trends: + title: Hashtags em alta statuses: back_to_account: Voltar para página da conta batch: delete: Deletar nsfw_off: Marcar como não-sensÃvel nsfw_on: Marcar como sensÃvel + deleted: ExcluÃdos failed_to_execute: Falha em executar media: title: MÃdia @@ -451,18 +493,16 @@ pt-BR: no_status_selected: Nenhum status foi modificado porque nenhum estava selecionado title: Postagens da conta with_media: Com mÃdia - subscriptions: - callback_url: URL de Callback - confirmed: Confirmado - expires_in: Expira em - last_delivery: Última entrega - topic: Tópico tags: - accounts: Contas - hidden: Escondido - hide: Esconder do diretório - unhide: Mostrar no diretório - visible: VisÃvel + context: Contexto + directory: No diretório + most_popular: Mais populares + most_recent: Mais recentes + name: Hashtag + title: Hashtags + trending_right_now: Em alta no momento + unreviewed: Não revisadas + updated_msg: Configurações de hashtag atualizadas com sucesso title: Administração warning_presets: add_new: Adicionar um novo @@ -478,8 +518,14 @@ pt-BR: body: "%{reporter} denunciou %{target}" body_remote: Alguém da instância %{domain} reportou %{target} subject: Nova denúncia sobre %{instance} (#%{id}) + appearance: + animations_and_accessibility: Animações e acessibilidade + confirmation_dialogs: Popups de confirmação + discovery: Descobrir + sensitive_content: Conteúdo sensÃvel application_mailer: notification_preferences: Mudar preferências de e-mail + salutation: "%{name}," settings: 'Mudar e-mail de preferência: %{link}' view: 'Visualizar:' view_profile: Ver perfil @@ -496,9 +542,13 @@ pt-BR: apply_for_account: Pedir um convite change_password: Senha checkbox_agreement_html: Eu concordo com <a href="%{rules_path}" target="_blank">as regras do servidor</a> e com <a href="%{terms_path}" target="_blank">os termos de serviço</a> - confirm_email: Confirmar e-mail + checkbox_agreement_without_rules_html: Concordo com os <a href="%{terms_path}" target="_blank">termos do serviço </a> delete_account: Excluir conta delete_account_html: Se você deseja excluir a sua conta, você pode <a href="%{path}">prosseguir para cá</a>. Uma confirmação será requisitada. + description: + prefix_invited_by_user: "@%{name} convidou você para entrar neste servidor do Mastodon!" + prefix_sign_up: Cadastre-se no Mastodon hoje! + suffix: Com uma conta, você poderá seguir pessoas, postar atualizações, trocar mensagens com usuários de qualquer servidor Mastodon e muito mais! didnt_get_confirmation: Não recebeu instruções de confirmação? forgot_password: Esqueceu a sua senha? invalid_reset_password_token: Token de modificação de senha é inválido ou expirou. Por favor, requisite um novo. @@ -507,6 +557,9 @@ pt-BR: migrate_account: Mudar para uma conta diferente migrate_account_html: Se você quer redirecionar essa conta para uma outra você pode <a href="%{path}">configurar isso aqui</a>. or_log_in_with: Ou faça login com + providers: + cas: CAS + saml: SAML register: Cadastrar-se registration_closed: "%{instance} não está aceitando novos membros" resend_confirmation: Reenviar instruções de confirmação @@ -525,8 +578,12 @@ pt-BR: return: Exibir o perfil do usuário web: Voltar para a página inicial title: Seguir %{acct} + challenge: + invalid_password: Senha inválida + prompt: Confirme sua senha para continuar datetime: distance_in_words: + about_x_hours: "%{count}h" about_x_months: "%{count} meses" about_x_years: "%{count} anos" almost_x_years: "%{count} anos" @@ -539,26 +596,25 @@ pt-BR: x_months: "%{count} meses" x_seconds: "%{count} segundos" deletes: - bad_password_msg: Boa tentativa, hackers! Senha incorreta + challenge_not_passed: As informações que você inseriu não estão corretas confirm_password: Insira a sua senha atual para verificar a sua identidade - description_html: Isto vai <strong>permanente e irreversivelmente</strong> remover conteúdo de sua conta e desativá-la. O seu nome de usuário permanecerá reservado para previnir futuros roubos de identidade. + confirm_username: Digite seu nome de usuário para confirmar o procedimento proceed: Excluir conta success_msg: A sua conta foi excluÃda com sucesso - warning_html: Apenas a exclusão de conteúdo desta instância em particular é garantida. Conteúdo que tenha sido largamente compartilhado muito provavelmente deixará traços. Servidores offline e servidores que se desinscreveram de suas atualizações não irão atualizar as suas bases de dados. - warning_title: Disponibilidade de conteúdo disseminado + warning: + before: 'Antes de prosseguir, por favor leia com cuidado:' + data_removal: Suas postagens e outros dados serão removidos permanentemente + username_available: Seu nome de usuário ficará disponÃvel novamente + username_unavailable: Seu nome de usuário permanecerá indisponÃvel directories: directory: Diretório de perfis - enabled: Você está na lista do diretório. - enabled_but_waiting: Você escolheu ser listado no diretório, mas você ainda não tem o mÃnimo de seguidores (%{min_followers}) para ser listado. explanation: Descobrir usuários baseado em seus interesses explore_mastodon: Explorar %{title} - how_to_enable: Você não se inscreveu no diretório. Você pode se inscrever abaixo. Use hashtags no texto da sua bio para ser listado em hashtags especÃficas! - people: - one: "%{count} pessoa" - other: "%{count} pessoas" errors: + '400': The request you submitted was invalid or malformed. '403': Você não tem permissão para ver esta página. '404': A página pela qual você está procurando não existe. + '406': This page is not available in the requested format. '410': A página pela qual você está procurando não existe mais. '422': content: A verificação de segurança falhou. Você desativou o uso de cookies? @@ -567,6 +623,7 @@ pt-BR: '500': content: Desculpe, algo deu errado. title: Esta página não está certa + '503': The page could not be served due to a temporary server failure. noscript_html: Para usar o aplicativo web do Mastodon, por favor ative o JavaScript. Ou, se quiser, experimente um dos <a href="%{apps_path}">apps nativos</a> para o Mastodon em sua plataforma. existing_username_validator: not_found: não foi possÃvel encontrar um usuário local com esse nome de usuário @@ -580,6 +637,7 @@ pt-BR: request: Solicitar o seu arquivo size: Tamanho blocks: Você bloqueou + csv: CSV domain_blocks: Bloqueios de domÃnio follows: Você segue lists: Listas @@ -684,9 +742,15 @@ pt-BR: too_many: Não é possÃvel anexar mais de 4 imagens migrations: acct: username@domain da nova conta - currently_redirecting: 'Seu perfil está configurado para redirecionar para:' - proceed: Salvar - updated_msg: As configurações de migração da sua conta foram atualizadas com sucesso! + incoming_migrations: Migrando de outra conta + moved_msg: Agora sua conta está redirecionando para %{acct} e seus seguidores estão sendo movidos. + not_redirecting: Sua conta não está redirecionando para nenhuma outra conta atualmente. + on_cooldown: Você migrou recentemente sua conta. Esta função ficará disponÃvel novamente em %{count} dias. + past_migrations: Migrações passadas + proceed_with_move: Migrar seguidores + redirecting_to: Sua conta está redirecionando para %{acct}. + warning: + before: 'Antes de prosseguir, por favor leia com cuidado:' moderation: title: Moderação notification_mailer: @@ -770,10 +834,6 @@ pt-BR: reply: proceed: Proceder para responder prompt: 'Você quer responder à esse toot:' - remote_unfollow: - error: Erro - title: TÃtulo - unfollowed: Deixou de seguir scheduled_statuses: over_daily_limit: Você excedeu o limite de %{limit} toots planejados para esse dia over_total_limit: Você excedeu o limite de %{limit} toots planejados @@ -782,13 +842,40 @@ pt-BR: activity: Última atividade browser: Navegador browsers: + alipay: Alipay + blackberry: BlackBerry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox generic: Navegador desconhecido + ie: Internet Explorer + micro_messenger: MicroMessenger nokia: Navegador Nokia S40 Ovi + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo current_session: Sessão atual description: "%{browser} em %{platform}" explanation: Estes são os navegadores que estão conectados com a sua conta do Mastodon. + ip: IP platforms: + adobe_air: Adobe Air + android: Android + blackberry: BlackBerry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac other: Plataforma desconhecida + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone revoke: Revogar revoke_success: Sessão revogada com sucesso title: Sessões @@ -842,6 +929,7 @@ pt-BR: vote: Votar show_more: Mostrar mais sign_in_to_participate: Entre para participar dessa conversa + title: '%{name}: "%{quote}"' visibilities: private: Apenas seguidores private_long: Mostrar apenas para seguidores @@ -853,6 +941,8 @@ pt-BR: pinned: Toot fixado reblogged: compartilhou sensitive_content: Conteúdo sensÃvel + tags: + does_not_match_previous_name: não corresponde ao nome anterior terms: body_html: | <h2>PolÃtica de privacidade</h2> @@ -969,6 +1059,7 @@ pt-BR: disable: Enquanto sua conta está congelada, seus dados estão intactos, mas você não pode realizar nenhuma ação até que sua conta seja desbloqueada. silence: Enquanto sua conta está limitada, somente pessoas que já estão seguindo você poderão ver seus toots nesse servidor, e você pode ser excluÃdo de diversas listagens públicas. No entanto, outros ainda podem seguir você manualmente. suspend: Sua conta está suspensa e todos os seus toots e mÃdias foram irreversivelmente removidas desse servidor e de servidores onde você tinha seguidores. + get_in_touch: Você pode responder a este e-mail para entrar em contato com a equipe de %{instance}. review_server_policies: Revisar as polÃticas do servidor subject: disable: Sua conta %{acct} foi congelada diff --git a/config/locales/pt.yml b/config/locales/pt-PT.yml similarity index 95% rename from config/locales/pt.yml rename to config/locales/pt-PT.yml index 9cd92f6bd8fc72f69540fc99e3e928c97142cd76..fe23160a2cd980ffd489b981878cf906f0d50adb 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt-PT.yml @@ -1,5 +1,5 @@ --- -pt: +pt-PT: about: about_hashtag_html: Estes são toots públicos marcados com <strong>#%{hashtag}</strong>. Podes interagir com eles se tiveres uma conta Mastodon. about_mastodon_html: Mastodon é uma rede social baseada em protocolos abertos da web e software livre e gratuito. É descentralizado como e-mail. @@ -10,10 +10,6 @@ pt: contact_missing: Não configurado contact_unavailable: n.d. documentation: Documentação - extended_description_html: | - <h3>Um bom lugar para regras</h3> - <p>A descrição estendida ainda não foi configurada.</p> - generic_description: "%{domain} é um servidor na rede" hosted_on: Mastodon em %{domain} learn_more: Saber mais privacy_policy: PolÃtica de privacidade @@ -431,18 +427,6 @@ pt: no_status_selected: Nenhum estado foi alterado porque nenhum foi selecionado title: Estado das contas with_media: Com media - subscriptions: - callback_url: URL de Callback - confirmed: Confirmado - expires_in: Expira em - last_delivery: Última entrega - topic: Tópico - tags: - accounts: Contas - hidden: Escondidas - hide: Esconder no diretório - unhide: Mostrar no diretório - visible: VisÃvel title: Administração warning_presets: add_new: Adicionar novo @@ -471,7 +455,6 @@ pt: your_token: O teu token de acesso auth: change_password: Palavra-passe - confirm_email: Confirmar e-mail delete_account: Eliminar conta delete_account_html: Se desejas eliminar a conta, podes <a href="%{path}">continua aqui</a>. Uma confirmação será pedida. didnt_get_confirmation: Não recebeu o email de confirmação? @@ -512,28 +495,18 @@ pt: x_months: "%{count} meses" x_seconds: "%{count} segundos" deletes: - bad_password_msg: Boa tentativa, hackers! Palavra-passe incorreta confirm_password: Introduz a palavra-passe atual para verificar a tua identidade - description_html: Isto vai <strong>permanente e irreversivelmente</strong> remover conteúdo da tua conta e desativá-la. O teu nome de utilizador permanecerá reservado para prevenir futuros roubos de identidade. proceed: Eliminar conta success_msg: A tua conta foi eliminada com sucesso - warning_html: |- - Apenas a eliminação de conteúdo desta instância é garantido. - Conteúdo que tenha sido partilhado com outras instâncias muito provavelmente deixará pegadas. Servidores offline e servidores que se desinscreveram das tuas atualizações não atualizarão as suas bases de dados. - warning_title: Disponibilidade de conteúdo disseminado directories: directory: Dirétorio de perfil - enabled: Neste momento tu estás listado no dirétorio. - enabled_but_waiting: Tu escolheste ser listado no diretório, mas ainda não tens o número mÃnimo de seguidores (%{min_followers}) para integrares esta lista. explanation: Descobre utilizadores com base nos seus interesses explore_mastodon: Explorar %{title} - how_to_enable: Tu ainda não integras este directório. Podes fazer isso abaixo. Usa hashtags na tua biografia para seres listado em hashtags especÃficas! - people: - one: "%{count} pessoa" - other: "%{count} pessoas" errors: + '400': The request you submitted was invalid or malformed. '403': Não tens a permissão necessária para ver esta página. '404': A página que estás a procurar não existe. + '406': This page is not available in the requested format. '410': A página que estás a procurar não existe mais. '422': content: "A verificação de segurança falhou. \nDesativaste o uso de cookies?" @@ -542,6 +515,7 @@ pt: '500': content: Desculpe, mas algo correu mal. title: Esta página não está correta + '503': The page could not be served due to a temporary server failure. noscript_html: Para usar o aplicativo web do Mastodon, por favor ativa o JavaScript. Alternativamente, experimenta um dos <a href="%{apps_path}">apps nativos</a> para o Mastodon na sua plataforma. exports: archive_takeout: @@ -634,9 +608,6 @@ pt: too_many: Não é possÃvel anexar mais de 4 arquivos migrations: acct: username@domain da nova conta - currently_redirecting: 'O teu perfil está configurado para redirecionar para:' - proceed: Salvar - updated_msg: As configurações de migração da tua conta foram atualizadas com sucesso! moderation: title: Moderação notification_mailer: @@ -707,10 +678,6 @@ pt: reply: proceed: Prosseguir com resposta prompt: 'Queres responder a esta publicação:' - remote_unfollow: - error: Erro - title: TÃtulo - unfollowed: Não seguido scheduled_statuses: over_daily_limit: Excedeste o limite de %{limit} publicações agendadas para esse dia over_total_limit: Tu excedeste o limite de %{limit} publicações agendadas diff --git a/config/locales/ro.yml b/config/locales/ro.yml index 6e6c6f403ec985c1a3485018f82564adb683d6b8..7deab602139c2b16289ad21c472521fb29d158d9 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -4,7 +4,6 @@ ro: hosted_on: Mastodon găzduit de %{domain} auth: change_password: Parolă - confirm_email: Confirmă email delete_account: Șterge contul delete_account_html: Dacă vrei să È™tergi acest cont <a href="%{path}">poÈ›i începe aici</a>. Va trebui să confirmi această acÈ›iune. didnt_get_confirmation: Nu ai primit instrucÈ›iunile de confirmare? @@ -44,22 +43,17 @@ ro: x_days: "%{count}z" x_months: "%{count}l" deletes: - bad_password_msg: Bună încercare, hackere! Parolă incorectă confirm_password: Introdu parola curentă pentru a-È›i verifica identitatea - description_html: Această acÈ›iune este <strong>permanentă È™i ireversibilă,</strong> elimină conÈ›inutul È™i dezactivează contul tău. Acest username va rămâne permanent rezervat pentru a evita furtul de identitate. proceed: Șterge contul success_msg: Contul tău a fost È™terg. Nu mai poate fi recuperat :D - warning_html: Doar È™tergerea conÈ›inutului de pe acest server este garantată. ConÈ›inutul tău care a fost redistribuit în alte instaÈ›e e posibil să lase urme. Serverele deconecate sau care nu mai sunt abonate la actualizările contului tău nu își vor mai actualiza baza de date. directories: explanation: Descoperă oameni È™i companii în funcÈ›ie de interesele lor explore_mastodon: Explorează %{title} - people: - few: "%{count} persoană" - one: "%{count} persoană" - other: "%{count} oameni" errors: + '400': The request you submitted was invalid or malformed. '403': Nu ai permisiunea să vizitezi această pagină. '404': Pagina pe care o cauÈ›i nu există. + '406': This page is not available in the requested format. '410': Pagina pe care o cauÈ›i nu mai există. '422': content: Verificarea securității a eÈ™uat. Ai blocat cookiurile? @@ -68,6 +62,7 @@ ro: '500': content: Ne pare rău, dar ceva a funcÈ›ionat greÈ™it. ÃŽncercaÈ›i din nou!? title: Această pagină nu este corectă + '503': The page could not be served due to a temporary server failure. noscript_html: Pentru a utiliza o aplicaÈ›ie web Mastodon, te rog activează JavaScript. Alternativ, încearcă una din <a href="%{apps_path}">aplicaÈ›iile native</a> Mastodon pentru platforma ta. exports: archive_takeout: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 7e336be984e7590919fc80a3c2f9fde8f61b807c..df4a040488c7556c993113fae9d5fc24b535e91f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -17,11 +17,7 @@ ru: contact_unavailable: неизв. discover_users: Ðаходите пользователей documentation: Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ - extended_description_html: | - <h3>Хорошее меÑто Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»</h3> - <p>РаÑширенное опиÑание еще не наÑтроено.</p> federation_hint_html: С учётной запиÑью на %{instance} вы Ñможете подпиÑыватьÑÑ Ð½Ð° людей Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ Ñервера Mastodon и не только. - generic_description: "%{domain} - один из Ñерверов Ñети" get_apps: Попробуйте мобильное приложение hosted_on: Mastodon размещен на %{domain} learn_more: Узнать больше @@ -37,6 +33,11 @@ ru: status_count_before: Опубликовано tagline: ПодпиÑывайтеÑÑŒ на друзей и заводите новые знакомÑтва terms: УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ + unavailable_content: Содержимое недоÑтупно + unavailable_content_description: + domain: Сервер + reason: Причина + rejecting_media: 'Медиафайлы Ñ Ñтих Ñерверов не будут обработаны или Ñохранены. Их миниатюры не будут отображатьÑÑ Ð¸ вам придётÑÑ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ нажимать на иÑходный файл:' user_count_after: few: Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ many: пользователей @@ -59,6 +60,7 @@ ru: media: Медиа moved_html: "%{name} переехал(а) на %{new_profile_link}:" network_hidden: Ðта Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупна + never_active: Ðикогда nothing_here: ЗдеÑÑŒ ничего нет! people_followed_by: Люди, на которых подпиÑан(а) %{name} people_who_follow: ПодпиÑчики %{name} @@ -119,7 +121,7 @@ ru: followers: ПодпиÑчики followers_url: URL подпиÑчиков follows: ПодпиÑки - header: Заголовок + header: Шапка inbox_url: URL входÑщих invited_by: Приглашение выдал(а) ip: IP @@ -171,6 +173,7 @@ ru: moderator: Модератор staff: ПерÑонал user: Пользователь + salmon_url: Страница Salmon search: ПоиÑк shared_inbox_url: URL общих входÑщих show: @@ -181,6 +184,7 @@ ru: statuses: СтатуÑÑ‹ subscribe: ПодпиÑатьÑÑ suspended: Заморожен + time_in_queue: Ожидание в очереди %{time} title: Ðккаунты unconfirmed_email: Ðеподтверждённый e-mail undo_silenced: СнÑть глушение @@ -189,6 +193,7 @@ ru: username: Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ warn: Предупредить web: Веб + whitelisted: Ð’ белом ÑпиÑке action_logs: actions: assigned_to_self_report: "%{name} назначил(а) жалобу %{target} на ÑебÑ" @@ -224,19 +229,24 @@ ru: deleted_status: "(удалённый ÑтатуÑ)" title: Журнал Ñобытий custom_emojis: + assign_category: Задать категорию by_domain: Домен copied_msg: Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ Ñмодзи уÑпешно Ñоздана copy: Копировать copy_failed_msg: Ðе удалоÑÑŒ Ñоздать локальную копию Ñмодзи + create_new_category: Создать новую категорию created_msg: Ðмодзи уÑпешно Ñоздано! delete: Удалить destroyed_msg: Ðмодзи уÑпешно удалено! disable: Отключить + disabled: Отключено disabled_msg: Ðмодзи уÑпешно отключено emoji: Ðмодзи enable: Включить + enabled: Включено enabled_msg: Ðмодзи уÑпешно включено image_hint: PNG до 50KB + list: СпиÑок listed: Ð’ ÑпиÑке new: title: Добавить новый Ñмодзи @@ -244,11 +254,13 @@ ru: shortcode: Шорткод shortcode_hint: Как минимум 2 Ñимвола, только алфавитно-цифровые Ñимволы и Ð¿Ð¾Ð´Ñ‡ÐµÑ€ÐºÐ¸Ð²Ð°Ð½Ð¸Ñ title: СобÑтвенные Ñмодзи + uncategorized: Вне категорий unlisted: Ðе в ÑпиÑке update_failed_msg: Ðевозможно обновить Ñтот Ñмодзи updated_msg: Ðмодзи обновлён! upload: Загрузить dashboard: + authorized_fetch_mode: Защищённый режим backlog: задачи config: ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ feature_deletions: Удаление аккаунтов @@ -256,10 +268,13 @@ ru: feature_profile_directory: Каталог профилей feature_registrations: РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ feature_relay: РетранÑлÑторы + feature_spam_check: Ðнти-Ñпам feature_timeline_preview: ПредпроÑмотр ленты features: ВозможноÑти hidden_service: Ð¤ÐµÐ´ÐµÑ€Ð°Ñ†Ð¸Ñ Ñо Ñкрытыми ÑервиÑами open_reports: открытых жалоб + pending_tags: Ñ…Ñштеги, ожидающие проверки + pending_users: пользователи, ожидающие проверки recent_users: Ðедавние пользователи search: ПолнотекÑтовый поиÑк single_user_mode: ОднопользовательÑкий режим @@ -267,15 +282,22 @@ ru: space: ИÑпользовано меÑта title: Панель ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ total_users: вÑего пользователей - trends: Тренды + trends: Ðктуальное week_interactions: взаимодейÑтвий на Ñтой неделе week_users_active: активно на Ñтой неделе week_users_new: пользователей на Ñтой неделе + whitelist_mode: Белый ÑпиÑок + domain_allows: + add_new: ВнеÑти в белый ÑпиÑок + created_msg: Домен добавлен в белый ÑпиÑок + destroyed_msg: Домен убран из белого ÑпиÑка + undo: Убрать из белого ÑпиÑка domain_blocks: add_new: Заблокировать домен created_msg: Блокировка домена обрабатываетÑÑ destroyed_msg: Блокировка домена ÑнÑта domain: Домен + edit: Редактировать блокировку new: create: Создать блокировку hint: Блокировка домена не предотвратит Ñоздание новых аккаунтов в базе данных, но ретроактивно и автоматичеÑки применит указанные методы модерации Ð´Ð»Ñ Ñтих аккаунтов. @@ -285,6 +307,8 @@ ru: silence: Глушение suspend: Блокировка title: ÐÐ¾Ð²Ð°Ñ Ð´Ð¾Ð¼ÐµÐ½Ð½Ð°Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ° + private_comment: Приватный комментарий + public_comment: Публичный комментарий reject_media: Запретить медиаконтент reject_media_hint: УдалÑет локально хранимый медиаконтент и запрещает его загрузку в будущем. Ðе имеет Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð² Ñлучае блокировки. reject_reports: ОтклонÑть жалобы @@ -331,9 +355,11 @@ ru: all: Ð’Ñе limited: Ограниченные title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ñ + private_comment: Приватный комментарий + public_comment: Публичный комментарий title: ИзвеÑтные узлы total_blocked_by_us: Заблокировано нами - total_followed_by_them: Заблокировано ими + total_followed_by_them: Их подпиÑчиков total_followed_by_us: Ðаших подпиÑчиков total_reported: Жалобы на них total_storage: Медиафайлы @@ -408,6 +434,13 @@ ru: custom_css: desc_html: Измените внешний вид Ñ CSS, загружаемым на каждой Ñтранице title: ОÑобый CSS + default_noindex: + title: ИÑключить пользователей из индекÑации поиÑковиками по умолчанию + domain_blocks: + all: Ð’Ñем + disabled: Ðикому + title: Доменные блокировки + users: Залогиненным локальным пользователÑм hero: desc_html: ОтображаетÑÑ Ð½Ð° главной Ñтранице. РекомендуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ не менее 600Ñ…100px. ЕÑли не уÑтановлено, иÑпользуетÑÑ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ узла title: Баннер узла @@ -458,6 +491,8 @@ ru: desc_html: Ð’Ñ‹ можете добавить Ñюда ÑобÑтвенную политику конфиденциальноÑти, пользовательÑкое Ñоглашение и другие документы. Можно иÑпользовать теги HTML title: УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ site_title: Ðазвание Ñайта + spam_check_enabled: + title: Ðнти-Ñпам thumbnail: desc_html: ИÑпользуетÑÑ Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¿Ñ€Ð¾Ñмотра Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ OpenGraph и API. РекомендуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ 1200x630px title: Картинка узла @@ -465,12 +500,15 @@ ru: desc_html: Показывать публичную ленту на приветÑтвенной Ñтранице title: ПредпроÑмотр ленты title: ÐаÑтройки Ñайта + trends: + title: ПопулÑрные Ñ…Ñштеги statuses: back_to_account: Ðазад к Ñтранице аккаунта batch: delete: Удалить nsfw_off: Выключить NSFW nsfw_on: Включить NSFW + deleted: Удалено failed_to_execute: Ðе удалоÑÑŒ выполнить media: title: Медиаконтент @@ -478,21 +516,13 @@ ru: no_status_selected: Ðе выбран ни один ÑтатуÑ, ничего не изменено title: СтатуÑÑ‹ аккаунта with_media: С медиаконтентом - subscriptions: - callback_url: Callback URL - confirmed: Подтверждено - expires_in: ИÑтекает через - last_delivery: ПоÑледнÑÑ Ð´Ð¾Ñтавка - title: WebSub - topic: Тема tags: - accounts: Ðккаунты - hidden: Скрыты - hide: Скрыть из каталога - name: Ð¥Ñштег + accounts_today: Уникальных иÑпользований за ÑÐµÐ³Ð¾Ð´Ð½Ñ + accounts_week: Уникальных иÑпользований за Ñту неделю + context: КонтекÑÑ‚ + directory: Ð’ каталоге + in_directory: "%{count} в каталоге" title: Ð¥Ñштеги - unhide: Показывать в каталоге - visible: Видны title: ÐдминиÑтрирование warning_presets: add_new: Добавить @@ -510,6 +540,8 @@ ru: subject: ÐÐ¾Ð²Ð°Ñ Ð¶Ð°Ð»Ð¾Ð±Ð°, узел %{instance} (#%{id}) appearance: advanced_web_interface: Многоколоночный Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ + confirmation_dialogs: Окна подтверждений + discovery: Обзор sensitive_content: ЧувÑтвительное Ñодержимое application_mailer: notification_preferences: Изменить наÑтройки e-mail @@ -530,7 +562,6 @@ ru: apply_for_account: ЗапроÑить приглашение change_password: Пароль checkbox_agreement_html: Я ÑоглашаюÑÑŒ Ñ <a href="%{rules_path}" target="_blank">правилами Ñервера</a> и <a href="%{terms_path}" target="_blank">УÑловиÑми иÑпользованиÑ</a> - confirm_email: Подтвердите email delete_account: Удалить аккаунт delete_account_html: ЕÑли Ð’Ñ‹ хотите удалить Ñвой аккаунт, вы можете <a href="%{path}">перейти Ñюда</a>. У Ð’Ð°Ñ Ð±ÑƒÐ´ÐµÑ‚ запрошено подтверждение. didnt_get_confirmation: Ðе получили инÑтрукцию Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ? @@ -539,7 +570,7 @@ ru: login: Войти logout: Выйти migrate_account: ПеренеÑти аккаунт - migrate_account_html: ЕÑли Ð’Ñ‹ хотите перенеÑти Ñтот аккаунт на другой, вы можете <a href="%{path}">Ñделать Ñто здеÑÑŒ</a>. + migrate_account_html: ЕÑли вы хотите перенаправить подпиÑчиков на другой аккаунт, Ñто можно <a href="%{path}">наÑтроить здеÑÑŒ</a>. or_log_in_with: Или войти Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ providers: cas: CAS @@ -550,6 +581,9 @@ ru: reset_password: СброÑить пароль security: БезопаÑноÑть set_new_password: Задать новый пароль + status: + functional: Ваш аккаунт в полном порÑдке. + redirecting_to: Ваш аккаунт признан неактивным, потому что он перенаправлÑет на %{acct}. trouble_logging_in: Ðе удаётÑÑ Ð²Ð¾Ð¹Ñ‚Ð¸? authorize_follow: already_following: Ð’Ñ‹ уже подпиÑаны на Ñтот аккаунт @@ -562,6 +596,9 @@ ru: return: ВернутьÑÑ Ðº профилю Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ web: Перейти к WWW title: ПодпиÑатьÑÑ Ð½Ð° %{acct} + challenge: + invalid_password: Ðеверный пароль + prompt: Введите пароль Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ datetime: distance_in_words: about_x_hours: "%{count}ч" @@ -577,28 +614,30 @@ ru: x_months: "%{count}меÑ" x_seconds: "%{count}Ñек" deletes: - bad_password_msg: Ðе вышло, хакеры! Ðеверный пароль + challenge_not_passed: Ð’Ð²ÐµÐ´Ñ‘Ð½Ð½Ð°Ñ Ð²Ð°Ð¼Ð¸ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½ÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð° confirm_password: Введите текущий пароль Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð’Ð°ÑˆÐµÐ¹ личноÑти - description_html: Ðто дейÑтвие <strong>перманентно и необратимо</strong> удалит контент Вашего аккаунта и деактивирует его. Ваше Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð±ÑƒÐ´ÐµÑ‚ зарезервировано Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¸Ð¼Ð¿ÐµÑ€Ñонации в будущем. + confirm_username: Введите Ñвой юзернейм Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ proceed: Удалить аккаунт success_msg: Ваш аккаунт был уÑпешно удален - warning_html: ГарантируетÑÑ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ðµ контента только на Ñтом узле. Широко раÑпроÑтранившийÑÑ ÐºÐ¾Ð½Ñ‚ÐµÐ½Ñ‚, Ñкорее вÑего, оÑтавит Ñледы. Сервера, отключенные от Ñети или отпиÑавшиеÑÑ Ð¾Ñ‚ Ваших обновлений, не обновÑÑ‚ Ñвои базы данных. - warning_title: О доÑтупноÑти раÑпроÑтранившегоÑÑ ÐºÐ¾Ð½Ñ‚ÐµÐ½Ñ‚Ð° + warning: + before: 'Внимательно прочитайте Ñледующую информацию перед началом:' + caches: Содержимое, которое было закÑшировано другими Ñерверами, может ÑохранитьÑÑ + data_removal: Ваши запиÑи и прочие данные будут безвозвратно удалены + email_change_html: <a href="%{path}">ПоменÑть Ñвой e-mail</a> можно не удалÑÑ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚ + irreversible: Ð’Ñ‹ не Ñможете воÑÑтановить или повторно активировать Ñвой аккаунт + username_available: Ваш юзернейм Ñнова Ñтанет доÑтупным + username_unavailable: Ваш юзернейм оÑтанетÑÑ Ð½ÐµÐ´Ð¾Ñтупным directories: directory: Каталог профилей - enabled: Ð’ наÑтоÑщий момент вы указаны в каталоге. - enabled_but_waiting: Ð’Ñ‹ ÑоглаÑилиÑÑŒ находитьÑÑ Ð² каталоге, но у Ð²Ð°Ñ ÐµÑ‰Ñ‘ нет необходимого количеÑтва подпиÑчиков (%{min_followers}), чтобы оказатьÑÑ Ð² каталоге. explanation: Ðаходите пользователей по интереÑам explore_mastodon: Изучайте %{title} - how_to_enable: Ð’Ñ‹ ещё не находитеÑÑŒ в каталоге. Можете добавитьÑÑ Ð½Ð¸Ð¶Ðµ. ИÑпользуйте Ñ…Ñштеги в разделе "о Ñебе", чтобы Ð²Ð°Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ð»Ð¸ по Ñтим Ñ…Ñштегам! - people: - few: "%{count} человека" - many: "%{count} человек" - one: "%{count} человек" - other: "%{count} человек" + domain_validator: + invalid_domain: не ÑвлÑетÑÑ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ñ‹Ð¼ доменным именем errors: + '400': Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð±Ñ‹Ð» недейÑтвительным или неправильным. '403': У Ð’Ð°Ñ Ð½ÐµÑ‚ доÑтупа к проÑмотру Ñтой Ñтраницы. '404': Страница, которую Ð’Ñ‹ иÑкали, не ÑущеÑтвует. + '406': This page is not available in the requested format. '410': Страница, которую Ð’Ñ‹ иÑкали, больше не ÑущеÑтвует. '422': content: Проверка безопаÑноÑти не удалаÑÑŒ. Возможно, Ð’Ñ‹ блокируете cookies? @@ -607,6 +646,7 @@ ru: '500': content: ПриноÑим извинениÑ, но на нашей Ñтороне что-то пошло не так. title: Страница неверна + '503': Страница не может быть отображена из-за временного ÑÐ±Ð¾Ñ Ð½Ð° Ñервере. noscript_html: Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Mastodon, пожалуйÑта, включите JavaScript. Кроме того, вы можете иÑпользовать одно из <a href="%{apps_path}">приложений</a> Mastodon Ð´Ð»Ñ Ð’Ð°ÑˆÐµÐ¹ платформы. existing_username_validator: not_found: не удалоÑÑŒ найти локального Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем @@ -650,6 +690,7 @@ ru: developers: Разработчикам more: Ещё… resources: СÑылки + trending_now: Ðктуально ÑÐµÐ¹Ñ‡Ð°Ñ generic: all: Ð’Ñе changes_saved_msg: Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑƒÑпешно Ñохранены! @@ -714,8 +755,8 @@ ru: many: "%{count} иÑп." one: 1 иÑп other: "%{count} иÑп" - max_uses_prompt: Без лимита - prompt: Генерируйте и делитеÑÑŒ ÑÑылками Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸, чтобы предоÑтавить им доÑтуп к Ñтому узлу + max_uses_prompt: Без Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ + prompt: Создавайте и делитеÑÑŒ ÑÑылками Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸, чтобы предоÑтавить им доÑтупом к Ñтому узлу table: expires_at: ИÑтекает uses: ИÑп. @@ -729,9 +770,27 @@ ru: too_many: ÐÐµÐ»ÑŒÐ·Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ более 4 файлов migrations: acct: имÑ@домен нового аккаунта - currently_redirecting: 'Ваш профиль будет перенаправлен на:' - proceed: Сохранить - updated_msg: ÐаÑтройки миграции вашего аккаунта обновлены! + cancel: Отменить переезд + cancel_explanation: Отмена Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾ активирует аккаунт, но не вернёт обратно подпиÑчиков, которые были перемещены на тот аккаунт. + cancelled_msg: Переезд был уÑпешно отменён. + errors: + on_cooldown: Ð’Ñ‹ пока не можете переезжать + followers_count: ПодпиÑчиков на момент переезда + incoming_migrations: Переезд Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð³Ð¾ аккаунта + moved_msg: Ваш аккаунт теперь перенаправлÑет на %{acct} и подпиÑчики перемещаютÑÑ Ñ‚ÑƒÐ´Ð°. + not_redirecting: Ваш аккаунт пока что не перенаправлÑет на любой другой аккаунт. + on_cooldown: Ð’Ñ‹ уже недавно переноÑили Ñвой аккаунт. Ðта возможноÑть будет Ñнова доÑтупна через %{count} дн. + past_migrations: Прошлые переезды + proceed_with_move: ПеренеÑти подпиÑчиков + redirecting_to: Ваша ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ перенаправлена на %{acct}. + set_redirect: ÐаÑтроить перенаправление + warning: + before: 'Прежде чем продолжить, внимательно прочитайте Ñледующую информацию:' + cooldown: ПоÑле переезда наÑтупает период, в течение которого вы не Ñможете ещё раз переехать + disabled_account: Ваш текущий аккаунт впоÑледÑтвии Ð½ÐµÐ»ÑŒÐ·Ñ Ð±ÑƒÐ´ÐµÑ‚ больше иÑпользовать. При Ñтом, у Ð²Ð°Ñ Ð±ÑƒÐ´ÐµÑ‚ доÑтуп к ÑкÑпорту данных, а также к повторной активации аккаунта. + followers: Ðто дейÑтвие перенеÑёт вÑех ваших подпиÑчиков Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ аккаунта на новый + other_data: Ðикакие другие данные не будут автоматичеÑки перенеÑены + redirect: Профиль Ñтого аккаунта будет обновлён Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ¾Ð¹ о перенаправлении, а также иÑключён из поиÑка moderation: title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ð¸Ñ notification_mailer: @@ -800,6 +859,7 @@ ru: too_many_options: может Ñодержать не больше %{max} вариантов preferences: other: Другое + posting_defaults: ÐаÑтройки отправки по умолчанию public_timelines: Публичные ленты relationships: activity: ÐктивноÑть аккаунта @@ -831,10 +891,6 @@ ru: reply: proceed: Ответить prompt: 'Ð’Ñ‹ ÑобираетеÑÑŒ ответить на Ñтот ÑтатуÑ:' - remote_unfollow: - error: Ошибка - title: Заголовок - unfollowed: ОтпиÑаны scheduled_statuses: over_daily_limit: Ð’Ñ‹ превыÑили лимит в %{limit} запланированных поÑтов на указанный день over_total_limit: Ð’Ñ‹ превыÑили лимит на %{limit} запланированных поÑтов @@ -883,6 +939,7 @@ ru: settings: account: Ð£Ñ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ account_settings: ÐаÑтройки учётной запиÑи + aliases: ПÑевдонимы аккаунта appearance: Внешний вид authorized_apps: Ðвторизованные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ back: Ðазад в Mastodon @@ -1063,6 +1120,7 @@ ru: silence: Пока ваш аккаунт ограничен, ваши поÑты на Ñтом Ñервере увидÑÑ‚ только ваши дейÑтвующие подпиÑчики, а ваш аккаунт может быть иÑключён из различных каталогов. Впрочем, оÑтальные могут подпиÑатьÑÑ Ð½Ð° Ð²Ð°Ñ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ. suspend: Ваш аккаунт заблокирован и вÑе ваши поÑты и загруженные медиафайлы безвозвратно удалены Ñ Ñтого Ñервера и других Ñерверов, где у Ð²Ð°Ñ Ð±Ñ‹Ð»Ð¸ подпиÑчики. review_server_policies: ПоÑмотреть правила Ñервера + statuses: 'Ð’ чаÑтноÑти, длÑ:' subject: disable: Ваш аккаунт %{acct} заморожен none: "%{acct}, вам вынеÑено предупреждение" @@ -1075,7 +1133,7 @@ ru: suspend: Ðккаунт заблокирован welcome: edit_profile_action: ÐаÑтроить профиль - edit_profile_step: Ð’Ñ‹ можете наÑтроить Ñвой профиль, загрузив аватар, обложку, Ñменив Ð¸Ð¼Ñ Ð¸ много чего ещё. ЕÑли вы хотите фильтровать подпиÑчиков до того, как они Ñмогут на Ð²Ð°Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑатьÑÑ, вы можете закрыть Ñвой аккаунт. + edit_profile_step: ÐаÑтройте Ñвой профиль, загрузив аватарку, шапку, изменив отображаемое Ð¸Ð¼Ñ Ð¸ ещё много чего. ЕÑли вы хотите вручную раÑÑматривать и подтверждать подпиÑчиков, можно закрыть Ñвой аккаунт. explanation: ÐеÑколько Ñоветов Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ‡ÐºÐ¾Ð² final_action: Ðачать поÑтить final_step: 'Ðачните поÑтить! Ваши публичные поÑты могут видеть другие, например, в локальной ленте или по Ñ…Ñштегам, даже еÑли у Ð²Ð°Ñ Ð½ÐµÑ‚ подпиÑчиков. Ð’Ñ‹ также можете поздороватьÑÑ Ñ Ð¾Ñтальными и предÑтавитьÑÑ, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ñ…Ñштег #приветÑтвие.' diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml index b948a5c5067037e5643af1ccd615f132a7398bae..d1b85d921a1f81e9ce039f79ecc337e002858a22 100644 --- a/config/locales/simple_form.ar.yml +++ b/config/locales/simple_form.ar.yml @@ -14,8 +14,9 @@ ar: bot: ÙŠÙØ¹Ù„ÙÙ… أنّ هذا Ø§Ù„ØØ³Ø§Ø¨ لا يمثل شخصًا context: ÙˆØ§ØØ¯ أو أكثر من السياقات التي يجب أن ينطبق عليها عامل التصÙية digest: ØªÙØ±Ø³ÙŽÙ„ إليك بعد Ù…ÙØ¶ÙŠÙ‘ مدة Ù…ÙÙ† خمول نشاطك Ùˆ Ùقط إذا ما تلقيت رسائل شخصية Ù…Ø¨Ø§Ø´ÙØ±Ø© أثناء ÙØªØ±Ø© غيابك Ù…ÙÙ† الشبكة + discoverable: Ø³ÙØ¬Ù„ Ø§Ù„Ù…Ù„ÙØ§Øª التعريÙية للمستخدمين هو طريقة أخرى لبلوغ جمهور أوسع email: سو٠تتلقى رسالة إلكترونية للتأكيد - fields: ÙŠÙمكنك عرض 4 عناصر على شكل جدول ÙÙŠ ملÙÙƒ الشخصي + fields: ÙŠÙمكنك عرض 4 عناصر على شكل جدول ÙÙŠ ØµÙØØªÙƒ التعريÙية header: مل٠PNG أو GIF أو JPG. ØØ¬Ù…Ù‡ على أقصى تصدير %{size}. سيتم تصغيره إلى %{dimensions}px inbox_url: نسخ العنوان الذي تريد استخدامه Ù…ÙÙ† ØµÙØØ© الاستقبال Ù„Ù„Ù…ÙØ±ØÙ‘ÙŽÙ„ irreversible: التبويقات التي تم تصÙيتها ستختÙÙŠ لا Ù…ØØ§Ù„Ø© ØØªÙ‰ Ùˆ إن تمت إزالة عامÙÙ„ التصÙية لاØÙ‚ًا @@ -25,14 +26,19 @@ ar: phrase: سو٠يتم العثور عليه مهما كان نوع النص أو ØØªÙ‰ Ùˆ إن كان داخل الويب Ùيه ØªØØ°ÙŠØ± عن Ø§Ù„Ù…ØØªÙˆÙ‰ scopes: ما هي المجالات Ø§Ù„Ù…Ø³Ù…ÙˆØ Ø¨Ù‡Ø§ ÙÙŠ التطبيق ØŸ إن قمت باختيار أعلى المجالات Ùيمكنك الاستغناء عن الخَيار اليدوي. setting_aggregate_reblogs: لا تقم بعرض المشارَكات الجديدة لتبويقات قد Ù‚Ùمتَ بمشاركتها سابقا (هذا الإجراء يعني المشاركات الجديدة Ùقط التي تلقيتَها) + setting_default_sensitive: ØªÙØ®ÙÙ‰ الوسائط Ø§Ù„ØØ³Ø§Ø³Ø© تلقائيا ويمكن اظهارها عن طريق النقر عليها setting_display_media_default: Ø¥Ø®ÙØ§Ø¡ الوسائط Ø§Ù„Ù…ÙØ¹ÙŠÙ‘َنة ÙƒØØ³Ø§Ø³Ø© setting_display_media_hide_all: Ø¥Ø®ÙØ§Ø¡ ÙƒØ§ÙØ© الوسائط دائمًا setting_display_media_show_all: دائمًا عرض الوسائط Ø§Ù„Ù…ÙØ¹ÙŠÙ‘َنة ÙƒØØ³Ø§Ø³Ø© - setting_hide_network: Ø§Ù„ØØ³Ø§Ø¨Ø§Øª التي ØªÙØªØ§Ø¨Ø¹Ù‡Ø§ Ùˆ التي ØªÙØªØ§Ø¨Ùعك على ØØ¯ سواء لن ØªÙØ¹Ø±ÙŽØ¶ على ØµÙØØªÙƒ الشخصية - setting_noindex: ذلك يؤثر على ØØ§Ù„Ø© ملÙÙƒ الشخصي Ùˆ ØµÙØØ§ØªÙƒ + setting_hide_network: Ø§Ù„ØØ³Ø§Ø¨Ø§Øª التي ØªÙØªØ§Ø¨Ø¹Ù‡Ø§ Ùˆ التي ØªÙØªØ§Ø¨Ùعك على ØØ¯ سواء لن ØªÙØ¹Ø±ÙŽØ¶ على ØµÙØØªÙƒ التعريÙية + setting_noindex: ذلك يؤثر على ØµÙØØªÙƒ التعريÙية ÙˆØµÙØØ§Øª المنشورات + setting_show_application: يتم عرض اسم التطبيق الذي تستخدمه عند التبويق ÙÙŠ العرض Ø§Ù„Ù…ÙØµÙ‘Ù„ لتبوبيقاتك + setting_use_pending_items: Ø¥Ø®ÙØ§Ø¡ ØªØØ¯ÙŠØ«Ø§Øª الخط وراء نقرة بدلًا Ù…ÙÙ† التمرير التلقائي للتدÙÙ‚ username: اسم المستخدم الخاص بك سو٠يكون ÙØ±ÙŠØ¯Ø§ Ù…ÙÙ† نوعه على %{domain} featured_tag: - name: 'Ø±ÙØ¨Ù‘َما تريد/ÙŠ استخدام Ø£ØØ¯ هؤلاء:' + name: 'Ø±ÙØ¨Ù‘َما تريد·ين استخدام ÙˆØ§ØØ¯ Ù…ÙÙ† هذه:' + form_challenge: + current_password: إنك بصدد الدخول إلى منطقة آمنة imports: data: مل٠CSV تم تصديره Ù…ÙÙ† خادوم ماستدون آخر invite_request: @@ -46,6 +52,10 @@ ar: fields: name: التسمية value: Ø§Ù„Ù…ØØªÙˆÙ‰ + account_alias: + acct: Ù…ÙØ¹Ø±Ù‘Ù Ø§Ù„ØØ³Ø§Ø¨ القديم + account_migration: + acct: Ù…ÙØ¹Ø±Ù‘Ù Ø§Ù„ØØ³Ø§Ø¨ الجديد account_warning_preset: text: نموذج نصي admin_account_action: @@ -72,7 +82,7 @@ ar: display_name: الاسم المعروض email: عنوان البريد الإلكتروني expires_in: تنتهي مدة صلاØÙŠØªÙ‡ بعد - fields: البيانات الوصÙية Ù„Ù„ØµÙØØ© الشخصية + fields: البيانات الوصÙية Ù„Ù„ØµÙØØ© التعريÙية header: الرأسية inbox_url: عنوان رابط صندوق Ø§Ù„Ù…ÙØ±ÙŽØÙ‘ÙÙ„ irreversible: إسقاط بدلا من Ø¥Ø®ÙØ§Ø¦Ù‡Ø§ @@ -103,7 +113,9 @@ ar: setting_show_application: اكش٠اسم التطبيقات المستخدمة لنشر التبويقات setting_system_font_ui: استخدم الخطوط Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ© للنظام setting_theme: سمة الموقع + setting_trends: اعرض ما ÙŠÙØªØ¯Ø§ÙˆÙŽÙ„ اليوم setting_unfollow_modal: إظهار مربع ØÙˆØ§Ø± للتأكيد قبل إلغاء متابعة أي ØØ³Ø§Ø¨ + setting_use_pending_items: الوضع البطيء severity: القوّة type: صيغة الاستيراد username: اسم المستخدم @@ -115,6 +127,8 @@ ar: must_be_follower: ØØ¸Ø± الإخطارات القادمة من ØØ³Ø§Ø¨Ø§Øª لا تتبعك must_be_following: ØØ¸Ø± الإخطارات القادمة من Ø§Ù„ØØ³Ø§Ø¨Ø§Øª التي لا تتابعها must_be_following_dm: ØØ¸Ø± الرسائل المباشرة القادمة من طر٠أشخاص لا تتبعهم + invite: + comment: التعليق invite_request: text: لماذا ترغب ÙÙŠ الانضمام؟ notification_emails: @@ -123,8 +137,15 @@ ar: follow: ابعث بريداً إلكترونيًا عندما يتبعك Ø£ØØ¯ follow_request: ابعث بريدا إلكترونيا عندما يقوم Ø£ØØ¯Ù‡Ù… بإرسال طلب بالمتابعة mention: ابعث بريداً إلكترونيًا عندما ÙŠÙØ´ÙŠØ± إليك أو ÙŠØ°ÙƒÙØ±Ùƒ Ø£ØØ¯Ù‡Ù… + pending_account: ابعث رسالة إلكترونية إن كان هناك ØØ³Ø§Ø¨ جديد Ø¨ØØ§Ø¬Ø© إلى مراجعة reblog: ابعث بريداً إلكترونيًا عندما يقوم Ø£ØØ¯Ù‡Ù… بترقية منشورك report: إرسال رسالة إلكترونية عند تلقّي إبلاغ جديد + trending_tag: ابعث رسالة إلكترونية إن كان هناك وسم متداوَل Ø¨ØØ§Ø¬Ø© إلى مراجعة + tag: + listable: Ø§Ø³Ù…Ø Ù„Ù‡Ø°Ø§ الوسم بالظهور ÙÙŠ Ø§Ù„Ø¨ØØ« ÙˆÙÙŠ دليل Ø§Ù„ØµÙØØ§Øª التعريÙية + name: الوسم + trendable: Ø§Ù„Ø³Ù…Ø§Ø Ù„Ù‡Ø°Ù‡ الكلمة Ø§Ù„Ù…ÙØªØ§ØÙŠØ© بالظهور ØªØØª المتداوَلة + usable: Ø§Ø³Ù…Ø Ù„Ù„ØªØ¨ÙˆÙŠÙ‚Ø§Øª باستخدام هذا الوسم 'no': لا recommended: موصى بها required: diff --git a/config/locales/simple_form.br.yml b/config/locales/simple_form.br.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7677c850c634b75b037ffd563bb1e1a6e36a5cf --- /dev/null +++ b/config/locales/simple_form.br.yml @@ -0,0 +1 @@ +br: diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index d8713e4caee9fceaad504a0bbe5b895c6a563732..517c7e3b0a74db8cdea9042510e06e924ed2d074 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -2,9 +2,14 @@ ca: simple_form: hints: + account_alias: + acct: Especifica l'usuari@domini del compte des d'on et vols moure + account_migration: + acct: Especifica l'usuari@domini del compte al que et vols moure account_warning_preset: text: Pots utilitzar totes les sintaxi com ara URL, etiquetes i mencions admin_account_action: + include_statuses: L'usuari veurà quin tuts ha causat l'acció de moderació o avÃs send_email_notification: L'usuari rebrà una explicació del que ha passat amb el seu compte text_html: Opcional. Pots utilitzar tota la sintaxi. Pots <a href="%{path}">afegir configuracions predefinides d'avÃs</a> per a estalviar temps type_html: Tria què fer amb <strong>%{acct}</strong> @@ -14,8 +19,10 @@ ca: avatar: PNG, GIF o JPG. Mà xim %{size}. S'escalarà a %{dimensions}px bot: Aquest compte realitza principalment accions automatitzades i pot no estar controlat per cap persona context: Un o diversos contextos on s'ha d'aplicar el filtre + current_password: Per motius de seguretat si us plau entra la contrasenya del compte actual + current_username: Per a confirmar, si us plau entra el nom d'usuari del compte actual digest: Només s'envia després d'un llarg perÃode d'inactivitat amb un resum de les mencions que has rebut en la teva absència - discoverable_html: El <a href="%{path}" target="_blank">directori</a> permet trobar usuaris en funció dels interessos i activitat. Requereix almenys %{min_followers} seguidors + discoverable: El directori de perfils és una altra manera per quin el teu compte pot assolir una audiència més à mplia email: Se t'enviarà un correu electrònic de confirmació fields: Pots tenir fins a 4 elements que es mostren com a taula al teu perfil header: PNG, GIF o JPG. Mà xim %{size}. S'escalarà a %{dimensions}px @@ -34,16 +41,24 @@ ca: setting_hide_network: Qui tu segueixes i els que et segueixen a tu no es mostraran en el teu perfil setting_noindex: Afecta el teu perfil públic i les pà gines d'estat setting_show_application: L'aplicació que fas servir per a publicar es mostrarà a la vista detallada dels teus toots + setting_use_blurhash: Els degradats es basen en els colors de les imatges ocultes, però enfosqueixen els detalls + setting_use_pending_items: Amaga les actualitzacions de la lÃnia de temps després d'un clic en comptes de desplaçar-se automà ticament username: El teu nom d'usuari serà únic a %{domain} whole_word: Quan la paraula clau o la frase sigui només alfanumèrica, s'aplicarà si coincideix amb la paraula sencera + domain_allow: + domain: Aquest domini podrà obtenir dades d’aquest servidor i les dades entrants d’aquests seran processades i emmagatzemades featured_tag: name: 'És possible que vulguis utilitzar un d''aquests:' + form_challenge: + current_password: Està s entrant en una à rea segura imports: data: Fitxer CSV exportat des d'un altre servidor de Mastodon invite_request: text: Això ens ajudarà a revisar la teva petició sessions: otp: 'Introdueix el codi de dos factors generat per el teu telèfon o utilitza un dels teus codis de recuperació:' + tag: + name: Només pots canviar la caixa de les lletres, per exemple, per fer-la més llegible user: chosen_languages: Quan estigui marcat, només es mostraran els toots de les llengües seleccionades en les lÃnies de temps públiques labels: @@ -51,9 +66,14 @@ ca: fields: name: Etiqueta value: Contingut + account_alias: + acct: Nom del compte vell + account_migration: + acct: Nom del nou compte account_warning_preset: text: Text predefinit admin_account_action: + include_statuses: Inclou tuts reportats en el correu electrònic send_email_notification: Notifica l'usuari per correu electrònic text: AvÃs personalitzat type: Acció @@ -108,7 +128,10 @@ ca: setting_show_application: Desvela l'aplicació utilitzada per enviar toots setting_system_font_ui: Utilitza el tipus de lletra predeterminat del sistema setting_theme: Tema del lloc + setting_trends: Mostra les tendències d'avui setting_unfollow_modal: Mostra el dià leg de confirmació abans de deixar de seguir a algú + setting_use_blurhash: Mostra degradats de colors per als Mèdia amagats + setting_use_pending_items: Mode lent severity: Severitat type: Importa el tipus username: Nom d'usuari @@ -120,6 +143,8 @@ ca: must_be_follower: Blocar les notificacions de persones que no et segueixen must_be_following: Bloca les notificacions de persones que no segueixes must_be_following_dm: Bloca els missatges directes de persones que no segueixes + invite: + comment: Comenta invite_request: text: Per què vols unir-te? notification_emails: @@ -131,6 +156,12 @@ ca: pending_account: Envia un correu electrònic quan es necessiti revisar un compte nou reblog: Envia un correu electrònic si algú comparteix el teu estat report: Envia un correu electrònic quan s'enviï un nou informe + trending_tag: Envia un correu quan una etiqueta sense revisar està en tendència + tag: + listable: Permet que aquesta etiqueta aparegui en les cerques i en el directori de perfils + name: Etiqueta + trendable: Permet que aquesta etiqueta aparegui en les tendències + usable: Permet als tuts emprar aquesta etiqueta 'no': 'No' recommended: Recomanat required: diff --git a/config/locales/simple_form.co.yml b/config/locales/simple_form.co.yml index 1f5dba43fb5f55316a8d5e0e2d514cafb9a8213a..245bcea08a32b8141b3c428272814e3e47440e81 100644 --- a/config/locales/simple_form.co.yml +++ b/config/locales/simple_form.co.yml @@ -2,9 +2,14 @@ co: simple_form: hints: + account_alias: + acct: Entrate u cugnome@duminiu di u contu attuale + account_migration: + acct: Entrate u cugnome@duminiu di u contu induve vulete traslucà account_warning_preset: text: Pudete utilizà a sintassa di i statuti, per esempiu l'URL, hashtag, minzione admin_account_action: + include_statuses: L'utilizatore viderà i statuti rispunsevuli di l'azzione o l'avertimentu di muderazione send_email_notification: L'utilizatore hà da riceve una spiegazione di cio chì hè accadutu à u so contu text_html: In uzzione. Pudete utilizà a sintassa di i statuti. Pudete ancu <a href="%{path}">aghjustà preselezzione d'avertimentu</a> per piglià tempu type_html: Sceglie chì fà cù <strong>%{acct}</strong> @@ -14,8 +19,10 @@ co: avatar: Furmatu PNG, GIF o JPG. %{size} o menu. Sarà ridottu à %{dimensions}px bot: Stu contu hè autumatizatu è ùn hè forse micca survegliatu context: Cuntestu·i induve u filtru deve esse applicatu + current_password: Per ragione di sicurità , entrate a chjave d'accessu di stu contu + current_username: Per cunfirmà , entrate u cugnome di questu contu digest: Solu mandatu dopu à una longa perioda d’inattività , è solu s’elli ci sò novi missaghji diretti - discoverable_html: L'<a href="%{path}" target="_blank">annuariu</a> permette à a ghjente di truvà conti à partesi d'interessi è d'attività . Ci vole à avè almenu %{min_followers} abbunati + discoverable: L'annuariu di i prufili hè un'altra manera per u vostru contu di tuccà un'audienza più larga email: Avete da riceve un'e-mail di cunfirmazione fields: Pudete avè fin’à 4 elementi mustrati cum’un tavulone nant’à u vostru prufile header: Furmatu PNG, GIF o JPG. %{size} o menu. Sarà ridottu à %{dimensions}px @@ -34,16 +41,24 @@ co: setting_hide_network: I vostri abbunati è abbunamenti ùn saranu micca mustrati nant’à u vostru prufile setting_noindex: Tocca à u vostru prufile pubblicu è i vostri statuti setting_show_application: L'applicazione chì voi utilizate per mandà statuti sarà affissata indè a vista ditagliata di quelli + setting_use_blurhash: I digradati blurhash sò basati nant'à i culori di u ritrattu piattatu ma senza i ditagli + setting_use_pending_items: Clicchi per messe à ghjornu i statuti invece di fà sfilà a linea autumaticamente username: U vostru cugnome sarà unicu nant'à %{domain} whole_word: Quandu a parolla o a frasa sana hè alfanumerica, sarà applicata solu s'ella currisponde à a parolla sana + domain_allow: + domain: Stu duminiu puderà ricuperà i dati di stu servore è i dati ch'affaccanu da quallà saranu trattati è cunservati featured_tag: name: 'Pudete vulè utilizà unu di quelli:' + form_challenge: + current_password: Entrate in in una zona sicurizata imports: data: Un fugliale CSV da un’altru servore di Mastodon invite_request: text: Quessu ci aiutarà à valutà a vostra dumanda sessions: otp: 'Entrate u codice d’identificazione à dui fattori nant’à u vostru telefuninu, o unu di i vostri codici di ricuperazione:' + tag: + name: Pudete solu cambià a cassa di i caratteri, per esempiu per u rende più lighjevule user: chosen_languages: Soli i statuti scritti in e lingue selezziunate saranu mustrati indè e linee pubbliche labels: @@ -51,9 +66,14 @@ co: fields: name: Marcu value: Cuntinutu + account_alias: + acct: Cugnome di l'anzianu contu + account_migration: + acct: Cugnome di u novu contu account_warning_preset: text: Testu preselezziunatu admin_account_action: + include_statuses: Inchjude i statuti palisati indè l'e-mail send_email_notification: Nutificà l'utilizatore cù un'e-mail text: Avertimentu persunalizatu type: Azzione @@ -108,7 +128,10 @@ co: setting_show_application: Indicà u nome di l'applicazione utilizata per mandà statuti setting_system_font_ui: Pulizza di caratteri di u sistemu setting_theme: Tema di u situ + setting_trends: Vede e tendenze per oghji setting_unfollow_modal: Mustrà una cunfirmazione per siguità qualch’unu + setting_use_blurhash: Vede digradati di culori per i media piattati + setting_use_pending_items: Modu lentu severity: Severità type: Tippu d’impurtazione username: Cugnome @@ -120,6 +143,8 @@ co: must_be_follower: Piattà e nutificazione di quelli·e ch’ùn vi seguitanu must_be_following: Piattà e nutificazione di quelli·e ch’ùn seguitate must_be_following_dm: Bluccà e missaghji diretti di quelli·e ch’ùn seguitate + invite: + comment: Cummentariu invite_request: text: Perchè vulete ghjunghje? notification_emails: @@ -131,6 +156,12 @@ co: pending_account: Mandà un'e-mail quandu un novu contu hà bisognu d'esse valutatu reblog: Mandà un’e-mail quandu qualch’unu sparte i mo statuti report: Mandà un'e-mail quandu c'hè un novu signalamentu + trending_tag: Mandà un'e-mail quandu un hashtag micca verificatu hè in e tendenze + tag: + listable: Auturizà stu hashtag à esse vistu nant'à l'annuariu di i prufili + name: Hashtag + trendable: Auturizà stu hashtag à esse vistu in e tendenze + usable: Auturizà i statuti à utilizà stu hashtag 'no': Nò recommended: Ricumandati required: diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml index 3bf74e9718232a79bb7d23c9879b7e7b06fba09f..047d59c6983bd352ec107da1766ade5458ccad38 100644 --- a/config/locales/simple_form.cs.yml +++ b/config/locales/simple_form.cs.yml @@ -2,9 +2,14 @@ cs: simple_form: hints: + account_alias: + acct: Zadejte pÅ™ezdÃvku@doménu úÄtu, ze kterého se chcete pÅ™esunout + account_migration: + acct: Zadejte pÅ™ezdÃvku@doménu úÄtu, na který se chcete pÅ™esunout account_warning_preset: text: Můžete použÃvat syntaxi tootů, jako napÅ™Ãklad URL, hashtagy a zmÃnky admin_account_action: + include_statuses: Uživatel uvidÃ, které tooty způsobily moderátorskou akci nebo varovánà send_email_notification: Uživatel obdržà vysvÄ›tlenà toho, co se stalo s jeho úÄtem text_html: Volitelné. Můžete použÃvat syntaxi tootů. Pro uÅ¡etÅ™enà Äasu si můžete <a href="%{path}">pÅ™idat pÅ™edlohy pro varovánÃ</a> type_html: Vyberte, co chcete udÄ›lat s úÄtem <strong>%{acct}</strong> @@ -14,8 +19,10 @@ cs: avatar: PNG, GIF Äi JPG. MaximálnÄ› %{size}. Bude zmenÅ¡en na %{dimensions} px bot: Tento úÄet provádà hlavnÄ› automatizované akce a nemusà být spravován context: Jeden Äi vÃce kontextů, ve kterých má být filtr uplatnÄ›n + current_password: Z bezpeÄnostnÃch důvodů prosÃm zadejte heslo aktuálnÃho úÄtu + current_username: ProsÃm potvrÄte zadánÃm uživatelského jména aktuálnÃho úÄtu digest: OdesÃláno pouze po dlouhé dobÄ› neÄinnosti a pouze, pokud jste pÅ™i své nepÅ™Ãtomnosti obdržel/a osobnà zprávy - discoverable_html: <a href="%{path}" target="_blank">Adresář</a> dovoluje lidem najÃt úÄty podle zájmů a aktivity. Vyžaduje alespoň %{min_followers} sledujÃcÃch + discoverable: Adresář profilů je dalšà způsob, dÃky kterému se může váš úÄet dostat k Å¡irÅ¡Ãmu publiku email: Bude vám poslán potvrzovacà e-mail fields: Na profilu můžete mÃt až 4 položky zobrazené jako tabulka header: PNG, GIF Äi JPG. MaximálnÄ› %{size}. Bude zmenÅ¡en na %{dimensions} px @@ -34,16 +41,24 @@ cs: setting_hide_network: Koho sledujete a kdo sleduje vás nebude zobrazeno na vaÅ¡em profilu setting_noindex: Ovlivňuje váš veÅ™ejný profil a stránky tootů setting_show_application: Aplikace, kterou použÃváte k psanà tootů, bude zobrazena v detailnÃm zobrazenà vaÅ¡ich tootů + setting_use_blurhash: Gradienty jsou založeny na barvách skryté grafiky, ale zakrývajà jakékoliv detaily + setting_use_pending_items: Skrýt aktualizace Äasové osy a naÄÃst je kliknutÃm namÃsto automatického rolovánà proudu username: VaÅ¡e uživatelské jméno bude na %{domain} unikátnà whole_word: Je-li klÃÄové slovo Äi fráze pouze alfanumerická, bude aplikována pouze, pokud se shoduje s celým slovem + domain_allow: + domain: Tato doména bude moci stahovat data z tohoto serveru a pÅ™Ãchozà data z nà budou zpracována a uložena featured_tag: name: 'NejspÃÅ¡ budete chtÃt použÃt jeden z tÄ›chto:' + form_challenge: + current_password: Vstupujete do zabezpeÄeného prostoru imports: data: Soubor CSV exportovaný z jiného serveru Mastodon invite_request: text: To nám pomůže posoudit váš požadavek sessions: otp: 'NapiÅ¡te dvoufázový kód vygenerovaný vašà mobilnà aplikacÃ, nebo použijte jeden z vaÅ¡ich záložnÃch kódů:' + tag: + name: Můžete mÄ›nit pouze velikost pÃsmen, napÅ™Ãklad kvůli lepšà Äitelnosti user: chosen_languages: Je-li tohle zaÅ¡krtnuto, budou ve veÅ™ejných Äasových osách zobrazeny pouze tooty ve zvolených jazycÃch labels: @@ -51,9 +66,14 @@ cs: fields: name: OznaÄenà value: Obsah + account_alias: + acct: Adresa starého úÄtu + account_migration: + acct: Adresa nového úÄtu account_warning_preset: text: Text pÅ™edlohy admin_account_action: + include_statuses: Zahrnout v e-mailu nahlášené tooty send_email_notification: Informovat uživatele e-mailem text: Vlastnà varovánà type: Akce @@ -108,7 +128,10 @@ cs: setting_show_application: Zobrazit aplikaci použÃvanou k psanà tootů setting_system_font_ui: PoužÃt výchozà pÃsmo systému setting_theme: Motiv stránky + setting_trends: Zobrazit dneÅ¡nà trendy setting_unfollow_modal: Zobrazovat pÅ™ed zruÅ¡enÃm sledovánà potvrzovacà okno + setting_use_blurhash: Zobrazit pro skrytá média barevné gradienty + setting_use_pending_items: Pomalý režim severity: PÅ™Ãsnost type: Typ importu username: Uživatelské jméno @@ -120,6 +143,8 @@ cs: must_be_follower: Blokovat oznámenà od lidÃ, kteřà vás nesledujà must_be_following: Blokovat oznámenà od lidÃ, které nesledujete must_be_following_dm: Blokovat pÅ™Ãmé zprávy od lidÃ, které nesledujete + invite: + comment: Komentář invite_request: text: ProÄ se chcete pÅ™ipojit? notification_emails: @@ -131,6 +156,12 @@ cs: pending_account: PosÃlat e-maily, když je tÅ™eba posoudit nový úÄet reblog: PosÃlat e-maily, když nÄ›kdo boostne váš toot report: PosÃlat e-maily, je-li odesláno nové nahlášenà + trending_tag: PosÃlat e-maily, když se neschválený hashtag stane populárnÃm + tag: + listable: Dovolit tomuto hashtagu objevovat se v hledánÃch a v adresáři profilů + name: Hashtag + trendable: Dovolit tomuto hashtagu objevovat se v trendech + usable: Dovolit tootům použÃvat tento hashtag 'no': Ne recommended: DoporuÄeno required: diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml index 023506f24fc1060be95add1ea5cd2d26c491a3d7..9b06bf473a0175925389e37194fdd304fd3b0039 100644 --- a/config/locales/simple_form.cy.yml +++ b/config/locales/simple_form.cy.yml @@ -2,9 +2,14 @@ cy: simple_form: hints: + account_alias: + acct: Rhowch enwdefnyddiwr@parth y cyfrif rydych chi am symud ohono + account_migration: + acct: Rhowch enwdefnyddiwr@parth y cyfrif rydych chi am symud iddo account_warning_preset: text: Gallwch defnyddio cystrawen tŵt, fel URLs, hashnodau a sôniadau admin_account_action: + include_statuses: Bydd y defnyddiwr yn gweld pa tŵtiau sydd wedi achosi'r weithred gymedroli neu'r rhybudd send_email_notification: Bydd y defnyddiwr yn derbyn esboniad o beth digwyddodd gyda'i cyfrif text_html: Yn ddewisol. Gallwch defnyddio cystrawen tŵt. Gallwch <a href="%{path}">ychwanegu rhagosodiadau rhybydd</a> i arbed amser type_html: Dewis beth i wneud gyda <strong>%{acct}</strong> @@ -14,8 +19,10 @@ cy: avatar: PNG, GIF neu JPG. %{size} ar y mwyaf. Caiff ei israddio i %{dimensions}px bot: Mae'r cyfrif hwn yn perfformio gweithredoedd awtomatig yn bennaf ac mae'n bosib nad yw'n cael ei fonitro context: Un neu fwy cyd-destun lle dylai'r hidlydd weithio + current_password: At ddibenion diogelwch, nodwch gyfrinair y cyfrif cyfredol + current_username: I gadarnhau, nodwch enw defnyddiwr y cyfrif cyfredol digest: Ond yn cael eu hanfon ar ôl cyfnod hir o anweithgarwch ac ond os ydych wedi derbyn unrhyw negeseuon personol yn eich absenoldeb - discoverable_html: Mae'r <a href="%{path}" target="_blank">cyfeiriadur</a> yn gadael i bobl ddarganfod cyfrifau wedi'i seilio ar ddiddordebau a gweithgareddau.. Mae hyn angen o leiaf %{min_followers} o ddilynwyr + discoverable: Mae'r cyfeirlyfr proffil yn ffordd arall y gall eich cyfrif gyrraedd cynulleidfa ehangach email: Byddwch yn derbyn e-bost i gadarnhau fields: Mae modd i chi arddangos hyd at 4 eitem fel tabl ar eich proffil header: PNG, GIF neu JPG. %{size} ar y mwyaf. Ceith ei israddio i %{dimensions}px @@ -34,16 +41,24 @@ cy: setting_hide_network: Ni fydd y rheini yr ydych yn eu dilyn a phwy sy'n eich dilyn chi yn cael ei ddangos ar eich proffil setting_noindex: Mae hyn yn effeithio ar eich proffil cyhoeddus a'ch tudalennau statws setting_show_application: Bydd y offer frydych yn defnyddio i dŵtio yn cael ei arddangos yn golwg manwl eich tŵtiau + setting_use_blurhash: Mae graddiannau wedi'u seilio ar liwiau'r delweddau cudd ond maent yn cuddio unrhyw fanylion + setting_use_pending_items: Cuddio diweddariadau llinell amser y tu ôl i glic yn lle sgrolio yn awtomatig username: Bydd eich enw defnyddiwr yn unigryw ar %{domain} whole_word: Os yw'r allweddair neu'r ymadrodd yn alffaniwmerig yn unig, mi fydd ond yn cael ei osod os yw'n cyfateb a'r gair cyfan + domain_allow: + domain: Bydd y parth hwn yn gallu nôl data o'r gweinydd hwn a bydd data sy'n dod i mewn ohono yn cael ei brosesu a'i storio featured_tag: name: 'Efallai hoffech defnyddio un o''r rhain:' + form_challenge: + current_password: Rydych chi'n mynd i mewn i ardal sicr imports: data: Allforiwyd dogfen CSV o achos Mastodon arall invite_request: text: Bydd hyn yn helpu ni adolygu eich cais sessions: otp: 'Mewnbynnwch y cod dau gam a gynhyrchwyd gan eich ap ffôn neu defnyddiwch un o''ch codau adfer:' + tag: + name: Dim ond er mwyn ei gwneud yn fwy darllenadwy y gallwch chi newid y llythrennau, er enghraifft user: chosen_languages: Wedi eu dewis, dim ond tŵtiau yn yr ieithoedd hyn bydd yn cael eu harddangos mewn ffrydiau cyhoeddus labels: @@ -51,9 +66,14 @@ cy: fields: name: Label value: Cynnwys + account_alias: + acct: Enw'r hen gyfrif + account_migration: + acct: Enw'r cyfrif newydd account_warning_preset: text: Testun rhagosodedig admin_account_action: + include_statuses: Cynhwyswch tŵtiau yr adroddwyd amdanynt yn yr e-bost send_email_notification: Hysbysu'r defnyddiwr trwy e-bost text: Rhybudd wedi'i addasu type: Gweithredu @@ -108,7 +128,10 @@ cy: setting_show_application: Datguddio'r offer defnyddwyd i anfon tŵtiau setting_system_font_ui: Defnyddio ffont rhagosodedig y system setting_theme: Thema'r wefan + setting_trends: Dangos tueddiadau o heddiw ymlaen setting_unfollow_modal: Dangos deialog cadarnhau cyn dad-ddilyn rhywun + setting_use_blurhash: Dangoswch raddiannau lliwgar ar gyfer cyfryngau cudd + setting_use_pending_items: Modd araf severity: Difrifoldeb type: Modd mewnforio username: Enw defnyddiwr @@ -120,6 +143,8 @@ cy: must_be_follower: Blocio hysbysiadau o bobl nad ydynt yn eich dilyn must_be_following: Blocio hysbysiadau o bobl nad ydych yn eu dilyn must_be_following_dm: Blocio negeseuon uniongyrchol o bobl nad ydych yn eu dilyn + invite: + comment: Sylw invite_request: text: Pam hoffech ymuno? notification_emails: @@ -131,6 +156,12 @@ cy: pending_account: Anfon ebost pan mae cyfrif newydd angen adolygiad reblog: Anfon e-bost pan mae rhywun yn bŵstio eich statws report: Anfon e-bost pan y cyflwynir adroddiad newydd + trending_tag: Anfonwch e-bost pan fydd hashnod heb ei adolygu yn tueddu + tag: + listable: Gadewch i'r hashnod hwn ymddangos mewn chwiliadau ac ar y cyfeiriadur proffil + name: Hashnod + trendable: Gadewch i'r hashnod hwn ymddangos o dan dueddiadau + usable: Caniatáu i tŵtiau ddefnyddio'r hashnod hwn 'no': Na recommended: Argymhellwyd required: diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index 324afece652e63461ee24727bf4c4cef8571ab8a..48a0a68834227506aaf6bc812f2fa726ab227a2e 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -26,6 +26,8 @@ da: setting_noindex: PÃ¥virker din offentlige profil og status sider username: Dit brugernavn vil være unikt pÃ¥ %{domain} whole_word: NÃ¥r nøgle ordet eller udtrykket kun er alfanumerisk, vil det kun blive brugt hvis det passer hele ordet + featured_tag: + name: 'Du kunne mÃ¥ske tænke dig at bruge en af følgende:' imports: data: CSV fil eksporteret fra en anden Mastodon server sessions: @@ -83,6 +85,7 @@ da: setting_system_font_ui: Brug systemets standard font setting_theme: Tema for side setting_unfollow_modal: Vis bekræftelses dialog før du stopper med at følge nogen + setting_use_pending_items: Langsom tilstand severity: Omfang type: Importtype username: Brugernavn @@ -92,6 +95,8 @@ da: must_be_follower: Bloker notifikationer fra folk der ikke følger dig must_be_following: Bloker notifikationer fra folk du ikke følger must_be_following_dm: Bloker direkte beskeder fra folk du ikke følger + invite_request: + text: Hvorfor ønsker du at tilmelde dig? notification_emails: digest: Send sammendrag via emails favourite: Send email nÃ¥r nogen favoriserer din status @@ -100,7 +105,11 @@ da: mention: Send e-mail nÃ¥r nogen nævner dig reblog: Send email nÃ¥r nogen fremhæver din status report: Send email nÃ¥r en ny anmeldelse bliver indsendt + tag: + name: Hashtag 'no': Nej + recommended: Anbefalet required: + mark: "*" text: pÃ¥krævet 'yes': Ja diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 61e0f9740d2168f18a228b9039d938766917a9ad..1b7667c84c5d02625b5e9434cc80a84eacba6cb6 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -2,9 +2,14 @@ de: simple_form: hints: + account_alias: + acct: Gib den benutzernamen@domain des Kontos an, von dem du umziehen möchtest + account_migration: + acct: Gib den benutzernamen@domain des Kontos an, zu dem du umziehen möchtest account_warning_preset: text: Du kannst Beitragssyntax benutzen, wie z.B. URLs, Hashtags und Erwähnungen admin_account_action: + include_statuses: Der Benutzer wird sehen, welche Beiträge diese Maßnahme verursacht haben send_email_notification: Benutzer_in wird Bescheid gegeben, was mit dem Konto geschehen ist text_html: Optional. Du kannst Beitragssyntax nutzen. Du kannst <a href="%{path}">Warnungsvorlagen</a> benutzen um Zeit zu sparen type_html: Wähle aus, was du mit <strong>%{acct}</strong> machen möchtest @@ -14,8 +19,10 @@ de: avatar: PNG, GIF oder JPG. Maximal %{size}. Wird auf %{dimensions} px herunterskaliert bot: Dieses Konto führt lediglich automatisierte Aktionen durch und wird möglicherweise nicht überwacht context: Ein oder mehrere Kontexte, wo der Filter aktiv werden soll + current_password: Aus Sicherheitsgründen gib bitte das Passwort des aktuellen Kontos ein + current_username: Um das zu bestätigen, gib den Benutzernamen des aktuellen Kontos ein digest: Wenn du eine lange Zeit inaktiv bist, wird dir eine Zusammenfassung von Erwähnungen zugeschickt, die du in deiner Abwesenheit empfangen hast - discoverable_html: Das <a href="%{path}" target="_blank">Verzeichnis</a> erlaubt es dein Profil durch deine Hashtags und deine Aktivitäten zu entdecken. Voraussetzung ist allerdings mindestens %{min_followers} Folger_innen + discoverable: Das Profilverzeichnis ist eine andere Möglichkeit, mit der dein Konto ein größeres Publikum erreichen kann email: Du wirst eine Bestätigungs-E-Mail erhalten fields: Du kannst bis zu 4 Elemente auf deinem Profil anzeigen lassen, die als Tabelle dargestellt werden header: PNG, GIF oder JPG. Maximal %{size}. Wird auf %{dimensions} px herunterskaliert @@ -34,16 +41,24 @@ de: setting_hide_network: Wem du folgst und wer dir folgt, wird in deinem Profil nicht angezeigt setting_noindex: Betrifft dein öffentliches Profil und deine Beiträge setting_show_application: Die Anwendung die du nutzst wird in der detaillierten Ansicht deiner Beiträge angezeigt - username: Dein Profilname wird auf %{domain} einzigartig sein + setting_use_blurhash: Die Farbverläufe basieren auf den Farben der versteckten Medien, aber verstecken irgendwelche Details + setting_use_pending_items: Neue Beiträge hinter einem Klick verstecken anstatt automatisch zu scrollen + username: Dein Benutzername wird auf %{domain} einzigartig sein whole_word: Wenn das Schlagwort nur aus Buchstaben und Zahlen besteht, wird es nur angewendet, wenn es dem ganzen Wort entspricht + domain_allow: + domain: Diese Domain kann Daten von diesem Server abrufen und eingehende Daten werden verarbeitet und gespeichert featured_tag: name: 'Du möchtest vielleicht einen von diesen benutzen:' + form_challenge: + current_password: Du betrittst einen sicheren Bereich imports: data: CSV-Datei, die aus einem anderen Mastodon-Server exportiert wurde invite_request: text: Dies wird uns helfen deine Anmeldungsanfrage besser zu verarbeiten sessions: otp: 'Gib die Zwei-Faktor-Authentifizierung von deinem Telefon ein oder benutze einen deiner Wiederherstellungscodes:' + tag: + name: Du kannst zum Beispiel nur die Groß- und Kleinschreibung der Buchstaben ändern, um es lesbarer zu machen user: chosen_languages: Wenn aktiviert, werden nur Beiträge in den ausgewählten Sprachen auf den öffentlichen Zeitleisten angezeigt labels: @@ -51,9 +66,14 @@ de: fields: name: Bezeichnung value: Inhalt + account_alias: + acct: Adresse des alten Kontos + account_migration: + acct: Adresse des neuen Kontos account_warning_preset: text: Vorlagentext admin_account_action: + include_statuses: Meldungen der E-Mail beifügen send_email_notification: Benachrichtige den Nutzer per E-Mail text: Eigene Warnung type: Aktion @@ -64,7 +84,7 @@ de: suspend: Deaktivieren und Benutzerdaten unwiderruflich löschen warning_preset_id: Benutze eine Warnungsvorlage defaults: - autofollow: Eingeladene Nutzer_innen sollen dir automatisch folgen + autofollow: Eingeladene Nutzer sollen dir automatisch folgen avatar: Profilbild bot: Dieses Profil ist ein Bot chosen_languages: Sprachen filtern @@ -108,7 +128,10 @@ de: setting_show_application: Anwendung preisgeben, die benutzt wurde um Beiträge zu versenden setting_system_font_ui: Standardschriftart des Systems verwenden setting_theme: Theme + setting_trends: Heutige Trends anzeigen setting_unfollow_modal: Bestätigungsdialog anzeigen, bevor jemandem entfolgt wird + setting_use_blurhash: Farbverlauf für versteckte Medien anzeigen + setting_use_pending_items: Langsamer Modus severity: Schweregrad type: Art des Imports username: Profilname @@ -120,6 +143,8 @@ de: must_be_follower: Benachrichtigungen von Profilen blockieren, die mir nicht folgen must_be_following: Benachrichtigungen von Profilen blockieren, denen ich nicht folge must_be_following_dm: Private Nachrichten von Profilen, denen ich nicht folge, blockieren + invite: + comment: Kommentar invite_request: text: Warum möchtest du beitreten? notification_emails: @@ -131,6 +156,12 @@ de: pending_account: E-Mail senden, wenn ein neues Benutzerkonto zur Überprüfung aussteht reblog: E-Mail senden, wenn jemand meinen Beitrag teilt report: E-Mail senden, wenn ein neuer Bericht vorliegt + trending_tag: E-Mail senden, wenn ein ausstehender Hashtag angesagt ist + tag: + listable: Erlaube diesem Hashtag im Profilverzeichnis zu erscheinen + name: Hashtag + trendable: Erlaube es diesen Hashtag in den Trends erscheinen zu lassen + usable: Beiträge erlauben, diesen Hashtag zu verwenden 'no': Nein recommended: Empfohlen required: diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index 67f3b64aa1b6f1be7289bd072e768566a3348f16..cbce97171ae0e8884c64bd1c46852c1ffa312530 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -2,9 +2,14 @@ el: simple_form: hints: + account_alias: + acct: ΟÏίστε το username@domain του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï Î±Ï€ÏŒ τον οποίο θÎλετε να μετακινηθείτε + account_migration: + acct: ΟÏίστε το username@domain του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÏ„Î¿Î½ οποίο θÎλετε να μετακινηθείτε account_warning_preset: text: ΜποÏεις να χÏησιμοποιήσεις συντακτικό ενός τουτ όπως διευθÏνσεις URL, ταμπÎλες και αναφοÏÎÏ‚ admin_account_action: + include_statuses: Ο χÏήστης θα δει ποια τουτ Ï€Ïοκάλεσαν την Ï€Ïοειδοποίηση ή την ενÎÏγεια των διαχειÏιστών send_email_notification: Ο χÏήστης θα λάβει μια εξήγηση του τι συνÎβη με τον λογαÏιασμό του text_html: Î ÏοαιÏετικό. ΜποÏείς να χÏησιμοποιήσεις συντακτικό ενός τουτ. ΜποÏείς να <a href="%{path}">οÏίσεις Ï€ÏοκαθοÏισμÎνες Ï€Ïοειδοποιήσεις</a> για να γλυτώσεις χÏόνο type_html: Διάλεξε τι θα κανείς με τον <strong>%{acct}</strong> @@ -14,8 +19,10 @@ el: avatar: PNG, GIF ή JPG. Έως %{size}. Θα πεÏιοÏιστεί σε διάσταση %{dimensions}px bot: Ο λογαÏιασμός αυτός εκτελεί κυÏίως αυτοματοποιημÎνες ενÎÏγειες και ίσως να μην παÏακολουθείται context: Ένα ή πεÏισσότεÏα πλαίσια στα οποία μποÏεί να εφαÏμόζεται αυτό το φίλτÏο + current_password: Για λόγους ασφαλείας παÏακαλώ γÏάψε τον κωδικό του Ï„ÏÎχοντος λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï + current_username: Για επιβεβαίωση, παÏακαλώ γÏάψε το όνομα χÏήστη του Ï„ÏÎχοντος λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï digest: ΑποστÎλλεται μόνο μετά από μακÏά πεÏίοδο αδÏάνειας και μόνο αν Îχεις λάβει Ï€Ïοσωπικά μηνÏματα κατά την απουσία σου - discoverable_html: "Ο <a href=\"%{path}\" target=\"_blank\">κατάλογος</a> \nσου επιτÏÎπει να βÏεις λογαÏιασμοÏÏ‚ βάσει ενδιαφεÏόντων και δÏαστηÏιότητας. Απαιτεί τουλάχιστον %{min_followers} ακόλουθους" + discoverable: Ο κατάλογος λογαÏιασμών είναι Îνας ακόμα Ï„Ïόπος ώστε ο λογαÏιασμός σου να βÏει μεγαλÏτεÏο κοινό email: Θα σου σταλεί email επιβεβαίωσης fields: ΜποÏείς να Îχεις Îως 4 σημειώσεις σε μοÏφή πίνακα στο Ï€Ïοφίλ σου header: PNG, GIF ή JPG. Έως %{size}. Θα πεÏιοÏιστεί σε διάσταση %{dimensions}px @@ -34,16 +41,24 @@ el: setting_hide_network: Δε θα εμφανίζεται στο Ï€Ïοφίλ σου ποιους ακολουθείς και ποιοι σε ακολουθοÏν setting_noindex: ΕπηÏεάζει το δημόσιο Ï€Ïοφίλ και τις δημοσιεÏσεις σου setting_show_application: Η εφαÏμογή που χÏησιμοποιείς για να στÎλνεις τα τουτ σου θα εμφανίζεται στις αναλυτικÎÏ‚ λεπτομÎÏειες τους + setting_use_blurhash: Οι χÏωματισμοί βασίζονται στα χÏώματα του κÏυμμÎνου πολυμÎσου αλλά θολώνουν τις λεπτομÎÏειες + setting_use_pending_items: Εμφάνιση ενημεÏώσεων Ïοής μετά από κλικ αντί για αυτόματη κÏλισή τους username: Το όνομα χÏήστη σου θα είναι μοναδικό στο %{domain} whole_word: Όταν η λÎξη ή η φÏάση κλειδί είναι μόνο αλφαÏιθμητική, θα εφαÏμοστεί μόνο αν ταιÏιάζει με ολόκληÏη τη λÎξη + domain_allow: + domain: Ο τομÎας αυτός θα επιτÏÎπεται να ανακτά δεδομÎνα από αυτό τον διακομιστή και τα εισεÏχόμενα δεδομÎνα θα επεξεÏγάζονται και θα αποθηκεÏονται featured_tag: name: 'Ίσως να θες να χÏησιμοποιήσεις μια από αυτÎÏ‚:' + form_challenge: + current_password: Μπαίνεις σε ασφαλή πεÏιοχή imports: data: ΑÏχείο CSV που Îχει εξαχθεί από διαφοÏετικό κόμβο Mastodon invite_request: text: Αυτό θα μας βοηθήσει να επιθεωÏήσουμε την αίτησή σου sessions: otp: 'Βάλε τον κωδικό δυο παÏαγόντων (2FA) από την εφαÏμογή του τηλεφώνου σου ή χÏησιμοποίησε κάποιον από τους κωδικοÏÏ‚ ανάκτησης σου:' + tag: + name: ΜποÏείς να αλλάξεις μόνο το πλαίσιο των χαÏακτήÏων, για παÏάδειγμα για να γίνει πεÏισσότεÏο ευανάγνωστο user: chosen_languages: Όταν ενεÏγοποιηθεί, στη δημόσια Ïοή θα εμφανίζονται τουτ μόνο από τις επιλεγμÎνες γλώσσες labels: @@ -51,9 +66,14 @@ el: fields: name: ΤαμπÎλα value: ΠεÏιεχόμενο + account_alias: + acct: ΔιακÏιτικό του Ï€Î±Î»Î¹Î¿Ï Î»Î¿Î³Î±ÏÎ¹Î±ÏƒÎ¼Î¿Ï + account_migration: + acct: ΔιακÏιτικό του νÎου λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï account_warning_preset: text: Î ÏοκαθοÏισμÎνο κείμενο admin_account_action: + include_statuses: ΣυμπεÏίληψη των καταγγελλομÎνων τουτ στο email send_email_notification: ΕνημÎÏωση χÏήστη μÎσω email text: Î ÏοσαÏμοσμÎνη Ï€Ïοειδοποίηση type: ΕνÎÏγεια @@ -108,7 +128,10 @@ el: setting_show_application: Αποκάλυψη εφαÏμογής που χÏησιμοποιήθηκε για την αποστολή των τουτ setting_system_font_ui: ΧÏησιμοποίησε την Ï€ÏοεπιλεγμÎνη γÏαμματοσειÏά του συστήματος setting_theme: ΘÎμα ιστότοπου + setting_trends: Εμφάνιση σημεÏινών τάσεων setting_unfollow_modal: Εμφάνιση εÏώτησης επιβεβαίωσης Ï€Ïιν διακόψεις την παÏακολοÏθηση κάποιου + setting_use_blurhash: Εμφάνιση χÏωματισμών για τα κÏυμμÎνα πολυμÎσα + setting_use_pending_items: ΑÏγή λειτουÏγία severity: ΑυστηÏότητα type: ΤÏπος εισαγωγής username: Όνομα χÏηστη @@ -120,17 +143,25 @@ el: must_be_follower: ΜπλόκαÏε τις ειδοποιήσεις από όσους δεν ακολουθείς must_be_following: ΜπλόκαÏε τις ειδοποιήσεις που Ï€ÏοÎÏχονται από άτομα που δεν τα ακολουθείς must_be_following_dm: ΜπλόκαÏε τα Ï€Ïοσωπικά μηνÏματα από όσους δεν ακολουθείς + invite: + comment: Σχόλια invite_request: text: Γιατί θÎλεις να συμμετάσχεις; notification_emails: digest: ΣτÎλνε συνοπτικά email - favourite: Στελνε email όταν κάποιος σημειώνει ως αγαπημÎνη τη δημοσίευσή σου - follow: Στελνε email όταν κάποιος σε ακολουθεί - follow_request: ΣτÎλνε email όταν κάποιος ζητάει να σε ακολουθήσει - mention: ΣτÎλνε email όταν κάποιος σε αναφÎÏει + favourite: Αποστολή email όταν κάποιος σημειώνει ως αγαπημÎνη τη δημοσίευσή σου + follow: Αποστολή email όταν κάποιος σε ακολουθεί + follow_request: Αποστολή email όταν κάποιος ζητάει να σε ακολουθήσει + mention: Αποστολή email όταν κάποιος σε αναφÎÏει pending_account: Αποστολή email όταν υπάÏχει νÎος λογαÏιασμός για επιθεώÏηση - reblog: ΣτÎλνε email όταν κάποιος Ï€Ïοωθεί τη δημοσίευση σου + reblog: Αποστολή email όταν κάποιος Ï€Ïοωθεί τη δημοσίευση σου report: Αποστολή email όταν υποβάλλεται νÎα καταγγελία + trending_tag: Αποστολή email όταν μια μη-εγκεκÏιμÎνη ταμπÎλα γίνεται δημοφιλής + tag: + listable: Εμφάνιση αυτής της ταμπÎλας στο δημόσιο κατάλογο + name: ΤαμπÎλα + trendable: Εμφάνιση της ταμπÎλας στις τάσεις + usable: ΧÏήση της ταμπÎλας σε τουτ 'no': Όχι recommended: Î Ïοτείνεται required: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 93235ad1f8f0ab3d238d5dfa53343afe8e42a7d6..2a08510ff02292a323ff1484ecdeb6c3417f926c 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -2,9 +2,14 @@ en: simple_form: hints: + account_alias: + acct: Specify the username@domain of the account you want to move from + account_migration: + acct: Specify the username@domain of the account you want to move to account_warning_preset: text: You can use toot syntax, such as URLs, hashtags and mentions admin_account_action: + include_statuses: The user will see which toots have caused the moderation action or warning send_email_notification: The user will receive an explanation of what happened with their account text_html: Optional. You can use toot syntax. You can <a href="%{path}">add warning presets</a> to save time type_html: Choose what to do with <strong>%{acct}</strong> @@ -14,8 +19,10 @@ en: avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px bot: This account mainly performs automated actions and might not be monitored context: One or multiple contexts where the filter should apply + current_password: For security purposes please enter the password of the current account + current_username: To confirm, please enter the username of the current account digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence - discoverable_html: The <a href="%{path}" target="_blank">directory</a> lets people find accounts based on interests and activity. Requires at least %{min_followers} followers + discoverable: The profile directory is another way by which your account can reach a wider audience email: You will be sent a confirmation e-mail fields: You can have up to 4 items displayed as a table on your profile header: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px @@ -35,16 +42,24 @@ en: setting_hide_network: Who you follow and who follows you will not be shown on your profile setting_noindex: Affects your public profile and status pages setting_show_application: The application you use to toot will be displayed in the detailed view of your toots + setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details + setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed username: Your username will be unique on %{domain} whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word + domain_allow: + domain: This domain will be able to fetch data from this server and incoming data from it will be processed and stored featured_tag: name: 'You might want to use one of these:' + form_challenge: + current_password: You are entering a secure area imports: data: CSV file exported from another Mastodon server invite_request: text: This will help us review your application sessions: otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:' + tag: + name: You can only change the casing of the letters, for example, to make it more readable user: chosen_languages: When checked, only toots in selected languages will be displayed in public timelines labels: @@ -52,9 +67,14 @@ en: fields: name: Label value: Content + account_alias: + acct: Handle of the old account + account_migration: + acct: Handle of the new account account_warning_preset: text: Preset text admin_account_action: + include_statuses: Include reported toots in the e-mail send_email_notification: Notify the user per e-mail text: Custom warning type: Action @@ -110,7 +130,10 @@ en: setting_show_application: Disclose application used to send toots setting_system_font_ui: Use system's default font setting_theme: Site theme + setting_trends: Show today's trends setting_unfollow_modal: Show confirmation dialog before unfollowing someone + setting_use_blurhash: Show colorful gradients for hidden media + setting_use_pending_items: Slow mode severity: Severity type: Import type username: Username @@ -122,6 +145,8 @@ en: must_be_follower: Block notifications from non-followers must_be_following: Block notifications from people you don't follow must_be_following_dm: Block direct messages from people you don't follow + invite: + comment: Comment invite_request: text: Why do you want to join? notification_emails: @@ -133,6 +158,12 @@ en: pending_account: Send e-mail when a new account needs review reblog: Send e-mail when someone boosts your status report: Send e-mail when a new report is submitted + trending_tag: Send e-mail when an unreviewed hashtag is trending + tag: + listable: Allow this hashtag to appear in searches and on the profile directory + name: Hashtag + trendable: Allow this hashtag to appear under trends + usable: Allow toots to use this hashtag 'no': 'No' recommended: Recommended required: diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 1b63b27a894b809581f200242336a6e41070ec9c..8c2a087c5a3a2c608e34c2e8e3524b98ee0babc6 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -15,7 +15,6 @@ eo: bot: Tiu konto ĉefe faras aÅtomatajn agojn, kaj povas esti ne kontrolata context: Unu ol pluraj kuntekstoj kie la filtrilo devus agi digest: Sendita nur post longa tempo de neaktiveco, kaj nur se vi ricevis personan mesaÄon en via foresto - discoverable_html: La <a href="%{path}" target="_blank">profilujo</a> permesas al homoj trovi kontojn laÅ interesoj kaj aktiveco. Postulas almenaÅ %{min_followers} sekvantojn email: Vi ricevos konfirman retmesaÄon fields: Vi povas havi Äis 4 tabelajn elementojn en via profilo header: Formato PNG, GIF aÅ JPG. Äœis %{size}. Estos malgrandigita al %{dimensions}px @@ -108,7 +107,9 @@ eo: setting_show_application: Publikigi la aplikaĵon uzatan por sendi mesaÄojn setting_system_font_ui: Uzi la dekomencan tiparon de la sistemo setting_theme: Reteja etoso + setting_trends: Montri hodiaÅajn furoraĵojn setting_unfollow_modal: Montri fenestron por konfirmi antaÅ ol ĉesi sekvi iun + setting_use_pending_items: Malrapida reÄimo severity: Graveco type: Importa tipo username: Uzantnomo @@ -120,6 +121,8 @@ eo: must_be_follower: Bloki sciigojn de nesekvantoj must_be_following: Bloki sciigojn de homoj, kiujn vi ne sekvas must_be_following_dm: Bloki rektajn mesaÄojn de homoj, kiujn vi ne sekvas + invite: + comment: Komento invite_request: text: Kial vi volas aliÄi? notification_emails: @@ -131,6 +134,11 @@ eo: pending_account: Sendi retmesaÄon kiam nova konto bezonas kontrolon reblog: Sendi retmesaÄon kiam iu diskonigas vian mesaÄon report: Sendi retmesaÄon kiam nova signalo estas sendita + trending_tag: Sendi retpoÅtmesaÄon kiam nekontrolita kradvorto furoras + tag: + name: Kradvorto + trendable: Permesi al ĉi tiu kradvorto aperi en furoraĵoj + usable: Permesi tootojn uzi ĉiun tiun haketon 'no': Ne recommended: Rekomendita required: diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml new file mode 100644 index 0000000000000000000000000000000000000000..515d5c1ed90e1b9725f1a1648a82ee512d0575ea --- /dev/null +++ b/config/locales/simple_form.es-AR.yml @@ -0,0 +1 @@ +es-AR: diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index 7b871e8ba0bf6c4475c93ae544c33941f30d402e..2fb33dbc3ca775de7ab65c8ad5c335700c61ba3d 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -2,16 +2,27 @@ es: simple_form: hints: + account_alias: + acct: Especifique el nombre de usuario@dominio de la cuenta desde la cual se desea migrar + account_migration: + acct: Especifique el nombre de usuario@dominio de la cuenta a la cual se desea migrar account_warning_preset: text: Puede usar sintaxis de toots, como URLs, hashtags y menciones admin_account_action: + include_statuses: El usuario verá qué toots han causado la acción de moderación o advertencia send_email_notification: El usuario recibirá una explicación de lo que sucedió con respecto a su cuenta + text_html: Opcional. Puede usar sintaxis de toots. Puede añadir <a href="%{path}">configuraciones predefinidas de advertencia</a> para ahorrar tiempo + type_html: Elige qué hacer con <strong>%{acct}</strong> + warning_preset_id: Opcional. Aún puede añadir texto personalizado al final de la configuración predefinida defaults: autofollow: Los usuarios que se registren mediante la invitación te seguirán automáticamente avatar: PNG, GIF o JPG. Máximo %{size}. Será escalado a %{dimensions}px bot: Esta cuenta ejecuta principalmente acciones automatizadas y podrÃa no ser monitorizada context: Uno o múltiples contextos en los que debe aplicarse el filtro + current_password: Por razones de seguridad por favor ingrese la contraseña de la cuenta actual + current_username: Para confirmar, por favor ingrese el nombre de usuario de la cuenta actual digest: Solo enviado tras un largo periodo de inactividad y solo si has recibido mensajes personales durante tu ausencia + discoverable: El directorio del perfil es otra forma en la que su cuenta puede llegar a un público más amplio email: Se le enviará un correo de confirmación fields: Puedes tener hasta 4 elementos mostrándose como una tabla en tu perfil header: PNG, GIF o JPG. Máximo %{size}. Será escalado a %{dimensions}px @@ -22,16 +33,32 @@ es: password: Utilice al menos 8 caracteres phrase: Se aplicará sin importar las mayúsculas o los avisos de contenido de un toot scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. + setting_aggregate_reblogs: No mostrar nuevos retoots para los toots que han sido recientemente retooteados (sólo afecta a los retoots recibidos recientemente) + setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un click + setting_display_media_default: Ocultar contenido multimedia marcado como sensible + setting_display_media_hide_all: Siempre ocultar todo el contenido multimedia + setting_display_media_show_all: Mostrar siempre contenido multimedia marcado como sensible setting_hide_network: A quién sigues y quién te sigue no será mostrado en tu perfil setting_noindex: Afecta a tu perfil público y páginas de estado setting_show_application: La aplicación que utiliza usted para publicar toots se mostrará en la vista detallada de sus toots + setting_use_blurhash: Los gradientes se basan en los colores de las imágenes ocultas pero haciendo borrosos los detalles + setting_use_pending_items: Ocultar nuevos estados detrás de un clic en lugar de desplazar automáticamente el feed + username: Tu nombre de usuario será único en %{domain} whole_word: Cuando la palabra clave o frase es solo alfanumérica, solo será aplicado si concuerda con toda la palabra + domain_allow: + domain: Este dominio podrá obtener datos de este servidor y los datos entrantes serán procesados y archivados + featured_tag: + name: 'Puede que quieras usar uno de estos:' + form_challenge: + current_password: Estás entrando en un área segura imports: data: Archivo CSV exportado desde otra instancia de Mastodon invite_request: text: Esto nos ayudará a revisar su aplicación sessions: otp: 'Introduce el código de autenticación de dos factores geberado por tu aplicación de teléfono o usa uno de tus códigos de recuperación:' + tag: + name: Sólo se puede cambiar el cajón de las letras, por ejemplo, para que sea más legible user: chosen_languages: Cuando se marca, solo se mostrarán los toots en los idiomas seleccionados en los timelines públicos labels: @@ -39,15 +66,26 @@ es: fields: name: Etiqueta value: Contenido + account_alias: + acct: Maneja la cuenta antigua + account_migration: + acct: Maneja la cuenta nueva + account_warning_preset: + text: Texto predefinido admin_account_action: + include_statuses: Incluir en el correo electrónico a los toots denunciados send_email_notification: Notificar al usuario por correo electrónico text: Aviso personalizado type: Acción types: disable: Deshabilitar + none: No hacer nada silence: Silenciar + suspend: Suspender y eliminar de forma irreversible la información de la cuenta + warning_preset_id: Usar un aviso predeterminado defaults: autofollow: Invitar a seguir tu cuenta + avatar: Avatar bot: Esta es una cuenta bot chosen_languages: Filtrar idiomas confirm_new_password: Confirmar nueva contraseña @@ -55,6 +93,7 @@ es: context: Filtrar contextos current_password: Contraseña actual data: Información + discoverable: Listar esta cuenta en el directorio display_name: Nombre para mostrar email: Dirección de correo electrónico expires_in: Expirar tras @@ -70,28 +109,42 @@ es: otp_attempt: Código de dos factores password: Contraseña phrase: Palabra clave o frase + setting_advanced_layout: Habilitar interfaz web avanzada + setting_aggregate_reblogs: Agrupar retoots en las lÃneas de tiempo setting_auto_play_gif: Reproducir automáticamente los GIFs animados setting_boost_modal: Mostrar ventana de confirmación antes de un Retoot setting_default_language: Idioma de publicación setting_default_privacy: Privacidad de publicaciones setting_default_sensitive: Marcar siempre imágenes como sensibles setting_delete_modal: Mostrar diálogo de confirmación antes de borrar un toot + setting_display_media: Visualización multimedia + setting_display_media_default: Por defecto + setting_display_media_hide_all: Ocultar todo + setting_display_media_show_all: Mostrar todo + setting_expand_spoilers: Siempre expandir los toots marcados con advertencias de contenido setting_hide_network: Ocultar tu red setting_noindex: Excluirse del indexado de motores de búsqueda setting_reduce_motion: Reducir el movimiento de las animaciones setting_show_application: Mostrar aplicación usada para publicar toots setting_system_font_ui: Utilizar la tipografÃa por defecto del sistema setting_theme: Tema del sitio + setting_trends: Mostrar las tendencias de hoy setting_unfollow_modal: Mostrar diálogo de confirmación antes de dejar de seguir a alguien + setting_use_blurhash: Mostrar gradientes coloridos para contenido multimedia oculto + setting_use_pending_items: Modo lento severity: Severidad type: Importar tipo username: Nombre de usuario username_or_email: Usuario o Email whole_word: Toda la palabra + featured_tag: + name: Etiqueta interactions: must_be_follower: Bloquear notificaciones de personas que no te siguen must_be_following: Bloquear notificaciones de personas que no sigues must_be_following_dm: Bloquear mensajes directos de la gente que no sigues + invite: + comment: Comentar invite_request: text: "¿Por qué quiere unirse usted?" notification_emails: @@ -103,8 +156,15 @@ es: pending_account: Enviar correo electrónico cuando una nueva cuenta necesita revisión reblog: Enviar correo electrónico cuando alguien comparta su publicación report: Enviar un correo cuando se envÃa un nuevo informe + trending_tag: Enviar correo electrónico cuando una etiqueta no revisada está de tendencia + tag: + listable: Permitir que esta etiqueta aparezca en las búsquedas y en el directorio del perfil + name: Etiqueta + trendable: Permitir que esta etiqueta aparezca bajo tendencias + usable: Permitir a los toots usar esta etiqueta 'no': 'No' recommended: Recomendado required: + mark: "*" text: necesario 'yes': Sà diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml new file mode 100644 index 0000000000000000000000000000000000000000..c52818a3541ee56164894edd37d7315dc6a92462 --- /dev/null +++ b/config/locales/simple_form.et.yml @@ -0,0 +1,152 @@ +--- +et: + simple_form: + hints: + account_warning_preset: + text: Te saate kasutada tuututuse süntaksi, näiteks URLe, silte ja mainimisi + admin_account_action: + send_email_notification: Konto omanik saab selgituse selle kohta, mis juhtus nende kontoga + text_html: Valikuline. Te saate kasutada tuututuse süntaksi. Te saate <a href="%{path}">lisada hoiatuste eelseadistusi</a> aega säästmiseks + type_html: Vali, mida teha kasutajaga <strong>%{acct}</strong> + warning_preset_id: Valikuline. Te saate ikka lisada mis tahes teksti eelseadistuse lõppu + defaults: + autofollow: Inimesed, kes loovad konto selle kutse läbi, automaatselt jälgivad Teid + avatar: PNG, GIF või JPG. Kõige rohkem %{size}. Vähendatakse %{dimensions} pikslini + bot: See konto teeb enamjaolt automatiseeritud tegevusi ja ei pruugi olla järelvalve all + context: Üks või mitu konteksti, mille vastu see filter peaks rakenduma + digest: Saadetakse ainult pärast pikka perioodi tegevusetust ja ainult siis, kui Teile on saadetud privaatseid sõnumeid + email: Teile saadetakse kinnituskiri e-posti teel + fields: Te saate oma profiilil tabelina kuvada kuni 4 asja + header: PNG, GIF või JPG. Kõige rohkem %{size}. Vähendatakse %{dimensions} pikslini + inbox_url: Kopeerige soovitud relee avalehe URL + irreversible: Filtreeritud tuututused kaovad taastamatult, isegi kui filter on hiljem eemaldatud + locale: Kasutajaliidese, e-kirjade ja push-teadete keel + locked: Nõuab käsitsi jälgijate kinnitamist + password: Sisestage vähemalt 8 tähemärki + phrase: Kattub olenemata tuututuse teksti suurtähtedest või sisuhoiatusest + scopes: Milliseid API-sid see rakendus tohib kasutada. Kui Te valite kõrgeima taseme, ei pea Te valima individuaalseid. + setting_aggregate_reblogs: Ära näita uusi upitusi tuututustele, mis on just hiljuti upitatud (ainult kehtib uutele upitusele) + setting_default_sensitive: Tundlik meedia on vaikimisi peidetud ning seda saab avada sellele klikkides + setting_display_media_default: Peida tundlikuks märgitud meedia + setting_display_media_hide_all: Alati peida kõik meedia + setting_display_media_show_all: Alati näita tundlikuks märgistatud meedia + setting_hide_network: Keda Te jälgite ja kes jägib Teid ei kuvata Teie profiilil + setting_noindex: Mõjutab Teie avalikku profiili ja staatuse lehekülgi + setting_show_application: Rakendus, mida kasutate tuututamiseks, kuvatakse tuututuste üksikasjade vaates + setting_use_blurhash: Värvid põhinevad peidetud visuaalidel, kuid hägustavad igasuguseid detaile + setting_use_pending_items: Peida ajajoone uuendused kliki taga selle asemel, et automaatselt kerida voogu + username: Teie kasutajanimi on %{domain}-il unikaalne + whole_word: Kui võtmesõna või fraas on ainult tähtnumbriline, rakendub see ainult siis, kui see kattub terve sõnaga + domain_allow: + domain: See domeen saab tõmmata andmeid sellelt serverilt ning sissetulevad andmed sellelt domeenilt töödeldakse ning salvestatakse + featured_tag: + name: 'Äkki soovite kasutada mõnda neist:' + imports: + data: CSV fail eksporditi teisest Mastodoni serverist + invite_request: + text: See aitab meil üle vaadata Teie taotlust + sessions: + otp: 'Sisesta kahesammulise autentimise kood loodud Teie mobiilirakenduse poolt või sisesta mõni taastuskood:' + tag: + name: Te saate ainult muuta tähtede tõstetust, näiteks selleks, et muuta seda rohkem loetavaks + user: + chosen_languages: Kui valitud, ainult valitud keeltes tuututused näidatakse avalikes ajajoontes + labels: + account: + fields: + name: Nimetus + value: Sisu + account_warning_preset: + text: Eelseadistatud tekst + admin_account_action: + send_email_notification: Teavita kasutajat e-posti teel + text: Hoiatus + type: Tegevus + types: + disable: Keela + none: Ära tee midagi + silence: Vaigista + suspend: Peata ja taastamatult kustuta konto andmed + warning_preset_id: Kasuta hoiatuse eelseadistust + defaults: + autofollow: Kutsu oma kontot jälgima + avatar: Profiilipilt + bot: See konto on robot + chosen_languages: Filtreeri keeli + confirm_new_password: Kinnita uus salasõna + confirm_password: Kinnita salasõna + context: Filtreeri kontekste + current_password: Kehtiv salasõna + data: Andmed + discoverable: Lisage see konto kataloogi + display_name: Kuvanimi + email: E-posti aadress + expires_in: Aegu pärast + fields: Profiili metaandmed + header: Päis + inbox_url: Relee sisendkausta URL + irreversible: Kustuta selle asemel, et peita + locale: Kasutajaliidese keel + locked: Lukusta konto + max_uses: Maksimum kasutajate arv + new_password: Uus salasõna + note: Elulugu + otp_attempt: Kahesammulise autentimise kood + password: Salasõna + phrase: Võtmesõna või fraas + setting_advanced_layout: Aktiveeri arenenud veebiliides + setting_aggregate_reblogs: Grupita upitused ajajoontes + setting_auto_play_gif: Mängi GIF-e automaatselt + setting_boost_modal: Näita kinnitusdialoogi enne upitamist + setting_default_language: Postituse keel + setting_default_privacy: Postituse privaatsus + setting_default_sensitive: Alati märgista meedia tundlikuks + setting_delete_modal: Näita kinnitusdialoogi enne tuututuse kustutamist + setting_display_media: Meedia kuvarežiim + setting_display_media_default: Vaikimisi + setting_display_media_hide_all: Peida kõik + setting_display_media_show_all: Kuva kõik + setting_expand_spoilers: Alati laienda sisuhoiatustega tuututused + setting_hide_network: Peida oma võrk + setting_noindex: Keeldu otsingumootorite indekseerimistest + setting_reduce_motion: Vähenda liikumist animatsioonides + setting_show_application: Avalikusta tuututuste saatmisel kasutatud rakendused + setting_system_font_ui: Kasuta süsteemi vaikefonti + setting_theme: Saidi teema + setting_trends: Näita tänaseid trende + setting_unfollow_modal: Näita kinnitusdialoogi enne jälgimise eemaldamist + setting_use_blurhash: Näita peidetud meedia asemel värvilist pilti + setting_use_pending_items: Aeglane režiim + severity: Tõsidus + type: Impordi tüüp + username: Kasutajanimi + username_or_email: Kasutajanimi või e-post + whole_word: Terve sõna + featured_tag: + name: Silt + interactions: + must_be_follower: Keela teavitused mittejälgijatelt + must_be_following: Keela teavitused kasutajatelt, keda sa ei jälgi + must_be_following_dm: Keela privaatsõnumid kasutajatelt, keda sa ei jälgi + invite_request: + text: Miks Te soovite liituda? + notification_emails: + digest: Saada ülevaatlike e-kirju + favourite: Saada e-kiri, kui keegi lisab teie staatuse lemmikuks + follow: Saada e-kiri, kui keegi alustab Teie jälgimist + follow_request: Saada e-kiri, kui keegi soovib Teid jälgida + mention: Saada e-kiri, kui keegi mainib Teid + pending_account: Saada e-kiri, kui uus konto vajab ülevaatlust + reblog: Saada e-kiri, kui keegi upitab Teie staatust + report: Saada e-kiri, kui esitatud on uus teavitus + trending_tag: Saada e-kiri, kui ülevaatamata silt trendib + tag: + listable: Luba sellel sildil ilmuda profiilide kataloogis + trendable: Luba sellel sildil trendida + usable: Luba tuututustel seda silti kasutada + 'no': Ei + recommended: Soovituslik + required: + mark: "*" + text: kohustuslik + 'yes': Jah diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml index acd5fd6d98b41cda86f79ffec92e88dbac1ced70..471577d79af65ad38e983d85f9ed988425f7acaf 100644 --- a/config/locales/simple_form.eu.yml +++ b/config/locales/simple_form.eu.yml @@ -2,6 +2,8 @@ eu: simple_form: hints: + account_alias: + acct: Zehaztu migrazioaren jatorri den kontuaren erabiltzailea@domeinua account_warning_preset: text: Toot sintaxia erabili dezakezu, URLak, traolak eta aipamenak admin_account_action: @@ -14,8 +16,9 @@ eu: avatar: PNG, GIF edo JPG. Gehienez %{size}. %{dimensions}px neurrira eskalatuko da bot: Kontu honek nagusiki automatizatutako ekintzak burutzen ditu eta agian ez du inork monitorizatzen context: Iragazkia aplikatzeko testuinguru bat edo batzuk - digest: Soilik jarduerarik gabeko epe luze bat eta gero, eta soilik ez zeudela mezu pertsonalen bat jaso baduzu - discoverable_html: <a href="%{path}" target="_blank">Direktorioa</a>k Jendea interesen eta jardueraren arabera aurkitzea ahalbidetzen du. Gutxienez %{min_followers} jarraitzaile behar dira bertan agertzeko + current_password: Segurtasunagatik sartu uneko kontuaren pasahitza + current_username: Berresteko sartu uneko kontuaren erabiltzaile-izena + digest: Jarduerarik gabeko epe luze bat eta gero mezu pertsonalen bat jaso baduzu, besterik ez email: Baieztapen e-mail bat bidaliko zaizu fields: 4 elementu bistaratu ditzakezu taula batean zure profilean header: PNG, GIF edo JPG. Gehienez %{size}. %{dimensions}px eskalara txikituko da @@ -26,7 +29,7 @@ eu: password: Erabili 8 karaktere gutxienez phrase: Bat egingo du Maiuskula/minuskula kontuan hartu gabe eta edukiaren abisua kontuan hartu gabe scopes: Zeintzuk API atzitu ditzakeen aplikazioak. Goi mailako arloa aukeratzen baduzu, ez dituzu azpikoak aukeratu behar. - setting_aggregate_reblogs: Ez erakutsi buktzada berriak berriki bultzada jaso duten tootentzat (berriki jasotako bultzadei eragiten die besterik ez) + setting_aggregate_reblogs: Ez erakutsi bultzada berriak berriki bultzada jaso duten toot-entzat (berriki jasotako bultzadei eragiten die bakarrik) setting_default_sensitive: Multimedia hunkigarria lehenetsita ezkutatzen da, eta sakatuz ikusi daiteke setting_display_media_default: Ezkutatu hunkigarri gisa markatutako multimedia setting_display_media_hide_all: Ezkutatu multimedia guztia beti @@ -34,10 +37,13 @@ eu: setting_hide_network: Nor jarraitzen duzun eta nork jarraitzen zaituen ez da bistaratuko zure profilean setting_noindex: Zure profil publiko eta Toot-en orrietan eragina du setting_show_application: Tootak bidaltzeko erabiltzen duzun aplikazioa zure tooten ikuspegi xehetsuan bistaratuko da + setting_use_blurhash: Gradienteak ezkutatutakoaren koloreetan oinarritzen dira, baina xehetasunak ezkutatzen dituzte username: Zure erabiltzaile-izena bakana izango da %{domain} domeinuan whole_word: Hitz eta esaldi gakoa alfanumerikoa denean, hitz osoarekin bat datorrenean besterik ez da aplikatuko featured_tag: name: 'Hauetakoren bat erabili zenezake:' + form_challenge: + current_password: Zonalde seguruan sartzen ari zara imports: data: Beste Mastodon zerbitzari batetik esportatutako CSV fitxategia invite_request: @@ -51,9 +57,14 @@ eu: fields: name: Etiketa value: Edukia + account_alias: + acct: Kontu zaharraren helbidea + account_migration: + acct: Kontu berriaren helbidea account_warning_preset: text: Aurrez ezarritako testua admin_account_action: + include_statuses: Txertatu salatutako toot-ak e-mailean send_email_notification: Jakinarazi erabiltzaileari e-mail bidez text: Abisu pertsonalizatua type: Ekintza @@ -108,7 +119,10 @@ eu: setting_show_application: Utzi agerian tootak bidaltzeko erabilitako aplikazioa setting_system_font_ui: Erabili sistemako tipografia lehenetsia setting_theme: Gunearen gaia + setting_trends: Erakutsi gaurko joerak setting_unfollow_modal: Erakutsi baieztapen elkarrizketa-koadroa inor jarraitzeari utzi aurretik + setting_use_blurhash: Erakutsi gradiente koloretsuak ezkutatutako multimediaren ordez + setting_use_pending_items: Modu geldoa severity: Larritasuna type: Inportazio mota username: Erabiltzaile-izena @@ -118,8 +132,10 @@ eu: name: Traola interactions: must_be_follower: Blokeatu jarraitzaile ez direnen jakinarazpenak - must_be_following: Blokeatu zuk jarraitzen ez dituzunen jakinarazpenak - must_be_following_dm: Blokeatu zuk jarraitzen ez dituzunen mezu zuzenak + must_be_following: Blokeatu zuk jarraitzen ez dituzu horien jakinarazpenak + must_be_following_dm: Blokeatu zuk jarraitzen ez dituzun horien mezu zuzenak + invite: + comment: Iruzkina invite_request: text: Zergatik elkartu nahi duzu? notification_emails: @@ -131,6 +147,12 @@ eu: pending_account: Bidali e-mail bat kontu bat berrikusi behar denean reblog: Bidali e-mail bat norbaitek zure mezuari bultzada ematen badio report: Bidali e-maila txosten berri bat aurkezten denean + trending_tag: Bidali e-mail bat errebisatu gabeko traola bat joeran dagoenean + tag: + listable: Baimendu traola hau bilaketetan agertzea eta profilen direktorioan + name: Traola + trendable: Baimendu traola hau joeretan agertzea + usable: Baimendu toot-ek traola hau erabiltzea 'no': Ez recommended: Aholkatua required: diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml index 7d2dbd8a94bc9785b5b1b64badf0c5cb6eb9abec..70ad0c66ab20879bc86a5ba1484acd833c0d9023 100644 --- a/config/locales/simple_form.fa.yml +++ b/config/locales/simple_form.fa.yml @@ -2,9 +2,14 @@ fa: simple_form: hints: + account_alias: + acct: نشانی username@domain را برای ØØ³Ø§Ø¨ÛŒ Ú©Ù‡ می‌خواهید از آن منتقل شوید بنویسید + account_migration: + acct: نشانی username@domain را برای ØØ³Ø§Ø¨ÛŒ Ú©Ù‡ می‌خواهید به آن منتقل شوید بنویسید account_warning_preset: text: می‌توانید مانند بوق‌های معمولی کاربران دیگر را نام ببرید یا پیوند Ùˆ برچسب بگذارید admin_account_action: + include_statuses: این کاربر خواهد دید Ú©Ù‡ کدام بوق او موجب اقدام مدیریتی یا هشدار شده است send_email_notification: توضیØÛŒ Ú©Ù‡ کاربر می‌بینید Ú©Ù‡ برای ØØ³Ø§Ø¨Ø´ Ú†Ù‡ رخ داده است text_html: اختیاری. می‌توانید مثل بوق‌های معمولی بنویسید. می‌توانید برای صرÙه‌جویی در زمان <a href="%{path}">هشدارهای ازپیش‌آماده Ø¨ÛŒÙØ²Ø§ÛŒÛŒØ¯</a> type_html: با ØØ³Ø§Ø¨ <strong>%{acct}</strong> می‌خواهید Ú†Ù‡ کار کنید؟‌ @@ -14,8 +19,10 @@ fa: avatar: یکی از قالب‌های PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد bot: این ØØ³Ø§Ø¨ بیشتر به طور خودکار ÙØ¹Ø§Ù„یت می‌کند Ùˆ نظارت پیوسته‌ای روی آن وجود ندارد context: یک یا چند زمینه Ú©Ù‡ Ùیلتر باید در آن‌ها اعمال شود + current_password: به دلایل امنیتی Ù„Ø·ÙØ§Ù‹ رمز این ØØ³Ø§Ø¨ را وارد کنید + current_username: برای تأیید، Ù„Ø·ÙØ§Ù‹ نام کاربری ØØ³Ø§Ø¨ ÙØ¹Ù„ÛŒ را وارد کنید digest: تنها وقتی ÙØ±Ø³ØªØ§Ø¯Ù‡ می‌شود Ú©Ù‡ مدتی طولانی ÙØ¹Ø§Ù„یتی نداشته باشید Ùˆ در این مدت برای شما پیغام خصوصی‌ای نوشته شده باشد - discoverable_html: با <a href="%{path}" target="_blank">Ùهرست گزیدهٔ کاربران</a> مردم می‌توانند ØØ³Ø§Ø¨â€ŒÙ‡Ø§ÛŒ این سرور را بر اساس علاقه‌مندی‌ها Ùˆ ÙØ¹Ø§Ù„یت‌شان پیدا کنند. هر ØØ³Ø§Ø¨ دست‌کم باید %{min_followers} پیگیر داشته باشد + discoverable: Ùهرست گزیدهٔ کاربران این سرور راه دیگری است Ú©Ù‡ با آن ØØ³Ø§Ø¨ شما می‌تواند مخاطبان بیشتری پیدا کند email: به شما ایمیل تأییدی ÙØ±Ø³ØªØ§Ø¯Ù‡ خواهد شد fields: شما می‌توانید تا چهار مورد را در یک جدول در نمایهٔ خود نمایش دهید header: یکی از قالب‌های PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد @@ -34,16 +41,24 @@ fa: setting_hide_network: Ùهرست پیگیران شما Ùˆ Ùهرست کسانی Ú©Ù‡ شما Ù¾ÛŒ می‌گیرید روی نمایهٔ شما دیده نخواهد شد setting_noindex: روی نمایهٔ عمومی Ùˆ ØµÙØÙ‡Ù” نوشته‌های شما تأثیر می‌گذارد setting_show_application: برنامه‌ای Ú©Ù‡ به Ú©Ù…Ú© آن بوق می‌زنید، در جزئیات بوق شما نمایش خواهد ÛŒØ§ÙØª + setting_use_blurhash: سایه‌ها بر اساس رنگ‌های Ø¨Ù‡â€ŒÚ©Ø§Ø±Ø±ÙØªÙ‡ در تصویر پنهان‌شده ساخته می‌شوند ولی جزئیات تصویر در آن‌ها آشکار نیست + setting_use_pending_items: به جای Ù¾ÛŒØ´â€ŒØ±ÙØªÙ† خودکار در Ùهرست، به‌روزرسانی Ùهرست نوشته‌ها را پشت یک کلیک پنهان Ú©Ù† username: نام کاربری شما روی %{domain} یکتا خواهد بود whole_word: اگر کلیدواژه Ùقط دارای ØØ±ÙˆÙ Ùˆ اعداد باشد، تنها وقتی پیدا می‌شود Ú©Ù‡ با Ú©Ù„ یک واژه در متن منطبق باشد، نه با بخشی از یک واژه + domain_allow: + domain: این دامین خواهد توانست داده‌ها از این سرور را Ø¯Ø±ÛŒØ§ÙØª کند Ùˆ داده‌های از این دامین در این‌جا پردازش Ùˆ ذخیره خواهند شد featured_tag: name: 'شاید بخواهید چنین چیزهایی را به کار ببرید:' + form_challenge: + current_password: شما در ØØ§Ù„ ورود به یک منطقهٔ‌ ØÙاظت‌شده هستید imports: data: پروندهٔ CSV Ú©Ù‡ از سرور ماستدون دیگری برون‌سپاری شده invite_request: text: این برای بررسی درخواست شما به ما Ú©Ù…Ú© خواهد کرد sessions: otp: 'کد تأیید دومرØÙ„ه‌ای Ú©Ù‡ اپ روی تلÙÙ† شما ساخته را وارد کنید یا یکی از کدهای بازیابی را به کار ببرید:' + tag: + name: شما تنها می‌توانید بزرگی Ùˆ Ú©ÙˆÚ†Ú©ÛŒ ØØ±ÙˆÙ را تغییر دهید تا مثلاً آن را خواناتر کنید user: chosen_languages: اگر انتخاب کنید، تنها نوشته‌هایی Ú©Ù‡ به زبان‌های برگزیدهٔ شما نوشته شده‌اند در Ùهرست نوشته‌های عمومی نشان داده می‌شوند labels: @@ -51,9 +66,14 @@ fa: fields: name: برچسب value: Ù…ØØªÙˆØ§ + account_alias: + acct: نشانی ØØ³Ø§Ø¨ قدیمی + account_migration: + acct: نشانی ØØ³Ø§Ø¨ تازه account_warning_preset: text: متن از پیش آماده‌شده admin_account_action: + include_statuses: بوق‌های گزارش‌شده را در ایمیل بگنجان send_email_notification: اطلاع‌رسانی به کاربر از راه ایمیل text: هشدار موردی type: نوع کنش @@ -108,7 +128,10 @@ fa: setting_show_application: برنامه‌ای Ú©Ù‡ به کار می‌برید آشکار شود setting_system_font_ui: به‌کاربردن قلم Ù¾ÛŒØ´â€ŒÙØ±Ø¶ سیستم setting_theme: تم سایت + setting_trends: نشان‌دادن موضوعات Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± روز setting_unfollow_modal: نمایش پیغام تأیید پیش از لغو پیگیری دیگران + setting_use_blurhash: به جای تصویرهای پنهان‌شده، سایه‌های رنگی نشان بده + setting_use_pending_items: ØØ§Ù„ت آهسته severity: شدت type: نوع درون‌ریزی username: نام کاربری (لاتین) @@ -120,6 +143,8 @@ fa: must_be_follower: مسدودکردن اعلان‌های همه به جز پیگیران must_be_following: مسدودکردن اعلان‌های کسانی Ú©Ù‡ شما Ù¾ÛŒ نمی‌گیرید must_be_following_dm: مسدودکردن پیغام‌های خصوصی کسانی Ú©Ù‡ شما Ù¾ÛŒ نمی‌گیرید + invite: + comment: ØªÙˆØ¶ÛŒØ invite_request: text: چرا می‌خواهید عضو شوید؟ notification_emails: @@ -131,6 +156,12 @@ fa: pending_account: وقتی ØØ³Ø§Ø¨ تازه‌ای نیاز به بازبینی داشت ایمیل Ø¨ÙØ±Ø³Øª reblog: وقتی کسی نوشتهٔ شما را بازبوقید ایمیل Ø¨ÙØ±Ø³Øª report: وقتی گزارش تازه‌ای ÙØ±Ø³ØªØ§Ø¯Ù‡ شد ایمیل Ø¨ÙØ±Ø³Øª + trending_tag: وقتی یک برچسب بازبینی‌نشده Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± شد ایمیل Ø¨ÙØ±Ø³Øª + tag: + listable: بگذارید Ú©Ù‡ این برچسب در جستجوها Ùˆ در Ùهرست گزیدهٔ کاربران نمایش داده شود + name: برچسب + trendable: بگذارید Ú©Ù‡ این برچسب در موضوعات Ù¾Ø±Ø·Ø±ÙØ¯Ø§Ø± دیده شود + usable: بگذارید Ú©Ù‡ این برچسب در بوق‌ها به کار بروند 'no': خیر recommended: توصیه می‌شود required: diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 6b3aa9bfbaca910c6bbc3b951e2823aa0e679ac1..cff0db7191001733aef7258ac2aa451e22fed76e 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -2,9 +2,14 @@ fr: simple_form: hints: + account_alias: + acct: Spécifiez le nom d’utilisateur@domaine du compte que vous souhaitez migrer + account_migration: + acct: Spécifiez le nom d’utilisateur@domaine du compte vers lequel vous souhaitez déménager account_warning_preset: text: Vous pouvez utiliser la syntaxe des pouets, comme les URLs, les hashtags et les mentions admin_account_action: + include_statuses: L’utilisateur·rice verra quels sont les pouets qui ont provoqué l’action de modération ou l’avertissement send_email_notification: L’utilisateur recevra une explication de ce qu’il s’est passé avec son compte text_html: Optionnel. Vous pouvez utilisez la syntaxe des pouets. Vous pouvez <a href="%{path}">ajouter des présélections d’attention</a> pour économiser du temps type_html: Choisir que faire avec <strong>%{acct}</strong> @@ -14,8 +19,10 @@ fr: avatar: Au format PNG, GIF ou JPG. %{size} maximum. Sera réduit à %{dimensions}px bot: Ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé context: Un ou plusieurs contextes où le filtre devrait s’appliquer + current_password: Pour des raisons de sécurité, veuillez saisir le mot de passe du compte courant + current_username: Pour confirmer, veuillez saisir le nom d'utilisateur du compte courant digest: Uniquement envoyé après une longue période d’inactivité et uniquement si vous avez reçu des messages personnels pendant votre absence - discoverable_html: L’<a href="%{path}" target="_blank">annuaire</a> permet aux gens de trouver des comptes en se basant sur les intérêts et les activités. Nécessite au moins %{min_followers} abonnés + discoverable: L’annuaire des profils est une autre façon pour votre compte d’atteindre une plus grand audience email: Vous recevrez un courriel de confirmation fields: Vous pouvez avoir jusqu’à 4 éléments affichés en tant que tableau sur votre profil header: Au format PNG, GIF ou JPG. %{size} maximum. Sera réduit à %{dimensions}px @@ -34,16 +41,24 @@ fr: setting_hide_network: Ceux que vous suivez et ceux qui vous suivent ne seront pas affichés sur votre profil setting_noindex: Affecte votre profil public ainsi que vos statuts setting_show_application: Le nom de l’application que vous utilisez afin d’envoyer des pouets sera affiché dans la vue détaillée de ceux-ci + setting_use_blurhash: Les dégradés sont basés sur les couleurs des visuels cachés mais ne divulgent pas les détails + setting_use_pending_items: Masquer les mises à jour chronologiques derrière un clic au lieu de les montrer automatiquement en faisant défiler le flux d'actualités username: Votre nom d’utilisateur sera unique sur %{domain} whole_word: Lorsque le mot-clef ou la phrase-clef est uniquement alphanumérique, ça sera uniquement appliqué s’il correspond au mot entier + domain_allow: + domain: Ce domaine pourra récupérer des données de ce serveur et les données entrantes seront traitées et stockées featured_tag: name: 'Vous pourriez vouloir utiliser l’un d’entre eux :' + form_challenge: + current_password: Vous entrez une zone sécurisée imports: data: Un fichier CSV généré par un autre serveur de Mastodon invite_request: text: Cela nous aidera à considérer votre demande sessions: otp: 'Entrez le code d’authentification à deux facteurs généré par l’application de votre téléphone ou utilisez un de vos codes de récupération :' + tag: + name: Vous ne pouvez modifier que la casse des lettres, par exemple, pour le rendre plus lisible user: chosen_languages: Lorsque coché, seuls les pouets dans les langues sélectionnées seront affichés sur les fils publics labels: @@ -51,9 +66,14 @@ fr: fields: name: Étiquette value: Contenu + account_alias: + acct: L’identifiant de l’ancien compte + account_migration: + acct: L’identifiant du nouveau compte account_warning_preset: text: Texte de présélection admin_account_action: + include_statuses: Inclure les pouets signalés dans le courriel send_email_notification: Notifier l’utilisateur par courriel text: Attention personnalisée type: Action @@ -108,7 +128,10 @@ fr: setting_show_application: Dévoiler le nom de l’application utilisée pour envoyer des pouets setting_system_font_ui: Utiliser la police par défaut du système setting_theme: Thème du site + setting_trends: Afficher les tendances du jour setting_unfollow_modal: Afficher une fenêtre de confirmation avant de vous désabonner d’un compte + setting_use_blurhash: Afficher les dégradés colorés pour les médias cachés + setting_use_pending_items: Mode lent severity: Sévérité type: Type d’import username: Identifiant @@ -120,6 +143,8 @@ fr: must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas must_be_following: Masquer les notifications des personnes que vous ne suivez pas must_be_following_dm: Bloquer les messages directs des personnes que vous ne suivez pas + invite: + comment: Commentaire invite_request: text: Pourquoi voulez-vous vous inscrire ? notification_emails: @@ -131,6 +156,12 @@ fr: pending_account: Envoyer un courriel lorsqu’un nouveau compte est en attente d’approbation reblog: Envoyer un courriel lorsque quelqu’un partage mes statuts report: Envoyer un courriel lorsqu’un nouveau rapport est soumis + trending_tag: Envoyer un e-mail quand un hashtag non contrôlé est en tendance actuelle + tag: + listable: Autoriser ce hashtag à apparaître dans les recherches et dans l’annuaire des profils + name: Hashtag + trendable: Autoriser ce hashtag à apparaître sous les tendances du jour + usable: Autoriser les toots à utiliser ce hashtag 'no': Non recommended: Recommandé required: diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 22389051fbdd82c6c3353c8cb0b50be619c1d60c..61308bf48b7dc70f0ae0cdf81672e718f0f9fd39 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -2,9 +2,14 @@ gl: simple_form: hints: + account_alias: + acct: Indica o usuaria@servidor da conta desde a cal queres migrar + account_migration: + acct: Indica o usuaria@servidor da conta a cal queres migrar account_warning_preset: - text: Vostede pode utilizar dar formato ao toot, como URLs, etiquetas e mencións + text: Pódeslle dar formato ao toot, como URLs, etiquetas e mencións admin_account_action: + include_statuses: A usuaria verá que toots causaron a acción da moderación ou aviso send_email_notification: A usuaria recibirá unha explicación sobre o que lle aconteceu a súa conta text_html: Optativo. Pode utilizar formato no toot. Pode <a href="%{path}">engadir avisos preestablecidos</a> para aforrar tempo type_html: Escolla que facer con <strong>%{acct}</strong> @@ -14,8 +19,10 @@ gl: avatar: PNG, GIF ou JPG. Máximo %{size}. Será reducida a %{dimensions}px bot: Esta conta realiza principalmente accións automatizadas e poderÃa non estar monitorizada context: Un ou varios contextos onde se deberÃa aplicar o filtro + current_password: Por razóns de seguridade, introduce o contrasinal da conta actual + current_username: Para confirmar, introduce o nome de usuaria da conta actual digest: Enviar só tras un longo perÃodo de inactividade e só si recibeu algunha mensaxe persoal na súa ausencia - discoverable_html: O <a href="%{path}" target="_blank">directorio</a> permite atopar contas en función de intereses e actividade. Require ter ao menos %{min_followers} seguidoras + discoverable: O directorio de perfil é outro xeito para que a túa conta alcance unha maior audiencia email: Enviaráselle un correo-e de confirmación fields: Pode ter ate 4 elementos no seu perfil mostrados como unha táboa header: PNG, GIF ou JPG. Máximo %{size}. Será reducida a %{dimensions}px @@ -34,16 +41,24 @@ gl: setting_hide_network: Non se mostrará no seu perfil quen a segue e quen a está a seguir setting_noindex: Afecta ao seu perfil público e páxinas de estado setting_show_application: A aplicación que está a utilizar para enviar toots mostrarase na vista detallada do toot + setting_use_blurhash: Os gradientes toman as cores da imaxe oculta pero esborranchando todos os detalles + setting_use_pending_items: Ocultar as actualizacións da liña temporal tras un click no lugar de desprazar automáticamente os comentarios username: O seu nome de usuaria será único en %{domain} whole_word: Se a chave ou frase de paso é só alfanumérica, só se aplicará se concorda a palabra completa + domain_allow: + domain: Este dominio estará en disposición de obter datos desde este servidor e datos de entrada a el poderán ser procesados e gardados featured_tag: name: 'PoderÃa utilizar algunha de estas:' + form_challenge: + current_password: Estás entrando nun área segura imports: data: Ficheiro CSV exportado desde outro servidor Mastodon invite_request: text: Esto axudaranos a revisar a súa aplicación sessions: - otp: 'Introduza o código de doble-factor xerado no aplicativo do seu móbil ou utilice un dos seus códigos de recuperación:' + otp: 'Introduza o código de dobre-factor creado pola aplicación do seu móbil ou utilice un dos seus códigos de recuperación:' + tag: + name: Só pode cambiar maiús/minúsculas, por exemplo, mellorar a lexibilidade user: chosen_languages: Se ten marca, só os toots nos idiomas seleccionados serán mostrados en liñas temporais públicas labels: @@ -51,9 +66,14 @@ gl: fields: name: Etiqueta value: Contido + account_alias: + acct: Xestina a conta antiga + account_migration: + acct: Xestiona a nova conta account_warning_preset: text: Texto preestablecido admin_account_action: + include_statuses: IncluÃr toots reportados no correo send_email_notification: Notificar a usuaria por correo-e text: Aviso personalizado type: Acción @@ -86,7 +106,7 @@ gl: max_uses: Número máximo de usos new_password: Novo contrasinal note: Sobre vostede - otp_attempt: Código de Doble-Factor + otp_attempt: Código de Dobre-Factor password: Contrasinal phrase: Palabra chave ou frase setting_advanced_layout: Activar interface web avanzada @@ -108,7 +128,10 @@ gl: setting_show_application: Mostrar a aplicación utilizada para tootear setting_system_font_ui: Utilizar a tipografÃa por defecto do sistema setting_theme: Decorado da instancia + setting_trends: Mostrar as tendencias de hoxe setting_unfollow_modal: Solicitar confirmación antes de deixar de seguir alguén + setting_use_blurhash: Mostrar gradientes coloridos para medios ocultos + setting_use_pending_items: Modo lento severity: Severidade type: Tipo de importación username: Nome de usuaria @@ -120,6 +143,8 @@ gl: must_be_follower: Bloquear as notificacións de non-seguidoras must_be_following: Bloquea as notificacións de personas que non segue must_be_following_dm: Bloquea as mensaxes directas de personas que non segue + invite: + comment: Comentar invite_request: text: Por que quere unirse? notification_emails: @@ -131,6 +156,12 @@ gl: pending_account: Enviar correo-e cando unha nova conta precisa revisión reblog: Enviar un correo cando alguén promociona a súa mensaxe report: Enviar un correo cando se envÃe un novo informe + trending_tag: Enviar correo cando unha etiqueta non revisada é tendencia + tag: + listable: Permitir a esta etiqueta aparecer no directorio de perfil + name: Etiqueta + trendable: Permitir a esta etiqueta aparecer baixo Tendencias + usable: Permitir que os toots utilicen esta etiqueta 'no': Non recommended: Recomendado required: diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index 7e5e4e79d51baee9a356caa77fc3c3450c7e3d14..de1830f5d57703ff0b99726cf6d6ca396ed579b9 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -2,9 +2,14 @@ hu: simple_form: hints: + account_alias: + acct: Adjad meg annak a fióknak a felhasználóneve@domainjét, ahonnan át szeretnéd mozgatni + account_migration: + acct: Add meg a fióknév@domain fiókot, melybe költözni szeretnél account_warning_preset: text: Használhatod a tülkökben szokásos szintaxist, URL-eket, hashtageket, megemlÃtéseket admin_account_action: + include_statuses: A felhasználó látni fogja, melyik tülk okozta a moderációt vagy figyelmeztetést send_email_notification: A felhasználó magyarázatot kap arra, mi történt a fiókjával text_html: Opcionális. A tülk szintaxis használható. EgyszerűsÃtés végett létre is hozhatsz <a href="%{path}">figyelmeztetéseket</a> type_html: Megmondhatod, mi legyen vele <strong>%{acct}</strong> @@ -14,8 +19,10 @@ hu: avatar: PNG, GIF vagy JPG. Maximum %{size}. Ãtméretezzük %{dimensions} pixelre bot: Ez a fiók automatikus műveleteket végez és valószÃnűleg nem figyeljük context: Kontextusok, ahol a szűrÅ‘nek működnie kell + current_password: Biztonsági okok miatt kérlek, Ãrd be a jelenlegi fiók jelszavát + current_username: A jóváhagyáshoz Ãrd be a jelenlegi fiók felhasználói nevét digest: Csak hosszú távollét esetén küldÅ‘dik és csak ha személyes üzenetet kaptál távollétedben - discoverable_html: A <a href="%{path}" target="_blank">profilok</a> adatbázisa lehetÅ‘vé teszi, hogy embereket közös érdeklÅ‘dés vagy tevékenység alapján találj meg. Legalább %{min_followers} követÅ‘re van szükség az adatbázisba való bekerüléshez + discoverable: A profil adatbázis egy újabb mód, ahogyan a fiókod szélesebb tömegeket érhet el email: Kapsz egy megerÅ‘sÃtÅ‘ e-mailt fields: A profilodon legfeljebb 4 bejegyzés szerepelhet táblázatos formában header: PNG, GIF vagy JPG. Maximum %{size}. Ãtméretezzük %{dimensions} pixelre @@ -34,16 +41,24 @@ hu: setting_hide_network: Nem látszik majd a profilodon, kik követnek és te kiket követsz setting_noindex: A nyilvános profilodra és a tülkjeidre vonatkozik setting_show_application: A tülkök részletes nézetében látszani fog, milyen alkalmazást használtál a tülköléshez + setting_use_blurhash: A kihomályosÃtás az eredeti képbÅ‘l történik, de minden részletet elrejt + setting_use_pending_items: IdÅ‘vonal frissÃtése csak kattintásra automatikus görgetés helyett username: A felhasználói neved egyedi lesz a %{domain} domainen whole_word: Ha a kulcsszó alfanumerikus, csak akkor minÅ‘sül majd találatnak, ha teljes szóra illeszkedik + domain_allow: + domain: Ez a domain adatot kérhet le a szerverünkrÅ‘l és az ettÅ‘l érkezÅ‘ adatokat feldolgozzuk és mentjük featured_tag: name: 'Ezeket esetleg használhatod:' + form_challenge: + current_password: Beléptél egy biztonsági térben imports: data: Egy másik Mastodon szerverrÅ‘l exportált CSV fájl invite_request: text: Ez segÃt nekünk átnézni a jelentkezésedet sessions: otp: 'Add meg a telefonodon generált kétlépcsÅ‘s azonosÃtó kódodat vagy használd az egyik tartalék bejelentkezÅ‘ kódot:' + tag: + name: Csak a kis/nagybetűséget változtathatod meg, pl. hogy olvashatóbb legyen user: chosen_languages: Ha aktÃv, csak a kiválasztott nyelvű tülkök jelennek majd meg a nyilvános idÅ‘vonalon labels: @@ -51,9 +66,14 @@ hu: fields: name: CÃmke value: Tartalom + account_alias: + acct: Régi fiók kezelése + account_migration: + acct: Új fiók kezelése account_warning_preset: text: FigyelmeztetÅ‘ szöveg admin_account_action: + include_statuses: Helyezd az e-mailbe a jelentett tülköket send_email_notification: Figyelmeztessük a felhasználót e-mailben text: Egyedi figyelmeztetés type: Művelet @@ -108,7 +128,10 @@ hu: setting_show_application: A tülkölésre használt alkalmazás feltüntetése setting_system_font_ui: Rendszer betűtÃpusának használata setting_theme: MegjelenÃtési sablon + setting_trends: Mai trend mutatása setting_unfollow_modal: MegerÅ‘sÃtés kérése mielÅ‘tt abbahagyod valaki követését + setting_use_blurhash: Rejtett média helyett homály mutatása + setting_use_pending_items: Lassú mód severity: Súlyosság type: Importálás tÃpusa username: Felhasználónév @@ -120,6 +143,8 @@ hu: must_be_follower: Nem követÅ‘idtÅ‘l érkezÅ‘ értesÃtések tiltása must_be_following: Nem követettjeidtÅ‘l érkezÅ‘ értesÃtések tiltása must_be_following_dm: Nem követettjeidtÅ‘l érkezÅ‘ üzenetek tiltása + invite: + comment: Hozzászólás invite_request: text: Miért akarsz csatlakozni? notification_emails: @@ -131,6 +156,12 @@ hu: pending_account: E-mail küldése, ha új fiókot kell engedélyezni reblog: E-mail küldése, amikor valaki megtolja a tülködet report: E-mail küldése, ha új bejelentés érkezett + trending_tag: E-mail küldése, ha egy még nem látott hashtag trendi lett + tag: + listable: A hashtag megjelenhet a profiladatbázisban + name: CÃmke + trendable: A hashtag megjelenhet a trendek között + usable: Tülkök használhatják ezt a hashtaget 'no': Nem recommended: Ajánlott required: diff --git a/config/locales/simple_form.id.yml b/config/locales/simple_form.id.yml index ba9fbb4e88180c16ef4253801127cb0c5b1ab9f2..54d10f21a66842ec1eb5e0d4d9135af8b014d902 100644 --- a/config/locales/simple_form.id.yml +++ b/config/locales/simple_form.id.yml @@ -2,42 +2,167 @@ id: simple_form: hints: + account_alias: + acct: Tentukan namapengguna@domain akun yang ingin Anda pindah + account_migration: + acct: Tentukan namapengguna@domain akun yang ingin Anda pindah + account_warning_preset: + text: Anda dapat memakai sintaks toot, seperti URL, tagar, dan sebutan + admin_account_action: + include_statuses: Pengguna dapat melihat toot mana yang menyebabkan aksi atau peringatan moderasi + send_email_notification: Pengguna akan menerima penjelasan tentang apa yang terjadi pada akun mereka + text_html: Opsional. Anda dapat memakai sintaks toot. Anda dapat <a href="%{path}">menambahkan preset peringatan</a> untuk hemat waktu + type_html: Pilih apa yang perlu dilakukan dengan <strong>%{acct}</strong> + warning_preset_id: Opsional. Anda tetap dapat menambahkan teks kustom pada akhir preset defaults: + autofollow: Orang yang ingin mendaftar lewat undangan, otomatis mengikuti Anda avatar: PNG, GIF atau JPG. Maksimal %{size}. Ukuran dikecilkan menjadi %{dimensions}px + bot: Akun ini melakukan aksi otomatis dan mungkin tak termonitor + context: Satu atau lebih konteks tempat saringan perlu diterapkan + current_password: Untuk tujuan keamanan mohon masukkan kata sandi akun ini + current_username: Untuk konfirmasi, mohon masukkan nama pengguna akun ini + digest: Hanya kirim setelah lama tidak aktif dan hanya jika Anda menerima pesan personal atas absensi Anda + discoverable: Direktori profil adalah cara lain agar akun Anda menyentuh audiens yang lebih luas + email: Anda akan dikirimi surel konfirmasi + fields: Anda bisa memiliki hingga 4 item utk ditampilkan sebagai tabel di profil Anda header: PNG, GIF atau JPG. Maksimal %{size}. Ukuran dikecilkan menjadi %{dimensions}px + inbox_url: Salin URL dari halaman depan relai yang ingin Anda pakai + irreversible: Toot tersaring akan hilang permanen bahkan jika saringan dihapus kemudian + locale: Bahasa antar muka pengguna, surel, dan notifikasi dorong locked: Anda harus menerima permintaan pengikut secara manual dan setting privasi postingan akan diubah khusus untuk pengikut + password: Gunakan minimal 8 karakter + phrase: Akan dicocokkan terlepas dari luaran dalam teks atau peringatan konten dari toot + scopes: API mana yang diizinkan untuk diakses aplikasi. Jika Anda memilih cakupan level-atas, Anda tak perlu memilih yang individual. + setting_aggregate_reblogs: Jangan tampilkan boost baru untuk toot yang baru saja di-boost (hanya memengaruhi boost yang baru diterima) + setting_default_sensitive: Media sensitif disembunyikan secara bawaan dan akan ditampilkan dengan klik + setting_display_media_default: Sembunyikan media yang ditandai sebagai sensitif + setting_display_media_hide_all: Selalu sembunyikan semua media + setting_display_media_show_all: Selalu tampilkan media sensitif + setting_hide_network: Siapa yang Anda ikuti dan yang mengikuti Anda tak akan ditampilkan dalam profil + setting_noindex: Pengaruhi profil publik dan halaman status Anda + setting_show_application: Aplikasi yang Anda pakai untuk men-toot akan ditampilkan di tampilan detail toot + setting_use_blurhash: Gradien didasarkan pada warna visual yang tersembunyi tetapi mengaburkan setiap detail + setting_use_pending_items: Sembunyikan pembaruan linimasa di balik klik alih-alih bergulir secara otomatis + username: Nama pengguna Anda unik di %{domain} + whole_word: Ketika kata kunci/frasa hanya alfanumerik, maka itu hanya akan diterapkan jika cocok dengan semua kata + domain_allow: + domain: Domain ini dapat mengambil data dari server ini dan data yang diterima akan diproses dan disimpan + featured_tag: + name: 'Anda mungkin ingin pakai salah satu dari ini:' + form_challenge: + current_password: Anda memasuki area aman imports: data: File CSV yang diexpor dari server Mastodon lain + invite_request: + text: Ini akan membantu kami meninjau aplikasi Anda sessions: otp: Masukkan kode dua-faktor dari handphone atau gunakan kode pemulihan anda. + user: + chosen_languages: Ketika dicentang, hanya toot dalam bahasa yang dipilih yang akan ditampilkan di linimasa publik labels: + account: + fields: + name: Label + value: Konten + account_alias: + acct: Menangani akun lama + account_migration: + acct: Menangani akun baru + account_warning_preset: + text: Teks preset + admin_account_action: + include_statuses: Sertakan toot terlapor pada surel + send_email_notification: Beritahu pengguna per surel + text: Peringatan kustom + type: Aksi + types: + disable: Matikan + none: Biarkan + silence: Diamkan + suspend: Tangguhkan dan hapus data akun scr permanen + warning_preset_id: Gunakan preset peringatan defaults: + autofollow: Undang untuk mengikuti Anda + avatar: Avatar + bot: Ini akun bot + chosen_languages: Saring bahasa confirm_new_password: Konfirmasi kata sandi baru confirm_password: Konfirmasi kata sandi + context: Saring konteks current_password: Kata sandi sekarang + data: Data + discoverable: Daftarkan akun ini di direktori display_name: Nama yang ditampilkan email: Alamat e-mail + expires_in: Kedaluwarsa setelah + fields: Metadata profil + header: Tajuk + inbox_url: URL kotak relai + irreversible: Hapus alih-alih sembunyikan locale: Bahasa locked: Buat akun menjadi pribadi + max_uses: Jumlah maksimal penggunaan new_password: Password baru + note: Bio otp_attempt: Kode dua-faktor password: Kata sandi + phrase: Kata kunci atau frasa + setting_advanced_layout: Aktifkan antar muka web mahir + setting_aggregate_reblogs: Boost grup di linimasa + setting_auto_play_gif: Mainkan otomatis animasi GIF setting_boost_modal: Tampilkan dialog konfirmasi dialog sebelum boost + setting_default_language: Bahasa posting setting_default_privacy: Privasi postingan + setting_default_sensitive: Selalu tandai media sebagai sensitif + setting_delete_modal: Tampilkan dialog konfirmasi sebelum hapus toot + setting_display_media: Tampilan media + setting_display_media_default: Bawaan + setting_display_media_hide_all: Sembunyikan semua + setting_display_media_show_all: Tunjukkan semua + setting_expand_spoilers: Selalu bentangkan toot yang bertanda peringatan konten + setting_hide_network: Sembunyikan jaringan Anda + setting_noindex: Opt-out dari pengindeksan mesin pencari + setting_reduce_motion: Kurangi gerakan animasi + setting_show_application: Singkap aplikasi yang dipakai utk kirim toot + setting_system_font_ui: Gunakan fon bawaan sistem + setting_theme: Tema situs + setting_trends: Tampilkan tren hari ini + setting_unfollow_modal: Tampilkan konfirmasi dialog sebelum berhenti mengikuti seseorang + setting_use_blurhash: Tampilkan gradien penuh warna utk media tersembunyi + setting_use_pending_items: Mode pelan severity: Keparahan type: Tipe impor username: Nama pengguna + username_or_email: Nama pengguna atau Surel + whole_word: Seluruh kata + featured_tag: + name: Tagar interactions: must_be_follower: Blokir notifikasi dari non-pengikut must_be_following: Blokir notifikasi dari orang yang tidak anda ikuti + must_be_following_dm: Blokir pesan langsung dari orang yang tak Anda ikuti + invite: + comment: Komentar + invite_request: + text: Mengapa Anda ingin gabung? notification_emails: digest: Kirim email berisi rangkuman favourite: Kirim email saat seseorang menyukai status anda follow: Kirim email saat seseorang mengikuti anda follow_request: Kirim email saat seseorang meminta untuk mengikuti anda mention: Kirim email saat seseorang menyebut anda + pending_account: Kirim surel ketika akun baru perlu ditinjau reblog: Kirim email saat seseorang mem-boost status anda + report: Kirim surel ketika laporan baru dikirim + trending_tag: Kirim surel ketika tagar tak tertinjau jadi tren + tag: + listable: Izinkan tagar ini muncul di penelusuran dan di direktori profil + name: Tagar + trendable: Izinkan tagar ini muncul di bawah tren + usable: Izinkan toot memakai tagar ini 'no': Tidak + recommended: Direkomendasikan required: + mark: "*" text: wajib 'yes': Ya diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 377a552935ab6f5c670b3d35a47ea71f2c2ae952..21c86da9e11c379088b451dcc15498fc5e910411 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -5,6 +5,7 @@ it: account_warning_preset: text: Puoi usare la sintassi dei toot, come URL, hashtag e menzioni admin_account_action: + include_statuses: L'utente vedrà quali toot hanno causato l'azione di moderazione o l'avviso send_email_notification: L'utente riceverà una spiegazione di ciò che è successo con suo account text_html: Opzionale. Puoi usare la sintassi dei toot. Puoi <a href="%{path}">aggiungere avvisi preimpostati</a> per risparmiare tempo type_html: Decidi cosa fare con <strong>%{acct}</strong> @@ -15,7 +16,6 @@ it: bot: Questo account esegue principalmente operazioni automatiche e potrebbe non essere tenuto sotto controllo da una persona context: Uno o più contesti nei quali il filtro dovrebbe essere applicato digest: Inviata solo dopo un lungo periodo di inattività e solo se hai ricevuto qualche messaggio personale in tua assenza - discoverable_html: La <a href="%{path}" target="_blank">directory</a> permette alle persone di trovare account in base a determinati interessi o attività . Richiede almeno %{min_followers} seguaci email: Ti manderemo una email di conferma fields: Puoi avere fino a 4 voci visualizzate come una tabella sul tuo profilo header: PNG, GIF o JPG. Al massimo %{size}. Verranno scalate a %{dimensions}px @@ -34,8 +34,12 @@ it: setting_hide_network: Chi segui e chi segue te non saranno mostrati sul tuo profilo setting_noindex: Ha effetto sul tuo profilo pubblico e sulle pagine degli status setting_show_application: L'applicazione che usi per pubblicare i toot sarà mostrata nella vista di dettaglio dei tuoi toot + setting_use_blurhash: I gradienti sono basati sui colori delle immagini nascoste ma offuscano tutti i dettagli + setting_use_pending_items: Fare clic per mostrare i nuovi messaggi invece di aggiornare la timeline automaticamente username: Il tuo nome utente sarà unico su %{domain} whole_word: Quando la parola chiave o la frase è solo alfanumerica, si applica solo se corrisponde alla parola intera + domain_allow: + domain: Questo dominio potrà recuperare i dati da questo server e i dati in arrivo da esso verranno elaborati e memorizzati featured_tag: name: 'Eccone alcuni che potresti usare:' imports: @@ -44,6 +48,8 @@ it: text: Questo ci aiuterà ad esaminare la tua richiesta sessions: otp: 'Inserisci il codice a due fattori generato dall''app del tuo telefono o usa uno dei codici di recupero:' + tag: + name: Puoi cambiare solo il minuscolo/maiuscolo delle lettere, ad esempio, per renderlo più leggibile user: chosen_languages: Quando una o più lingue sono contrassegnate, nelle timeline pubbliche vengono mostrati solo i toot nelle lingue selezionate labels: @@ -54,6 +60,7 @@ it: account_warning_preset: text: Testo preimpostato admin_account_action: + include_statuses: Includi i toots segnalati nell'email send_email_notification: Informa l'utente via email text: Avviso personalizzato type: Azione @@ -108,7 +115,10 @@ it: setting_show_application: Rendi pubblica l'applicazione usata per inviare i toot setting_system_font_ui: Usa il carattere predefinito del sistema setting_theme: Tema sito + setting_trends: Mostra tendenze di oggi setting_unfollow_modal: Chiedi conferma prima di smettere di seguire qualcuno + setting_use_blurhash: Mostra i gradienti colorati per i media nascosti + setting_use_pending_items: Modalità lenta severity: Severità type: Tipo importazione username: Nome utente @@ -121,7 +131,7 @@ it: must_be_following: Blocca notifiche dalle persone che non segui must_be_following_dm: Blocca i messaggi diretti dalle persone che non segui invite_request: - text: Perchè vuoi unirti? + text: Perché vuoi iscriverti? notification_emails: digest: Invia email riassuntive favourite: Invia email quando segna come preferito al tuo stato @@ -131,6 +141,12 @@ it: pending_account: Invia e-mail quando un nuovo account richiede l'approvazione reblog: Invia email quando qualcuno da un boost al tuo stato report: Manda una mail quando viene inviato un nuovo rapporto + trending_tag: Invia e-mail quando un hashtag non controllato è in tendenza + tag: + listable: Permetti a questo hashtag di apparire nella directory dei profili + name: Hashtag + trendable: Permetti a questo hashtag di apparire nelle tendenze + usable: Permetti ai toot di utilizzare questo hashtag 'no': 'No' recommended: Consigliato required: diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index ccc6c5ca1c73b2c767c14cc81ae29e5b219c6eff..b936c8e9a905da9e31f08014712a64f683466f28 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -2,9 +2,14 @@ ja: simple_form: hints: + account_alias: + acct: 引ã£è¶Šã—å…ƒã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å@ドメインを指定ã—ã¦ãã ã•ã„ + account_migration: + acct: 引ã£è¶Šã—å…ˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å@ドメインを指定ã—ã¦ãã ã•ã„ account_warning_preset: text: URLã€ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ã€ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ãªã©ã€æŠ•稿ã«ç”¨ã„ã‚‹æ§‹æ–‡ãŒä½¿ç”¨ã§ãã¾ã™ admin_account_action: + include_statuses: ユーザーã¯å–られãŸåˆ¶é™ã‚„è¦å‘Šã®åŽŸå› ã¨ãªã£ãŸãƒˆã‚¥ãƒ¼ãƒˆã‚’確èªã§ãるよã†ã«ãªã‚Šã¾ã™ send_email_notification: ユーザーã¯è‡ªåˆ†ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ä½•ãŒèµ·ã“ã£ãŸã®ã‹èª¬æ˜Žã‚’å—ã‘å–りã¾ã™ text_html: オプションã§ã™ã€‚投稿ã«ç”¨ã„る構文を使ã†ã“ã¨ãŒã§ãã¾ã™ã€‚簡略化ã®ãŸã‚<a href="%{path}">プリセットè¦å‘Šæ–‡ã‚’è¿½åŠ </a>ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ type_html: "<strong>%{acct}</strong>ã•ã‚“ã«å¯¾ã—ã€ä½•を行ã†ã‹é¸æŠžã—ã¦ãã ã•ã„" @@ -14,8 +19,10 @@ ja: avatar: "%{size}ã¾ã§ã®PNGã€GIFã€JPGãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚%{dimensions}pxã¾ã§ç¸®å°ã•れã¾ã™" bot: ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ä¸»ã«è‡ªå‹•ã§å‹•作ã—ã€äººãŒè¦‹ã¦ã„ãªã„å¯èƒ½æ€§ãŒã‚りã¾ã™ context: フィルターをé©ç”¨ã™ã‚‹å¯¾è±¡ (è¤‡æ•°é¸æŠžå¯) + current_password: ç¾åœ¨ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„ + current_username: 確èªã®ãŸã‚ã€ç¾åœ¨ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åを入力ã—ã¦ãã ã•ã„ digest: 長期間使用ã—ã¦ã„ãªã„å ´åˆã¨ä¸åœ¨æ™‚ã«è¿”ä¿¡ã‚’å—ã‘ãŸå ´åˆã®ã¿é€ä¿¡ã•れã¾ã™ - discoverable_html: <a href="%{path}" target="_blank">ディレクトリ</a> ã¯èˆˆå‘³ã‚„活動をもã¨ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’見ã¤ã‘ã‚‹ã“ã¨ã‚’å¯èƒ½ã«ã—ã¾ã™ã€‚ 掲載ã«ã¯ %{min_followers} 人以上ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼ãŒå¿…è¦ã§ã™ + discoverable: ディレクトリã¯ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’より多ãã®äººã«è¦‹ã¤ã‘ã¦ã‚‚らã†ãŸã‚ã®ã²ã¨ã¤ã®æ‰‹æ®µã§ã™ email: 確èªã®ãƒ¡ãƒ¼ãƒ«ãŒé€ä¿¡ã•れã¾ã™ fields: プãƒãƒ•ィールã«è¡¨ã¨ã—ã¦4ã¤ã¾ã§ã®é …目を表示ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ header: "%{size}ã¾ã§ã®PNGã€GIFã€JPGãŒåˆ©ç”¨å¯èƒ½ã§ã™ã€‚ %{dimensions}pxã¾ã§ç¸®å°ã•れã¾ã™" @@ -34,16 +41,24 @@ ja: setting_hide_network: フォãƒãƒ¼ã¨ãƒ•ã‚©ãƒãƒ¯ãƒ¼ã®æƒ…å ±ãŒãƒ—ãƒãƒ•ィールページã§è¦‹ã‚‰ã‚Œãªã„よã†ã«ã—ã¾ã™ setting_noindex: 公開プãƒãƒ•ィールãŠã‚ˆã³å„投稿ページã«å½±éŸ¿ã—ã¾ã™ setting_show_application: トゥートã™ã‚‹ã®ã«ä½¿ç”¨ã—ãŸã‚¢ãƒ—リãŒãƒˆã‚¥ãƒ¼ãƒˆã®è©³ç´°ãƒ“ューã«è¡¨ç¤ºã•れるよã†ã«ãªã‚Šã¾ã™ + setting_use_blurhash: ã¼ã‹ã—ã¯ãƒ¡ãƒ‡ã‚£ã‚¢ã®è‰²ã‚’å…ƒã«ç”Ÿæˆã•れã¾ã™ãŒã€ç´°éƒ¨ã¯è¦‹ãˆã«ãããªã£ã¦ã„ã¾ã™ + setting_use_pending_items: æ–°ç€ãŒã‚ã£ã¦ã‚‚タイムラインを自動的ã«ã‚¹ã‚¯ãƒãƒ¼ãƒ«ã—ãªã„よã†ã«ã—ã¾ã™ username: ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å㯠%{domain} ã®ä¸ã§é‡è¤‡ã—ã¦ã„ãªã„å¿…è¦ãŒã‚りã¾ã™ whole_word: ã‚ーワードã¾ãŸã¯ãƒ•レーズãŒè‹±æ•°å—ã®ã¿ã®å ´åˆã€å˜èªžå…¨ä½“ã¨ä¸€è‡´ã™ã‚‹å ´åˆã®ã¿é©ç”¨ã•れるよã†ã«ãªã‚Šã¾ã™ + domain_allow: + domain: 登録ã™ã‚‹ã¨ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’å—ä¿¡ã—ãŸã‚Šã€ã“ã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã‹ã‚‰å—ä¿¡ã™ã‚‹ãƒ‡ãƒ¼ã‚¿ã‚’処ç†ã—ã¦ä¿å˜ã§ãるよã†ã«ãªã‚Šã¾ã™ featured_tag: name: 'ã“れらを使ã†ã¨ã„ã„ã‹ã‚‚ã—れã¾ã›ã‚“:' + form_challenge: + current_password: ã‚»ã‚ュリティ上é‡è¦ãªã‚¨ãƒªã‚¢ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã„ã¾ã™ imports: data: ä»–ã® Mastodon サーãƒãƒ¼ã‹ã‚‰ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ãŸCSVãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ä¸‹ã•ã„ invite_request: text: ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ç¾åœ¨æ‰¿èªåˆ¶ã§ã™ã€‚申請を承èªã™ã‚‹éš›ã«å½¹ç«‹ã¤ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’æ·»ãˆã¦ãã ã•ã„ sessions: otp: 'æºå¸¯é›»è©±ã®ã‚¢ãƒ—リã§ç”Ÿæˆã•れãŸäºŒæ®µéšŽèªè¨¼ã‚³ãƒ¼ãƒ‰ã‚’入力ã™ã‚‹ã‹ã€ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ã‚’使用ã—ã¦ãã ã•ã„:' + tag: + name: è¦–èªæ€§å‘上ãªã©ã®ãŸã‚ã«ã‚¢ãƒ«ãƒ•ァベット大文å—å°æ–‡å—ã®å¤‰æ›´ã®ã¿è¡Œã†ã“ã¨ãŒã§ãã¾ã™ user: chosen_languages: é¸æŠžã™ã‚‹ã¨ã€é¸æŠžã—ãŸè¨€èªžã®ãƒˆã‚¥ãƒ¼ãƒˆã®ã¿ãŒå…¬é–‹ã‚¿ã‚¤ãƒ ラインã«è¡¨ç¤ºã•れるよã†ã«ãªã‚Šã¾ã™ labels: @@ -51,9 +66,14 @@ ja: fields: name: ラベル value: 内容 + account_alias: + acct: 引ã£è¶Šã—å…ƒã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ ID + account_migration: + acct: 引ã£è¶Šã—å…ˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ ID account_warning_preset: text: プリセットè¦å‘Šæ–‡ admin_account_action: + include_statuses: é€šå ±ã•れãŸãƒˆã‚¥ãƒ¼ãƒˆã‚’メールã«å«ã‚ã‚‹ send_email_notification: メールã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é€šçŸ¥ text: カスタムè¦å‘Šæ–‡ type: アクション @@ -108,7 +128,10 @@ ja: setting_show_application: é€ä¿¡ã—ãŸã‚¢ãƒ—リを開示ã™ã‚‹ setting_system_font_ui: システムã®ãƒ‡ãƒ•ォルトフォントを使ㆠsetting_theme: サイトテーマ + setting_trends: 本日ã®ãƒˆãƒ¬ãƒ³ãƒ‰ã‚¿ã‚°ã‚’表示ã™ã‚‹ setting_unfollow_modal: フォãƒãƒ¼ã‚’解除ã™ã‚‹å‰ã«ç¢ºèªãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’表示ã™ã‚‹ + setting_use_blurhash: éžè¡¨ç¤ºã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’色付ãã®ã¼ã‹ã—ã§è¡¨ç¤ºã™ã‚‹ + setting_use_pending_items: 手動更新モード severity: é‡å¤§æ€§ type: インãƒãƒ¼ãƒˆã™ã‚‹é …ç›® username: ユーザーå @@ -120,6 +143,8 @@ ja: must_be_follower: フォãƒãƒ¯ãƒ¼ä»¥å¤–ã‹ã‚‰ã®é€šçŸ¥ã‚’ブãƒãƒƒã‚¯ must_be_following: フォãƒãƒ¼ã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®é€šçŸ¥ã‚’ブãƒãƒƒã‚¯ must_be_following_dm: フォãƒãƒ¼ã—ã¦ã„ãªã„ユーザーã‹ã‚‰ã®ãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’ブãƒãƒƒã‚¯ + invite: + comment: コメント invite_request: text: æ„æ°—è¾¼ã¿ã‚’ãŠèžã‹ã›ãã ã•ã„ notification_emails: @@ -131,6 +156,12 @@ ja: pending_account: æ–°ã—ã„ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æ‰¿èªãŒå¿…è¦ãªæ™‚ã«ãƒ¡ãƒ¼ãƒ«ã§é€šçŸ¥ã™ã‚‹ reblog: トゥートãŒãƒ–ーストã•ã‚ŒãŸæ™‚ã«ãƒ¡ãƒ¼ãƒ«ã§é€šçŸ¥ã™ã‚‹ report: é€šå ±ã‚’å—ã‘ãŸæ™‚ã«ãƒ¡ãƒ¼ãƒ«ã§é€šçŸ¥ã™ã‚‹ + trending_tag: 未審査ã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°ãŒäººæ°—ã®æ™‚ã«ãƒ¡ãƒ¼ãƒ«ã§é€šçŸ¥ã™ã‚‹ + tag: + listable: 検索ã¨ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¸ã®ä½¿ç”¨ã‚’許å¯ã™ã‚‹ + name: ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° + trendable: トレンドã¸ã®è¡¨ç¤ºã‚’許å¯ã™ã‚‹ + usable: トゥートã¸ã®ä½¿ç”¨ã‚’許å¯ã™ã‚‹ 'no': ã„ã„㈠recommended: ãŠã™ã™ã‚ required: diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 8147cde5dbb8abd80fdd93ccb20cd9bd406b1972..28af44cbb3b8b1aa76678cdf3e988e691a497ac3 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -2,23 +2,30 @@ ko: simple_form: hints: + account_alias: + acct: ì´ë™í•˜ê³ ìž í•˜ëŠ” ê³„ì •ì˜ ì‚¬ìš©ìžì´ë¦„@ë„ë©”ì¸ì„ ì„¤ì •í•˜ì„¸ìš” + account_migration: + acct: ì´ë™í•˜ê³ ìž í•˜ëŠ” 목ì ì§€ ê³„ì •ì˜ ì‚¬ìš©ìžì´ë¦„@ë„ë©”ì¸ì„ ì„¤ì •í•˜ì„¸ìš” account_warning_preset: text: URL, 해시태그, 멘션과 ê°™ì€ íˆ¿ ë¬¸ë²•ì„ ì‚¬ìš©í• ìˆ˜ 있습니다 admin_account_action: + include_statuses: 사용ìžëŠ” ì–´ë–¤ íˆ¿ì— ëŒ€í•´ ê²½ê³ ë‚˜ 조치가 취해졌는지 ë³¼ 수 있게 ë©ë‹ˆë‹¤ send_email_notification: ìœ ì €ëŠ” ì–´ë–¤ ì¼ì´ ì¼ì–´ë‚¬ëŠ” ì§€ì— ëŒ€í•œ ì„¤ëª…ì„ ë°›ê²Œ ë©ë‹ˆë‹¤ text_html: ì„ íƒì‚¬í•. 툿 ë¬¸ë²•ì„ ì‚¬ìš©í• ìˆ˜ 있습니다. <a href="%{path}">ê²½ê³ í‹€ì„ ì¶”ê°€</a>하여 ì‹œê°„ì„ ì ˆì•½í• ìˆ˜ 있습니다 type_html: "<strong>%{acct}</strong>ì— ëŒ€í•´ ì·¨í• í–‰ë™ ì„ íƒ" warning_preset_id: ì„ íƒì‚¬í•. í‹€ì˜ ë§ˆì§€ë§‰ì— ìž„ì˜ì˜ í…스트를 추가 í• ìˆ˜ 있습니다 defaults: autofollow: ì´ ì´ˆëŒ€ë¥¼ 통해 가입하는 ì‚¬ëžŒì€ ë‹¹ì‹ ì„ ìžë™ìœ¼ë¡œ 팔로우 하게 ë©ë‹ˆë‹¤ - avatar: PNG, GIF í˜¹ì€ JPG. 최대 %{size}. %{dimensions}px로 ë‹¤ìš´ìŠ¤ì¼€ì¼ ë 것임 + avatar: PNG, GIF í˜¹ì€ JPG. 최대 %{size}. %{dimensions}px로 축소 ë¨ bot: 사람들ì—게 ê³„ì •ì´ ì‚¬ëžŒì´ ì•„ë‹˜ì„ ì•Œë¦½ë‹ˆë‹¤ context: 필터를 ì ìš© í• í•œ ê°œ ì´ìƒì˜ 컨í…스트 + current_password: ë³´ì•ˆì„ ìœ„í•´ 현재 ê³„ì •ì˜ ì•”í˜¸ë¥¼ ìž…ë ¥í•´ì£¼ì„¸ìš” + current_username: 확ì¸ì„ 위해, 현재 ê³„ì •ì˜ ì‚¬ìš©ìžëª…ì„ ìž…ë ¥í•´ì£¼ì„¸ìš” digest: 오랫ë™ì•ˆ 활ë™í•˜ì§€ ì•Šì•˜ì„ ë•Œ ë°›ì€ ë©˜ì…˜ë“¤ì— ëŒ€í•œ 요약 받기 - discoverable_html: <a href="%{path}" target="_blank">ë””ë ‰í† ë¦¬</a> 는 ì‚¬ëžŒë“¤ì˜ ê´€ì‹¬ì‚¬ì™€ 활ë™ì— ê´€ë ¨ ëœ ê³„ì •ë“¤ì„ ì°¾ì„ ìˆ˜ 있게 í•´ ì¤ë‹ˆë‹¤. 최소 %{min_followers}ëª…ì˜ íŒ”ë¡œì–´ê°€ 필요합니다 + discoverable: 프로필 ë””ë ‰í„°ë¦¬ëŠ” ë‚´ ê³„ì •ì´ ë” ë§Žì€ ê´€ì‹¬ì„ ê°–ê²Œ í• ìˆ˜ 있는 다른 방법입니다 email: ë‹¹ì‹ ì€ í™•ì¸ ë©”ì¼ì„ 받게 ë©ë‹ˆë‹¤ fields: ë‹¹ì‹ ì˜ í”„ë¡œíŒŒì¼ì— 최대 4개까지 표 형ì‹ìœ¼ë¡œ 나타낼 수 있습니다 - header: PNG, GIF í˜¹ì€ JPG. 최대 %{size}. %{dimensions}px로 ë‹¤ìš´ìŠ¤ì¼€ì¼ ë¨ + header: PNG, GIF í˜¹ì€ JPG. 최대 %{size}. %{dimensions}px로 축소 ë¨ inbox_url: 사용 í• ë¦´ë ˆì´ ì„œë²„ì˜ í”„ë¡ íŠ¸íŽ˜ì´ì§€ì—서 URLì„ ë³µì‚¬í•©ë‹ˆë‹¤ irreversible: í•„í„°ë§ ëœ íˆ¿ì€ ë‚˜ì¤‘ì— í•„í„°ê°€ 사ë¼ì§€ë”ë¼ë„ ëŒì•„오지 않게 ë©ë‹ˆë‹¤ locale: ìœ ì € ì¸í„°íŽ˜ì´ìФ, ì´ë©”ì¼, 푸시 알림 언어 @@ -34,16 +41,24 @@ ko: setting_hide_network: 나를 팔로우 하는 사람들과 ë‚´ê°€ 팔로우 하는 ì‚¬ëžŒë“¤ì´ ë‚´ í”„ë¡œí•„ì— í‘œì‹œë˜ì§€ 않게 합니다 setting_noindex: 공개 프로필 ë° ê° íˆ¿íŽ˜ì´ì§€ì— ì˜í–¥ì„ 미칩니다 setting_show_application: ë‹¹ì‹ ì´ íˆ¿ì„ ìž‘ì„±í•˜ëŠ”ë°ì— 사용한 ì•±ì´ íˆ¿ì˜ ìƒì„¸ì •ë³´ì— í‘œì‹œ ë©ë‹ˆë‹¤ + setting_use_blurhash: ê·¸ë¼ë””언트는 숨겨진 ë‚´ìš©ì˜ ìƒ‰ìƒì„ 기반으로 하지만 ìƒì„¸ ë‚´ìš©ì€ ë³´ì´ì§€ 않게 합니다 + setting_use_pending_items: 타임ë¼ì¸ì˜ 새 ê²Œì‹œë¬¼ì„ ìžë™ìœ¼ë¡œ ë³´ì—¬ 주는 ëŒ€ì‹ , í´ë¦í•´ì„œ 나타내ë„ë¡ í•©ë‹ˆë‹¤ username: ë‹¹ì‹ ì˜ ìœ ì €ë„¤ìž„ì€ %{domain} 안ì—서 ìœ ì¼í•´ì•¼ 합니다 whole_word: 키워드가 ì˜ë¬¸ê³¼ 숫ìžë¡œë§Œ ì´ë£¨ì–´ ì§„ 경우, 단어 ì „ì²´ì— ë§¤ì¹ ë˜ì—ˆì„ 때ì—ë§Œ ìž‘ë™í•˜ê²Œ 합니다 + domain_allow: + domain: ì´ ë„ë©”ì¸ì€ ì´ ì„œë²„ì—서 ë°ì´í„°ë¥¼ ê°€ì ¸ê°ˆ 수 ìžˆê³ ì´ ë„ë©”ì¸ì—서 ë³´ë‚´ì§„ ë°ì´í„°ëŠ” 처리ë˜ê³ ì €ìž¥ ë©ë‹ˆë‹¤ featured_tag: name: 'ì´ê²ƒë“¤ì„ 사용하면 ì¢‹ì„ ê²ƒ 같습니다:' + form_challenge: + current_password: ë‹¹ì‹ ì€ ë³´ì•ˆ 구ì—ì— ì§„ìž…í•˜ê³ ìžˆìŠµë‹ˆë‹¤ imports: data: 다른 ë§ˆìŠ¤í† ëˆ ì„œë²„ì—서 ì¶”ì¶œëœ CSV íŒŒì¼ invite_request: text: ì´ ì •ë³´ëŠ” 우리가 심사를 하는 ë°ì— ì°¸ê³ í• ìˆ˜ 있습니다 sessions: otp: 'íœ´ëŒ€ì „í™”ì—서 ìƒì„± ëœ 2단계 ì¸ì¦ 코드를 ìž…ë ¥í•˜ê±°ë‚˜, 복구 코드 중 하나를 사용하세요:' + tag: + name: ì½ê¸° 쉽게하기 위한 글ìžì˜ 대소문ìžë§Œ ë³€ê²½í• ìˆ˜ 있습니다. user: chosen_languages: ì²´í¬í•˜ë©´, ì„ íƒ ëœ ì–¸ì–´ë“¤ë§Œ 공개 타임ë¼ì¸ì— 보여집니다 labels: @@ -51,9 +66,14 @@ ko: fields: name: ë¼ë²¨ value: ë‚´ìš© + account_alias: + acct: 기존 ê³„ì •ì˜ í•¸ë“¤ + account_migration: + acct: 새 ê³„ì •ì˜ í•¸ë“¤ account_warning_preset: text: 프리셋 í…스트 admin_account_action: + include_statuses: ì‹ ê³ ëœ íˆ¿ì„ ì´ë©”ì¼ì— í¬í•¨ send_email_notification: ì´ë©”ì¼ë¡œ ìœ ì €ì—게 알리기 text: 커스텀 ê²½ê³ type: 조치 @@ -108,7 +128,10 @@ ko: setting_show_application: 툿 ìž‘ì„±ì— ì‚¬ìš©í•œ ì•±ì„ ê³µê°œ setting_system_font_ui: ì‹œìŠ¤í…œì˜ ì´ˆê¸° ì„¤ì • í°íŠ¸ë¥¼ 사용 setting_theme: 사ì´íЏ 테마 + setting_trends: ì˜¤ëŠ˜ì˜ ìœ í–‰ ë³´ì´ê¸° setting_unfollow_modal: 언팔로우 ì „ 언팔로우 í™•ì¸ í‘œì‹œ + setting_use_blurhash: 숨겨진 ë¯¸ë””ì–´ì— ëŒ€í•´ ê·¸ë¼ë””언트 표시 + setting_use_pending_items: ëŠë¦° 모드 severity: 심ê°ë„ type: 불러오기 종류 username: ìœ ì € ì´ë¦„ @@ -120,6 +143,8 @@ ko: must_be_follower: 나를 팔로우 하지 않는 사람ì—게서 온 ì•Œë¦¼ì„ ì°¨ë‹¨ must_be_following: ë‚´ê°€ 팔로우 하지 않는 사람ì—게서 온 ì•Œë¦¼ì„ ì°¨ë‹¨ must_be_following_dm: ë‚´ê°€ 팔로우 하지 ì•Šì€ ì‚¬ëžŒì—게서 오는 다ì´ë ‰íŠ¸ë©”ì‹œì§€ë¥¼ 차단 + invite: + comment: ì£¼ì„ invite_request: text: ê°€ìž…í•˜ë ¤ëŠ” ì´ìœ ê°€ 무엇ì¸ê°€ìš”? notification_emails: @@ -131,8 +156,15 @@ ko: pending_account: 새 ê³„ì •ì´ ì‹¬ì‚¬ê°€ í•„ìš”í• ë•Œ ì´ë©”ì¼ ë³´ë‚´ê¸° reblog: 누군가 ë‚´ íˆ¿ì„ ë¶€ìŠ¤íŠ¸ í–ˆì„ ë•Œ ì´ë©”ì¼ ë³´ë‚´ê¸° report: 새 ì‹ ê³ ë“±ë¡ì‹œ ì´ë©”ì¼ë¡œ 알리기 + trending_tag: 리뷰 ë˜ì§€ ì•Šì€ í•´ì‹œíƒœê·¸ê°€ ìœ í–‰í• ë•Œ ì´ë©”ì¼ ë³´ë‚´ê¸° + tag: + listable: ì´ í•´ì‹œíƒœê·¸ê°€ 프로필 ë””ë ‰í† ë¦¬ì— ë³´ì—¬ì§€ë„ë¡ í—ˆìš© + name: 해시태그 + trendable: ì´ í•´ì‹œíƒœê·¸ê°€ ìœ í–‰ì— ë³´ì—¬ì§€ë„ë¡ í—ˆìš© + usable: ì´ í•´ì‹œíƒœê·¸ë¥¼ íˆ¿ì— ì‚¬ìš© 가능하ë„ë¡ í—ˆìš© 'no': 아니오 recommended: 추천함 required: + mark: "*" text: 필수 í•목 'yes': 네 diff --git a/config/locales/simple_form.mk.yml b/config/locales/simple_form.mk.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b9144a988941222eba3d4c61c19a62a8b057db4 --- /dev/null +++ b/config/locales/simple_form.mk.yml @@ -0,0 +1 @@ +mk: diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 58d29ce12c036755ccba4da4fcbfa580796898f3..77445b0cbca8c0c66d697a105136aec3f3afe0ee 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -2,9 +2,14 @@ nl: simple_form: hints: + account_alias: + acct: Vul de gebruikersnaam@domein van het account in, die je wilt verhuizen + account_migration: + acct: Vul de gebruikersnaam@domein van het account in, waarnaartoe je wilt verhuizen account_warning_preset: text: Je kunt voor toots specifieke tekst gebruiken, zoals URL's, hashtags en vermeldingen admin_account_action: + include_statuses: De gebruiker ziet welke toots verantwoordelijk zijn voor de moderatieactie of waarschuwing send_email_notification: De gebruiker ontvangt een uitleg over wat er met hun account is gebeurd text_html: Optioneel. Je kunt voor toots specifieke tekst gebruiken. Om tijd te besparen kun je <a href="%{path}">voorinstellingen van waarschuwingen toevoegen</a> type_html: Kies wat er met <strong>%{acct}</strong> moet gebeuren @@ -14,8 +19,10 @@ nl: avatar: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px bot: Dit is een geautomatiseerd account en wordt mogelijk niet gemonitord context: Een of meerdere locaties waar de filter actief moet zijn + current_password: Voer voor veiligheidsredenen het wachtwoord van je huidige account in + current_username: Voer ter bevestiging de gebruikersnaam van je huidige account in digest: Wordt alleen na een lange periode van inactiviteit verzonden en alleen wanneer je tijdens jouw afwezigheid persoonlijke berichten hebt ontvangen - discoverable_html: In de <a href="%{path}" target="_blank">gebruikersgids</a> kunnen mensen andere accounts vinden aan de hand van interesses en activiteit. Dit vereist tenminste %{min_followers} volgers + discoverable: De gebruikersgids is een andere manier waarmee jouw account een groter publiek kan bereiken email: Je krijgt een bevestigingsmail fields: Je kan maximaal 4 items als een tabel op je profiel weergeven header: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px @@ -34,16 +41,24 @@ nl: setting_hide_network: Wie jij volgt en wie jou volgen wordt niet op jouw profiel getoond setting_noindex: Heeft invloed op jouw openbare profiel en toots setting_show_application: De toepassing de je gebruikt om te tooten wordt in de gedetailleerde weergave van de toot getoond + setting_use_blurhash: Wazige kleurovergangen zijn gebaseerd op de kleuren van de verborgen media, waarmee elk detail verdwijnt + setting_use_pending_items: De tijdlijn wordt bijgewerkt door op het aantal nieuwe items te klikken, in plaats van dat deze automatisch wordt bijgewerkt username: Jouw gebruikersnaam is uniek op %{domain} whole_word: Wanneer het trefwoord of zinsdeel alfanumeriek is, wordt het alleen gefilterd wanneer het hele woord overeenkomt + domain_allow: + domain: Dit domein is in staat om gegevens van deze server op te halen, en binnenkomende gegevens worden verwerkt en opgeslagen featured_tag: name: 'Je wilt misschien een van deze gebruiken:' + form_challenge: + current_password: Je betreedt een veilige omgeving imports: data: CSV-bestand dat op een andere Mastodonserver werd geëxporteerd invite_request: text: Dit helpt ons om jouw aanvraag te beoordelen sessions: otp: 'Voer de tweestaps-aanmeldcode vanaf jouw mobiele telefoon in of gebruik een van jouw herstelcodes:' + tag: + name: Je kunt elk woord met een hoofdletter beginnen, om zo bijvoorbeeld de tekst leesbaarder te maken user: chosen_languages: Alleen toots in de aangevinkte talen worden op de openbare tijdlijnen getoond labels: @@ -51,9 +66,14 @@ nl: fields: name: Label value: Inhoud + account_alias: + acct: Mastodonadres van het oude account + account_migration: + acct: Mastodonadres van het nieuwe account account_warning_preset: text: Tekst van voorinstelling admin_account_action: + include_statuses: Gerapporteerde toots aan de e-mail toevoegen send_email_notification: Meld dit per e-mail aan de gebruiker text: Aangepaste waarschuwing type: Actie @@ -108,7 +128,10 @@ nl: setting_show_application: Toepassing onthullen die je voor het verzenden van toots gebruikt setting_system_font_ui: Standaardlettertype van jouw systeem gebruiken setting_theme: Thema website + setting_trends: Trends van vandaag tonen setting_unfollow_modal: Vraag voor het ontvolgen van iemand een bevestiging + setting_use_blurhash: Wazige kleurovergangen voor verborgen media tonen + setting_use_pending_items: Langzame modus severity: Zwaarte type: Importtype username: Gebruikersnaam @@ -120,6 +143,8 @@ nl: must_be_follower: Meldingen van mensen die jou niet volgen blokkeren must_be_following: Meldingen van mensen die jij niet volgt blokkeren must_be_following_dm: Directe berichten van mensen die jij niet volgt blokkeren + invite: + comment: Opmerking invite_request: text: Waarom wil jij je aanmelden? notification_emails: @@ -131,6 +156,12 @@ nl: pending_account: Een e-mail verzenden wanneer een nieuw account moet worden beoordeeld reblog: Een e-mail versturen wanneer iemand jouw toot heeft geboost report: Verstuur een e-mail wanneer een nieuw rapportage is ingediend + trending_tag: Een e-mail versturen wanneer een nog niet beoordeelde hashtag trending is + tag: + listable: Toestaan dat deze hashtag in zoekopdrachten en in de gebruikersgids te zien valt + name: Hashtag + trendable: Toestaan dat deze hashtag onder trends te zien valt + usable: Toestaan dat deze hashtag in toots gebruikt mag worden 'no': Nee recommended: Aanbevolen required: diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml new file mode 100644 index 0000000000000000000000000000000000000000..777f4e600f65daa6a550f2cf0f04551a49cf4fcd --- /dev/null +++ b/config/locales/simple_form.nn.yml @@ -0,0 +1 @@ +nn: diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml index e0bfcfef9c57864c842fd4b3389c20035e978174..59651d9de3565e7e2c2aabd12b74bb50cb832ecd 100644 --- a/config/locales/simple_form.oc.yml +++ b/config/locales/simple_form.oc.yml @@ -2,9 +2,14 @@ oc: simple_form: hints: + account_alias: + acct: Donatz l’utilizaire@domeni del compte que volètz desplaçar + account_migration: + acct: Donatz l’utilizaire@domeni del compte ont volètz anar account_warning_preset: text: Podètz utilizar la sintaxi dels tuts, coma las URL, las etiquetas e las mencions admin_account_action: + include_statuses: L’utilizaire veirà s quals tuts a provocat l’accion de moderacion o avertiment send_email_notification: L’utilizaire recebrà una explicacion de çò qu’arribèt a son compte text_html: Opcional. Podètz utilizar la sintaxi dels tuts. Podètz <a href="%{path}">ajustar un avertiment personalizat</a> per estalviar de temps type_html: Causir de qué far amb <strong>%{acct}</strong> @@ -14,8 +19,10 @@ oc: avatar: PNG, GIF o JPG. Maximum %{size}. Serà retalhat en %{dimensions}px bot: Avisar lo monde qu’aqueste compte es pas d’una persona context: Un o mai de contèxtes ont lo filtre deuriá s’aplicar + current_password: Per de rasons de seguretat volgatz picar lo senhal del compte actual + current_username: Per confirmar, volgatz picar lo nom d’utilizaire del compte actual digest: Solament enviat aprèp un long moment d’inactivitat e solament s’avètz recebut de messatges personals pendent vòstra abséncia - discoverable_html: L’<a href="%{path}" target="_blank">annuari</a> permet al monde de trobar de comptes segon lor interèsses e activitats. RequerÃs almens %{min_followers} seguidors + discoverable: L’annuari de perfiles es un biais mai per que vòstre compte aja una audiéncia mai granda email: Vos mandarem un corrièl de confirmacion fields: Podètz far veire cap a 4 elements sus vòstre perfil header: PNG, GIF o JPG. Maximum %{size}. Serà retalhada en %{dimensions}px @@ -27,22 +34,31 @@ oc: phrase: Serà pres en compte que siá en majuscula o minuscula o dins un avertiment de contengut sensible scopes: A quinas APIs poirà n accedir las aplicacions. Se seleccionatz un encastre de naut nivèl, fa pas mestièr de seleccionar los nivèls mai basses. setting_aggregate_reblogs: Mostrar pas los nòus partatges que son estats partejats recentament (afecta pas que los nòus partatges recebuts) + setting_default_sensitive: Los mèdias sensibles son resconduts per defaut e se revelhan amb un clic setting_display_media_default: Rescondre los mèdias marcats coma sensibles setting_display_media_hide_all: Totjorn rescondre los mèdias setting_display_media_show_all: Totjorn mostrar los mèdias marcats coma sensibles setting_hide_network: Vòstre perfil mostrarà pas los que vos sègon e lo monde que seguètz setting_noindex: Aquò es destinat a vòstre perfil public e vòstra pagina d’estatuts setting_show_application: Lo nom de l’aplicacion qu’utilizatz per publicar serà mostrat dins la vista detalhada de vòstres tuts + setting_use_blurhash: Los degradats venon de las colors de l’imatge rescondut en enfoscar los detalhs + setting_use_pending_items: Rescondre las actualizacions del flux d’actualitat aprèp un clic allòc de desfilar lo flux automaticament username: Vòstre nom d’utilizaire serà unic sus %{domain} whole_word: Quand lo mot-clau o frasa es solament alfranumeric, serà pas qu’aplicat se correspond al mot complèt + domain_allow: + domain: Aqueste domeni poirà recuperar las donadas d’aqueste servidor estant e las donadas venent d’aqueste domeni serà n tractadas e gardadas featured_tag: name: 'Benlèu que volètz utilizar una d’aquestas causas :' + form_challenge: + current_password: Dintratz dins una zòna segura imports: data: Fichièr CSV exportat d’un autre servidor Mastodon invite_request: text: Aquò nos ajudarà per validar vòstra demanda sessions: otp: 'Picatz lo còdi d’autentificacion en dos temps (Two factor code) de vòstra aplicacion mobil o utilizatz un de vòstres còdis de recuperacion :' + tag: + name: Podètz pas que cambiar la talha de las letras, per exemple, per que sián de melhor legir user: chosen_languages: Quand seleccionadas, solament los tuts dins las lengas triadas serà n mostrats dins vòstre flux d’actualitat labels: @@ -50,9 +66,14 @@ oc: fields: name: Nom value: Contengut + account_alias: + acct: Identificant de l’ancian compte + account_migration: + acct: Identificant del nòu compte account_warning_preset: text: Tèxt predefinit admin_account_action: + include_statuses: Inclure los tuts senhalats dins lo corrièl send_email_notification: Avisar l’utilizaire per corrièl text: Avertiment personalizat type: Accions @@ -64,6 +85,7 @@ oc: warning_preset_id: Utilizar un avertiment predefinit defaults: autofollow: Convidar a sègre vòstre compte + avatar: Avatar bot: Aquò es lo compte a un robòt chosen_languages: Filtrar las lengas confirm_new_password: Confirmacion del nòu senhal @@ -83,9 +105,11 @@ oc: locked: Far venir lo compte privat max_uses: Limit d’utilizacions new_password: Nòu senhal + note: Bio otp_attempt: Còdi Two-factor password: Senhal phrase: Senhal o frasa + setting_advanced_layout: Activar l’interfà cia web avançada setting_aggregate_reblogs: Agropar los partatges dins lo flux d’actualitat setting_auto_play_gif: Lectura automatica dels GIFS animats setting_boost_modal: Mostrar una fenèstra de confirmacion abans de partejar un estatut @@ -104,7 +128,10 @@ oc: setting_show_application: Revelar lo nom de l’aplicacion utilizada per enviar de tuts setting_system_font_ui: Utilizar la polissa del sistèma setting_theme: Tèma del site + setting_trends: Veire las tendéncias d’uèi setting_unfollow_modal: Mostrar una confirmacion abans de quitar de sègre qualqu’un + setting_use_blurhash: Mostrar los degradats colorats pels mèdias resconduts + setting_use_pending_items: Mòde lent severity: Severitat type: Tipe d’impòrt username: Nom d’utilizaire @@ -116,6 +143,8 @@ oc: must_be_follower: Blocar las notificacions del mond que vos sègon pas must_be_following: Blocar las notificacions del mond que seguètz pas must_be_following_dm: Blocar los messatges del monde que seguètz pas + invite: + comment: Comentari invite_request: text: Perqué volètz vos marcar ? notification_emails: @@ -127,7 +156,15 @@ oc: pending_account: Enviar un corrièl quand cal validar un compte novèl reblog: Enviar un corrièl quand qualqu’un tòrna partejar vòstre estatut report: Enviar un corrièl pels nòus senhalaments + trending_tag: Enviar un corrièl quand una etiqueta pas repassada es en tendéncia + tag: + listable: Permetre a aquesta etiqueta d’aparéisser a las recèrcas e a l’annuari de perfils + name: Etiqueta + trendable: Permetre a aquesta etiqueta d’aparéisser a las tendéncias + usable: Permetre als tuts d’utilizar aquesta etiqueta 'no': Non + recommended: Recomandat required: + mark: "*" text: requesit 'yes': Ã’c diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index 2f9bf5329f17d78d06b644109e1d0fa3cbfb8e39..6958179851fe5ddb8604f41f21b4d7aedd892db0 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -15,7 +15,6 @@ pl: bot: To konto wykonuje głównie zautomatyzowane dziaÅ‚ania i może nie być monitorowane context: Jedno lub wiele miejsc, w których filtr zostanie zastosowany digest: WysyÅ‚ane tylko po dÅ‚ugiej nieaktywnoÅ›ci, jeżeli w tym czasie otrzymaleÅ› jakÄ…Å› wiadomość bezpoÅ›redniÄ… - discoverable_html: <a href="%{path}" target="_blank">Katalog</a> pozwala znaleźć konta na podstawie zainteresowaÅ„ i aktywnoÅ›ci. Profil musi Å›ledzić przynajmniej %{min_followers} osób email: Otrzymasz e-mail potwierdzajÄ…cy fields: Możesz ustawić maksymalnie 4 niestandardowe pola wyÅ›wietlane jako tabela na Twoim profilu header: PNG, GIF lub JPG. Maksymalnie %{size}. Zostanie zmniejszony do %{dimensions}px @@ -34,6 +33,7 @@ pl: setting_hide_network: Informacje o tym, kto CiÄ™ Å›ledzi i kogo Å›ledzisz nie bÄ™dÄ… widoczne setting_noindex: WpÅ‚ywa na widoczność strony profilu i Twoich wpisów setting_show_application: W informacjach o wpisie bÄ™dzie widoczna informacja o aplikacji, z której zostaÅ‚ wysÅ‚any + setting_use_blurhash: Gradienty sÄ… oparte na kolorach ukrywanej zawartoÅ›ci, ale uniewidaczniajÄ… wszystkie szczegóły username: Twoja nazwa użytkownika bÄ™dzie niepowtarzalna na %{domain} whole_word: JeÅ›li sÅ‚owo lub fraza skÅ‚ada siÄ™ jedynie z liter lub cyfr, filtr bÄ™dzie zastosowany tylko do peÅ‚nych wystÄ…pieÅ„ featured_tag: @@ -109,6 +109,7 @@ pl: setting_system_font_ui: Używaj domyÅ›lnej czcionki systemu setting_theme: Motyw strony setting_unfollow_modal: Pytaj o potwierdzenie przed cofniÄ™ciem Å›ledzenia + setting_use_blurhash: Pokazuj kolorowe gradienty dla ukrytej zawartoÅ›ci multimedialnej severity: Priorytet type: Importowane dane username: Nazwa użytkownika diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index df7af796cdf4ea8830d725c3ce122d253dfa5d33..72f179d5238af5affcbc180a482355af9c46b882 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -2,9 +2,14 @@ pt-BR: simple_form: hints: + account_alias: + acct: Especifique o usuário@domÃnio da conta da qual você deseja mover + account_migration: + acct: Especifique o usuário@domÃnio da conta para o qual você deseja mover account_warning_preset: text: Você pode usar a sintaxe de um toot, como URLs, hashtags e menções admin_account_action: + include_statuses: O usuário verá quais toots causaram o aviso ou ação da moderação send_email_notification: O usuário vai receber uma explicação do que aconteceu com a sua conta text_html: Opcional. Você pode usar a sintaxe de toots. Você pode <a href="%{path}">adicionar avisos pré-definidos</a> para ganhar tempo type_html: Escolha o que fazer com <strong>%{acct}</strong> @@ -14,8 +19,9 @@ pt-BR: avatar: PNG, GIF or JPG. Arquivos de até %{size}. Eles serão diminuÃdos para %{dimensions}px bot: Essa conta executa principalmente ações automatizadas e pode não ser monitorada context: Um ou mais contextos onde o filtro deve ser aplicado + current_password: Para fins de segurança digite a senha da conta atual + current_username: Para confirmar, por favor, digite o usuário da conta atual digest: Enviado após um longo perÃodo de inatividade com um resumo das menções que você recebeu em sua ausência - discoverable_html: O <a href="%{path}" target="_blank">diretório</a> permite encontrar contas baseado em seus interesses e atividades. Requer pelo menos %{min_followers} seguidores email: Você receberá um email de confirmação fields: Você pode ter até 4 itens exibidos em forma de tabela no seu perfil header: PNG, GIF or JPG. Arquivos de até %{size}. Eles serão diminuÃdos para %{dimensions}px @@ -27,16 +33,21 @@ pt-BR: phrase: Vai coincidir, independente de maiúsculas ou minúsculas, no texto ou no aviso de conteúdo de um toot scopes: Quais APIs a aplicação vai ter permissão de acessar. Se você selecionar um escopo de alto nÃvel, você não precisa selecionar individualmente os outros. setting_aggregate_reblogs: Não mostrar novos compartilhamentos para toots que foram compartilhados recentemente (afeta somente novos compartilhamentos recebidos) + setting_default_sensitive: MÃdia sensÃvel está oculta por padrão e pode ser revelada com um clique setting_display_media_default: Esconder mÃdia marcada como sensÃvel setting_display_media_hide_all: Sempre esconder todas as mÃdias setting_display_media_show_all: Sempre mostrar mÃdia marcada como sensÃvel setting_hide_network: Quem você segue e quem segue você não serão exibidos no seu perfil setting_noindex: Afeta seu perfil público e as páginas de suas postagens setting_show_application: A aplicação que você usar para enviar seus toots vai aparecer na visão detalhada dos seus toots + setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer detalhes + setting_use_pending_items: Ocultar atualizações de linha de tempo atrás de um clique ao invés de rolar automaticamente o feed username: Seu nome de usuário será único em %{domain} whole_word: Quando a palavra ou frase é inteiramente alfanumérica, ela será aplicada somente se corresponder a palavra inteira featured_tag: name: 'Você pode querer usar um destes:' + form_challenge: + current_password: Você está entrando em uma área segura imports: data: Arquivo CSV exportado de outra instância do Mastodon invite_request: @@ -53,6 +64,7 @@ pt-BR: account_warning_preset: text: Texto pré-definido admin_account_action: + include_statuses: Incluir toots reportados no e-mail send_email_notification: Notificar o usuário por e-mail text: Aviso customizado type: Ação @@ -64,6 +76,7 @@ pt-BR: warning_preset_id: Usar um aviso pré-definido defaults: autofollow: Convite para seguir a sua conta + avatar: Imagem de Perfil bot: Esta é uma conta-robô chosen_languages: Filtros de idioma confirm_new_password: Confirmar nova senha @@ -83,6 +96,7 @@ pt-BR: locked: Trancar conta max_uses: Número máximo de usos new_password: Nova senha + note: Biografia otp_attempt: Código de autenticação em dois passos password: Senha phrase: Palavra-chave ou frase @@ -111,10 +125,14 @@ pt-BR: username: Nome de usuário username_or_email: Nome de usuário ou e-mail whole_word: Palavra inteira + featured_tag: + name: Hashtag interactions: must_be_follower: Bloquear notificações de não-seguidores must_be_following: Bloquear notificações de pessoas que você não segue must_be_following_dm: Bloquear mensagens diretas de pessoas que você não segue + invite: + comment: Comentário invite_request: text: Por que você quer se cadastrar? notification_emails: @@ -126,7 +144,15 @@ pt-BR: pending_account: Mandar um -mail quando uma nova conta precisar ser revisada reblog: Mandar um e-mail quando alguém compartilhar suas postagens report: Mandar um e-mail quando uma nova denúncia é submetida + trending_tag: Enviar e-mail quando uma hashtag não revisada estiver em alta + tag: + listable: Permitir que esta hashtag apareça em pesquisas e no diretório de perfis + name: Hashtag + trendable: Permitir que esta hashtag apareça em alta + usable: Permitir que toots usem esta hastag 'no': Não + recommended: Recomendado required: + mark: "*" text: obrigatório 'yes': Sim diff --git a/config/locales/simple_form.pt.yml b/config/locales/simple_form.pt-PT.yml similarity index 94% rename from config/locales/simple_form.pt.yml rename to config/locales/simple_form.pt-PT.yml index bf638188984afe3e3b01e0590473a49aa6212d86..57acde4d9631dabb3c8304cd17352f1768cfa899 100644 --- a/config/locales/simple_form.pt.yml +++ b/config/locales/simple_form.pt-PT.yml @@ -1,10 +1,15 @@ --- -pt: +pt-PT: simple_form: hints: + account_alias: + acct: Especifique o nome de usuário@domÃnio da conta de onde você deseja mover + account_migration: + acct: Especifique o nome de usuário@domÃnio da conta para onde você deseja mover account_warning_preset: text: Tu podes usar sintaxe de escrita, como URLs, hashtags e referências admin_account_action: + include_statuses: O usuário verá quais ferramentas causaram a ação de moderação ou aviso send_email_notification: O utilizador receberá uma explicação sobre o que aconteceu com a sua conta text_html: Opcional. Tu podes usar sintaxe de escrita. Tu podes <a href="%{path}"> adicionar predefinições de aviso</a> para poupar tempo type_html: Escolhe o que fazer com <strong>%{acct}</strong> @@ -14,8 +19,8 @@ pt: avatar: PNG, GIF or JPG. Arquivos até %{size}. Vão ser reduzidos para %{dimensions}px bot: Esta conta executa essencialmente acções automáticas e pode não poder ser monitorizada context: Um ou múltiplos contextos nos quais o filtro deve ser aplicado + current_password: Para fins de segurança, por favor, digite a senha da conta atual digest: Enviado após um longo perÃodo de inatividade e apenas se foste mencionado na tua ausência - discoverable_html: O <a href="%{path}" target="_blank">directory</a> permite encontrar contas de pessoas com base nos seus interesses e actividades. Exige, pelo menos %{min_followers} seguidores email: Será enviado um e-mail de confirmação fields: Podes ter até 4 itens expostos, em forma de tabela, no teu perfil header: PNG, GIF or JPG. Arquivos até %{size}. Vão ser reduzidos para %{dimensions}px diff --git a/config/locales/simple_form.ro.yml b/config/locales/simple_form.ro.yml index 4df2fe16100d1f84b9b3df9bf27a5d41954a1bb7..ac4c344f192ae0490507479cc477a1afb6a76aad 100644 --- a/config/locales/simple_form.ro.yml +++ b/config/locales/simple_form.ro.yml @@ -15,7 +15,6 @@ ro: bot: Acest cont performează în cea mai mare parte acÈ›iuni automate È™i nu poate fi monitorizat context: Contextele în care filtrul trebuie aplicat digest: Este trimis doar după o lungă perioadă de inactivitate È™i numai dacă primeÈ™ti mesaje personale în perioada de absență - discoverable_html: <a href="%{path}" target="_blank">Directorul</a> permite utilizatorilor să găsească conturi după interese È™i activități. Necesită minim %{min_followers} urmăritori email: Vei primi un e-mail de confirmare fields: Poti afiÈ™a pană la maxim 4 adrese sub formă de tabel pe pofilul tău header: PNG, GIF sau JPG. Cel mult %{size}. Vor fi redimensionate la %{dimensions}px diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 26a73c3c6d9925306cc07d39722e9d965b9ccb56..ab5eb855e27acae53600503a0d5ed0c4eaa2acca 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -10,19 +10,18 @@ ru: type_html: Выберите, что делать Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð¾Ð¼ <strong>%{acct}</strong> warning_preset_id: ÐеобÑзательно. Ð’Ñ‹ можете добавить ÑобÑтвенный текÑÑ‚ в конце шаблона defaults: - autofollow: Люди, пришедшие по Ñтому приглашению автоматичеÑки будут подпиÑаны на Ð’Ð°Ñ - avatar: PNG, GIF или JPG. МакÑимально %{size}. Будет уменьшено до %{dimensions}px + autofollow: Люди, пришедшие по Ñтому приглашению, автоматичеÑки будут подпиÑаны на Ð²Ð°Ñ + avatar: ПоддерживаетÑÑ PNG, GIF и JPG. МакÑимальный размер — %{size}. Будет уменьшен до %{dimensions}px bot: Ðтот аккаунт обычно выполÑет автоматизированные дейÑÑ‚Ð²Ð¸Ñ Ð¸ может не проÑматриватьÑÑ Ð²Ð»Ð°Ð´ÐµÐ»ÑŒÑ†ÐµÐ¼ context: Один или неÑколько контекÑтов, к которым должны быть применены фильтры digest: ОтÑылаетÑÑ Ð»Ð¸ÑˆÑŒ поÑле длительной неактивноÑти, еÑли вы в Ñто Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°Ð»Ð¸ личные ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ - discoverable_html: <a href="%{path}" target="_blank">Каталог</a> позволÑет пользователÑм иÑкать людей по интереÑам и активноÑти. Ðеобходимо наличие не менее %{min_followers} подпиÑчиков email: Вам будет отправлено Ñлектронное пиÑьмо Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸ÐµÐ¼ fields: Ð’ профиле можно отобразить до 4 пунктов как таблицу - header: PNG, GIF или JPG. МакÑимально %{size}. Будет уменьшено до %{dimensions}px + header: ПоддерживаетÑÑ PNG, GIF и JPG. МакÑимальный размер — %{size}. Будет уменьшена до %{dimensions}px inbox_url: Копировать URL Ñ Ð³Ð»Ð°Ð²Ð½Ð¾Ð¹ Ñтраницы ретранÑлÑтора, который вы хотите иÑпользовать irreversible: Отфильтрованные ÑтатуÑÑ‹ будут утерÑны навÑегда, даже еÑли в будущем фильтр будет убран locale: Язык интерфейÑа, e-mail пиÑем и push-уведомлений - locked: Потребует от Ð²Ð°Ñ Ñ€ÑƒÑ‡Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков, изменит приватноÑть поÑтов по умолчанию на "только Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñчиков" + locked: ПодпиÑчиков нужно будет подтверждать ÑамоÑтоÑтельно password: Укажите не менее 8 Ñимволов phrase: Будет ÑопоÑтавлено незавиÑимо от приÑутÑÑ‚Ð²Ð¸Ñ Ð² текÑте или Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¾ Ñодержании ÑтатуÑа scopes: Какие API приложению будет позволено иÑпользовать. ЕÑли вы выберете Ñамый верхний, нижеÑтоÑщие будут выбраны автоматичеÑки. @@ -34,8 +33,12 @@ ru: setting_hide_network: Те, на кого вы подпиÑаны и кто подпиÑан на ВаÑ, не будут отображены в вашем профиле setting_noindex: ОтноÑитÑÑ Ðº вашему публичному профилю и Ñтраницам ÑтатуÑов setting_show_application: Ð’ окне проÑмотра вашего ÑтатуÑа будет видно, Ñ ÐºÐ°ÐºÐ¾Ð³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¾Ð½ был отправлен + setting_use_blurhash: Градиенты оÑнованы на цветах Ñкрытых медиа, но Ñкрывают любые детали + setting_use_pending_items: Показывать Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð² ленте только поÑле клика вмеÑто автоматичеÑкой прокрутки username: Ваш юзернейм будет уникальным на %{domain} whole_word: ЕÑли Ñлово или фраза ÑоÑтоит только из букв и цифр, ÑопоÑтавление произойдёт только по полному Ñовпадению + domain_allow: + domain: Ðтот домен Ñможет получать данные Ñ Ñтого Ñервера и его входÑщие данные будут обрабатыватьÑÑ Ð¸ Ñохранены featured_tag: name: 'Возможно, вы захотите выбрать из них:' imports: @@ -44,6 +47,8 @@ ru: text: Ðто поможет нам раÑÑмотреть вашу заÑвку sessions: otp: 'Введите код двухфакторной аутентификации, Ñгенерированный в мобильном приложении, или иÑпользуйте один из ваших кодов воÑÑтановлениÑ:' + tag: + name: Ð’Ñ‹ можете изменить только региÑтр букв чтобы, например, Ñделать тег более читаемым user: chosen_languages: ЕÑли выбрано, то в публичных лентах будут показаны только поÑты на выбранных Ñзыках labels: @@ -64,7 +69,7 @@ ru: suspend: Заблокировать и безвозвратно удалить вÑе данные аккаунта warning_preset_id: ИÑпользовать шаблон defaults: - autofollow: ПриглаÑите подпиÑатьÑÑ Ð½Ð° Ваш аккаунт + autofollow: С подпиÑкой на ваш аккаунт avatar: Ðватар bot: Ðто аккаунт бота chosen_languages: Фильтр Ñзыков @@ -74,7 +79,7 @@ ru: current_password: Текущий пароль data: Данные discoverable: Показывать Ñтот аккаунт в каталоге - display_name: Показываемое Ð¸Ð¼Ñ + display_name: Отображаемое Ð¸Ð¼Ñ email: ÐÐ´Ñ€ÐµÑ e-mail expires_in: ИÑтекает через fields: Метаданные Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ @@ -83,20 +88,20 @@ ru: irreversible: УдалÑть, а не Ñкрывать locale: Язык интерфейÑа locked: Сделать аккаунт закрытым - max_uses: МакÑ. чиÑло иÑпользований + max_uses: МакÑимальное чиÑло иÑпользований new_password: Ðовый пароль - note: О Ð’Ð°Ñ + note: О Ñебе otp_attempt: Двухфакторный код password: Пароль phrase: Слово или фраза setting_advanced_layout: Включить многоколоночный Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ setting_aggregate_reblogs: Группировать Ð¿Ñ€Ð¾Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð² лентах setting_auto_play_gif: ÐвтоматичеÑки проигрывать анимированные GIF - setting_boost_modal: Показывать диалог Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ продвижением + setting_boost_modal: Ð’Ñегда Ñпрашивать перед продвижением setting_default_language: Язык отправлÑемых ÑтатуÑов setting_default_privacy: ВидимоÑть поÑтов setting_default_sensitive: Ð’Ñегда отмечать медиаконтент как чувÑтвительный - setting_delete_modal: Показывать диалог Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ удалением + setting_delete_modal: Ð’Ñегда Ñпрашивать перед удалении поÑта setting_display_media: Отображение медиафайлов setting_display_media_default: По умолчанию setting_display_media_hide_all: Скрывать вÑе @@ -108,7 +113,10 @@ ru: setting_show_application: РаÑкрывать приложение, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ отправлÑÑŽÑ‚ÑÑ ÑтатуÑÑ‹ setting_system_font_ui: ИÑпользовать шрифт ÑиÑтемы по умолчанию setting_theme: Тема Ñайта - setting_unfollow_modal: Показывать диалог Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ тем, как отпиÑатьÑÑ Ð¾Ñ‚ аккаунта + setting_trends: Показывать ÑегоднÑшние тренды + setting_unfollow_modal: Ð’Ñегда Ñпрашивать перед отпиÑкой от аккаунта + setting_use_blurhash: Показать цветные градиенты Ð´Ð»Ñ Ñкрытых медиа + setting_use_pending_items: Медленный режим severity: СтрогоÑть type: Тип импорта username: Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ @@ -131,6 +139,11 @@ ru: pending_account: ОтправлÑть e-mail при наличии новых заÑвок на приÑоединение reblog: УведомлÑть по e-mail, когда кто-то продвинул ваш ÑÑ‚Ð°Ñ‚ÑƒÑ report: УведомлÑть по e-mail при Ñоздании жалобы + trending_tag: ОтправлÑть e-mail при непроверенных Ñ…Ñштегах в трендах + tag: + listable: Разрешить показ Ñ…Ñштега в поиÑке или в каталоге профилей + trendable: Разрешить показ Ñ…Ñштега в трендах + usable: Разрешить иÑпользовать Ñтот Ñ…Ñштег в поÑтах 'no': Ðет recommended: РекомендуетÑÑ required: diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml index 8470f793912267482982ee814e00735efe419693..b908c0196ce5ab3740cc0a9da327ee99d04a744c 100644 --- a/config/locales/simple_form.sk.yml +++ b/config/locales/simple_form.sk.yml @@ -5,6 +5,7 @@ sk: account_warning_preset: text: MôžeÅ¡ použÃvaÅ¥ rovnakú syntaxiu ako v rámci prÃspevkov, Äiže URL, haÅ¡tagy, a spomenutia admin_account_action: + include_statuses: UžÃvateľ uvidÃ, ktoré prÃspevky majú za následok moderaÄný zásah, alebo upozornenie send_email_notification: UžÃvateľ dostane vysvetlenie ohľadom toho, Äo sa stalo s ich úÄtom text_html: Voliteľné. MôžeÅ¡ použÃvaÅ¥ rovnakú syntaxiu ako v prÃspevkoch. MôžeÅ¡ pridaÅ¥ <a href="%{path}">varovné predlohy</a> a uÅ¡etriÅ¥ tak Äas type_html: Vyber si, Äo urobiÅ¥ s úÄtom <strong>%{acct}</strong> @@ -15,7 +16,6 @@ sk: bot: Tento úÄet vykonáva hlavne automatizované akcie, a je pravdepodobne nespravovaný context: Jedno, alebo viac kritériÃ, v ktorých má byÅ¥ filtrovanie uplatnené digest: Odoslané iba v prÃpade dlhodobej neprÃtomnosti, a len ak si obdržal/a nejaké osobné správy kým si bol/a preÄ - discoverable_html: Táto <a href="%{path}" target="_blank">databáza</a> umožňuje ľudom nájsÅ¥ profily podľa záujmu a aktÃvnosti. Vyžaduje aby mali aspoň %{min_followers} sledovateľov email: Bude ti odoslaný potvrdzujúci email fields: Až Å¡tyri položky môžeÅ¡ maÅ¥ na svojom profile zobrazené vo forme tabuľky header: PNG, GIF, alebo JPG. Maximálne %{size}. Bude zmenÅ¡ený na %{dimensions}px @@ -34,8 +34,11 @@ sk: setting_hide_network: Koho následujeÅ¡, a kto následuje teba, nebude zobrazené na tvojom profile setting_noindex: Ovplyvňuje verejný profil a stránky s prÃspevkami setting_show_application: Aplikácia, ktorú použÃvaÅ¡ na pÃsanie prÃspevkov, bude zobrazená v podrobnom náhľade jednotlivých tvojÃch prÃspevkov + setting_use_pending_items: Skry aktualizovanie Äasovej osi tak, aby bola naÄitávaná iba po kliknutÃ, namiesto samostatného posúvania username: Tvoja prezývka bude unikátna pre server %{domain} whole_word: Ak je kľúÄové slovo, alebo fráza poskladaná iba s pÃsmen a ÄÃsel, bude použité iba ak sa zhoduje s celým výrazom + domain_allow: + domain: Táto doména bude schopná zÃskavaÅ¥ dáta z tohto servera, a prichádzajúce dáta nÃm budú spracovávané a uložené featured_tag: name: 'Možno by si chcel/a použiÅ¥ niektoré z týchto:' imports: @@ -54,6 +57,7 @@ sk: account_warning_preset: text: Text predlohy admin_account_action: + include_statuses: Zahrnúť nahlásené prÃspevky v emaile send_email_notification: Oznam užÃvateľovi cez email text: Å pecifické varovanie type: Úkon @@ -101,14 +105,17 @@ sk: setting_display_media_default: Å tandard setting_display_media_hide_all: Ukry vÅ¡etky setting_display_media_show_all: Ukáž vÅ¡etky - setting_expand_spoilers: Stále rozbaľ prÃspevky oznaÄené varovanÃm o obsahu + setting_expand_spoilers: Stále rozbaľ prÃspevky oznaÄené varovanÃm o chúlostivom obsahu setting_hide_network: Ukry svoju sieÅ¥ kontaktov setting_noindex: NezaraÄuj prÃspevky do indexu pre vyhľadávÄe setting_reduce_motion: Mierni pohyb pri animáciách setting_show_application: Zverejni akú aplikáciu použÃvaÅ¡ na posielanie prÃspevkov setting_system_font_ui: Použi základné systémové pÃsmo setting_theme: Vzhľad webu + setting_trends: Ukáž dneÅ¡né trendy setting_unfollow_modal: Vyžaduj potvrdenie pred skonÄenÃm sledovania iného užÃvateľa + setting_use_blurhash: Ukáž farebné prechody pre skryté médiá + setting_use_pending_items: Pomalý režim severity: ZávažnosÅ¥ type: Typ importu username: Prezývka @@ -131,8 +138,15 @@ sk: pending_account: ZaslaÅ¥ email, ak treba prehodnotiÅ¥ nový úÄet reblog: ZaslaÅ¥ email, ak niekto re-tootne tvoj prÃspevok report: ZaslaÅ¥ email, ak niekto podá nové nahlásenie + trending_tag: PoÅ¡li email, ak sa neoverený haÅ¡tag stane populárnym + tag: + listable: Povoľ zobrazovanie tohto haÅ¡tagu v zozname profilov + name: HaÅ¡tag + trendable: Povoľ zobrazovanie tohto haÅ¡tagu medzi trendujúcimi + usable: Povoľ použÃvanie tohto haÅ¡tagu v prÃspevkoch 'no': Nie recommended: OdporúÄané required: + mark: "*" text: povinné 'yes': Ãno diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 2e0495551f4a0063b6659c5f0c6d4929671931df..771edf383794fa14ec4a9301a6a581509218a35e 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -2,6 +2,13 @@ sl: simple_form: hints: + account_warning_preset: + text: Lahko uporabite skladnjo tuta, kot so URL-ji, kljuÄniki in omembe + admin_account_action: + send_email_notification: Uporabnik bo prejel razlago, kaj se je zgodilo z njihovim raÄunom + text_html: Neobvezno. Lahko uporabite skladnjo tuta. <a href="%{path}">Prednastavite opozorila</a>, da prihranite Äas + type_html: Izberite, kaj boste storili z <strong>%{acct}</strong> + warning_preset_id: Neobvezno. Å e vedno lahko dodate besedilo po meri na konec prednastavitve defaults: autofollow: Osebe, ki se prijavijo prek povabila, vas bodo samodejno sledile avatar: PNG, GIF ali JPG. NajveÄ %{size}. ZmanjÅ¡ana bo na %{dimensions}px @@ -18,15 +25,26 @@ sl: password: Uporabite najmanj 8 znakov phrase: Se bo ujemal, ne glede na zaÄetnice v tekstu ali opozorilo o vsebini troba scopes: Do katerih API-jev bo imel program dostop. ÄŒe izberete obseg najviÅ¡je ravni, vam ni treba izbrati posameznih. + setting_aggregate_reblogs: Ne prikažite novih spodbud za tute, ki so bili nedavno spodbujeni (vpliva samo na novo prejete spodbude) + setting_default_sensitive: ObÄutljivi mediji so privzeto skriti in jih je mogoÄe razkriti s klikom setting_display_media_default: Skrij medij, ki je oznaÄen kot obÄutljiv setting_display_media_hide_all: Vedno skrij vse medije setting_display_media_show_all: Vedno pokaži medij, ki je oznaÄen kot obÄutljiv setting_hide_network: Kogar spremljate in kdo vas spremlja ne bo prikazano na vaÅ¡em profilu setting_noindex: Vpliva na vaÅ¡ javni profil in na strani s stanjem + setting_show_application: Aplikacija, ki jo uporabljate za tutanje, bo prikazana v podrobnem pogledu vaÅ¡ih tutov + setting_use_blurhash: Gradienti temeljijo na barvah skrite vizualne slike, vendar zakrivajo vse podrobnosti + setting_use_pending_items: Skrij posodobitev Äasovnice za klikom namesto samodejnega posodabljanja username: VaÅ¡e uporabniÅ¡ko ime bo edinstveno na %{domain} whole_word: Ko je kljuÄna beseda ali fraza samo alfanumeriÄna, se bo uporabljala le, Äe se bo ujemala s celotno besedo + domain_allow: + domain: Ta domena bo lahko prejela podatke s tega strežnika, dohodni podatki z nje pa bodo obdelani in shranjeni + featured_tag: + name: 'Morda boste želeli uporabiti eno od teh:' imports: data: Izvožena CSV datoteka iz drugega Mastodon vozliÅ¡Äa + invite_request: + text: To nam bo pomagalo pregledati vaÅ¡o prijavo sessions: otp: 'Vnesite dvomestno kodo, ki je ustvarjena z aplikacijo na telefonu, ali uporabite eno od vaÅ¡ih obnovitvenih kod:' user: @@ -36,6 +54,18 @@ sl: fields: name: Oznaka value: Vsebina + account_warning_preset: + text: Prednastavljeno besedilo + admin_account_action: + send_email_notification: Obvesti uporabnika po e-poÅ¡ti + text: Opozorilo po meri + type: Dejanje + types: + disable: OnemogoÄi + none: Ne naredi niÄesar + silence: UtiÅ¡aj + suspend: Suspendiraj in nepovratno izbriÅ¡i podatke raÄuna + warning_preset_id: Uporabi prednastavljeno opozorilo defaults: autofollow: Povabite, da sledi vaÅ¡emu raÄunu avatar: Podoba @@ -46,6 +76,7 @@ sl: context: Filtriraj vsebino current_password: Trenutno geslo data: Podatki + discoverable: Dodaj ta raÄun v imenik display_name: Prikazno ime email: E-poÅ¡tni naslov expires_in: PreteÄe po @@ -57,9 +88,12 @@ sl: locked: Zaklenjen raÄun max_uses: NajveÄje Å¡tevilo uporabnikov new_password: Novo geslo + note: Bio otp_attempt: Dvofaktorska koda password: Geslo phrase: KljuÄna beseda ali fraza + setting_advanced_layout: OmogoÄi napredni spletni vmesnik + setting_aggregate_reblogs: Skupinske spodbude na Äasovnicah setting_auto_play_gif: Samodejno predvajanje animiranih GIF-ov setting_boost_modal: Pred sunkom pokaži potrditveno okno setting_default_language: Jezik objavljanja @@ -74,27 +108,37 @@ sl: setting_hide_network: Skrij svoje omrežje setting_noindex: Odsotnost indeksiranja iskalnikov setting_reduce_motion: ZmanjÅ¡anje premikanja v animacijah + setting_show_application: Razkrij aplikacijo za poÅ¡iljanje tutov setting_system_font_ui: Uporabi privzeto pisavo sistema setting_theme: Tema strani setting_unfollow_modal: Pokaži potrditveno okno, preden nekoga prenehamo slediti + setting_use_blurhash: Pokaži barvite gradiente za skrite medije + setting_use_pending_items: PoÄasen naÄin severity: Strogost type: Vrsta uvoza username: UporabniÅ¡ko ime username_or_email: UporabniÅ¡ko ime ali E-poÅ¡ta whole_word: Celotna beseda + featured_tag: + name: KljuÄnik interactions: must_be_follower: Blokiraj obvestila nesledilcev must_be_following: Blokiraj obvestila oseb, ki jim ne sledite must_be_following_dm: Blokiraj neposredna sporoÄila oseb, ki jim ne sledite + invite_request: + text: Zakaj se želite pridružiti? notification_emails: digest: PoÅ¡lji izvleÄke e-poÅ¡t favourite: PoÅ¡lji e-poÅ¡to, ko nekdo doda vaÅ¡e stanje med priljubljene follow: PoÅ¡lji e-poÅ¡to, ko vas nekdo sledi follow_request: PoÅ¡lji e-poÅ¡to, ko vam nekdo želi slediti mention: PoÅ¡lji e-poÅ¡to, ko vas nekdo omeni + pending_account: PoÅ¡lji e-poÅ¡to, ko je potreben pregled novega raÄuna reblog: PoÅ¡lji e-poÅ¡to, ko nekdo sune vaÅ¡e stanje report: PoÅ¡lji e-poÅ¡to, ko je oddana nova prijava 'no': Ne + recommended: PriporoÄeno required: + mark: "*" text: zahtevano 'yes': Da diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml index 04ef12c9a79347bf1ba130fa9a5300ec5af8c3d1..b365bdda86e3046baa5d29e7c4423366fe26d21c 100644 --- a/config/locales/simple_form.sq.yml +++ b/config/locales/simple_form.sq.yml @@ -15,7 +15,6 @@ sq: bot: Kjo llogari kryesisht bën veprime të automatizuara dhe mund të mos mbikëqyret dot context: Një ose disa kontekste kur duhet të zbatohet filtri digest: I dërguar vetëm pas një periudhe të gjatë pasiviteti dhe vetëm nëse keni marrë ndonjë mesazh personal gjatë mungesës suaj - discoverable_html: <a href="%{path}" target="_blank">Drejtoria</a> u lejon njerëzve të gjejnë llogari bazuar në interesat dhe veprimtarinë. Lyp të paktën %{min_followers} ndjekës email: Do t’ju dërgohet një email ripohimi fields: Te profili juaj mund të keni deri në 4 objekte të shfaqur si tabelë header: PNG, GIF ose JPG. E shumta %{size}. Do të ripërmasohet në %{dimensions}px diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml index a097be5dd3e2c0f227e21c01e2693fad15054de5..6901b842c1318e3a2c8bfc11f63a578db13828d8 100644 --- a/config/locales/simple_form.sr.yml +++ b/config/locales/simple_form.sr.yml @@ -15,7 +15,6 @@ sr: bot: Овај налог углавном врши аутоматизоване радње и можда Ñе не надгледа context: Један или више контекÑта у којима треба да Ñе примени филтер digest: ПоÑлато поÑле дужег периода неактивноÑти Ñа прегледом Ñвих битних Ñтвари које Ñте добили док Ñте били одÑутни - discoverable_html: <a href="%{path}" target="_blank">Директоријум</a> омогућава људима да пронађу налоге заÑноване на интереÑима и активноÑти. Захтева бар %{min_followers} пратиоца email: Биће вам поÑлата е-пошта Ñа потврдом fields: Можете имати до 4 Ñтавке приказане као табела на вашем профилу header: PNG, GIF или JPG. Ðајвише %{size}. Биће Ñмањена на %{dimensions}px diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 171714ab081562b5ab25f73fc0c3c02322bfbeb6..56bdcf7681e09ba35e1555ce7d31f8b9b6cf017c 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -2,11 +2,18 @@ sv: simple_form: hints: + account_alias: + acct: Ange användarnamn@domän för kontot du flyttar frÃ¥n + account_migration: + acct: Ange användarnamn@domän för kontot du flyttar till account_warning_preset: text: Du kan använda inläggssyntax som webbadresser, hashtaggar och omnämnanden admin_account_action: + include_statuses: Användaren ser de toots som orsakat moderering eller varning send_email_notification: Användaren kommer att fÃ¥ en förklaring av vad som hände med sitt konto + text_html: Extra. Du kan använda toot syntax. Du kan <a href="%{path}">lägga till förvalda varningar</a> för att spara tid type_html: Välj vad du vill göra med <strong>%{acct}</strong> + warning_preset_id: Extra. Du kan lägga till valfri text i slutet av förinställningen defaults: autofollow: Användarkonton som skapas genom din inbjudan kommer automatiskt följa dig avatar: PNG, GIF eller JPG. Högst %{size}. Kommer att skalas ner till %{dimensions}px diff --git a/config/locales/simple_form.th.yml b/config/locales/simple_form.th.yml index 9d6b75ed0d7cf3cde54e8125b5cafa657263f0bb..500ff7f766674f966fefaa20e5eb24b56dc5c998 100644 --- a/config/locales/simple_form.th.yml +++ b/config/locales/simple_form.th.yml @@ -15,7 +15,6 @@ th: bot: บัà¸à¸Šà¸µà¸™à¸µà¹‰à¸—ำà¸à¸²à¸£à¸à¸£à¸°à¸—ำà¸à¸±à¸•โนมัติเป็นหลัà¸à¹à¸¥à¸°à¸à¸²à¸ˆà¹„ม่ได้รับà¸à¸²à¸£à¸ªà¸±à¸‡à¹€à¸à¸•à¸à¸²à¸£à¸“์ context: บริบทจำนวนหนึ่งหรืà¸à¸¡à¸²à¸à¸à¸§à¹ˆà¸²à¸—ี่ตัวà¸à¸£à¸à¸‡à¸„วรใช้ digest: ส่งเฉพาะหลังจาà¸à¹„ม่มีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¹€à¸›à¹‡à¸™à¹€à¸§à¸¥à¸²à¸™à¸²à¸™à¹à¸¥à¸°à¹ƒà¸™à¸à¸£à¸“ีที่คุณได้รับข้à¸à¸„วามส่วนบุคคลใด ๆ เมื่à¸à¸„ุณไม่à¸à¸¢à¸¹à¹ˆà¹€à¸—่านั้น - discoverable_html: <a href="%{path}" target="_blank">ไดเรà¸à¸—à¸à¸£à¸µ</a> ช่วยให้ผู้คนค้นหาบัà¸à¸Šà¸µà¸•ามความสนใจà¹à¸¥à¸°à¸à¸´à¸ˆà¸à¸£à¸£à¸¡ ต้à¸à¸‡à¸à¸²à¸£à¸à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸à¸¢ %{min_followers} ผู้ติดตาม email: คุณจะได้รับà¸à¸µà¹€à¸¡à¸¥à¸¢à¸·à¸™à¸¢à¸±à¸™ fields: คุณสามารถมีได้มาà¸à¸–ึง 4 รายà¸à¸²à¸£à¹à¸ªà¸”งเป็นตารางในโปรไฟล์ขà¸à¸‡à¸„ุณ header: PNG, GIF หรืภJPG สูงสุด %{size} จะถูà¸à¸¢à¹ˆà¸à¸‚นาดเป็น %{dimensions}px @@ -34,10 +33,14 @@ th: setting_hide_network: จะไม่à¹à¸ªà¸”งผู้ที่คุณติดตามà¹à¸¥à¸°à¸œà¸¹à¹‰à¸—ี่ติดตามคุณในโปรไฟล์ขà¸à¸‡à¸„ุณ setting_noindex: มีผลต่à¸à¹‚ปรไฟล์สาธารณะà¹à¸¥à¸°à¸«à¸™à¹‰à¸²à¸ªà¸–านะขà¸à¸‡à¸„ุณ setting_show_application: จะà¹à¸ªà¸”งà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่คุณใช้เพื่à¸à¹‚พสต์ในมุมมà¸à¸‡à¹‚ดยละเà¸à¸µà¸¢à¸”ขà¸à¸‡à¹‚พสต์ขà¸à¸‡à¸„ุณ + setting_use_blurhash: à¸à¸²à¸£à¹„ล่ระดับสีà¸à¸´à¸‡à¸•ามสีขà¸à¸‡à¸ าพที่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆà¹à¸•่ทำให้รายละเà¸à¸µà¸¢à¸”ใด ๆ คลุมเครืภ+ setting_use_pending_items: ซ่à¸à¸™à¸à¸²à¸£à¸à¸±à¸›à¹€à¸”ตเส้นเวลาไว้หลังà¸à¸²à¸£à¸„ลิà¸à¹à¸—นที่จะเลื่à¸à¸™à¸Ÿà¸µà¸”โดยà¸à¸±à¸•โนมัติ username: ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸‚à¸à¸‡à¸„ุณจะไม่ซ้ำà¸à¸±à¸™à¸šà¸™ %{domain} whole_word: เมื่à¸à¸„ำสำคัà¸à¸«à¸£à¸·à¸à¸§à¸¥à¸µà¸¡à¸µà¹à¸„่ตัวà¸à¸±à¸à¸©à¸£à¹à¸¥à¸°à¸•ัวเลข จะถูà¸à¹ƒà¸Šà¹‰à¸«à¸²à¸à¸•รงà¸à¸±à¸™à¸—ั้งคำเท่านั้น featured_tag: name: 'คุณà¸à¸²à¸ˆà¸•้à¸à¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸«à¸™à¸¶à¹ˆà¸‡à¹ƒà¸™à¸™à¸µà¹‰:' + form_challenge: + current_password: คุณà¸à¸³à¸¥à¸±à¸‡à¹€à¸‚้าสู่พื้นที่ปลà¸à¸”ภัย imports: data: ไฟล์ CSV ที่ส่งà¸à¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ Mastodon à¸à¸·à¹ˆà¸™ invite_request: @@ -108,7 +111,10 @@ th: setting_show_application: เปิดเผยà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่ใช้ในà¸à¸²à¸£à¸ªà¹ˆà¸‡à¹‚พสต์ setting_system_font_ui: ใช้à¹à¸šà¸šà¸à¸±à¸à¸©à¸£à¹€à¸£à¸´à¹ˆà¸¡à¸•้นขà¸à¸‡à¸£à¸°à¸šà¸š setting_theme: ชุดรูปà¹à¸šà¸šà¹„ซต์ + setting_trends: à¹à¸ªà¸”งà¹à¸™à¸§à¹‚น้มขà¸à¸‡à¸§à¸±à¸™à¸™à¸µà¹‰ setting_unfollow_modal: à¹à¸ªà¸”งà¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸à¹ˆà¸à¸™à¹€à¸¥à¸´à¸à¸•ิดตามใครสัà¸à¸„น + setting_use_blurhash: à¹à¸ªà¸”งà¸à¸²à¸£à¹„ล่ระดับสีที่มีสีสันสำหรับสื่à¸à¸—ี่ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆ + setting_use_pending_items: โหมดช้า severity: ความรุนà¹à¸£à¸‡ type: ชนิดà¸à¸²à¸£à¸™à¸³à¹€à¸‚้า username: ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ @@ -120,6 +126,8 @@ th: must_be_follower: ปิดà¸à¸±à¹‰à¸™à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¸—ี่ไม่ใช่ผู้ติดตาม must_be_following: ปิดà¸à¸±à¹‰à¸™à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸ˆà¸²à¸à¸œà¸¹à¹‰à¸„นที่คุณไม่ได้ติดตาม must_be_following_dm: ปิดà¸à¸±à¹‰à¸™à¸‚้à¸à¸„วามโดยตรงจาà¸à¸œà¸¹à¹‰à¸„นที่คุณไม่ได้ติดตาม + invite: + comment: ความคิดเห็น invite_request: text: ทำไมคุณจึงต้à¸à¸‡à¸à¸²à¸£à¹€à¸‚้าร่วม? notification_emails: @@ -131,6 +139,9 @@ th: pending_account: ส่งà¸à¸µà¹€à¸¡à¸¥à¹€à¸¡à¸·à¹ˆà¸à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆà¸•้à¸à¸‡à¸à¸²à¸£à¸à¸²à¸£à¸•รวจทาน reblog: ส่งà¸à¸µà¹€à¸¡à¸¥à¹€à¸¡à¸·à¹ˆà¸à¹ƒà¸„รสัà¸à¸„นดันสถานะขà¸à¸‡à¸„ุณ report: ส่งà¸à¸µà¹€à¸¡à¸¥à¹€à¸¡à¸·à¹ˆà¸à¸¡à¸µà¸à¸²à¸£à¸ªà¹ˆà¸‡à¸£à¸²à¸¢à¸‡à¸²à¸™à¹ƒà¸«à¸¡à¹ˆ + tag: + name: à¹à¸®à¸Šà¹à¸—็ภ+ usable: à¸à¸™à¸¸à¸à¸²à¸•ให้โพสต์ใช้à¹à¸®à¸Šà¹à¸—็à¸à¸™à¸µà¹‰ 'no': ไม่ recommended: à¹à¸™à¸°à¸™à¸³ required: diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 68b4c24c974fc753ca23db3900c645adcced9daa..663fce5e0a58555c8913df32fd88adc92aaa415b 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -2,51 +2,169 @@ tr: simple_form: hints: + account_alias: + acct: Taşımak istediÄŸiniz hesabın kullanıcı-adı@alan-adını belirtin + account_migration: + acct: Taşınmak istediÄŸiniz hesabın kullanıcı-adı@alan-adını belirtin + account_warning_preset: + text: URL'ler, etiketler ve bahsetmeler gibi gönderi sözdizimleri kullanabilirsiniz admin_account_action: + include_statuses: Kullanıcı hangi gönderilerin denetleme eylemi ya da uyarısına neden olduÄŸunu görecektir send_email_notification: Kullanıcı, hesabına ne olduÄŸu hakkında bir bildirim alacak + text_html: İsteÄŸe baÄŸlı. Gönderi sözdizimleri kullanabilirsiniz. Zamandan kazanmak için <a href="%{path}">uyarı ön-ayarları ekleyebilirsiniz</a> + type_html: "<strong>%{acct}</strong> ile ne yapılacağını seçin" warning_preset_id: İsteÄŸe baÄŸlı. Hazır ayarın sonuna hala özel metin ekleyebilirsiniz defaults: autofollow: Davetiyeyle kaydolan kiÅŸiler sizi otomatik olarak takip eder avatar: En fazla %{size} olacak ÅŸekilde PNG, GIF veya JPG formatında yükleyiniz. %{dimensions}px büyüklüğüne indirgenecektir + bot: Bu hesap temelde otomatik eylemler gerçekleÅŸtirir ve izlenmeyebilir + context: Filtrenin geçerli olması gereken bir veya daha fazla içerik + current_password: Güvenlik nedeniyle lütfen ÅŸu anki hesabın parolasını girin + current_username: Onaylamak için lütfen ÅŸu anki hesabın kullanıcı adını girin + digest: Yalnızca uzun süre kullanılmadığında ve yalnızca yokluÄŸunda kiÅŸisel mesajlar aldıysanız gönderilir + discoverable: Profil dizini, hesabınızın daha geniÅŸ bir kitleye ulaÅŸmasının baÅŸka bir yoludur + email: Onay e-postası gönderilecek + fields: Profilinizde tablo olarak görüntülenen en fazla 4 ögeye sahip olabilirsiniz header: En fazla %{size} olacak ÅŸekilde PNG, GIF veya JPG formatında yükleyiniz. %{dimensions}px büyüklüğüne indirgenecektir. + inbox_url: Kullanmak istediÄŸiniz aktarıcının ön sayfasından URL'yi kopyalayın + irreversible: Filtre uygulanmış gönderiler, filtre daha sonra çıkartılsa bile geri dönüşümsüz biçimde kaybolur + locale: Kullanıcı arayüzünün dili, e-postalar ve push bildirimleri locked: Takipçilerinizi manuel olarak kabul etmenizi ve gönderilerinizi varsayılan olarak sadece takipçilerinizin göreceÄŸi ÅŸekilde paylaÅŸmanızı saÄŸlar. + password: En az 8 karakter kullanın + phrase: Metnin büyük/küçük harf durumundan veya gönderinin içerik uyarısından bağımsız olarak eÅŸleÅŸtirilecek + scopes: Uygulamanın eriÅŸmesine izin verilen API'ler. Üst seviye bir kapsam seçtiyseniz, bireysel kapsam seçmenize gerek yoktur. + setting_aggregate_reblogs: Yakın zamanda yinelenmiÅŸ gönderiler için yeni yinelemeler gösterme (yalnızca yeni alınan yinelemeleri etkiler) + setting_default_sensitive: Hassas medya varsayılan olarak gizlenir ve bir tıklama ile görüntülenebilir + setting_display_media_default: Hassas olarak iÅŸaretlenmiÅŸ medyayı gizle + setting_display_media_hide_all: Tüm medyayı gizle + setting_display_media_show_all: Hassas olarak iÅŸaretlenmiÅŸ medyayı göster + setting_hide_network: Takip edilenler ve takipçiler profilinizde gösterilmeyecek + setting_noindex: Herkese açık profilinizi ve durum sayfalarınızı etkiler + setting_show_application: Gönderi için kullandığınız uygulama, gönderilerinizin detaylı görünümünde gösterilecektir + setting_use_blurhash: Gradyenler gizli görsellerin renklerine dayanır, ancak detayları gizler + setting_use_pending_items: Zaman çizelgesi güncellemelerini, akışı otomatik olarak kaydırmak yerine bir tıklamanın arkasına gizleyin + username: Kullanıcı adınız %{domain} alanında benzersiz olacak + whole_word: Anahtar kelime veya kelime öbeÄŸi yalnızca alfasayısal olduÄŸunda, yalnızca tüm sözcükle eÅŸleÅŸirse uygulanır + domain_allow: + domain: Bu alan adı, bu sunucudan veri alabilecek ve ondan gelen veri iÅŸlenecek ve saklanacaktır + featured_tag: + name: 'Bunlardan birini kullanmak isteyebilirsiniz:' + form_challenge: + current_password: Güvenli bir bölgeye giriyorsunuz imports: data: DiÄŸer Mastodon sunucusundan dışarı aktardığınız CSV dosyası + invite_request: + text: Bu, baÅŸvurunuzu gözden geçirmemize yardımcı olacaktır sessions: otp: Telefonunuzdaki two-factor kodunuzu giriniz veya kurtarma kodlarınızdan birini giriniz. + tag: + name: Harflerin, örneÄŸin daha okunabilir yapmak için, sadece büyük/küçük harf durumlarını deÄŸiÅŸtirebilirsiniz + user: + chosen_languages: İşaretlendiÄŸinde, yalnızca seçilen dillerdeki karakterler genel zaman çizelgelerinde görüntülenir labels: + account: + fields: + name: Etiket + value: İçerik + account_alias: + acct: Eski hesabın tanıtıcısı + account_migration: + acct: Yeni hesabın tanıtıcısı + account_warning_preset: + text: Ön-ayar metni + admin_account_action: + include_statuses: Birdirilen gönderileri e-postaya dahil et + send_email_notification: E-postayla kullanıcıyı bilgilendir + text: Özel uyarı + type: Eylem + types: + disable: Devre dışı + none: Hiç birÅŸey + silence: Sessiz + suspend: Hesap verilerini askıya alın ve geri alınamaz ÅŸekilde silin + warning_preset_id: Bir uyarı ön-ayarı kullan defaults: + autofollow: Hesabınızı takip etmeye davet edin avatar: Profil resmi + bot: Bu bir bot hesabı + chosen_languages: Dilleri filtrele confirm_new_password: Yeni parolanız (tekrar) confirm_password: Parolanız (tekrar) + context: İçeriÄŸi filtrele current_password: Mevcut parolanız data: Dosya + discoverable: Bu hesabı dizinde listele display_name: Görünen adınız email: E-posta adresiniz + expires_in: BitiÅŸ tarihi + fields: Profil Metaverisi header: Kapak resmi + inbox_url: Aktarıcı gelen kutusunun URL'si + irreversible: Gizlemek yerine bırak locale: Dil locked: Hesabımı kilitle + max_uses: Maksimum kullanım sayısı new_password: Yeni parolanız note: KiÅŸisel bilgiler otp_attempt: İki-faktörlü kod password: Parolanız + phrase: Anahtar kelime veya kelime öbeÄŸi + setting_advanced_layout: GeliÅŸmiÅŸ web arayüzünü etkinleÅŸtir + setting_aggregate_reblogs: Zaman çizelgesindeki grup yinelemeleri setting_auto_play_gif: GIF'leri otomatik oynatt setting_boost_modal: Boost etmeden önce onay diyaloÄŸu göster + setting_default_language: Gönderi dili setting_default_privacy: Gönderi gizliliÄŸi + setting_default_sensitive: Her zaman hassas medya olarak iÅŸaretle + setting_delete_modal: Bir gönderiyi silmeden önce onay iletiÅŸim kutusunu göster + setting_display_media: Medya görünümü + setting_display_media_default: Varsayılan + setting_display_media_hide_all: Tümünü gizle + setting_display_media_show_all: Tümünü göster + setting_expand_spoilers: İçerik uyarılarıyla iÅŸaretli gönderileri her zaman geniÅŸlet + setting_hide_network: Ağını gizle + setting_noindex: Arama motoru endekslemesini iptal et + setting_reduce_motion: Animasyonlarda hareketi azalt + setting_show_application: İçerik göndermek için kullanılan uygulamayı belirt + setting_system_font_ui: Sistemin varsayılan yazı tipini kullan + setting_theme: Site teması + setting_trends: Bugünün trendlerini göster + setting_unfollow_modal: Birini takip etmeden önce onay iletiÅŸim kutusunu göster + setting_use_blurhash: Gizli ortamlar için renkli gradyen göster + setting_use_pending_items: YavaÅŸ mod severity: Zorluk type: Dosya türü username: Kullanıcı adınız + username_or_email: Kullanıcı adı ya da email + whole_word: Tüm dünya + featured_tag: + name: Hashtag interactions: must_be_follower: Takipçim olmayan kiÅŸilerden gelen bildirimleri engelle must_be_following: Takip etmediÄŸim kiÅŸilerden gelen bildirimleri engelle + must_be_following_dm: Takip etmediÄŸiniz kiÅŸilerin doÄŸrudan ileti göndermesini engelle + invite: + comment: Yorum + invite_request: + text: Neden katılmak istiyorsun? notification_emails: digest: Özet e-postaları gönder favourite: Biri durumumu favorilerine eklediginde bana e-posta gönder follow: Biri beni takip ettiÄŸinde bana e-posta gönder follow_request: Biri bana takip isteÄŸi gönderdiÄŸinde, bana e-posta gönder mention: Biri benden bahsettiÄŸinde, bana e-posta gönder + pending_account: Yeni bir hesap incelemesi gerektiÄŸinde e-posta gönder reblog: Biri durumumu paylaÅŸtığında, bana e-posta gönder + report: Yeni bir rapor gönderildiÄŸinde e-posta gönder + trending_tag: İncelenmemiÅŸ bir hashtag trend olduÄŸunda e-posta gönder + tag: + listable: Bu etiketin aramalarda ve profil dizininde görünmesine izin ver + name: Hashtag + trendable: Bu etiketin trendlerin altında görünmesine izin ver + usable: Gönderilerin bu etiketi kullanmasına izin ver 'no': Hayır + recommended: Önerilen required: + mark: "*" text: gerekli 'yes': Evet diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml index 35b20d8a98a6444d42222afad648f998806f0d50..a6ae14f6deb3ef39356f7785007714c53d7bfa48 100644 --- a/config/locales/simple_form.uk.yml +++ b/config/locales/simple_form.uk.yml @@ -2,48 +2,152 @@ uk: simple_form: hints: + account_warning_preset: + text: Ви можете викориÑтовувати ÑинтакÑÐ¸Ñ Ð´Ð¼ÑƒÑ…Ñ–Ð², наприклад URLи, хештеґи та згадки + admin_account_action: + include_statuses: КориÑтувач побачить, Ñкі дмухи призвели до адмініÑтративних дій або попереджень + send_email_notification: КориÑтувач отримає роз'ÑÑненнÑ, що ÑталоÑÑ Ð· його обліковим запиÑом + text_html: Ðеобов'Ñзково. Ви можете викориÑтовувати ÑÐ¸Ð½Ñ‚Ð°ÐºÑ Ð´Ð¼ÑƒÑ…Ñ–Ð². Ви можете <a href="%{path}">додати шаблони попереджень</a>, щоб заощадити Ñ‡Ð°Ñ + type_html: Оберіть, що робити з <strong>%{acct}</strong> + warning_preset_id: Ðеобов'Ñзково. Ви можете ще додати будь-Ñкий текÑÑ‚ до ÐºÑ–Ð½Ñ†Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñƒ defaults: + autofollow: Люди, що зареєÑтрувалиÑÑ Ð·Ð° вашим запрошеннÑм, автоматично підпишутьÑÑ Ð½Ð° Ð²Ð°Ñ avatar: PNG, GIF, або JPG. МакÑимум - %{size}. Буде зменшено до %{dimensions}px bot: Цей аккаунт в оÑновному виконує автоматичні дії та може не відÑтежуватіÑÑ + digest: Буде поÑлано тільки піÑÐ»Ñ Ð´Ð¾Ð²Ð³Ð¾Ð³Ð¾ періоду неактивноÑті, та тільки Ñкщо ви отримаєте перÑональне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñƒ цей період + discoverable: Ще один шлÑÑ…, за Ñким про Ð²Ð°Ñ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ дізнатиÑÑ ÐºÐ¾Ñ€Ð¸Ñтувачі — каталог профілів + email: Вам надійде електронний лиÑÑ‚ з підтвердженнÑм + fields: До 4 елементів може бути відображено Ñк Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ñƒ вашому профілі header: PNG, GIF, або JPG. МакÑимум - %{size}. Буде зменшено до %{dimensions}px + inbox_url: Скопіюйте інтернет-адреÑу з титульної Ñторінки ретранÑлÑтора + irreversible: ВідÑÑ–Ñні дмухи зникнуть назавжди, навіть Ñкщо фільтр потім буде знÑто + locale: Мова інтерфейÑу, електронних лиÑтів та push-Ñповіщень locked: Буде вимагати від Ð’Ð°Ñ ÑамоÑтійного Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників, змінить приватніÑть поÑтів за замовчуваннÑм на "тільки Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників" + password: Ðе менше 8 Ñимволів + phrase: Шукає без Ð²Ñ€Ð°Ñ…ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ³Ñ–Ñтру у текÑті дмуха або у його попередженні про вміÑÑ‚ + setting_aggregate_reblogs: Ðе показувати передмухи Ð´Ð»Ñ Ð´Ð¼ÑƒÑ…Ñ–Ð², Ñкі нещодавно вже були передмухнуті (не вплине на вже отримані передмухи) + setting_default_sensitive: Дражливі медіа приховані за промовчаннÑм та можуть бути розкрити кліком + setting_display_media_default: Приховувати медіа, помічені Ñк дражливі + setting_display_media_hide_all: Приховувати будь-Ñкі медіа + setting_display_media_show_all: Завжди відображати медіа, помічені Ñк дражливі + setting_hide_network: У вашому профілі не буде відображено підпиÑки та підпиÑників + setting_noindex: Впливає на ваш публічний профіль та Ñторінки ÑтатуÑу + setting_show_application: ЗаÑтоÑунок, за допомогою Ñкого ви дмухнули, буде відображено Ñеред деталей дмуху + setting_use_blurhash: Градієнти, що базуютьÑÑ Ð½Ð° кольорах прихованих медіа, але роблÑть нерозрізненними будь-Ñкі деталі + setting_use_pending_items: Ðе додавати нові Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð¾ Ñтрічок миттєво. Показувати Ñ—Ñ… тільки піÑÐ»Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ð³Ð¾ клацаннÑ. + username: Ваше ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача буде унікальним у %{domain} + whole_word: Якщо пошукове Ñлово або фраза міÑтить тільки літери та цифри, воно має Ñпівпадати цілком + domain_allow: + domain: Цей домен зможе отримувати дані з цього Ñерверу. Вхідні дані будуть оброблені та збережені + featured_tag: + name: 'Можливо, ви захочете викориÑтовувати один з цих:' imports: - data: Файл CSV, ÑкÑпортированный Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð³Ð¾ узла Mastodon + data: Файл CSV, екÑпортований з іншого Ñервера Mastodon + invite_request: + text: Це допоможе нам розглÑнути вашу заÑву sessions: otp: Введите код двухфакторной аутентификации или иÑпользуйте один из Ваших кодов воÑÑтановлениÑ. + tag: + name: Тут ви можете лише змінювати регіÑтр літер, щоб підвищити читабельніÑть + user: + chosen_languages: У глобальних Ñтрічках будуть відображатиÑÑ Ð´Ð¼ÑƒÑ…Ð¸ тільки обраними мовами labels: + account: + fields: + name: Позначка + value: ВміÑÑ‚ + account_warning_preset: + text: ТекÑÑ‚ шаблону + admin_account_action: + include_statuses: Додати дмухи, на Ñкі ви ÑкаржитеÑÑŒ, до електронного лиÑта + send_email_notification: СповіÑтити кориÑтувача електронною поштою + text: КориÑтувацьке Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ + type: Ð”Ñ–Ñ + types: + disable: Вимкнути + none: Ðічого не робити + silence: Ð“Ð»ÑƒÑˆÐµÐ½Ð½Ñ + suspend: Призупинити та незворотньо видалити дані облікового запиÑу + warning_preset_id: ВикориÑтати шаблон Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ defaults: + autofollow: ЗапроÑити Ñлідкувати за вашим обліковим запиÑом avatar: Ðватар + bot: Це обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±Ð¾Ñ‚Ð° + chosen_languages: Фільтрувати мови confirm_new_password: Підтвердіть новий пароль confirm_password: Підтвердіть пароль + context: Фільтр контекÑту current_password: Поточний пароль data: Дані + discoverable: Оприлюднити обліковий Ð·Ð°Ð¿Ð¸Ñ Ñƒ каталозі display_name: Ім'Ñ email: Email адреÑа + expires_in: ЗакінчуєтьÑÑ Ð¿Ñ–ÑÐ»Ñ + fields: Метадані Ð¿Ñ€Ð¾Ñ„Ñ–Ð»Ñ header: Заголовок + inbox_url: URL поштової Ñкриньки ретранÑлÑтора + irreversible: Видалити назавжди, а не проÑто Ñховати locale: Мова locked: Зробити акаунт приватним + max_uses: МакÑимальна кількіÑть викориÑтань new_password: Ðовий пароль note: Про Ð’Ð°Ñ otp_attempt: Двофакторний код password: Пароль + phrase: Ключове Ñлово або фраза + setting_advanced_layout: Увімкнути розширений web-Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ setting_auto_play_gif: Ðвтоматично відтворювати анімовані GIF - setting_boost_modal: Показывать диалог Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ продвижением - setting_default_privacy: ВидимоÑть поÑтов - severity: СтрогоÑть - type: Тип импорта - username: Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ + setting_boost_modal: Відображати діалог Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ´Ð¼ÑƒÑ…ÑƒÐ²Ð°Ð½Ð½Ñ + setting_default_language: Мова дмухів + setting_default_privacy: ВидиміÑть поÑтів + setting_default_sensitive: Позначити медіа Ñк дражливе + setting_delete_modal: Показувати діалог Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´Ð¼ÑƒÑ…Ñƒ + setting_display_media: Ð’Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¼ÐµÐ´Ñ–Ð° + setting_display_media_default: За промовчаннÑм + setting_display_media_hide_all: Сховати вÑÑ– + setting_display_media_show_all: Показати вÑÑ– + setting_expand_spoilers: Завжди розгортати дмухи з попередженнÑми про вміÑÑ‚ + setting_hide_network: Сховати вашу мережу + setting_noindex: ВідмовитиÑÑ Ð²Ñ–Ð´ індекÑÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÐ¾Ð²Ð¸Ð¼Ð¸ ÑиÑтемами + setting_reduce_motion: Менше руху в анімаціÑÑ… + setting_show_application: Відображати заÑтоÑунки, викориÑтані Ð´Ð»Ñ Ð´Ð¼ÑƒÑ…Ð°Ð½Ð½Ñ + setting_system_font_ui: ВикориÑтовувати типовий ÑиÑтемний шрифт + setting_theme: Тема Ñайту + setting_trends: Показати Ñьогоднішні тренди + setting_unfollow_modal: Відображати діалог Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð²Ñ–Ð´Ð¿Ð¸Ñки від когоÑÑŒ + setting_use_blurhash: Відображати барвиÑті градієнти заміÑть прихованих медіа + setting_use_pending_items: Повільний режим + severity: СерйозніÑть + type: Тип імпорту + username: Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача + username_or_email: Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або електронна пошта + whole_word: Ціле Ñлово + featured_tag: + name: Хештеґ interactions: must_be_follower: Блокувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ непідпиÑаних людей must_be_following: Блокувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ людей, на Ñких ви не підпиÑані + must_be_following_dm: Заблокувати прÑмі ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ людей, на Ñких ви не підпиÑані + invite_request: + text: Чому ви хочете приєднатиÑÑ? notification_emails: - digest: ПриÑылать дайджеÑÑ‚ по e-mail + digest: ÐадÑилати дайджеÑÑ‚ електронною поштою favourite: ÐадÑилати лиÑта, коли комуÑÑŒ подобаєтьÑÑ Ð’Ð°Ñˆ ÑÑ‚Ð°Ñ‚ÑƒÑ follow: ÐадÑилати лиÑта, коли хтоÑÑŒ підпиÑуєтьÑÑ Ð½Ð° Ð’Ð°Ñ follow_request: ÐадÑилати лиÑта, коли хтоÑÑŒ запитує дозволу на підпиÑку mention: ÐадÑилати лиÑта, коли хтоÑÑŒ згадує Ð’Ð°Ñ + pending_account: ÐадÑилати електронного лиÑта, коли новий обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” розглÑду reblog: ÐадÑилати лиÑта, коли хтоÑÑŒ передмухує Ваш ÑÑ‚Ð°Ñ‚ÑƒÑ + report: ÐадÑилати електронного лиÑта, коли з'ÑвлÑєтьÑÑ Ð½Ð¾Ð²Ð° Ñкарга + trending_tag: ÐадÑилати електронного лиÑта, коли нерозглÑнутий хештеґ Ñтає популÑрним + tag: + listable: Дозволити поÑву цього хештеґа у каталозі профілів + name: Хештеґ + trendable: Дозволити поÑву цього хештеґа у ÑпиÑку популÑрних хештеґів + usable: Дозволити дмухам викориÑтовувати цей хештеґ 'no': ÐÑ– + recommended: Рекомендовано required: + mark: "*" text: обов'Ñзкове 'yes': Так diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index b781deb32b154664df3213152e04e72b24b0474a..9bff15d1d7b9abe062565b43bb809f19bba74afa 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -15,7 +15,6 @@ zh-CN: bot: æ¥è‡ªè¿™ä¸ªå¸æˆ·çš„ç»å¤§å¤šæ•°æ“作都是自动进行的,并且å¯èƒ½æ— 人监控 context: 过滤器的应用场景 digest: ä»…åœ¨ä½ é•¿æ—¶é—´æœªç™»å½•ï¼Œä¸”æ”¶åˆ°äº†ç§ä¿¡æ—¶å‘é€ - discoverable_html: <a href="%{path}" target="_blank">目录</a> è®©å¤§å®¶èƒ½æ ¹æ®å…´è¶£å’Œæ´»åŠ¨å¯»æ‰¾ç”¨æˆ·ã€‚éœ€è¦è‡³å°‘ %{min_followers} ä½å…³æ³¨è€… email: 我们会å‘ä½ å‘é€ä¸€å°ç¡®è®¤é‚®ä»¶ fields: è¿™å°†ä¼šåœ¨ä¸ªäººèµ„æ–™é¡µä¸Šä»¥è¡¨æ ¼çš„å½¢å¼å±•示,最多 4 个项目 header: 文件大å°é™åˆ¶ %{size}ï¼Œåªæ”¯æŒ PNGã€GIF 或 JPG æ ¼å¼ã€‚图片分辨率将会压缩至 %{dimensions}px @@ -34,6 +33,7 @@ zh-CN: setting_hide_network: ä½ å…³æ³¨çš„äººå’Œå…³æ³¨ä½ çš„äººå°†ä¸ä¼šåœ¨ä½ 的个人资料页上展示 setting_noindex: æ¤è®¾ç½®ä¼šå½±å“åˆ°ä½ çš„å…¬å¼€ä¸ªäººèµ„æ–™ä»¥åŠå˜Ÿæ–‡é¡µé¢ setting_show_application: ä½ ç”¨æ¥å‘表嘟文的应用程åºå°†ä¼šåœ¨ä½ å˜Ÿæ–‡çš„è¯¦ç»†å†…å®¹ä¸æ˜¾ç¤º + setting_use_blurhash: æ¸å˜æ˜¯åŸºäºŽæ¨¡ç³ŠåŽçš„éšè—内容生æˆçš„ username: ä½ çš„ç”¨æˆ·å在 %{domain} 上是独特的 whole_word: 如果关键è¯åªåŒ…å«å—æ¯å’Œæ•°å—,就åªä¼šåœ¨æ•´ä¸ªè¯è¢«åŒ¹é…æ—¶æ‰ä¼šå¥—用 featured_tag: @@ -73,7 +73,7 @@ zh-CN: context: 过滤器场景 current_password: 当å‰å¯†ç data: æ•°æ®æ–‡ä»¶ - discoverable: 在本站用户资料目录ä¸åˆ—出æ¤è´¦æˆ· + discoverable: åœ¨æœ¬ç«™ç”¨æˆ·ç›®å½•ä¸æ”¶å½•æ¤è´¦æˆ· display_name: 昵称 email: 电åé‚®ä»¶åœ°å€ expires_in: 失效时间 @@ -109,6 +109,7 @@ zh-CN: setting_system_font_ui: 使用系统默认å—体 setting_theme: 站点主题 setting_unfollow_modal: åœ¨å–æ¶ˆå…³æ³¨å‰è¯¢é—®æˆ‘ + setting_use_blurhash: å°†éšè—媒体显示为彩色æ¸å˜ severity: 级别 type: 导入数æ®ç±»åž‹ username: 用户å @@ -131,6 +132,8 @@ zh-CN: pending_account: 在有账户需è¦å®¡æ ¸æ—¶ï¼Œå‘é€ç”µå邮件æé†’我 reblog: 当有用户转嘟了我的嘟文时,å‘é€ç”µå邮件æé†’我 report: 在æäº¤æ–°ä¸¾æŠ¥æ—¶ï¼Œå‘é€ç”µå邮件æé†’我 + tag: + listable: å…许这个è¯é¢˜æ ‡ç¾åœ¨ç”¨æˆ·ç›®å½•䏿˜¾ç¤º 'no': å¦ recommended: 推è required: diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index 4da117b61a2ba3baad683cd98dbe452cc404b208..fbbbff6ca2de7db468956b2ef9e0dcc43a484819 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -2,6 +2,10 @@ zh-TW: simple_form: hints: + account_alias: + acct: 指定欲移動之帳戶的 使用者å稱@ç«™å° + account_migration: + acct: 指定欲移動至之帳戶的 使用者å稱@ç«™å° account_warning_preset: text: 您å¯ä½¿ç”¨å˜Ÿæ–‡èªžæ³•,例如網å€ã€ã€Œ#ã€æ¨™ç±¤å’ŒæåŠåŠŸèƒ½ admin_account_action: @@ -15,7 +19,6 @@ zh-TW: bot: æ¤å¸³æˆ¶ä¸»è¦åŸ·è¡Œè‡ªå‹•æ“作且å¯èƒ½æœªè¢«ç›£æŽ§ context: æ‡‰è©²å¥—ç”¨éŽæ¿¾å™¨çš„ä¸€é …æˆ–å¤šé …å…§å®¹ digest: åƒ…åœ¨ä½ é•·æ™‚é–“æœªç™»å…¥ä¸”åœ¨æœªç™»å…¥æœŸé–“æ”¶åˆ°ç§è¨Šæ™‚å‚³é€ - discoverable_html: <a href="%{path}" target="_blank">目錄</a> 讓使用者們能基於興趣與活動尋找帳戶。需è¦è‡³å°‘ %{min_followers} ä½é—œæ³¨è€… email: 您將收到一å°ç¢ºèªé›»å郵件 fields: 您å¯åœ¨å€‹äººè³‡æ–™ä¸Šæœ‰è‡³å¤š 4 å€‹ä»¥è¡¨æ ¼å½¢å¼é¡¯ç¤ºçš„é …ç›® header: æ”¯æ´ PNG, GIF 或 JPG 圖片,檔案最大為 %{size}ï¼ŒæœƒæŒ‰æ¯”ä¾‹ç¸®å°æˆ %{dimensions} åƒç´ diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 75a43e322f6e345064c711626f886afc12738f9a..4975035fe2a7753ae98f6f87e69458a59f4723b8 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -11,21 +11,20 @@ sk: apps: Aplikácie apps_platforms: Uživaj Mastodon z iOSu, Androidu a iných platforiem browse_directory: Prehľadávaj databázu profilov, filtruj podľa záujmov - browse_public_posts: Prebádaj naživo prúd verejných prÃspevkov na Mastodone + browse_public_posts: Sleduj naživo prúd verejných prÃspevkov na Mastodone contact: Kontakt contact_missing: Nezadaný contact_unavailable: Neuvedený/á discover_users: Objavuj užÃvateľov documentation: Dokumentácia - extended_description_html: | - <h3>Pravidlá</h3> - <p>Žiadne zatiaľ uvedené nie sú</p> - federation_hint_html: S úÄtom na %{instance} budeÅ¡ môcÅ¥ následovaÅ¥ ľúdà na hociakom Mastodon serveri, ale aj inde. - generic_description: "%{domain} je jeden server v sieti" + federation_hint_html: S úÄtom na %{instance} budeÅ¡ môcÅ¥ následovaÅ¥ ľúdà na hociakom Mastodon serveri, ale aj na iných serveroch. get_apps: Vyskúšaj aplikácie hosted_on: Mastodon hostovaný na %{domain} + instance_actor_flash: | + Tento úÄet je virtuálnym aktérom, ktorý predstavuje samotný server a nie žiadného jedného užÃvateľa. + Je využÃvaný pre potreby federovania a nemal by byÅ¥ blokovaný, pokiaľ nechceÅ¡ zablokovaÅ¥ celý server, Äo ide lepÅ¡ie dosiahnúť cez blokovanie domény. learn_more: Zisti viac - privacy_policy: Ustanovenia o súkromà + privacy_policy: Zásady súkromia see_whats_happening: Pozoruj, Äo sa deje server_stats: 'Serverové Å¡tatistiky:' source_code: Zdrojový kód @@ -36,16 +35,22 @@ sk: other: prÃspevky status_count_before: Ktorà napÃsali tagline: Následuj kamarátov, a objavuj nových - terms: Podmienky užÃvania + terms: Podmienky užitia + unavailable_content: Nedostupný obsah + unavailable_content_description: + domain: Server + reason: 'Dôvod:' user_count_after: few: užÃvateľov - many: užÃvateľov + many: užÃvatelia one: užÃvateľ - other: uživatelia + other: užÃvateľov user_count_before: Domov pre what_is_mastodon: ÄŒo je Mastodon? accounts: choices_html: "%{name}vé voľby:" + endorsements_hint: MôžeÅ¡ ukázaÅ¥ sledovaných užÃvateľov, s ktorými si spriaznený/á cez webové rozhranie, a tà tu budú zobrazenÃ. + featured_tags_hint: MôžeÅ¡ zvýrazniÅ¥ urÄité haÅ¡tagy, ktoré tu budú zobrazené. follow: Následuj followers: few: Sledovateľov @@ -59,6 +64,7 @@ sk: media: Médiá moved_html: "%{name} úÄet bol presunutý na %{new_profile_link}:" network_hidden: Táto informácia nieje k dispozÃcii + never_active: Nikdy nothing_here: NiÄ tu nie je! people_followed_by: Ľudia, ktorých %{name} sleduje people_who_follow: Ľudia sledujúci %{name} @@ -191,11 +197,12 @@ sk: username: Prezývka warn: Varuj web: Web + whitelisted: Na bielej listine action_logs: actions: - assigned_to_self_report: "%{name}pridelil/a hlásenie užÃvateľa %{target}sebe" + assigned_to_self_report: "%{name} pridelil/a hlásenie užÃvateľa %{target} sebe" change_email_user: "%{name} zmenil/a emailovú adresu užÃvateľa %{target}" - confirm_user: "%{name} potvrdil e-mailovú adresu použÃvateľa %{target}" + confirm_user: "%{name} potvrdil emailovú adresu použÃvateľa %{target}" create_account_warning: "%{name} poslal/a varovanie užÃvateľovi %{target}" create_custom_emoji: "%{name} nahral nový emoji %{target}" create_domain_block: "%{name} zablokoval doménu %{target}" @@ -210,7 +217,7 @@ sk: disable_user: "%{name} zakázal prihlásenie pre použÃvateľa %{target}" enable_custom_emoji: "%{name} povolil emoji %{target}" enable_user: "%{name} povolil prihlásenie pre použÃvateľa %{target}" - memorialize_account: '%{name} zmenil úÄet %{target} na stránku "Navždy budeme spomÃnaÅ¥"' + memorialize_account: "%{name} zmenil úÄet %{target} na pamätnú stránku" promote_user: "%{name} povýšil/a použÃvateľa %{target}" remove_avatar_user: "%{name} odstránil/a %{target}ov avatár" reopen_report: "%{name} znovu otvoril/a hlásenie užÃvateľa %{target}" @@ -226,19 +233,24 @@ sk: deleted_status: "(zmazaný prÃspevok)" title: Kontrólny záznam custom_emojis: + assign_category: PriraÄ kategóriu by_domain: Doména copied_msg: Miestna kópia emoji bola úspeÅ¡ne vytvorená copy: KopÃruj copy_failed_msg: Nebolo možné vytvoriÅ¥ miestnu kópiu tohto emoji + create_new_category: Vytvor novú kategóriu created_msg: Emoji úspeÅ¡ne vytvorené! delete: Zmaž destroyed_msg: Emoji úspeÅ¡ne zniÄené! disable: Zakáž + disabled: Vypnuté disabled_msg: Emoji bolo úspeÅ¡ne zakázané emoji: Emotikony enable: Povoľ + enabled: Povolené enabled_msg: Emoji bolo úspeÅ¡ne povolené image_hint: PNG do 50KB + list: Zoznam listed: V zozname new: title: Pridaj nové, vlastné emoji @@ -246,11 +258,14 @@ sk: shortcode: Skratka shortcode_hint: Aspoň 2 znaky, povolené sú alfanumerické, alebo podÄiarkovnÃk title: Vlastné emoji + uncategorized: Nezaradené + unlist: NezaraÄ unlisted: Nie je na zozname update_failed_msg: Nebolo možné aktualizovaÅ¥ toto emoji updated_msg: Emoji bolo úspeÅ¡ne aktualizované! upload: Nahraj dashboard: + authorized_fetch_mode: Autorizovaný režim backlog: odložené aktivity config: Nastavenia feature_deletions: Vymazanie úÄtov @@ -258,10 +273,13 @@ sk: feature_profile_directory: Katalóg profilov feature_registrations: Registrácie feature_relay: Federovacà mostÃk + feature_spam_check: Proti spamu feature_timeline_preview: Náhľad Äasovej osi features: Vymoženosti hidden_service: Federácia so skrytými službami open_reports: otvorené hlásenia + pending_tags: haÅ¡tagy Äakajúce na posúdenie + pending_users: užÃvatelia Äakajúci na posúdenie recent_users: Nedávni užÃvatelia search: Celofrázové vyhľadávanie single_user_mode: Jednouživateľské rozhranie @@ -273,11 +291,18 @@ sk: week_interactions: Tohto-týždňové interakcie week_users_active: aktÃvni tento týždeň week_users_new: užÃvateľov poÄas tohto týždňa + whitelist_mode: Režim povolených + domain_allows: + add_new: Povolená doména + created_msg: Doména bola úspeÅ¡ne povolená + destroyed_msg: Doména bola odstránená zo zoznamu povolených + undo: Odober zo zoznamu povolených domain_blocks: add_new: Blokuj novú doménu created_msg: Doména je v Å¡tádiu blokovania destroyed_msg: Blokovanie domény bolo zruÅ¡ené domain: Doména + edit: Uprav blokovanie domény existing_domain_block_html: Pre úÄet %{name} si už nahodil/a pÅ™ÃsnejÅ¡ie obmedzenie, najskôr ho teda musÃÅ¡ <a href="%{unblock_url}">odblokovaÅ¥</a>. new: create: Vytvor blokovanie domény @@ -288,6 +313,8 @@ sk: silence: StÃÅ¡ suspend: VylÃºÄ title: Nové blokovanie domény + private_comment: Súkromný komentár + public_comment: Verejný komentár reject_media: Odmietaj súbory s obrázkami, alebo videami reject_media_hint: Vymaže miestne uložené súbory médià a odmietne ich sÅ¥ahovanie v budúcnosti. Nepodstatné pri vylúÄenà reject_reports: Zamietni hlásenia @@ -309,6 +336,7 @@ sk: title: ZruÅ¡ blokovanie domény %{domain} undo: Vráť späť undo: Odvolaj blokovanie domény + view: Ukáž blokovanie domén email_domain_blocks: add_new: Pridaj nový created_msg: Emailová doména bola úspeÅ¡ne pridaná do zoznamu zakázaných @@ -334,6 +362,8 @@ sk: all: VÅ¡etky limited: Obmedzené title: Moderácia + private_comment: Súkromný komentár + public_comment: Verejný komentár title: Federácia total_blocked_by_us: Nami blokované total_followed_by_them: Nimi sledované @@ -411,6 +441,16 @@ sk: custom_css: desc_html: Uprav vzhľad pomocou CSS, ktoré je naÄÃtané na každej stránke title: Vlastné CSS + default_noindex: + desc_html: Ovplyvňuje vÅ¡etkých užÃvateľov, ktorà si toto nasavenie nezmenili sami + title: VyraÄ užÃvateľov z indexovania vyhľadávaÄmi, ako východzie nastavenie + domain_blocks: + all: VÅ¡etkým + disabled: Nikomu + title: Ukáž blokované domény + users: Prihláseným, miestnym užÃvateľom + domain_blocks_rationale: + title: Ukáž zdôvodnenie hero: desc_html: Zobrazuje sa na hlavnej stránke. DoporuÄené je rozliÅ¡enie aspoň 600x100px. Pokiaľ niÄ nieje dodané, bude nastavený základný orázok serveru. title: Obrázok hrdinu @@ -461,6 +501,9 @@ sk: desc_html: MôžeÅ¡ si napÃsaÅ¥ svoje vlastné pravidla o súkromÃ, prevádzke, alebo aj iné legality. MôžeÅ¡ tu použÃvaÅ¥ HTML kód title: Vlastné pravidlá prevádzky site_title: Názov servera + spam_check_enabled: + desc_html: Mastodon môže sám stÃÅ¡iÅ¥, a nahlásiÅ¥ úÄty v závislosti od rozpoznania parametrov ako naprÃklad opakované rozosielanie nevyžiadanej komunikácie. Môže dôjsÅ¥ aj k nesprávnej identifikácii. + title: Proti spamu thumbnail: desc_html: PoužÃvané pre náhľady cez OpenGraph a API. DoporuÄuje sa rozliÅ¡enie 1200x630px title: Miniatúra servera @@ -468,12 +511,16 @@ sk: desc_html: ZobraziÅ¥ verejnú nástenku na hlavnej stránke title: Náhľad nástenky title: Nastavenia stránky + trends: + desc_html: Verejne zobraz už schválené haÅ¡tagy, ktoré práve trendujú + title: Populárne haÅ¡tagy statuses: back_to_account: Späť na úÄet batch: delete: Vymaž nsfw_off: OznaÄ ako nechúlostivé nsfw_on: OznaÄ ako chúlostivé + deleted: Vymazané failed_to_execute: Nepodarilo sa vykonaÅ¥ media: title: Médiá @@ -481,21 +528,24 @@ sk: no_status_selected: Žiadne prÃspevky neboli zmenené, keÄže si žiadne nemal/a zvolené title: PrÃspevky na úÄte with_media: S médiami - subscriptions: - callback_url: Zdrojová adresa URL - confirmed: Potvrdené - expires_in: Vypršà do - last_delivery: Posledné doruÄenie - title: WebSub - topic: Téma tags: - accounts: ÚÄty - hidden: Skryté - hide: Ukri od databázy + accounts_today: JedineÄných užÃvateľov za dneÅ¡ok + accounts_week: JedineÄných užÃvateľov tento týždeň + breakdown: Rozpis dneÅ¡ného využitia podľa zdroja + context: Súvis + directory: V zozname + in_directory: "%{count} v zozname" + last_active: Naposledy aktÃvny + most_popular: NajpopulárnejÅ¡ie + most_recent: NajnovÅ¡ie name: HaÅ¡tag + review: PrehodnoÅ¥ stav + reviewed: Zhodnotené title: HaÅ¡tagy - unhide: Ukáž v databázi - visible: Viditeľné + trending_right_now: Práve populárne + unique_uses_today: "%{count} dnes prispievajú" + unreviewed: Neposúdené + updated_msg: Nastavenia haÅ¡tagov boli úspeÅ¡ne aktualizované title: Spravovanie warning_presets: add_new: Pridaj nové @@ -511,13 +561,21 @@ sk: body: "%{reporter} nahlásil/a %{target}" body_remote: Niekto z %{domain} nahlásil/a %{target} subject: Nové hlásenie pre %{instance} (#%{id}) + new_trending_tag: + subject: Nový haÅ¡tag oÄakáva preverenie na %{instance} (#%{name}) + aliases: + add_new: Vytvor alias + remove: Odpoj alias appearance: advanced_web_interface: PokroÄilé webové rozhranie + advanced_web_interface_hint: 'Ak chceÅ¡ využiÅ¥ celkovú Å¡Ãrku tvojej obrazovky, pokroÄilé webové rozhranie ti umožňuje nastaviÅ¥ mnoho rôznych stĺpcov, aby si videl/a toľko informácià naraz, koľko chceÅ¡: Domov, oboznámenia, federovanú Äasovú os, a ľubovolný poÄet zoznamov, Äi haÅ¡tagov.' animations_and_accessibility: Animácie a prÃstupnosÅ¥ confirmation_dialogs: Potvrdzovacie dialógy + discovery: Nájdenie sensitive_content: Chúlostivý obsah application_mailer: notification_preferences: Zmeň emailové voľby + salutation: "%{name}," settings: 'Zmeň emailové voľby: %{link}' view: 'ZobraziÅ¥:' view_profile: Zobraz profil @@ -534,9 +592,13 @@ sk: apply_for_account: Vyžiadaj si pozvánku change_password: Heslo checkbox_agreement_html: SúhlasÃm s <a href="%{rules_path}" target="_blank">pravidlami servera</a>, aj s <a href="%{terms_path}" target="_blank">prevoznými podmienkami</a> - confirm_email: PotvrÄ email + checkbox_agreement_without_rules_html: SúhlasÃm s <a href="%{terms_path}" target="_blank">podmienkami užÃvania</a> delete_account: Vymaž úÄet delete_account_html: Pokiaľ chceÅ¡ svoj úÄet odtiaľto vymazaÅ¥, môžeÅ¡ tak <a href="%{path}">urobiÅ¥ tu</a>. BudeÅ¡ požiadaný/á o potvrdenie tohto kroku. + description: + prefix_invited_by_user: "@%{name} Å¥a pozýva na tento Mastodon server!" + prefix_sign_up: Zaregistruj sa na Mastodone už dnes! + suffix: S pomocou úÄtu budeÅ¡ môcÅ¥ následovaÅ¥ ľudÃ, posielaÅ¥ prÃspevky, a vymienaÅ¥ si správy s užÃvateľmi na hociakom Mastodon serveri, ale aj na iných serveroch! didnt_get_confirmation: Neobdržal/a si kroky na potvrdenie? forgot_password: Zabudnuté heslo? invalid_reset_password_token: Token na obnovu hesla vyprÅ¡al. ProsÃm vypÃtaj si nový. @@ -551,6 +613,16 @@ sk: reset_password: Obnov heslo security: ZabezpeÄenie set_new_password: Nastav nové heslo + setup: + email_below_hint_html: Ak je nižšie uvedená emailová adresa nesprávna, môžeÅ¡ ju zmeniÅ¥ a dostaÅ¥ nový potvrdzovacà email. + email_settings_hint_html: Potvrdzovacà email bol odoslaný na %{email}. Ak táto emailová adresa nieje správna, môžeÅ¡ si ju zmeniÅ¥ v nastaveniach úÄtu. + title: Nastavenie + status: + account_status: Stav úÄtu + confirming: ÄŒaká sa na dokonÄenie potvrdenia emailom. + functional: Tvoj úÄet je plne funkÄný. + pending: Tvoja žiadosÅ¥ Äaká na schvÃlenie od nášho týmu. Môže to chviľu potrvaÅ¥. Ak bude tvoja žiadosÅ¥ schválená, dostaneÅ¡ o tom email. + redirecting_to: Tvoj úÄet je neaktÃvny, lebo v súÄasnosti presmerováva na %{acct}. trouble_logging_in: Problém s prihlásenÃm? authorize_follow: already_following: Tento úÄet už následujeÅ¡ @@ -563,6 +635,10 @@ sk: return: Ukáž užÃvateľov profil web: Prejdi do siete title: Následuj %{acct} + challenge: + confirm: PokraÄuj + invalid_password: Nesprávne heslo + prompt: Pre pokraÄovanie potvrÄ svoje heslo datetime: distance_in_words: about_x_hours: "%{count}hod" @@ -578,28 +654,28 @@ sk: x_months: "%{count}mesiace" x_seconds: "%{count}sek" deletes: - bad_password_msg: Dobrý pokus, hakeri! Nesprávne heslo confirm_password: NapÃÅ¡te svoje terajÅ¡ie heslo pre overenie vaÅ¡ej identity - description_html: Týmto <strong> natrvalo, nenavrátiteľne </strong> vymažeÅ¡ obsah tvojho úÄtu, a deaktivujeÅ¡ ho. Tvoja prezývka ale ostane rezervovaná ako prevencia pred budúcimi impersonáciami. + confirm_username: Zadaj svoju prezývku, na potvrdenie úkonu proceed: Vymaž úÄet success_msg: Tvoj úÄet bol úspeÅ¡ne vymazaný - warning_html: Iba vymazanie obsahu z tohto konkrétneho serveru je zaruÄené. Obsah, ktorý bol zdieľaný Å¡iroko-Äaleko pravdepodobne zanechá nejaké stopy. Servery ktoré sú offline a tie ktoré ignorujú tvoje zmeny teda nezaktualizujú svoje databázy. - warning_title: DostupnosÅ¥ rozÅ¡Ãrovaného obsahu + warning: + before: 'Než budeÅ¡ pokraÄovaÅ¥, prosÃm pozorne si preÄÃtaj tieto poznámky:' + caches: Obsah, ktorý bol predÄÃtaný inými servermi môže zanechaÅ¥ pozostatky + data_removal: Tvoje prÃspevky a iné dáta budú natrvalo odstránené + more_details_html: Pre viac podrobnostÃ, pozri <a href="%{terms_path}">zásady súkromia</a>. + username_available: Tvoje užÃvateľské meno bude znova dostupné + username_unavailable: Tvoja prezývka ostane neprÃstupná directories: directory: Katalóg profilov - enabled: Momentálne si uvedený/á na zozname profilov. - enabled_but_waiting: Vyjadril/a si záujem o uvedenie na zozname profilov, lenže eÅ¡te nemáš minimálny vyžadovaný poÄet následovateľov (%{min_followers}), aby si tam bol/a uveden/á. explanation: Pátraj po užÃvateľoch podľa ich záujmov explore_mastodon: Prebádaj %{title} - how_to_enable: Momentálne niesi zaradený/á do verejnej profilovej databázy. PrihlásiÅ¥ sa môžeÅ¡ nižšie. Použi haÅ¡tagy vo svojom biografickom popise na profile, ak chceÅ¡ byÅ¥ uvedený/á aj pod konkrétnými haÅ¡tagmi! - people: - few: "%{count} ľudÃ" - many: "%{count} ľudÃ" - one: "%{count} Älovek" - other: "%{count} ľudia" + domain_validator: + invalid_domain: nieje správny tvar domény errors: + '400': Požiadavka, ktorú si odoslal/a, bola buÄ nesprávna, alebo znehodnotená. '403': Nemáš povolenie pre zobrazenie tejto stránky. '404': Stránka ktorú hľadáš nieje tu. + '406': Táto stránka nie je dostupná v požadovanom formáte. '410': Stránka ktorú si tu hľadal/a sa tu už viac nenachádza. '422': content: BezpeÄtnostné overenie zlyhalo. BlokujeÅ¡ cookies? @@ -608,6 +684,7 @@ sk: '500': content: Ospravedlňujem sa. NieÄo sa pokazilo na naÅ¡om konci. title: Táto stránka nieje v poriadku + '503': Táto stránka nemôže byÅ¥ naÄÃtaná, kvôli doÄasnému výpadku servera. noscript_html: Aby bolo možné použÃvaÅ¥ Mastodon web aplikáciu, povoľ prosÃm JavaScript. Alebo skús jednu z <a href="%{apps_path}"> aplikácii </a> dostupných pre vaÅ¡u platformu. existing_username_validator: not_found: nepodarilo sa nájsÅ¥ miestného užÃvateľa s takouto prezývkou @@ -651,18 +728,34 @@ sk: developers: Vývojári more: Viac… resources: Podklady + trending_now: Teraz populárne generic: all: VÅ¡etko changes_saved_msg: Zmeny boli úspeÅ¡ne uložené! copy: KopÃruj + no_batch_actions_available: Na tejto stránke niesú k dispozÃcii žiadne hromadné akcie order_by: ZoraÄ podľa save_changes: Ulož zmeny + validation_errors: + few: NieÄo eÅ¡te nieje celkom v poriadku! ProsÃm skontroluj %{count} chýb uvedených nižšie + many: NieÄo eÅ¡te nieje celkom v poriadku! ProsÃm skontroluj %{count} chýb uvedených nižšie + one: NieÄo eÅ¡te nieje celkom v poriadku! ProsÃm skontroluj chybu uvedenú nižšie + other: NieÄo eÅ¡te nieje celkom v poriadku! ProsÃm skontroluj %{count} chyby uvedené nižšie + html_validator: + invalid_markup: 'obsahuje neplatný HTML kód: %{error}' identity_proofs: active: AktÃvne authorize: Ãno, povoľ authorize_connection_prompt: PovoliÅ¥ toto kryptografické prepojenie? errors: failed: Kryptografické prepojenie sa nepodarilo. ProsÃm skús to znova z %{provider}. + keybase: + invalid_token: Keybase tokeny sú haÅ¡ovaniami podpisov a musia maÅ¥ 66 znakov + verification_failed: Keybase nerozpoznáva tento token ako podpis od Keybase užÃvateľa menom %{kb_username}. ProsÃm skús to znova cez Keybase. + wrong_user: Nemožno vytvoriÅ¥ overenie pre %{proving}, pokiaľ si prihlásený/á ako %{current}. Prihlás sa za %{proving} a skús to znova. + explanation_html: |- + Tu si môžeÅ¡ kryptograficky prepojiÅ¥ svoje iné identity, ako naprÃklad tvoj profil na Keybase. + Umožňuje to ostatnÃm ľudom posielaÅ¥ ti enkryptované správy a veriÅ¥ obsahu ktorý im poÅ¡leÅ¡ ty. i_am_html: Na %{service} som %{username}. identity: Identita inactive: NeaktÃvne @@ -718,9 +811,24 @@ sk: too_many: NemôžeÅ¡ priložiÅ¥ viac ako 4 súbory migrations: acct: prezývka@doména nového úÄtu - currently_redirecting: 'Tvoj profil má nastavené presmerovanie na:' - proceed: UložiÅ¥ - updated_msg: Tvoje nastavenia pre presmerovanie úÄtu boli úspeÅ¡ne aktualizované! + cancel: ZruÅ¡ presmerovanie + cancelled_msg: Presmerovanie úspeÅ¡ne zruÅ¡ené. + errors: + missing_also_known_as: neodkazuje spätne na tento úÄet + move_to_self: nemôže to byÅ¥ tvoj súÄasný úÄet + not_found: nebolo možné nájsÅ¥ + on_cooldown: Si v spánkovom stave + followers_count: Následovatelia v Äase presunu + incoming_migrations: Presúvam sa z iného úÄtu + not_redirecting: Tvoj úÄet v súÄasnosti nepresmerováva na žiaden iný úÄet. + past_migrations: PredoÅ¡lé presuny + proceed_with_move: Presuň sledovateľov + redirecting_to: Tvoj úÄet presmerováva na %{acct}. + set_redirect: Nastav presmerovanie + warning: + backreference_required: Nový úÄet musà byÅ¥ najskôr nastavený tak, aby spätne odkazoval na tento + before: 'Než budeÅ¡ pokraÄovaÅ¥, prosÃm pozorne si preÄÃtaj tieto poznámky:' + other_data: Žiadne iné dáta nebudú presunuté automaticky moderation: title: Moderovanie notification_mailer: @@ -728,6 +836,16 @@ sk: action: ZobraziÅ¥ vÅ¡etky notifikácie body: Tu nájdete krátky súhrn správ ktoré ste zmeÅ¡kali od svojej poslednj návÅ¡tevi od %{since} mention: "%{name} Å¥a spomenul/a v:" + new_followers_summary: + few: A eÅ¡te, kým si bol/a preÄ, si zÃskal/a %{count} nových následovateľov! Hurá! + many: A eÅ¡te, kým si bol/a preÄ, si zÃskal/a %{count} nových následovateľov! Hurá! + one: A eÅ¡te, kým si bol/a preÄ, si zÃskal/a jedného nového následovateľa! Hurá! + other: A eÅ¡te, kým si bol/a preÄ, si zÃskal/a %{count} nových následovateľov! Hurá! + subject: + few: "%{count} nových oboznámenà od tvojej poslednej návÅ¡tevy \U0001F418" + many: "%{count} nových oboznámenà od tvojej poslednej návÅ¡tevy \U0001F418" + one: "Jedno nové oboznámenie od tvojej poslednej návÅ¡tevy \U0001F418" + other: "%{count} nové oboznámenia od tvojej poslednej návÅ¡tevy \U0001F418" title: Zatiaľ Äo si bol/a preÄ… favourite: body: 'Tvoj prÃspevok bol uložený medzi obľúbené užÃvateľa %{name}:' @@ -768,6 +886,7 @@ sk: too_many_options: nemôže zahŕňaÅ¥ viac ako %{max} položiek preferences: other: Ostatné + posting_defaults: Východiskové nastavenia prÃspevkov public_timelines: Verejné Äasové osi relationships: activity: Aktivita úÄtu @@ -778,6 +897,7 @@ sk: mutual: SpoloÄné primary: Hlavné relationship: VzÅ¥ah + remove_selected_domains: Vymaž vÅ¡etkých následovateľov z vybraných domén remove_selected_followers: Odstráň vybraných následovatrľov remove_selected_follows: Prestaň sledovaÅ¥ vybraných užÃvateľov status: Stav úÄtu @@ -798,10 +918,6 @@ sk: reply: proceed: PokraÄuj odpovedanÃm prompt: 'ChceÅ¡ odpovedaÅ¥ na tento prÃspevok:' - remote_unfollow: - error: Chyba - title: Názov - unfollowed: Už nesledujeÅ¡ scheduled_statuses: over_daily_limit: PrekroÄil/a si denný limit %{limit} predplánovaných prÃspevkov over_total_limit: PrekroÄil/a si limit %{limit} predplánovaných prÃspevkov @@ -856,17 +972,37 @@ sk: statuses: attached: description: 'Priložené: %{attached}' + image: + few: "%{count} obrázkov" + many: "%{count} obrázkov" + one: "%{count} obrázok" + other: "%{count} obrázky" + video: + few: "%{count} videÃ" + many: "%{count} videÃ" + one: "%{count} video" + other: "%{count} videá" boosted_from_html: Vyzdvihnuté od %{acct_link} content_warning: 'Varovanie o obsahu: %{warning}' + disallowed_hashtags: + few: 'obsah nepovolených haÅ¡tagov: %{tags}' + many: 'obsah nepovolených haÅ¡tagov: %{tags}' + one: 'obsahoval nepovolený haÅ¡tag: %{tags}' + other: 'obsahoval nepovolené haÅ¡tagy: %{tags}' language_detection: Zisti automaticky open_in_web: Otvor v okne na webe over_character_limit: limit %{max} znakov bol presiahnutý pin_errors: limit: Už si si pripol ten najvyššà možný poÄet hlášok ownership: Nieje možné pripnúť hlášku od niekoho iného - private: Neverejné prÃspevky nemôžu byÅ¥ pripnuté + private: Neverejný prÃspevok nemôže byÅ¥ pripnutý reblog: Vyzdvihnutie sa nedá pripnúť poll: + total_votes: + few: "%{count} hlasov" + many: "%{count} hlasov" + one: "%{count} hlas" + other: "%{count} hlasy" vote: Hlasuj show_more: Ukáž viac sign_in_to_participate: Prihlás sa pre zapojenie do diskusie @@ -882,6 +1018,8 @@ sk: pinned: Pripnutý prÃspevok reblogged: vyzdvihnutý sensitive_content: SenzitÃvny obsah + tags: + does_not_match_previous_name: nezhoduje sa s predoÅ¡lým názvom terms: body_html: | <h2>Podmienky súkromia</h2> @@ -944,6 +1082,7 @@ sk: silence: Kým máš úÄet obmedzený, tvoje prÃspevky na tomto serveri uvidia iba tà ľudia, ktorà ťa už následujú, a môžeÅ¡ byÅ¥ vylúÄený/á z rôznych verejných záznamov. Ostatnà ťa vÅ¡ak stále budú môcÅ¥ následovaÅ¥ manuálne. suspend: Tvoj úÄet bol vylúÄený, a vÅ¡etky tvoje prÃspevky a nahraté médiálné súbory boli nenávratne zmazané z tohto serveru, a zo serverov na ktorých si mal následovateľov. review_server_policies: PrehodnoÅ¥ pravidlá servera + statuses: 'Konkrétne kvôli:' subject: disable: Tvoj úÄet %{acct} bol zamrazený none: Varovanie pre %{acct} @@ -956,7 +1095,7 @@ sk: suspend: Tvoj úÄet bol vylúÄený welcome: edit_profile_action: Nastav profil - edit_profile_step: Profil si môžeÅ¡ prispôsobiÅ¥ nahratÃm portrétu a hlaviÄky, môžeÅ¡ upraviÅ¥ svoje meno a viac. Pokiaľ chceÅ¡ preverovaÅ¥ nových následovateľov predtým než Å¥a budú môcÅ¥ sledovaÅ¥, môžeÅ¡ uzamknúť svoj úÄet. + edit_profile_step: Profil si môžeÅ¡ prispôsobiÅ¥ nahratÃm portrétu a záhlavia, môžeÅ¡ upraviÅ¥ svoje meno a viac. Pokiaľ chceÅ¡ preverovaÅ¥ nových následovateľov predtým než Å¥a budú môcÅ¥ sledovaÅ¥, môžeÅ¡ uzamknúť svoj úÄet. explanation: Tu nájdeÅ¡ nejaké tipy do zaÄiatku final_action: ZaÄni prispievaÅ¥ final_step: 'ZaÄni pÃsaÅ¥! Aj bez následovateľov budú tvoje verejné prÃspevky videné ostatnými, naprÃklad na miestnej osi a pod haÅ¡tagmi. Ak chceÅ¡, môžeÅ¡ sa ostatným predstaviÅ¥ pod haÅ¡tagom #introductions.' diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 85e167ca9d8a2fc3a6c63a1d97dfe63488c4af3b..22b58e7b629fd7066b3f5e9fcb8f5355482e7d07 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1,22 +1,32 @@ --- sl: about: - about_hashtag_html: To so javni tuti, oznaÄeni z <strong>#%{hashtag}</strong>. Z njimi se lahko povežete, Äe imate raÄun kjerkoli v fediversu. + about_hashtag_html: To so javni tuti, oznaÄeni z <strong>#%{hashtag}</strong>. Z njimi se lahko povežete, Äe imate raÄun kjerkoli v fediverse-u. about_mastodon_html: Mastodon je socialno omrežje, ki temelji na odprtih spletnih protokolih in prosti ter odprtokodni programski opremi. Je decentraliziran, kot e-poÅ¡ta. about_this: O Mastodonu + active_count_after: dejaven + active_footnote: Aktivni meseÄni uporabniki (AMU) administered_by: 'Upravlja:' + api: API apps: Mobilne aplikacije + apps_platforms: Uporabljajte Mastodon iz iOS, Android ali iz drugih platform + browse_directory: Brskajte po imeniku profilov in filtriranje po interesih + browse_public_posts: Brskajte javnih objav v živo na Mastodonu contact: Kontakt contact_missing: Ni nastavljeno contact_unavailable: Ni na voljo + discover_users: Odkrijte uporabnike documentation: Dokumentacija - extended_description_html: | - <h3>Dober prostor za pravila</h3> - <p>RazÅ¡irjen opis Å¡e ni bil nastavljen.</p> - generic_description: "%{domain} je en strežnik v omrežju" + federation_hint_html: Z raÄunom na %{instance} boste lahko spremljali ljudi na kateremkoli Mastodon strežniku. + get_apps: Poskusite mobilno aplikacijo hosted_on: Mastodon gostuje na %{domain} - learn_more: Spoznaj veÄ - privacy_policy: Politika zasebnosti + instance_actor_flash: | + Ta raÄun je navidezni igralec, ki predstavlja strežnik in ne posameznega uporabnika. + Uporablja se za namene federacije in se ne blokira, Äe ne želite blokirati celotne instance. V tem primeru blokirajte domeno. + learn_more: NauÄi se veÄ + privacy_policy: Pravilnik o zasebnosti + see_whats_happening: Poglejte, kaj se dogaja + server_stats: 'Statistika strežnika:' source_code: Izvorna koda status_count_after: few: stanja @@ -24,12 +34,13 @@ sl: other: stanj two: stanja status_count_before: Ki so avtorji + tagline: Sledite prijateljem in odkrijte nove terms: Pogoji storitve user_count_after: few: uporabniki one: uporabnik other: uporabnikov - two: uporabniki + two: uporabnika user_count_before: Dom za what_is_mastodon: Kaj je Mastodon? accounts: @@ -38,56 +49,61 @@ sl: followers: few: Sledilci one: Sledilec - other: Sledilci - two: Sledilci + other: Sledilcev + two: Sledilca following: Sledim joined: Se je pridružil na %{date} - last_active: zadnji aktivni + last_active: zadnja dejavnost link_verified_on: LastniÅ¡tvo te povezave je bilo preverjeno na %{date} - media: Medij + media: Mediji moved_html: "%{name} se je prestavil na %{new_profile_link}:" - network_hidden: Te informacije niso na voljo - nothing_here: NiÄ ni tukaj! + network_hidden: Ta informacija ni na voljo + nothing_here: Tukaj ni niÄesar! people_followed_by: Ljudje, ki jim sledi %{name} people_who_follow: Ljudje, ki sledijo %{name} pin_errors: following: Verjetno že sledite osebi, ki jo želite potrditi posts: - few: Trob - one: Trob - other: Trob - two: Trob - posts_tab_heading: Trobi + few: Tuti + one: Tut + other: Tutov + two: Tuta + posts_tab_heading: Tuti posts_with_replies: Tuti in odgovori reserved_username: UporabniÅ¡ko ime je zasedeno roles: admin: Skrbnik bot: Robot + moderator: Mod + unavailable: Profil ni na voljo unfollow: Prenehaj slediti admin: account_actions: action: Izvedi dejanje - title: Izvedi moderirano dejanje %{acct} + title: Izvedi moderirano dejanje za %{acct} account_moderation_notes: create: Pusti opombo - created_msg: UspeÅ¡no ustvarjena opomba moderiranja! + created_msg: Moderirana opomba je uspeÅ¡no ustvarjena! delete: IzbriÅ¡i destroyed_msg: Moderirana opomba je uspeÅ¡no uniÄena! accounts: - are_you_sure: Ali si prepriÄan? + approve: Odobri + approve_all: Odobri vse + are_you_sure: Ali ste prepriÄani? + avatar: Podoba by_domain: Domena change_email: changed_msg: E-poÅ¡ta raÄuna je uspeÅ¡no spremenjena! - current_email: Trenutna E-poÅ¡ta - label: Spremeni E-poÅ¡to - new_email: Nova E-poÅ¡ta - submit: Spremeni E-poÅ¡to - title: Spremeni E-poÅ¡to za %{username} + current_email: Trenutna e-poÅ¡ta + label: Spremeni e-poÅ¡to + new_email: Nova e-poÅ¡ta + submit: Spremeni e-poÅ¡to + title: Spremeni e-poÅ¡to za %{username} confirm: Potrdi confirmed: Potrjeno confirming: Potrjujem deleted: Izbrisano - demote: Ponižaj + demote: Degradiraj disable: OnemogoÄi disable_two_factor_authentication: OnemogoÄi 2FA disabled: OnemogoÄeno @@ -95,44 +111,50 @@ sl: domain: Domena edit: Uredi email: E-poÅ¡ta - email_status: Stanje E-poÅ¡te + email_status: Stanje e-poÅ¡te enable: OmogoÄi enabled: OmogoÄeno - feed_url: URL vir + feed_url: URL vira followers: Sledilci - followers_url: URL sledilci + followers_url: URL sledilcev follows: Sledi header: Glava - inbox_url: URl v mapi "Prejeto" + inbox_url: URL mape "Prejeto" invited_by: Povabljen od + ip: IP joined: Pridružil location: all: Vse - local: Lokalno + local: Lokalni remote: Oddaljeni title: Lokacija login_status: Stanje prijave - media_attachments: Medijske priloge + media_attachments: Predstavnostne priloge memorialize: Spremenite v spomin moderation: active: Dejaven all: Vse + pending: Na Äakanju silenced: UtiÅ¡an suspended: Suspendiran title: Moderiranje moderation_notes: Opombe moderiranja - most_recent_activity: Zadnja aktivnost + most_recent_activity: Zadnja dejavnost most_recent_ip: Zadnji IP + no_account_selected: Noben raÄun ni bil spremenjen, ker ni bil izbran noben no_limits_imposed: Brez omejitev - not_subscribed: Ni naroÄeno - outbox_url: URl za poÅ¡iljanje - perform_full_suspension: ZaÄasno ustavi + not_subscribed: Ni naroÄen + outbox_url: URL za poÅ¡iljanje + pending: ÄŒakanje na pregled + perform_full_suspension: Suspendiraj profile_url: URL profila - promote: Spodbujanje + promote: Promoviraj protocol: Protokol public: Javen push_subscription_expires: NaroÄnina PuSH preteÄe redownload: Osveži profil + reject: Zavrni + reject_all: Zavrni vse remove_avatar: Odstrani podobo remove_header: Odstrani glavo resend_confirmation: @@ -145,9 +167,11 @@ sl: role: Dovoljenja roles: admin: Skrbnik + moderator: Moderator staff: Osebje user: Uporabnik - search: PoiÅ¡Äi + salmon_url: URL lososa + search: Iskanje shared_inbox_url: URL mape "Prejeto v skupni rabi" show: created_reports: Narejene prijave @@ -157,6 +181,7 @@ sl: statuses: Stanja subscribe: NaroÄi suspended: Suspendiran + time_in_queue: ÄŒakanje v vrsti %{time} title: RaÄuni unconfirmed_email: Nepotrjena e-poÅ¡ta undo_silenced: Razveljavi utiÅ¡anje @@ -165,33 +190,34 @@ sl: username: UporabniÅ¡ko ime warn: Opozori web: Splet + whitelisted: Na belem seznamu action_logs: actions: assigned_to_self_report: "%{name} je prijavil %{target} sebi" change_email_user: "%{name} je spremenil naslov e-poÅ¡te uporabnika %{target}" confirm_user: "%{name} je potrdil naslov e-poÅ¡te uporabnika %{target}" create_account_warning: "%{name} je poslal opozorilo %{target}" - create_custom_emoji: "%{name} je poslal nove emotikone %{target}" + create_custom_emoji: "%{name} je posodobil emotikone %{target}" create_domain_block: "%{name} je blokiral domeno %{target}" create_email_domain_block: "%{name} je dal na Ärni seznam e-poÅ¡to domene %{target}" demote_user: "%{name} je degradiral uporabnika %{target}" - destroy_custom_emoji: "%{name} je uniÄil emotikon %{target}" + destroy_custom_emoji: "%{name} je uniÄil emotikone %{target}" destroy_domain_block: "%{name} je odblokiral domeno %{target}" destroy_email_domain_block: "%{name} je dal na beli seznam e-poÅ¡to domene %{target}" destroy_status: "%{name} je odstranil stanje od %{target}" disable_2fa_user: "%{name} je onemogoÄil dvofaktorsko zahtevo za uporabnika %{target}" - disable_custom_emoji: "%{name} je onemogoÄil emotikon %{target}" + disable_custom_emoji: "%{name} je onemogoÄil emotikone %{target}" disable_user: "%{name} je onemogoÄil prijavo za uporabnika %{target}" - enable_custom_emoji: "%{name} je omogoÄil emotikon %{target}" + enable_custom_emoji: "%{name} je omogoÄil emotikone %{target}" enable_user: "%{name} je omogoÄil prijavo za uporabnika %{target}" memorialize_account: "%{name} je spremenil raÄun od %{target} v stran spominov" - promote_user: "%{name} je spodbudil uporabnika %{target}" + promote_user: "%{name} je promoviral uporabnika %{target}" remove_avatar_user: "%{name} je odstranil podobo od %{target}" reopen_report: "%{name} je ponovno odprl prijavo %{target}" reset_password_user: "%{name} je ponastavil geslo od uporabnika %{target}" resolve_report: "%{name} je razreÅ¡il prijavo %{target}" silence_account: "%{name} je utiÅ¡al raÄun od %{target}" - suspend_account: "%{name} je zaÄasno ustavil raÄun od %{target}" + suspend_account: "%{name} je suspendiral raÄun od %{target}" unassigned_report: "%{name} je nedodeljeno prijavil %{target}" unsilence_account: "%{name} je preklical utiÅ¡anje raÄuna od %{target}" unsuspend_account: "%{name} je aktiviral raÄun od %{target}" @@ -201,9 +227,9 @@ sl: title: Dnevnik revizije custom_emojis: by_domain: Domena - copied_msg: Lokalna kopija emotikona je bila uspeÅ¡no ustvarjena + copied_msg: Lokalna kopija emotikonov je bila uspeÅ¡no ustvarjena copy: Kopiraj - copy_failed_msg: Lokalne kopije emotikona ni bilo mogoÄe ustvariti + copy_failed_msg: Lokalne kopije emotikonov ni bilo mogoÄe ustvariti created_msg: Emotikon je uspeÅ¡no ustvarjen! delete: IzbriÅ¡i destroyed_msg: Emotikon je uspeÅ¡no uniÄen! @@ -225,13 +251,15 @@ sl: updated_msg: Emotikon je uspeÅ¡no posodobljen! upload: PoÅ¡lji dashboard: - backlog: Zaostala opravila + backlog: zaostala opravila config: Nastavitve feature_deletions: Brisanje raÄunov - feature_invites: Poveza povabil - feature_profile_directory: Mapa profila + feature_invites: Povezave povabil + feature_profile_directory: Imenik profilov feature_registrations: Registracije feature_relay: Rele federacije + feature_spam_check: Anti-spam + feature_timeline_preview: Predogled Äasovnice features: Zmožnosti hidden_service: Federacija s skritimi storitvami open_reports: odprte prijave @@ -241,22 +269,28 @@ sl: software: Programska oprema space: Uporaba prostora title: Nadzorna ploÅ¡Äa - total_users: Skupaj uporabnikov + total_users: skupaj uporabnikov trends: Trendi week_interactions: interakcije ta teden week_users_active: aktivni ta teden week_users_new: uporabniki ta teden + domain_allows: + add_new: Dodaj domeno na beli seznam + created_msg: Domena je bila uspeÅ¡no dodana na beli seznam + destroyed_msg: Domena je bila odstranjena iz belega seznama + undo: Odstrani iz belega seznama domain_blocks: add_new: Dodaj nov domenski blok created_msg: Domenski blok se sedaj obdeluje destroyed_msg: Domenski blok je bil razveljavljen domain: Domena + existing_domain_block_html: Uvedli ste strožje omejitve za %{name}, sedaj ga morate najprej <a href="%{unblock_url}">odblokirati</a>. new: create: Ustvari blok hint: Domenski blok ne bo prepreÄil ustvarjanja vnosov raÄunov v zbirko podatkov, ampak bo retroaktivno in samodejno uporabil posebne metode moderiranja na teh raÄunih. severity: desc_html: "<strong>UtiÅ¡aj</strong> bo vse objave raÄuna naredil nevidne vsem, ki jih ne sledijo. <strong>Suspendiraj</strong> bo odstranil vso vsebino, medije in podatke profila raÄuna. Uporabi <strong>niÄ</strong>, Äe želite le zavrniti predstavnostne datoteke." - noop: NiÄ + noop: Brez silence: UtiÅ¡aj suspend: Suspendiraj title: Nov domenski blok @@ -271,13 +305,13 @@ sl: suspend: suspendirani show: affected_accounts: - few: "%{count} raÄunov v bazi podatkov so prizadeti" + few: "%{count} raÄuni v bazi podatkov so prizadeti" one: En raÄun v bazi podatkov je prizadet - other: "%{count} raÄunov v bazi podatkov so prizadeti" - two: "%{count} raÄunov v bazi podatkov so prizadeti" + other: "%{count} raÄunov v bazi podatkov je prizadetih" + two: "%{count} raÄuna v bazi podatkov so prizadeta" retroactive: silence: PrekliÄi utiÅ¡anje za vse obstojeÄe raÄune iz te domene - suspend: Odsuspendiraj vse obstojeÄe raÄune iz te domene + suspend: Aktiviraj vse obstojeÄe raÄune iz te domene title: Razveljavi domenski blok za %{domain} undo: Razveljavi undo: Razveljavi domenski blok @@ -290,17 +324,18 @@ sl: new: create: Dodaj domeno title: Nov vnos e-poÅ¡te na Ärni seznam - title: ÄŒrni seznam e-poÅ¡te + title: ÄŒrni seznam e-poÅ¡t followers: back_to_account: Nazaj na raÄun title: Sledilci od %{acct} instances: + by_domain: Domena delivery_available: Na voljo je dostava known_accounts: - few: "%{count} znanih raÄunov" + few: "%{count} znani raÄuni" one: "%{count} znan raÄun" other: "%{count} znanih raÄunov" - two: "%{count} znanih raÄunov" + two: "%{count} znana raÄuna" moderation: all: Vse limited: Omejeno @@ -317,15 +352,18 @@ sl: all: Vse available: Razpoložljivo expired: Potekel + title: Filter title: Povabila + pending_accounts: + title: "(%{count}) raÄunov na Äakanju" relays: add_new: Dodaj nov rele delete: IzbriÅ¡i - description_html: "<strong>Rele federacije</strong> je posredniÅ¡ki strežnik, ki si izmenjuje velike koliÄine javnih trobov med strežniki, ki so se naroÄili in objavili na njem. <strong>Majhnim in srednjim strežnikom lahko pomaga pri odkrivanju vsebine iz sistema fediverse</strong>, kar bi sicer zahtevalo, da lokalni uporabniki roÄno sledijo druge osebe na oddaljenih strežnikih." + description_html: "<strong>Rele federacije</strong> je posredniÅ¡ki strežnik, ki si izmenjuje velike koliÄine javnih tutov med strežniki, ki so se naroÄili in objavili na njem. <strong>Majhnim in srednjim strežnikom lahko pomaga pri odkrivanju vsebine iz sistema fediverse</strong>, kar bi sicer zahtevalo, da lokalni uporabniki roÄno sledijo druge osebe na oddaljenih strežnikih." disable: OnemogoÄi disabled: OnemogoÄeno enable: OmogoÄi - enable_hint: Ko je omogoÄen, se bo vaÅ¡ strežnik naroÄil na vse javne trobe iz tega releja in zaÄel poÅ¡iljati javne trobe tega strežnika. + enable_hint: Ko je omogoÄen, se bo vaÅ¡ strežnik naroÄil na vse javne tute iz tega releja in zaÄel poÅ¡iljati javne tute tega strežnika. enabled: OmogoÄeno inbox_url: URL releja pending: ÄŒakanje na odobritev releja @@ -345,7 +383,7 @@ sl: assign_to_self: Dodeli meni assigned: Dodeljen moderator comment: - none: NiÄ + none: Brez created_at: Prijavljeno mark_as_resolved: OznaÄi kot reÅ¡eno mark_as_unresolved: OznaÄi kot nereÅ¡eno @@ -359,19 +397,19 @@ sl: report: 'Prijavi #%{id}' reported_account: Prijavljeni raÄun reported_by: Prijavljen od - resolved: RazreÅ¡eno + resolved: RazreÅ¡eni resolved_msg: Prijava je uspeÅ¡no razreÅ¡ena! status: Stanje title: Prijave - unassign: Odstopi - unresolved: NereÅ¡eno - updated_at: Posodobljen + unassign: Odstopljeni + unresolved: NereÅ¡eni + updated_at: Posodobljeni settings: activity_api_enabled: desc_html: Å tevilke lokalno objavljenih stanj, aktivnih uporabnikov in novih registracij na tedenskih seznamih title: Objavi združeno statistiko o dejavnosti uporabnikov bootstrap_timeline_accounts: - desc_html: VeÄ uporabniÅ¡kih imen loÄite z vejico. Deluje samo na lokalnih in odklenjenih raÄunih. Privzeto, ko je prazno, pri vseh lokalnih skrbnikih. + desc_html: VeÄ uporabniÅ¡kih imen loÄite z vejico. Deluje samo na lokalnih in odklenjenih raÄunih. Privzeto, ko je prazno, je pri vseh lokalnih skrbnikih. title: Privzeta sledenja za nove uporabnike contact_information: email: Poslovna e-poÅ¡ta @@ -379,24 +417,685 @@ sl: custom_css: desc_html: Spremeni videz z naloženim CSS na vsaki strani title: CSS po meri + hero: + desc_html: Prikazano na sprednji strani. PriporoÄa se vsaj 600x100px. Ko ni nastavljen, se vrne na sliÄico strežnika + title: Slika junaka + mascot: + desc_html: Prikazano na veÄ straneh. PriporoÄena je najmanj 293 × 205 px. Ko ni nastavljen, se vrne na privzeto maskoto + title: Slika maskote + peers_api_enabled: + desc_html: Domene, na katere je ta strežnik naletel na fediverse-u + title: Objavi seznam odkritih strežnikov + preview_sensitive_media: + desc_html: Predogledi povezav na drugih spletiÅ¡Äih bodo prikazali sliÄico, tudi Äe je medij oznaÄen kot obÄutljiv + title: Prikaži obÄutljive medije v predogledih OpenGraph + profile_directory: + desc_html: Dovoli uporabnikom, da jih lahko odkrijejo + title: OmogoÄi imenik profilov + registrations: + closed_message: + desc_html: Prikazano na prvi strani, ko so registracije zaprte. Lahko uporabite oznake HTML + title: SporoÄilo o zaprti registraciji + deletion: + desc_html: Dovoli vsakomur, da izbriÅ¡e svoj raÄun + title: Odpri brisanje raÄuna + min_invite_role: + disabled: NihÄe + title: Dovoli vabila od + registrations_mode: + modes: + approved: Potrebna je odobritev za prijavo + none: NihÄe se ne more prijaviti + open: Vsakdo se lahko prijavi + title: NaÄin registracije + show_known_fediverse_at_about_page: + desc_html: Ko preklopite, bo prikazal tute vseh znanih fediverse-ov v predogledu. V nasprotnem primeru bodo prikazani samo lokalni tuti. + title: Pokaži znane fediverse-e v predogledu Äasovnice + show_staff_badge: + desc_html: Prikaži znaÄko osebja na uporabniÅ¡ki strani + title: Prikaži znaÄko osebja + site_description: + desc_html: Uvodni odstavek na API-ju. OpiÅ¡ite, zakaj je ta Mastodon strežnik poseben in karkoli pomembnega. Lahko uporabite HTML oznake, zlasti <code><a></code> in <code><em></code>. + title: Opis strežnika + site_description_extended: + desc_html: Dober kraj za vaÅ¡ kodeks ravnanja, pravila, smernice in druge stvari, ki loÄujejo vaÅ¡ strežnik. Lahko uporabite oznake HTML + title: RazÅ¡irjene informacije po meri + site_short_description: + desc_html: Prikazano v stranski vrstici in metaoznakah. V enem odstavku opiÅ¡ite, kaj je Mastodon in kaj naredi ta strežnik poseben. + title: Kratek opis strežnika + site_terms: + desc_html: Lahko napiÅ¡ete svojo pravilnik o zasebnosti, pogoje storitve ali druge pravne dokumente. Lahko uporabite oznake HTML + title: Pogoji storitve po meri + site_title: Ime strežnika + spam_check_enabled: + desc_html: Mastodon lahko samodejno utiÅ¡a in samodejno prijavi raÄune, ki temeljijo na ukrepih, kot je odkrivanje raÄunov, ki poÅ¡iljajo ponavljajoÄa se neželena sporoÄila. Lahko pride do zmot. + title: Anti-spam + thumbnail: + desc_html: Uporablja se za predogled prek OpenGrapha in API-ja. PriporoÄamo 1200x630px + title: SliÄica strežnika + timeline_preview: + desc_html: Prikaži javno Äasovnico na ciljni strani + title: Predogled Äasovnice + title: Nastavitve strani + statuses: + back_to_account: Nazaj na stran raÄuna + batch: + delete: IzbriÅ¡i + nsfw_off: OznaÄi, da ni obÄutljivo + nsfw_on: OznaÄi, kot obÄutljivo + failed_to_execute: Ni bilo mogoÄe izvesti + media: + title: Mediji + no_media: Ni medijev + no_status_selected: Nobeno stanje ni bilo spremenjeno, ker ni bilo izbrano nobeno + title: Stanja raÄuna + with_media: Z mediji + tags: + title: KljuÄniki + title: Upravljanje + warning_presets: + add_new: Dodaj novo + delete: IzbriÅ¡i + edit: Uredi + edit_preset: Uredi prednastavitev opozoril + title: Upravljaj prednastavitev opozoril + admin_mailer: + new_pending_account: + body: Podrobnosti o novem raÄunu so navedene spodaj. To aplikacijo lahko odobrite ali zavrnete. + subject: Nov raÄun za pregled na %{instance} (%{username}) + new_report: + body: "%{reporter} je prijavil %{target}" + body_remote: Nekdo iz %{domain} je prijavil %{target} + subject: Nove prijave za %{instance} (#%{id}) + appearance: + advanced_web_interface: Napredni spletni vmesnik + advanced_web_interface_hint: 'ÄŒe želite uporabiti celotno Å¡irino zaslona, vam napredni spletni vmesnik omogoÄa, da si nastavite veÄ razliÄnih stolpcev in da si hkrati ogledate toliko informacij, kot želite: domaÄo stran, obvestila, združeno Äasovnico, poljubno Å¡tevilo seznamov in kljuÄnikov.' + animations_and_accessibility: Animacije in dostopnost + confirmation_dialogs: Potrditvena okna + sensitive_content: ObÄutljiva vsebina + application_mailer: + notification_preferences: Spremenite e-poÅ¡tne nastavitve + salutation: "%{name}," + settings: 'Spremenite e-poÅ¡tne nastavitve: %{link}' + view: 'Pogled:' + view_profile: Ogled profila + view_status: Ogled stanja + applications: + created: Aplikacija je bila uspeÅ¡no ustvarjena + destroyed: Aplikacija je bila uspeÅ¡no izbrisana + invalid_url: Navedeni URL je neveljaven + regenerate_token: Obnovite dostopni žeton + token_regenerated: Dostopni žeton je bil uspeÅ¡no regeneriran + warning: Bodite zelo previdni s temi podatki. Nikoli jih ne delite z nikomer! + your_token: VaÅ¡ dostopni žeton + auth: + apply_for_account: Zahtevajte povabilo + change_password: Geslo + checkbox_agreement_html: Strinjam se s <a href="%{rules_path}" target="_blank">pravili strežnika</a> in <a href="%{terms_path}" target="_blank">pogoji storitve</a> + checkbox_agreement_without_rules_html: Strinjam se s <a href="%{terms_path}" target="_blank">pogoji storitve</a> + delete_account: IzbriÅ¡i raÄun + delete_account_html: ÄŒe želite izbrisati svoj raÄun, lahko nadaljujete <a href="%{path}">tukaj</a>. Prosili vas bomo za potrditev. + didnt_get_confirmation: Niste prejeli navodil za potrditev? + forgot_password: Ste pozabili svoje geslo? + invalid_reset_password_token: Žeton za ponastavitev gesla je neveljaven ali je potekel. Zahtevajte novo. + login: Prijava + logout: Odjava + migrate_account: Premakni se na drug raÄun + migrate_account_html: ÄŒe želite ta raÄun preusmeriti na drugega, ga lahko <a href="%{path}">nastavite tukaj</a>. + or_log_in_with: Ali se prijavite z + providers: + cas: CAS + saml: SAML + register: Vpis + registration_closed: "%{instance} ne sprejema novih Älanov" + resend_confirmation: Ponovno poÅ¡lji navodila za potrditev + reset_password: Ponastavi geslo + security: Varnost + set_new_password: Nastavi novo geslo + setup: + email_below_hint_html: ÄŒe spodnji e-poÅ¡tni naslov ni pravilen, ga lahko spremenite tukaj in prejmete novo potrditveno e-poÅ¡to. + email_settings_hint_html: Potrditvena e-poÅ¡ta je bila poslana na %{email}. ÄŒe ta e-poÅ¡tni naslov ni pravilen, ga lahko spremenite v nastavitvah raÄuna. + title: Nastavitev + status: + account_status: Stanje raÄuna + confirming: ÄŒakanje na potrditev e-poÅ¡te. + pending: NaÅ¡e osebje preverja vaÅ¡o prijavo. To lahko traja nekaj Äasa. ÄŒe bo vaÅ¡a prijava odobrena, boste prejeli e-poÅ¡to. + trouble_logging_in: Težave pri prijavi? + authorize_follow: + already_following: Temu raÄunu že sledite + error: Na žalost je priÅ¡lo do napake pri iskanju oddaljenega raÄuna + follow: Sledi + follow_request: 'ProÅ¡njo za sledenje se poslali:' + following: 'Uspeh! Zdaj sledite:' + post_follow: + close: Lahko pa tudi zaprete to okno. + return: Prikaži uporabnikov profil + web: Pojdi na splet + title: Sledi %{acct} + datetime: + distance_in_words: + about_x_hours: "%{count}h" + about_x_months: "%{count}mo" + about_x_years: "%{count}y" + almost_x_years: "%{count}y" + half_a_minute: Pravkar + less_than_x_minutes: "%{count}m" + less_than_x_seconds: Pravkar + over_x_years: "%{count}y" + x_days: "%{count}d" + x_minutes: "%{count}m" + x_months: "%{count}mo" + x_seconds: "%{count}s" + deletes: + confirm_password: Vnesite svoje trenutno geslo, da potrdite svojo identiteto + proceed: IzbriÅ¡i raÄun + success_msg: VaÅ¡ raÄun je bil uspeÅ¡no izbrisan + directories: + directory: Imenik profilov + explanation: Odkrijte uporabnike glede na njihove interese + explore_mastodon: RaziÅ¡Äi %{title} errors: - '403': You don't have permission to view this page. - '404': The page you are looking for isn't here. - '410': The page you were looking for doesn't exist here anymore. - '422': - '429': Throttled - '500': + '400': The request you submitted was invalid or malformed. + '403': Nimate dovoljenja za ogled te strani. + '404': Iskana stran ne obstaja. + '406': This page is not available in the requested format. + '410': Iskana stran ne obstaja veÄ. + '422': + content: Varnostno preverjanje ni uspelo. Ali blokirate piÅ¡kotke? + title: Varnostno preverjanje je spodletelo + '429': Omejeno + '500': + content: Žal nam je, toda na naÅ¡em koncu je priÅ¡lo do napake. + title: Ta stran ni pravilna + '503': The page could not be served due to a temporary server failure. + noscript_html: ÄŒe želite uporabljati spletno aplikacijo Mastodon, omogoÄite JavaScript. Druga možnost je, da za svojo platformo poskusite eno od <a href="%{apps_path}">lastnih aplikacij</a> za Mastodon. + existing_username_validator: + not_found: s tem uporabniÅ¡kim imenom ni bilo mogoÄe najti lokalnega uporabnika + not_found_multiple: ni bilo mogoÄe najti %{usernames} + exports: + archive_takeout: + date: Datum + download: Prenesi svoj arhiv + hint_html: Zahtevate lahko arhiv vaÅ¡ih <strong>tutov in naloženih medijev</strong>. Izvoženi podatki bodo v formatu ActivityPub, ki ga bo mogoÄe brati s katerokoli skladno programsko opremo. Arhiv lahko zahtevate vsakih 7 dni. + in_progress: Prevajanje arhiva... + request: Zahtevajte svoj arhiv + size: Velikost + blocks: Blokirate + csv: CSV + domain_blocks: Bloki domene + follows: Sledite + lists: Seznami + mutes: UtiÅ¡ate + storage: Shranjeni mediji + featured_tags: + add_new: Dodaj novo + errors: + limit: Ste že dodali najveÄje Å¡tevilo kljuÄnikov + filters: + contexts: + home: DomaÄa Äasovnica + notifications: Obvestila + public: Javne Äasovnice + thread: Pogovori + edit: + title: Uredite filter + errors: + invalid_context: Ne vsebuje nobenega ali vsebuje neveljaven kontekst + invalid_irreversible: Nepovratno filtriranje deluje le v kontekstu doma ali obvestil + index: + delete: IzbriÅ¡i + title: Filtri + new: + title: Dodaj nov filter + footer: + developers: Razvijalci + more: VeÄ… + resources: Viri + generic: + all: Vse + changes_saved_msg: Spremembe so uspeÅ¡no shranjene! + copy: Kopiraj + order_by: Razvrsti po + save_changes: Shrani spremembe + validation_errors: + few: Nekaj Å¡e ni Äisto v redu! Spodaj si oglejte %{count} napake + one: Nekaj Å¡e ni Äisto v redu! Spodaj si oglejte napako + other: Nekaj Å¡e ni Äisto v redu! Spodaj si oglejte %{count} napak + two: Nekaj Å¡e ni Äisto v redu! Spodaj si oglejte %{count} napaki + html_validator: + invalid_markup: 'vsebuje neveljavno oznako HTML: %{error}' + identity_proofs: + active: Dejaven + authorize: Da, odobri + authorize_connection_prompt: Odobrite to kriptografsko povezavo? + errors: + failed: Kriptografska povezava ni uspela. Poskusite znova od %{provider}. + keybase: + invalid_token: Žetoni Keybase so algoritem podpisov in morajo biti sestavljeni iz 66 heksadecimalnih znakov + verification_failed: Keybase ne prepozna tega žetona kot podpis uporabnika %{kb_username}. Poskusite znova s Keybase-om. + wrong_user: Dokler se prijavite kot %{current}, ni mogoÄe ustvariti dokazila za %{proving}. Prijavite se kot %{proving} in poskusite znova. + explanation_html: Tukaj lahko kriptografsko povežete druge identitete, na primer profil Keybase. To omogoÄa drugim, da vam poÅ¡ljejo Å¡ifrirana sporoÄila in zaupate vsebino, ki ste jo poslali. + i_am_html: Jaz sem %{username} na %{service}. + identity: Identiteta + inactive: Neaktiven + publicize_checkbox: 'In to tutnite:' + publicize_toot: 'Dokazano je! Jaz sem %{username} na %{service}: %{url}' + status: Stanje preverjanja + view_proof: Oglejte si dokaz + imports: + modes: + merge: Združi + merge_long: Ohrani obstojeÄe zapise in dodaj nove + overwrite: PrepiÅ¡i + overwrite_long: Zamenjaj trenutne zapise z novimi + preface: Podatke, ki ste jih izvozili iz drugega strežnika, lahko uvozite. Na primer seznam oseb, ki jih spremljate ali blokirate. + success: VaÅ¡i podatki so bili uspeÅ¡no naloženi in bodo zdaj pravoÄasno obdelani + types: + blocking: Seznam blokiranih + domain_blocking: Seznam blokiranih domen + following: Seznam uporabnikov, katerim sledite + muting: Seznam utiÅ¡anih + upload: PoÅ¡lji + in_memoriam_html: V spomin. invites: + delete: OnemogoÄi + expired: Poteklo expires_in: - '1800': 30 minutes - '21600': 6 hours - '3600': 1 hour - '43200': 12 hours - '604800': 1 week - '86400': 1 day + '1800': 30 minut + '21600': 6 ur + '3600': 1 ura + '43200': 12 ur + '604800': 1 teden + '86400': 1 dan + expires_in_prompt: Nikoli + generate: Ustvari + invited_by: 'Povabil/a vas je:' + max_uses: + few: "%{count} uporabe" + one: 1 uporaba + other: "%{count} uporab" + two: "%{count} uporabi" + max_uses_prompt: Brez omejitve + prompt: Ustvarite in delite povezave z drugimi, da omogoÄite dostop do tega strežnika + table: + expires_at: PoteÄe + uses: Uporabe + title: Povabite ljudi + lists: + errors: + limit: Dosegli ste najveÄje Å¡tevilo seznamov + media_attachments: + validations: + images_and_video: Videoposnetka ni mogoÄe priložiti stanju, ki že vsebuje slike + too_many: Ni možno priložiti veÄ kot 4 datoteke + migrations: + acct: username@domain novega raÄuna + moderation: + title: Moderiranje + notification_mailer: + digest: + action: Prikaži vsa obvestila + body: Tukaj je kratek povzetek sporoÄil, ki ste jih zamudili od vaÅ¡ega zadnjega obiska v %{since} + mention: "%{name} vas je omenil/a v:" + new_followers_summary: + few: Prav tako ste pridobili %{count} nove sledilce, ko ste bili odsotni! Juhu! + one: Prav tako ste pridobili enega novega sledilca, ko ste bili odsotni! Juhu! + other: Prav tako ste pridobili %{count} novih sledilcev, ko ste bili odsotni! Juhu! + two: Prav tako ste pridobili %{count} nova sledilca, ko ste bili odsotni! Juhu! + subject: + few: "%{count} nova obvestila od vaÅ¡ega zadnjega obiska \U0001F418" + one: "1 novo obvestilo od vaÅ¡ega zadnjega obiska \U0001F418" + other: "%{count} novih obvestil od vaÅ¡ega zadnjega obiska \U0001F418" + two: "%{count} novi obvestili od vaÅ¡ega zadnjega obiska \U0001F418" + title: V vaÅ¡i odsotnosti... + favourite: + body: "%{name} je vzljubil/a vaÅ¡e stanje:" + subject: "%{name} je vzljubil/a vaÅ¡e stanje" + title: Novo priljubljeno + follow: + body: "%{name} vam sedaj sledi!" + subject: "%{name} vam sedaj sledi" + title: Novi sledilec + follow_request: + action: Upravljajte s proÅ¡njami za sledenje + body: "%{name} vas je prosil/a za sledenje" + subject: 'ÄŒakajoÄi sledilec/ka: %{name}' + title: Nova proÅ¡nja za sledenje + mention: + action: Odgovori + body: "%{name} vas je omenil/a v:" + subject: "%{name} vas je omenil/a" + title: Nova omemba + reblog: + body: "%{name} je spodbudil/a vaÅ¡e stanje:" + subject: "%{name} je spodbudil/a vaÅ¡e stanje" + title: Nova spodbuda + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T + pagination: + newer: NovejÅ¡e + next: Naprej + older: StarejÅ¡e + prev: Nazaj + truncate: "…" + polls: + errors: + already_voted: Na tej anketi ste že glasovali + duplicate_options: vsebuje podvojene elemente + duration_too_long: je predaleÄ v prihodnosti + duration_too_short: je prezgodaj + expired: Glasovanje se je že zakljuÄilo + over_character_limit: ne more biti daljÅ¡e od %{max} znakov + too_few_options: mora imeti veÄ kot en element + too_many_options: ne more vsebovati veÄ kot %{max} elementov + preferences: + other: Ostalo + posting_defaults: Privzete nastavitev objavljanja + public_timelines: Javne Äasovnice + relationships: + activity: Dejavnost raÄuna + dormant: Skrit + last_active: Zadnja dejavnost + most_recent: NajnovejÅ¡a + moved: Prestavljeno + mutual: Vzajemna + primary: Primarna + relationship: Razmerje + remove_selected_domains: Odstrani vse sledilce iz izbranih domen + remove_selected_followers: Odstrani izbrane sledilce + remove_selected_follows: Prenehaj slediti izbranim uporabnikom + status: Stanje raÄuna + remote_follow: + acct: Vnesite uporabniÅ¡ko_ime@domena, iz katerega želite delovati + missing_resource: Za vaÅ¡ raÄun ni bilo mogoÄe najti zahtevanega URL-ja za preusmeritev + no_account_html: Å e nimate raÄuna? Tukaj se lahko <a href='%{sign_up_path}' target='_blank'>prijavite</a> + proceed: Nadaljujte + prompt: 'Sledili boste:' + reason_html: "<strong>Zakaj je ta korak potreben?</strong> <code>%{instance}</code> morda ni strežnik, kjer ste registrirani, zato vas moramo najprej preusmeriti na domaÄi strežnik." + remote_interaction: + favourite: + proceed: Nadaljuj s priljubljenim + prompt: 'Ali želite vzljubiti ta tut:' + reblog: + proceed: Nadaljuj s spodbudo + prompt: 'Ali želite spodbuditi ta tut:' + reply: + proceed: Nadaljuj z odgovorom + prompt: 'Ali želite odgovoriti na ta tut:' + scheduled_statuses: + over_daily_limit: Za ta dan ste presegli omejitev %{limit} naÄrtovanih tutov + over_total_limit: Presegli ste omejitev %{limit} naÄrtovanih tutov + too_soon: NaÄrtovani datum mora biti v prihodnosti + sessions: + activity: Zadnja dejavnost + browser: Brskalnik + browsers: + alipay: Alipay + blackberry: BlackBerry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox + generic: Neznan brskalnik + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UC Browser + weibo: Weibo + current_session: Trenutna seja + description: "%{browser} na %{platform}" + explanation: To so spletni brskalniki, ki so trenutno prijavljeni v vaÅ¡ Mastodon raÄun. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: BlackBerry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: neznana platforma + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + revoke: PrekliÄi + revoke_success: Seja je bila uspeÅ¡no preklicana + title: Seje + settings: + account: RaÄun + account_settings: Nastavitve raÄuna + appearance: Videz + authorized_apps: PooblaÅ¡Äene aplikacije + back: Nazaj na Mastodon + delete: Brisanje raÄuna + development: Razvoj + edit_profile: Uredi profil + export: Izvoz podatkov + featured_tags: VkljuÄeni kljuÄniki + identity_proofs: Dokazi o identiteti + import: Uvozi + import_and_export: Uvoz in izvoz + migrate: Selitev raÄuna + notifications: Obvestila + preferences: Nastavitve + profile: Profil + relationships: Sledenja in sledilci + two_factor_authentication: Dvofaktorsko overjanje statuses: + attached: + description: 'Priloženo: %{attached}' + image: + few: "%{count} slike" + one: "%{count} slika" + other: "%{count} slik" + two: "%{count} sliki" + video: + few: "%{count} video posnetki" + one: "%{count} video posnetek" + other: "%{count} video posnetkov" + two: "%{count} video posnetka" + boosted_from_html: Spodbujeno iz %{acct_link} + content_warning: 'Opozorilo o vsebini: %{warning}' + disallowed_hashtags: + few: 'vsebuje nedovoljene kljuÄnike: %{tags}' + one: 'vsebuje nedovoljeni kljuÄnik: %{tags}' + other: 'vsebuje nedovoljenih kljuÄnikov: %{tags}' + two: 'vsebuje nedovoljena kljuÄnika: %{tags}' + language_detection: Samodejno zaznaj jezik + open_in_web: Odpri na spletu + over_character_limit: omejitev %{max} znakov je presežena pin_errors: + limit: Pripeli ste najveÄje Å¡tevilo tutov ownership: Trob nekoga drugega ne more biti pripet private: Nejavnega troba ni mogoÄe pripeti + reblog: Spodbuda ne more biti pripeta + poll: + total_votes: + few: "%{count} glasovi" + one: "%{count} glas" + other: "%{count} glasov" + two: "%{count} glasova" + vote: Glasuj + show_more: Pokaži veÄ + sign_in_to_participate: Prijavite se, Äe želite sodelovati v pogovoru + title: '%{name}: "%{quote}"' + visibilities: + private: Samo sledilci + private_long: Prikaži samo sledilcem + public: Javno + public_long: Vsi lahko vidijo + unlisted: Ni prikazano + unlisted_long: Vsi lahko vidijo, vendar ni objavljeno na javnih Äasovnicah stream_entries: pinned: Pripet trob + reblogged: spodbujen + sensitive_content: ObÄutljiva vsebina + terms: + body_html: | + <h2>Pravilnik o zasebnosti</h2> + <h3 id="collect">Katere informacije zbiramo?</h3> + + <ul> + <li><em>Osnovni podatki o raÄunu</em>: ÄŒe se registrirate na tem strežniku, boste morda morali vnesti uporabniÅ¡ko ime, e-poÅ¡tni naslov in geslo. Vnesete lahko tudi dodatne informacije o profilu, na primer prikazno ime in biografijo, ter naložite sliko profila in sliko glave. UporabniÅ¡ko ime, prikazno ime, biografija, slika profila in slika glave so vedno javno dostopni.</li> + <li><em>Objave, sledenja in druge javne informacije</em>: Seznam oseb, ki jim sledite, je javno dostopen, enako velja za vaÅ¡e sledilce. Ko poÅ¡ljete sporoÄilo, sta datum in Äas shranjena, kot tudi aplikacija, iz katere ste poslali sporoÄilo. SporoÄila lahko vsebujejo medijske priloge, kot so slike in video posnetki. Javne in neprikazane objave so javno dostopne. Ko v profilu vkljuÄite objavo, je to tudi javno dostopna informacija. VaÅ¡e objave, ki so dostavljene vaÅ¡im sledilcem, so vÄasih dostavljeni na razliÄne strežnike, kjer se kopije objav tudi shranijo. Ko izbriÅ¡ete objave, se to prav tako dostavi vaÅ¡im sledilcem. Spodbujanje in vzljubitev drugih objav sta veno javni.</li> + <li><em>Neposredne objave in objave samo za sledilce</em>: Vse objave so shranjene in obdelane na strežniku. Objave samo za sledilce se dostavijo vaÅ¡im sledilcem in uporabnikom, ki so v njih omenjeni. Neposredne objave se posredujejo samo uporabnikom, ki so v njih omenjeni. V nekaterih primerih so dostavljeni na razliÄne strežnike, kopije pa se shranijo tam. V dobri veri si prizadevamo omejiti dostop do teh objav samo pooblaÅ¡Äenim osebam, vendar drugi strežniki to morda ne bodo storili. Zato je pomembno, da pregledate strežnike, na katerih so sledilci. V nastavitvah lahko preklapljate med možnostmi za odobritev in zavrnitev novih sledilcev. <em> Ne pozabite, da lahko operaterji strežnika in kateri koli prejemni strežnik takÅ¡na sporoÄila pregledajo </em> in da jih lahko prejemniki posnamejo, kopirajo ali drugaÄe ponovno delijo. <em> Ne poÅ¡iljajte nevarnih informacij skozi Mastodon. </em> </li> + <li><em>IP-ji in drugi metapodatki</em>: Ko se prijavite, zabeležimo naslov IP, s katerega se prijavljate, in ime aplikacije brskalnika. V nastavitvah so za pregled in preklic na voljo vse prijavljene seje. Zadnji uporabljeni IP naslov je shranjen do 12 mesecev. Prav tako lahko obdržimo dnevnike strežnikov, ki vsebujejo IP naslov vsake zahteve na naÅ¡ strežnik. </li> + </ul> + + <hr class="spacer" /> + + <h3 id="use">Za kaj uporabljamo vaÅ¡e podatke?</h3> + + <p>Vse informacije, ki jih zbiramo od vas, so lahko uporabljene na naslednje naÄine:</p> + + <ul> + <li>Za zagotavljanje osrednje funkcionalnosti Mastodona. Komunicirate lahko z vsebino drugih oseb in objavljate lastno vsebino, ko ste prijavljeni. Na primer, lahko spremljate druge osebe in si ogledate njihove kombinirane objave v svoji prilagojeni domaÄi Äasovnici.</li> + <li>Za pomoÄ pri moderiranju skupnosti, na primer primerjavo vaÅ¡ega naslova IP naslova z drugimi znanimi, za izobÄitev izmikanja ali drugih krÅ¡itev.</li> + <li>E-poÅ¡tni naslov, ki ga navedete, se lahko uporabi za poÅ¡iljanje informacij, obvestil o drugih osebah, ki komunicirajo z vaÅ¡o vsebino ali poÅ¡iljanju sporoÄil, ter za odzivanje na poizvedbe in/ali druge zahteve ali vpraÅ¡anja.</li> + </ul> + + <hr class="spacer" /> + + <h3 id="protect">Kako zaÅ¡Äitimo vaÅ¡e podatke?</h3> + + <p>Za ohranitev varnosti vaÅ¡ih osebnih podatkov izvajamo razliÄne varnostne ukrepe, ko vnaÅ¡ate, poÅ¡iljate ali dostopate do vaÅ¡ih osebnih podatkov. Med drugim je seja brskalnika, pa tudi promet med vaÅ¡imi aplikacijami in API-jem zaÅ¡Äitena s SSL-jem, geslo pa je zgoÅ¡Äeno z uporabo moÄnega enosmernega algoritma. ÄŒe želite omogoÄiti varen dostop do raÄuna, lahko omogoÄite dvofaktorsko preverjanje pristnosti.</p> + + <hr class="spacer" /> + + <h3 id="data-retention">KakÅ¡na je naÅ¡a politika hrambe podatkov?</h3> + + <p>Prizadevali si bomo za:</p> + + <ul> + <li>Shranjevanje dnevnike strežnikov, ki vsebujejo naslov IP vseh zahtev za ta strežnik, Äe so hranjeni, najveÄ 90 dni.</li> + <li>Obdržiitev naslovov IP, povezane z registriranimi uporabniki, ne veÄ kot 12 mesecev.</li> + </ul> + + <p>Lahko zahtevate in prenesete arhiv vaÅ¡e vsebine, vkljuÄno z objavami, predstavnostnimi prilogami, sliko profila in sliko glave.</p> + + <p>RaÄun lahko kadar koli nepovratno izbriÅ¡ete.</p> + + <hr class="spacer"/> + + <h3 id="cookies">Ali uporabljamo piÅ¡kotke?</h3> + + <p>Da. PiÅ¡kotki so majhne datoteke, ki jih spletno mesto ali njegov ponudnik storitev prenese na trdi disk vaÅ¡ega raÄunalnika prek spletnega brskalnika (Äe dovolite). Ti piÅ¡kotki omogoÄajo, da spletno mesto prepozna vaÅ¡ brskalnik in ga, Äe imate registriran raÄun, povežete z vaÅ¡im registriranim raÄunom.</p> + + <p>PiÅ¡kotke uporabljamo za razumevanje in shranjevanje vaÅ¡ih nastavitev za prihodnje obiske.</p> + + <hr class="spacer" /> + + <h3 id="disclose">Ali razkrivamo informacije zunanjim strankam?</h3> + + <p>VaÅ¡ih osebnih podatkov ne prodajamo, preprodajamo ali kako drugaÄe posredujemo zunanjim osebam. To ne vkljuÄuje zaupanja vrednih tretjih oseb, ki nam pomagajo pri upravljanju naÅ¡e spletne strani, vodenju naÅ¡ega poslovanja ali storitev, Äe se te strani strinjajo, da bodo te informacije zaupne. VaÅ¡e podatke lahko tudi objavimo, Äe menimo, da je objava ustrezna in v skladu z zakonom, uveljavlja pravilnike o spletnih mestih ali Å¡Äiti naÅ¡e ali druge pravice, lastnino ali varnost.</p> + + <p>VaÅ¡e javne vsebine lahko prenesejo drugi strežniki v omrežju. VaÅ¡e objave in objave samo za sledilce so dostavljene na strežnike, na katerih prebivajo vaÅ¡i sledilci, in neposredna sporoÄila so dostavljena na strežnike prejemnikov, Äe so ti sledilci ali prejemniki na drugem strežniku.</p> + + <p>Ko odobrite aplikacijo za uporabo vaÅ¡ega raÄuna, lahko glede na obseg dovoljenj, ki jih odobravate, dostopa do vaÅ¡ih javnih podatkov o profilu, seznama osebam, ki jim sledite, vaÅ¡ih sledilcev, seznamov, vseh vaÅ¡ih objav in priljubljenih. Aplikacije ne morejo nikoli dostopati do vaÅ¡ega e-poÅ¡tnega naslova ali gesla.</p> + + <hr class="spacer" /> + + <h3 id="children">Uporaba strani s strani otrok</h3> + + <p>ÄŒe je ta strežnik v EU ali EEA: NaÅ¡e spletno mesto, izdelki in storitve so namenjeni ljudem, ki so stari vsaj 16 let. ÄŒe ste mlajÅ¡i od 16 let, po zahtevah GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) ne uporabljajte tega spletnega mesta.</p> + + <p>ÄŒe je ta strežnik v ZDA NaÅ¡e spletno mesto, izdelki in storitve so namenjeni ljudem, ki so stari vsaj 13 let. ÄŒe ste mlajÅ¡i od 13 let, po zahtevah COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) ne uporabljajte tega spletnega mesta.</p> + + <p>ÄŒe je ta strežnik v drugi jurisdikciji, so lahko zakonske zahteve drugaÄne.</p> + + <hr class="spacer" /> + + <h3 id="changes">Spremembe naÅ¡ega pravilnika o zasebnosti</h3> + + <p>ÄŒe se odloÄimo za spremembo naÅ¡ega pravilnika o zasebnosti, bomo te spremembe objavili na tej strani.</p> + + <p>Ta dokument je CC-BY-SA. Zadnja posodobitev je bila 7. marca 2018.</p> + + <p>Prvotno je bila prilagojena v skladu s <a href="https://github.com/discourse/discourse">pravilnikom o zasebnosti diskurza</a>.</p> + title: "%{instance} Pogoji storitve in pravilnik o zasebnosti" + themes: + contrast: Mastodon (Visok kontrast) + default: Mastodon (Temna) + mastodon-light: Mastodon (Svetla) + time: + formats: + default: "%b %d, %Y, %H:%M" + month: "%b %Y" + two_factor_authentication: + code_hint: Za potrditev vnesite kodo, ki jo je ustvarila aplikacija za preverjanje pristnosti + description_html: ÄŒe omogoÄite <strong>dvofaktorsko preverjanje pristnosti</strong>, boste za prijavo morali imeti svoj telefon, s katerim boste ustvarili žetone za vstop. + disable: OnemogoÄi + enable: OmogoÄi + enabled: Dvofaktorsko preverjanje pristnosti je omogoÄeno + enabled_success: Dvofaktorsko preverjanje pristnosti je uspeÅ¡no omogoÄeno + generate_recovery_codes: Ustvari kode za obnovitev + instructions_html: "<strong>Skenirajte QR kodo z Google Authenticator ali s podobno aplikacijo TOTP</strong>. Od zdaj naprej bo ta aplikacija ustvarjala žetone, ki jih boste morali vnesti ob prijavi." + lost_recovery_codes: Obnovitvene kode vam omogoÄajo, da ponovno pridobite dostop do svojega raÄuna, Äe izgubite telefon. ÄŒe ste izgubili obnovitvene kode, jih lahko obnovite tukaj. VaÅ¡e stare obnovitvene kode bodo neveljavne. + manual_instructions: 'ÄŒe ne morete skenirati QR kode in jo morate vnesti roÄno, je tu skrivnost v tekstovni obliki:' + recovery_codes: Varnostna kopija obnovitvenih kod + recovery_codes_regenerated: Obnovitvene kode so bile uspeÅ¡no regenerirane + recovery_instructions_html: ÄŒe kdaj izgubite dostop do telefona, lahko uporabite eno od spodnjih obnovitvenih kod, da ponovno pridobite dostop do svojega raÄuna. <strong>Shranite obnovitvene kode</strong>. Lahko jih natisnete in shranite z drugimi pomembnimi dokumenti. + setup: Nastavi + wrong_code: Vnesena koda je bila neveljavna! Ali sta Äas strežnika in Äas naprave pravilna? + user_mailer: + backup_ready: + explanation: Zahtevali ste popolno varnostno kopijo raÄuna Mastodon. Zdaj je pripravljen za prenos! + subject: VaÅ¡ arhiv je pripravljen za prenos + title: Prevzem arhiva + warning: + explanation: + disable: Medtem ko je vaÅ¡ raÄun zamrznjen, podatki vaÅ¡ega raÄuna ostanejo nedotaknjeni, vendar ne morete izvajati nobenih dejanj, dokler ga ne odklenete. + silence: Medtem ko je vaÅ¡ raÄun omejen, bodo na tem strežniku videli vaÅ¡e tute samo osebe, ki vam že sledijo in morda boste izkljuÄeni iz razliÄnih javnih seznamov. Vendar vam lahko Å¡e vedno roÄno sledijo drugi. + suspend: VaÅ¡ raÄun je bil zaÄasno ukinjen, vsi vaÅ¡i tuti in naložene predstavnostne datoteke so bili nepovratno odstranjeni s tega strežnika in strežnikov, kjer ste imeli sledilce. + review_server_policies: Preglejte pravilnike strežnika + subject: + disable: VaÅ¡ raÄun %{acct} je zamrznjen + none: Opozorila za %{acct} + silence: VaÅ¡ raÄun %{acct} je omejen + suspend: VaÅ¡ raÄun %{acct} je suspendiran + title: + disable: RaÄun je zamrznjen + none: Opozorilo + silence: RaÄun je omejen + suspend: RaÄun je suspendiran + welcome: + edit_profile_action: Nastavitve profila + edit_profile_step: Profil lahko prilagodite tako, da naložite podobo, glavo, spremenite prikazno ime in drugo. ÄŒe želite pregledati nove sledilce, preden jim dovolite sledenje, lahko zaklenete svoj raÄun. + explanation: Tu je nekaj nasvetov za zaÄetek + final_action: ZaÄnite objavljati + final_step: 'ZaÄnite objavljati! Tudi brez sledilcev bodo vaÅ¡a javna sporoÄila videli drugi, na primer na lokalni Äasovnici in v kljuÄnikih. Morda se želite predstaviti s kljuÄnikom #introductions.' + full_handle: VaÅ¡a polna roÄica + full_handle_hint: To bi povedali svojim prijateljem, da vam lahko poÅ¡ljejo sporoÄila ali vam sledijo iz drugega strežnika. + review_preferences_action: Spremenite nastavitve + review_preferences_step: Poskrbite, da doloÄite svoje nastavitve, na primer, katera e-poÅ¡tna sporoÄila želite prejemati ali katere privzete ravni zasebnosti bodo imele vaÅ¡e objave. ÄŒe nimate potovalne slabosti, lahko omogoÄite samodejno predvajanje GIF-ov. + subject: DobrodoÅ¡li na Mastodon + tip_federated_timeline: Združena Äasovnica je pogled na mrežo Mastodona. VkljuÄuje pa samo ljudi, na katere so naroÄeni vaÅ¡i sosedje, zato ni popolna. + tip_following: Privzeto sledite skrbnikom strežnika. ÄŒe želite najti veÄ zanimivih ljudi, preverite lokalne in združene Äasovnice. + tip_local_timeline: Lokalna Äasovnica je strežniÅ¡ki pogled ljudi na %{instance}. To so vaÅ¡i neposredni sosedje! + tip_mobile_webapp: ÄŒe vam mobilni brskalnik ponuja, da dodate Mastodon na domaÄi zaslon, lahko prejmete potisna obvestila. Deluje kot lastna aplikacija na veÄ naÄinov! + tips: Nasveti + title: DobrodoÅ¡li, %{name}! + users: + follow_limit_reached: Ne morete spremljati veÄ kot %{limit} ljudi + invalid_email: E-poÅ¡tni naslov je napaÄen + invalid_otp_token: Neveljavna dvofaktorska koda + otp_lost_help_html: ÄŒe ste izgubili dostop do obeh, stopite v stik z %{email} + seamless_external_login: Prijavljeni ste prek zunanje storitve, tako da nastavitve gesla in e-poÅ¡te niso na voljo. + signed_in_as: 'Vpisani kot:' + verification: + explanation_html: '<strong>V metapodatkih svojega profila se lahko potrdite kot lastnik povezav</strong>. Za to mora povezano spletno mesto vsebovati povezavo do vaÅ¡ega Mastodon profila. Povezava <strong>mora</strong> imeti atribut <code>el="me"</code>. Vsebina besedila povezave ni pomembna. Tukaj je primer:' + verification: Potrditev diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 6cab033321008a32681f8dcb10d0b8dc67bda20b..ac811b0b0e73f8cf531c1272e8441c9330a72566 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -9,10 +9,6 @@ sq: contact: Kontakt contact_missing: I parregulluar documentation: Dokumentim - extended_description_html: | - <h3>Një vend i mirë për rregulla</h3> - <p>Përshkrimi i zgjeruar s’është sajuar ende.</p> - generic_description: "%{domain} është një shërbyes te rrjeti" hosted_on: Mastodon i strehuar në %{domain} learn_more: Mësoni më tepër privacy_policy: Rregulla privatësie @@ -423,19 +419,8 @@ sq: no_status_selected: S’u ndryshua ndonjë gjendje, ngaqë s’u përzgjodh ndonjë e tillë title: Gjendje llogarish with_media: Me media - subscriptions: - callback_url: URL Callback-u - confirmed: U ripohua - expires_in: Skadon më - last_delivery: Dorëzimi e fundit - topic: Temë tags: - accounts: Llogari - hidden: Fshehur - hide: Fshihe prej drejtorie title: Hashtage - unhide: Shfaqe në drejtori - visible: E dukshme title: Administrim warning_presets: add_new: Shtoni të ri @@ -464,7 +449,6 @@ sq: your_token: Token-i juaj për hyrje auth: change_password: Fjalëkalim - confirm_email: Ripohoni email-in delete_account: Fshije llogarinë delete_account_html: Nëse dëshironi të fshihni llogarinë tuaj, mund <a href="%{path}">ta bëni që këtu</a>. Do t’ju kërkohet ta ripohoni. didnt_get_confirmation: S’morët udhëzime ripohimi? @@ -502,23 +486,18 @@ sq: over_x_years: "%{count}v" x_months: "%{count}mj" deletes: - bad_password_msg: Provë e bukur, trimosha! Fjalëkalim i pasaktë confirm_password: Jepni fjalëkalimin tuaj të tanishëm që të verifikohet identiteti juaj - description_html: Kjo të heqë <strong>në mënyrë të përhershme, të pakthyeshme</strong> lëndë nga llogaria juaj dhe do ta çaktivizojë atë. Emri juaj i përdoruesit do të mbetet i rezervuar për të shmangur sozi të ardhme. proceed: Fshini llogarinë success_msg: Llogaria juaj u fshi me sukses - warning_html: Garantohet vetëm fshirja e lëndës prej këtij shërbyesi të veçantë. Lënda që është ndarë gjerësisht me të tjerët ka gjasa të lërë gjurmë. Shërbyesit <em>offline</em> dhe shërbyesit që janë shpajtuar prej përditësimeve tuaja, s’do t’i përditësojnë bazat e tyre të të dhënave. - warning_title: Mund të ketë lëndë të përhapur directories: directory: Drejtori profilesh - enabled: Gjendeni te lista e drejtorisë. - enabled_but_waiting: Keni zgjedhur të jeni pjesë e drejtorisë, por ende s’keni numrin minimum të ndjekësve (%{min_followers}) për përfshirje në të. explanation: Zbuloni përdorues bazuar në interesat e tyre explore_mastodon: Eksploroni %{title} - how_to_enable: S’keni zgjedhur të jeni i pranishëm te drejtoria. Mund ta bëni më poshtë. Përdorni te teksti i jetëshkrimit tuaj hashtagë, për t’u përfshirë nën hashtagë specifikë! errors: + '400': The request you submitted was invalid or malformed. '403': S’keni leje të shihni këtë faqe. '404': Faqja që po kërkonit, s’gjendet këtu. + '406': This page is not available in the requested format. '410': Faqja që po kërkonit, s’gjendet më këtu. '422': content: Verifikimi i sigurisë dështoi. Mos i bllokoni gjë cookie-t? @@ -527,6 +506,7 @@ sq: '500': content: Na ndjeni, diçka shkoi ters në anën tonë. title: Kjo faqe s’është e saktë + '503': The page could not be served due to a temporary server failure. noscript_html: Që të përdorni aplikacionin web Mastodon, ju lutemi, aktivizoni JavaScript-in. Ndryshe, provoni për Mastodon-in një nga <a href="%{apps_path}">aplikacionet e brendshëm</a> të platformës tuaj. exports: archive_takeout: @@ -618,9 +598,6 @@ sq: too_many: S’mund të bashkëngjiten më shumë se 4 kartela migrations: acct: emërpërdoruesi@përkatësi e llogarisë së re - currently_redirecting: 'Profili juaj është caktuar të ridrejtojë te:' - proceed: Ruaje - updated_msg: Rregullimi juaj për migrim llogarish u përditësua me sukses! moderation: title: Moderim notification_mailer: @@ -686,10 +663,6 @@ sq: reply: proceed: Ripohoni përgjigjen prompt: 'Doni t’i përgjigjeni këtij mesazhi:' - remote_unfollow: - error: Gabim - title: Titull - unfollowed: U hoq ndjekja scheduled_statuses: over_daily_limit: Keni tejkaluar kufirin e %{limit} mesazheve të planifikuara për atë ditë over_total_limit: Keni tejkaluar kufirin prej %{limit} mesazhesh të planifikuara diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 3310716e0f0ac4e640144944db4efbeb7204c30b..2bddaf3115d329d3ba45c7d9b4d6070888253401 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -6,10 +6,6 @@ sr-Latn: about_this: O instanci contact: Kontakt contact_missing: Nije postavljeno - extended_description_html: | - <h3>Dobro mesto za pravila</h3> - <p>ProÅ¡ireni opis koji joÅ¡ nije postavljen.</p> - generic_description: "%{domain} je server na mreži" hosted_on: Mastodont hostovan na %{domain} learn_more: Saznajte viÅ¡e source_code: Izvorni kod @@ -263,10 +259,6 @@ sr-Latn: no_media: Bez multimedije title: Statusi naloga with_media: Sa multimedijom - subscriptions: - confirmed: PotvrÄ‘eno - expires_in: IstiÄe za - last_delivery: Poslednja dostava title: Administracija admin_mailer: new_report: @@ -318,22 +310,21 @@ sr-Latn: over_x_years: "%{count}god" x_months: "%{count}mesec" deletes: - bad_password_msg: Dobar pokuÅ¡aj, hakeri! Neispravna lozinka confirm_password: Unesite trenutnu lozinku da bismo proverili VaÅ¡ identitet - description_html: Ovo će <strong>trajno, bespovratno</strong> ukloniti sadržaj sa VaÅ¡ef naloga i deaktivirati ga. VaÅ¡e korisniÄko ime će ostati rezervisano da se spreÄi da se neko ne predstavlja kao Vi sutra. proceed: ObriÅ¡i nalog success_msg: VaÅ¡ nalog je uspeÅ¡no obrisan - warning_html: Garantovano je samo brisanje sadržaja sa ove instance. Sadržaj koji je deljen dalje će verovatno da ostavi neke tragove. Nedostupni i ugaÅ¡eni serveri, kao i serveri koji su odjavljeni od primanja statusa od Vas, neće ažurirati svoje baze. - warning_title: Dostupnost rasejanog sadržaja errors: + '400': The request you submitted was invalid or malformed. '403': Nemate dozvola da vidite ovu stranu. '404': Strana koju ste tražili ne postoji. + '406': This page is not available in the requested format. '410': Strana koju ste tražili viÅ¡e ne postoji. '422': '429': Uspored '500': content: Izvinjavamo se, neÅ¡to je poÅ¡lo po zlu sa ove strane. title: Strana nije ispravna + '503': The page could not be served due to a temporary server failure. noscript_html: Da biste koristili Mastodont veb aplikaciju, omogućite JavaScript. U suprotnom, probajte neku od <a href="%{apps_path}">originalnih aplikacija</a> za Mastodont za VaÅ¡u platformu. exports: blocks: Blokirali ste @@ -386,9 +377,6 @@ sr-Latn: too_many: Ne može se prikaÄiti viÅ¡e od 4 fajla migrations: acct: korisnik@domen novog naloga - currently_redirecting: 'Profil Vam je podeÅ¡en da preusmerava na :' - proceed: SaÄuvaj - updated_msg: Prebacivanje postavki VaÅ¡eg naloga uspeÅ¡no izmenjeno! moderation: title: Moderacija notification_mailer: diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 1555fb235def46aa834865b8c250be565df82343..404310fe91a2dfd65c8657ce4e051c5f711bb2d5 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -9,10 +9,6 @@ sr: contact: Контакт contact_missing: Ðије поÑтављено documentation: Документација - extended_description_html: | - <h3>Добро меÑто за правила</h3> - <p>Проширени Ð¾Ð¿Ð¸Ñ ÐºÐ¾Ñ˜Ð¸ још није поÑтављен.</p> - generic_description: "%{domain} је Ñервер на мрежи" hosted_on: МаÑтодонт хоÑтован на %{domain} learn_more: Сазнајте више privacy_policy: ПолиÑа приватноÑти @@ -440,18 +436,8 @@ sr: no_status_selected: Ðиједан ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð¸Ñ˜Ðµ промењен јер ниједан није изабран title: СтатуÑи налога with_media: Са мултимедијом - subscriptions: - confirmed: Потврђено - expires_in: ИÑтиче за - last_delivery: ПоÑледња доÑтава tags: - accounts: Ðалози - hidden: Скривено - hide: Сакриј од фаÑцикле - name: Тараба title: Тараба - unhide: Прикажи у фаÑцикли - visible: Видљиво title: ÐдминиÑтрација warning_presets: add_new: Додај нови @@ -480,7 +466,6 @@ sr: your_token: Ваш приÑтупни токен auth: change_password: Лозинка - confirm_email: Потврдите адреÑу е-поште delete_account: Обриши налог delete_account_html: Ðко желите да обришете Ваш налог, можете <a href="%{path}">наÑтавити овде</a>. Бићете упитани да потврдите. didnt_get_confirmation: ÐиÑте добили поруку Ñа упутÑтвима за потврду налога? @@ -521,25 +506,18 @@ sr: x_days: "%{count}д" x_months: "%{count}меÑец" deletes: - bad_password_msg: Добар покушај, хакери! ÐеиÑправна лозинка confirm_password: УнеÑите тренутну лозинку да биÑмо проверили Ваш идентитет - description_html: Ово ће <strong>трајно, беÑповратно</strong> уклонити Ñадржај Ñа Вашеф налога и деактивирати га. Ваше кориÑничко име ће оÑтати резервиÑано да Ñе Ñпречи да Ñе неко не предÑтавља као Ви Ñутра. proceed: Обриши налог success_msg: Ваш налог је уÑпешно обриÑан - warning_html: Гарантовано је Ñамо бриÑање Ñадржаја Ñа ове инÑтанце. Садржај који је дељен даље ће вероватно да оÑтави неке трагове. ÐедоÑтупни и угашени Ñервери, као и Ñервери који Ñу одјављени од примања ÑтатуÑа од ВаÑ, неће ажурирати Ñвоје базе. - warning_title: ДоÑтупноÑÑ‚ раÑејаног Ñадржаја directories: directory: Профил фаÑцикле - enabled: Ви Ñте тренутно видљиви у фаÑцикли. explanation: Откријте кориÑнике на оÑнову њихових интереÑа explore_mastodon: ИÑтражи %{title} - people: - few: "%{count} људе" - one: "%{count} оÑоба/е" - other: "%{count} људи" errors: + '400': The request you submitted was invalid or malformed. '403': Ðемате дозвола да видите ову Ñтрану. '404': Страна коју Ñте тражили не поÑтоји. + '406': This page is not available in the requested format. '410': Страна коју Ñте тражили више не поÑтоји. '422': content: БезбедоноÑна провера није уÑпела. Да не блокирате колачиће? @@ -548,6 +526,7 @@ sr: '500': content: Извињавамо Ñе, нешто је пошло по злу Ñа ове Ñтране. title: Страна није иÑправна + '503': The page could not be served due to a temporary server failure. noscript_html: Да биÑте кориÑтили МаÑтодонт веб апликацију, омогућите JavaScript. У Ñупротном, пробајте неку од <a href="%{apps_path}">оригиналних апликација</a> за МаÑтодонт за Вашу платформу. exports: archive_takeout: @@ -631,9 +610,6 @@ sr: too_many: Ðе може Ñе прикачити више од 4 фајла migrations: acct: кориÑник@домен новог налога - currently_redirecting: 'Профил Вам је подешен да преуÑмерава на :' - proceed: Сачувај - updated_msg: Пребацивање поÑтавки Вашег налога уÑпешно измењено! moderation: title: Модерација notification_mailer: @@ -693,10 +669,6 @@ sr: reply: proceed: ÐаÑтавите да биÑте одговорили prompt: 'Желите да одговорите на ову трубу:' - remote_unfollow: - error: Грешка - title: ÐаÑлов - unfollowed: Отпраћени scheduled_statuses: over_daily_limit: Прекорачили Ñте границу од %{limit} планираних труба за тај дан over_total_limit: Прекорачили Ñте границу од %{limit} планираних труба diff --git a/config/locales/sv.yml b/config/locales/sv.yml index d3d0cb888d0d1177234d5f793463841e78c7838e..c980b7d05bee903b871d2c3d38f8d5a32075ee1b 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -8,10 +8,6 @@ sv: api: API contact: Kontakt contact_missing: Inte inställd - extended_description_html: | - <h3>En bra plats för regler</h3> - <p>Den utökade beskrivningen har inte konfigurerats ännu.</p> - generic_description: "%{domain} är en server i nätverket" hosted_on: Mastodon värd pÃ¥ %{domain} learn_more: Lär dig mer source_code: Källkod @@ -22,25 +18,38 @@ sv: accounts: follow: Följa following: Följer + joined: Gick med %{date} + last_active: senast aktiv media: Media moved_html: "%{name} har flyttat till %{new_profile_link}:" network_hidden: Denna information är inte tillgänglig + never_active: Aldrig nothing_here: Det finns inget här! people_followed_by: Personer som %{name} följer people_who_follow: Personer som följer %{name} + posts_tab_heading: Toots posts_with_replies: Toots med svar reserved_username: Användarnamnet är reserverat roles: + admin: Administratör + bot: Robot moderator: Moderator + unavailable: Profilen är inte tillgänglig unfollow: Sluta följa admin: + account_actions: + action: Utför Ã¥tgärd + title: Utför aktivitet för moderering pÃ¥ %{acct} account_moderation_notes: create: Lämna kommentar created_msg: Modereringsnotering skapad utan problem! delete: Ta bort destroyed_msg: Modereringsnotering borttagen utan problem! accounts: + approve: Godkänn + approve_all: Godkänn alla are_you_sure: Är du säker? + avatar: Profilbild by_domain: Domän change_email: changed_msg: E-postadressen har ändrats! @@ -52,6 +61,7 @@ sv: confirm: Bekräfta confirmed: Bekräftad confirming: Bekräftande + deleted: Raderad demote: Degradera disable: inaktivera disable_two_factor_authentication: Inaktivera 2FA @@ -313,12 +323,6 @@ sv: no_media: Ingen media title: Kontostatus with_media: med media - subscriptions: - callback_url: Ã…teranrop URL - confirmed: Bekräftad - expires_in: UtgÃ¥r om - last_delivery: Sista leverans - topic: Ämne admin_mailer: new_report: body: "%{reporter} har rapporterat %{target}" @@ -340,7 +344,6 @@ sv: your_token: Din access token auth: change_password: Lösenord - confirm_email: Bekräfta e-postadress delete_account: Ta bort konto delete_account_html: Om du vill radera ditt konto kan du <a href="%{path}">fortsätta här</a>. Du kommer att bli ombedd att bekräfta. didnt_get_confirmation: Fick inte instruktioner om bekräftelse? @@ -384,16 +387,14 @@ sv: x_months: "%{count}mÃ¥n" x_seconds: "%{count}sek" deletes: - bad_password_msg: Bra försök, hackare! Fel lösenord confirm_password: Ange ditt lösenord för att verifiera din identitet - description_html: Detta vill <strong>permanent, irreversibelt</strong> ta bort innehÃ¥ll frÃ¥n ditt konto och avaktivera det. Ditt användarnamn kommer att förbli reserverat för att förhindra framtida efterföljare. proceed: Ta bort konto success_msg: Ditt konto har tagits bort - warning_html: Endast borttagning av innehÃ¥ll frÃ¥n denna speciella instans garanteras. InnehÃ¥ll som har delats i stor utsträckning kommer sannolikt att lämna spÃ¥r. Offline-servrar och servrar som har avstängt frÃ¥n dina uppdateringar uppdaterar inte sina databaser. - warning_title: Spridet innehÃ¥ll och tillgänglighet errors: + '400': The request you submitted was invalid or malformed. '403': Du har inte behörighet att visa den här sidan. '404': Sidan du letade efter existerar inte. + '406': This page is not available in the requested format. '410': Sidan du letade efter existerar inte längre. '422': content: Säkerhetsverifiering misslyckades Blockerar du cookies? @@ -402,6 +403,7 @@ sv: '500': content: Vi är ledsna, men nÃ¥got gick fel frÃ¥n vÃ¥rat hÃ¥ll. title: Den här sidan är inte korrekt + '503': The page could not be served due to a temporary server failure. noscript_html: För att använda Mastodon webbapplikationen, vänligen aktivera JavaScript. Alternativt kan du prova en av <a href="%{apps_path}">inhemska appar</a> för Mastodon för din plattform. exports: archive_takeout: @@ -462,9 +464,6 @@ sv: too_many: Det gÃ¥r inte att bifoga mer än 4 filer migrations: acct: användarnamn@domän av det nya kontot - currently_redirecting: 'Din profil är satt att omdirigeras till:' - proceed: Spara - updated_msg: Dina kontoflyttsinställning har uppdaterats! moderation: title: Moderera notification_mailer: @@ -513,10 +512,6 @@ sv: missing_resource: Det gick inte att hitta den begärda omdirigeringsadressen för ditt konto proceed: Fortsätt för att följa prompt: 'Du kommer att följa:' - remote_unfollow: - error: Fel - title: Titel - unfollowed: Slutade följa sessions: activity: Senaste aktivitet browser: Webbläsare @@ -560,6 +555,8 @@ sv: notifications: Meddelanden preferences: Inställningar two_factor_authentication: TvÃ¥stegsautentisering + spam_check: + spam_detected: Det här är en automatisk rapport. Spam har upptäckts. statuses: attached: description: 'Bifogad: %{attached}' diff --git a/config/locales/ta.yml b/config/locales/ta.yml index eef06fa7caabb2c2129df768eef29391c507c56a..75bb81fad0256d157ab3f91ca8724ca12a15edc3 100644 --- a/config/locales/ta.yml +++ b/config/locales/ta.yml @@ -1,12 +1,15 @@ --- ta: errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. invites: expires_in: '1800': 30 minutes diff --git a/config/locales/te.yml b/config/locales/te.yml index d4a2f507d78e4041b2eb2cc8a215155cae6a5067..526b03b4974fa02bd0ad153bcc8953a63d588c31 100644 --- a/config/locales/te.yml +++ b/config/locales/te.yml @@ -10,10 +10,6 @@ te: contact_missing: ఇంకా సెటౠచేయలేదౠcontact_unavailable: వరà±à°¤à°¿à°‚చదౠdocumentation: పతà±à°°à±€à°•à°°à°£ - extended_description_html: | - <h3>నియమాలకౠఒక మంచి à°ªà±à°°à°¦à±‡à°¶à°‚</h3> - <p>మరింత విశదీకరణ ఇంకా సెటౠచేయబడలేదà±.</p> - generic_description: "%{domain} అనేది నెటà±à°µà°°à±à°•à±à°²à±‹à°¨à°¿ à°’à°• సరà±à°µà°°à±" hosted_on: మాసà±à°Ÿà±Šà°¡à°¾à°¨à± %{domain} లో హోసà±à°Ÿà± చేయబడింది learn_more: మరింత తెలà±à°¸à±à°•ోండి privacy_policy: గోపà±à°¯à°¤ విధానమౠ@@ -113,12 +109,15 @@ te: most_recent_activity: ఇటీవల యాకà±à°Ÿà°¿à°µà°¿à°Ÿà±€ most_recent_ip: ఇటీవలి IP errors: + '400': The request you submitted was invalid or malformed. '403': You don't have permission to view this page. '404': The page you are looking for isn't here. + '406': This page is not available in the requested format. '410': The page you were looking for doesn't exist here anymore. '422': '429': Throttled '500': + '503': The page could not be served due to a temporary server failure. invites: expires_in: '1800': 30 minutes diff --git a/config/locales/th.yml b/config/locales/th.yml index 7a16bc2f3288ffdd34761126e02634bb56883bfd..5a56bcd35121e3ab012f23e3d6de1300982239aa 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1,6 +1,7 @@ --- th: about: + about_hashtag_html: มีà¸à¸²à¸£à¹à¸—็à¸à¹‚พสต์สาธารณะเหล่านี้ด้วย <strong>#%{hashtag}</strong> คุณสามารถโต้ตà¸à¸šà¸à¸±à¸šà¹‚พสต์หาà¸à¸„ุณมีบัà¸à¸Šà¸µà¸—ี่ใดà¸à¹‡à¸•ามในเฟดิเวิร์ส about_mastodon_html: Mastodon เป็นเครืà¸à¸‚่ายสังคมที่ทำงานบนโปรโตคà¸à¸¥à¹€à¸§à¹‡à¸šà¹à¸šà¸šà¹€à¸›à¸´à¸”à¹à¸¥à¸°à¸‹à¸à¸Ÿà¸•์à¹à¸§à¸£à¹Œà¹€à¸ªà¸£à¸µà¸—ี่เปิดต้นฉบับ à¸à¸£à¸°à¸ˆà¸²à¸¢à¸¨à¸¹à¸™à¸¢à¹Œà¹€à¸«à¸¡à¸·à¸à¸™à¸à¸µà¹€à¸¡à¸¥ about_this: เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š active_count_after: ที่ใช้งาน @@ -16,11 +17,9 @@ th: contact_unavailable: ไม่มี discover_users: ค้นพบผู้ใช้ documentation: เà¸à¸à¸ªà¸²à¸£à¸›à¸£à¸°à¸à¸à¸š - extended_description_html: | - <h3>สถานที่ที่ดีสำหรับà¸à¸Ž</h3> - <p>ยังไม่ได้ตั้งคำà¸à¸˜à¸´à¸šà¸²à¸¢à¹à¸šà¸šà¸‚ยาย</p> - generic_description: "%{domain} เป็นเซิร์ฟเวà¸à¸£à¹Œà¸«à¸™à¸¶à¹ˆà¸‡à¹ƒà¸™à¹€à¸„รืà¸à¸‚่าย" + federation_hint_html: ด้วยบัà¸à¸Šà¸µà¸—ี่ %{instance} คุณจะสามารถติดตามผู้คนในเซิร์ฟเวà¸à¸£à¹Œ Mastodon à¹à¸¥à¸°à¸à¸·à¹ˆà¸™ ๆ get_apps: ลà¸à¸‡à¹à¸à¸›à¸ªà¸³à¸«à¸£à¸±à¸šà¸¡à¸·à¸à¸–ืภ+ hosted_on: Mastodon ที่โฮสต์ที่ %{domain} learn_more: เรียนรู้เพิ่มเติม privacy_policy: นโยบายความเป็นส่วนตัว see_whats_happening: ดูสิ่งที่à¸à¸³à¸¥à¸±à¸‡à¹€à¸à¸´à¸”ขึ้น @@ -29,7 +28,11 @@ th: status_count_after: other: สถานะ status_count_before: ผู้สร้าง + tagline: ติดตามเพื่à¸à¸™ ๆ à¹à¸¥à¸°à¸„้นพบเพื่à¸à¸™à¹ƒà¸«à¸¡à¹ˆ ๆ terms: เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£ + unavailable_content_description: + domain: เซิร์ฟเวà¸à¸£à¹Œ + reason: 'เหตุผล:' user_count_after: other: ผู้ใช้ user_count_before: บ้านขà¸à¸‡ @@ -45,6 +48,7 @@ th: media: สื่ภmoved_html: "%{name} ได้ย้ายไปยัง %{new_profile_link}:" network_hidden: ไม่มีข้à¸à¸¡à¸¹à¸¥à¸™à¸µà¹‰ + never_active: ไม่เลย nothing_here: ไม่มีสิ่งใดที่นี่! people_followed_by: ผู้คนที่ %{name} ติดตาม people_who_follow: ผู้คนที่ติดตาม %{name} @@ -156,18 +160,33 @@ th: warn: เตืà¸à¸™ web: เว็บ action_logs: + actions: + change_email_user: "%{name} ได้เปลี่ยนที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ %{target}" + confirm_user: "%{name} ได้ยืนยันที่à¸à¸¢à¸¹à¹ˆà¸à¸µà¹€à¸¡à¸¥à¸‚à¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ %{target}" + create_account_warning: "%{name} ได้ส่งคำเตืà¸à¸™à¹„ปยัง %{target}" + create_domain_block: "%{name} ได้ปิดà¸à¸±à¹‰à¸™à¹‚ดเมน %{target}" + destroy_custom_emoji: "%{name} ได้ทำลายà¸à¸µà¹‚มจิ %{target}" + destroy_domain_block: "%{name} ได้เลิà¸à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน %{target}" + destroy_status: "%{name} ได้เà¸à¸²à¸ªà¸–านะโดย %{target} à¸à¸à¸" + disable_custom_emoji: "%{name} ได้ปิดใช้งานà¸à¸µà¹‚มจิ %{target}" + enable_custom_emoji: "%{name} ได้เปิดใช้งานà¸à¸µà¹‚มจิ %{target}" + update_custom_emoji: "%{name} ได้à¸à¸±à¸›à¹€à¸”ตà¸à¸µà¹‚มจิ %{target}" + update_status: "%{name} ได้à¸à¸±à¸›à¹€à¸”ตสถานะโดย %{target}" deleted_status: "(สถานะที่ลบà¹à¸¥à¹‰à¸§)" title: รายà¸à¸²à¸£à¸šà¸±à¸™à¸—ึà¸à¸à¸²à¸£à¸•รวจสà¸à¸š custom_emojis: by_domain: โดเมน copy: คัดลà¸à¸ + create_new_category: สร้างหมวดหมู่ใหม่ created_msg: สร้างà¸à¸µà¹‚มจิสำเร็จ! delete: ลบ destroyed_msg: ทำลายà¸à¸µà¹‚มโจสำเร็จ! disable: ปิดใช้งาน + disabled: ปิดใช้งานà¸à¸¢à¸¹à¹ˆ disabled_msg: ปิดใช้งานà¸à¸µà¹‚มจินั้นสำเร็จ emoji: à¸à¸µà¹‚มจิ enable: เปิดใช้งาน + enabled: เปิดใช้งานà¸à¸¢à¸¹à¹ˆ enabled_msg: เปิดใช้งานà¸à¸µà¹‚มจินั้นสำเร็จ image_hint: PNG สูงสุด 50KB new: @@ -204,6 +223,7 @@ th: created_msg: à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥à¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน destroyed_msg: เลิà¸à¸—ำà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมนà¹à¸¥à¹‰à¸§ domain: โดเมน + edit: à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน new: create: สร้างà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™ hint: à¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมนจะไม่ป้à¸à¸‡à¸à¸±à¸™à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸£à¸²à¸¢à¸à¸²à¸£à¸šà¸±à¸à¸Šà¸µà¹ƒà¸™à¸à¸²à¸™à¸‚้à¸à¸¡à¸¹à¸¥ à¹à¸•่จะใช้วิธีà¸à¸²à¸£à¸„วบคุมเฉพาะà¸à¸±à¸šà¸šà¸±à¸à¸Šà¸µà¹€à¸«à¸¥à¹ˆà¸²à¸™à¸±à¹‰à¸™à¸¢à¹‰à¸à¸™à¸«à¸¥à¸±à¸‡à¹à¸¥à¸°à¹‚ดยà¸à¸±à¸•โนมัติ @@ -213,6 +233,8 @@ th: silence: เงียบ suspend: ระงับ title: à¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมนใหม่ + private_comment: ความคิดเห็นส่วนตัว + public_comment: ความคิดเห็นสาธารณะ reject_media: ปà¸à¸´à¹€à¸ªà¸˜à¹„ฟล์สื่ภreject_media_hint: เà¸à¸²à¹„ฟล์สื่à¸à¸—ี่จัดเà¸à¹‡à¸šà¹„ว้ในเว็บà¸à¸à¸à¹à¸¥à¸°à¸›à¸à¸´à¹€à¸ªà¸˜à¸—ี่จะดาวน์โหลดไฟล์ใด ๆ ในà¸à¸™à¸²à¸„ต ไม่เà¸à¸µà¹ˆà¸¢à¸§à¸‚้à¸à¸‡à¸à¸±à¸šà¸à¸²à¸£à¸£à¸°à¸‡à¸±à¸š reject_reports: ปà¸à¸´à¹€à¸ªà¸˜à¸£à¸²à¸¢à¸‡à¸²à¸™ @@ -228,6 +250,7 @@ th: title: เลิà¸à¸—ำà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมนสำหรับ %{domain} undo: เลิà¸à¸—ำ undo: เลิà¸à¸—ำà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน + view: ดูà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน email_domain_blocks: add_new: เพิ่มใหม่ delete: ลบ @@ -243,6 +266,8 @@ th: all: ทั้งหมด limited: จำà¸à¸±à¸”à¸à¸¢à¸¹à¹ˆ title: à¸à¸²à¸£à¸„วบคุม + private_comment: ความคิดเห็นส่วนตัว + public_comment: ความคิดเห็นสาธารณะ title: à¸à¸²à¸£à¸•ิดต่à¸à¸à¸±à¸šà¸ ายนà¸à¸ total_storage: ไฟล์à¹à¸™à¸šà¸ªà¸·à¹ˆà¸ invites: @@ -287,11 +312,15 @@ th: unresolved: ยังไม่ได้à¹à¸à¹‰à¸›à¸±à¸à¸«à¸² updated_at: à¸à¸±à¸›à¹€à¸”ตเมื่ภsettings: + bootstrap_timeline_accounts: + title: à¸à¸²à¸£à¸•ิดตามเริ่มต้นสำหรับผู้ใช้ใหม่ contact_information: email: à¸à¸µà¹€à¸¡à¸¥à¸˜à¸¸à¸£à¸à¸´à¸ˆ username: ชื่à¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹ƒà¸™à¸à¸²à¸£à¸•ิดต่ภcustom_css: title: CSS ที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡ + domain_blocks: + title: à¹à¸ªà¸”งà¸à¸²à¸£à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¹‚ดเมน profile_directory: title: เปิดใช้งานไดเรà¸à¸—à¸à¸£à¸µà¹‚ปรไฟล์ registrations: @@ -320,36 +349,33 @@ th: title: ข้à¸à¸¡à¸¹à¸¥à¹à¸šà¸šà¸‚ยายที่à¸à¸³à¸«à¸™à¸”เà¸à¸‡ site_short_description: title: คำà¸à¸˜à¸´à¸šà¸²à¸¢à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¹à¸šà¸šà¸ªà¸±à¹‰à¸™ + site_terms: + title: เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¸—ี่à¸à¸³à¸«à¸™à¸”เà¸à¸‡ site_title: ชื่à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ timeline_preview: desc_html: à¹à¸ªà¸”งเส้นเวลาสาธารณะในหน้าเริ่มต้น title: ตัวà¸à¸¢à¹ˆà¸²à¸‡à¹€à¸ªà¹‰à¸™à¹€à¸§à¸¥à¸² title: à¸à¸²à¸£à¸•ั้งค่าไซต์ + trends: + title: à¹à¸®à¸Šà¹à¸—็à¸à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸™à¸´à¸¢à¸¡ statuses: back_to_account: à¸à¸¥à¸±à¸šà¹„ปที่หน้าบัà¸à¸Šà¸µ batch: delete: ลบ nsfw_off: ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¹„ม่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ nsfw_on: ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢à¸§à¹ˆà¸²à¸¥à¸°à¹€à¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ + deleted: ลบà¹à¸¥à¹‰à¸§ media: title: สื่ภno_media: ไม่มีสื่ภtitle: สถานะบัà¸à¸Šà¸µ - subscriptions: - callback_url: URL เรียà¸à¸à¸¥à¸±à¸š - confirmed: ยืนยันà¹à¸¥à¹‰à¸§ - expires_in: หมดà¸à¸²à¸¢à¸¸à¸ ายใน - last_delivery: ส่งล่าสุด - title: WebSub - topic: หัวข้ภtags: - accounts: บัà¸à¸Šà¸µ - hidden: ซ่à¸à¸™à¸à¸¢à¸¹à¹ˆ - hide: ซ่à¸à¸™à¸ˆà¸²à¸à¹„ดเรà¸à¸—à¸à¸£à¸µ + context: บริบท + directory: ในไดเรà¸à¸—à¸à¸£à¸µ + in_directory: "%{count} ในไดเรà¸à¸—à¸à¸£à¸µ" name: à¹à¸®à¸Šà¹à¸—็ภtitle: à¹à¸®à¸Šà¹à¸—็ภ- unhide: à¹à¸ªà¸”งในไดเรà¸à¸—à¸à¸£à¸µ - visible: มà¸à¸‡à¹€à¸«à¹‡à¸™ + trending_right_now: à¸à¸³à¸¥à¸±à¸‡à¸™à¸´à¸¢à¸¡ title: à¸à¸²à¸£à¸”ูà¹à¸¥ warning_presets: add_new: เพิ่มใหม่ @@ -360,6 +386,8 @@ th: appearance: advanced_web_interface: ส่วนติดต่à¸à¹€à¸§à¹‡à¸šà¸‚ั้นสูง animations_and_accessibility: ภาพเคลื่à¸à¸™à¹„หวà¹à¸¥à¸°à¸à¸²à¸£à¸Šà¹ˆà¸§à¸¢à¸à¸²à¸£à¹€à¸‚้าถึง + confirmation_dialogs: à¸à¸¥à¹ˆà¸à¸‡à¹‚ต้ตà¸à¸šà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™ + discovery: ค้นพบ sensitive_content: เนื้à¸à¸«à¸²à¸—ี่ละเà¸à¸µà¸¢à¸”à¸à¹ˆà¸à¸™ application_mailer: notification_preferences: เปลี่ยนà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ลัà¸à¸©à¸“ะà¸à¸µà¹€à¸¡à¸¥ @@ -368,11 +396,14 @@ th: view_profile: ดูโปรไฟล์ view_status: ดูสถานะ applications: + created: สร้างà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันสำเร็จ + destroyed: ลบà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันสำเร็จ invalid_url: URL ที่ระบุไม่ถูà¸à¸•้à¸à¸‡ auth: apply_for_account: ขà¸à¸„ำเชิภchange_password: รหัสผ่าน - confirm_email: ยืนยันà¸à¸µà¹€à¸¡à¸¥ + checkbox_agreement_html: ฉันยà¸à¸¡à¸£à¸±à¸š <a href="%{rules_path}" target="_blank">à¸à¸Žà¸‚à¸à¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œ</a> à¹à¸¥à¸° <a href="%{terms_path}" target="_blank">เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£</a> + checkbox_agreement_without_rules_html: ฉันยà¸à¸¡à¸£à¸±à¸š <a href="%{terms_path}" target="_blank">เงื่à¸à¸™à¹„ขà¸à¸²à¸£à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£</a> delete_account: ลบบัà¸à¸Šà¸µ forgot_password: ลืมรหัสผ่านขà¸à¸‡à¸„ุณ? login: เข้าสู่ระบบ @@ -387,6 +418,8 @@ th: reset_password: ตั้งรหัสผ่านใหม่ security: ความปลà¸à¸”ภัย set_new_password: ตั้งรหัสผ่านใหม่ + status: + account_status: สถานะบัà¸à¸Šà¸µ trouble_logging_in: มีปัà¸à¸«à¸²à¹ƒà¸™à¸à¸²à¸£à¹€à¸‚้าสู่ระบบ? authorize_follow: already_following: คุณà¸à¸³à¸¥à¸±à¸‡à¸•ิดตามบัà¸à¸Šà¸µà¸™à¸µà¹‰à¸à¸¢à¸¹à¹ˆà¹à¸¥à¹‰à¸§ @@ -397,6 +430,10 @@ th: return: à¹à¸ªà¸”งโปรไฟล์ขà¸à¸‡à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ web: ไปยังเว็บ title: ติดตาม %{acct} + challenge: + confirm: ดำเนินà¸à¸²à¸£à¸•่ภ+ invalid_password: รหัสผ่านไม่ถูà¸à¸•้à¸à¸‡ + prompt: ยืนยันรหัสผ่านเพื่à¸à¸”ำเนินà¸à¸²à¸£à¸•่ภdatetime: distance_in_words: about_x_hours: "%{count} ชั่วโมง" @@ -419,8 +456,10 @@ th: explanation: ค้นพบผู้ใช้ตามความสนใจขà¸à¸‡à¹€à¸‚า explore_mastodon: สำรวจ %{title} errors: + '400': The request you submitted was invalid or malformed. '403': คุณไม่มีสิทธิà¸à¸™à¸¸à¸à¸²à¸•เพื่à¸à¸”ูหน้านี้ '404': หน้าที่คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸à¸‡à¸«à¸²à¹„ม่ได้à¸à¸¢à¸¹à¹ˆà¸—ี่นี่ + '406': This page is not available in the requested format. '410': หน้าที่คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸à¸‡à¸«à¸²à¹„ม่มีà¸à¸¢à¸¹à¹ˆà¸—ี่นี่à¸à¸µà¸à¸•่à¸à¹„ป '422': content: à¸à¸²à¸£à¸•รวจสà¸à¸šà¸„วามปลà¸à¸”ภัยล้มเหลว คุณà¸à¸³à¸¥à¸±à¸‡à¸›à¸´à¸”à¸à¸±à¹‰à¸™à¸„ุà¸à¸à¸µà¹‰à¸«à¸£à¸·à¸à¹„ม่? @@ -428,6 +467,9 @@ th: '429': Throttled '500': title: หน้านี้ไม่ถูà¸à¸•้à¸à¸‡ + '503': The page could not be served due to a temporary server failure. + existing_username_validator: + not_found_multiple: ไม่พบ %{usernames} exports: archive_takeout: date: วันที่ @@ -460,6 +502,7 @@ th: developers: นัà¸à¸žà¸±à¸’นา more: เพิ่มเติม… resources: ทรัพยาà¸à¸£ + trending_now: à¸à¸³à¸¥à¸±à¸‡à¸™à¸´à¸¢à¸¡ generic: all: ทั้งหมด changes_saved_msg: บันทึà¸à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸ªà¸³à¹€à¸£à¹‡à¸ˆ! @@ -488,9 +531,12 @@ th: '86400': 1 วัน expires_in_prompt: ไม่เลย generate: สร้าง + max_uses: + other: "%{count} à¸à¸²à¸£à¹ƒà¸Šà¹‰" max_uses_prompt: ไม่มีขีดจำà¸à¸±à¸” table: expires_at: หมดà¸à¸²à¸¢à¸¸à¹€à¸¡à¸·à¹ˆà¸ + uses: à¸à¸²à¸£à¹ƒà¸Šà¹‰ title: เชิà¸à¸œà¸¹à¹‰à¸„น media_attachments: validations: @@ -498,7 +544,10 @@ th: too_many: ไม่สามารถà¹à¸™à¸šà¸¡à¸²à¸à¸à¸§à¹ˆà¸² 4 ไฟล์ migrations: acct: username@domain ขà¸à¸‡à¸šà¸±à¸à¸Šà¸µà¹ƒà¸«à¸¡à¹ˆ - proceed: บันทึภ+ cancel: ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸ªà¹‰à¸™à¸—าง + proceed_with_move: ย้ายผู้ติดตาม + redirecting_to: บัà¸à¸Šà¸µà¸‚à¸à¸‡à¸„ุณà¸à¸³à¸¥à¸±à¸‡à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸ªà¹‰à¸™à¸—างไปยัง %{acct} + set_redirect: ตั้งà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸ªà¹‰à¸™à¸—าง moderation: title: à¸à¸²à¸£à¸„วบคุม notification_mailer: @@ -536,7 +585,11 @@ th: older: เà¸à¹ˆà¸²à¸à¸§à¹ˆà¸² prev: à¸à¹ˆà¸à¸™à¸«à¸™à¹‰à¸² truncate: "…" + polls: + errors: + duplicate_options: มีรายà¸à¸²à¸£à¸—ี่ซ้ำà¸à¸±à¸™ preferences: + other: à¸à¸·à¹ˆà¸™ ๆ posting_defaults: ค่าเริ่มต้นà¸à¸²à¸£à¹‚พสต์ public_timelines: เส้นเวลาสาธารณะ relationships: @@ -562,10 +615,6 @@ th: reply: proceed: ดำเนินà¸à¸²à¸£à¸•่à¸à¹€à¸žà¸·à¹ˆà¸à¸•à¸à¸šà¸à¸¥à¸±à¸š prompt: 'คุณต้à¸à¸‡à¸à¸²à¸£à¸•à¸à¸šà¸à¸¥à¸±à¸šà¹‚พสต์นี้:' - remote_unfollow: - error: ข้à¸à¸œà¸´à¸”พลาด - title: ชื่à¸à¹€à¸£à¸·à¹ˆà¸à¸‡ - unfollowed: เลิà¸à¸•ิดตามà¹à¸¥à¹‰à¸§ sessions: activity: à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸¥à¹ˆà¸²à¸ªà¸¸à¸” browser: เบราว์เซà¸à¸£à¹Œ @@ -613,7 +662,8 @@ th: development: à¸à¸²à¸£à¸žà¸±à¸’นา edit_profile: à¹à¸à¹‰à¹„ขโปรไฟล์ export: à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸à¸‚้à¸à¸¡à¸¹à¸¥ - import: นำเข้า + featured_tags: à¹à¸®à¸Šà¹à¸—็à¸à¸—ี่à¹à¸™à¸°à¸™à¸³ + import: à¸à¸²à¸£à¸™à¸³à¹€à¸‚้า import_and_export: à¸à¸²à¸£à¸™à¸³à¹€à¸‚้าà¹à¸¥à¸°à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸à¸à¸ notifications: à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™ preferences: à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ลัà¸à¸©à¸“ะ @@ -628,12 +678,14 @@ th: video: other: "%{count} วิดีโà¸" content_warning: 'คำเตืà¸à¸™à¹€à¸™à¸·à¹‰à¸à¸«à¸²: %{warning}' + language_detection: ตรวจหาภาษาโดยà¸à¸±à¸•โนมัติ open_in_web: เปิดในเว็บ pin_errors: reblog: ไม่สามารถปัà¸à¸«à¸¡à¸¸à¸”à¸à¸²à¸£à¸”ัน poll: total_votes: other: "%{count} à¸à¸²à¸£à¸¥à¸‡à¸„ะà¹à¸™à¸™" + vote: ลงคะà¹à¸™à¸™ show_more: à¹à¸ªà¸”งเพิ่มเติม sign_in_to_participate: ลงชื่à¸à¹€à¸‚้าเพื่à¸à¹€à¸‚้าร่วมà¸à¸²à¸£à¸ªà¸™à¸—นา title: '%{name}: "%{quote}"' diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 3113e7a08f8cae0fa540d36afce2fec20bafb039..7e53e7f2c75aa8222598ad1632af3fa4c490b16d 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -4,25 +4,44 @@ tr: about_hashtag_html: Bunlar <strong>#%{hashtag}X</strong> ile etiketlenen genel paylaşımlar. Açık alanda herhangi bir yerde bir hesabınız varsa, onlarla etkileÅŸime geçebilirsiniz. about_mastodon_html: Mastodon <em>ücretsiz ve açık kaynaklı</em> bir sosyal aÄŸdır. <em>MerkezileÅŸtirilmemiÅŸ</em> yapısı sayesinde diÄŸer ticari sosyal platformların aksine iletiÅŸimininizin tek bir firmada tutulmasının/yönetilmesinin önüne geçer. GüvendiÄŸiniz bir sunucuyu seçerek oradaki kiÅŸilerle etkileÅŸimde bulunabilirsiniz. Herkes kendi Mastodon sunucusunu kurabilir ve sorunsuz bir ÅŸekilde Mastodon <em>sosyal ağına</em> dahil edebilir. about_this: Bu sunucu hakkında + active_count_after: etkin + active_footnote: Aylık Aktif Kullanıcılar (AAK) administered_by: 'Tarafından yönetildi:' + api: API apps: Mobil uygulamalar + apps_platforms: İos, Android ve diÄŸer platformlardaki Mastodon'u kullanın + browse_directory: Bir profil dizinine göz atın ve ilgi alanlarına göre filtreleyin + browse_public_posts: Mastodon'daki herkese açık yayınlara göz atın contact: İletiÅŸim contact_missing: Ayarlanmadı contact_unavailable: Yok + discover_users: Kullanıcıları keÅŸfet documentation: Belgeler - extended_description_html: | - <h3>Kural için iyi bir yer</h3> - <p>GeniÅŸletilmiÅŸ açıklama henüz ayarlanmamış.</p> - generic_description: "%{domain} aÄŸdaki bir sunucudur" + federation_hint_html: "%{instance} hesabınızla, herhangi bir Mastodon sunucusundaki ve haricindeki kiÅŸileri takip edebilirsiniz." + get_apps: Bir mobil uygulamayı deneyin hosted_on: Mastodon %{domain} üzerinde barındırılıyor + instance_actor_flash: | + Bu hesap, herhangi bir kullanıcıyı deÄŸil sunucunun kendisini temsil etmek için kullanılan sanal bir aktördür. + Federasyon amaçlı kullanılır ve tüm yansıyı engellemek istemediÄŸiniz sürece engellenmemelidir; bu durumda bir etki alanı bloÄŸu kullanmanız gerekir. learn_more: Daha fazla bilgi edinin privacy_policy: Gizlilik politikası + see_whats_happening: Neler olduÄŸunu görün + server_stats: 'Sunucu istatistikleri:' source_code: Kaynak kodu status_count_after: one: durum other: durum status_count_before: Åžu ana kadar + tagline: ArkadaÅŸlarını takip et ve yenilerini keÅŸfet terms: Kullanım ÅŸartları + unavailable_content: Mevcut olmayan içerik + unavailable_content_description: + domain: Sunucu + reason: Sebep + rejecting_media: 'Bu sunuculardaki medya dosyaları iÅŸlenmeyecek ya da saklanmayacak, ve hiçbir küçük resim gösterilmeyecektir, dolayısıyla orjinal dosyaya manuel tıklama gerekecektir:' + silenced: 'Bu sunuculardan gelen gönderiler genel zaman çizelgelerinde ve konuÅŸmalarda gizlenecek ve siz onları takip etmediÄŸiniz sürece, kullanıcıların etkileÅŸimlerinden hiçbir bildirim alınmayacaktır:' + suspended: 'Bu sunuculardaki hiçbir veri iÅŸlenmeyecek, saklanmayacak veya deÄŸiÅŸ tokuÅŸ edilmeyecektir, dolayısıyla bu sunuculardaki kullanıcılarla herhangi bir etkileÅŸim ya da iletiÅŸim imkansız olacaktır:' + unavailable_content_html: Mastodon, genel olarak fediverse'teki herhangi bir sunucudan içerik görüntülemenize ve kullanıcılarıyla etkileÅŸim kurmanıza izin verir. Bunlar, bu sunucuda yapılmış olan istisnalardır. user_count_after: one: kullanıcı other: kullanıcı @@ -30,38 +49,51 @@ tr: what_is_mastodon: Mastodon nedir? accounts: choices_html: "%{name} seçimleri:" + endorsements_hint: Takip ettiÄŸiniz kiÅŸileri web arayüzünden onaylayabilirsiniz, burada görünecekler. + featured_tags_hint: Burada görüntülenecek belirli etiketlere sahip olabilirsiniz. follow: Takip et followers: one: Takipçi other: Takipçi following: Takip ediliyor joined: "%{date} tarihinde katıldı" + last_active: son aktivite link_verified_on: Bu baÄŸlantının mülkiyeti %{date} tarihinde kontrol edildi media: Medya moved_html: "%{name}, %{new_profile_link} adresine taşındı:" network_hidden: Bu bilgi mevcut deÄŸil + never_active: Asla nothing_here: Burada henüz hiçbir gönderi yok! people_followed_by: Kullanıcı %{name}'in takip ettikleri people_who_follow: Kullanıcı %{name}'i takip edenler pin_errors: following: Onaylamak istediÄŸiniz kiÅŸiyi zaten takip ediyor olmalısınız + posts: + one: Gönderi + other: Tootlar posts_tab_heading: Tootlar posts_with_replies: Tootlar ve yanıtlar reserved_username: Kullanıcı adı saklıdır roles: admin: Yönetici + bot: Bot moderator: Denetleyici + unavailable: Profil kullanılamıyor unfollow: Takibi bırak admin: account_actions: action: Eylemi gerçekleÅŸtir + title: "%{acct} üzerinde denetleme eylemi gerçekleÅŸtir" account_moderation_notes: create: Not bırakın created_msg: Denetim notu baÅŸarıyla oluÅŸturuldu! delete: Sil destroyed_msg: Denetim notu baÅŸarıyla yok edildi! accounts: + approve: Onayla + approve_all: Tümünü onayla are_you_sure: Emin misiniz? + avatar: Avatar by_domain: Sunucu change_email: changed_msg: Hesap e-postası baÅŸarıyla deÄŸiÅŸtirildi! @@ -74,6 +106,7 @@ tr: confirmed: Onaylandı confirming: Onaylama deleted: Silinen + demote: Düşür disable: Devre dışı disable_two_factor_authentication: 2AD kapat disabled: Kapalı @@ -91,6 +124,7 @@ tr: header: Üstbilgi inbox_url: Gelen kutusu baÄŸlantısı invited_by: Tarafından davet edildi + ip: IP joined: Katıldı location: all: Hepsi @@ -101,14 +135,20 @@ tr: media_attachments: Medya ekleri memorialize: Bir hatıraya dön moderation: + active: Etkin all: Hepsi + pending: Bekliyor silenced: Susturulanlar suspended: UzaklaÅŸtırılanlar title: Yönetim moderation_notes: Denetleme notları most_recent_activity: Son aktivite most_recent_ip: Son IP + no_account_selected: Hiçbiri seçilmediÄŸinden hiçbir hesap deÄŸiÅŸtirilmedi + no_limits_imposed: Sınır koymaz not_subscribed: Abone edilmedi + outbox_url: Giden Kutusu URL'si + pending: Bekleyen yorum perform_full_suspension: Askıya al profile_url: Profil linki promote: Yükselt @@ -116,6 +156,8 @@ tr: public: Herkese açık push_subscription_expires: PuSH aboneliÄŸi dolumu redownload: Profili yenile + reject: Reddet + reject_all: Tümünü reddet remove_avatar: Avatarı kaldır remove_header: Üstbilgiyi kaldır resend_confirmation: @@ -142,6 +184,7 @@ tr: statuses: Durumlar subscribe: Abone ol suspended: Askıya alındı + time_in_queue: "%{time} kuyruÄŸunda bekliyor" title: Hesaplar unconfirmed_email: Onaylanmamış e-posta undo_silenced: Susturmayı geri al @@ -149,25 +192,136 @@ tr: unsubscribe: Abonelikten çık username: Kullanıcı adı warn: Uyar + web: Web + whitelisted: Beyaz listede action_logs: actions: + assigned_to_self_report: "%{name} kendilerine %{target} adlı raporu verdi" + change_email_user: "%{name}, %{target} kullanıcısının e-posta adresini deÄŸiÅŸtirdi" confirm_user: "%{name} %{target} kullanıcısının e-posta adresini onayladı" + create_account_warning: "%{name} %{target} 'a bir uyarı gönderdi" create_custom_emoji: "%{name} yeni ifade yükledi %{target}" + create_domain_block: "%{target} alanı, %{name} tarafından engellendi" + create_email_domain_block: "%{target} e-posta alanı, %{name} tarafından kara listeye alınmış" + demote_user: "%{name} %{target} kullanıcısını düşürdü" + destroy_custom_emoji: "%{target} emoji, %{name} tarafından kaldırıldı" + destroy_domain_block: "%{target} alan adının engeli %{name} tarafından kaldırıldı" + destroy_email_domain_block: "%{target} e-posta sunucusu, %{name} tarafından beyaz listeye alındı" + destroy_status: "%{name}, %{target} kullanıcısının durumunu kaldırdı" disable_2fa_user: "%{name}, %{target} kullanıcısı için iki adım gereksinimini kapattı" + disable_custom_emoji: "%{target} emoji, %{name} tarafından devre dışı bırakıldı" + disable_user: "%{name} %{target} kullanıcısı için oturum açmayı devre dışı bıraktı" + enable_custom_emoji: "%{name} %{target} için emojiyi etkinleÅŸtirdi" + enable_user: "%{name} %{target} için oturum açmayı etkinleÅŸtirdi" + memorialize_account: "%{name} %{target}'in hesabını bir hatıra sayfasına dönüştürdü" + promote_user: "%{name} %{target} kullanıcısını yükseltti" + remove_avatar_user: "%{name} %{target}'in avatarını kaldırdı" + reopen_report: "%{name} %{target} ÅŸikayetini yeniden açtı" + reset_password_user: "%{name} %{target} kullanıcısının parolasını resetledi" + resolve_report: "%{name} %{target} ÅŸikayetini çözdü" + silence_account: "%{name} %{target}'in hesabını susturdu" + suspend_account: "%{name} %{target}'in hesabını uzaklaÅŸtırdı" + unassigned_report: "%{name} %{target} ÅŸikayetinin atamasını geri aldı" + unsilence_account: "%{name} %{target}'in hesabının susturmasını kaldırdı" + unsuspend_account: "%{name} %{target}'in hesabının uzaklaÅŸtırmasını kaldırdı" + update_custom_emoji: "%{name} %{target} emojiyi güncelledi" + update_status: "%{name}, %{target} kullanıcısının durumunu güncelledi" + deleted_status: "(silinmiÅŸ durum)" + title: Denetim günlüğü + custom_emojis: + assign_category: Kategori ata + by_domain: Alan adı + copied_msg: Emojinin yerel kopyası baÅŸarıyla oluÅŸturuldu + copy: Kopyala + copy_failed_msg: Bu emojinin yerel bir kopyası oluÅŸturulamadı + create_new_category: Yeni kategori ekle + created_msg: Emoji baÅŸarıyla oluÅŸturuldu! + delete: Sil + destroyed_msg: Emojo baÅŸarıyla yok edildi! + disable: Devre dışı bırak + disabled: Devre dışı + disabled_msg: Bu emoji baÅŸarıyla devre dışı bırakıldı + emoji: İfadeler + enable: EtkinleÅŸtir + enabled: Etkin + enabled_msg: Bu emojiyi baÅŸarıyla etkinleÅŸtirdi + image_hint: 50 KB'a kadar PNG + list: Liste + listed: Listelenen + new: + title: Yeni özel emoji ekle + overwrite: Üzerine yaz + shortcode: Kısa kod + shortcode_hint: En az 2 karakter, sadece alfanümerik karakterler ve alt çizgiler + title: Özel emojiler + uncategorized: KategorilenmemiÅŸ + unlist: Liste dışı + unlisted: ListelenmemiÅŸ + update_failed_msg: Bu emoji güncellenemedi + updated_msg: Emoji baÅŸarıyla güncellendi! + upload: Yükle + dashboard: + authorized_fetch_mode: Güvenli mod + backlog: bekleyen iÅŸler + config: Yapılandırma + feature_deletions: Hesap silme + feature_invites: Davet linkleri + feature_profile_directory: Profil dizini + feature_registrations: Kayıtlar + feature_relay: Federasyon aktarıcısı + feature_spam_check: Anti-spam + feature_timeline_preview: Zaman çizelgesi önizlemesi + features: Özellikler + hidden_service: Gizli servislere sahip federasyon + open_reports: raporları aç + pending_tags: inceleme için bekleyen hashtag'ler + pending_users: inceleme için bekleyen kullanıcılar + recent_users: Son kullanıcılar + search: Tam metin araması + single_user_mode: Tek kullanıcı modu + software: Yazılım + space: Alan kullanımı + title: Kontrol Paneli + total_users: toplam kullanıcı + trends: Trendler + week_interactions: bu haftaki etkileÅŸimler + week_users_active: bu hafta aktif + week_users_new: bu hafta kullanıcılar + whitelist_mode: Beyaz liste modu + domain_allows: + add_new: Alan adını beyaz listeye al + created_msg: Alan adı baÅŸarıyla beyaz listeye alındı + destroyed_msg: Alan adı beyaz listeden kaldırıldı + undo: Beyaz listeden çıkar domain_blocks: add_new: Yeni ekle created_msg: Domain bloÄŸu ÅŸu an iÅŸleniyor destroyed_msg: Domain bloÄŸu silindi + domain: Alan adı + edit: Etki alanı bloÄŸunu düzenle + existing_domain_block_html: '%{name}''e zaten daha katı sınırlar uyguladınız, önce <a href="%{unblock_url}">engellemesini kaldırmanız</a> gerekiyor.' new: create: Yeni blok oluÅŸtur hint: Domain bloÄŸu, veri tabanında hesap kayıtlarının oluÅŸturulmasını engellemez, fakat o hesapların üzerine otomatik olarak belirli yönetim metodlarını olarak uygular. severity: desc_html: "<strong>Susturma</strong>, uygulanan hesabın gönderilerini, o hesabı takip etmeyen diÄŸer herkese gizler. <strong>UzaklaÅŸtırma</strong> hesabın bütün içeriÄŸini, ortam dosyalarını ve profil verisini siler." + noop: Yok silence: Sustur suspend: UzaklaÅŸtır title: Yeni domain bloÄŸu + private_comment: Özel yorum + private_comment_hint: Denetleyiciler tarafından dahili kullanım için bu alan adı sınırlaması hakkında yorum. + public_comment: Genel yorum + public_comment_hint: EÄŸer alan adı sınırlamaları listesinin tanıtılması etkinleÅŸtirilmiÅŸse, genel kullanım için bu alan adı sınırlaması hakkında yorum. reject_media: Ortam dosyalarını reddetme reject_media_hint: Yerel olarak depolanmış ortam dosyalarını ve gelecekte indirilecek olanları reddeder. UzaklaÅŸtırma için uygun deÄŸildir + reject_reports: Raporları reddet + reject_reports_hint: Bu alan adından gelen tüm ÅŸikayetleri yok sayın. UzaklaÅŸtırmalar için mevzu dışıdır + rejecting_media: ortam dosyalarını reddet + rejecting_reports: ÅŸikayetleri reddet + severity: + silence: susturulmuÅŸ + suspend: uzaklaÅŸtırılmış show: affected_accounts: one: Veritabanındaki bir hesap etkilendi @@ -178,69 +332,319 @@ tr: title: "%{domain} domain'i için yapılan iÅŸlemi geri al" undo: Geri al undo: Geri al + view: Alan adı bloÄŸunu görüntüle + email_domain_blocks: + add_new: Yeni ekle + created_msg: E-posta alan adı kara listeye baÅŸarıyla eklendi + delete: Sil + destroyed_msg: E-posta alan adı kara listeden baÅŸarıyla silindi + domain: Alan adı + new: + create: Alan adı ekle + title: Yeni e-posta kara liste giriÅŸi + title: E-posta kara listesi + followers: + back_to_account: Hesaba Geri Dön + title: "%{acct} Takipçileri" instances: + by_domain: Alan adı + delivery_available: Teslimat mevcut + known_accounts: + one: "%{count} bilinen hesap" + other: "%{count} bilinen hesap" + moderation: + all: Tümü + limited: Sınırlı + title: Denetim + private_comment: Özel yorum + public_comment: Genel yorum title: Bilinen Sunucular + total_blocked_by_us: Tarafımızca engellenen + total_followed_by_them: Onlar tarafından takip edilen + total_followed_by_us: Tarafımızca takip edilen + total_reported: Onlar hakkında ÅŸikayetler + total_storage: Medya ekleri + invites: + deactivate_all: Tümünü devre dışı bırak + filter: + all: Tümü + available: Mevcut + expired: Süresi dolmuÅŸ + title: Filtre + title: Davetler + pending_accounts: + title: Bekleyen hesaplar (%{count}) + relays: + add_new: Yeni aktarıcı ekle + delete: Sil + description_html: "<strong>Federasyon aktarıcısı</strong>, kendisine abone olan ve yayın yapan sunucular arasında büyük miktarlarda herkese açık gönderilerin deÄŸiÅŸ tokuÅŸunu yapan aracı bir sunucudur. <strong>Küçük ve orta boyutlu sunucuların fediverse'ten içerik keÅŸfetmesine yardımcı olurlar</strong>, aksi takdirde yerel kullanıcıların uzak sunuculardaki diÄŸer kiÅŸileri manuel olarak takip etmeleri gerekecektir." + disable: Devre dışı + disabled: Devre dışı + enable: Etkin + enable_hint: EtkinleÅŸtirildiÄŸinde, sunucunuz bu aktarıcıdan gelecek tüm herkese açık gönderilere abone olacak, ve kendisinin herkese açık gönderilerini bu aktarıcıya göndermeye baÅŸlayacaktır. + enabled: Etkin + inbox_url: Aktarıcı URL'si + pending: Aktarıcının onaylaması için bekleniyor + save_and_enable: Kaydet ve etkinleÅŸtir + setup: Bir aktarıcı baÄŸlantısı kur + signatures_not_enabled: Güvenli mod ya da beyaz liste modu etkin iken aktarıcılar düzgün çalışmayacaktır + status: Durum + title: Aktarıcılar + report_notes: + created_msg: Åžikayet notu baÅŸarıyla oluÅŸturuldu! + destroyed_msg: Åžikayet notu baÅŸarıyla silindi! reports: + account: + note: not + report: ÅŸikayet + action_taken_by: tarafından gerçekleÅŸtirilen eylem + are_you_sure: Emin misiniz? + assign_to_self: Bana ata + assigned: Denetleyici atandı comment: none: Yok + created_at: Åžikayet edildi mark_as_resolved: Giderildi olarak iÅŸaretle + mark_as_unresolved: ÇözümlenmemiÅŸ olarak iÅŸaretle + notes: + create: Not Ekle + create_and_resolve: Not ile çözümle + create_and_unresolve: Not ile tekrar aç + delete: Sil + placeholder: Hangi iÅŸlemlerin yapıldığını, ya da diÄŸer ilgili güncellemeleri açıklayın... + reopen: Åžikayeti tekrar aç report: 'Åžikayet #%{id}' reported_account: Åžikayet edilen hesap reported_by: Åžikayet eden resolved: Giderildi + resolved_msg: Åžikayet baÅŸarıyla çözümlendi! status: Durum title: Åžikayetler + unassign: Atamayı geri al unresolved: Giderilmedi + updated_at: Güncellendi settings: + activity_api_enabled: + desc_html: Yerel olarak yayınlanan durumların, aktif kullanıcıların, ve haftalık kovalardaki yeni kayıtların sayısı + title: Kullanıcı etkinliÄŸi hakkında toplu istatistikler yayınlayın + bootstrap_timeline_accounts: + desc_html: Birden fazla kullanıcı adını virgülle ayırın. Yalnızca yerel ve kilitlenmemiÅŸ hesaplar geçerlidir. BoÅŸ olduÄŸunda varsayılan tüm yerel yöneticilerdir. + title: Yeni kullanıcılar için varsayılan takipler contact_information: email: Herkese açık e-posta adresiniz username: Bir kullanıcı adı giriniz + custom_css: + desc_html: Görünümü her sayfada yüklenecek CSS ile deÄŸiÅŸtirin + title: Özel CSS + default_noindex: + desc_html: Bu ayarı kendileri deÄŸiÅŸtirmeyen tüm kullanıcıları etkiler + title: Varsayılan olarak kullanıcıları arama motoru indekslemesinin dışında tut + domain_blocks: + all: Herkes için + disabled: Hiç kimseye + title: Alan adı bloklarını göster + users: Oturum açan yerel kullanıcılara + domain_blocks_rationale: + title: Gerekçeyi göster + hero: + desc_html: Önsayfada görüntülenir. En az 600x100px önerilir. Ayarlanmadığında, sunucu küçük resmi kullanılır + title: Kahraman görseli + mascot: + desc_html: Birden fazla sayfada görüntülenir. En az 293x205px önerilir. Ayarlanmadığında, varsayılan maskot kullanılır + title: Maskot görseli + peers_api_enabled: + desc_html: Bu sunucunun fediverse'te karşılaÅŸtığı alan adları + title: KeÅŸfedilen sunucuların listesini yayınla + preview_sensitive_media: + desc_html: Medya duyarlı olarak iÅŸaretlenmiÅŸ olsa bile, diÄŸer web sitelerindeki baÄŸlantı ön izlemeleri küçük resim gösterecektir + title: OpenGraph ön izlemelerinde hassas medyayı göster + profile_directory: + desc_html: Kullanıcıların keÅŸfedilebilir olmasına izin ver + title: Profil dizinini etkinleÅŸtir registrations: closed_message: desc_html: Kayıt alımları kapatıldığında ana sayfada görüntülenecek mesajdır. <br> HTML etiketleri kullanabilirsiniz title: Kayıt alımları kapatılma mesajı + deletion: + desc_html: Herkese hesabını silme izni ver + title: Hesap silmeyi aç + min_invite_role: + disabled: Hiç kimse + title: tarafından yapılan davetlere izin ver + registrations_mode: + modes: + approved: Kayıt için onay gerekli + none: Hiç kimse kayıt olamaz + open: Herkes kaydolabilir + title: Kayıt modu + show_known_fediverse_at_about_page: + desc_html: DeÄŸiÅŸtirildiÄŸinde, bilinen bütün fediverse'lerden gönderileri ön izlemede gösterir. DiÄŸer türlü sadece yerel gönderileri gösterecektir. + title: Zaman çizelgesi ön izlemesinde bilinen fediverse'i göster + show_staff_badge: + desc_html: Kullanıcının sayfasında bir personel rozeti göster + title: Personel rozeti göster site_description: desc_html: Ana sayfada paragraf olarak görüntülenecek bilgidir.<br>Özellikle <code><a></code> ve <code><em></code> olmak suretiyle HTML etiketlerini kullanabilirsiniz. title: Site açıklaması site_description_extended: desc_html: Harici bilgi sayfasında gösterilir.<br>HTML etiketleri girebilirsiniz title: Sunucu hakkında detaylı bilgi + site_short_description: + desc_html: Kenar çubuÄŸunda ve meta etiketlerinde görüntülenir. Mastodon'un ne olduÄŸunu ve bu sunucuyu özel kılan ÅŸeyleri tek bir paragrafta açıklayın. + title: Kısa sunucu açıklaması + site_terms: + desc_html: Kendi gizlilik politikanızı, hizmet ÅŸartlarınızı ya da diÄŸer hukuki metinlerinizi yazabilirsiniz. HTML etiketleri kullanabilirsiniz + title: Özel hizmet ÅŸartları site_title: Site baÅŸlığı + spam_check_enabled: + desc_html: Mastodon, tekrar eden istenmeyen mesajlar gönderen hesapları otomatik olarak susturabilir ve ÅŸikayet edebilir. Yanlışlar olabilir. + title: Anti-spam otomasyonu + thumbnail: + desc_html: OpenGraph ve API ile ön izlemeler için kullanılır. 1200x630px tavsiye edilir + title: Sunucu küçük resmi + timeline_preview: + desc_html: Açılış sayfasında genel zaman çizelgesini görüntüle + title: Zaman çizelgesi önizlemesi title: Site Ayarları - subscriptions: - callback_url: Callback linki - confirmed: Onaylandı - expires_in: BitiÅŸ Tarihi - last_delivery: Son gönderim - topic: Konu + trendable_by_default: + desc_html: Daha önce izin verilmeyen etiketleri etkiler + title: Ön inceleme yapmadan etiketlerin trend olmasına izin ver + trends: + desc_html: Åžu anda trend olan ve daha önce incelenen etiketleri herkese açık olarak göster + title: Trend etiketler + statuses: + back_to_account: Hesap sayfasına geri dön + batch: + delete: Sil + nsfw_off: Hassas deÄŸil olarak iÅŸaretle + nsfw_on: Hassas olarak iÅŸaretle + deleted: Silindi + failed_to_execute: Çalıştırılamadı + media: + title: Medya + no_media: Medya yok + no_status_selected: Hiçbiri seçilmediÄŸinden hiçbir durum deÄŸiÅŸtirilmedi + title: Hesap durumları + with_media: Medya ile tags: - accounts: Hesaplar - name: Etiketler + accounts_today: Bugünkü eÅŸsiz kullanımlar + accounts_week: Bu haftaki eÅŸsiz kullanımlar + breakdown: Bugünkü kullanımın kaynaÄŸa göre dağılımı + context: İçerik + directory: Dizinde + in_directory: Dizinde %{count} + last_active: Son aktiflik + most_popular: En popüler + most_recent: En yeni + name: Etiket + review: Durumu gözden geçir + reviewed: Gözden geçirildi title: Etiketler + trending_right_now: Åžu anda trend + unique_uses_today: bugün %{count} gönderi + unreviewed: Gözden geçirilmedi + updated_msg: Etiket ayarları baÅŸarıyla güncellendi title: Yönetim warning_presets: add_new: Yeni ekle delete: Sil edit: Düzenle + edit_preset: Uyarı ön-ayarını düzenle + title: Uyarı ön-ayarlarını yönet + admin_mailer: + new_pending_account: + body: Yeni hesabın detayları aÅŸağıdadır. Bu baÅŸvuruyu onaylayabilir ya da reddedebilirsiniz. + subject: "%{instance} üzerinde gözden geçirmek için yeni hesap (%{username})" + new_report: + body: "%{reporter} %{target}'i ÅŸikayet etti" + body_remote: "%{domain}'den birisi %{target}'i ÅŸikayet etti" + subject: "%{instance} için yeni ÅŸikayet (#%{id})" + new_trending_tag: + body: "#%{name} etiketi bugün trend, ancak daha önce incelenmedi. Siz izin vermediÄŸiniz sürece herkese açık olarak gösterilmeyecek, ya da bir daha asla hakkında bir ÅŸey duymamak için olduÄŸu ÅŸekliyle formu kaydedin." + subject: "%{instance} üzerinde gözden geçirmek için yeni etiket (#%{name})" + aliases: + add_new: Takma ad oluÅŸtur + created_msg: Yeni takma ad baÅŸarıyla oluÅŸturuldu. Artık eski hesaptan taşınmayı baÅŸlatabilirsiniz. + deleted_msg: Yeni takma ad baÅŸarıyla kaldırıldı. O hesaptan bu hesaba taşınmak artık mümkün deÄŸil. + hint_html: BaÅŸka bir hesaptan bu hesaba taşınmak istiyorsanız, takipçileri eski hesaptan bu hesaba taşımadan önce gerekli olan takma adı burada oluÅŸturabilirsiniz. Bu eylem kendi başına <strong>zararsızdır ve geri döndürülebilir</strong>. <strong>Hesap taşıma iÅŸlemi eski hesaptan baÅŸlatılır</strong>. + remove: Takma adların baÄŸlantısını kaldır + appearance: + advanced_web_interface: GeliÅŸmiÅŸ web arayüzü + advanced_web_interface_hint: 'Tüm ekran geniÅŸliÄŸinizden yararlanmak istiyorsanız, geliÅŸmiÅŸ web arayüzü istediÄŸiniz kadar bilgi görecek kadar çok sayıda farklı sütunu yapılandırmanıza olanak tanır: Anasayfa, bildirimler, birleÅŸik zaman çizelgesi, istediÄŸiniz sayıda liste ve etiket.' + animations_and_accessibility: Animasyonlar ve eriÅŸilebilirlik + confirmation_dialogs: Onay iletiÅŸim kutuları + discovery: KeÅŸfet + sensitive_content: Hassas içerik application_mailer: + notification_preferences: E-posta tercihlerini deÄŸiÅŸtir + salutation: "%{name}," settings: 'E-mail tercihlerini deÄŸiÅŸtir: %{link}' view: 'Görüntüle:' + view_profile: Profili Görüntüle + view_status: Durumu görüntüle applications: + created: Uygulama baÅŸarıyla oluÅŸturuldu + destroyed: Uygulama baÅŸarıyla silindi invalid_url: Verilen URL geçerli deÄŸil + regenerate_token: EriÅŸim belirtecini yeniden oluÅŸtur + token_regenerated: EriÅŸim belirteci baÅŸarıyla oluÅŸturuldu + warning: Bu verilere çok dikkat edin. Asla kimseyle paylaÅŸmayın! + your_token: EriÅŸim belirteciniz auth: + apply_for_account: Davet et + change_password: Parola + checkbox_agreement_html: <a href="%{rules_path}" target="_blank">sunucu kuralları</a> ve<a href="%{terms_path}" target="_blank">hizmet ÅŸartlarını</a> kabul ediyorum + checkbox_agreement_without_rules_html: <a href="%{terms_path}" target="_blank">Hizmet ÅŸartlarını</a> kabul ediyorum + delete_account: Hesabı sil + delete_account_html: Hesabınızı silmek isterseniz, <a href="%{path}">buradan devam edebilirsiniz</a>. Sizden onay istenecektir. + description: + prefix_invited_by_user: "@%{name} sizi Mastodon'un bu sunucusuna katılmaya davet ediyor!" + prefix_sign_up: Bugün Mastodon'a kaydolun! + suffix: Bir hesapla, kiÅŸileri takip edebilir, güncellemeler gönderebilir, herhangi bir Mastodon sunucusundan kullanıcılarla mesaj alışveriÅŸinde bulunabilir ve daha birçok ÅŸey yapabilirsin! didnt_get_confirmation: Hesap doÄŸrulama mailini almadınız mı? forgot_password: Parolanızı unuttunuz mu? + invalid_reset_password_token: Parola sıfırlama belirteci geçersiz veya süresi dolmuÅŸ. Lütfen yeni bir tane talep edin. login: GiriÅŸ yap logout: Çıkış + migrate_account: Farklı bir hesaba taşının + migrate_account_html: Bu hesabı baÅŸka bir hesaba yönlendirmek istiyorsanız, <a href="%{path}">buradan yapılandırabilirsiniz</a>. + or_log_in_with: Veya giriÅŸ yapın + providers: + cas: CAS + saml: SAML register: Üye ol + registration_closed: "%{instance} yeni üyeler kabul etmemektedir" resend_confirmation: DoÄŸrulama mailini tekrar gönder reset_password: Parolayı deÄŸiÅŸtir security: Kimlik bilgileri set_new_password: Yeni parola oluÅŸtur + setup: + email_below_hint_html: EÄŸer aÅŸağıdaki e-posta adresi yanlışsa, onu burada deÄŸiÅŸtirebilir ve yeni bir doÄŸrulama e-postası alabilirsiniz. + email_settings_hint_html: Onaylama e-postası %{email} adresine gönderildi. EÄŸer bu e-posta adresi doÄŸru deÄŸilse, hesap ayarlarından deÄŸiÅŸtirebilirsiniz. + title: Kurulum + status: + account_status: Hesap durumu + confirming: E-posta doÄŸrulamasının tamamlanması bekleniyor. + functional: Hesabınız tamamen kullanıma hazır. + pending: BaÅŸvurunuz personelimiz tarafından gözden geçirilmeyi beklemektedir. Bu biraz zaman alabilir. BaÅŸvurunuz onaylanırsa bir e-posta alacaksınız. + redirecting_to: Hesabınız aktif deÄŸil çünkü ÅŸu anda %{acct} adresine yönlendirilmektedir. + trouble_logging_in: Oturum açarken sorun mu yaşıyorsunuz? authorize_follow: + already_following: Bu hesabı zaten takip ediyorsunuz error: Uzak hesap aranırken bir hata oluÅŸtu follow: Takip et + follow_request: 'Åžuna takip isteÄŸi gönderdiniz:' + following: 'BaÅŸarılı! Artık ÅŸunu takip ediyorsunuz:' + post_follow: + close: Ya da, sadece bu pencereyi kapatabilirsiniz. + return: Kullanıcının profilini göster + web: Web'e git title: "%{acct}'i takip et" + challenge: + confirm: Devam et + hint_html: "<strong>İpucu:</strong> Önümüzdeki saat boyunca sana parolanı sormayacağız." + invalid_password: Geçersiz parola + prompt: Devam etmek parolayı doÄŸrulayın datetime: distance_in_words: about_x_hours: "%{count}sa" @@ -255,48 +659,197 @@ tr: x_minutes: "%{count}dk" x_months: "%{count}ay" x_seconds: "%{count}sn" + deletes: + challenge_not_passed: GirdiÄŸiniz bilgi doÄŸru deÄŸildi + confirm_password: KimliÄŸinizi doÄŸrulamak için mevcut parolanızı girin + confirm_username: Prosedürü doÄŸrulamak için kullanıcı adınızı girin + proceed: Hesabı sil + success_msg: Hesabınız baÅŸarıyla silindi + warning: + before: 'Devam etmeden önce, lütfen bu notları dikkatlice okuyun:' + caches: DiÄŸer sunucular tarafından ön belleÄŸe alınan içerik kalabilir + data_removal: Gönderileriniz ve diÄŸer verileriniz kalıcı olarak silinecektir + email_change_html: Hesabınızı silmeden <a href="%{path}">e-posta adresinizi deÄŸiÅŸtirebilirsiniz</a> + email_contact_html: EÄŸer hala ulaÅŸmazsa, yardım için <a href="mailto:%{email}">%{email}</a> adresine e-posta gönderebilirsiniz + email_reconfirmation_html: EÄŸer doÄŸrulama e-postası almıyorsanız, <a href="%{path}">tekrar talep edebilirsiniz</a> + irreversible: Hesabınızı geri yükleyemeyecek ya da yeniden etkinleÅŸtiremeyeceksiniz + more_details_html: Daha fazla ayrıntı için, <a href="%{terms_path}">gizlilik politikası</a>na göz atın. + username_available: Kullanıcı adınız tekrar kullanılabilir olacaktır + username_unavailable: Kullanıcı adınız kullanılamaz kalacaktır + directories: + directory: Profil dizini + explanation: Kullanıcıları ilgi alanlarına göre keÅŸfedin + explore_mastodon: "%{title} keÅŸfet" + domain_validator: + invalid_domain: geçerli bir alan adı deÄŸil errors: + '400': GönderdiÄŸiniz istek geçersiz veya hatalı biçimlendirilmiÅŸ. '403': Bu sayfayı görmek için izniniz yok. '404': Aradığınız sayfa bulunamadı. + '406': Bu sayfa istenen formatta mevcut deÄŸil. '410': Aradığınız sayfa artık yok. '422': content: Güvenlik doÄŸrulaması baÅŸarısız oldu. Site cookie'lerini engellemiÅŸ olabilirsiniz. title: Güvenlik doÄŸrulamasu baÅŸarısız - '429': Throttled - '500': + '429': Kısıtlandı + '500': + content: Üzgünüz, ancak bir ÅŸey ters gitti. + title: Bu sayfa doÄŸru deÄŸil + '503': Geçici sunucu hatası nedeniyle sayfa görüntülenemedi. + noscript_html: Mastodon web uygulamasını kullanmak için lütfen JavaScript'i etkinleÅŸtirin. Alternatif olarak, platformunuz için Mastodon <a href="%{apps_path}">yerel uygulamalardan</a> birini deneyin. + existing_username_validator: + not_found: bu kullanıcı adına sahip yerel bir kullanıcı bulunamadı + not_found_multiple: "%{usernames} bulunamadı" exports: + archive_takeout: + date: Tarih + download: ArÅŸivinizi indirin + hint_html: "<strong>Gönderileriniz ve yüklediÄŸiniz ortamların</strong> bir arÅŸivini talep edebilirsiniz. Dışa aktarılan veriler, herhangi bir uyumlu yazılım tarafından okunabilen ActivityPub formatında olacaktır. Her 7 günde bir arÅŸiv talep edebilirsiniz." + in_progress: ArÅŸivinizi derliyoruz... + request: ArÅŸiv isteÄŸi + size: Boyut blocks: Blokladıklarınız + csv: CSV + domain_blocks: Alan adı blokları follows: Takip ettikleriniz + lists: Listeler mutes: Susturduklarınız storage: Ortam deposu + featured_tags: + add_new: Yeni ekle + errors: + limit: Zaten azami hashtag miktarı belirlediniz + hint_html: "<strong>Öne çıkan etiketler nelerdir?</strong> Genel profilinizde belirgin bir ÅŸekilde görüntülenirler ve kiÅŸilerin genel yayınlarınıza özellikle bu etiketler altında göz atmalarına izin verir. Yaratıcı çalışmaları veya uzun vadeli projeleri takip etmek için harika bir araçtır." + filters: + contexts: + home: Ana zaman çizelgesi + notifications: Bildirimler + public: Genel zaman çizelgesi + thread: Sohbetler + edit: + title: Filtreyi düzenle + errors: + invalid_context: Sıfır ya da geçersiz içerik saÄŸlandı + invalid_irreversible: Geri dönüşümsüz filtreleme sadece anasayfa ya da bildirim baÄŸlamında çalışır + index: + delete: Sil + title: Filtreler + new: + title: Yeni filtre ekle + footer: + developers: GeliÅŸtiriciler + more: Daha Fazla… + resources: Kaynaklar + trending_now: Trendler generic: + all: Tümü changes_saved_msg: DeÄŸiÅŸiklikler baÅŸarıyla kaydedildi! + copy: Kopyala + no_batch_actions_available: Bu sayfada toplu iÅŸlem yok + order_by: Sıralama ölçütü save_changes: DeÄŸiÅŸiklikleri kaydet validation_errors: one: Bir ÅŸeyler ters gitti! Lütfen aÅŸağıdaki hatayı gözden geçiriniz other: Bir ÅŸeyler ters gitti! Lütfen aÅŸağıdaki %{count} hatayı gözden geçiriniz + html_validator: + invalid_markup: 'geçersiz HTML markup içermektedir: %{error}' + identity_proofs: + active: Aktif + authorize: Evet, yetkilendir + authorize_connection_prompt: Bu kriptolu baÄŸlantıyı yetkilendir? + errors: + failed: Kriptolu baÄŸlantı baÅŸarısız oldu. Lütfen %{provider} üzerinden tekrar deneyin. + keybase: + invalid_token: Keybase belirteçleri imza hash'leridir ve 66 hex karakter içermelidir + verification_failed: Keybase, bu belirteci, %{kb_username} Keybase kullanıcısının imzası olarak tanımıyor. Lütfen Keybase'den tekrar deneyin. + wrong_user: "%{current} olarak giriÅŸ yaparken %{proving} için bir kanıt oluÅŸturulamıyor. %{proving} olarak giriÅŸ yapın ve tekrar deneyin." + explanation_html: Burada, Keybase profili gibi diÄŸer kimliklerinizi ÅŸifreli olarak baÄŸlayabilirsiniz. Bu, diÄŸer kiÅŸilerin size ÅŸifreli mesajlar göndermesini ve gönderdiÄŸiniz içeriÄŸe güvenmelerini saÄŸlar. + i_am_html: Ben %{service} üzerinde %{username}. + identity: Kimlik + inactive: Pasif + publicize_checkbox: 'Ve bunu paylaÅŸ:' + publicize_toot: 'İspatlandı! Ben %{service} üzerinde %{username}: %{url}' + status: DoÄŸrulama durumu + view_proof: Kanıt görüntüle imports: + modes: + merge: BirleÅŸtir + merge_long: Mevcut kayıtları sakla ve yenileri ekle + overwrite: Üzerine yaz + overwrite_long: Mevcut kayıtları yenileriyle deÄŸiÅŸtir preface: DiÄŸer sunucudan alarak oluÅŸturduÄŸunuz dosyalar sayesinde, bu sunucudaki hesabınıza takipçilerinizi aktarabilir veya istemediÄŸiniz kiÅŸileri otomatik olarak engelleyebilirsiniz. success: Verileriniz baÅŸarıyla yüklendi ve zaman içinde iÅŸlenecek types: blocking: Engellenenler listesi + domain_blocking: Alan adı engelleme listesi following: Takip edilenler listesi muting: Susturulanlar listesi upload: Yükle + in_memoriam_html: Hatırada. invites: + delete: Devre dışı bırak + expired: Süresi dolmuÅŸ expires_in: - '1800': 30 minutes - '21600': 6 hours - '3600': 1 hour - '43200': 12 hours - '604800': 1 week - '86400': 1 day + '1800': 30 dakika + '21600': 6 saat + '3600': 1 saat + '43200': 12 saat + '604800': 1 hafta + '86400': 1 gün + expires_in_prompt: Asla + generate: OluÅŸtur + invited_by: 'Tarafından davet edildi:' + max_uses: + one: 1 kullanım + other: "%{count} kullanım" + max_uses_prompt: Limit yok + prompt: Bu sunucuya eriÅŸim vermek için baÄŸlantılar oluÅŸturun ve baÅŸkalarıyla paylaşın + table: + expires_at: BitiÅŸ tarihi + uses: Kullanım + title: İnsanları davet et + lists: + errors: + limit: Maksimum liste miktarına ulaÅŸtınız media_attachments: validations: images_and_video: Halihazırda görsel içeren bir gönderiye video ekleyemezsiniz too_many: 4'ten fazla dosya ekleyemezsiniz + migrations: + acct: Taşındı + cancel: Yönlendirmeyi iptal et + cancel_explanation: Yönlendirmeyi iptal etmek, mevcut hesabınızı yeniden etkinleÅŸtirecek, ancak söz konusu hesaba taşınan takipçileri geri getirmeyecektir. + cancelled_msg: Yönlendirme baÅŸarıyla iptal edildi. + errors: + already_moved: zaten taşındığınızla aynı hesap + missing_also_known_as: bu hesaba geri referans yapmamaktadır + move_to_self: mevcut hesap olamaz + not_found: bulunamadı + on_cooldown: SoÄŸuma aÅŸamasındasınız + followers_count: Taşınma zamanındaki takipçiler + incoming_migrations: Farklı bir hesaptan taşınma + incoming_migrations_html: BaÅŸka bir hesaptan bu hesaba taşınmak için önce bir <a href="%{path}">hesap takma adı oluÅŸturmanız</a> gerekmektedir. + moved_msg: Hesabınız ÅŸimdi %{acct} adresine yönlendiriliyor ve takipçileriniz taşınıyor. + not_redirecting: Hesabınız ÅŸu anda baÅŸka bir hesaba yönlendirilmemektedir. + on_cooldown: Son zamanlarda hesabınızı taşıdınız. Bu iÅŸlev %{count} gün içinde tekrar kullanılabilir olacaktır. + past_migrations: GeçmiÅŸ taşınmalar + proceed_with_move: Takipçileri taşı + redirecting_to: Hesabınız %{acct} hesabına yönlendirilmektedir. + set_redirect: Yönlendirme ayarla + warning: + backreference_required: Öncelikle, yeni hesap bu hesabı geri referans edecek ÅŸekilde yapılandırılmalıdır + before: 'Devam etmeden önce, lütfen bu notları dikkatlice okuyun:' + cooldown: Taşındıktan sonra, yeniden taşınma imkanınızın olmayacağı bir soÄŸuma periyodu vardır + disabled_account: Sonrasında, mevcut hesabınız tamamen kullanılabilir olmayacaktır. Ancak, yeniden etkinleÅŸtirme iÅŸleminin yanı sıra veri dışa aktarma eriÅŸimine sahip olacaksınız. + followers: Bu eylem tüm takipçileri ÅŸu anki hesaptan yeni hesaba taşıyacaktır + only_redirect_html: Alternatif olarak, <a href="%{path}">sadece profilinize bir yönlendirme koyabilirsiniz</a>. + other_data: BaÅŸka bir veri otomatik olarak taşınmayacaktır + redirect: Mevcut hesabınızın profili bir yönlendirme bildirimi ile güncellenecek ve aramaların dışında tutulacaktır + moderation: + title: Yönetim notification_mailer: digest: + action: Tüm bildirimleri görüntüle body: Son ziyaretiniz olan %{since}'den beri'da kaçırdığınız ÅŸeylerin özeti mention: "%{name} senden bahsetti:" new_followers_summary: @@ -305,41 +858,190 @@ tr: subject: one: "Son ziyaretinizden beri 1 yeni bildiriminiz var \U0001F418" other: "Son ziyaretinizden beri %{count} yeni bildiriminiz var \U0001F418" + title: Senin yokluÄŸunda... favourite: body: "%{name} durumunuzu favorilere ekledi:" subject: "%{name} favorilere ekledi" + title: Yeni favori follow: body: "%{name} sizi takip etmeye baÅŸladı!" subject: "%{name} sizi takip etmeye baÅŸladı" + title: Yeni takipçi follow_request: + action: Takip isteklerini yönet body: "%{name} size takip isteÄŸi gönderdi" subject: 'Takip isteÄŸi: %{name}' + title: Yeni takip isteÄŸi mention: + action: Yanıt body: "%{name} sizden bahsetti:" subject: "%{name} sizden bahsetti" + title: Yeni bahsetme reblog: body: "%{name} durumunuzu boost etti:" subject: "%{name} durumunuzu boost etti" + title: Yeni gönderi + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T pagination: + newer: Daha yeni next: Sonraki + older: Daha Eski prev: Önceki + truncate: "…" + polls: + errors: + already_voted: Bu ankete daha önce oy verdiniz + duplicate_options: kopya ögeler içeriyor + duration_too_long: ileriye doÄŸru çok geç + duration_too_short: çok erken + expired: Anket çoktan sona erdi + over_character_limit: her biri %{max} karakterden daha uzun olamaz + too_few_options: birden fazla öğeye sahip olmalı + too_many_options: "%{max} öğeden fazla öğe içeremez" + preferences: + other: DiÄŸer + posting_defaults: Gönderi varsayılanları + public_timelines: Genel zaman çizelgeleri + relationships: + activity: Hesap etkinliÄŸi + dormant: Atıl + last_active: Son aktivite + most_recent: En son + moved: Taşındı + mutual: Ortak + primary: Birincil + relationship: İliÅŸki + remove_selected_domains: Seçili alan adlarından tüm takipçileri kaldır + remove_selected_followers: Seçili takipçileri kaldır + remove_selected_follows: Seçili kullanıcıları takip etmeyi bırak + status: Hesap durumu remote_follow: acct: Takip edeceÄŸiniz kiÅŸiyi kullaniciadi@sunuculinki ÅŸeklinde giriniz missing_resource: Hesabınız için yönlendirme linki bulunamadı + no_account_html: Hesabınız yok mu? <a href='%{sign_up_path}' target='_blank'>Buradan kaydolabilirsiniz</a> proceed: Takip onayı prompt: Bu kullanıcıyı takip etmek istediÄŸinize emin misiniz? + reason_html: "<strong>Bu adım neden gerekli?</strong><code>%{instance}</code> kayıtlı olduÄŸunuz sunucu olmayabilir, bu yüzden önce sizi kendi sunucunuza yönlendirmemiz gerekmektedir." + remote_interaction: + favourite: + proceed: Favorilere eklemek için ilerle + prompt: 'Bu gönderiyi favorilerinize eklemek istiyorsunuz:' + reblog: + proceed: Yinelemek için ilerle + prompt: 'Bu gönderiyi yinelemek istiyorsunuz:' + reply: + proceed: Cevap vermek için ilerle + prompt: 'Bu gönderiye cevap vermek istiyorsunuz:' + scheduled_statuses: + over_daily_limit: O gün için %{limit} zamanlanmış gönderi sınırını aÅŸtınız + over_total_limit: "%{limit} zamanlanmış gönderi sınırını aÅŸtınız" + too_soon: Programlanan tarih bugünden ileri bir tarihte olmalıdır + sessions: + activity: Son aktivite + browser: Tarayıcı + browsers: + alipay: Alipay + blackberry: BlackBerry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox + generic: Bilinmeyen tarayıcı + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UC Browser + weibo: Weibo + current_session: Åžu anki oturum + description: "%{platform} üzerinde %{browser}" + explanation: Bunlar ÅŸu anda Mastodon hesabınızda oturum açan web tarayıcılarıdır. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: GNU/Linux + mac: Mac + other: bilinmeyen platform + windows: Windows + windows_mobile: Windows Mobil + windows_phone: Windows Phone + revoke: İptal + revoke_success: Oturum baÅŸarıyla iptal edildi + title: Oturumlar settings: + account: Hesap + account_settings: Hesap ayarları + aliases: Hesap takma adları + appearance: Görünüm authorized_apps: Yetkilendirilen uygulamalar back: Mastodon'a geri dön + delete: Hesap silme + development: GeliÅŸtirme edit_profile: Profili düzenle export: Dışa aktar + featured_tags: Öne çıkan hashtag'ler + identity_proofs: Kimlik belgesi import: İçe aktar + import_and_export: İçe al ve dışarı aktar + migrate: Hesap taşıma + notifications: Bildirim preferences: Tercihler + profile: Profil + relationships: Takip edilenler ve takipçiler two_factor_authentication: İki-faktörlü doÄŸrulama + spam_check: + spam_detected: Bu otomatik bir ÅŸikayettir. Spam tespit edildi. statuses: + attached: + description: 'Ekli: %{attached}' + image: + one: "%{count} görsel" + other: "%{count} görsel" + video: + one: "%{count} video" + other: "%{count} video" + boosted_from_html: "%{acct_link} den yinelendi" + content_warning: 'İçerik uyarısı: %{warning}' + disallowed_hashtags: + one: 'izin verilmeyen bir etiket içeriyordu: %{tags}' + other: 'izin verilmeyen hashtag''leri içeriyordu: %{tags}' + language_detection: Dili otomatik olarak algıla open_in_web: Web sayfasında aç over_character_limit: "%{max} karakter limiti aşıldı" + pin_errors: + limit: Hali hazırda maksimum sayıda gönderiyi sabitlediniz + ownership: BaÅŸkasının gönderisi sabitlenemez + private: Halka açık olmayan gönderi sabitlenemez + reblog: Bir yineleme sabitlenemez + poll: + total_people: + one: "%{count} kiÅŸi" + other: "%{count} kiÅŸi" + total_votes: + one: "%{count} oy" + other: "%{count} oy" + vote: Oy show_more: Daha fazla + sign_in_to_participate: Sohbete katılmak için oturum açın + title: '%{name}: "%{quote}"' visibilities: private: Sadece takipçiler private_long: Sadece takipçilerime gönder @@ -348,22 +1050,57 @@ tr: unlisted: ListelenmemiÅŸ unlisted_long: Herkes görebilir fakat herkese açık zaman tünellerinde listelenmez stream_entries: + pinned: SabitlenmiÅŸ gönderi reblogged: boost edildi sensitive_content: Hassas içerik + tags: + does_not_match_previous_name: önceki adla eÅŸleÅŸmiyor + terms: + title: "%{instance} Hizmet Åžartları ve Gizlilik Politikası" + themes: + contrast: Mastodon (Yüksek karşıtlık) + default: Mastodon (Karanlık) + mastodon-light: Mastodon (Açık) + time: + formats: + default: "%b %d, %Y, %H:%M" + month: "%b %Y" two_factor_authentication: code_hint: Onaylamak için kimlik doÄŸrulama uygulamanızın oluÅŸturduÄŸu kodu giriniz description_html: EÄŸer <strong>iki-faktörlü kimlik doÄŸrulamayı</strong> aktif ederseniz, giriÅŸ yaparken sizin için giriÅŸ kodu üreten telefonunuza ihtiyaç duyacaksınız. disable: Devre dışı bırak enable: AktifleÅŸtir + enabled: İki adımlı kimlik doÄŸrulama etkin enabled_success: İki-faktörlü kimlik doÄŸrulama baÅŸarıyla aktif edildi generate_recovery_codes: Kurtarma Kodlarını OluÅŸtur instructions_html: <strong>Bu QR kodunu, telefonunuzdaki <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a> veya benzer bir TOTP uygulamasıyla taratınız</strong>. Bundan sonra giriÅŸ yaparken uygulamanın ürettiÄŸi kodu kullanarak giriÅŸ yapacaksınız. lost_recovery_codes: Kurtarma kodları telefonunuzu kaybettiÄŸiniz durumlarda hesabınıza eriÅŸim yapabilmenize olanak tanır. EÄŸer kurtarma kodlarınızı kaybettiyseniz burada tekrar oluÅŸturabilirsiniz. Eski kurtarma kodlarınız geçersiz hale gelecektir. manual_instructions: 'EÄŸer QR kodunu taratamıyorsanız ve elle giriÅŸ yapmanız gerekiyorsa buradaki gizli düz metni girebilirsiniz:' + recovery_codes: Kurtarma kodlarını yedekle recovery_codes_regenerated: Kurtarma kodları baÅŸarıyla oluÅŸturuldu recovery_instructions_html: 'EÄŸer telefonunuza eriÅŸiminizi kaybederseniz, aÅŸağıdaki kurtarma kodlarından birini kullanarak hesabınıza giriÅŸ yapabilirsiniz. Kurtarma kodlarınızı güvenli halde tutunuz. ÖrneÄŸin: kodların çıktısını alıp diÄŸer önemli belgeleriniz ile birlikte saklayabilirsiniz.' setup: Kuruluma baÅŸla wrong_code: GirdiÄŸiniz kod geçersiz! Telefonunuzun saati geri/ileri kalmış olabilir. + user_mailer: + backup_ready: + explanation: Mastodon hesabınızın tam bir yedeÄŸini istediniz. Åžimdi indirmeye hazır! + subject: ArÅŸiviniz indirilmeye hazır + title: ArÅŸiv paketlemesi + warning: + explanation: + disable: Hesabınız donmuÅŸken, hesap verileriniz bozulmadan kalır, ancak kilidi açılıncaya kadar herhangi bir iÅŸlem gerçekleÅŸtiremezsiniz. + silence: Hesabınız sınırlı iken, yalnızca sizi takip eden kiÅŸiler bu sunucuda gönderilerinizi görecek ve çeÅŸitli halka açık listelerin dışında tutulabilirsiniz. Ancak, diÄŸerleri hala sizi manuel olarak takip edebilir. + suspend: Hesabınız askıya alındı ve tüm gönderileriniz ve yüklediÄŸiniz medya dosyalarınız bu sunucudan ve takipçilerinizin bulunduÄŸu sunuculardan geri alınamaz ÅŸekilde kaldırıldı. + get_in_touch: "%{instance} çalışanlarıyla iletiÅŸim kurmak için bu e-postayı yanıtlayabilirsiniz." + review_server_policies: Sunucu politikalarını inceleyin + subject: + disable: "%{acct} hesabınız donduruldu" + none: "%{acct} için uyarı" + silence: "%{acct} hesabınız sınırlandırıldı" + suspend: "%{acct} hesabınız askıya alındı" + title: + disable: Hesap donduruldu + none: Uyarı users: invalid_email: E-posta adresiniz geçersiz invalid_otp_token: İki-faktörlü kodunuz geçersiz diff --git a/config/locales/uk.yml b/config/locales/uk.yml index e027b6baee48a24f7fbd8f3d99e72e62389f6e99..1d3459a0f7729efd2f3222b756b8dc096be890ce 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -1,63 +1,113 @@ --- uk: about: - about_hashtag_html: Ðемає публічних поÑтів з хештегом<strong>#%{hashtag}</strong>. Ви можете You can interact with them if you have an account anywhere in the fediverse. - about_mastodon_html: Mastodon - це <em>вільна</em> Ñоціальна мережа з <em>відкритим вихідним кодом</em>. Вона Ñ” <em>децентралізованою</em> альтернативою комерційним платформам, що дозволÑÑ” уникнути ризиків монополізації вашого ÑÐ¿Ñ–Ð»ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð´Ð½Ñ–Ñ”ÑŽ компанією. Виберіть Ñервер, Ñкому ви довірÑєте — що б ви не вибрали, Ви зможете ÑпілкуватиÑÑŒ з уÑіма іншими. Будь-Ñкий кориÑтувач може запуÑтити влаÑну інÑтанцію Mastodon та без проблем брати учаÑть в <em>Ñоціальній мережі</em>. + about_hashtag_html: Це публічні дмухи, позначені Ñимволом <strong>#%{hashtag}</strong>. Ви можете взаємодіÑти з ними, Ñкщо маєте обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ´ÑŒ-де у федіверÑÑ–. + about_mastodon_html: Mastodon — це Ñоціальна мережа, заÑнована на відкритих веб-протоколах та вільному програмному забезпеченні з відкритим кодом. Вона Ñ” децентралізованою на кшталт електронної пошти. about_this: Про цю інÑтанцію + active_count_after: активних + active_footnote: Ðктивні кориÑтувачі міÑÑÑ†Ñ (MAU) administered_by: 'ÐдмініÑтратор:' + api: API + apps: Мобільні додатки + apps_platforms: КориÑтуйтеÑÑŒ Mastodon на iOS, Android та інших платформах + browse_directory: ПереглÑдайте каталог профілів та фільтруйте за інтереÑами + browse_public_posts: ПереглÑдайте потік публічних поÑтів на Mastodon contact: Зв'ÑзатиÑÑ contact_missing: Ðе зазначено contact_unavailable: ÐедоÑтупно + discover_users: Знайдіть цікавих кориÑтувачів documentation: Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ - extended_description_html: | - <h3>Гарне міÑце Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»</h3> - <p>Детальний Ð¾Ð¿Ð¸Ñ Ñ‰Ðµ не налаштований.</p> - generic_description: "%{domain} Ñ” одним Ñервером у мережі" + federation_hint_html: З обліковим запиÑом на %{instance} ви зможете Ñлідкувати за людьми на будь-Ñкому Ñервері Mastodon та поза ним. + get_apps: Спробуйте мобільний додаток hosted_on: Mastodon розміщено на %{domain} + instance_actor_flash: 'Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” віртуальною оÑобою, Ñка викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ñамого Ñервера, а не певного кориÑтувача. Він викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ± федерації Ñ– не повинен бути заблокований, Ñкщо тільки ви не хочете заблокувати веÑÑŒ Ñервер, у цьому випадку ви повинні ÑкориÑтатиÑÑ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñм домену. + +' learn_more: ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ privacy_policy: Політика приватноÑті + see_whats_happening: ПоглÑньте, що відбуваєтьÑÑ + server_stats: 'СтатиÑтика Ñерверу:' source_code: Вихідний код + status_count_after: + few: ÑтатуÑа + many: ÑтатуÑів + one: ÑÑ‚Ð°Ñ‚ÑƒÑ + other: ÑтатуÑи status_count_before: Опубліковано + tagline: Слідкуйте за друзÑми та знаходьте нових terms: Правила викориÑÑ‚Ð°Ð½Ð½Ñ + unavailable_content: ÐедоÑтупний вміÑÑ‚ + unavailable_content_description: + domain: Сервер + reason: Причина + user_count_after: + few: кориÑтувача + many: кориÑтувачів + one: кориÑтувач + other: кориÑтувачі user_count_before: Тут живе what_is_mastodon: Що таке Mastodon? accounts: + choices_html: 'Ð’Ð¿Ð¾Ð´Ð¾Ð±Ð°Ð½Ð½Ñ %{name}:' follow: ПідпиÑатиÑÑ + followers: + few: ПідпиÑника + many: ПідпиÑників + one: ПідпиÑник + other: ПідпиÑників following: ПідпиÑаний(-а) joined: ПриєднавÑÑ %{date} + last_active: оÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть + link_verified_on: Права влаÑноÑті на це поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¸ перевірені %{date} media: Медіа - moved_html: "%{name} переїхав на %{new_profile_link}:" + moved_html: "%{name} переїхав до %{new_profile_link}:" network_hidden: Ð¦Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð½ÐµÐ´Ð¾Ñтупна + never_active: Ðіколи nothing_here: Тут нічого немає! people_followed_by: Люди, на Ñких підпиÑаний(-а) %{name} people_who_follow: ПідпиÑники %{name} - posts_with_replies: ПоÑти Ñ– відповіді + pin_errors: + following: Ви повинні бути підпиÑаним на людину, Ñку бажаєте Ñхвалити + posts: + few: Дмуха + many: Дмухів + one: Дмух + other: Дмухів + posts_tab_heading: Дмухи + posts_with_replies: Дмухи та відповіді reserved_username: Це ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача зарезервоване roles: admin: ÐдмініÑтратор bot: Бот moderator: Мод + unavailable: Профіль недоÑтупний unfollow: ВідпиÑатиÑÑ admin: + account_actions: + action: Виконати дію + title: ЗдійÑнити модераційну дію над %{acct} account_moderation_notes: - create: Залишити примітки - created_msg: Примітку модератора уÑпішно Ñтворено! + create: Залишити нотатку + created_msg: Ðотатку модератора уÑпішно Ñтворено! delete: Видалити - destroyed_msg: Примітку модератора уÑпішно видалено! + destroyed_msg: Ðотатку модератора уÑпішно видалено! accounts: + approve: Схвалити + approve_all: Схвалити вÑÑ–Ñ… are_you_sure: Ви впевнені? avatar: Ðватар by_domain: Домен change_email: - changed_msg: Поштова адреÑа аккаунту уÑпішно змінена! - current_email: Поточна поштова адреÑа - label: Змінити поштову адреÑу - new_email: Ðовий e-mail - submit: Змінити поштову адреÑу - title: Змінити поштову адреÑу Ð´Ð»Ñ %{username} + changed_msg: ÐдреÑу електронної пошти облікового запиÑу уÑпішно змінено! + current_email: Поточна адреÑа електронної пошти + label: Змінити адреÑу електронної пошти + new_email: Ðова адреÑа електронної пошти + submit: Змінити адреÑу електронної пошти + title: Змінити адреÑу електронної пошти Ð´Ð»Ñ %{username} confirm: Зберегти confirmed: Збережено confirming: ЗберігаєтьÑÑ + deleted: Видалено demote: УÑунути disable: Вимкнути disable_two_factor_authentication: Вимкнути двофакторну авторизацію @@ -65,14 +115,19 @@ uk: display_name: Відображуване ім'Ñ domain: Домен edit: Змінити - email_status: Ð¡Ñ‚Ð°Ñ‚ÑƒÑ e-mail + email: Електронна пошта + email_status: Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти enable: Увімкнути enabled: Увімкнено - feed_url: URL фіду + feed_url: URL-адреÑа каналу followers: ПідпиÑники followers_url: URL підпиÑників follows: ПідпиÑки - inbox_url: Вхідний URL + header: Заголовок + inbox_url: URL вхідних повідомлень + invited_by: 'ЗапроÑив:' + ip: IP + joined: ПриєднавÑÑ location: all: УÑÑ– local: Локальні @@ -82,29 +137,37 @@ uk: media_attachments: Мультимедійні Ð²ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ memorialize: Зробити пам'Ñтником moderation: + active: Ðктивний all: УÑÑ– + pending: Очікують silenced: Заглушені - suspended: Заблоковані + suspended: Призупинені title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ñ–Ñ - moderation_notes: Примітки модераторів + moderation_notes: Ðотатки модераторів most_recent_activity: ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть most_recent_ip: ОÑтанній IP + no_account_selected: Жоден обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ðµ було змінено, оÑкільки жоден не було вибрано + no_limits_imposed: Жодних обмежень не накладено not_subscribed: Ðе підпиÑані - outbox_url: Вихідний URL - perform_full_suspension: Повне Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ + outbox_url: URL вихідних повідомлень + pending: Відгук в очікуванні + perform_full_suspension: Призупинити profile_url: URL профілю promote: ПроÑунути protocol: Протокол public: Публічний push_subscription_expires: ПідпиÑка PuSH Ñпливає redownload: Оновити аватар + reject: Відхилити + reject_all: Відхилити уÑе remove_avatar: Видалити аватар + remove_header: Видалити заголовок resend_confirmation: already_confirmed: Цей кориÑтувач уже підтверджений - send: ÐадіÑлати Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ‰Ðµ раз - success: ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· підтвердженнÑм уÑпішно надіÑлано! + send: ÐадіÑлати електронний лиÑÑ‚-Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ‰Ðµ раз + success: Електронний лиÑÑ‚-Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÑƒÑпішно надіÑлано! reset: Скинути - reset_password: Зкинути пароль + reset_password: Скинути пароль resubscribe: ПерепідпиÑатиÑÑ role: Дозволи roles: @@ -112,30 +175,38 @@ uk: moderator: Модератор staff: ПерÑонал user: КориÑтувач + salmon_url: Salmon URL search: Пошук shared_inbox_url: URL Ñпільного вхідного кошика show: - created_reports: Скарги Ñтворені цим аккаунтом + created_reports: Скарги, Ñтворені цим аккаунтом targeted_reports: Скарги щодо цього аккаунту silence: Ð“Ð»ÑƒÑˆÐµÐ½Ð½Ñ + silenced: Заглушені statuses: СтатуÑи subscribe: ПідпиÑатиÑÑ - title: Ðкаунти - unconfirmed_email: Ðепідтверджений e-mail + suspended: Призупинені + time_in_queue: Очікує в черзі %{time} + title: Облікові запиÑи + unconfirmed_email: Ðепідтверджена адреÑа електронної пошти undo_silenced: ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ - undo_suspension: ЗнÑти Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ + undo_suspension: ЗнÑти Ð¿Ñ€Ð¸Ð·ÑƒÐ¿Ð¸Ð½ÐµÐ½Ð½Ñ unsubscribe: ВідпиÑатиÑÑ username: Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача - web: WWW + warn: ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ + web: Веб + whitelisted: У білому ÑпиÑку action_logs: actions: assigned_to_self_report: "%{name} призначив(-ла) Ñкаргу %{target} на Ñебе" change_email_user: "%{name} змінив(-ла) поштову адреÑу кориÑтувача %{target}" confirm_user: "%{name} підтвердив(-ла) ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¿Ð¾ÑˆÑ‚Ð¾Ð²Ð¾Ñ— адреÑи кориÑтувача %{target}" + create_account_warning: "%{name} надіÑлав Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ð´Ð¾ %{target}" create_custom_emoji: "%{name} вивантажив(-ла) нове емодзі %{target}" create_domain_block: "%{name} заблокував(-ла) домен %{target}" create_email_domain_block: "%{name} додав(-ла) поштовий домен %{target} до чорного ÑпиÑку" demote_user: "%{name} понизив(-ла) %{target}" + destroy_custom_emoji: "%{name} знищив(-ла) емодзі %{target}" destroy_domain_block: "%{name} розблокував(-ла) домен %{target}" destroy_email_domain_block: "%{name} додав(-ла) поштовий домен %{target} до білого ÑпиÑку" destroy_status: "%{name} видалив(-ла) ÑÑ‚Ð°Ñ‚ÑƒÑ ÐºÐ¾Ñ€Ð¸Ñтувача %{target}" @@ -144,19 +215,20 @@ uk: disable_user: "%{name} заборонив(-ла) авторизацію кориÑтувачу %{target}" enable_custom_emoji: "%{name} увімкнув(-ла) емодзі %{target}" enable_user: "%{name} увімкнув(-ла) авторизацію кориÑтувачу %{target}" - memorialize_account: "%{name} перетворив(-ла) Ñторінку %{target} у пам'Ñтник" + memorialize_account: "%{name} перетворив(-ла) обліковий Ð·Ð°Ð¿Ð¸Ñ %{target} на Ñторінку пам'Ñті" promote_user: "%{name} підвищив(-ла) кориÑтувача %{target}" remove_avatar_user: "%{name} прибрав(-ла) аватар кориÑтувача %{target}" reopen_report: "%{name} перевідкрив(-ла) Ñкаргу %{target}" reset_password_user: "%{name} Ñкинув(-ла) пароль кориÑтувача %{target}" resolve_report: "%{name} розв'Ñзав(-ла) Ñкаргу %{target}" - silence_account: "%{name} заглушив(-ла) аккаунт %{target}" - suspend_account: "%{name} заблокував аккаунт кориÑтувача %{target}" + silence_account: "%{name} заглушив(-ла) обліковий Ð·Ð°Ð¿Ð¸Ñ %{target}" + suspend_account: "%{name} заморозив обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %{target}" unassigned_report: "%{name} знÑв(-ла) Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñкарги %{target}" - unsilence_account: "%{name} розглушив(-ла) аккаунт %{target}" - unsuspend_account: "%{name} розблокував аккаунт кориÑтувача %{target}" + unsilence_account: "%{name} розглушив(-ла) обліковий Ð·Ð°Ð¿Ð¸Ñ %{target}" + unsuspend_account: "%{name} розморозив обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %{target}" update_custom_emoji: "%{name} оновив(-ла) емодзі %{target}" update_status: "%{name} змінив(-ла) ÑÑ‚Ð°Ñ‚ÑƒÑ ÐºÐ¾Ñ€Ð¸Ñтуача %{target}" + deleted_status: "(видалений ÑтатуÑ)" title: Журнал подій custom_emojis: by_domain: Домен @@ -167,69 +239,100 @@ uk: delete: Видалити destroyed_msg: Емодзі уÑіпішно видалене! disable: Вимкнути + disabled: Вимкнено disabled_msg: Емодзі уÑпішно вимкнено emoji: Емодзі enable: Увімкнути + enabled: Увімкнено enabled_msg: Емодзі уÑпішно увімкнене image_hint: PNG розміром до 50 КБ + list: СпиÑок listed: У ÑпиÑку new: title: Додати новий емодзі overwrite: ПерепиÑати shortcode: Шорткод - shortcode_hint: Мінімум два Ñимволи, тільки цифрові й латинÑькі Ñимволи або нижні підкреÑÐ»ÐµÐ½Ð½Ñ + shortcode_hint: Мінімум два Ñимволи, тільки цифрові й латинÑькі Ñимволи або підкреÑÐ»ÐµÐ½Ð½Ñ title: ОÑобливі емодзі + uncategorized: Без категорії unlisted: Ðе у ÑпиÑку update_failed_msg: Ðе вийшло оновити емозді updated_msg: Емодзі уÑпішно оновлене! upload: Вивантажити dashboard: + backlog: відкладені Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ config: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ - feature_deletions: Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ñ–Ð² + feature_deletions: Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ñ… запиÑів feature_invites: ПоÑиланнÑ-Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ + feature_profile_directory: Каталог профілів feature_registrations: РеєÑтрації + feature_relay: РетранÑлÑтор дмухів між Ñерверами + feature_spam_check: Ðнти-Ñпам + feature_timeline_preview: Передпоказ Ñтрічки features: МожливоÑті hidden_service: Ð¤ÐµÐ´ÐµÑ€Ð°Ñ†Ñ–Ñ Ð· прихованими ÑервіÑами open_reports: відкриті Ñкарги + pending_tags: хештеги, що очікують на переглÑд + pending_users: кориÑтувачі, що очікують на переглÑд recent_users: ОÑтанні кориÑтувачі search: ПовнотекÑтовий пошук single_user_mode: Режим одного кориÑтувача software: Програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ space: ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð¸Ñкового проÑтору - title: Дашборд + title: Приборна панель total_users: кориÑтувачів загалом trends: Тренди week_interactions: дій за цей тиждень week_users_active: активно протÑгом Ñ‚Ð¸Ð¶Ð½Ñ week_users_new: кориÑтувачів цього Ñ‚Ð¸Ð¶Ð½Ñ + whitelist_mode: Режим білого ÑпиÑку + domain_allows: + add_new: Додати домен до білого ÑпиÑку + created_msg: Домен було уÑпішно додано до білого ÑпиÑку + destroyed_msg: Домен було видалено з білого ÑпиÑку + undo: Видалити з білого ÑпиÑку domain_blocks: add_new: Додати нове - created_msg: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ оброблюєтьÑÑ + created_msg: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ оброблÑєтьÑÑ destroyed_msg: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ знÑто domain: Домен + edit: Редагувати Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñ–Ð² + existing_domain_block_html: Ви вже наклали більш Ñуворі Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð° %{name}, вам треба Ñпочатку <a href="%{unblock_url}">розблокувати його</a>. new: create: Створити Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ - hint: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ не завадить Ñтворенню нових акаунтів у базі даних, але ретроактивно та автоматично заÑтоÑує вказані методи модерації Ð´Ð»Ñ Ñ†Ð¸Ñ… акаунтів. + hint: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ не завадить Ñтворенню нових облікових запиÑів у базі даних, але ретроактивно та автоматично заÑтоÑує до них конкретні методи модерації. severity: - desc_html: "<strong>ГлушеннÑ</strong> зробить ÑтатуÑи акаунту невидимими Ð´Ð»Ñ Ð²ÑÑ–Ñ…, окрім їхніх підпиÑників. <strong>БлокуваннÑ</strong> видалить увеÑÑŒ контент акаунту, включаючи мультимедійні Ð²ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ Ñ‚Ð° дані профілю." + desc_html: "<strong>ГлушеннÑ</strong> зробить поÑти облікового запиÑу невидимими Ð´Ð»Ñ Ð²ÑÑ–Ñ…, окрім його підпиÑників. <strong>ЗаморожуваннÑ</strong> видалить увеÑÑŒ контент, медіа та дані профілю облікового запиÑу. Якщо ви хочете лише заборонити медіафайли, оберіть <strong>Ðічого</strong>." noop: Ðічого silence: Ð“Ð»ÑƒÑˆÐµÐ½Ð½Ñ suspend: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ title: Ðове Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ + private_comment: Приватний коментар + private_comment_hint: Прокоментуйте Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ домену, а модератори прочитають. + public_comment: Публічний коментар + public_comment_hint: Прокоментуйте Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ домену, це зможуть прочитати вÑÑ–, Ñкщо така Ð¾Ð¿Ñ†Ñ–Ñ Ð²Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð°. reject_media: Заборонити медіаконтент reject_media_hint: ВидалÑÑ” медіаконтент, збережений локально, Ñ– заборонÑÑ” його Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ майбутньому. Ðе має Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ випадку Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ + reject_reports: Відхилити Ñкарги + reject_reports_hint: Ігнорувати вÑÑ– Ñкарги з цього домену. Ðе має Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ випадку Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ + rejecting_media: Ð²Ñ–Ð´Ñ…Ð¸Ð»ÐµÐ½Ð½Ñ Ð¼ÐµÐ´Ñ–Ð°Ñ„Ð°Ð¹Ð»Ñ–Ð² + rejecting_reports: Ð²Ñ–Ð´Ñ…Ð¸Ð»ÐµÐ½Ð½Ñ Ñкарг + severity: + silence: заглушені + suspend: призупинені show: affected_accounts: - few: Впливає на %{count} акаунти у базі даних - many: Впливає на %{count} акаунтів у базі даних - one: Впливає на один акаунт у базі даних - other: Впливає на %{count} акаунтів у базі даних + few: Впливає на %{count} облікових запиÑи у базі даних + many: Впливає на %{count} облікових запиÑів у базі даних + one: Впливає на один обліковий Ð·Ð°Ð¿Ð¸Ñ Ñƒ базі даних + other: Впливає на %{count} облікових запиÑів у базі даних retroactive: - silence: ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ… Ñ–Ñнуючих акаунтів цього домену - suspend: ЗнÑти Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð· уÑÑ–Ñ… Ñ–Ñнуючих акаунтів цього домену + silence: ЗнÑти Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ… Ñ–Ñнуючих заглушених облікових запиÑів цього домену + suspend: Розморозити Ñ–Ñнуючі заморожені облікові запиÑи з цього домену title: ЗнÑти Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð· домена %{domain} undo: Відмінити undo: Відмінити + view: ПереглÑнути заблоковані домени email_domain_blocks: add_new: Додати created_msg: УÑпішно додано поштовий домен до чорного ÑпиÑку @@ -238,25 +341,63 @@ uk: domain: Домен new: create: Додати домен - title: Ðове доменне Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ email + title: Ðове Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑˆÑ‚Ð¾Ð²Ð¾Ð³Ð¾ домену title: Чорний ÑпиÑок поштових доменів + followers: + back_to_account: ПовернутиÑÑ Ð´Ð¾ Облікового запиÑу + title: ПідпиÑники %{acct} instances: + by_domain: Домен + delivery_available: ДоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне + known_accounts: + few: "%{count} відомих облікових запиÑів" + many: "%{count} відомих облікових запиÑів" + one: "%{count} відомий обліковий запиÑ" + other: "%{count} відомих облікових запиÑів" + moderation: + all: УÑÑ– + limited: Обмежені + title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ñ–Ñ + private_comment: Приватний коментар + public_comment: Публічний коментар title: Відомі інÑтанції + total_blocked_by_us: Заблокованих нами + total_followed_by_them: Вони Ñтежать за + total_followed_by_us: Ми Ñтежимо за + total_reported: Звітів про них + total_storage: Мультимедійні Ð²ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ invites: + deactivate_all: Деактивувати вÑÑ– filter: all: Ð’Ñе available: ДоÑтупно expired: ПроÑрочено title: Фільтр title: Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ + pending_accounts: + title: Облікові запиÑи у черзі (%{count}) relays: + add_new: Додати новий ретранÑлÑтор + delete: Видалити + description_html: "<strong>РетлÑнÑлÑтор дмухів</strong> (federation relay) — це проміжний Ñервер, що обмінюєтьÑÑ Ð²ÐµÐ»Ð¸ÐºÐ¸Ð¼Ð¸ обÑÑгами публічних дмухів між Ñерверами, Ñкі цього хочуть. Він може допомогти маленьким та Ñереднім Ñерверам отримувати вміÑÑ‚ з уÑього <strong>федеÑвіту</strong> (fediverse). Без нього локальним кориÑтувачам довелоÑÑ Ð± вручну підпиÑуватиÑÑ Ð½Ð° людей з віддалених Ñерверів." + disable: Вимкнути + disabled: Вимкнено + enable: Увімкнути + enable_hint: Коли ретранÑлÑцію увімкнено, ваш Ñервер буде підпиÑаний на уÑÑ– публічні дмухи з цього ретранÑлÑтора, та почне поÑилати до нього Ñвої публічні дмухи. + enabled: Увімкнено + inbox_url: URL ретранÑлÑтора + pending: Очікуємо на Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÑ‚Ñ€Ð°Ð½ÑлÑтором + save_and_enable: Зберегти та увімкнути + setup: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· ретранÑлÑтором + signatures_not_enabled: РетранÑлÑтори не будуть добре працювати поки ввімкнений безопаÑний режим або режим білого ÑпиÑка status: Ð¡Ñ‚Ð°Ñ‚ÑƒÑ + title: РетранÑлÑтори report_notes: created_msg: Скарга уÑпішно Ñтворена! destroyed_msg: Скарга уÑпішно видалена! reports: account: - note: примітка + note: нотатка report: Ñкарга action_taken_by: Ð”Ñ–Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð° are_you_sure: Ви впевнені? @@ -275,7 +416,7 @@ uk: placeholder: Опишіть, Ñкі дії були виконані, або інші зміни, що ÑтоÑуютьÑÑ Ñправи... reopen: Перевідкрити Ñкаргу report: 'Скарга #%{id}' - reported_account: Ðкаунт порушника + reported_account: Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ñ€ÑƒÑˆÐ½Ð¸ÐºÐ° reported_by: Відправник Ñкарги resolved: Вирішено resolved_msg: Скаргу уÑпішно вирішено! @@ -289,32 +430,55 @@ uk: desc_html: КількіÑть локальних поÑтів, активних та нових кориÑтувачів у тижневих розрізах title: ÐŸÑƒÐ±Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð°Ð³Ñ€ÐµÐ³Ð¾Ð²Ð°Ð½Ð¾Ñ— ÑтатиÑтики про активніÑть кориÑтувачів bootstrap_timeline_accounts: - title: ПідпиÑки за замовчуваннÑм Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… кориÑтувачів + desc_html: РозділÑйте імена кориÑтувачів комами. Працюватимуть тільки локальні Ñ– розблоковані облікові запиÑи. Якщо порожньо, то типово це вÑÑ– локальні адмініÑтратори. + title: Типові підпиÑки Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… кориÑтувачів contact_information: email: Введіть публічний email username: Введіть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача + custom_css: + desc_html: Відобразити виглÑд, коли CSS завантажено Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ— Ñторінки + title: КориÑтувацький CSS + default_noindex: + desc_html: Впливає на уÑÑ–Ñ… кориÑтувачів, Ñкі не змінили це наÑÑ‚Ñ€Ð¾ÑŽÐ²Ð°Ð½Ð½Ñ ÑамоÑтійно + domain_blocks: + all: Ð’Ñi + disabled: Ðікого + title: Показати, Ñкі домени заблоковані + domain_blocks_rationale: + title: ÐžÐ±Ò‘Ñ€ÑƒÐ½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ hero: - desc_html: ВідображаєтьÑÑ Ð½Ð° головній Ñторінці. Рекомендована Ñк мінімум 600x100 пікÑелів. Якщо не вказано, буде викориÑтано передпоказ інÑтанції + desc_html: ВідображаєтьÑÑ Ð½Ð° головній Ñторінці. Рекомендовано Ñк мінімум 600x100 пікÑелів. Якщо не вказано, буде викориÑтано передпоказ інÑтанції title: Банер інÑтанції + mascot: + desc_html: ВідображаєтьÑÑ Ð½Ð° багатьох Ñторінках. Щонайменше 293×205 пікÑелів рекомендовано. Якщо не вказано, буде викориÑтано таліÑман інÑтанції + title: ТаліÑман peers_api_enabled: desc_html: Доменні ім'Ñ, помічені цією інÑтанцією федиÑвіту title: Опублікувати ÑпиÑок знайдених інÑтанцій preview_sensitive_media: - desc_html: Передпоказ поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° інших Ñайтах буде відображати мініатюру навіть Ñкщо медіа відмічене Ñк вразливе - title: Показувати вразливе Ð¼ÐµÐ´Ñ–Ñ Ñƒ перепоказі OpenGraph + desc_html: Передпоказ поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° інших Ñайтах буде відображати мініатюру навіть Ñкщо медіа відмічене Ñк дражливе + title: Показувати дражливе медіа у передпоказах OpenGraph + profile_directory: + desc_html: Дозволити кориÑтувачам бути видимими + title: Увімкнути каталог профілів registrations: closed_message: desc_html: ВідображаєтьÑÑ Ð½Ð° титульній Ñторінці, коли реєÑÑ‚Ñ€Ð°Ñ†Ñ–Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð° <br>Можна викориÑтовувати HTML-теги title: ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ закриту реєÑтрацію deletion: - desc_html: Дозволити будь-кому видалÑти Ñвій аккаунт - title: Дозволити Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ñ–Ð² + desc_html: Дозволити будь-кому видалÑти Ñвій обліковий Ð·Ð°Ð¿Ð¸Ñ + title: Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¾Ð³Ð¾ облікового запиÑу min_invite_role: disabled: Ðіхто title: Дозволити Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð²Ñ–Ð´ + registrations_mode: + modes: + approved: Ð”Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ потрібне ÑÑ…Ð²Ð°Ð»ÐµÐ½Ð½Ñ + none: Ðіхто не може увійти + open: Будь-хто може увійти show_known_fediverse_at_about_page: - desc_html: Коли увімкнено, будуть показані поÑти з уÑього відомого федиÑвіту у передпоказі. Інакше будуть показані локальні поÑти. - title: Показувати доÑтупний федиÑвіт у передпоказі фіду + desc_html: Коли увімкнено, будуть показані поÑти з уÑього відомого федиÑвіту у передпоказі. Інакше будуть показані лише локальні дмухи. + title: Показувати доÑтупний федиÑвіт у передпоказі Ñтрічки show_staff_badge: desc_html: Відмічати перÑонал на Ñторінці кориÑтувачів title: Показувати перÑонал @@ -324,44 +488,87 @@ uk: site_description_extended: desc_html: ВідображаєтьÑÑ Ð½Ð° Ñторінці додаткової информації<br>Можна викориÑтовувати HTML-теги title: Розширений Ð¾Ð¿Ð¸Ñ Ñайту + site_short_description: + title: Короткий Ð¾Ð¿Ð¸Ñ Ñервера site_terms: desc_html: |- Ви можене напиÑати влаÑну політику приватноÑті, умови викориÑтанні та інші законні штуки<br> Можете викориÑтовувати HTML теги title: ОÑобливі умови викориÑÑ‚Ð°Ð½Ð½Ñ site_title: Ðазва Ñайту + spam_check_enabled: + desc_html: Mastodon може автоматично глушити та автоматично звітувати про облікові запиÑи, Ñкі надÑилають повторні небажані повідомленнÑ. Можливі хибно-позитивні ÑпрацьовуваннÑ. + title: ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð°Ð½Ñ‚Ð¸Ñпаму thumbnail: desc_html: ВикориÑтовуєтьÑÑ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð¿Ð¾ÐºÐ°Ð·Ñ–Ð² через OpenGraph та API. Бажано розміром 1200Ñ…640 пікÑелів title: Мініатюра інÑтанції timeline_preview: - desc_html: Показувати публічний фід на головній Ñторінці + desc_html: Показувати публічну Ñтрічку на головній Ñторінці title: Передпоказ фіду title: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñайту + trends: + desc_html: Відображати розглÑнуті хештеґи, Ñкі популÑрні зараз + title: ПопулÑрні хештеги statuses: - back_to_account: Ðазад на Ñторінку профілю + back_to_account: Ðазад до Ñторінки облікового запиÑу batch: delete: Видалити - nsfw_off: Відмітити ÑприйнÑтливим - nsfw_on: Відмітити неÑприйнÑтливим + nsfw_off: Відмітити прийнÑтним + nsfw_on: Відмітити неприйнÑтним + deleted: Видалено failed_to_execute: Ðе вийшло media: title: Медіа no_media: Ðемає медіа - title: СтатуÑи аккаунтів + no_status_selected: Жодного ÑтатуÑа не було змінено, оÑкільки жодного не було вибрано + title: СтатуÑи облікових запиÑів with_media: З медіа - subscriptions: - confirmed: Підтверджено - expires_in: Спливає через - last_delivery: ОÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð¾Ñтавка - topic: Тема + tags: + accounts_today: Унікальних викориÑтань за Ñьогодні + accounts_week: Унікальних викориÑтань за тиждень + breakdown: Ðналіз викориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð° Ñьогодні за джерелом + context: КонтекÑÑ‚ + directory: У каталозі + in_directory: "%{count} у каталозі" + name: Хештеґ + review: ПереглÑнути ÑÑ‚Ð°Ñ‚ÑƒÑ + reviewed: ПереглÑнуто + title: Хештеґи + trending_right_now: ПопулÑрні Ñаме зараз + unique_uses_today: "%{count} поÑÑ‚Ñть Ñьогодні" + unreviewed: Ðе переглÑнуто + updated_msg: Параметри хештеґів уÑпішно оновлені title: ÐдмініÑÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ + warning_presets: + add_new: Додати новий + delete: Видалити + edit: Редагувати + edit_preset: Редагувати шаблон Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ + title: Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸ попереджень admin_mailer: + new_pending_account: + body: Деталі нового облікового запиÑу наведено нижче. Ви можете Ñхвалити або відхилити цю заÑву. + subject: Ðовий обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð°Ð´Ñ–Ñлано на розглÑд на %{instance} (%{username}) new_report: - body: "%{reporter} поÑкарживÑÑ(-лаÑÑ) %{target}" - body_remote: ХтоÑÑŒ з домену %{domain} поÑкарживÑÑ(-лаÑÑ) %{target} + body: "%{reporter} поÑкарживÑÑ(-лаÑÑ) на %{target}" + body_remote: ХтоÑÑŒ з домену %{domain} поÑкарживÑÑ(-лаÑÑ) на %{target} subject: Ðова Ñкарга до %{instance} (#%{id}) + new_trending_tag: + subject: Ðовий хештеґ надіÑлано на розглÑд до %{instance} (#%{name}) + aliases: + add_new: Створити пÑевдонім + created_msg: Ðовий пÑевдонім уÑпішно Ñтворено. Тепер ви можете починати Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð·Ñ– Ñтарого облікового запиÑу. + deleted_msg: ПÑевдонім уÑпішно видалено. ÐŸÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð· того облікового запиÑу до цього більше не можливе. + appearance: + advanced_web_interface: Розширений web-Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ + advanced_web_interface_hint: 'Розширений веб-Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð°Ñ” змогу бачити багато Ñтовпчиків одночаÑно: оÑновна Ñторінка, ÑповіщеннÑ, глобальна Ñтрічка, будь-Ñкі ÑпиÑки та хештеґи. Потребує широкого екрана.' + animations_and_accessibility: ÐÐ½Ñ–Ð¼Ð°Ñ†Ñ–Ñ Ñ‚Ð° доÑтупніÑть + confirmation_dialogs: Діалоги Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ + discovery: ВиÑÐ²Ð»ÐµÐ½Ð½Ñ + sensitive_content: Дражливий вміÑÑ‚ application_mailer: notification_preferences: Змінити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ e-mail + salutation: "%{name}," settings: 'Змінити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ e-mail: %{link}' view: 'ПереглÑд:' view_profile: Показати профіль @@ -375,26 +582,46 @@ uk: warning: Будьте дуже обережні з цими даними. Ðіколи не ділітьÑÑ Ð½Ð¸Ð¼Ð¸ ні з ким! your_token: Ваш токен доÑтупу auth: + apply_for_account: Запитати Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ change_password: Пароль - confirm_email: Підтвердьте e-mail адреÑу - delete_account: Видалити аккаунт - delete_account_html: Якщо ви хочете видалити аккаунт, ви можете <a href="%{path}">перейти Ñюди</a>. Ð’Ð°Ñ Ð¿Ð¾Ð¿Ñ€Ð¾ÑÑть підтвердити дію. + checkbox_agreement_html: Я погоджуюÑÑŒ з <a href="%{rules_path}" target="_blank">правилами Ñервера</a> та <a href="%{terms_path}" target="_blank">умовами викориÑтаннÑ</a> + checkbox_agreement_without_rules_html: Я погоджуюÑÑ Ð· <a href="%{terms_path}" target="_blank">умовами викориÑтаннÑ</a> + delete_account: Видалити обліковий Ð·Ð°Ð¿Ð¸Ñ + delete_account_html: Якщо ви хочете видалити Ñвій обліковий запиÑ, ви можете <a href="%{path}">перейти Ñюди</a>. Ð’Ð°Ñ Ð¿Ð¾Ð¿Ñ€Ð¾ÑÑть підтвердити дію. + description: + prefix_invited_by_user: "@%{name} запрошує Ð²Ð°Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ñ‚Ð¸ÑÑ Ð´Ð¾ цього Ñервера Mastodon!" + prefix_sign_up: ЗареєÑтруйтеÑÑ Ð½Ð° Mastodon Ñьогодні! + suffix: Маючи обліковий запиÑ, ви зможете підпиÑуватиÑÑ Ð½Ð° людей, публікувати поÑти та лиÑтуватиÑÑ Ð· кориÑтувачами будь-Ñкого Ñервера Mastodon! didnt_get_confirmation: Ви не отримали інÑтрукції з підтвердженнÑ? - forgot_password: Забули Ñвій пароль? + forgot_password: Забули пароль? invalid_reset_password_token: Токен ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ неправильний або проÑрочений. Спробуйте попроÑити новий. login: Увійти logout: Вийти - migrate_account: Переїхати до іншого аккаунту - migrate_account_html: Якщо ви бажаєте, щоб відвідувачі цього акканту були перенаправлені до іншого, ви можете <a href="%{path}">налаштувати це тут</a>. + migrate_account: Переїхати на інший обліковий Ð·Ð°Ð¿Ð¸Ñ + migrate_account_html: Якщо ви бажаєте перенаправити цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° інший, ви можете <a href="%{path}">налаштувати це тут</a>. or_log_in_with: Ðбо увійдіть з + providers: + cas: CAS + saml: SAML register: ЗареєÑтруватиÑÑ + registration_closed: "%{instance} не приймає нових членів" resend_confirmation: Повторно відправити інÑтрукції з Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ reset_password: Скинути пароль security: Зміна паролю set_new_password: Ð’Ñтановити новий пароль + setup: + email_below_hint_html: Якщо Ñ†Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° адреÑа не Ñ” вірною, ви можете змінити Ñ—Ñ— тут та отримати новий лиÑÑ‚ Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ. + email_settings_hint_html: Електронний лиÑÑ‚-Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ виÑлано до %{email}. Якщо Ñ†Ñ Ð°Ð´Ñ€ÐµÑа електронної пошти не Ñ” вірною, ви можете змінити Ñ—Ñ— в налаштуваннÑÑ… облікового запиÑу. + title: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ + status: + account_status: Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу + confirming: Очікуємо на Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð·Ð° допомогою електронної пошти. + functional: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð½Ñ–Ñтю робочій. + pending: Ваша заÑва очікує на розглÑд нашим перÑоналом. Це може зайнÑти деÑкий чаÑ. Ви отримаєте електронний лиÑÑ‚, Ñкщо ваша заÑва буде Ñхвалена. + trouble_logging_in: Проблема під Ñ‡Ð°Ñ Ð²Ñ…Ð¾Ð´Ñƒ? authorize_follow: - already_following: Ви вже підпиÑані на цей аккаунт - error: Ðа жаль, при пошуку віддаленого аккаунту виникла помилка + already_following: Ви вже Ñлідкуєте за цим обліковим запиÑом + error: Ðа жаль, під Ñ‡Ð°Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ віддаленого облікового запиÑу ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° follow: ПідпиÑатиÑÑ follow_request: 'Вам надіÑлали запит на підпиÑку:' following: 'Ура! Ви тепер підпиÑані на:' @@ -418,17 +645,32 @@ uk: x_months: "%{count}міÑ" x_seconds: "%{count}Ñек" deletes: - bad_password_msg: Гарна Ñпроба, гакери! Ðеправильний пароль - confirm_password: Введіть актуальний пароль щоб перевірити що ви це ви - description_html: Це <strong>безвідворотно Ñ– назавжди</strong> видалить контент з вашого аккаунту та деактивує його. Ваше ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача буде залишатиÑÑ Ð·Ð°Ñ€ÐµÐ·ÐµÑ€Ð²Ð¾Ð²Ð°Ð½Ð¸Ð¼ Ð´Ð»Ñ ÑƒÐ½Ð¸ÐºÐ½ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— деперÑоналізації. - proceed: Видалити аккаунт - success_msg: Ваш аккаунт було уÑпішно видалено - warning_html: Ми можемо гарантувати Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ½Ñ‚Ð¸ <b>лише з цього Ñайту</b>. Контент, що був поширений залишає Ñліди. Сервери, що Ñ” офлайн та ті, що відпиÑалиÑÑ Ð²Ñ–Ð´ наших оновлень не запишуть змін до Ñвоїх баз даних. - warning_title: Про доÑтупніÑть поширеного контенту + confirm_password: Введіть актуальний пароль, щоб перевірити що ви це ви + proceed: Видалити обліковий Ð·Ð°Ð¿Ð¸Ñ + success_msg: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ уÑпішно видалено + warning: + before: 'До того Ñк продовжити, будь лаÑка уважно прочитайте це:' + caches: ІнформаціÑ, кешована іншими Ñерверами, може залишитиÑÑ + data_removal: Ваші поÑти та інші дані будуть видалені назавжди + email_change_html: Ви можете <a href="%{path}">змінити вашу електронну адреÑу</a>, не видалÑючи ваш обліковий Ð·Ð°Ð¿Ð¸Ñ + email_contact_html: Якщо його вÑе ще немає, ви можете напиÑали до <a href="mailto:%{email}">%{email}</a> Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¸ + email_reconfirmation_html: Якщо ви не отримали електронного лиÑта з підтвердженнÑм, ви можете <a href="%{path}">запроÑити його знову</a> + irreversible: Буде неможливо відновити ваш обліковий Ð·Ð°Ð¿Ð¸Ñ + more_details_html: Подробиці за поÑиланнÑм <a href="%{terms_path}">політика конфіденційноÑті</a>. + username_available: Ваше ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ñтане доÑтупним Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ + username_unavailable: Ваше ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача залишитьÑÑ Ð½ÐµÐ´Ð¾Ñтупним Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ + directories: + directory: Каталог профілів + explanation: Шукайте кориÑтувачів за Ñ—Ñ… інтереÑами + explore_mastodon: ДоÑліджуйте %{title} + domain_validator: + invalid_domain: не Ñ” допуÑтимим ім'Ñм домену errors: + '400': Ваш запит був недійÑним або неправильним. '403': У Ð’Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до переглÑду даної Ñторінки. - '404': Сторінка, Ñку Ви шукали, не Ñ–Ñнує. - '410': Сторінка, Ñку Ви шукали, більше не Ñ–Ñнує. + '404': Сторінки, Ñку Ви шукали, не Ñ–Ñнує. + '406': Ð¦Ñ Ñторінка недоÑтупна у запрошеному форматі. + '410': Сторінки, Ñку Ви шукали, більше не Ñ–Ñнує. '422': content: Перевірка безпеки не вдалаÑÑ. Можливо, Ви блокуєте cookies? title: Перевірка безпеки не вдалаÑÑ @@ -436,7 +678,11 @@ uk: '500': content: Пробачте, та щоÑÑŒ пішло не так з нашого боку. title: Ð¦Ñ Ñторінка неправильна + '503': Ð¦Ñ Ñторінка не може бути оброблена через тимчаÑову відмову Ñервера. noscript_html: Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð²ÐµÐ±-заÑтоÑунку Mastodon, будь-лаÑка увімкніть JavaScript. Якщо у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” такої можливоÑті, ÑкориÑтайтеÑÑŒ одним із <a href="%{apps_path}">нативних заÑтоÑунків</a> Ð´Ð»Ñ Mastodon Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— платформи. + existing_username_validator: + not_found: не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ локального кориÑтувача з таким ім'Ñм + not_found_multiple: не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ %{usernames} exports: archive_takeout: date: Дата @@ -446,14 +692,22 @@ uk: request: Зробити запит на архів size: Розмір blocks: СпиÑок блокувань + csv: CSV + domain_blocks: Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñ–Ð² follows: ПідпиÑки + lists: СпиÑки mutes: СпиÑок Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ storage: Ваш медіаконтент + featured_tags: + add_new: Додати новий + errors: + limit: Ви доÑÑгли макÑимальної кількоÑті хештеґів + hint_html: "<strong>Що таке виділені хештеґи?</strong> Це ті, що відображаютьÑÑ Ð½Ð¸ видному міÑці у вашому публічному профілі. Вони дають змогу людÑм фільтрувати ваші публічні поÑти за цими хештеґами. Це дуже кориÑно Ð´Ð»Ñ Ð²Ñ–Ð´ÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¼Ð¸Ñтецьких творів та довготривалих проектів." filters: contexts: - home: Ваш фід + home: Ваша Ñтрічка notifications: Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ - public: Публічний фід + public: Глобальні Ñтрічки thread: ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ edit: title: Редагувати фільтр @@ -465,14 +719,48 @@ uk: title: Фільтри new: title: Додати фільтр + footer: + developers: Розробникам + more: Більше… + resources: РеÑурÑи + trending_now: Ðктуальні generic: + all: УÑÑ– changes_saved_msg: Зміни уÑпішно збережені! + copy: Копіювати + order_by: Сортувати за save_changes: Зберегти зміни + validation_errors: + few: ЩоÑÑŒ доÑÑ– не гаразд! ПереглÑньте %{count} повідомлень про помилки + many: ЩоÑÑŒ доÑÑ– не гаразд! ПереглÑньте %{count} повідомлень про помилки + one: ЩоÑÑŒ доÑÑ– не гаразд! ПереглÑньте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку + other: ЩоÑÑŒ доÑÑ– не гаразд! ПереглÑньте %{count} повідомлень про помилки + html_validator: + invalid_markup: 'міÑтить неприпуÑтиму HTML розмітку: %{error}' + identity_proofs: + active: Ðктивне + authorize: Так, авторизувати + authorize_connection_prompt: Ðвторизувати це зашифроване з'єднаннÑ? + errors: + failed: Ðе вдалоÑÑ Ð²Ñтановити це зашифроване з'єднаннÑ. Спробуйте ще раз за допомогою %{provider}. + i_am_html: Я %{username} з %{service}. + identity: ІдентичніÑть + inactive: Ðеактивний + publicize_checkbox: 'Та дмухнути це:' + publicize_toot: 'Це доведено! Я таки %{username} з %{service}: %{url}' + status: Стан перевірки + view_proof: ПереглÑнути доказ imports: + modes: + merge: Ð—Ð»Ð¸Ñ‚Ñ‚Ñ + merge_long: Зберегти Ñ–Ñнуючі запиÑи та додати нові + overwrite: ПерезапиÑувати + overwrite_long: Замінити поточні запиÑи новими preface: Ð’Ñ‹ можете завантажити деÑкі дані, наприклад, ÑпиÑки людей, на Ñких Ви підпиÑані чи Ñких блокуєте, в Ваш акаунт на цій інÑтанції з файлів, екÑпортованих з іншої інÑтанції. success: Ваші дані були уÑпішно загружені та будуть оброблені в найближчий момент types: blocking: СпиÑок блокувань + domain_blocking: СпиÑок заблокованих Ñайтів following: ПідпиÑки muting: СпиÑок Ð³Ð»ÑƒÑˆÐµÐ½Ð½Ñ upload: Завантажити @@ -490,6 +778,11 @@ uk: expires_in_prompt: Ðіколи generate: Згенерувати invited_by: 'Ð’Ð°Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñив(-ла):' + max_uses: + few: "%{count} викориÑтаннÑ" + many: "%{count} викориÑтань" + one: 1 викориÑÑ‚Ð°Ð½Ð½Ñ + other: "%{count} викориÑтань" max_uses_prompt: Без Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ prompt: Генеруйте та ділітьÑÑ Ð¿Ð¾ÑиланнÑм з іншими Ð´Ð»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу до Ñайту table: @@ -498,16 +791,14 @@ uk: title: ЗапроÑити людей lists: errors: - limit: Ви доÑÑгнули макÑимальної кількоÑті ÑпиÑків + limit: Ви доÑÑгли макÑимальної кількоÑті ÑпиÑків media_attachments: validations: images_and_video: Ðе можна додати відео до ÑтатуÑу з зображеннÑми too_many: Ðе можна додати більше 4 файлів migrations: - acct: username@domain нового аккаунту - currently_redirecting: 'Ваш профіль налаштований перенаправлÑти на:' - proceed: Зберегти - updated_msg: Переїзд вашого аккаунту уÑпішно оновлений! + acct: username@domain нового облікового запиÑу + cancel: СкаÑувати Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ moderation: title: ÐœÐ¾Ð´ÐµÑ€Ð°Ñ†Ñ–Ñ notification_mailer: @@ -531,12 +822,12 @@ uk: subject: КориÑтувачу %{name} ÑподобавÑÑ Ð²Ð°Ñˆ ÑÑ‚Ð°Ñ‚ÑƒÑ title: Ðове Ð²Ð¿Ð¾Ð´Ð¾Ð±Ð°Ð½Ð½Ñ follow: - body: "%{name} тепер підпиÑаний на ваÑ!" + body: "%{name} тепер підпиÑаний(-а) на ваÑ!" subject: "%{name} тепер підпиÑаний(-а) на ваÑ" - title: Ðовий підпиÑник + title: Ðовий підпиÑник(-цÑ) follow_request: action: Керувати запитами на підпиÑку - body: "%{name} запитав Ð’Ð°Ñ Ð¿Ñ€Ð¾ підпиÑку" + body: "%{name} запитав(-ла) Ð’Ð°Ñ Ð¿Ñ€Ð¾ підпиÑку" subject: "%{name} хоче підпиÑатиÑÑ Ð½Ð° ВаÑ" title: Ðовий запит на підпиÑку mention: @@ -546,11 +837,12 @@ uk: title: Ðова згадка reblog: body: 'Ваш ÑÑ‚Ð°Ñ‚ÑƒÑ Ð±ÑƒÐ»Ð¾ передмухнуто %{name}:' - subject: "%{name} передмухнув ваш ÑтатуÑ" + subject: "%{name} передмухнув(-ла) ваш ÑтатуÑ" title: Ðове Ð¿ÐµÑ€ÐµÐ´Ð¼ÑƒÑ…ÑƒÐ²Ð°Ð½Ð½Ñ number: human: decimal_units: + format: "%n%u" units: billion: млрд million: млн @@ -560,49 +852,138 @@ uk: pagination: newer: Ðовіше next: Далі + older: Старіші prev: Ðазад + truncate: "…" + polls: + errors: + already_voted: Ви вже голоÑували в цьому опитуванні + duplicate_options: міÑтить повторювані варіанти + duration_too_long: надто далеко у майбутньому + duration_too_short: надто мала триваліÑть + expired: Це Ð¾Ð¿Ð¸Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¶Ðµ завершено + over_character_limit: не може бути довше ніж %{max} Ñимволів кожен + too_few_options: має міÑтити більше ніж один варіант + too_many_options: не може мати більше ніж %{max} варіантів preferences: other: Інше + posting_defaults: ÐŸÑ€Ð¾Ð¼Ð¾Ð²Ñ‡Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ð¾Ñтів + public_timelines: Глобальні Ñтрічки + relationships: + activity: ДіÑльніÑть облікового запиÑу + dormant: Ðеактивні + last_active: ÐšÑ€Ð°Ð¹Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть + most_recent: За чаÑом ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ + moved: Переміщено + mutual: Взаємні ÑƒÐ¿Ð¾Ð´Ð¾Ð±Ð°Ð½Ð½Ñ + primary: Первинні + relationship: Зв’Ñзок + remove_selected_domains: Видалити уÑÑ–Ñ… підпиÑників з обраних доменів + remove_selected_followers: Видалити обраних підпиÑників + remove_selected_follows: Ðе Ñтежити за обраними кориÑтувачами + status: Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу remote_follow: acct: Введіть username@domain, Ñким ви хочете підпиÑатиÑÑ - missing_resource: Пошук потрібного Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ URL Ð´Ð»Ñ Ð’Ð°ÑˆÐ¾Ð³Ð¾ аккаунта закінчивÑÑ Ð½ÐµÐ²Ð´Ð°Ñ‡ÐµÑŽ - no_account_html: Ðе маєте аккаунту? Ðе біда, ви можете <a href='%{sign_up_path}' target='_blank'>зареєÑтруватиÑÑ</a> + missing_resource: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ необхідний URL переадреÑації Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу + no_account_html: Ðе маєте облікового запиÑу? Ви можете <a href='%{sign_up_path}' target='_blank'>зареєÑтруватиÑÑ Ñ‚ÑƒÑ‚</a> proceed: Перейти до підпиÑки prompt: 'Ви хочете підпиÑатиÑÑ Ð½Ð°:' - remote_unfollow: - error: Помилка - title: Заголовок - unfollowed: ВідпиÑані + reason_html: "<strong>Чому це необхідно?</strong> <code>%{instance}</code> можливо, не Ñ” Ñервером, на Ñкому ви зареєÑтровані, тому ми маємо ÑпрÑмувати Ð²Ð°Ñ Ð´Ð¾ вашого домашнього Ñервера." + remote_interaction: + favourite: + proceed: Перейти до Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð² улюблені + prompt: 'Ви хочете зробити улюбленим цей дмух:' + reblog: + proceed: Перейти до Ð¿ÐµÑ€ÐµÐ´Ð¼ÑƒÑ…ÑƒÐ²Ð°Ð½Ð½Ñ + prompt: 'Ви хочете передмухнути цей дмух:' + reply: + proceed: Перейти до відповіді + prompt: 'Ви хочете відповіÑти на цей дмух:' + scheduled_statuses: + over_daily_limit: Ви перевищили ліміт в %{limit} запланованих дмухів на Ñьогодні + over_total_limit: Ви перевищили ліміт в %{limit} запланованих дмухів + too_soon: Запланована дата має бути в майбутньому sessions: activity: ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть browser: Браузер browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Хром + edge: Microsoft Edge + electron: Electron + firefox: Firefox generic: Ðевідомий браузер + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Опера + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Сафарі + uc_browser: UCBrowser + weibo: Weibo current_session: Ðктивна ÑеÑÑ–Ñ description: "%{browser} на %{platform}" - explanation: Це веб-браузери, нині авторизовані до вашого аккаунту Mastodon. + explanation: Це веб-браузери, авторизовані у вашому обліковому запиÑÑ– Mastodon. + ip: IP platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac other: невідома платформа + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone revoke: Закінчити revoke_success: СеÑÑ–Ñ ÑƒÑпішно закінчена title: СеÑÑ–Ñ— settings: + account: Обліковий Ð·Ð°Ð¿Ð¸Ñ + account_settings: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу + appearance: ВиглÑд authorized_apps: Ðвторизовані заÑтоÑунки - back: Ðазад у Mastodon - delete: Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ñƒ + back: Ðазад до Mastodon + delete: Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу development: Розробка edit_profile: Редагувати профіль export: ЕкÑпорт даних + featured_tags: Рекомендовані хештеґи + identity_proofs: Докази ідентичноÑті import: Імпорт - migrate: ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð°ÐºÐ°ÑƒÐ½Ñ‚Ñƒ + import_and_export: Імпорт та екÑпорт + migrate: ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу notifications: Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ preferences: ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ + profile: Профіль + relationships: ПідпиÑки та підпиÑники two_factor_authentication: Двофакторна Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ statuses: attached: description: 'Прикріплено: %{attached}' + image: + few: "%{count} зображень" + many: "%{count} зображень" + one: "%{count} зображеннÑ" + other: "%{count} зображеннÑ" + video: + few: "%{count} відео" + many: "%{count} відео" + one: "%{count} відео" + other: "%{count} відео" boosted_from_html: ПроÑунуто від %{acct_link} content_warning: 'ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ контент: %{warning}' + disallowed_hashtags: + few: 'заборонених хештеґа: %{tags}' + many: 'заборонених хештеґів: %{tags}' + one: 'заборонений хештеґ: %{tags}' + other: 'заборонених хештеґів: %{tags}' language_detection: Ðвтоматично визначати мову open_in_web: Відкрити у вебі over_character_limit: перевищено ліміт Ñимволів (%{max}) @@ -611,7 +992,16 @@ uk: ownership: Ðе можна закріпити чужий поÑÑ‚ private: Ðе можна закріпити непублічний поÑÑ‚ reblog: Ðе можна закріпити проÑунутий поÑÑ‚ + poll: + total_votes: + few: "%{count} голоÑа" + many: "%{count} голоÑів" + one: "%{count} голоÑ" + other: "%{count} голоÑи" + vote: ПроголоÑувати show_more: Детальніше + sign_in_to_participate: Увійдіть, щоб брати учаÑть у беÑіді + title: '%{name}: "%{quote}"' visibilities: private: Ð”Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників private_long: Показувати тільки підпиÑникам @@ -622,13 +1012,20 @@ uk: stream_entries: pinned: Закріплений поÑÑ‚ reblogged: передмухнув(-ла) - sensitive_content: ÐеÑприйнÑтливий контент + sensitive_content: Дражливий контент + tags: + does_not_match_previous_name: не збігаєтьÑÑ Ð· попереднім ім'Ñм terms: + body_html: "<h2>Політика конфіденційноÑті</h2>\n<h3 id=\"collect\">Яку інформацію ми збираємо?</h3>\n\n<ul>\n<li><em>ОÑновна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ обліковий запиÑ</em>: Якщо ви реєÑтруєтеÑÑŒ на цьому Ñервері, Ð²Ð°Ñ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ попроÑити ввеÑти Ñ–Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, електронну адреÑу та пароль. Ви також можете ввеÑти додаткову інформацію профілю, наприклад, ім'Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð° біографію, завантажити Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ та Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°. Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, відображуване ім’Ñ, біографіÑ, Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ та Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° завжди Ñ” загальнодоÑтупними.</li>\n<li><em>ПовідомленнÑ, підпиÑки та інша публічна інформаціÑ</em>: СпиÑок людей, на Ñких ви підпиÑані, Ñ” публічним, це ж ÑтоÑуєтьÑÑ Ñ– ÑпиÑка ваших підпиÑників. Коли ви надÑилаєте повідомленнÑ, дата та Ñ‡Ð°Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°ÑŽÑ‚ÑŒÑÑ, а також програма, за допомогою Ñкої ви надіÑлали повідомленнÑ. ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ міÑтити мультимедійні вкладеннÑ, такі Ñк Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð° відео. ЗагальнодоÑтупні публікації, навіть приховані зі Ñтрічок, доÑтупні Ð´Ð»Ñ Ð²ÑÑ–Ñ…. Коли ви розміщуєте публікацію у Ñвоєму профілі, це також загальнодоÑтупна інформаціÑ. Ваші публікації доÑтавлÑютьÑÑ Ð²Ð°ÑˆÐ¸Ð¼ підпиÑникам, у деÑких випадках це означає, що вони доÑтавлÑютьÑÑ Ð½Ð° інші Ñервери Ñ– копії зберігаютьÑÑ Ñ‚Ð°Ð¼. Коли ви видалÑєте публікації, Ñ†Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ñ‚Ð°ÐºÐ¾Ð¶ доÑтавлÑєтьÑÑ Ð²Ð°ÑˆÐ¸Ð¼ \nпідпиÑникам. ПерепоÑти та Ð²Ð¿Ð¾Ð´Ð¾Ð±Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð¶Ð´Ð¸ публічні.</li>\n<li><em>ПрÑмі публікації та поÑти лише Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників</em>: УÑÑ– Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°ÑŽÑ‚ÑŒÑÑ Ñ‚Ð° оброблÑютьÑÑ Ð½Ð° Ñервері. Публікації лише Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників доÑтавлÑютьÑÑ Ð²Ð°ÑˆÐ¸Ð¼ підпиÑникам та кориÑтувачам, Ñкі згадуютьÑÑ Ð² них, а прÑмі Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð´ÑилаютьÑÑ Ð»Ð¸ÑˆÐµ тим кориÑтувачам, Ñкі в них згадуютьÑÑ. У \nдеÑких випадках це означає, що вони доÑтавлÑютьÑÑ Ð½Ð° інші Ñервери Ñ– копії зберігаютьÑÑ Ñ‚Ð°Ð¼. Ми докладаємо Ñумлінних зуÑиль, щоб обмежити доÑтуп до цих поÑтів лише уповноваженим оÑобам, але інші Ñервери можуть цього не зробити. Тому важливо переглÑнути Ñервери, до Ñких належать ваші підпиÑники. Ви можете переключити параметр Ð´Ð»Ñ ÑÑ…Ð²Ð°Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° Ð²Ñ–Ð´Ñ…Ð¸Ð»ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… підпиÑників вручну в налаштуваннÑÑ…. <em>Будь лаÑка, майте на увазі, що оператори нашого Ñервера та будь-Ñкого приймаючого Ñервера, можуть переглÑдати такі повідомленнÑ</em>, Ñ– що одержувачі можуть робити Ñкріншот, копіювати або повторно ділитиÑÑ Ð½Ð¸Ð¼Ð¸. <em>Ðе ділітьÑÑ Ð±ÑƒÐ´ÑŒ-Ñкою небезпечною інформацією на Mastodon.</em></li>\n<li><em>IP-адреÑи та інші метадані</em>: Коли ви входите в ÑиÑтему, ми запиÑуємо IP-адреÑу, з Ñкої ви входите, а також назву веб-переглÑдача. УÑÑ– ÑеанÑи, Ñкими ви ввійшли в ÑиÑтему, доÑтупні вам Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду та ÑкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð² налаштуваннÑÑ…. ОÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñтана IP-адреÑа зберігаєтьÑÑ Ð´Ð¾ 12 міÑÑців. Ми також можемо зберігати журнали Ñерверів, Ñкі включають IP-адреÑу кожного запиту на наш Ñервер.</li>\n</ul>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"use\">Ð”Ð»Ñ Ñ‡Ð¾Ð³Ð¾ ми викориÑтовуємо вашу інформацію?</h3>\n\n<p>Будь-Ñка інформаціÑ, Ñку ми збираємо від ваÑ, може викориÑтовуватиÑÑ Ñ‚Ð°ÐºÐ¸Ð¼Ð¸ ÑпоÑобами:</p>\n\n<ul>\n<li>Ð”Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð¾Ñновної функціональноÑті Mastodon. Ви можете взаємодіÑти з вміÑтом інших людей та розміщувати влаÑний вміÑÑ‚ лише тоді, коли ви ввійшли в ÑиÑтему. Ðаприклад, ви можете підпиÑатиÑÑŒ на інших людей, щоб переглÑдати Ñ—Ñ… публікації об’єднаними на вашій влаÑній перÑоналізованій локальній Ñтрічці. </li>\n<li>Щоб ÑприÑти модерації Ñпільноти, наприклад, порівнюючи вашу IP-адреÑу з іншими відомими адреÑами Ð´Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÑƒÑ…Ð¸Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ бану чи інших порушень.</li>\n<li>Електронна адреÑа, Ñку ви вводите, може викориÑтовуватиÑÑ Ð´Ð»Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð²Ð°Ð¼ інформації, Ñповіщень про інших людей, Ñкі взаємодіють з вашим вміÑтом або надÑилають вам повідомленнÑ, а також Ð´Ð»Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ñ– на запити та/або інші запитаннÑ./li>\n</ul>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"protect\">Як ми захищаємо вашу інформацію?</h3>\n\n<p>Ми заÑтоÑовуємо різноманітні заходи безпеки Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÐºÐ¸ безпеки вашої оÑобиÑтої інформації під Ñ‡Ð°Ñ Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ, Ð¿Ð¾Ð´Ð°Ð½Ð½Ñ Ñ‡Ð¸ доÑтупу до вашої оÑобиÑтої інформації. Крім уÑього іншого, ÑÐµÐ°Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ веб-переглÑдача, а також трафік між вашими програмами та API захищені SSL, а ваш пароль хешируєтьÑÑ Ð·Ð° допомогою Ñильного одноÑтороннього алгоритму. Ви можете дозволити двофакторну автентифікацію Ð´Ð»Ñ Ð¿Ð¾Ð´Ð°Ð»ÑŒÑˆÐ¾Ð³Ð¾ захиÑту доÑтупу до Ñвого облікового запиÑу.</p>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"data-retention\">Яка наша політика Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…?</h3>\n\n<p>Ми докладемо зуÑиль Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб:</p>\n\n<ul>\n<li>Зберігати журнали Ñервера, що міÑÑ‚Ñть IP-адреÑу вÑÑ–Ñ… запитів на цьому Ñервері, але більше 90 днів.</li>\n<li>Зберігати IP-адреÑи, пов’Ñзані з зареєÑтрованими кориÑтувачами, не більше 12 міÑÑців.</li>\n</ul>\n\n<p>Ви можете запитати та завантажити архів Ñвого вміÑту, включаючи ваші публікації, медіа-додатки, Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ та Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°.</p>\n\n<p>Ви можете в будь-Ñкий Ñ‡Ð°Ñ Ð±ÐµÐ·Ð¿Ð¾Ð²Ð¾Ñ€Ð¾Ñ‚Ð½Ð¾ видалити Ñвій обліковий запиÑ.</p>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"cookies\">Чи викориÑтовуємо ми файли cookie?</h3>\n\n<p>Так. Файли cookie — це невеликі файли, Ñкі Ñайт або його поÑтачальник поÑлуг передає на жорÑткий диÑк вашого комп'ютера через веб-браузер (Ñкщо ви це дозволите). Ці файли cookie дозволÑють Ñайту розпізнавати ваш веб-переглÑдач Ñ–, Ñкщо у Ð²Ð°Ñ Ð·Ð°Ñ€ÐµÑ”Ñтрований обліковий запиÑ, пов’Ñзувати його зі Ñвоїм зареєÑтрованим обліковим запиÑом.</p>\n\n<p>Ми викориÑтовуємо файли cookie, щоб зрозуміти Ñ– зберегти ваші Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¼Ð°Ð¹Ð±ÑƒÑ‚Ð½Ñ–Ñ… відвідувань. </p>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"disclose\">Чи розкриваємо ми будь-Ñку інформацію іншим Ñторонам?</h3>\n\n<p>Ми не продаємо, не торгуємо та іншим чином не передаємо назовні вашої оÑобиÑтої інформації. Це не ÑтоÑуєтьÑÑ Ð´Ð¾Ð²Ñ–Ñ€ÐµÐ½Ð¸Ñ… третіх оÑіб, Ñкі допомагають нам керувати нашим Ñайтом, веÑти наш Ð±Ñ–Ð·Ð½ÐµÑ Ð°Ð±Ð¾ обÑлуговувати ваÑ, Ñкщо ці Ñторони погоджуютьÑÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ñ‚Ð¸ цю інформацію конфіденційною. Ми також можемо оприлюднити вашу інформацію, коли вважаємо, що випуÑк доцільний Ð´Ð»Ñ Ð´Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð·Ð°ÐºÐ¾Ð½Ð¾Ð´Ð°Ð²Ñтва, \nзаÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð»Ñ–Ñ‚Ð¸ÐºÐ¸ нашого веб-Ñайта чи захиÑту наших або інших прав, влаÑноÑті чи безпеки.</p>\n\n<p>Ваш загальнодоÑтупний вміÑÑ‚ може завантажуватиÑÑ Ñ–Ð½ÑˆÐ¸Ð¼Ð¸ Ñерверами в мережі. Ваші загальнодоÑтупні публікації та публікації лише Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñників, доÑтавлÑютьÑÑ Ð½Ð° Ñервери, де \"проживають\" ваші підпиÑники, а прÑмі Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð´Ñ…Ð¾Ð´Ñть на Ñервери одержувачів, Ñкщо ці підпиÑники або одержувачі проживають на іншому Ñервері, ніж цей./p>\n\n<p>Коли ви дозволÑєте додатку викориÑтовувати ваш обліковий запиÑ, залежно від обÑÑгу дозволів, Ñкі ви затверджуєте, він може отримати доÑтуп до вашої інформації про загальнодоÑтупний профіль, ÑпиÑок ваших підпиÑок, ваші підпиÑники, ваші ÑпиÑки, вÑÑ– ваші публікації та вибране. Програми ніколи не можуть отримати доÑтуп до вашої електронної адреÑи чи паролÑ.</p>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"children\">ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñайту дітьми</h3>\n\n<p>Якщо цей Ñервер знаходитьÑÑ Ð² ЄС або ЄЕП: наш Ñайт, продукти та поÑлуги ÑпрÑмовані на людей, Ñким не менше 16 років. Якщо вам не виповнилоÑÑ 16 років, відповідно до вимог GDPR (<a href=\"https://en.wikipedia.org/wiki/General_Data_Protection_Regulation\">Загальне Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ захиÑÑ‚ даних</a>) не викориÑтовуйте цей веб-Ñайт.</p>\n\n<p>Якщо цей Ñервер знаходитьÑÑ Ð² СШÐ: наш Ñайт, продукти та поÑлуги ÑпрÑмовані на людей, Ñким не менше 13 років. Якщо вам не виповнилоÑÑ 13 років, відповідно до вимог COPPA (<a href=\"https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act\">Закон про захиÑÑ‚ конфіденційноÑті дітей в Інтернеті</a>) не викориÑтовуйте цей Ñайт.</p>\n\n<p>Законодавчі вимоги можуть бути різними, Ñкщо цей Ñервер знаходитьÑÑ Ð² іншій юриÑдикції.</p>\n\n<hr class=\"spacer\"/>\n\n<h3 id=\"changes\">Зміни в нашій Політиці конфіденційноÑті</h3>\n\n<p>Якщо ми вирішимо змінити нашу політику конфіденційноÑті, ми опублікуємо ці зміни на цій Ñторінці. </p>\n\n<p>Цей документ Ñ” CC-BY-SA. ВоÑтаннє оновлено 7 Ð±ÐµÑ€ÐµÐ·Ð½Ñ 2018 року.</p>\n\n<p>ПервіÑно адаптовано з <a href=\"https://github.com/discourse/discourse\">політики конфіденційноÑті диÑкурÑу</a>.</p>\n" title: Умови викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° Політика приватноÑті %{instance} themes: contrast: ВиÑока контраÑніÑть default: Mastodon mastodon-light: Mastodon (Ñвітла) + time: + formats: + default: "%b %d, %Y, %H:%M" + month: "%b %Y" two_factor_authentication: code_hint: Ð”Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð½Ñ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ код, згенерований заÑтоÑунком аутентифікатора description_html: При увімкненні <strong>двофакторної аутентифікації</strong>, вхід буде вимагати від Ð’Ð°Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð’Ð°ÑˆÐ¾Ð³Ð¾ телефона, Ñкий згенерує вхідний код. @@ -638,36 +1035,58 @@ uk: enabled_success: Двофакторна Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÑƒÑпішно увімкнена generate_recovery_codes: Згенерувати коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ instructions_html: "<strong>ВідÑкануйте цей QR-код за допомогою Google Authenticator чи іншого TOTP-заÑтоÑунку на Вашому телефоні</strong>. З цього моменту він буде генерувати коди, Ñкі буде необхідно ввеÑти Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ." - lost_recovery_codes: Коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»Ñють повернути доÑтуп до акаунту у випадку втрати телефону. Якщо Ви втратили Ваші коди відновленнÑ, Ви можете знову згенерувати Ñ—Ñ… тут. Тоді ваші Ñтарі коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ´ÑƒÑ‚ÑŒ анульовані. + lost_recovery_codes: Коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»Ñють повернути доÑтуп до вашого облікового запиÑу у випадку втрати телефону. Якщо ви втратили ваші коди відновленнÑ, ви можете знову згенерувати Ñ—Ñ… тут. Ваші Ñтарі коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ´ÑƒÑ‚ÑŒ анульовані. manual_instructions: 'Якщо Ви не можете відÑканувати QR-код та хочете ввеÑти його вручну, Ñекрет предÑтавлений тут відкритим текÑтом:' recovery_codes: ЗапаÑні коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ recovery_codes_regenerated: Коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑƒÑпішно згенеровані - recovery_instructions_html: У випадку втрати доÑтупу до вашого телефону ви можете викориÑтати один з кодів відновленнÑ, вказаних нижче, щоб повернути доÑтуп до акаунту. Тримайте коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñƒ безпеці, наприклад, роздруйте Ñ—Ñ… та зберігайте Ñ—Ñ… з іншими важливими документами. + recovery_instructions_html: У випадку втрати доÑтупу до вашого телефону ви можете викориÑтати один з нижчевказаних кодів відновленнÑ, щоб повернути доÑтуп до вашого облікового запиÑу. <strong>Тримайте коди Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñƒ безпеці</strong>, наприклад, роздрукуйте Ñ—Ñ… та зберігайте разом з іншими важливими документами. setup: Ðалаштувати wrong_code: Введений код неправильний! Чи правильно вÑтановлений Ñ‡Ð°Ñ Ð½Ð° Ñервері та приÑтрої? user_mailer: backup_ready: - explanation: Ви зробили запит на повний архів вашого аккаунту Mastodon. Він вже готовий Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ! + explanation: Ви робили запит повної резервної копії вашого облікового запиÑу Mastodon. Вона вже готова Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ! subject: Ваш архів готовий до Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ title: ВинеÑÐµÐ½Ð½Ñ Ð°Ñ€Ñ…Ñ–Ð²Ñƒ + warning: + explanation: + disable: Поки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¼Ð¾Ñ€Ð¾Ð¶ÐµÐ½Ð¸Ð¹, його дані залишаютьÑÑ Ð½ÐµÐ·Ð¼Ñ–Ð½Ð½Ð¸Ð¼Ð¸. Проте ви не зможете виконувати будь-Ñкі дії над обліковим запиÑом, доки його не буде розблоковано. + silence: Поки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾, ваші дмухи на цьому Ñервері бачитимуть лише ті люди, Ñкі вже Ñлідкують за вами, а Ð²Ð°Ñ Ð¼Ð¾Ð¶Ðµ бути виключено з різних публічних ÑпиÑків. Тим не менш, інші можуть Ñлідкувати за вами вручну. + suspend: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ призупинено, а вÑÑ– ваші дмухи Ñ– вивантажені медіафайли - безповоротно видалено з цього Ñервера та Ñерверів, де ви мали поÑлідовників. + get_in_touch: Ви можете відповіÑти на цей електронний лиÑÑ‚, щоб зконтактувати з працівниками %{instance}. + review_server_policies: ПереглÑнути політики Ñервера + subject: + disable: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ %{acct} було заморожено + none: ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ %{acct} + silence: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ %{acct} було обмежено + suspend: Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ %{acct} було призупинено + title: + disable: Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¼Ð¾Ñ€Ð¾Ð¶ÐµÐ½Ð¾ + none: ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ + silence: Ообліковий Ð·Ð°Ð¿Ð¸Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾ + suspend: Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¸Ð·ÑƒÐ¿Ð¸Ð½ÐµÐ½Ð¾ welcome: edit_profile_action: Ðалаштувати профіль - edit_profile_step: Ви можете налаштувати профіль під Ñебе завантаживши аватар, шпалери, змінивши відображуване ім'Ñ Ñ‚Ð¾Ñ‰Ð¾. Якщо ви захочете переглÑдати нових підпиÑників до того, Ñк вони зможуть підпиÑатиÑÑ Ð½Ð° ваÑ, ви можете заблокувати Ñвій аккаунт. + edit_profile_step: Ви можете налаштувати ваш профіль, завантаживши аватар, шпалери, змінивши відображуване ім'Ñ Ñ‚Ð¾Ñ‰Ð¾. Якщо ви захочете переглÑдати нових підпиÑників до того, Ñк вони зможуть підпиÑатиÑÑ Ð½Ð° ваÑ, ви можете заблокувати Ñвій обліковий запиÑ. explanation: ОÑÑŒ декілька порад Ð´Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ final_action: Почати поÑтити - final_step: 'ПочніÑть поÑтити! Ðавіть не підпиÑавшиÑÑŒ на ваÑ, інші зможуть побачити ваші поÑти, наприкоал, у локальному фіді та у хештеґах. Якщо ви хочете предÑтавитиÑÑ, можете ÑкориÑтатиÑÑ Ñ…ÐµÑˆÑ‚ÐµÒ‘Ð¾Ð¼ #introductions.' + final_step: 'ПочніÑть поÑтити! Ðавіть не підпиÑавшиÑÑŒ на ваÑ, інші зможуть побачити ваші поÑти, наприклад, у локальній Ñтрічці та у хештеґах. Якщо ви хочете предÑтавитиÑÑ, можете ÑкориÑтатиÑÑ Ñ…ÐµÑˆÑ‚ÐµÒ‘Ð¾Ð¼ #introductions.' full_handle: Ваше Ð·Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ full_handle_hint: Те, що ви хочете Ñказати друзÑм, щоб вони могли напиÑати вам або підпиÑатиÑÑ Ð· інших Ñайтів. review_preferences_action: Змінити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ review_preferences_step: ПереконайтеÑÑ Ñƒ тому, що ви налаштували вÑе необхідне, Ñк от Ñкі e-mail Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð²Ð¸ хочете отримувати, або Ñкий рівень приватноÑті ви хочете вÑтановити вашим поÑтам за замовчуваннÑм. Якщо хочете, ви можете увімкнути автоматичне Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð²Ð°Ð½Ð½Ñ GIF анімацій. subject: ЛаÑкаво проÑимо до Mastodon tip_federated_timeline: Федерований фід Ñ” широким поглÑдом на мережу Mastodon. Ðле він включає лише людей, на Ñких підпиÑані ваші ÑуÑіди по Ñайту, тому він не Ñ” повним. - tip_following: Ви автоматично підпиÑані на адмініÑтратора(-ів) Ñервера. Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб знайти ще цікавих людей, доÑлідіть локальний та федерований фіди. + tip_following: Ви автоматично підпиÑані на адмініÑтратора(-ів) Ñервера. Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб знайти ще цікавих людей, доÑлідіть локальну та глобальну Ñтрічки. tip_local_timeline: Локальний фід - це поглÑд згори на людей на %{instance}. Це ваші прÑмі ÑуÑіди! tip_mobile_webapp: Якщо ваш мобільний браузер пропонує вам додати Mastodon на робочий Ñтіл, ви можете отримувати push-ÑповіщеннÑ. Ð’Ñе може виглÑдати Ñк нативний заÑтоÑунок у багатьох речах. tips: Поради title: ЛаÑкаво проÑимо, %{name}! users: + follow_limit_reached: Ðе можна Ñлідкувати більш ніж за %{limit} людей invalid_email: Введена адреÑа e-mail неправильна invalid_otp_token: Введено неправильний код otp_lost_help_html: Якщо ви втратили доÑтуп до обох, ви можете отримати доÑтуп з %{email} + seamless_external_login: Ви увійшли за допомогою зовнішнього ÑервіÑу, тому Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ та електронної пошти недоÑтупні. + signed_in_as: 'Ви увійшли Ñк:' + verification: + verification: ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 0c9b291ad3d301fd27b8b41b07bff9ab9b288af3..55685c82e90d008a9173db9a3dc515c95d977f27 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -10,18 +10,14 @@ zh-CN: api: API apps: 移动应用 apps_platforms: 在 iOSã€Android 和其他平å°ä¸Šä½¿ç”¨ Mastodon - browse_directory: æµè§ˆç”¨æˆ·èµ„料目录并按兴趣ç›é€‰ + browse_directory: æµè§ˆç”¨æˆ·ç›®å½•并按兴趣ç›é€‰ browse_public_posts: æµè§ˆ Mastodon ä¸Šå…¬å…±å˜Ÿæ–‡çš„å®žæ—¶ä¿¡æ¯æµ contact: è”ç³»æ–¹å¼ contact_missing: 未设定 contact_unavailable: 未公开 discover_users: å‘现用户 documentation: 文档 - extended_description_html: | - <h3>这里å¯ä»¥å†™ä¸€äº›è§„定</h3> - <p>本站尚未设置详细介ç»ã€‚</p> federation_hint_html: 在%{instance} 上拥有账户åŽï¼Œä½ å¯ä»¥å…³æ³¨ä»»ä½• Mastodon æœåŠ¡å™¨æˆ–å…¶ä»–æœåŠ¡å™¨ä¸Šçš„äººã€‚ - generic_description: "%{domain} 是这个庞大网络ä¸çš„䏀尿œåС噍" get_apps: å°è¯•移动应用 hosted_on: 一个在 %{domain} 上è¿è¡Œçš„ Mastodon 实例 learn_more: 了解详情 @@ -32,7 +28,7 @@ zh-CN: status_count_after: other: æ¡å˜Ÿæ–‡ status_count_before: 他们共嘟出了 - tagline: 关注朋å‹å¹¶å‘çŽ°æ–°æœ‹å‹ + tagline: 关注并å‘çŽ°æ–°æœ‹å‹ terms: ä½¿ç”¨æ¡æ¬¾ user_count_after: other: ä½ç”¨æˆ· @@ -90,7 +86,7 @@ zh-CN: title: 为 %{username} 更改电åé‚®ä»¶åœ°å€ confirm: 确认 confirmed: 已确认 - confirming: 确认 + confirming: ç¡®è®¤ä¸ deleted: å·²åˆ é™¤ demote: é™ä»» disable: åœç”¨ @@ -179,6 +175,7 @@ zh-CN: username: 用户å warn: è¦å‘Š web: ç«™å†…é¡µé¢ + whitelisted: å·²åŠ å…¥ç™½åå• action_logs: actions: assigned_to_self_report: "%{name} 接管了举报 %{target}" @@ -243,13 +240,16 @@ zh-CN: config: æœåС噍é…ç½® feature_deletions: 叿ˆ·åˆ 除 feature_invites: 邀请链接 - feature_profile_directory: 用户资料目录 + feature_profile_directory: 用户目录 feature_registrations: 公开注册 - feature_relay: åŒæ¥ä¸ç»§ + feature_relay: ä¸ç»§æœåС噍 + feature_spam_check: å垃圾 feature_timeline_preview: 时间轴预览 features: 功能 hidden_service: åŒ¿åæœåŠ¡è¿žé€šæ€§ open_reports: 待处ç†ä¸¾æŠ¥æ•° + pending_tags: ç‰å¾…å®¡æ ¸çš„æ ‡ç¾ + pending_users: ç‰å¾…å®¡æ ¸çš„ç”¨æˆ· recent_users: 新用户 search: 全文æœç´¢ single_user_mode: å•ç”¨æˆ·æ¨¡å¼ @@ -261,11 +261,18 @@ zh-CN: week_interactions: 本周互动数 week_users_active: 本周活跃用户数 week_users_new: 本周新用户数 + whitelist_mode: 白å啿¨¡å¼ + domain_allows: + add_new: 白åå•域å + created_msg: 域å已被列入白åå• + destroyed_msg: 域å已从白åå•ä¸ç§»é™¤ + undo: 从白åå•ä¸ç§»é™¤ domain_blocks: add_new: æ·»åŠ æ–°å±è”½åŸŸå created_msg: æ£åœ¨è¿›è¡ŒåŸŸåå±è”½ destroyed_msg: 域åå±è”½å·²æ’¤é”€ domain: 域å + edit: 编辑域åå±è”½ existing_domain_block_html: 您已ç»å¯¹ %{name} æ–½åŠ äº†æ›´ä¸¥æ ¼çš„é™åˆ¶ï¼Œæ‚¨éœ€è¦å…ˆ <a href="%{unblock_url}">è§£å°</a>。 new: create: æ·»åŠ å±è”½ @@ -276,6 +283,8 @@ zh-CN: silence: 自动éšè— suspend: 自动å°ç¦ title: æ·»åŠ åŸŸåå±è”½ + private_comment: ç§å¯†è¯„论 + public_comment: 公开评论 reject_media: æ‹’ç»æŽ¥æ”¶åª’ä½“æ–‡ä»¶ reject_media_hint: åˆ é™¤æœ¬åœ°å·²ç¼“å˜çš„媒体文件,并且ä¸å†æŽ¥æ”¶æ¥è‡ªè¯¥åŸŸå的任何媒体文件。æ¤é€‰é¡¹ä¸å½±å“å°ç¦ reject_reports: æ‹’ç»æŽ¥æ”¶ä¸¾æŠ¥ @@ -294,6 +303,7 @@ zh-CN: title: 撤销对 %{domain} 的域åå±è”½ undo: 撤销 undo: 撤销å±è”½åŸŸå + view: 查看域åå±è”½ email_domain_blocks: add_new: æ·»åŠ æ–°æ¡ç›® created_msg: 电å邮件域åå±è”½æ·»åŠ æˆåŠŸ @@ -316,6 +326,8 @@ zh-CN: all: 全部 limited: å—é™çš„ title: è¿è¥ + private_comment: ç§å¯†è¯„论 + public_comment: 公开评论 title: 已知实例 total_blocked_by_us: 被我方å±è”½çš„ total_followed_by_them: 被对方关注的 @@ -333,9 +345,9 @@ zh-CN: pending_accounts: title: 待处ç†çš„叿ˆ· (%{count}) relays: - add_new: æ·»åŠ æ–°çš„ä¸ç»§ + add_new: 订阅新的ä¸ç»§ delete: åˆ é™¤ - description_html: "<strong>åŒæ¥ä¸ç»§</strong>是一ç§ä¸é—´æœåŠ¡å™¨ï¼Œå„实例å¯ä»¥é€šè¿‡è®¢é˜…ä¸ç»§å’Œå‘ä¸ç»§æŽ¨é€ä¿¡æ¯çš„æ–¹å¼æ¥å¤§é‡äº¤æ¢å…¬å¼€å˜Ÿæ–‡ã€‚<strong>它å¯ä»¥å¸®åŠ©ä¸å°åž‹å®žä¾‹å‘现网络ä¸çš„内容</strong>ï¼Œè€Œæ— éœ€æœ¬åœ°ç”¨æˆ·æ‰‹åŠ¨å…³æ³¨å…¶ä»–è¿œç¨‹å®žä¾‹ä¸Šçš„ç”¨æˆ·ã€‚" + description_html: "<strong>ä¸ç»§æœåС噍</strong>是一个信æ¯ç»ŸåˆæœåŠ¡å™¨ï¼Œå„æœåС噍å¯ä»¥é€šè¿‡è®¢é˜…ä¸ç»§æœåŠ¡å™¨å’Œå‘ä¸ç»§æœåŠ¡å™¨æŽ¨é€ä¿¡æ¯æ¥äº¤æ¢å¤§é‡å…¬å¼€å˜Ÿæ–‡ã€‚<strong>它å¯ä»¥å¸®åŠ©ä¸å°åž‹æœåС噍å‘现è”邦宇宙ä¸çš„å…¶ä»–æœåŠ¡å™¨çš„å†…å®¹</strong>ï¼Œè€Œæ— éœ€æœ¬ç«™ç”¨æˆ·æ‰‹åŠ¨å…³æ³¨å…¶ä»–è¿œç¨‹æœåŠ¡å™¨ä¸Šçš„ç”¨æˆ·ã€‚" disable: ç¦ç”¨ disabled: å·²ç¦ç”¨ enable: å¯ç”¨ @@ -400,14 +412,14 @@ zh-CN: desc_html: 用于在首页展示。推è分辨率 293×205px 以上。未指定的情况下将使用默认å‰ç¥¥ç‰©ã€‚ title: å‰ç¥¥ç‰©å›¾åƒ peers_api_enabled: - desc_html: æˆªè‡³ç›®å‰æœ¬å®žä¾‹åœ¨ç½‘络ä¸å·²å‘现的域å + desc_html: æˆªè‡³ç›®å‰æœ¬æœåŠ¡å™¨åœ¨è”邦宇宙ä¸å·²å‘现的域å title: 公开已知实例的列表 preview_sensitive_media: desc_html: 始终在站外链接预览ä¸å±•ç¤ºç¼©ç•¥å›¾ï¼Œæ— è®ºåª’ä½“å†…å®¹æ˜¯å¦æ ‡è®°ä¸ºæ•感 title: 在 OpenGraph é¢„è§ˆä¸æ˜¾ç¤ºæ•感媒体内容 profile_directory: - desc_html: å…许用户å¯è¢«å‘现 - title: å¯ç”¨ç”¨æˆ·èµ„料目录 + desc_html: å…许用户被å‘现 + title: å¯ç”¨ç”¨æˆ·ç›®å½• registrations: closed_message: desc_html: æœ¬ç«™å…³é—æ³¨å†ŒæœŸé—´çš„æç¤ºä¿¡æ¯ã€‚å¯ä»¥ä½¿ç”¨ HTML æ ‡ç¾ @@ -443,6 +455,9 @@ zh-CN: desc_html: å¯ä»¥å¡«å†™è‡ªå·±çš„éšç§æƒæ”¿ç–ã€ä½¿ç”¨æ¡æ¬¾æˆ–其他法律文本。å¯ä»¥ä½¿ç”¨ HTML æ ‡ç¾ title: è‡ªå®šä¹‰ä½¿ç”¨æ¡æ¬¾ site_title: 本站åç§° + spam_check_enabled: + desc_html: Mastodonå¯ä»¥è‡ªåЍéšè—和举报é‡å¤å‘é€åžƒåœ¾æ¶ˆæ¯çš„账户。但是本功能有å¯èƒ½è¯¯ä¼¤æ— 辜。 + title: 自动å垃圾 thumbnail: desc_html: 用于在 OpenGraph å’Œ API 䏿˜¾ç¤ºé¢„览图。推è分辨率 1200×630px title: 本站缩略图 @@ -450,12 +465,15 @@ zh-CN: desc_html: 在主页显示公共时间轴 title: 时间轴预览 title: 网站设置 + trends: + title: çƒé—¨æ ‡ç¾ statuses: back_to_account: è¿”å›žå¸æˆ·ä¿¡æ¯é¡µ batch: delete: åˆ é™¤ nsfw_off: æ ‡è®°ä¸ºéžæ•感内容 nsfw_on: æ ‡è®°ä¸ºæ•æ„Ÿå†…容 + deleted: å·²åˆ é™¤ failed_to_execute: 执行失败 media: title: 媒体文件 @@ -463,21 +481,15 @@ zh-CN: no_status_selected: å› ä¸ºæ²¡æœ‰å˜Ÿæ–‡è¢«é€‰ä¸ï¼Œæ‰€ä»¥æ²¡æœ‰æ›´æ”¹ title: 叿ˆ·å˜Ÿæ–‡ with_media: 嫿œ‰åª’体文件 - subscriptions: - callback_url: 回调 URL - confirmed: 已确认 - expires_in: 失效时间 - last_delivery: 最åŽä¸€æ¬¡æŽ¥æ”¶æ•°æ®çš„æ—¶é—´ - title: WebSub - topic: è¯é¢˜ tags: - accounts: 叿ˆ· - hidden: éšè— - hide: 从目录éšè— - name: è¯é¢˜æ ‡ç¾ + accounts_today: 今日活跃用户 + accounts_week: 本周活跃用户 + directory: åœ¨ç›®å½•ä¸ + review: å®¡æ ¸çŠ¶æ€ + reviewed: å·²å®¡æ ¸ title: è¯é¢˜æ ‡ç¾ - unhide: åœ¨ç›®å½•ä¸æ˜¾ç¤º - visible: å¯è§ + trending_right_now: 当å‰çƒé—¨ + unreviewed: æœªå®¡æ ¸ title: ç®¡ç† warning_presets: add_new: æ·»åŠ æ–°æ¡ç›® @@ -498,6 +510,7 @@ zh-CN: advanced_web_interface_hint: å¦‚æžœä½ æƒ³ä½¿ç”¨æ•´ä¸ªå±å¹•宽度,高级 web 界é¢å…许您é…置多个ä¸åŒçš„æ ç›®ï¼Œå¯ä»¥åŒæ—¶çœ‹åˆ°æ›´å¤šçš„ä¿¡æ¯ï¼šä¸»é¡µã€é€šçŸ¥ã€è·¨ç«™æ—¶é—´è½´ã€ä»»æ„æ•°é‡çš„列表和è¯é¢˜æ ‡ç¾ã€‚ animations_and_accessibility: 动画和访问选项 confirmation_dialogs: ç¡®è®¤å¯¹è¯æ¡† + discovery: å‘现 sensitive_content: æ•æ„Ÿå†…容 application_mailer: notification_preferences: 更改电å邮件首选项 @@ -518,7 +531,6 @@ zh-CN: apply_for_account: 请求邀请 change_password: 密ç checkbox_agreement_html: æˆ‘åŒæ„ <a href="%{rules_path}" target="_blank">æœåŠ¡å™¨è§„åˆ™</a> å’Œ <a href="%{terms_path}" target="_blank">æœåŠ¡æ¡æ¬¾</a> - confirm_email: 确认电åé‚®ä»¶åœ°å€ delete_account: åˆ é™¤å¸æˆ· delete_account_html: å¦‚æžœä½ æƒ³åˆ é™¤ä½ çš„å¸æˆ·ï¼Œè¯·<a href="%{path}">点击这里继ç»</a>ã€‚ä½ éœ€è¦ç¡®è®¤ä½ çš„æ“作。 didnt_get_confirmation: 没有收到确认邮件? @@ -538,6 +550,11 @@ zh-CN: reset_password: é‡ç½®å¯†ç security: 叿ˆ·å®‰å…¨ set_new_password: 设置新密ç + setup: + title: åˆå§‹è®¾ç½® + status: + account_status: 叿ˆ·çŠ¶æ€ + confirming: ç‰å¾…电å邮件确认完æˆã€‚ trouble_logging_in: 登录有问题? authorize_follow: already_following: ä½ å·²ç»åœ¨å…³æ³¨æ¤ç”¨æˆ·äº† @@ -565,25 +582,20 @@ zh-CN: x_months: "%{count}个月" x_seconds: "%{count}ç§’" deletes: - bad_password_msg: 想得美,黑客ï¼å¯†ç 输入错误 confirm_password: è¾“å…¥ä½ å½“å‰çš„å¯†ç æ¥éªŒè¯èº«ä»½ - description_html: ç»§ç»æ“作将会<strong>永久地ã€ä¸å¯æ’¤é”€åœ°</strong>åˆ é™¤å¸æˆ·ä¸çš„æ‰€æœ‰å†…容,然åŽå†»ç»“叿ˆ·ã€‚ä½ çš„ç”¨æˆ·å将会被ä¿ç•™ï¼Œä»¥é˜²æœ‰äººå†’ç”¨ä½ çš„èº«ä»½ã€‚ proceed: åˆ é™¤å¸æˆ· success_msg: ä½ çš„å¸æˆ·å·²ç»æˆåŠŸåˆ é™¤ - warning_html: 我们åªèƒ½ä¿è¯æœ¬æœåŠ¡å™¨ä¸Šçš„å†…å®¹å°†ä¼šè¢«å½»åº•åˆ é™¤ã€‚å¯¹äºŽå·²ç»è¢«å¹¿æ³›ä¼ æ’的内容,它们在本æœåŠ¡å™¨ä»¥å¤–çš„æŸäº›åœ°æ–¹å¯èƒ½ä»ç„¶å¯è§ã€‚æ¤å¤–,失去连接的æœåС噍以åŠåœæ¢æŽ¥æ”¶è®¢é˜…çš„æœåŠ¡å™¨æ‰€å˜å‚¨çš„æ•°æ®äº¦æ— æ³•åˆ é™¤ã€‚ - warning_title: å…³äºŽå·²ä¼ æ’的内容的è¦å‘Š directories: - directory: 用户资料目录 - enabled: 您目å‰å·²è¢«åˆ—入目录ä¸ã€‚ - enabled_but_waiting: 您已选择列入目录,但是您没有达到关注者数é‡ä¸‹é™ (%{min_followers} å) 。 + directory: 用户目录 explanation: æ ¹æ®å…´è¶£å‘现用户 explore_mastodon: 探索 %{title} - how_to_enable: æ‚¨ç›®å‰æ²¡æœ‰é€‰æ‹©é€‰æ‹©åˆ—入到目录ä¸ã€‚您å¯ä»¥åœ¨ä¸‹é¢é€‰æ‹©åˆ—入。å¯ä»¥åœ¨ä¸ªäººç®€ä»‹ä¸åŠ ä¸Šè¯é¢˜æ ‡ç¾ï¼Œè¯é¢˜æ ‡ç¾ä¹Ÿä¼šæ˜¾ç¤ºåœ¨ç”¨æˆ·èµ„æ–™ç›®å½•é‡Œï¼ - people: - other: "%{count} 人" + domain_validator: + invalid_domain: 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„域å errors: + '400': 您æäº¤çš„è¯·æ±‚æ— æ•ˆæˆ–æ ¼å¼ä¸æ£ç¡®ã€‚ '403': ä½ æ²¡æœ‰è®¿é—®è¿™ä¸ªé¡µé¢çš„æƒé™ã€‚ '404': æ— æ³•æ‰¾åˆ°ä½ æ‰€è¦è®¿é—®çš„页é¢ã€‚ + '406': This page is not available in the requested format. '410': ä½ æ‰€è¦è®¿é—®çš„页颿¤å¤„å·²ä¸å˜åœ¨ã€‚ '422': content: æ— æ³•ç¡®è®¤ç™»å½•ä¿¡æ¯ã€‚ä½ æ˜¯ä¸æ˜¯å±è”½äº† Cookie? @@ -592,6 +604,7 @@ zh-CN: '500': content: 抱æ‰ï¼Œæˆ‘们的åŽå°å‡ºé”™äº†ã€‚ title: è¿™ä¸ªé¡µé¢æœ‰é—®é¢˜ + '503': The page could not be served due to a temporary server failure. noscript_html: 使用 Mastodon 网页版应用需è¦å¯ç”¨ JavaScriptã€‚ä½ ä¹Ÿå¯ä»¥é€‰æ‹©é€‚ç”¨äºŽä½ çš„å¹³å°çš„ <a href="%{apps_path}">Mastodon 应用</a>。 existing_username_validator: not_found: 在本站找ä¸åˆ°æ¤ç”¨æˆ· @@ -635,6 +648,7 @@ zh-CN: developers: å¼€å‘者 more: 更多… resources: èµ„æº + trending_now: 现在æµè¡Œ generic: all: 全部 changes_saved_msg: 更改ä¿å˜æˆåŠŸï¼ @@ -708,9 +722,6 @@ zh-CN: too_many: 最多åªèƒ½æ·»åŠ 4 å¼ å›¾ç‰‡ migrations: acct: æ–°å¸æˆ·çš„ 用户å@域å - currently_redirecting: ç›®å‰ä½ çš„ä¸ªäººèµ„æ–™é¡µæ˜¾ç¤ºçš„æ–°å¸æˆ·æ˜¯ï¼š - proceed: ä¿å˜ - updated_msg: 叿ˆ·è¿ç§»è®¾ç½®æ›´æ–°æˆåŠŸï¼ moderation: title: è¿è¥ notification_mailer: @@ -748,6 +759,7 @@ zh-CN: number: human: decimal_units: + format: "%n%u" units: billion: B million: M @@ -759,6 +771,7 @@ zh-CN: next: 下一页 older: æ›´æ—© prev: 上一页 + truncate: "…" polls: errors: already_voted: ä½ å·²ç»åœ¨è¿™é‡ŒæŠ•过票了 @@ -803,10 +816,6 @@ zh-CN: reply: proceed: ç¡®è®¤å›žå¤ prompt: 您想è¦å›žå¤æ¤å˜Ÿæ–‡ï¼š - remote_unfollow: - error: 错误 - title: æ ‡é¢˜ - unfollowed: 已喿¶ˆå…³æ³¨ scheduled_statuses: over_daily_limit: æ‚¨å·²è¶…å‡ºæ¯æ—¥å®šæ—¶å˜Ÿæ–‡çš„上é™ï¼ˆ%{limit} æ¡ï¼‰ over_total_limit: 您已超出定时嘟文的上é™ï¼ˆ%{limit} æ¡ï¼‰ @@ -870,7 +879,7 @@ zh-CN: notifications: 通知 preferences: 首选项 profile: 个人资料 - relationships: æ£åœ¨å…³æ³¨ä»¥åŠå…³æ³¨è€… + relationships: å…³æ³¨ç®¡ç† two_factor_authentication: åŒé‡è®¤è¯ statuses: attached: @@ -909,7 +918,90 @@ zh-CN: pinned: 置顶嘟文 reblogged: 转嘟 sensitive_content: æ•æ„Ÿå†…容 + tags: + does_not_match_previous_name: 和之å‰çš„åç§°ä¸åŒ¹é… terms: + body_html: | + <h2>éšç§æ”¿ç–</h2> + <h3 id="collect">我们收集什么信æ¯ï¼Ÿ</h3> + + <ul> + <li><em>åŸºæœ¬å¸æˆ·ä¿¡æ¯</em>:å¦‚æžœæ‚¨åœ¨æ¤æœåŠ¡å™¨ä¸Šæ³¨å†Œï¼Œå¯èƒ½ä¼šè¦æ±‚您输入用户å,电å邮件地å€å’Œå¯†ç 。 您还å¯ä»¥è¾“入其他个人资料信æ¯ï¼Œä¾‹å¦‚显示åç§°å’Œä¼ è®°ï¼Œå¹¶ä¸Šä¼ ä¸ªäººèµ„æ–™ç…§ç‰‡å’Œæ ‡é¢˜å›¾åƒã€‚ 用户å,显示åç§°ï¼Œä¼ è®°ï¼Œä¸ªäººèµ„æ–™å›¾ç‰‡å’Œæ ‡é¢˜å›¾ç‰‡å§‹ç»ˆå…¬å¼€åˆ—å‡ºã€‚</li> + <li><em>帖å,关注和其他公共信æ¯</em>: 您关注的人员列表会公开列出,您的粉ä¸ä¹Ÿæ˜¯å¦‚æ¤ã€‚ æäº¤é‚®ä»¶æ—¶ï¼Œä¼šå˜å‚¨æ—¥æœŸå’Œæ—¶é—´ä»¥åŠæ‚¨æäº¤é‚®ä»¶çš„应用程åºã€‚ 消æ¯å¯èƒ½åŒ…å«åª’体附件,例如图片和视频。 公开和éžä¸Šå¸‚帖åå¯å…¬å¼€èŽ·å–。 å½“æ‚¨åœ¨ä¸ªäººèµ„æ–™ä¸æ·»åР取志¶ï¼Œè¿™ä¹Ÿæ˜¯å…¬å¼€ä¿¡æ¯ã€‚ 您的帖å会å‘é€ç»™æ‚¨çš„关注者,在æŸäº›æƒ…况下,这æ„味ç€ä»–们会将其å‘é€åˆ°ä¸åŒçš„æœåŠ¡å™¨ï¼Œå¹¶å°†å‰¯æœ¬å˜å‚¨åœ¨é‚£é‡Œã€‚ å½“æ‚¨åˆ é™¤å¸–åæ—¶ï¼ŒåŒæ ·ä¼šå°†å…¶å‘é€ç»™æ‚¨çš„关注者。 釿–°è®°å½•或赞æˆå…¶ä»–èŒä½çš„行为始终是公开的。</li> + <li><em>直接和关注者的帖å</em>: 所有帖å都在æœåŠ¡å™¨ä¸Šå˜å‚¨å’Œå¤„ç†ã€‚ ä»…é™å…³æ³¨è€…的帖å会å‘é€ç»™æ‚¨çš„关注者和用户,并且直接帖å仅会å‘é€ç»™ä»–ä»¬ä¸æåˆ°çš„ç”¨æˆ·ã€‚ 在æŸäº›æƒ…况下,这æ„味ç€å®ƒä»¬è¢«ä¼ é€åˆ°ä¸åŒçš„æœåŠ¡å™¨å¹¶ä¸”å‰¯æœ¬å˜å‚¨åœ¨é‚£é‡Œã€‚ 我们善æ„努力é™åˆ¶åªæœ‰æŽˆæƒäººå‘˜è®¿é—®è¿™äº›å¸–å,但其他æœåС噍å¯èƒ½æ— æ³•è¿™æ ·åšã€‚ å› æ¤ï¼ŒæŸ¥çœ‹æ‚¨çš„关注者所属的æœåС噍éžå¸¸é‡è¦ã€‚ 您å¯ä»¥åœ¨è®¾ç½®ä¸åˆ‡æ¢é€‰é¡¹ä»¥æ‰‹åŠ¨æ‰¹å‡†å’Œæ‹’ç»æ–°å…³æ³¨è€…。 <em>请记ä½ï¼ŒæœåŠ¡å™¨å’Œä»»ä½•æŽ¥æ”¶æœåŠ¡å™¨çš„æ“作员å¯èƒ½ä¼šæŸ¥çœ‹æ¤ç±»æ¶ˆæ¯</em>, 并且收件人å¯ä»¥æˆªå›¾ï¼Œå¤åˆ¶æˆ–以其他方å¼é‡æ–°å…±äº«å®ƒä»¬ã€‚ <em> ä¸è¦åœ¨ Mastodon 上分享任何å±é™©ä¿¡æ¯ã€‚</em></li> + <li><em>IP和其他元数æ®</em>: 登录时,我们会记录您登录的IP地å€ä»¥åŠæµè§ˆå™¨åº”用程åºçš„å称。 所有登录的会è¯éƒ½å¯ä¾›æ‚¨åœ¨è®¾ç½®ä¸æŸ¥çœ‹å’Œæ’¤é”€ã€‚ 使用的最新IPåœ°å€æœ€é•¿å¯å˜å‚¨12个月。 我们还å¯ä»¥ä¿ç•™æœåŠ¡å™¨æ—¥å¿—ï¼Œå…¶ä¸åŒ…括我们æœåŠ¡å™¨çš„æ¯ä¸ªè¯·æ±‚çš„IP地å€ã€‚</li> + </ul> + + <hr class="spacer" /> + + <h3 id="use">我们将您的信æ¯ç”¨äºŽä»€ä¹ˆï¼Ÿ</h3> + + <p>æˆ‘ä»¬å‘æ‚¨æ”¶é›†çš„任何信æ¯å‡å¯é€šè¿‡ä»¥ä¸‹æ–¹å¼ä½¿ç”¨ï¼š</p> + + <ul> + <li>æä¾›Mastodonçš„æ ¸å¿ƒåŠŸèƒ½ã€‚ 您åªèƒ½åœ¨ç™»å½•时与其他人的内容进行互动并å‘布您自己的内容。例如,您å¯ä»¥å…³æ³¨å…¶ä»–äººåœ¨æ‚¨è‡ªå·±çš„ä¸ªæ€§åŒ–å®¶åºæ—¶é—´è½´ä¸æŸ¥çœ‹ä»–们的组åˆå¸–å。</li> + <li>为了帮助社区适度,例如将您的IP地å€ä¸Žå…¶ä»–已知的IP地å€è¿›è¡Œæ¯”è¾ƒï¼Œä»¥ç¡®å®šç¦æ¢é€ƒç¨Žæˆ–å…¶ä»–è¿è§„行为。</li> + <li>您æä¾›çš„电å邮件地å€å¯èƒ½ç”¨äºŽå‘您å‘é€ä¿¡æ¯ï¼Œæœ‰å…³å…¶ä»–äººä¸Žæ‚¨çš„å†…å®¹äº¤äº’æˆ–å‘æ‚¨å‘逿¶ˆæ¯çš„通知,以åŠå›žå¤æŸ¥è¯¢å’Œ/或其他请求或问题。</li> + </ul> + + <hr class="spacer" /> + + <h3 id="protect">æˆ‘ä»¬å¦‚ä½•ä¿æŠ¤æ‚¨çš„ä¿¡æ¯ï¼Ÿ</h3> + + <p>当您输入,æäº¤æˆ–è®¿é—®æ‚¨çš„ä¸ªäººä¿¡æ¯æ—¶ï¼Œæˆ‘们会实施å„ç§å®‰å…¨æŽªæ–½ä»¥ç»´æŠ¤æ‚¨çš„个人信æ¯çš„安全。 除æ¤ä¹‹å¤–,您的æµè§ˆå™¨ä¼šè¯ä»¥åŠåº”用程åºå’ŒAPI之间的æµé‡éƒ½ä½¿ç”¨SSLè¿›è¡Œä¿æŠ¤ï¼Œæ‚¨çš„å¯†ç 使用强大的å•å‘算法进行哈希处ç†ã€‚ 您å¯ä»¥å¯ç”¨åŒå› ç´ èº«ä»½éªŒè¯ï¼Œä»¥è¿›ä¸€æ¥ä¿æŠ¤å¯¹æ‚¨å¸æˆ·çš„访问。</p> + + <hr class="spacer" /> + + <h3 id="data-retention">我们的数æ®ä¿ç•™æ”¿ç–是什么?</h3> + + <p>我们真诚的努力:</p> + + <ul> + <li>ä¿ç•™åŒ…嫿¤æœåŠ¡å™¨çš„æ‰€æœ‰è¯·æ±‚çš„IP地å€çš„æœåŠ¡å™¨æ—¥å¿—ï¼Œåªè¦ä¿ç•™æ¤ç±»æ—¥å¿—,ä¸è¶…过90天。</li> + <li>ä¿ç•™ä¸Žæ³¨å†Œç”¨æˆ·å…³è”çš„IP地å€ä¸è¶…过12个月。</li> + </ul> + + <p>您å¯ä»¥è¯·æ±‚å¹¶ä¸‹è½½æˆ‘ä»¬å†…å®¹çš„å˜æ¡£ï¼ŒåŒ…括您的帖åï¼Œåª’ä½“é™„ä»¶ï¼Œä¸ªäººèµ„æ–™å›¾ç‰‡å’Œæ ‡é¢˜å›¾ç‰‡ã€‚</p> + + <p>您å¯ä»¥éšæ—¶ä¸å¯é€†è½¬åœ°åˆ é™¤æ‚¨çš„å¸æˆ·ã€‚</p> + + <hr class="spacer"/> + + <h3 id="cookies">我们使用 cookies å—?</h3> + + <p>是。 Cookie是网站或其æœåŠ¡æä¾›å•†é€šè¿‡Webæµè§ˆå™¨ä¼ è¾“åˆ°è®¡ç®—æœºç¡¬ç›˜çš„å°æ–‡ä»¶ï¼ˆå¦‚æžœå…许)。 这些cookie使网站能够识别您的æµè§ˆå™¨ï¼Œå¦‚æžœæ‚¨æœ‰æ³¨å†Œå¸æˆ·ï¼Œåˆ™å°†å…¶ä¸Žæ‚¨çš„æ³¨å†Œå¸æˆ·ç›¸å…³è”。</p> + + <p>我们使用Cookieæ¥äº†è§£å¹¶ä¿å˜æ‚¨å¯¹æœªæ¥è®¿é—®çš„å好。</p> + + <hr class="spacer" /> + + <h3 id="disclose">我们是å¦é€éœ²ä»»ä½•ä¿¡æ¯ç»™å…¶ä»–方?</h3> + + <p>我们ä¸ä¼šå°†æ‚¨çš„个人身份信æ¯å‡ºå”®ï¼Œäº¤æ˜“或以其他方å¼è½¬è®©ç»™å¤–方。 è¿™ä¸åŒ…括å助我们æ“作我们的网站,开展业务或为您æœåŠ¡çš„å—信任的第三方,åªè¦è¿™äº›æ–¹åŒæ„ä¿å¯†è¿™äº›ä¿¡æ¯ã€‚ 当我们认为å‘布适åˆéµå®ˆæ³•å¾‹ï¼Œæ‰§è¡Œæˆ‘ä»¬çš„ç½‘ç«™æ”¿ç–æˆ–ä¿æŠ¤æˆ‘ä»¬æˆ–ä»–äººçš„æƒåˆ©ï¼Œè´¢äº§æˆ–安全时,我们也å¯èƒ½ä¼šå‘布您的信æ¯ã€‚</p> + + <p>您的公共内容å¯èƒ½ä¼šè¢«ç½‘络ä¸çš„å…¶ä»–æœåŠ¡å™¨ä¸‹è½½ã€‚ 您的公开帖å和关注者帖å会å‘é€åˆ°å…³æ³¨è€…所在的æœåŠ¡å™¨ï¼Œå¹¶ä¸”ç›´æŽ¥é‚®ä»¶ä¼šä¼ é€’åˆ°æ”¶ä»¶äººçš„æœåŠ¡å™¨ï¼Œåªè¦è¿™äº›å…³æ³¨è€…或收件人ä½äºŽä¸Žæ¤ä¸åŒçš„æœåŠ¡å™¨ä¸Šã€‚</p> + + <p>当您授æƒåº”用程åºä½¿ç”¨æ‚¨çš„叿ˆ·æ—¶ï¼Œæ ¹æ®æ‚¨æ‰¹å‡†çš„æƒé™èŒƒå›´ï¼Œå®ƒå¯èƒ½ä¼šè®¿é—®æ‚¨çš„公开个人资料信æ¯ï¼Œä»¥ä¸‹åˆ—表,您的关注者,您的列表,所有帖å和您的收è—夹。 åº”ç”¨ç¨‹åºæ°¸è¿œä¸èƒ½è®¿é—®æ‚¨çš„电åé‚®ä»¶åœ°å€æˆ–密ç 。</p> + + <hr class="spacer" /> + + <h3 id="children">儿童使用网站</h3> + + <p>å¦‚æžœæ¤æœåС噍ä½äºŽæ¬§ç›Ÿæˆ–æ¬§æ´²ç»æµŽåŒºï¼šæˆ‘们的网站,产å“å’ŒæœåŠ¡éƒ½æ˜¯é’ˆå¯¹è‡³å°‘16å²çš„人。 如果您未满16å²ï¼Œåˆ™ç¬¦åˆGDPRçš„è¦æ±‚(<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) ä¸è¦ä½¿ç”¨è¿™ä¸ªç½‘站。</p> + + <p>å¦‚æžœæ¤æœåС噍ä½äºŽç¾Žå›½ï¼šæˆ‘们的网站,产å“å’ŒæœåŠ¡å‡é¢å‘至少13å²çš„人。 如果您未满13å²ï¼Œåˆ™ç¬¦åˆCOPPAçš„è¦æ±‚ (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) ä¸è¦ä½¿ç”¨è¿™ä¸ªç½‘站。</p> + + <p>å¦‚æžœæ¤æœåС噍ä½äºŽå¦ä¸€ä¸ªè¾–åŒºï¼Œåˆ™æ³•å¾‹è¦æ±‚å¯èƒ½ä¸åŒã€‚</p> + + <hr class="spacer" /> + + <h3 id="changes">我们éšç§æ”¿ç–çš„å˜æ›´</h3> + + <p>如果我们决定更改我们的éšç§æ”¿ç–,我们会在æ¤é¡µé¢ä¸Šå‘布这些更改。</p> + + <p>本文件为CC-BY-SA。 å®ƒæœ€åŽæ›´æ–°äºŽ2018å¹´3月7日。</p> + + <p>æœ€åˆæ”¹ç¼–自 <a href="https://github.com/discourse/discourse">Discourse éšç§æ”¿ç–</a>.</p> title: "%{instance} ä½¿ç”¨æ¡æ¬¾å’Œéšç§æƒæ”¿ç–" themes: contrast: Mastodon(高对比度) @@ -945,7 +1037,7 @@ zh-CN: disable: è™½ç„¶æ‚¨çš„å¸æˆ·è¢«å†»ç»“ï¼Œæ‚¨çš„å¸æˆ·æ•°æ®ä»ç„¶å®Œæ•´ï¼›ä½†æ˜¯æ‚¨æ— 法在解é”剿‰§è¡Œä»»ä½•æ“作。 silence: å½“æ‚¨çš„å¸æˆ·å—陿—¶ï¼Œåªæœ‰å·²ç»å…³æ³¨è¿‡ä½ 的人æ‰ä¼šè¿™å°æœåŠ¡å™¨ä¸Šçœ‹åˆ°ä½ çš„å˜Ÿæ–‡ï¼Œå¹¶ä¸”æ‚¨ä¼šè¢«æŽ’é™¤åœ¨å„ç§å…¬å…±åˆ—表之外。但是,其他人ä»ç„¶å¯ä»¥æ‰‹åŠ¨å…³æ³¨ä½ ã€‚ suspend: æ‚¨çš„å¸æˆ·å·²è¢«å°ç¦ï¼Œæ‰€æœ‰çš„å˜Ÿæ–‡å’Œæ‚¨ä¸Šä¼ çš„åª’ä½“æ–‡ä»¶éƒ½å·²ç»ä»Žè¯¥æœåŠ¡å™¨å’Œæ‚¨çš„å…³æ³¨è€…çš„æœåŠ¡å™¨ä¸Šåˆ é™¤å¹¶ä¸”ä¸å¯æ¢å¤ã€‚ - review_server_policies: 审阅æœåŠ¡å™¨æ¡æ¬¾ + review_server_policies: 查看æœåŠ¡å™¨æ”¿ç– subject: disable: æ‚¨çš„å¸æˆ· %{acct} 已被冻结 none: 对 %{acct} çš„è¦å‘Š diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 25e7475a88d5ec5e584f4b90af7ffe643c48212b..338cf810ece545acd54b94754361eb5707e9b5c0 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -8,10 +8,6 @@ zh-HK: contact: è¯çµ¡ contact_missing: 未è¨å®š contact_unavailable: 未公開 - extended_description_html: | - <h3>這裡å¯ä»¥å¯«ä¸€äº›ç¶²ç«™è¦å‰‡</h3> - <p>本站未有詳細介紹</p> - generic_description: "%{domain} 是 Mastodon 網絡ä¸å…¶ä¸ä¸€å€‹æœå‹™ç«™" hosted_on: 在 %{domain} é‹ä½œçš„ Mastodon æœå‹™ç«™ learn_more: 了解更多 source_code: æºä»£ç¢¼ @@ -327,13 +323,6 @@ zh-HK: no_media: ä¸å«åª’體檔案 title: å¸³æˆ¶æ–‡ç« with_media: 嫿œ‰åª’體檔案 - subscriptions: - callback_url: 回傳 URL - confirmed: 確定 - expires_in: æœŸé™ - last_delivery: è³‡æ–™æœ€å¾Œé€æŠµæ™‚é–“ - title: PuSH 訂閱 - topic: æ‰€è¨‚é–±è³‡æº title: ç®¡ç† admin_mailer: new_report: @@ -357,7 +346,6 @@ zh-HK: your_token: token auth: change_password: 密碼 - confirm_email: 確èªé›»éƒµ delete_account: 刪除帳戶 delete_account_html: å¦‚æžœä½ æƒ³åˆªé™¤ä½ çš„å¸³æˆ¶ï¼Œè«‹<a href="%{path}">點擊這裡繼續</a>ã€‚ä½ éœ€è¦ç¢ºèªä½ çš„æ“作。 didnt_get_confirmation: æ²’æœ‰æ”¶åˆ°ç¢ºèªæŒ‡ç¤ºé›»éƒµï¼Ÿ @@ -399,16 +387,14 @@ zh-HK: x_months: "%{count}個月" x_seconds: "%{count}ç§’" deletes: - bad_password_msg: 想得美,黑客ï¼å¯†ç¢¼è¼¸å…¥éŒ¯èª¤ confirm_password: è¼¸å…¥ä½ ç¾åœ¨çš„密碼來驗è‰èº«ä»½ - description_html: 繼續æ“作將會<strong>永久地ã€ä¸å¯é‚„原地</strong>刪除帳戶ä¸çš„æ‰€æœ‰å…§å®¹ï¼Œç„¶å¾Œå‡çµå¸³æˆ¶ã€‚ä½ çš„ç”¨æˆ¶å將會被ä¿ç•™ï¼Œä»¥é˜²æœ‰äººå†’ç”¨ä½ çš„èº«ä»½ã€‚ proceed: 刪除帳戶 success_msg: ä½ çš„å¸³æˆ¶å·²ç¶“æˆåŠŸåˆªé™¤ - warning_html: 我們åªèƒ½ä¿è‰æœ¬æœå‹™ç«™ä¸Šçš„å…§å®¹å°‡æœƒè¢«å¾¹åº•åˆªé™¤ã€‚å°æ–¼å·²ç¶“被廣泛傳æ’的內容,它們在本æœå‹™ç«™ä»¥å¤–çš„æŸäº›åœ°æ–¹å¯èƒ½ä»ç„¶å¯è¦‹ã€‚æ¤å¤–,失去連接的æœå‹™ç«™ä»¥åŠåœæ¢æŽ¥æ”¶è¨‚閱的æœå‹™ç«™æ‰€å˜å„²çš„æ•¸æ“šäº¦ç„¡æ³•刪除。 - warning_title: 關於已傳æ’的內容的è¦å‘Š errors: + '400': The request you submitted was invalid or malformed. '403': ä½ æ²’æœ‰è§€çœ‹æœ¬é 的權é™ã€‚ '404': 找ä¸åˆ°å…§å®¹ã€‚ + '406': This page is not available in the requested format. '410': 內容已被刪除。 '422': content: 無法確èªç™»å…¥è³‡è¨Šã€‚æœƒä¸æœƒä½ 阻擋了本站使用 Cookies 的權é™ï¼Ÿ @@ -417,6 +403,7 @@ zh-HK: '500': content: 抱æ‰ï¼Œæˆ‘們的後å°å‡ºéŒ¯äº†ã€‚ title: 這個é 颿œ‰å•題 + '503': The page could not be served due to a temporary server failure. noscript_html: 使用 Mastodon ç¶²é 版應用需è¦å•Ÿç”¨ JavaScriptã€‚ä½ ä¹Ÿå¯ä»¥é¸æ“‡é©ç”¨æ–¼ä½ 的平å°çš„ <a href="%{apps_path}">Mastodon 應用</a>。 exports: archive_takeout: @@ -474,9 +461,6 @@ zh-HK: too_many: ä¸å¯ä»¥åŠ å…¥è¶…éŽ 4 個檔案 migrations: acct: 新帳戶的 用戶å@域å - currently_redirecting: ç›®å‰ä½ 的個人資料é 顯示的新帳戶是: - proceed: ä¿å˜ - updated_msg: 帳戶é·ç§»è¨ç½®æ›´æ–°æˆåŠŸï¼ moderation: title: ç‡Ÿé‹ notification_mailer: @@ -524,10 +508,6 @@ zh-HK: missing_resource: ç„¡æ³•æ‰¾åˆ°ä½ ç”¨æˆ¶çš„è½‰æŽ¥ç¶²å€ proceed: ä¸‹ä¸€æ¥ prompt: ä½ å¸Œæœ›é—œæ³¨ï¸° - remote_unfollow: - error: 錯誤 - title: 標題 - unfollowed: å–æ¶ˆé—œæ³¨ sessions: activity: 最近活動 browser: ç€è¦½å™¨ diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index d3dcf5133f5f4203aa923456606298784f6dcd16..bb6e761c7067c7a1a87b0d320015343c3058bebd 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -16,10 +16,6 @@ zh-TW: contact_unavailable: 未公開 discover_users: 探索使用者 documentation: 文件 - extended_description_html: | - <h3>這裡å¯ä»¥å¯«ä¸€äº›ç¶²ç«™è¦å‰‡</h3> - <p>本站點未有詳細介紹</p> - generic_description: "%{domain} 是 Mastodon 網路ä¸å…¶ä¸ä¸€å€‹ç«™é»ž" get_apps: å˜—è©¦è¡Œå‹•æ‡‰ç”¨ç¨‹å¼ hosted_on: 在 %{domain} é‹ä½œçš„ Mastodon 站點 learn_more: 了解詳細 @@ -401,13 +397,6 @@ zh-TW: no_media: ä¸å«åª’體檔案 title: 帳戶嘟文 with_media: 嫿œ‰åª’體檔案 - subscriptions: - callback_url: å›žå‚³ç¶²å€ - confirmed: å·²ç¢ºèª - expires_in: æœŸé™ - last_delivery: 最後éžé€ - title: WebSub 訂閱 - topic: 主題 title: 管ç†ä»‹é¢ admin_mailer: new_report: @@ -431,7 +420,6 @@ zh-TW: your_token: ä½ çš„ token auth: change_password: 密碼 - confirm_email: 確èªé›»åä¿¡ç®±ä½å€ delete_account: 刪除帳戶 delete_account_html: å¦‚æžœä½ æƒ³åˆªé™¤ä½ çš„å¸³æˆ¶ï¼Œè«‹<a href="%{path}">點擊這裡繼續</a>ã€‚ä½ éœ€è¦ç¢ºèªä½ çš„æ“作。 didnt_get_confirmation: 沒有收到驗è‰ä¿¡? @@ -473,16 +461,14 @@ zh-TW: x_months: "%{count}個月" x_seconds: "%{count}ç§’" deletes: - bad_password_msg: 想得美,é§å®¢! 密碼輸入錯誤 confirm_password: è¼¸å…¥ä½ ç¾åœ¨çš„密碼來驗è‰èº«ä»½ - description_html: 繼續æ“作將會<strong>永久地ã€ä¸å¯é‚„原地</strong>刪除帳戶ä¸çš„æ‰€æœ‰å…§å®¹ï¼Œç„¶å¾Œå‡çµå¸³æˆ¶ã€‚ä½ çš„ä½¿ç”¨è€…å稱將會被ä¿ç•™ï¼Œä»¥é˜²æœ‰äººå†’ç”¨ä½ çš„èº«ä»½ã€‚ proceed: 刪除帳戶 success_msg: ä½ çš„å¸³æˆ¶å·²ç¶“æˆåŠŸåˆªé™¤ - warning_html: 我們åªèƒ½ä¿è‰æœ¬ä¼ºæœå™¨ä¸Šçš„å…§å®¹å°‡æœƒè¢«å¾¹åº•åˆªé™¤ã€‚å°æ–¼å·²ç¶“被廣泛傳æ’的內容,它們在本伺æœå™¨ä»¥å¤–çš„æŸäº›åœ°æ–¹å¯èƒ½ä»ç„¶å¯è¦‹ã€‚æ¤å¤–,離線伺æœå™¨ä»¥åŠåœæ¢æŽ¥æ”¶è¨‚閱的伺æœå™¨æ‰€å„²å˜çš„資料亦無法刪除。 - warning_title: 關於已傳æ’的內容è¦å‘Š errors: + '400': The request you submitted was invalid or malformed. '403': ä½ æ²’æœ‰è§€çœ‹é€™å€‹é é¢çš„æ¬Šé™ã€‚ '404': 您所尋找的網é ä¸å˜åœ¨ã€‚ + '406': This page is not available in the requested format. '410': æ‚¨æ‰€å°‹æ‰¾çš„ç¶²é æ¤è™•å·²ä¸å˜åœ¨ã€‚ '422': content: 安全驗è‰å¤±æ•—。請確定有開啟ç€è¦½å™¨ Cookies 功能? @@ -491,6 +477,7 @@ zh-TW: '500': content: 抱æ‰ï¼Œæˆ‘們的後å°å‡ºç¾å•題了。 title: 這個é 颿œ‰å•題 + '503': The page could not be served due to a temporary server failure. noscript_html: 使用 Mastodon ç¶²é 版應用需è¦å•Ÿç”¨ JavaScriptã€‚ä½ ä¹Ÿå¯ä»¥é¸æ“‡é©ç”¨æ–¼ä½ 的平å°çš„ <a href="%{apps_path}">Mastodon 應用</a>。 exports: archive_takeout: @@ -544,9 +531,6 @@ zh-TW: too_many: ç„¡æ³•åŠ å…¥è¶…éŽ 4 個檔案 migrations: acct: 新帳戶的 使用者å稱@站點網域 - currently_redirecting: ç›®å‰ä½ 的個人資料é 顯示的新帳戶是: - proceed: å„²å˜ - updated_msg: 帳戶æ¬é·è¨å®šæ›´æ–°æˆåŠŸ! moderation: title: ç‡Ÿé‹ notification_mailer: @@ -587,10 +571,6 @@ zh-TW: missing_resource: ç„¡æ³•æ‰¾åˆ°è³‡æº proceed: ä¸‹ä¸€æ¥ prompt: '您希望關注:' - remote_unfollow: - error: 錯誤 - title: 標題 - unfollowed: å–æ¶ˆé—œæ³¨ sessions: activity: 最近活動 browser: ç€è¦½å™¨ diff --git a/config/navigation.rb b/config/navigation.rb index df10241892997bfde20f25ac999d5e8ac7b8ae0a..eebd4f75e38ba3ba336fbe6ac2d1f93050972a99 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -4,42 +4,42 @@ SimpleNavigation::Configuration.run do |navigation| navigation.items do |n| n.item :web, safe_join([fa_icon('chevron-left fw'), t('settings.back')]), root_url - n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_url do |s| - s.item :profile, safe_join([fa_icon('pencil fw'), t('settings.appearance')]), settings_profile_url, highlights_on: %r{/settings/profile|/settings/migration} + n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_url, if: -> { current_user.functional? } do |s| + s.item :profile, safe_join([fa_icon('pencil fw'), t('settings.appearance')]), settings_profile_url s.item :featured_tags, safe_join([fa_icon('hashtag fw'), t('settings.featured_tags')]), settings_featured_tags_url s.item :identity_proofs, safe_join([fa_icon('key fw'), t('settings.identity_proofs')]), settings_identity_proofs_path, highlights_on: %r{/settings/identity_proofs*}, if: proc { current_account.identity_proofs.exists? } end - n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_url do |s| + n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_url, if: -> { current_user.functional? } do |s| s.item :appearance, safe_join([fa_icon('desktop fw'), t('settings.appearance')]), settings_preferences_appearance_url s.item :notifications, safe_join([fa_icon('bell fw'), t('settings.notifications')]), settings_preferences_notifications_url s.item :other, safe_join([fa_icon('cog fw'), t('preferences.other')]), settings_preferences_other_url end - n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_url - n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters} + n.item :relationships, safe_join([fa_icon('users fw'), t('settings.relationships')]), relationships_url, if: -> { current_user.functional? } + n.item :filters, safe_join([fa_icon('filter fw'), t('filters.index.title')]), filters_path, highlights_on: %r{/filters}, if: -> { current_user.functional? } n.item :security, safe_join([fa_icon('lock fw'), t('settings.account')]), edit_user_registration_url do |s| - s.item :password, safe_join([fa_icon('lock fw'), t('settings.account_settings')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete} + s.item :password, safe_join([fa_icon('lock fw'), t('settings.account_settings')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete|/settings/migration|/settings/aliases} s.item :two_factor_authentication, safe_join([fa_icon('mobile fw'), t('settings.two_factor_authentication')]), settings_two_factor_authentication_url, highlights_on: %r{/settings/two_factor_authentication} s.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url end n.item :data, safe_join([fa_icon('cloud-download fw'), t('settings.import_and_export')]), settings_export_url do |s| - s.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url + s.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url, if: -> { current_user.functional? } s.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url end - n.item :invites, safe_join([fa_icon('user-plus fw'), t('invites.title')]), invites_path, if: proc { Setting.min_invite_role == 'user' } - n.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url + n.item :invites, safe_join([fa_icon('user-plus fw'), t('invites.title')]), invites_path, if: proc { Setting.min_invite_role == 'user' && current_user.functional? } + n.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url, if: -> { current_user.functional? } n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s| s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports} s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts} s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path - s.item :tags, safe_join([fa_icon('tag fw'), t('admin.tags.title')]), admin_tags_path - s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks}, if: -> { current_user.admin? } + s.item :tags, safe_join([fa_icon('hashtag fw'), t('admin.tags.title')]), admin_tags_path, highlights_on: %r{/admin/tags} + s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.admin? } s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? } end @@ -47,8 +47,7 @@ SimpleNavigation::Configuration.run do |navigation| s.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url s.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/settings} s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis} - s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/relays} - s.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url, if: -> { current_user.admin? } + s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? && !whitelist_mode? }, highlights_on: %r{/admin/relays} s.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? } s.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? } end diff --git a/config/puma.rb b/config/puma.rb index 6a96867d54d27e04f2090bc4191131da39b8a76c..224be79036556e4fa1cc86f16ffc9b05169cce56 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,3 +1,5 @@ +persistent_timeout ENV.fetch('PERSISTENT_TIMEOUT') { 20 }.to_i + threads_count = ENV.fetch('MAX_THREADS') { 5 }.to_i threads threads_count, threads_count diff --git a/config/routes.rb b/config/routes.rb index 764db8db2e61882d1dc2507de5a50fa04beb2f58..e43e201a5d78f0a18280da8e76e9d4c6eaf4a2d1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,8 +6,12 @@ require 'sidekiq-scheduler/web' Sidekiq::Web.set :session_secret, Rails.application.secrets[:secret_key_base] Rails.application.routes.draw do + root 'home#index' + mount LetterOpenerWeb::Engine, at: 'letter_opener' if Rails.env.development? + health_check_routes + authenticate :user, lambda { |u| u.admin? } do mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq mount PgHero::Engine, at: 'pghero', as: :pghero @@ -20,17 +24,28 @@ Rails.application.routes.draw do end get '.well-known/host-meta', to: 'well_known/host_meta#show', as: :host_meta, defaults: { format: 'xml' } + get '.well-known/nodeinfo', to: 'well_known/nodeinfo#index', as: :nodeinfo, defaults: { format: 'json' } get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger get '.well-known/change-password', to: redirect('/auth/edit') get '.well-known/keybase-proof-config', to: 'well_known/keybase_proof_config#show' + get '/nodeinfo/2.0', to: 'well_known/nodeinfo#show', as: :nodeinfo_schema + get 'manifest', to: 'manifests#show', defaults: { format: 'json' } get 'intent', to: 'intents#show' get 'custom.css', to: 'custom_css#show', as: :custom_css + resource :instance_actor, path: 'actor', only: [:show] do + resource :inbox, only: [:create], module: :activitypub + end + devise_scope :user do get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite - match '/auth/finish_signup' => 'auth/confirmations#finish_signup', via: [:get, :patch], as: :finish_signup + + namespace :auth do + resource :setup, only: [:show, :update], controller: :setup + resource :challenge, only: [:create], controller: :challenges + end end devise_for :users, path: 'auth', controllers: { @@ -45,12 +60,6 @@ Rails.application.routes.draw do get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" } resources :accounts, path: 'users', only: [:show], param: :username do - resources :stream_entries, path: 'updates', only: [:show] do - member do - get :embed - end - end - get :remote_follow, to: 'remote_follow#new' post :remote_follow, to: 'remote_follow#create' @@ -58,8 +67,9 @@ Rails.application.routes.draw do member do get :activity get :embed - get :replies end + + resources :replies, only: [:index], module: :activitypub end resources :followers, only: [:index], controller: :follower_accounts @@ -127,8 +137,13 @@ Rails.application.routes.draw do end resource :delete, only: [:show, :destroy] - resource :migration, only: [:show, :update] + resource :migration, only: [:show, :create] + namespace :migration do + resource :redirect, only: [:new, :create, :destroy] + end + + resources :aliases, only: [:index, :create, :destroy] resources :sessions, only: [:destroy] resources :featured_tags, only: [:index, :create, :destroy] end @@ -146,16 +161,18 @@ Rails.application.routes.draw do get '/public', to: 'public_timelines#show', as: :public_timeline get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy - # Remote follow - resource :remote_unfollow, only: [:create] resource :authorize_interaction, only: [:show, :create] resource :share, only: [:show, :create] namespace :admin do get '/dashboard', to: 'dashboard#index' - resources :subscriptions, only: [:index] - resources :domain_blocks, only: [:new, :create, :show, :destroy] + resources :domain_allows, only: [:new, :create, :show, :destroy] + resources :domain_blocks, only: [:new, :create, :show, :destroy, :update] do + member do + get :edit + end + end resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resources :action_logs, only: [:index] resources :warning_presets, except: [:new] @@ -191,8 +208,6 @@ Rails.application.routes.draw do resources :accounts, only: [:index, :show] do member do - post :subscribe - post :unsubscribe post :enable post :unsilence post :unsuspend @@ -236,20 +251,19 @@ Rails.application.routes.draw do resource :two_factor_authentication, only: [:destroy] end - resources :custom_emojis, only: [:index, :new, :create, :update, :destroy] do - member do - post :copy - post :enable - post :disable + resources :custom_emojis, only: [:index, :new, :create] do + collection do + post :batch end end resources :account_moderation_notes, only: [:create, :destroy] - resources :tags, only: [:index] do - member do - post :hide - post :unhide + resources :tags, only: [:index, :show, :update] do + collection do + post :approve_all + post :reject_all + post :batch end end end @@ -257,16 +271,6 @@ Rails.application.routes.draw do get '/admin', to: redirect('/admin/dashboard', status: 302) namespace :api do - # PubSubHubbub outgoing subscriptions - resources :subscriptions, only: [:show] - post '/subscriptions/:id', to: 'subscriptions#update' - - # PubSubHubbub incoming subscriptions - post '/push', to: 'push#update', as: :push - - # Salmon - post '/salmon/:id', to: 'salmon#update', as: :salmon - # OEmbed get '/oembed', to: 'oembed#show', as: :oembed @@ -294,12 +298,10 @@ Rails.application.routes.draw do member do get :context - get :card end end namespace :timelines do - resource :direct, only: :show, controller: :direct resource :home, only: :show, controller: :home resource :public, only: :show, controller: :public resources :tag, only: :show @@ -318,16 +320,15 @@ Rails.application.routes.draw do end end - get '/search', to: 'search#index', as: :search - - resources :follows, only: [:create] resources :media, only: [:create, :update] resources :blocks, only: [:index] resources :mutes, only: [:index] resources :favourites, only: [:index] resources :reports, only: [:create] + resources :trends, only: [:index] resources :filters, only: [:index, :create, :show, :update, :destroy] resources :endorsements, only: [:index] + resources :markers, only: [:index, :create] namespace :apps do get :verify_credentials, to: 'credentials#show' @@ -341,6 +342,7 @@ Rails.application.routes.draw do end resource :domain_blocks, only: [:show, :create, :destroy] + resource :directory, only: [:show] resources :follow_requests, only: [:index] do member do @@ -352,7 +354,6 @@ Rails.application.routes.draw do resources :notifications, only: [:index, :show] do collection do post :clear - post :dismiss # Deprecated end member do @@ -391,6 +392,12 @@ Rails.application.routes.draw do resource :accounts, only: [:show, :create, :destroy], controller: 'lists/accounts' end + namespace :featured_tags do + get :suggestions, to: 'suggestions#index' + end + + resources :featured_tags, only: [:index, :create, :destroy] + resources :polls, only: [:create, :show] do resources :votes, only: :create, controller: 'polls/votes' end @@ -440,14 +447,10 @@ Rails.application.routes.draw do get '/web/(*any)', to: 'home#index', as: :web - get '/about', to: 'about#show' - get '/about/more', to: 'about#more' - get '/terms', to: 'about#terms' - - root 'home#index' + get '/about', to: 'about#show' + get '/about/more', to: 'about#more' + get '/terms', to: 'about#terms' - match '*unmatched_route', - via: :all, - to: 'application#raise_not_found', - format: false + match '/', via: [:post, :put, :patch, :delete], to: 'application#raise_not_found', format: false + match '*unmatched_route', via: :all, to: 'application#raise_not_found', format: false end diff --git a/config/settings.yml b/config/settings.yml index da6ee643a2b0838af7a2993653c8026279a6ba7c..a39eb2ea80bcd872125cd21676aae0c0d1a2d7d4 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -33,6 +33,10 @@ defaults: &defaults theme: 'default' aggregate_reblogs: true advanced_layout: false + use_blurhash: true + use_pending_items: false + trends: true + trendable_by_default: false notification_emails: follow: false reblog: false @@ -42,6 +46,7 @@ defaults: &defaults digest: true report: true pending_account: true + trending_tag: true interactions: must_be_follower: false must_be_following: false @@ -60,6 +65,9 @@ defaults: &defaults activity_api_enabled: true peers_api_enabled: true show_known_fediverse_at_about_page: true + spam_check_enabled: true + show_domain_blocks: 'disabled' + show_domain_blocks_rationale: 'disabled' development: <<: *defaults diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 0ec1742abfeb2547627c6691f972e553ff36dd58..5de25de234be1f5ebcf246090c4a91cb9bce14cf 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -9,9 +9,9 @@ scheduled_statuses_scheduler: every: '5m' class: Scheduler::ScheduledStatusesScheduler - subscriptions_scheduler: - cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *' - class: Scheduler::SubscriptionsScheduler + trending_tags_scheduler: + every: '5m' + class: Scheduler::TrendingTagsScheduler media_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *' class: Scheduler::MediaCleanupScheduler @@ -24,9 +24,6 @@ user_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *' class: Scheduler::UserCleanupScheduler - subscriptions_cleanup_scheduler: - cron: '<%= Random.rand(0..59) %> <%= Random.rand(1..3) %> * * 0' - class: Scheduler::SubscriptionsCleanupScheduler ip_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *' class: Scheduler::IpCleanupScheduler diff --git a/config/webpack/development.js b/config/webpack/development.js index 1e50a4f468efb19a792f13abc0c6f294b3be6c2b..56f6e43f03a3423d253cbbf426b3a49bc43e9b62 100644 --- a/config/webpack/development.js +++ b/config/webpack/development.js @@ -56,5 +56,6 @@ module.exports = merge(sharedConfig, { settings.dev_server.watch_options, watchOptions ), + writeToDisk: filePath => /ocr/.test(filePath), }, }); diff --git a/config/webpack/rules/css.js b/config/webpack/rules/css.js index 3b5b51232029fdfbda34b49a4408c11e073e4d70..bc1f42c13482bffbfb6890ec5cc45f021fd79a9b 100644 --- a/config/webpack/rules/css.js +++ b/config/webpack/rules/css.js @@ -9,7 +9,6 @@ module.exports = { options: { sourceMap: true, importLoaders: 2, - localIdentName: '[name]__[local]___[hash:base64:5]', }, }, { diff --git a/config/webpack/shared.js b/config/webpack/shared.js index cb4e5a85f0e70eba3376c64a78a9fc8609961ca7..d5e399ced80a1320c7c9967e6469f90b37136180 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -5,6 +5,7 @@ const { basename, dirname, join, relative, resolve } = require('path'); const { sync } = require('glob'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const AssetsManifestPlugin = require('webpack-assets-manifest'); +const CopyPlugin = require('copy-webpack-plugin'); const extname = require('path-complete-extname'); const { env, settings, themes, output } = require('./configuration'); const rules = require('./rules'); @@ -84,6 +85,10 @@ module.exports = { writeToDisk: true, publicPath: true, }), + new CopyPlugin([ + { from: 'node_modules/tesseract.js/dist/worker.min.js', to: 'ocr' }, + { from: 'node_modules/tesseract.js-core/tesseract-core.wasm.js', to: 'ocr' }, + ]), ], resolve: { diff --git a/crowdin.yml b/crowdin.yml index f94417f2ed7b776f30cc4e90f01812319120fd2b..88a24d6211d50910a1568b3186540f5e5fc97689 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,4 +1,4 @@ -commit_message: "[ci skip]" +commit_message: '[ci skip]' files: - source: /app/javascript/mastodon/locales/en.json translation: /app/javascript/mastodon/locales/%two_letters_code%.json diff --git a/db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb b/db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb index 2c24b53d09edc347f1b757e9eea9abb75180987c..3a559ccd6da2da644d458d2bf399fe17caf34b9b 100644 --- a/db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb +++ b/db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb @@ -3,7 +3,7 @@ class AddInReplyToAccountIdToStatuses < ActiveRecord::Migration[5.0] add_column :statuses, :in_reply_to_account_id, :integer, null: true, default: nil ActiveRecord::Base.transaction do - Status.where.not(in_reply_to_id: nil).includes(:thread).find_each do |status| + Status.unscoped.where.not(in_reply_to_id: nil).includes(:thread).find_each do |status| next if status.thread.nil? status.in_reply_to_account_id = status.thread.account_id diff --git a/db/migrate/20170209184350_add_reply_to_statuses.rb b/db/migrate/20170209184350_add_reply_to_statuses.rb index c5074728bf1f8bee38ced0fce5b3353dcdfd0509..b8b5c130681a5994623654fab3196433bcaa4792 100644 --- a/db/migrate/20170209184350_add_reply_to_statuses.rb +++ b/db/migrate/20170209184350_add_reply_to_statuses.rb @@ -1,7 +1,7 @@ class AddReplyToStatuses < ActiveRecord::Migration[5.0] def up add_column :statuses, :reply, :boolean, nil: false, default: false - Status.update_all('reply = (in_reply_to_id IS NOT NULL)') + Status.unscoped.update_all('reply = (in_reply_to_id IS NOT NULL)') end def down diff --git a/db/migrate/20170918125918_ids_to_bigints.rb b/db/migrate/20170918125918_ids_to_bigints.rb index c6feed8f9187877d4afca73b34af543fa65b6389..8e19468db851a25941b34e1cfe753c7ffe6c7e24 100644 --- a/db/migrate/20170918125918_ids_to_bigints.rb +++ b/db/migrate/20170918125918_ids_to_bigints.rb @@ -5,70 +5,70 @@ class IdsToBigints < ActiveRecord::Migration[5.1] disable_ddl_transaction! - INCLUDED_COLUMNS = [ - [:account_domain_blocks, :account_id], - [:account_domain_blocks, :id], - [:accounts, :id], - [:blocks, :account_id], - [:blocks, :id], - [:blocks, :target_account_id], - [:conversation_mutes, :account_id], - [:conversation_mutes, :id], - [:domain_blocks, :id], - [:favourites, :account_id], - [:favourites, :id], - [:favourites, :status_id], - [:follow_requests, :account_id], - [:follow_requests, :id], - [:follow_requests, :target_account_id], - [:follows, :account_id], - [:follows, :id], - [:follows, :target_account_id], - [:imports, :account_id], - [:imports, :id], - [:media_attachments, :account_id], - [:media_attachments, :id], - [:mentions, :account_id], - [:mentions, :id], - [:mutes, :account_id], - [:mutes, :id], - [:mutes, :target_account_id], - [:notifications, :account_id], - [:notifications, :from_account_id], - [:notifications, :id], - [:oauth_access_grants, :application_id], - [:oauth_access_grants, :id], - [:oauth_access_grants, :resource_owner_id], - [:oauth_access_tokens, :application_id], - [:oauth_access_tokens, :id], - [:oauth_access_tokens, :resource_owner_id], - [:oauth_applications, :id], - [:oauth_applications, :owner_id], - [:reports, :account_id], - [:reports, :action_taken_by_account_id], - [:reports, :id], - [:reports, :target_account_id], - [:session_activations, :access_token_id], - [:session_activations, :user_id], - [:session_activations, :web_push_subscription_id], - [:settings, :id], - [:settings, :thing_id], - [:statuses, :account_id], - [:statuses, :application_id], - [:statuses, :in_reply_to_account_id], - [:stream_entries, :account_id], - [:stream_entries, :id], - [:subscriptions, :account_id], - [:subscriptions, :id], - [:tags, :id], - [:users, :account_id], - [:users, :id], - [:web_settings, :id], - [:web_settings, :user_id], - ] - INCLUDED_COLUMNS << [:deprecated_preview_cards, :id] if table_exists?(:deprecated_preview_cards) - def migrate_columns(to_type) + included_columns = [ + [:account_domain_blocks, :account_id], + [:account_domain_blocks, :id], + [:accounts, :id], + [:blocks, :account_id], + [:blocks, :id], + [:blocks, :target_account_id], + [:conversation_mutes, :account_id], + [:conversation_mutes, :id], + [:domain_blocks, :id], + [:favourites, :account_id], + [:favourites, :id], + [:favourites, :status_id], + [:follow_requests, :account_id], + [:follow_requests, :id], + [:follow_requests, :target_account_id], + [:follows, :account_id], + [:follows, :id], + [:follows, :target_account_id], + [:imports, :account_id], + [:imports, :id], + [:media_attachments, :account_id], + [:media_attachments, :id], + [:mentions, :account_id], + [:mentions, :id], + [:mutes, :account_id], + [:mutes, :id], + [:mutes, :target_account_id], + [:notifications, :account_id], + [:notifications, :from_account_id], + [:notifications, :id], + [:oauth_access_grants, :application_id], + [:oauth_access_grants, :id], + [:oauth_access_grants, :resource_owner_id], + [:oauth_access_tokens, :application_id], + [:oauth_access_tokens, :id], + [:oauth_access_tokens, :resource_owner_id], + [:oauth_applications, :id], + [:oauth_applications, :owner_id], + [:reports, :account_id], + [:reports, :action_taken_by_account_id], + [:reports, :id], + [:reports, :target_account_id], + [:session_activations, :access_token_id], + [:session_activations, :user_id], + [:session_activations, :web_push_subscription_id], + [:settings, :id], + [:settings, :thing_id], + [:statuses, :account_id], + [:statuses, :application_id], + [:statuses, :in_reply_to_account_id], + [:stream_entries, :account_id], + [:stream_entries, :id], + [:subscriptions, :account_id], + [:subscriptions, :id], + [:tags, :id], + [:users, :account_id], + [:users, :id], + [:web_settings, :id], + [:web_settings, :user_id], + ] + included_columns << [:deprecated_preview_cards, :id] if table_exists?(:deprecated_preview_cards) + # Print out a warning that this will probably take a while. say '' say 'WARNING: This migration may take a *long* time for large instances' @@ -86,7 +86,7 @@ class IdsToBigints < ActiveRecord::Migration[5.1] sleep 1 end - tables = INCLUDED_COLUMNS.map(&:first).uniq + tables = included_columns.map(&:first).uniq table_sizes = {} # Sort tables by their size @@ -94,7 +94,7 @@ class IdsToBigints < ActiveRecord::Migration[5.1] table_sizes[table] = estimate_rows_in_table(table) end - ordered_columns = INCLUDED_COLUMNS.sort_by do |col_parts| + ordered_columns = included_columns.sort_by do |col_parts| [-table_sizes[col_parts.first], col_parts.last] end diff --git a/db/migrate/20180528141303_fix_accounts_unique_index.rb b/db/migrate/20180528141303_fix_accounts_unique_index.rb index bd4e158b7ecccc39892957ceab8b869b452d41b0..bbbf28d8175ddf9b9f720f91a74f3ca80e5deb4d 100644 --- a/db/migrate/20180528141303_fix_accounts_unique_index.rb +++ b/db/migrate/20180528141303_fix_accounts_unique_index.rb @@ -12,6 +12,11 @@ class FixAccountsUniqueIndex < ActiveRecord::Migration[5.2] end end + class StreamEntry < ApplicationRecord + # Dummy class, to make migration possible across version changes + belongs_to :account, inverse_of: :stream_entries + end + disable_ddl_transaction! def up diff --git a/db/migrate/20181024224956_migrate_account_conversations.rb b/db/migrate/20181024224956_migrate_account_conversations.rb index b718f9e1d524e73782bc5824372307c8cc24944b..d4bc3b91d9245e7aa7455d96aac3d4cc22ad5452 100644 --- a/db/migrate/20181024224956_migrate_account_conversations.rb +++ b/db/migrate/20181024224956_migrate_account_conversations.rb @@ -52,6 +52,6 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2] end def notifications_about_direct_statuses - Notification.joins(mention: :status).where(activity_type: 'Mention', statuses: { visibility: :direct }) + Notification.joins('INNER JOIN mentions ON mentions.id = notifications.activity_id INNER JOIN statuses ON statuses.id = mentions.status_id').where(activity_type: 'Mention', statuses: { visibility: :direct }) end end diff --git a/db/migrate/20190403141604_add_comment_to_invites.rb b/db/migrate/20190403141604_add_comment_to_invites.rb new file mode 100644 index 0000000000000000000000000000000000000000..f0d7b1dcdd3c39c9176073a1af1bed3cd11c954b --- /dev/null +++ b/db/migrate/20190403141604_add_comment_to_invites.rb @@ -0,0 +1,5 @@ +class AddCommentToInvites < ActiveRecord::Migration[5.2] + def change + add_column :invites, :comment, :text + end +end diff --git a/db/migrate/20190627222225_create_custom_emoji_categories.rb b/db/migrate/20190627222225_create_custom_emoji_categories.rb new file mode 100644 index 0000000000000000000000000000000000000000..4713793e660568c8301d042755a5dfd03ef62b40 --- /dev/null +++ b/db/migrate/20190627222225_create_custom_emoji_categories.rb @@ -0,0 +1,9 @@ +class CreateCustomEmojiCategories < ActiveRecord::Migration[5.2] + def change + create_table :custom_emoji_categories do |t| + t.string :name, index: { unique: true } + + t.timestamps + end + end +end diff --git a/db/migrate/20190627222826_add_category_id_to_custom_emojis.rb b/db/migrate/20190627222826_add_category_id_to_custom_emojis.rb new file mode 100644 index 0000000000000000000000000000000000000000..873b4d05fe10036abb7e9e0c3508fd59f8bf4fdf --- /dev/null +++ b/db/migrate/20190627222826_add_category_id_to_custom_emojis.rb @@ -0,0 +1,5 @@ +class AddCategoryIdToCustomEmojis < ActiveRecord::Migration[5.2] + def change + add_column :custom_emojis, :category_id, :bigint + end +end diff --git a/db/migrate/20190701022101_add_trust_level_to_accounts.rb b/db/migrate/20190701022101_add_trust_level_to_accounts.rb new file mode 100644 index 0000000000000000000000000000000000000000..917486d2ed7eb3e72f9f90db129054029bac142f --- /dev/null +++ b/db/migrate/20190701022101_add_trust_level_to_accounts.rb @@ -0,0 +1,5 @@ +class AddTrustLevelToAccounts < ActiveRecord::Migration[5.2] + def change + add_column :accounts, :trust_level, :integer + end +end diff --git a/db/migrate/20190705002136_create_domain_allows.rb b/db/migrate/20190705002136_create_domain_allows.rb new file mode 100644 index 0000000000000000000000000000000000000000..83b0728d9d5571baf8193eea66885ef46af4f9da --- /dev/null +++ b/db/migrate/20190705002136_create_domain_allows.rb @@ -0,0 +1,9 @@ +class CreateDomainAllows < ActiveRecord::Migration[5.2] + def change + create_table :domain_allows do |t| + t.string :domain, default: '', null: false, index: { unique: true } + + t.timestamps + end + end +end diff --git a/db/migrate/20190715164535_add_instance_actor.rb b/db/migrate/20190715164535_add_instance_actor.rb new file mode 100644 index 0000000000000000000000000000000000000000..a26d54949320aa7aead2492b9a6a10fad4597eb3 --- /dev/null +++ b/db/migrate/20190715164535_add_instance_actor.rb @@ -0,0 +1,9 @@ +class AddInstanceActor < ActiveRecord::Migration[5.2] + def up + Account.create!(id: -99, actor_type: 'Application', locked: true, username: Rails.configuration.x.local_domain) + end + + def down + Account.find_by(id: -99, actor_type: 'Application').destroy! + end +end diff --git a/db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb b/db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..057fc86baa8fd25b399cbdbc1ed1229f6dfd0848 --- /dev/null +++ b/db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb @@ -0,0 +1,28 @@ +class AddCaseInsensitiveIndexToTags < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + Tag.connection.select_all('SELECT string_agg(id::text, \',\') AS ids FROM tags GROUP BY lower(name) HAVING count(*) > 1').to_hash.each do |row| + canonical_tag_id = row['ids'].split(',').first + redundant_tag_ids = row['ids'].split(',')[1..-1] + + safety_assured do + execute "UPDATE accounts_tags AS t0 SET tag_id = #{canonical_tag_id} WHERE tag_id IN (#{redundant_tag_ids.join(', ')}) AND NOT EXISTS (SELECT t1.tag_id FROM accounts_tags AS t1 WHERE t1.tag_id = #{canonical_tag_id} AND t1.account_id = t0.account_id)" + execute "UPDATE statuses_tags AS t0 SET tag_id = #{canonical_tag_id} WHERE tag_id IN (#{redundant_tag_ids.join(', ')}) AND NOT EXISTS (SELECT t1.tag_id FROM statuses_tags AS t1 WHERE t1.tag_id = #{canonical_tag_id} AND t1.status_id = t0.status_id)" + execute "UPDATE featured_tags AS t0 SET tag_id = #{canonical_tag_id} WHERE tag_id IN (#{redundant_tag_ids.join(', ')}) AND NOT EXISTS (SELECT t1.tag_id FROM featured_tags AS t1 WHERE t1.tag_id = #{canonical_tag_id} AND t1.account_id = t0.account_id)" + end + + Tag.where(id: redundant_tag_ids).in_batches.delete_all + end + + safety_assured { execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower ON tags (lower(name))' } + remove_index :tags, name: 'index_tags_on_name' + remove_index :tags, name: 'hashtag_search_index' + end + + def down + add_index :tags, :name, unique: true, algorithm: :concurrently + safety_assured { execute 'CREATE INDEX CONCURRENTLY hashtag_search_index ON tags (name text_pattern_ops)' } + remove_index :tags, name: 'index_tags_on_name_lower' + end +end diff --git a/db/migrate/20190729185330_add_score_to_tags.rb b/db/migrate/20190729185330_add_score_to_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..75fee4b57052c1dc14248532dedf1164acfc35d5 --- /dev/null +++ b/db/migrate/20190729185330_add_score_to_tags.rb @@ -0,0 +1,5 @@ +class AddScoreToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :score, :int + end +end diff --git a/db/migrate/20190805123746_add_capabilities_to_tags.rb b/db/migrate/20190805123746_add_capabilities_to_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..43c7763b1bf0ac1e44de9f20c783a2ca7d11dd39 --- /dev/null +++ b/db/migrate/20190805123746_add_capabilities_to_tags.rb @@ -0,0 +1,9 @@ +class AddCapabilitiesToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :usable, :boolean + add_column :tags, :trendable, :boolean + add_column :tags, :listable, :boolean + add_column :tags, :reviewed_at, :datetime + add_column :tags, :requested_review_at, :datetime + end +end diff --git a/db/migrate/20190807135426_add_comments_to_domain_blocks.rb b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb new file mode 100644 index 0000000000000000000000000000000000000000..b660a71adb92d33b7a28342fd77b9d1a9ae1659d --- /dev/null +++ b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb @@ -0,0 +1,7 @@ +class AddCommentsToDomainBlocks < ActiveRecord::Migration[5.2] + def change + add_column :domain_blocks, :private_comment, :text + add_column :domain_blocks, :public_comment, :text + end +end + diff --git a/db/migrate/20190815225426_add_last_status_at_to_tags.rb b/db/migrate/20190815225426_add_last_status_at_to_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..d83537c47f9e8282f72b54c8294a3d36009577a6 --- /dev/null +++ b/db/migrate/20190815225426_add_last_status_at_to_tags.rb @@ -0,0 +1,6 @@ +class AddLastStatusAtToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :last_status_at, :datetime + add_column :tags, :last_trend_at, :datetime + end +end diff --git a/db/migrate/20190819134503_add_deleted_at_to_statuses.rb b/db/migrate/20190819134503_add_deleted_at_to_statuses.rb new file mode 100644 index 0000000000000000000000000000000000000000..5af109097e8b10afa8afe9e2658bc9bfd373c01f --- /dev/null +++ b/db/migrate/20190819134503_add_deleted_at_to_statuses.rb @@ -0,0 +1,5 @@ +class AddDeletedAtToStatuses < ActiveRecord::Migration[5.2] + def change + add_column :statuses, :deleted_at, :datetime + end +end diff --git a/db/migrate/20190820003045_update_statuses_index.rb b/db/migrate/20190820003045_update_statuses_index.rb new file mode 100644 index 0000000000000000000000000000000000000000..5c2ea1f6a2416461cb0d4f787665ccc88b5c254d --- /dev/null +++ b/db/migrate/20190820003045_update_statuses_index.rb @@ -0,0 +1,13 @@ +class UpdateStatusesIndex < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + safety_assured { add_index :statuses, [:account_id, :id, :visibility, :updated_at], where: 'deleted_at IS NULL', order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20190820 } + remove_index :statuses, name: :index_statuses_20180106 + end + + def down + safety_assured { add_index :statuses, [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106 } + remove_index :statuses, name: :index_statuses_20190820 + end +end diff --git a/db/migrate/20190823221802_add_local_index_to_statuses.rb b/db/migrate/20190823221802_add_local_index_to_statuses.rb new file mode 100644 index 0000000000000000000000000000000000000000..deca25c3515ef17c1853ebc5533ec310c2f8bd6c --- /dev/null +++ b/db/migrate/20190823221802_add_local_index_to_statuses.rb @@ -0,0 +1,11 @@ +class AddLocalIndexToStatuses < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + add_index :statuses, [:id, :account_id], name: :index_statuses_local_20190824, algorithm: :concurrently, order: { id: :desc }, where: '(local OR (uri IS NULL)) AND deleted_at IS NULL AND visibility = 0 AND reblog_of_id IS NULL AND ((NOT reply) OR (in_reply_to_account_id = account_id))' + end + + def down + remove_index :statuses, name: :index_statuses_local_20190824 + end +end diff --git a/db/migrate/20190901035623_add_max_score_to_tags.rb b/db/migrate/20190901035623_add_max_score_to_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..f936e987182f26a0b3e00bda3e4d49fa8f8d3ca3 --- /dev/null +++ b/db/migrate/20190901035623_add_max_score_to_tags.rb @@ -0,0 +1,6 @@ +class AddMaxScoreToTags < ActiveRecord::Migration[5.2] + def change + add_column :tags, :max_score, :float + add_column :tags, :max_score_at, :datetime + end +end diff --git a/db/migrate/20190904222339_create_markers.rb b/db/migrate/20190904222339_create_markers.rb new file mode 100644 index 0000000000000000000000000000000000000000..71ca70ac3d07af5c8b3d3dc967df2f4d5b668209 --- /dev/null +++ b/db/migrate/20190904222339_create_markers.rb @@ -0,0 +1,14 @@ +class CreateMarkers < ActiveRecord::Migration[5.2] + def change + create_table :markers do |t| + t.references :user, foreign_key: { on_delete: :cascade, index: false } + t.string :timeline, default: '', null: false + t.bigint :last_read_id, default: 0, null: false + t.integer :lock_version, default: 0, null: false + + t.timestamps + end + + add_index :markers, [:user_id, :timeline], unique: true + end +end diff --git a/db/migrate/20190914202517_create_account_migrations.rb b/db/migrate/20190914202517_create_account_migrations.rb new file mode 100644 index 0000000000000000000000000000000000000000..cb9d71c0964302f8c5cc63b02c11fad2da00bef4 --- /dev/null +++ b/db/migrate/20190914202517_create_account_migrations.rb @@ -0,0 +1,12 @@ +class CreateAccountMigrations < ActiveRecord::Migration[5.2] + def change + create_table :account_migrations do |t| + t.belongs_to :account, foreign_key: { on_delete: :cascade } + t.string :acct, null: false, default: '' + t.bigint :followers_count, null: false, default: 0 + t.belongs_to :target_account, foreign_key: { to_table: :accounts, on_delete: :nullify } + + t.timestamps + end + end +end diff --git a/db/migrate/20190915194355_create_account_aliases.rb b/db/migrate/20190915194355_create_account_aliases.rb new file mode 100644 index 0000000000000000000000000000000000000000..32ce031d91884c58c3165cfd86c663ec9fdd1120 --- /dev/null +++ b/db/migrate/20190915194355_create_account_aliases.rb @@ -0,0 +1,11 @@ +class CreateAccountAliases < ActiveRecord::Migration[5.2] + def change + create_table :account_aliases do |t| + t.belongs_to :account, foreign_key: { on_delete: :cascade } + t.string :acct, null: false, default: '' + t.string :uri, null: false, default: '' + + t.timestamps + end + end +end diff --git a/db/migrate/20190917213523_add_remember_token_index.rb b/db/migrate/20190917213523_add_remember_token_index.rb new file mode 100644 index 0000000000000000000000000000000000000000..c5b41ce644651cfe27a9ab3a3f9f0021da1063e3 --- /dev/null +++ b/db/migrate/20190917213523_add_remember_token_index.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddRememberTokenIndex < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def change + add_index :users, :remember_token, algorithm: :concurrently, unique: true + end +end diff --git a/db/migrate/20190927232842_add_voters_count_to_polls.rb b/db/migrate/20190927232842_add_voters_count_to_polls.rb new file mode 100644 index 0000000000000000000000000000000000000000..84638570090e28302133f2827c89d8039e966424 --- /dev/null +++ b/db/migrate/20190927232842_add_voters_count_to_polls.rb @@ -0,0 +1,5 @@ +class AddVotersCountToPolls < ActiveRecord::Migration[5.2] + def change + add_column :polls, :voters_count, :bigint + end +end diff --git a/db/migrate/20191001213028_add_lock_version_to_account_stats.rb b/db/migrate/20191001213028_add_lock_version_to_account_stats.rb new file mode 100644 index 0000000000000000000000000000000000000000..47f37cca29d8042e0f3700cca350f091d1c6733f --- /dev/null +++ b/db/migrate/20191001213028_add_lock_version_to_account_stats.rb @@ -0,0 +1,15 @@ +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddLockVersionToAccountStats < ActiveRecord::Migration[5.2] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def up + safety_assured { add_column_with_default :account_stats, :lock_version, :integer, allow_null: false, default: 0 } + end + + def down + remove_column :account_stats, :lock_version + end +end diff --git a/db/migrate/20191007013357_update_pt_locales.rb b/db/migrate/20191007013357_update_pt_locales.rb new file mode 100644 index 0000000000000000000000000000000000000000..b7288d38a0d7c88b9ef01e06fc9a188d05472718 --- /dev/null +++ b/db/migrate/20191007013357_update_pt_locales.rb @@ -0,0 +1,11 @@ +class UpdatePtLocales < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + User.where(locale: 'pt').in_batches.update_all(locale: 'pt-PT') + end + + def down + User.where(locale: 'pt-PT').in_batches.update_all(locale: 'pt') + end +end diff --git a/db/post_migrate/20190706233204_drop_stream_entries.rb b/db/post_migrate/20190706233204_drop_stream_entries.rb new file mode 100644 index 0000000000000000000000000000000000000000..1fecece0551828fbff60adfea7b64226a9def429 --- /dev/null +++ b/db/post_migrate/20190706233204_drop_stream_entries.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class DropStreamEntries < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + drop_table :stream_entries + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/post_migrate/20190715031050_drop_subscriptions.rb b/db/post_migrate/20190715031050_drop_subscriptions.rb new file mode 100644 index 0000000000000000000000000000000000000000..3719afe4a0cbb9977041028d5c8bcc22878c04b1 --- /dev/null +++ b/db/post_migrate/20190715031050_drop_subscriptions.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class DropSubscriptions < ActiveRecord::Migration[5.2] + def up + drop_table :subscriptions + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/post_migrate/20190901040524_remove_score_from_tags.rb b/db/post_migrate/20190901040524_remove_score_from_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..a1112700b5d51e771190efa87b143899e65e5719 --- /dev/null +++ b/db/post_migrate/20190901040524_remove_score_from_tags.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class RemoveScoreFromTags < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def change + safety_assured do + remove_column :tags, :score, :int + remove_column :tags, :last_trend_at, :datetime + end + end +end diff --git a/db/post_migrate/20190927124642_remove_invalid_web_push_subscription.rb b/db/post_migrate/20190927124642_remove_invalid_web_push_subscription.rb new file mode 100644 index 0000000000000000000000000000000000000000..c2397476ad0749408d742605d4b53d1184f38f0e --- /dev/null +++ b/db/post_migrate/20190927124642_remove_invalid_web_push_subscription.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class RemoveInvalidWebPushSubscription < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + invalid_web_push_subscriptions = Web::PushSubscription.where(endpoint: '') + .or(Web::PushSubscription.where(key_p256dh: '')) + .or(Web::PushSubscription.where(key_auth: '')) + .preload(:session_activation) + invalid_web_push_subscriptions.find_each do |web_push_subscription| + web_push_subscription.session_activation&.update!(web_push_subscription_id: nil) + web_push_subscription.destroy! + end + end + + def down; end +end diff --git a/db/schema.rb b/db/schema.rb index f5a0448114d1da7d3d616b9e46587d0dbf8f7e6e..cd1f26281c5134d3ff7b19f2336645ed4d9c09e0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,20 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_05_29_143559) do +ActiveRecord::Schema.define(version: 2019_10_07_013357) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "account_aliases", force: :cascade do |t| + t.bigint "account_id" + t.string "acct", default: "", null: false + t.string "uri", default: "", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_account_aliases_on_account_id" + end + create_table "account_conversations", force: :cascade do |t| t.bigint "account_id" t.bigint "conversation_id" @@ -49,6 +58,17 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["account_id"], name: "index_account_identity_proofs_on_account_id" end + create_table "account_migrations", force: :cascade do |t| + t.bigint "account_id" + t.string "acct", default: "", null: false + t.bigint "followers_count", default: 0, null: false + t.bigint "target_account_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_account_migrations_on_account_id" + t.index ["target_account_id"], name: "index_account_migrations_on_target_account_id" + end + create_table "account_moderation_notes", force: :cascade do |t| t.text "content", null: false t.bigint "account_id", null: false @@ -77,6 +97,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.datetime "last_status_at" + t.integer "lock_version", default: 0, null: false t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true end @@ -148,6 +169,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.string "also_known_as", array: true t.datetime "silenced_at" t.datetime "suspended_at" + t.integer "trust_level" t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index ["moved_to_account_id"], name: "index_accounts_on_moved_to_account_id" @@ -208,6 +230,13 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["uri"], name: "index_conversations_on_uri", unique: true end + create_table "custom_emoji_categories", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true + end + create_table "custom_emojis", force: :cascade do |t| t.string "shortcode", default: "", null: false t.string "domain" @@ -221,6 +250,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.string "uri" t.string "image_remote_url" t.boolean "visible_in_picker", default: true, null: false + t.bigint "category_id" t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true end @@ -236,6 +266,13 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["account_id"], name: "index_custom_filters_on_account_id" end + create_table "domain_allows", force: :cascade do |t| + t.string "domain", default: "", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["domain"], name: "index_domain_allows_on_domain", unique: true + end + create_table "domain_blocks", force: :cascade do |t| t.string "domain", default: "", null: false t.datetime "created_at", null: false @@ -243,6 +280,8 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.integer "severity", default: 0 t.boolean "reject_media", default: false, null: false t.boolean "reject_reports", default: false, null: false + t.text "private_comment" + t.text "public_comment" t.index ["domain"], name: "index_domain_blocks_on_domain", unique: true end @@ -326,6 +365,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "autofollow", default: false, null: false + t.text "comment" t.index ["code"], name: "index_invites_on_code", unique: true t.index ["user_id"], name: "index_invites_on_user_id" end @@ -347,6 +387,17 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["account_id"], name: "index_lists_on_account_id" end + create_table "markers", force: :cascade do |t| + t.bigint "user_id" + t.string "timeline", default: "", null: false + t.bigint "last_read_id", default: 0, null: false + t.integer "lock_version", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id", "timeline"], name: "index_markers_on_user_id_and_timeline", unique: true + t.index ["user_id"], name: "index_markers_on_user_id" + end + create_table "media_attachments", force: :cascade do |t| t.bigint "status_id" t.string "file_file_name" @@ -479,6 +530,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "lock_version", default: 0, null: false + t.bigint "voters_count" t.index ["account_id"], name: "index_polls_on_account_id" t.index ["status_id"], name: "index_polls_on_status_id" end @@ -625,8 +677,10 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.bigint "application_id" t.bigint "in_reply_to_account_id" t.bigint "poll_id" + t.datetime "deleted_at" t.boolean "local_only" - t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20180106", order: { id: :desc } + t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20190820", order: { id: :desc }, where: "(deleted_at IS NULL)" + t.index ["id", "account_id"], name: "index_statuses_local_20190824", order: { id: :desc }, where: "((local OR (uri IS NULL)) AND (deleted_at IS NULL) AND (visibility = 0) AND (reblog_of_id IS NULL) AND ((NOT reply) OR (in_reply_to_account_id = account_id)))" t.index ["in_reply_to_account_id"], name: "index_statuses_on_in_reply_to_account_id" t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id" t.index ["reblog_of_id", "account_id"], name: "index_statuses_on_reblog_of_id_and_account_id" @@ -668,8 +722,15 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.string "name", default: "", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index "lower((name)::text) text_pattern_ops", name: "hashtag_search_index" - t.index ["name"], name: "index_tags_on_name", unique: true + t.boolean "usable" + t.boolean "trendable" + t.boolean "listable" + t.datetime "reviewed_at" + t.datetime "requested_review_at" + t.datetime "last_status_at" + t.float "max_score" + t.datetime "max_score_at" + t.index "lower((name)::text)", name: "index_tags_on_name_lower", unique: true end create_table "tombstones", force: :cascade do |t| @@ -729,6 +790,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id" t.index ["email"], name: "index_users_on_email", unique: true + t.index ["remember_token"], name: "index_users_on_remember_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end @@ -753,10 +815,13 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do t.index ["user_id"], name: "index_web_settings_on_user_id", unique: true end + add_foreign_key "account_aliases", "accounts", on_delete: :cascade add_foreign_key "account_conversations", "accounts", on_delete: :cascade add_foreign_key "account_conversations", "conversations", on_delete: :cascade add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade add_foreign_key "account_identity_proofs", "accounts", on_delete: :cascade + add_foreign_key "account_migrations", "accounts", column: "target_account_id", on_delete: :nullify + add_foreign_key "account_migrations", "accounts", on_delete: :cascade add_foreign_key "account_moderation_notes", "accounts" add_foreign_key "account_moderation_notes", "accounts", column: "target_account_id" add_foreign_key "account_pins", "accounts", column: "target_account_id", on_delete: :cascade @@ -788,6 +853,7 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do add_foreign_key "list_accounts", "follows", on_delete: :cascade add_foreign_key "list_accounts", "lists", on_delete: :cascade add_foreign_key "lists", "accounts", on_delete: :cascade + add_foreign_key "markers", "users", on_delete: :cascade add_foreign_key "media_attachments", "accounts", name: "fk_96dd81e81b", on_delete: :nullify add_foreign_key "media_attachments", "scheduled_statuses", on_delete: :nullify add_foreign_key "media_attachments", "statuses", on_delete: :nullify @@ -824,8 +890,6 @@ ActiveRecord::Schema.define(version: 2019_05_29_143559) do add_foreign_key "statuses", "statuses", column: "reblog_of_id", on_delete: :cascade add_foreign_key "statuses_tags", "statuses", on_delete: :cascade add_foreign_key "statuses_tags", "tags", name: "fk_3081861e21", on_delete: :cascade - add_foreign_key "stream_entries", "accounts", name: "fk_5659b17554", on_delete: :cascade - add_foreign_key "subscriptions", "accounts", name: "fk_9847d1cbb5", on_delete: :cascade add_foreign_key "tombstones", "accounts", on_delete: :cascade add_foreign_key "user_invite_requests", "users", on_delete: :cascade add_foreign_key "users", "accounts", name: "fk_50500f500d", on_delete: :cascade diff --git a/db/seeds.rb b/db/seeds.rb index 9a6e9dd78ed1edf0e061ff29b91670181eb469ab..0bfb5d0db5a709b1e432a86600ac1d6133844d9d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,10 @@ -Doorkeeper::Application.create!(name: 'Web', superapp: true, redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow') +Doorkeeper::Application.create!(name: 'Web', superapp: true, redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow push') + +domain = ENV['LOCAL_DOMAIN'] || Rails.configuration.x.local_domain +account = Account.find_or_initialize_by(id: -99, actor_type: 'Application', locked: true, username: domain) +account.save! if Rails.env.development? - domain = ENV['LOCAL_DOMAIN'] || Rails.configuration.x.local_domain admin = Account.where(username: 'admin').first_or_initialize(username: 'admin') admin.save(validate: false) User.where(email: "admin@#{domain}").first_or_initialize(email: "admin@#{domain}", password: 'mastodonadmin', password_confirmation: 'mastodonadmin', confirmed_at: Time.now.utc, admin: true, account: admin, agreement: true, approved: true).save! diff --git a/dist/nginx.conf b/dist/nginx.conf index 7c429bad42e5b25127db73d487968dd66e4e62e8..b6591e8976d13c7f3fdc11fa94ce7e0356b579b5 100644 --- a/dist/nginx.conf +++ b/dist/nginx.conf @@ -19,7 +19,7 @@ server { listen [::]:443 ssl http2; server_name example.com; - ssl_protocols TLSv1.2; + ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; diff --git a/docker-compose.yml b/docker-compose.yml index 7406849663e61280d44fcbfda16176692eaaee65..20649e4245c06749c836d7cdef36baaecfa424d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: - external_network - internal_network healthcheck: - test: ["CMD-SHELL", "wget -q --spider --header 'x-forwarded-proto: https' --proxy=off localhost:3000/api/v1/instance || exit 1"] + test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"] ports: - "127.0.0.1:3000:3000" depends_on: @@ -63,7 +63,7 @@ services: - external_network - internal_network healthcheck: - test: ["CMD-SHELL", "wget -q --spider --header 'x-forwarded-proto: https' --proxy=off localhost:4000/api/v1/streaming/health || exit 1"] + test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"] ports: - "127.0.0.1:4000:4000" depends_on: diff --git a/lib/chewy/strategy/custom_sidekiq.rb b/lib/chewy/strategy/custom_sidekiq.rb new file mode 100644 index 0000000000000000000000000000000000000000..3e54326ba8b31b01c76059f7495f0aa54f0b33db --- /dev/null +++ b/lib/chewy/strategy/custom_sidekiq.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Chewy + class Strategy + class CustomSidekiq < Base + class Worker + include ::Sidekiq::Worker + + sidekiq_options queue: 'pull' + + def perform(type, ids, options = {}) + options[:refresh] = !Chewy.disable_refresh_async if Chewy.disable_refresh_async + type.constantize.import!(ids, options) + end + end + + def update(type, objects, _options = {}) + return unless Chewy.enabled? + + ids = type.root.id ? Array.wrap(objects) : type.adapter.identify(objects) + + return if ids.empty? + + Worker.perform_async(type.name, ids) + end + + def leave; end + end + end +end diff --git a/lib/cli.rb b/lib/cli.rb index be276583d22568659eaa77aabfaf313875f1867b..fbdf49fc34a6766640382a7c10521337cb229fbd 100644 --- a/lib/cli.rb +++ b/lib/cli.rb @@ -9,6 +9,7 @@ require_relative 'mastodon/search_cli' require_relative 'mastodon/settings_cli' require_relative 'mastodon/statuses_cli' require_relative 'mastodon/domains_cli' +require_relative 'mastodon/preview_cards_cli' require_relative 'mastodon/cache_cli' require_relative 'mastodon/version' @@ -42,6 +43,9 @@ module Mastodon desc 'domains SUBCOMMAND ...ARGS', 'Manage account domains' subcommand 'domains', Mastodon::DomainsCLI + desc 'preview_cards SUBCOMMAND ...ARGS', 'Manage preview cards' + subcommand 'preview_cards', Mastodon::PreviewCardsCLI + desc 'cache SUBCOMMAND ...ARGS', 'Manage cache' subcommand 'cache', Mastodon::CacheCLI diff --git a/lib/devise/ldap_authenticatable.rb b/lib/devise/ldap_authenticatable.rb deleted file mode 100644 index 6903d468dc4958a8d2cf504f626e3f3871591be4..0000000000000000000000000000000000000000 --- a/lib/devise/ldap_authenticatable.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require 'net/ldap' -require 'devise/strategies/authenticatable' - -module Devise - module Strategies - class LdapAuthenticatable < Authenticatable - def authenticate! - if params[:user] - ldap = Net::LDAP.new( - host: Devise.ldap_host, - port: Devise.ldap_port, - base: Devise.ldap_base, - encryption: { - method: Devise.ldap_method, - tls_options: tls_options, - }, - auth: { - method: :simple, - username: Devise.ldap_bind_dn, - password: Devise.ldap_password, - }, - connect_timeout: 10 - ) - - filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, email: email) - - if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: filter, password: password)) - user = User.ldap_get_user(user_info.first) - success!(user) - else - return fail(:invalid) - end - end - end - - def email - params[:user][:email] - end - - def password - params[:user][:password] - end - - def tls_options - OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.tap do |options| - options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Devise.ldap_tls_no_verify - end - end - end - end -end - -Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable) diff --git a/lib/devise/two_factor_ldap_authenticatable.rb b/lib/devise/two_factor_ldap_authenticatable.rb new file mode 100644 index 0000000000000000000000000000000000000000..065aa2de88acab28682cfab4680d0239b994aa04 --- /dev/null +++ b/lib/devise/two_factor_ldap_authenticatable.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'net/ldap' +require 'devise/strategies/base' + +module Devise + module Strategies + class TwoFactorLdapAuthenticatable < Base + def valid? + valid_params? && mapping.to.respond_to?(:authenticate_with_ldap) + end + + def authenticate! + resource = mapping.to.authenticate_with_ldap(params[scope]) + + if resource && !resource.otp_required_for_login? + success!(resource) + else + fail(:invalid) + end + end + + protected + + def valid_params? + params[scope] && params[scope][:password].present? + end + end + end +end + +Warden::Strategies.add(:two_factor_ldap_authenticatable, Devise::Strategies::TwoFactorLdapAuthenticatable) diff --git a/lib/devise/two_factor_pam_authenticatable.rb b/lib/devise/two_factor_pam_authenticatable.rb new file mode 100644 index 0000000000000000000000000000000000000000..5ce723b331d319217ad02a34250ee294f9ce39e4 --- /dev/null +++ b/lib/devise/two_factor_pam_authenticatable.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'devise/strategies/base' + +module Devise + module Strategies + class TwoFactorPamAuthenticatable < Base + def valid? + valid_params? && mapping.to.respond_to?(:authenticate_with_pam) + end + + def authenticate! + resource = mapping.to.authenticate_with_pam(params[scope]) + + if resource && !resource.otp_required_for_login? + success!(resource) + else + fail(:invalid) + end + end + + protected + + def valid_params? + params[scope] && params[scope][:password].present? + end + end + end +end + +Warden::Strategies.add(:two_factor_pam_authenticatable, Devise::Strategies::TwoFactorPamAuthenticatable) diff --git a/lib/json_ld/identity.rb b/lib/json_ld/identity.rb new file mode 100644 index 0000000000000000000000000000000000000000..4fb3f8e9d9c20114665d57f8f7213ef4faf0ac26 --- /dev/null +++ b/lib/json_ld/identity.rb @@ -0,0 +1,87 @@ +# -*- encoding: utf-8 -*- +# frozen_string_literal: true +# This file generated automatically from http://w3id.org/identity/v1 +require 'json/ld' +class JSON::LD::Context + add_preloaded("http://w3id.org/identity/v1") do + new(term_definitions: { + "Credential" => TermDefinition.new("Credential", id: "https://w3id.org/credentials#Credential", simple: true), + "CryptographicKey" => TermDefinition.new("CryptographicKey", id: "https://w3id.org/security#Key", simple: true), + "CryptographicKeyCredential" => TermDefinition.new("CryptographicKeyCredential", id: "https://w3id.org/credentials#CryptographicKeyCredential", simple: true), + "EncryptedMessage" => TermDefinition.new("EncryptedMessage", id: "https://w3id.org/security#EncryptedMessage", simple: true), + "GraphSignature2012" => TermDefinition.new("GraphSignature2012", id: "https://w3id.org/security#GraphSignature2012", simple: true), + "Group" => TermDefinition.new("Group", id: "https://www.w3.org/ns/activitystreams#Group", simple: true), + "Identity" => TermDefinition.new("Identity", id: "https://w3id.org/identity#Identity", simple: true), + "LinkedDataSignature2015" => TermDefinition.new("LinkedDataSignature2015", id: "https://w3id.org/security#LinkedDataSignature2015", simple: true), + "Organization" => TermDefinition.new("Organization", id: "http://schema.org/Organization", simple: true), + "Person" => TermDefinition.new("Person", id: "http://schema.org/Person", simple: true), + "PostalAddress" => TermDefinition.new("PostalAddress", id: "http://schema.org/PostalAddress", simple: true), + "about" => TermDefinition.new("about", id: "http://schema.org/about", type_mapping: "@id"), + "accessControl" => TermDefinition.new("accessControl", id: "https://w3id.org/permissions#accessControl", type_mapping: "@id"), + "address" => TermDefinition.new("address", id: "http://schema.org/address", type_mapping: "@id"), + "addressCountry" => TermDefinition.new("addressCountry", id: "http://schema.org/addressCountry", simple: true), + "addressLocality" => TermDefinition.new("addressLocality", id: "http://schema.org/addressLocality", simple: true), + "addressRegion" => TermDefinition.new("addressRegion", id: "http://schema.org/addressRegion", simple: true), + "cipherAlgorithm" => TermDefinition.new("cipherAlgorithm", id: "https://w3id.org/security#cipherAlgorithm", simple: true), + "cipherData" => TermDefinition.new("cipherData", id: "https://w3id.org/security#cipherData", simple: true), + "cipherKey" => TermDefinition.new("cipherKey", id: "https://w3id.org/security#cipherKey", simple: true), + "claim" => TermDefinition.new("claim", id: "https://w3id.org/credentials#claim", type_mapping: "@id"), + "comment" => TermDefinition.new("comment", id: "http://www.w3.org/2000/01/rdf-schema#comment", simple: true), + "created" => TermDefinition.new("created", id: "http://purl.org/dc/terms/created", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"), + "creator" => TermDefinition.new("creator", id: "http://purl.org/dc/terms/creator", type_mapping: "@id"), + "cred" => TermDefinition.new("cred", id: "https://w3id.org/credentials#", simple: true, prefix: true), + "credential" => TermDefinition.new("credential", id: "https://w3id.org/credentials#credential", type_mapping: "@id"), + "dc" => TermDefinition.new("dc", id: "http://purl.org/dc/terms/", simple: true, prefix: true), + "description" => TermDefinition.new("description", id: "http://schema.org/description", simple: true), + "digestAlgorithm" => TermDefinition.new("digestAlgorithm", id: "https://w3id.org/security#digestAlgorithm", simple: true), + "digestValue" => TermDefinition.new("digestValue", id: "https://w3id.org/security#digestValue", simple: true), + "domain" => TermDefinition.new("domain", id: "https://w3id.org/security#domain", simple: true), + "email" => TermDefinition.new("email", id: "http://schema.org/email", simple: true), + "expires" => TermDefinition.new("expires", id: "https://w3id.org/security#expiration", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"), + "familyName" => TermDefinition.new("familyName", id: "http://schema.org/familyName", simple: true), + "givenName" => TermDefinition.new("givenName", id: "http://schema.org/givenName", simple: true), + "id" => TermDefinition.new("id", id: "@id", simple: true), + "identity" => TermDefinition.new("identity", id: "https://w3id.org/identity#", simple: true, prefix: true), + "identityService" => TermDefinition.new("identityService", id: "https://w3id.org/identity#identityService", type_mapping: "@id"), + "idp" => TermDefinition.new("idp", id: "https://w3id.org/identity#idp", type_mapping: "@id"), + "image" => TermDefinition.new("image", id: "http://schema.org/image", type_mapping: "@id"), + "initializationVector" => TermDefinition.new("initializationVector", id: "https://w3id.org/security#initializationVector", simple: true), + "issued" => TermDefinition.new("issued", id: "https://w3id.org/credentials#issued", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"), + "issuer" => TermDefinition.new("issuer", id: "https://w3id.org/credentials#issuer", type_mapping: "@id"), + "label" => TermDefinition.new("label", id: "http://www.w3.org/2000/01/rdf-schema#label", simple: true), + "member" => TermDefinition.new("member", id: "http://schema.org/member", type_mapping: "@id"), + "memberOf" => TermDefinition.new("memberOf", id: "http://schema.org/memberOf", type_mapping: "@id"), + "name" => TermDefinition.new("name", id: "http://schema.org/name", simple: true), + "nonce" => TermDefinition.new("nonce", id: "https://w3id.org/security#nonce", simple: true), + "normalizationAlgorithm" => TermDefinition.new("normalizationAlgorithm", id: "https://w3id.org/security#normalizationAlgorithm", simple: true), + "owner" => TermDefinition.new("owner", id: "https://w3id.org/security#owner", type_mapping: "@id"), + "password" => TermDefinition.new("password", id: "https://w3id.org/security#password", simple: true), + "paymentProcessor" => TermDefinition.new("paymentProcessor", id: "https://w3id.org/payswarm#processor", simple: true), + "perm" => TermDefinition.new("perm", id: "https://w3id.org/permissions#", simple: true, prefix: true), + "postalCode" => TermDefinition.new("postalCode", id: "http://schema.org/postalCode", simple: true), + "preferences" => TermDefinition.new("preferences", id: "https://w3id.org/payswarm#preferences", type_mapping: "@vocab"), + "privateKey" => TermDefinition.new("privateKey", id: "https://w3id.org/security#privateKey", type_mapping: "@id"), + "privateKeyPem" => TermDefinition.new("privateKeyPem", id: "https://w3id.org/security#privateKeyPem", simple: true), + "ps" => TermDefinition.new("ps", id: "https://w3id.org/payswarm#", simple: true, prefix: true), + "publicKey" => TermDefinition.new("publicKey", id: "https://w3id.org/security#publicKey", type_mapping: "@id"), + "publicKeyPem" => TermDefinition.new("publicKeyPem", id: "https://w3id.org/security#publicKeyPem", simple: true), + "publicKeyService" => TermDefinition.new("publicKeyService", id: "https://w3id.org/security#publicKeyService", type_mapping: "@id"), + "rdf" => TermDefinition.new("rdf", id: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", simple: true, prefix: true), + "rdfs" => TermDefinition.new("rdfs", id: "http://www.w3.org/2000/01/rdf-schema#", simple: true, prefix: true), + "recipient" => TermDefinition.new("recipient", id: "https://w3id.org/credentials#recipient", type_mapping: "@id"), + "revoked" => TermDefinition.new("revoked", id: "https://w3id.org/security#revoked", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"), + "schema" => TermDefinition.new("schema", id: "http://schema.org/", simple: true, prefix: true), + "sec" => TermDefinition.new("sec", id: "https://w3id.org/security#", simple: true, prefix: true), + "signature" => TermDefinition.new("signature", id: "https://w3id.org/security#signature", simple: true), + "signatureAlgorithm" => TermDefinition.new("signatureAlgorithm", id: "https://w3id.org/security#signatureAlgorithm", simple: true), + "signatureValue" => TermDefinition.new("signatureValue", id: "https://w3id.org/security#signatureValue", simple: true), + "streetAddress" => TermDefinition.new("streetAddress", id: "http://schema.org/streetAddress", simple: true), + "title" => TermDefinition.new("title", id: "http://purl.org/dc/terms/title", simple: true), + "type" => TermDefinition.new("type", id: "@type", simple: true), + "url" => TermDefinition.new("url", id: "http://schema.org/url", type_mapping: "@id"), + "writePermission" => TermDefinition.new("writePermission", id: "https://w3id.org/permissions#writePermission", type_mapping: "@id"), + "xsd" => TermDefinition.new("xsd", id: "http://www.w3.org/2001/XMLSchema#", simple: true, prefix: true) + }) + end + alias_preloaded("https://w3id.org/identity/v1", "http://w3id.org/identity/v1") +end diff --git a/lib/json_ld/security.rb b/lib/json_ld/security.rb index 1230206f035d1e315fec6522ac06eab6877163a5..a6fbce95f584316fa7a95f24c084972549007e94 100644 --- a/lib/json_ld/security.rb +++ b/lib/json_ld/security.rb @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- # frozen_string_literal: true -# This file generated automatically from https://w3id.org/security/v1 +# This file generated automatically from http://w3id.org/security/v1 require 'json/ld' class JSON::LD::Context - add_preloaded("https://w3id.org/security/v1") do + add_preloaded("http://w3id.org/security/v1") do new(processingMode: "json-ld-1.0", term_definitions: { "CryptographicKey" => TermDefinition.new("CryptographicKey", id: "https://w3id.org/security#Key", simple: true), "EcdsaKoblitzSignature2016" => TermDefinition.new("EcdsaKoblitzSignature2016", id: "https://w3id.org/security#EcdsaKoblitzSignature2016", simple: true), @@ -47,4 +47,5 @@ class JSON::LD::Context "xsd" => TermDefinition.new("xsd", id: "http://www.w3.org/2001/XMLSchema#", simple: true, prefix: true) }) end + alias_preloaded("https://w3id.org/security/v1", "http://w3id.org/security/v1") end diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb index 7d02153131c89682fb062dfe239bdf8b2b661d73..6dbb7568993c1a817065cdbf8150718b552b3df7 100644 --- a/lib/mastodon/accounts_cli.rb +++ b/lib/mastodon/accounts_cli.rb @@ -7,6 +7,8 @@ require_relative 'cli_helper' module Mastodon class AccountsCLI < Thor + include CLIHelper + def self.exit_on_failure? true end @@ -26,18 +28,20 @@ module Mastodon if options[:all] processed = 0 delay = 0 + scope = Account.local.without_suspended + progress = create_progress_bar(scope.count) - Account.local.without_suspended.find_in_batches do |accounts| + scope.find_in_batches do |accounts| accounts.each do |account| rotate_keys_for_account(account, delay) + progress.increment processed += 1 - say('.', :green, false) end delay += 5.minutes end - say + progress.finish say("OK, rotated keys for #{processed} accounts", :green) elsif username.present? rotate_keys_for_account(Account.find_local(username)) @@ -181,7 +185,7 @@ module Mastodon end say("Deleting user with #{account.statuses_count} statuses, this might take a while...") - SuspendAccountService.new.call(account, including_user: true) + SuspendAccountService.new.call(account, reserve_email: false) say('OK', :green) end @@ -206,6 +210,7 @@ module Mastodon say('OK', :green) end + option :concurrency, type: :numeric, default: 5, aliases: [:c] option :dry_run, type: :boolean desc 'cull', 'Remove remote accounts that no longer exist' long_desc <<-LONG_DESC @@ -215,63 +220,45 @@ module Mastodon Accounts that have had confirmed activity within the last week are excluded from the checks. - - Domains that are unreachable are not checked. - - With the --dry-run option, no deletes will actually be carried - out. LONG_DESC def cull skip_threshold = 7.days.ago - culled = 0 - dry_run_culled = [] - skip_domains = Set.new dry_run = options[:dry_run] ? ' (DRY RUN)' : '' + skip_domains = Concurrent::Set.new - Account.remote.where(protocol: :activitypub).partitioned.find_each do |account| - next if account.updated_at >= skip_threshold || (account.last_webfingered_at.present? && account.last_webfingered_at >= skip_threshold) + processed, culled = parallelize_with_progress(Account.remote.where(protocol: :activitypub).partitioned) do |account| + next if account.updated_at >= skip_threshold || (account.last_webfingered_at.present? && account.last_webfingered_at >= skip_threshold) || skip_domains.include?(account.domain) code = 0 - unless skip_domains.include?(account.domain) - begin - code = Request.new(:head, account.uri).perform(&:code) - rescue HTTP::ConnectionError - skip_domains << account.domain - rescue StandardError - next - end + + begin + code = Request.new(:head, account.uri).perform(&:code) + rescue HTTP::ConnectionError + skip_domains << account.domain end if [404, 410].include?(code) - if options[:dry_run] - dry_run_culled << account.acct - else - SuspendAccountService.new.call(account, destroy: true) - end - culled += 1 - say('+', :green, false) + SuspendAccountService.new.call(account, reserve_username: false) unless options[:dry_run] + 1 else - account.touch # Touch account even during dry run to avoid getting the account into the window again - say('.', nil, false) + # Touch account even during dry run to avoid getting the account into the window again + account.touch end end - say - say("Removed #{culled} accounts. #{skip_domains.size} servers skipped#{dry_run}", skip_domains.empty? ? :green : :yellow) + say("Visited #{processed} accounts, removed #{culled}#{dry_run}", :green) unless skip_domains.empty? - say('The following servers were not available during the check:', :yellow) + say('The following domains were not available during the check:', :yellow) skip_domains.each { |domain| say(' ' + domain) } end - - unless dry_run_culled.empty? - say('The following accounts would have been deleted:', :green) - dry_run_culled.each { |account| say(' ' + account) } - end end option :all, type: :boolean option :domain + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] + option :dry_run, type: :boolean desc 'refresh [USERNAME]', 'Fetch remote user data and files' long_desc <<-LONG_DESC Fetch remote user data and files for one or multiple accounts. @@ -280,21 +267,23 @@ module Mastodon Through the --domain option, this can be narrowed down to a specific domain only. Otherwise, a single remote account must be specified with USERNAME. - - All processing is done in the background through Sidekiq. LONG_DESC def refresh(username = nil) + dry_run = options[:dry_run] ? ' (DRY RUN)' : '' + if options[:domain] || options[:all] - queued = 0 scope = Account.remote scope = scope.where(domain: options[:domain]) if options[:domain] - scope.select(:id).reorder(nil).find_in_batches do |accounts| - Maintenance::RedownloadAccountMediaWorker.push_bulk(accounts.map(&:id)) - queued += accounts.size + processed, = parallelize_with_progress(scope) do |account| + next if options[:dry_run] + + account.reset_avatar! + account.reset_header! + account.save end - say("Scheduled refreshment of #{queued} accounts", :green, true) + say("Refreshed #{processed} accounts#{dry_run}", :green, true) elsif username.present? username, domain = username.split('@') account = Account.find_remote(username, domain) @@ -304,72 +293,53 @@ module Mastodon exit(1) end - Maintenance::RedownloadAccountMediaWorker.perform_async(account.id) - say('OK', :green) + unless options[:dry_run] + account.reset_avatar! + account.reset_header! + account.save + end + + say("OK#{dry_run}", :green) else say('No account(s) given', :red) exit(1) end end - desc 'follow ACCT', 'Make all local accounts follow account specified by ACCT' - long_desc <<-LONG_DESC - Make all local accounts follow an account specified by ACCT. ACCT can be - a simple username, in case of a local user. It can also be in the format - username@domain, in case of a remote user. - LONG_DESC - def follow(acct) - target_account = ResolveAccountService.new.call(acct) - processed = 0 - failed = 0 + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] + desc 'follow USERNAME', 'Make all local accounts follow account specified by USERNAME' + def follow(username) + target_account = Account.find_local(username) if target_account.nil? - say("Target account (#{acct}) could not be resolved", :red) + say('No such account', :red) exit(1) end - Account.local.without_suspended.find_each do |account| - begin - FollowService.new.call(account, target_account) - processed += 1 - say('.', :green, false) - rescue StandardError - failed += 1 - say('.', :red, false) - end + processed, = parallelize_with_progress(Account.local.without_suspended) do |account| + FollowService.new.call(account, target_account) end - say("OK, followed target from #{processed} accounts, skipped #{failed}", :green) + say("OK, followed target from #{processed} accounts", :green) end + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] desc 'unfollow ACCT', 'Make all local accounts unfollow account specified by ACCT' - long_desc <<-LONG_DESC - Make all local accounts unfollow an account specified by ACCT. ACCT can be - a simple username, in case of a local user. It can also be in the format - username@domain, in case of a remote user. - LONG_DESC def unfollow(acct) target_account = Account.find_remote(*acct.split('@')) - processed = 0 - failed = 0 if target_account.nil? - say("Target account (#{acct}) was not found", :red) + say('No such account', :red) exit(1) end - target_account.followers.local.find_each do |account| - begin - UnfollowService.new.call(account, target_account) - processed += 1 - say('.', :green, false) - rescue StandardError - failed += 1 - say('.', :red, false) - end + parallelize_with_progress(target_account.followers.local) do |account| + UnfollowService.new.call(account, target_account) end - say("OK, unfollowed target from #{processed} accounts, skipped #{failed}", :green) + say("OK, unfollowed target from #{processed} accounts", :green) end option :follows, type: :boolean, default: false @@ -392,51 +362,50 @@ module Mastodon account = Account.find_local(username) if account.nil? - say('No user with such username', :red) + say('No such account', :red) exit(1) end - if options[:follows] - processed = 0 - failed = 0 + total = 0 + total += Account.where(id: ::Follow.where(account: account).select(:target_account_id)).count if options[:follows] + total += Account.where(id: ::Follow.where(target_account: account).select(:account_id)).count if options[:followers] + progress = create_progress_bar(total) + processed = 0 - say("Unfollowing #{account.username}'s followees, this might take a while...") + if options[:follows] + scope = Account.where(id: ::Follow.where(account: account).select(:target_account_id)) - Account.where(id: ::Follow.where(account: account).select(:target_account_id)).find_each do |target_account| + scope.find_each do |target_account| begin UnfollowService.new.call(account, target_account) + rescue => e + progress.log pastel.red("Error processing #{target_account.id}: #{e}") + ensure + progress.increment processed += 1 - say('.', :green, false) - rescue StandardError - failed += 1 - say('.', :red, false) end end BootstrapTimelineWorker.perform_async(account.id) - - say("OK, unfollowed #{processed} followees, skipped #{failed}", :green) end if options[:followers] - processed = 0 - failed = 0 - - say("Removing #{account.username}'s followers, this might take a while...") + scope = Account.where(id: ::Follow.where(target_account: account).select(:account_id)) - Account.where(id: ::Follow.where(target_account: account).select(:account_id)).find_each do |target_account| + scope.find_each do |target_account| begin UnfollowService.new.call(target_account, account) + rescue => e + progress.log pastel.red("Error processing #{target_account.id}: #{e}") + ensure + progress.increment processed += 1 - say('.', :green, false) - rescue StandardError - failed += 1 - say('.', :red, false) end end - - say("OK, removed #{processed} followers, skipped #{failed}", :green) end + + progress.finish + say("Processed #{processed} relationships", :green, true) end option :number, type: :numeric, aliases: [:n] diff --git a/lib/mastodon/cache_cli.rb b/lib/mastodon/cache_cli.rb index e9b6667b3b610a5a0bbb4169a35a8c93e1698421..803404c34f2599be54e8f23b2364bffac69e2877 100644 --- a/lib/mastodon/cache_cli.rb +++ b/lib/mastodon/cache_cli.rb @@ -6,6 +6,8 @@ require_relative 'cli_helper' module Mastodon class CacheCLI < Thor + include CLIHelper + def self.exit_on_failure? true end @@ -15,5 +17,44 @@ module Mastodon Rails.cache.clear say('OK', :green) end + + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] + desc 'recount TYPE', 'Update hard-cached counters' + long_desc <<~LONG_DESC + Update hard-cached counters of TYPE by counting referenced + records from scratch. TYPE can be "accounts" or "statuses". + + It may take a very long time to finish, depending on the + size of the database. + LONG_DESC + def recount(type) + case type + when 'accounts' + processed, = parallelize_with_progress(Account.local.includes(:account_stat)) do |account| + account_stat = account.account_stat + account_stat.following_count = account.active_relationships.count + account_stat.followers_count = account.passive_relationships.count + account_stat.statuses_count = account.statuses.where.not(visibility: :direct).count + + account_stat.save if account_stat.changed? + end + when 'statuses' + processed, = parallelize_with_progress(Status.includes(:status_stat)) do |status| + status_stat = status.status_stat + status_stat.replies_count = status.replies.where.not(visibility: :direct).count + status_stat.reblogs_count = status.reblogs.count + status_stat.favourites_count = status.favourites.count + + status_stat.save if status_stat.changed? + end + else + say("Unknown type: #{type}", :red) + exit(1) + end + + say + say("OK, recounted #{processed} records", :green) + end end end diff --git a/lib/mastodon/cli_helper.rb b/lib/mastodon/cli_helper.rb index 2f807d08c84f94dbefc9f304dcff4dc8616cd6f8..ec4d9a81e5f2a0cc06ec578ac411d6aa557a807c 100644 --- a/lib/mastodon/cli_helper.rb +++ b/lib/mastodon/cli_helper.rb @@ -7,3 +7,66 @@ ActiveRecord::Base.logger = dev_null ActiveJob::Base.logger = dev_null HttpLog.configuration.logger = dev_null Paperclip.options[:log] = false + +module Mastodon + module CLIHelper + def create_progress_bar(total = nil) + ProgressBar.create(total: total, format: '%c/%u |%b%i| %e') + end + + def parallelize_with_progress(scope) + if options[:concurrency] < 1 + say('Cannot run with this concurrency setting, must be at least 1', :red) + exit(1) + end + + ActiveRecord::Base.configurations[Rails.env]['pool'] = options[:concurrency] + 1 + + progress = create_progress_bar(scope.count) + pool = Concurrent::FixedThreadPool.new(options[:concurrency]) + total = Concurrent::AtomicFixnum.new(0) + aggregate = Concurrent::AtomicFixnum.new(0) + + scope.reorder(nil).find_in_batches do |items| + futures = [] + + items.each do |item| + futures << Concurrent::Future.execute(executor: pool) do + begin + if !progress.total.nil? && progress.progress + 1 > progress.total + # The number of items has changed between start and now, + # since there is no good way to predict the final count from + # here, just change the progress bar to an indeterminate one + + progress.total = nil + end + + progress.log("Processing #{item.id}") if options[:verbose] + + result = ActiveRecord::Base.connection_pool.with_connection do + yield(item) + end + + aggregate.increment(result) if result.is_a?(Integer) + rescue => e + progress.log pastel.red("Error processing #{item.id}: #{e}") + ensure + progress.increment + end + end + end + + total.increment(items.size) + futures.map(&:value) + end + + progress.stop + + [total.value, aggregate.value] + end + + def pastel + @pastel ||= Pastel.new + end + end +end diff --git a/lib/mastodon/domains_cli.rb b/lib/mastodon/domains_cli.rb index b081581fe5797089d3e2a2f00c85209c33a303c8..b5435bb5e5204384e2e65774ea6934dd97cf26cc 100644 --- a/lib/mastodon/domains_cli.rb +++ b/lib/mastodon/domains_cli.rb @@ -7,41 +7,58 @@ require_relative 'cli_helper' module Mastodon class DomainsCLI < Thor + include CLIHelper + def self.exit_on_failure? true end + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] option :dry_run, type: :boolean - desc 'purge DOMAIN', 'Remove accounts from a DOMAIN without a trace' + option :whitelist_mode, type: :boolean + desc 'purge [DOMAIN...]', 'Remove accounts from a DOMAIN without a trace' long_desc <<-LONG_DESC Remove all accounts from a given DOMAIN without leaving behind any records. Unlike a suspension, if the DOMAIN still exists in the wild, it means the accounts could return if they are resolved again. + + When the --whitelist-mode option is given, instead of purging accounts + from a single domain, all accounts from domains that are not whitelisted + are removed from the database. LONG_DESC - def purge(domain) - removed = 0 + def purge(*domains) dry_run = options[:dry_run] ? ' (DRY RUN)' : '' - Account.where(domain: domain).find_each do |account| - SuspendAccountService.new.call(account, destroy: true) unless options[:dry_run] - removed += 1 - say('.', :green, false) + scope = begin + if options[:whitelist_mode] + Account.remote.where.not(domain: DomainAllow.pluck(:domain)) + elsif !domains.empty? + Account.remote.where(domain: domains) + else + say('No domain(s) given', :red) + exit(1) + end + end + + processed, = parallelize_with_progress(scope) do |account| + SuspendAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless options[:dry_run] end - DomainBlock.where(domain: domain).destroy_all unless options[:dry_run] + DomainBlock.where(domain: domains).destroy_all unless options[:dry_run] - say - say("Removed #{removed} accounts#{dry_run}", :green) + say("Removed #{processed} accounts#{dry_run}", :green) - custom_emojis = CustomEmoji.where(domain: domain) + custom_emojis = CustomEmoji.where(domain: domains) custom_emojis_count = custom_emojis.count custom_emojis.destroy_all unless options[:dry_run] + say("Removed #{custom_emojis_count} custom emojis", :green) end option :concurrency, type: :numeric, default: 50, aliases: [:c] - option :silent, type: :boolean, default: false, aliases: [:s] option :format, type: :string, default: 'summary', aliases: [:f] + option :exclude_suspended, type: :boolean, default: false, aliases: [:x] desc 'crawl [START]', 'Crawl all known peers, optionally beginning at START' long_desc <<-LONG_DESC Crawl the fediverse by using the Mastodon REST API endpoints that expose @@ -52,26 +69,31 @@ module Mastodon The --concurrency (-c) option controls the number of threads performing HTTP requests at the same time. More threads means the crawl may complete faster. - The --silent (-s) option controls progress output. - The --format (-f) option controls how the data is displayed at the end. By default (`summary`), a summary of the statistics is returned. The other options are `domains`, which returns a newline-delimited list of all discovered peers, and `json`, which dumps all the aggregated data raw. + + The --exclude-suspended (-x) option means that domains that are suspended + instance-wide do not appear in the output and are not included in summaries. + This also excludes subdomains of any of those domains. LONG_DESC def crawl(start = nil) - stats = Concurrent::Hash.new - processed = Concurrent::AtomicFixnum.new(0) - failed = Concurrent::AtomicFixnum.new(0) - start_at = Time.now.to_f - seed = start ? [start] : Account.remote.domains + stats = Concurrent::Hash.new + processed = Concurrent::AtomicFixnum.new(0) + failed = Concurrent::AtomicFixnum.new(0) + start_at = Time.now.to_f + seed = start ? [start] : Account.remote.domains + blocked_domains = Regexp.new('\\.?' + DomainBlock.where(severity: 1).pluck(:domain).join('|') + '$') + progress = create_progress_bar pool = Concurrent::ThreadPoolExecutor.new(min_threads: 0, max_threads: options[:concurrency], idletime: 10, auto_terminate: true, max_queue: 0) work_unit = ->(domain) do next if stats.key?(domain) + next if options[:exclude_suspended] && domain.match(blocked_domains) + stats[domain] = nil - processed.increment begin Request.new(:get, "https://#{domain}/api/v1/instance").perform do |res| @@ -91,11 +113,11 @@ module Mastodon next unless res.code == 200 stats[domain]['activity'] = Oj.load(res.to_s) end - - say('.', :green, false) unless options[:silent] rescue StandardError failed.increment - say('.', :red, false) unless options[:silent] + ensure + processed.increment + progress.increment unless progress.finished? end end @@ -109,10 +131,9 @@ module Mastodon pool.shutdown pool.wait_for_termination(20) ensure + progress.finish pool.shutdown - say unless options[:silent] - case options[:format] when 'summary' stats_to_summary(stats, processed, failed, start_at) diff --git a/lib/mastodon/emoji_cli.rb b/lib/mastodon/emoji_cli.rb index 97a822e4558dead73b7f38948230477c8d1d17c5..beac1b1fd22606f3821f71c98d8ad78bb5e2fc65 100644 --- a/lib/mastodon/emoji_cli.rb +++ b/lib/mastodon/emoji_cli.rb @@ -15,6 +15,7 @@ module Mastodon option :suffix option :overwrite, type: :boolean option :unlisted, type: :boolean + option :category desc 'import PATH', 'Import emoji from a TAR GZIP archive at PATH' long_desc <<-LONG_DESC Imports custom emoji from a TAR GZIP archive specified by PATH. @@ -22,6 +23,9 @@ module Mastodon Existing emoji will be skipped unless the --overwrite option is provided, in which case they will be overwritten. + You can specifiy a --category under which the emojis will be + grouped together. + With the --prefix option, a prefix can be added to all generated shortcodes. Likewise, the --suffix option controls the suffix of all shortcodes. @@ -33,6 +37,7 @@ module Mastodon imported = 0 skipped = 0 failed = 0 + category = options[:category] ? CustomEmojiCategory.find_or_create_by(name: options[:category]) : nil Gem::Package::TarReader.new(Zlib::GzipReader.open(path)) do |tar| tar.each do |entry| @@ -50,6 +55,7 @@ module Mastodon custom_emoji.image = StringIO.new(entry.read) custom_emoji.image_file_name = File.basename(entry.full_name) custom_emoji.visible_in_picker = !options[:unlisted] + custom_emoji.category = category if custom_emoji.save imported += 1 diff --git a/lib/mastodon/feeds_cli.rb b/lib/mastodon/feeds_cli.rb index fe11c3df400c1890d8d20b61911a11064b01b0be..578ea15c587238d440e479fe044d4522b6d6a99c 100644 --- a/lib/mastodon/feeds_cli.rb +++ b/lib/mastodon/feeds_cli.rb @@ -6,55 +6,32 @@ require_relative 'cli_helper' module Mastodon class FeedsCLI < Thor + include CLIHelper + def self.exit_on_failure? true end option :all, type: :boolean, default: false - option :background, type: :boolean, default: false + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] option :dry_run, type: :boolean, default: false - option :verbose, type: :boolean, default: false desc 'build [USERNAME]', 'Build home and list feeds for one or all users' long_desc <<-LONG_DESC Build home and list feeds that are stored in Redis from the database. With the --all option, all active users will be processed. Otherwise, a single user specified by USERNAME. - - With the --background option, regeneration will be queued into Sidekiq, - and the command will exit as soon as possible. - - With the --dry-run option, no work will be done. - - With the --verbose option, when accounts are processed sequentially in the - foreground, the IDs of the accounts will be printed. LONG_DESC def build(username = nil) dry_run = options[:dry_run] ? '(DRY RUN)' : '' if options[:all] || username.nil? - processed = 0 - queued = 0 - - User.active.select(:id, :account_id).reorder(nil).find_in_batches do |users| - if options[:background] - RegenerationWorker.push_bulk(users.map(&:account_id)) unless options[:dry_run] - queued += users.size - else - users.each do |user| - RegenerationWorker.new.perform(user.account_id) unless options[:dry_run] - options[:verbose] ? say(user.account_id) : say('.', :green, false) - processed += 1 - end - end + processed, = parallelize_with_progress(Account.joins(:user).merge(User.active)) do |account| + PrecomputeFeedService.new.call(account) unless options[:dry_run] end - if options[:background] - say("Scheduled feed regeneration for #{queued} accounts #{dry_run}", :green, true) - else - say - say("Regenerated feeds for #{processed} accounts #{dry_run}", :green, true) - end + say("Regenerated feeds for #{processed} accounts #{dry_run}", :green, true) elsif username.present? account = Account.find_local(username) @@ -63,11 +40,7 @@ module Mastodon exit(1) end - if options[:background] - RegenerationWorker.perform_async(account.id) unless options[:dry_run] - else - RegenerationWorker.new.perform(account.id) unless options[:dry_run] - end + PrecomputeFeedService.new.call(account) unless options[:dry_run] say("OK #{dry_run}", :green, true) else diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb index 6152d5a0950ccaf42502d6b0ca5908abaec65290..e48175134c78783a0eb41423e11e9e43697c6773 100644 --- a/lib/mastodon/media_cli.rb +++ b/lib/mastodon/media_cli.rb @@ -7,14 +7,15 @@ require_relative 'cli_helper' module Mastodon class MediaCLI < Thor include ActionView::Helpers::NumberHelper + include CLIHelper def self.exit_on_failure? true end - option :days, type: :numeric, default: 7 - option :background, type: :boolean, default: false - option :verbose, type: :boolean, default: false + option :days, type: :numeric, default: 7, aliases: [:d] + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, default: false, aliases: [:v] option :dry_run, type: :boolean, default: false desc 'remove', 'Remove remote media files' long_desc <<-DESC @@ -22,49 +23,95 @@ module Mastodon The --days option specifies how old media attachments have to be before they are removed. It defaults to 7 days. + DESC + def remove + time_ago = options[:days].days.ago + dry_run = options[:dry_run] ? '(DRY RUN)' : '' + + processed, aggregate = parallelize_with_progress(MediaAttachment.cached.where.not(remote_url: '').where('created_at < ?', time_ago)) do |media_attachment| + next if media_attachment.file.blank? + + size = media_attachment.file_file_size + + unless options[:dry_run] + media_attachment.file.destroy + media_attachment.save + end + + size + end + + say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)}) #{dry_run}", :green, true) + end + + option :account, type: :string + option :domain, type: :string + option :status, type: :numeric + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, default: false, aliases: [:v] + option :dry_run, type: :boolean, default: false + option :force, type: :boolean, default: false + desc 'refresh', 'Fetch remote media files' + long_desc <<-DESC + Re-downloads media attachments from other servers. You must specify the + source of media attachments with one of the following options: - With the --background option, instead of deleting the files sequentially, - they will be queued into Sidekiq and the command will exit as soon as - possible. In Sidekiq they will be processed with higher concurrency, but - it may impact other operations of the Mastodon server, and it may overload - the underlying file storage. + Use the --status option to download attachments from a specific status, + using the status local numeric ID. - With the --dry-run option, no work will be done. + Use the --account option to download attachments from a specific account, + using username@domain handle of the account. - With the --verbose option, when media attachments are processed sequentially in the - foreground, the IDs of the media attachments will be printed. + Use the --domain option to download attachments from a specific domain. + + By default, attachments that are believed to be already downloaded will + not be re-downloaded. To force re-download of every URL, use --force. DESC - def remove - time_ago = options[:days].days.ago - queued = 0 - processed = 0 - size = 0 - dry_run = options[:dry_run] ? '(DRY RUN)' : '' - - if options[:background] - MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).select(:id, :file_file_size).reorder(nil).find_in_batches do |media_attachments| - queued += media_attachments.size - size += media_attachments.reduce(0) { |sum, m| sum + (m.file_file_size || 0) } - Maintenance::UncacheMediaWorker.push_bulk(media_attachments.map(&:id)) unless options[:dry_run] + def refresh + dry_run = options[:dry_run] ? ' (DRY RUN)' : '' + + if options[:status] + scope = MediaAttachment.where(status_id: options[:status]) + elsif options[:account] + username, domain = username.split('@') + account = Account.find_remote(username, domain) + + if account.nil? + say('No such account', :red) + exit(1) end + + scope = MediaAttachment.where(account_id: account.id) + elsif options[:domain] + scope = MediaAttachment.joins(:account).merge(Account.by_domain_and_subdomains(options[:domain])) else - MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).reorder(nil).find_in_batches do |media_attachments| - media_attachments.each do |m| - size += m.file_file_size || 0 - Maintenance::UncacheMediaWorker.new.perform(m) unless options[:dry_run] - options[:verbose] ? say(m.id) : say('.', :green, false) - processed += 1 - end - end + exit(1) end - say + processed, aggregate = parallelize_with_progress(scope) do |media_attachment| + next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?) - if options[:background] - say("Scheduled the deletion of #{queued} media attachments (approx. #{number_to_human_size(size)}) #{dry_run}", :green, true) - else - say("Removed #{processed} media attachments (approx. #{number_to_human_size(size)}) #{dry_run}", :green, true) + unless options[:dry_run] + media_attachment.reset_file! + media_attachment.save + end + + media_attachment.file_file_size end + + say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true) + end + + desc 'usage', 'Calculate disk space consumed by Mastodon' + def usage + say("Attachments:\t#{number_to_human_size(MediaAttachment.sum(:file_file_size))} (#{number_to_human_size(MediaAttachment.where(account: Account.local).sum(:file_file_size))} local)") + say("Custom emoji:\t#{number_to_human_size(CustomEmoji.sum(:image_file_size))} (#{number_to_human_size(CustomEmoji.local.sum(:image_file_size))} local)") + say("Preview cards:\t#{number_to_human_size(PreviewCard.sum(:image_file_size))}") + say("Avatars:\t#{number_to_human_size(Account.sum(:avatar_file_size))} (#{number_to_human_size(Account.local.sum(:avatar_file_size))} local)") + say("Headers:\t#{number_to_human_size(Account.sum(:header_file_size))} (#{number_to_human_size(Account.local.sum(:header_file_size))} local)") + say("Backups:\t#{number_to_human_size(Backup.sum(:dump_file_size))}") + say("Imports:\t#{number_to_human_size(Import.sum(:data_file_size))}") + say("Settings:\t#{number_to_human_size(SiteUpload.sum(:file_file_size))}") end end end diff --git a/lib/mastodon/preview_cards_cli.rb b/lib/mastodon/preview_cards_cli.rb new file mode 100644 index 0000000000000000000000000000000000000000..cf4407250c5578604ab42f3397d3ae0a9bcbce12 --- /dev/null +++ b/lib/mastodon/preview_cards_cli.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'tty-prompt' +require_relative '../../config/boot' +require_relative '../../config/environment' +require_relative 'cli_helper' + +module Mastodon + class PreviewCardsCLI < Thor + include ActionView::Helpers::NumberHelper + include CLIHelper + + def self.exit_on_failure? + true + end + + option :days, type: :numeric, default: 180 + option :concurrency, type: :numeric, default: 5, aliases: [:c] + option :verbose, type: :boolean, aliases: [:v] + option :dry_run, type: :boolean, default: false + option :link, type: :boolean, default: false + desc 'remove', 'Remove preview cards' + long_desc <<-DESC + Removes local thumbnails for preview cards. + + The --days option specifies how old preview cards have to be before + they are removed. It defaults to 180 days. Since preview cards will + not be re-fetched unless the link is re-posted after 2 weeks from + last time, it is not recommended to delete preview cards within the + last 14 days. + + With the --link option, only link-type preview cards will be deleted, + leaving video and photo cards untouched. + DESC + def remove + time_ago = options[:days].days.ago + dry_run = options[:dry_run] ? ' (DRY RUN)' : '' + link = options[:link] ? 'link-type ' : '' + scope = PreviewCard.cached + scope = scope.where(type: :link) if options[:link] + scope = scope.where('updated_at < ?', time_ago) + + processed, aggregate = parallelize_with_progress(scope) do |preview_card| + next if preview_card.image.blank? + + size = preview_card.image_file_size + + unless options[:dry_run] + preview_card.image.destroy + preview_card.save + end + + size + end + + say("Removed #{processed} #{link}preview cards (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true) + end + end +end diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb index 42ad93f1edbfa271a7a9cd091a4848adcbc1165a..8bd5f9543ffe38092477c9f840b687c54170e9f5 100644 --- a/lib/mastodon/search_cli.rb +++ b/lib/mastodon/search_cli.rb @@ -6,6 +6,7 @@ require_relative 'cli_helper' module Mastodon class SearchCLI < Thor + option :processes, default: 2, aliases: [:p] desc 'deploy', 'Create or update an ElasticSearch index and populate it' long_desc <<~LONG_DESC If ElasticSearch is empty, this command will create the necessary indices @@ -13,10 +14,28 @@ module Mastodon This command will also upgrade indices if the underlying schema has been changed since the last run. + + With the --processes option, parallelize execution of the command. The + default is 2. If "auto" is specified, the number is automatically + derived from available CPUs. LONG_DESC def deploy - processed = Chewy::RakeHelper.upgrade - Chewy::RakeHelper.sync(except: processed) + processed = Chewy::RakeHelper.upgrade(parallel: processes) + Chewy::RakeHelper.sync(except: processed, parallel: processes) + end + + private + + def processes + return true if options[:processes] == 'auto' + + num = options[:processes].to_i + + if num < 2 + nil + else + num + end end end end diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 99d709c980da5b516829314b91c727e0181c4de1..f3ead6d8d96d6a2e8d4b9fe902b03cee74f0798b 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -5,15 +5,15 @@ module Mastodon module_function def major - 2 + 3 end def minor - 9 + 0 end def patch - 3 + 1 end def flags diff --git a/lib/paperclip/gif_transcoder.rb b/lib/paperclip/gif_transcoder.rb index cbab6fd99a29e7ac7f95a966c0614ee5f27904b9..64f12f9637e57fd63953aa2aa2e6293dd9a40eee 100644 --- a/lib/paperclip/gif_transcoder.rb +++ b/lib/paperclip/gif_transcoder.rb @@ -1,5 +1,103 @@ # frozen_string_literal: true +class GifReader + attr_reader :animated + + EXTENSION_LABELS = [0xf9, 0x01, 0xff].freeze + GIF_HEADERS = %w(GIF87a GIF89a).freeze + + class GifReaderException; end + + class UnknownImageType < GifReaderException; end + + class CannotParseImage < GifReaderException; end + + def self.animated?(path) + new(path).animated + rescue GifReaderException + false + end + + def initialize(path, max_frames = 2) + @path = path + @nb_frames = 0 + + File.open(path, 'rb') do |s| + raise UnknownImageType unless GIF_HEADERS.include?(s.read(6)) + + # Skip to "packed byte" + s.seek(4, IO::SEEK_CUR) + + # "Packed byte" gives us the size of the GIF color table + packed_byte, = s.read(1).unpack('C') + + # Skip background color and aspect ratio + s.seek(2, IO::SEEK_CUR) + + if packed_byte & 0x80 != 0 + # GIF uses a global color table, skip it + s.seek(3 * (1 << ((packed_byte & 0x07) + 1)), IO::SEEK_CUR) + end + + # Now read data + while @nb_frames < max_frames + separator = s.read(1) + + case separator + when ',' # Image block + @nb_frames += 1 + + # Skip to "packed byte" + s.seek(8, IO::SEEK_CUR) + packed_byte, = s.read(1).unpack('C') + + if packed_byte & 0x80 != 0 + # Image uses a local color table, skip it + s.seek(3 * (1 << ((packed_byte & 0x07) + 1)), IO::SEEK_CUR) + end + + # Skip lzw min code size + raise InvalidValue unless s.read(1).unpack('C')[0] >= 2 + + # Skip image data sub-blocks + skip_sub_blocks!(s) + when '!' # Extension block + skip_extension_block!(s) + when ';' # Trailer + break + else + raise CannotParseImage + end + end + end + + @animated = @nb_frames > 1 + end + + private + + def skip_extension_block!(file) + if EXTENSION_LABELS.include?(file.read(1).unpack('C')[0]) + block_size, = file.read(1).unpack('C') + file.seek(block_size, IO::SEEK_CUR) + end + + # Read until extension block end marker + skip_sub_blocks!(file) + end + + # Skip sub-blocks up until block end marker + def skip_sub_blocks!(file) + loop do + size, = file.read(1).unpack('C') + + break if size.zero? + + file.seek(size, IO::SEEK_CUR) + end + end +end + module Paperclip # This transcoder is only to be used for the MediaAttachment model # to convert animated gifs to webm @@ -19,8 +117,7 @@ module Paperclip private def needs_convert? - num_frames = identify('-format %n :file', file: file.path).to_i - options[:style] == :original && num_frames > 1 + options[:style] == :original && GifReader.animated?(file.path) end end end diff --git a/lib/paperclip/video_transcoder.rb b/lib/paperclip/video_transcoder.rb index c3504c17c4fbef2c959d2e86d924b67994530fed..66f7feda5603201043366962b72f8cbf3ff7a3e3 100644 --- a/lib/paperclip/video_transcoder.rb +++ b/lib/paperclip/video_transcoder.rb @@ -6,7 +6,9 @@ module Paperclip class VideoTranscoder < Paperclip::Processor def make meta = ::Av.cli.identify(@file.path) + attachment.instance.type = MediaAttachment.types[:gifv] unless meta[:audio_encode] + options[:format] = File.extname(attachment.instance.file_file_name)[1..-1] if options[:keep_same_format] Paperclip::Transcoder.make(file, options, attachment) end diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index ee9657b0eff06c272091bc7775b8c90e7b9850d2..2e92e8dedbe115588ab0ca52969670706eb18883 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -135,7 +135,7 @@ namespace :mastodon do prompt.say "\n" if prompt.yes?('Do you want to store uploaded files on the cloud?', default: false) - case prompt.select('Provider', ['Amazon S3', 'Wasabi', 'Minio']) + case prompt.select('Provider', ['Amazon S3', 'Wasabi', 'Minio', 'Google Cloud Storage']) when 'Amazon S3' env['S3_ENABLED'] = 'true' env['S3_PROTOCOL'] = 'https' @@ -217,6 +217,34 @@ namespace :mastodon do q.required true q.modify :strip end + when 'Google Cloud Storage' + env['S3_ENABLED'] = 'true' + env['S3_PROTOCOL'] = 'https' + env['S3_HOSTNAME'] = 'storage.googleapis.com' + env['S3_ENDPOINT'] = 'https://storage.googleapis.com' + env['S3_MULTIPART_THRESHOLD'] = 50.megabytes + + env['S3_BUCKET'] = prompt.ask('GCS bucket name:') do |q| + q.required true + q.default "files.#{env['LOCAL_DOMAIN']}" + q.modify :strip + end + + env['S3_REGION'] = prompt.ask('GCS region:') do |q| + q.required true + q.default 'us-west1' + q.modify :strip + end + + env['AWS_ACCESS_KEY_ID'] = prompt.ask('GCS access key:') do |q| + q.required true + q.modify :strip + end + + env['AWS_SECRET_ACCESS_KEY'] = prompt.ask('GCS secret key:') do |q| + q.required true + q.modify :strip + end end if prompt.yes?('Do you want to access the uploaded files from your own domain?') diff --git a/lib/tasks/repo.rake b/lib/tasks/repo.rake index 8ceec3085461f3e556e10c829942f383968c17fc..d1de17b7c0347da7ce820a6c90c7bdafa4b747d6 100644 --- a/lib/tasks/repo.rake +++ b/lib/tasks/repo.rake @@ -76,4 +76,19 @@ namespace :repo do tmp.unlink end end + + task check_locales_files: :environment do + pastel = Pastel.new + + missing_yaml_files = I18n.available_locales.reject { |locale| File.exist?(Rails.root.join('config', 'locales', "#{locale}.yml")) } + missing_json_files = I18n.available_locales.reject { |locale| File.exist?(Rails.root.join('app', 'javascript', 'mastodon', 'locales', "#{locale}.json")) } + + if missing_json_files.empty? && missing_yaml_files.empty? + puts pastel.green('OK') + else + puts pastel.red("Missing YAML files: #{pastel.bold(missing_yaml_files.join(', '))}") unless missing_yaml_files.empty? + puts pastel.red("Missing JSON files: #{pastel.bold(missing_json_files.join(', '))}") unless missing_json_files.empty? + exit(1) + end + end end diff --git a/package.json b/package.json index c691d8e4345c5c1220a307461f933fb674a0a83d..433496b84ad54cb159db48d2923a534136ce6071 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "mastodon", + "name": "@tootsuite/mastodon", "license": "AGPL-3.0-or-later", "engines": { - "node": ">=8.12 <12" + "node": ">=8.12 <13" }, "scripts": { "postversion": "git push --tags", @@ -60,22 +60,22 @@ "private": true, "dependencies": { "@babel/core": "^7.4.5", - "@babel/plugin-proposal-class-properties": "^7.4.4", + "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/plugin-proposal-object-rest-spread": "^7.4.4", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-react-inline-elements": "^7.2.0", "@babel/plugin-transform-react-jsx-self": "^7.2.0", - "@babel/plugin-transform-react-jsx-source": "^7.2.0", - "@babel/plugin-transform-runtime": "^7.4.4", - "@babel/preset-env": "^7.4.5", + "@babel/plugin-transform-react-jsx-source": "^7.5.0", + "@babel/plugin-transform-runtime": "^7.5.5", + "@babel/preset-env": "^7.6.0", "@babel/preset-react": "^7.0.0", - "@babel/runtime": "^7.4.5", - "@clusterws/cws": "^0.14.0", + "@babel/runtime": "^7.5.4", + "@clusterws/cws": "^0.15.0", "array-includes": "^3.0.3", - "autoprefixer": "^9.6.0", + "autoprefixer": "^9.6.1", "axios": "^0.19.0", - "babel-loader": "^8.0.5", + "babel-loader": "^8.0.6", "babel-plugin-lodash": "^3.3.4", "babel-plugin-preval": "^3.0.1", "babel-plugin-react-intl": "^3.1.3", @@ -83,33 +83,35 @@ "babel-runtime": "^6.26.0", "blurhash": "^1.0.0", "classnames": "^2.2.5", - "compression-webpack-plugin": "^2.0.0", + "compression-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^5.0.4", "cross-env": "^5.1.4", - "css-loader": "^2.1.1", + "css-loader": "^3.2.0", "cssnano": "^4.1.10", "detect-passive-events": "^1.0.2", "dotenv": "^8.0.0", "emoji-mart": "Gargron/emoji-mart#build", - "es6-symbol": "^3.1.1", + "es6-symbol": "^3.1.2", "escape-html": "^1.0.3", "exif-js": "^2.3.0", "express": "^4.17.1", - "file-loader": "^4.0.0", + "file-loader": "^4.2.0", "font-awesome": "^4.7.0", "glob": "^7.1.1", + "history": "^4.10.1", "http-link-header": "^1.0.2", "immutable": "^3.8.2", "imports-loader": "^0.8.0", "intersection-observer": "^0.7.0", "intl": "^1.2.5", "intl-messageformat": "^2.2.0", - "intl-relativeformat": "^2.2.0", + "intl-relativeformat": "^6.4.3", "is-nan": "^1.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.7.11", + "lodash": "^4.17.14", "mark-loader": "^0.1.6", "marky": "^1.2.1", - "mini-css-extract-plugin": "^0.5.0", + "mini-css-extract-plugin": "^0.8.0", "mkdirp": "^0.5.1", "npmlog": "^4.1.2", "object-assign": "^4.1.1", @@ -133,7 +135,7 @@ "react-motion": "^0.5.2", "react-notification": "^6.8.4", "react-overlays": "^0.8.3", - "react-redux": "^6.0.1", + "react-redux": "^7.1.1", "react-redux-loading-bar": "^4.0.8", "react-router-dom": "^4.1.1", "react-router-scroll-4": "^1.0.0-beta.1", @@ -143,44 +145,46 @@ "react-textarea-autosize": "^7.1.0", "react-toggle": "^4.0.1", "redis": "^2.7.1", - "redux": "^4.0.1", + "redux": "^4.0.4", "redux-immutable": "^4.0.0", "redux-thunk": "^2.2.0", "rellax": "^1.10.0", "requestidlecallback": "^0.3.0", "reselect": "^4.0.0", - "rimraf": "^2.6.3", - "sass": "^1.20.3", + "rimraf": "^3.0.0", + "sass": "^1.22.12", "sass-loader": "^7.0.3", - "stringz": "^1.0.0", + "stringz": "^2.0.0", "substring-trie": "^1.0.2", - "terser-webpack-plugin": "^1.3.0", + "terser-webpack-plugin": "^1.4.1", + "tesseract.js": "^2.0.0-alpha.16", "throng": "^4.0.0", "tiny-queue": "^0.2.1", "uuid": "^3.1.0", - "webpack": "^4.34.0", + "wavesurfer.js": "^3.0.0", + "webpack": "^4.35.3", "webpack-assets-manifest": "^3.1.1", "webpack-bundle-analyzer": "^3.3.2", - "webpack-cli": "^3.3.4", + "webpack-cli": "^3.3.7", "webpack-merge": "^4.2.1", "websocket.js": "^0.1.12" }, "devDependencies": { - "babel-eslint": "^10.0.1", - "babel-jest": "^24.8.0", + "babel-eslint": "^10.0.3", + "babel-jest": "^24.9.0", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", - "eslint": "^5.11.1", - "eslint-plugin-import": "~2.17.3", - "eslint-plugin-jsx-a11y": "~6.2.1", - "eslint-plugin-promise": "~4.1.1", - "eslint-plugin-react": "~7.12.1", - "jest": "^24.8.0", + "eslint": "^6.5.0", + "eslint-plugin-import": "~2.18.2", + "eslint-plugin-jsx-a11y": "~6.2.3", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.3", + "jest": "^24.9.0", "raf": "^3.4.1", "react-intl-translations-manager": "^5.0.3", "react-test-renderer": "^16.8.6", "sass-lint": "^1.13.1", - "webpack-dev-server": "^3.5.1", - "yargs": "^12.0.5" + "webpack-dev-server": "^3.8.0", + "yargs": "^13.3.0" } } diff --git a/public/ocr/lang-data/eng.traineddata.gz b/public/ocr/lang-data/eng.traineddata.gz new file mode 100644 index 0000000000000000000000000000000000000000..e83c1267675ff3a6641b110002363782db7a1885 Binary files /dev/null and b/public/ocr/lang-data/eng.traineddata.gz differ diff --git a/scalingo.json b/scalingo.json index dd8fb5530cfd5494a205e6eae478082faffb5dec..324356df0c262b1d386c4542a968f9c5ac97dff2 100644 --- a/scalingo.json +++ b/scalingo.json @@ -8,15 +8,6 @@ "description": "The domain that your Mastodon instance will run on (this can be appname.scalingo.io or a custom domain)", "required": true }, - "LOCAL_HTTPS": { - "description": "Will your domain support HTTPS? (Automatic for *.scalingo.io, requires manual configuration for custom domains)", - "value": "true", - "required": true - }, - "PAPERCLIP_SECRET": { - "description": "The secret key for storing media files", - "generator": "secret" - }, "SECRET_KEY_BASE": { "description": "The secret key base", "generator": "secret" diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index b728d719f9dea829be2294a2a24d3e7fcbe91f3e..3d2a0665da8a4b4b8e44d31a2148f03284806a02 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -48,37 +48,6 @@ RSpec.describe AccountsController, type: :controller do end end - context 'atom' do - let(:format) { 'atom' } - let(:content_type) { 'application/atom+xml' } - - shared_examples 'responsed streams' do - it 'assigns @entries' do - entries = assigns(:entries).to_a - expect(entries.size).to eq expected_statuses.size - entries.each.zip(expected_statuses.each) do |entry, expected_status| - expect(entry.status).to eq expected_status - end - end - end - - include_examples 'responses' - - context 'without max_id nor since_id' do - let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] } - - include_examples 'responsed streams' - end - - context 'with max_id and since_id' do - let(:max_id) { status4.stream_entry.id } - let(:since_id) { status1.stream_entry.id } - let(:expected_statuses) { [status3, status2] } - - include_examples 'responsed streams' - end - end - context 'activitystreams2' do let(:format) { 'json' } let(:content_type) { 'application/activity+json' } diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb index eab4b8c3e65c2d04ee4d1c89b8aef3e7e71410d6..a9ee7549002175eaee3cdd391a5cd23db5d1fa58 100644 --- a/spec/controllers/activitypub/inboxes_controller_spec.rb +++ b/spec/controllers/activitypub/inboxes_controller_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' RSpec.describe ActivityPub::InboxesController, type: :controller do describe 'POST #create' do - context 'if signed_request_account' do + context 'with signed_request_account' do it 'returns 202' do allow(controller).to receive(:signed_request_account) do Fabricate(:account) @@ -15,7 +15,7 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do end end - context 'not signed_request_account' do + context 'without signed_request_account' do it 'returns 401' do allow(controller).to receive(:signed_request_account) do false diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index a348ab3d75eddd9daf9805bf5372591c4c72a859..608606ff90c0f37519b00cbba9812afbac8704c3 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -75,44 +75,6 @@ RSpec.describe Admin::AccountsController, type: :controller do end end - describe 'POST #subscribe' do - subject { post :subscribe, params: { id: account.id } } - - let(:current_user) { Fabricate(:user, admin: admin) } - let(:account) { Fabricate(:account) } - - context 'when user is admin' do - let(:admin) { true } - - it { is_expected.to redirect_to admin_account_path(account.id) } - end - - context 'when user is not admin' do - let(:admin) { false } - - it { is_expected.to have_http_status :forbidden } - end - end - - describe 'POST #unsubscribe' do - subject { post :unsubscribe, params: { id: account.id } } - - let(:current_user) { Fabricate(:user, admin: admin) } - let(:account) { Fabricate(:account) } - - context 'when user is admin' do - let(:admin) { true } - - it { is_expected.to redirect_to admin_account_path(account.id) } - end - - context 'when user is not admin' do - let(:admin) { false } - - it { is_expected.to have_http_status :forbidden } - end - end - describe 'POST #memorialize' do subject { post :memorialize, params: { id: account.id } } diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb index b7e2894e90af2c8c74f6564fc496bcd29a55746c..a8d96948caebad57d6d9654509c9ea162420841d 100644 --- a/spec/controllers/admin/custom_emojis_controller_spec.rb +++ b/spec/controllers/admin/custom_emojis_controller_spec.rb @@ -52,64 +52,4 @@ describe Admin::CustomEmojisController do end end end - - describe 'PUT #update' do - let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') } - let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') } - - before do - put :update, params: { id: custom_emoji.id, custom_emoji: params } - end - - context 'when parameter is valid' do - let(:params) { { shortcode: 'updated', image: image } } - - it 'succeeds in updating custom emoji' do - expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.updated_msg') - expect(custom_emoji.reload).to have_attributes(shortcode: 'updated') - end - end - - context 'when parameter is invalid' do - let(:params) { { shortcode: 'u', image: image } } - - it 'fails to update custom emoji' do - expect(flash[:alert]).to eq I18n.t('admin.custom_emojis.update_failed_msg') - expect(custom_emoji.reload).to have_attributes(shortcode: 'test') - end - end - end - - describe 'POST #copy' do - subject { post :copy, params: { id: custom_emoji.id } } - - let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') } - - it 'copies custom emoji' do - expect { subject }.to change { CustomEmoji.where(shortcode: 'test').count }.by(1) - expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.copied_msg') - end - end - - describe 'POST #enable' do - let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: true) } - - before { post :enable, params: { id: custom_emoji.id } } - - it 'enables custom emoji' do - expect(response).to redirect_to admin_custom_emojis_path - expect(custom_emoji.reload).to have_attributes(disabled: false) - end - end - - describe 'POST #disable' do - let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: false) } - - before { post :disable, params: { id: custom_emoji.id } } - - it 'enables custom emoji' do - expect(response).to redirect_to admin_custom_emojis_path - expect(custom_emoji.reload).to have_attributes(disabled: true) - end - end end diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index c358506d6dce88b136487dbefd266d00719aec8d..2a1598123c1db962163d979d4d0838af54f6878b 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -47,7 +47,7 @@ describe Admin::ReportedStatusesController do it 'removes a status' do allow(RemovalWorker).to receive(:perform_async) subject.call - expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first) + expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first, immediate: true) end end diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb index 1a08c10b7e873fd1c2052419601e999d797acee3..d9690d83f228acc5469e95c614952eafa9ffe554 100644 --- a/spec/controllers/admin/statuses_controller_spec.rb +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -65,7 +65,7 @@ describe Admin::StatusesController do it 'removes a status' do allow(RemovalWorker).to receive(:perform_async) subject.call - expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first) + expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first, immediate: true) end end diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb deleted file mode 100644 index 967152abe36195704f00a935f0f8173519f6bd78..0000000000000000000000000000000000000000 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -require 'rails_helper' - -RSpec.describe Admin::SubscriptionsController, type: :controller do - render_views - - describe 'GET #index' do - around do |example| - default_per_page = Subscription.default_per_page - Subscription.paginates_per 1 - example.run - Subscription.paginates_per default_per_page - end - - before do - sign_in Fabricate(:user, admin: true), scope: :user - end - - it 'renders subscriptions' do - Fabricate(:subscription) - specified = Fabricate(:subscription) - - get :index - - subscriptions = assigns(:subscriptions) - expect(subscriptions.count).to eq 1 - expect(subscriptions[0]).to eq specified - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/controllers/admin/tags_controller_spec.rb b/spec/controllers/admin/tags_controller_spec.rb index 3af994071ce724cb09b66fd26f4d601cd1c39679..5c1944fc770dcd225dca79d48db20abc22804818 100644 --- a/spec/controllers/admin/tags_controller_spec.rb +++ b/spec/controllers/admin/tags_controller_spec.rb @@ -10,62 +10,14 @@ RSpec.describe Admin::TagsController, type: :controller do end describe 'GET #index' do - before do - account_tag_stat = Fabricate(:tag).account_tag_stat - account_tag_stat.update(hidden: hidden, accounts_count: 1) - get :index, params: { hidden: hidden } - end - - context 'with hidden tags' do - let(:hidden) { true } - - it 'returns status 200' do - expect(response).to have_http_status(200) - end - end - - context 'without hidden tags' do - let(:hidden) { false } - - it 'returns status 200' do - expect(response).to have_http_status(200) - end - end - end - - describe 'POST #hide' do - let(:tag) { Fabricate(:tag) } + let!(:tag) { Fabricate(:tag) } before do - tag.account_tag_stat.update(hidden: false) - post :hide, params: { id: tag.id } - end - - it 'hides tag' do - tag.reload - expect(tag).to be_hidden - end - - it 'redirects to admin_tags_path' do - expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params))) - end - end - - describe 'POST #unhide' do - let(:tag) { Fabricate(:tag) } - - before do - tag.account_tag_stat.update(hidden: true) - post :unhide, params: { id: tag.id } - end - - it 'unhides tag' do - tag.reload - expect(tag).not_to be_hidden + get :index end - it 'redirects to admin_tags_path' do - expect(response).to redirect_to(admin_tags_path(controller.instance_variable_get(:@filter_params))) + it 'returns status 200' do + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/base_controller_spec.rb b/spec/controllers/api/base_controller_spec.rb index 750ccc8cf67235865fa2790eec50794990640618..05a42d1c19597b608061fc01ccaddbc7a615a992 100644 --- a/spec/controllers/api/base_controller_spec.rb +++ b/spec/controllers/api/base_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::BaseController do end end - describe 'Forgery protection' do + describe 'forgery protection' do before do routes.draw { post 'success' => 'api/base#success' } end @@ -27,7 +27,45 @@ describe Api::BaseController do end end - describe 'Error handling' do + describe 'non-functional accounts handling' do + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } + + controller do + before_action :require_user! + end + + before do + routes.draw { post 'success' => 'api/base#success' } + allow(controller).to receive(:doorkeeper_token) { token } + end + + it 'returns http forbidden for unconfirmed accounts' do + user.update(confirmed_at: nil) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for pending accounts' do + user.update(approved: false) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for disabled accounts' do + user.update(disabled: true) + post 'success' + expect(response).to have_http_status(403) + end + + it 'returns http forbidden for suspended accounts' do + user.account.suspend! + post 'success' + expect(response).to have_http_status(403) + end + end + + describe 'error handling' do ERRORS_WITH_CODES = { ActiveRecord::RecordInvalid => 422, Mastodon::ValidationError => 422, diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb index 7fee15a3532748a9c6d349b0f31b2c65d66da62c..b9082bde1e7d255ef8296e2916373692d8939bcd 100644 --- a/spec/controllers/api/oembed_controller_spec.rb +++ b/spec/controllers/api/oembed_controller_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Api::OEmbedController, type: :controller do describe 'GET #show' do before do request.host = Rails.configuration.x.local_domain - get :show, params: { url: account_stream_entry_url(alice, status.stream_entry) }, format: :json + get :show, params: { url: short_account_status_url(alice, status) }, format: :json end it 'returns http success' do diff --git a/spec/controllers/api/push_controller_spec.rb b/spec/controllers/api/push_controller_spec.rb deleted file mode 100644 index d769d8554fcce3c339a7e09af63aaf06d0e01408..0000000000000000000000000000000000000000 --- a/spec/controllers/api/push_controller_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'rails_helper' - -RSpec.describe Api::PushController, type: :controller do - describe 'POST #update' do - context 'with hub.mode=subscribe' do - it 'creates a subscription' do - service = double(call: ['', 202]) - allow(Pubsubhubbub::SubscribeService).to receive(:new).and_return(service) - account = Fabricate(:account) - account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom" - post :update, params: { - 'hub.mode' => 'subscribe', - 'hub.topic' => account_topic_url, - 'hub.callback' => 'https://callback.host/api', - 'hub.lease_seconds' => '3600', - 'hub.secret' => 'as1234df', - } - - expect(service).to have_received(:call).with( - account, - 'https://callback.host/api', - 'as1234df', - '3600', - nil - ) - expect(response).to have_http_status(202) - end - end - - context 'with hub.mode=unsubscribe' do - it 'unsubscribes the account' do - service = double(call: ['', 202]) - allow(Pubsubhubbub::UnsubscribeService).to receive(:new).and_return(service) - account = Fabricate(:account) - account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom" - post :update, params: { - 'hub.mode' => 'unsubscribe', - 'hub.topic' => account_topic_url, - 'hub.callback' => 'https://callback.host/api', - } - - expect(service).to have_received(:call).with( - account, - 'https://callback.host/api', - ) - expect(response).to have_http_status(202) - end - end - - context 'with unknown mode' do - it 'returns an unknown mode error' do - post :update, params: { 'hub.mode' => 'fake' } - - expect(response).to have_http_status(422) - expect(response.body).to match(/Unknown mode/) - end - end - end -end diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb deleted file mode 100644 index 235a29af0041e5207b145b521867789fbd36ed65..0000000000000000000000000000000000000000 --- a/spec/controllers/api/salmon_controller_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'rails_helper' - -RSpec.describe Api::SalmonController, type: :controller do - render_views - - let(:account) { Fabricate(:user, account: Fabricate(:account, username: 'catsrgr8')).account } - - before do - stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt')) - stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt')) - stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt')) - stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt')) - end - - describe 'POST #update' do - context 'with valid post data' do - before do - post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml')) - end - - it 'contains XML in the request body' do - expect(request.body.read).to be_a String - end - - it 'returns http success' do - expect(response).to have_http_status(202) - end - - it 'creates remote account' do - expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil - end - - it 'creates status' do - expect(Status.find_by(uri: 'tag:quitter.no,2016-03-20:noticeId=1276923:objectType=note')).to_not be_nil - end - - it 'creates mention for target account' do - expect(account.mentions.count).to eq 1 - end - end - - context 'with empty post data' do - before do - post :update, params: { id: account.id }, body: '' - end - - it 'returns http client error' do - expect(response).to have_http_status(400) - end - end - - context 'with invalid post data' do - before do - service = double(call: false) - allow(VerifySalmonService).to receive(:new).and_return(service) - - post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml')) - end - - it 'returns http client error' do - expect(response).to have_http_status(401) - end - end - end -end diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb deleted file mode 100644 index 7a4252fe6737ec9778099e0ad4c6a3c2c2bd07a5..0000000000000000000000000000000000000000 --- a/spec/controllers/api/subscriptions_controller_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'rails_helper' - -RSpec.describe Api::SubscriptionsController, type: :controller do - render_views - - let(:account) { Fabricate(:account, username: 'gargron', domain: 'quitter.no', remote_url: 'topic_url', secret: 'abc') } - - describe 'GET #show' do - context 'with valid subscription' do - before do - get :show, params: { :id => account.id, 'hub.topic' => 'topic_url', 'hub.challenge' => '456', 'hub.lease_seconds' => "#{86400 * 30}" } - end - - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'echoes back the challenge' do - expect(response.body).to match '456' - end - end - - context 'with invalid subscription' do - before do - expect_any_instance_of(Account).to receive_message_chain(:subscription, :valid?).and_return(false) - get :show, params: { :id => account.id } - end - - it 'returns http success' do - expect(response).to have_http_status(404) - end - end - end - - describe 'POST #update' do - let(:feed) { File.read(Rails.root.join('spec', 'fixtures', 'push', 'feed.atom')) } - - before do - stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt')) - stub_request(:get, "https://quitter.no/notice/1269244").to_return(status: 404) - stub_request(:get, "https://quitter.no/notice/1265331").to_return(status: 404) - stub_request(:get, "https://community.highlandarrow.com/notice/54411").to_return(status: 404) - stub_request(:get, "https://community.highlandarrow.com/notice/53857").to_return(status: 404) - stub_request(:get, "https://community.highlandarrow.com/notice/51852").to_return(status: 404) - stub_request(:get, "https://social.umeahackerspace.se/notice/424348").to_return(status: 404) - stub_request(:get, "https://community.highlandarrow.com/notice/50467").to_return(status: 404) - stub_request(:get, "https://quitter.no/notice/1243309").to_return(status: 404) - stub_request(:get, "https://quitter.no/user/7477").to_return(status: 404) - stub_request(:any, "https://community.highlandarrow.com/user/1").to_return(status: 404) - stub_request(:any, "https://social.umeahackerspace.se/user/2").to_return(status: 404) - stub_request(:any, "https://gs.kawa-kun.com/user/2").to_return(status: 404) - stub_request(:any, "https://mastodon.social/users/Gargron").to_return(status: 404) - - request.env['HTTP_X_HUB_SIGNATURE'] = "sha1=#{OpenSSL::HMAC.hexdigest('sha1', 'abc', feed)}" - - post :update, params: { id: account.id }, body: feed - end - - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'creates statuses for feed' do - expect(account.statuses.count).to_not eq 0 - end - end -end diff --git a/spec/controllers/api/v1/follow_requests_controller_spec.rb b/spec/controllers/api/v1/follow_requests_controller_spec.rb index 87292d9ce1ee452e12203753d5b845da0fc405f1..ae92a9627a11346bd8b3d76c5dde929c2a7241e8 100644 --- a/spec/controllers/api/v1/follow_requests_controller_spec.rb +++ b/spec/controllers/api/v1/follow_requests_controller_spec.rb @@ -38,6 +38,12 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do it 'allows follower to follow' do expect(follower.following?(user.account)).to be true end + + it 'returns JSON with followed_by=true' do + json = body_as_json + + expect(json[:followed_by]).to be true + end end describe 'POST #reject' do @@ -54,5 +60,11 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do it 'removes follow request' do expect(FollowRequest.where(target_account: user.account, account: follower).count).to eq 0 end + + it 'returns JSON with followed_by=false' do + json = body_as_json + + expect(json[:followed_by]).to be false + end end end diff --git a/spec/controllers/api/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb deleted file mode 100644 index 089e0fe5ecadcb5462143bda6f924d16f2324a3c..0000000000000000000000000000000000000000 --- a/spec/controllers/api/v1/follows_controller_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'rails_helper' - -RSpec.describe Api::V1::FollowsController, type: :controller do - render_views - - let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:follows') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'POST #create' do - before do - stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt')) - stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt')) - stub_request(:head, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(:status => 405, :body => "", :headers => {}) - stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt')) - stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt')) - stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:post, "https://quitter.no/main/salmon/user/7477").to_return(:status => 200, :body => "", :headers => {}) - - post :create, params: { uri: 'gargron@quitter.no' } - end - - it 'returns http success' do - expect(response).to have_http_status(200) - end - - it 'creates account for remote user' do - expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil - end - - it 'creates a follow relation between user and remote user' do - expect(user.account.following?(Account.find_by(username: 'gargron', domain: 'quitter.no'))).to be true - end - - it 'sends a salmon slap to the remote user' do - expect(a_request(:post, "https://quitter.no/main/salmon/user/7477")).to have_been_made - end - - it 'subscribes to remote hub' do - expect(a_request(:post, "https://quitter.no/main/push/hub")).to have_been_made - end - - it 'returns http success if already following, too' do - post :create, params: { uri: 'gargron@quitter.no' } - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/controllers/api/v1/markers_controller_spec.rb b/spec/controllers/api/v1/markers_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..556a75b9b1a0fb8cbe508316e793e6fa3b680608 --- /dev/null +++ b/spec/controllers/api/v1/markers_controller_spec.rb @@ -0,0 +1,65 @@ +require 'rails_helper' + +RSpec.describe Api::V1::MarkersController, type: :controller do + render_views + + let!(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses write:statuses') } + + before { allow(controller).to receive(:doorkeeper_token) { token } } + + describe 'GET #index' do + before do + Fabricate(:marker, timeline: 'home', last_read_id: 123, user: user) + Fabricate(:marker, timeline: 'notifications', last_read_id: 456, user: user) + + get :index, params: { timeline: %w(home notifications) } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'returns markers' do + json = body_as_json + + expect(json.key?(:home)).to be true + expect(json[:home][:last_read_id]).to eq '123' + expect(json.key?(:notifications)).to be true + expect(json[:notifications][:last_read_id]).to eq '456' + end + end + + describe 'POST #create' do + context 'when no marker exists' do + before do + post :create, params: { home: { last_read_id: '69420' } } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'creates a marker' do + expect(user.markers.first.timeline).to eq 'home' + expect(user.markers.first.last_read_id).to eq 69420 + end + end + + context 'when a marker exists' do + before do + post :create, params: { home: { last_read_id: '69420' } } + post :create, params: { home: { last_read_id: '70120' } } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'updates a marker' do + expect(user.markers.first.timeline).to eq 'home' + expect(user.markers.first.last_read_id).to eq 70120 + end + end + end +end diff --git a/spec/controllers/api/v1/search_controller_spec.rb b/spec/controllers/api/v1/search_controller_spec.rb deleted file mode 100644 index c9e544cc790ac4fe4066b0da229526287ccbc7ab..0000000000000000000000000000000000000000 --- a/spec/controllers/api/v1/search_controller_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::SearchController, type: :controller do - render_views - - let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - it 'returns http success' do - get :index, params: { q: 'test' } - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb index 8bc3b0c6718bb85cb5a40928906b8e552c2877bf..9ff5fcd3b54b46fbe7253c9de6a904223cc3170f 100644 --- a/spec/controllers/api/v1/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/statuses_controller_spec.rb @@ -91,13 +91,6 @@ RSpec.describe Api::V1::StatusesController, type: :controller do expect(response).to have_http_status(404) end end - - describe 'GET #card' do - it 'returns http unautharized' do - get :card, params: { id: status.id } - expect(response).to have_http_status(404) - end - end end context 'with a public status' do @@ -120,13 +113,6 @@ RSpec.describe Api::V1::StatusesController, type: :controller do expect(response).to have_http_status(200) end end - - describe 'GET #card' do - it 'returns http success' do - get :card, params: { id: status.id } - expect(response).to have_http_status(200) - end - end end end end diff --git a/spec/controllers/api/v1/timelines/direct_controller_spec.rb b/spec/controllers/api/v1/timelines/direct_controller_spec.rb deleted file mode 100644 index a22c2cbea598b386671e8037c2e46b595d012718..0000000000000000000000000000000000000000 --- a/spec/controllers/api/v1/timelines/direct_controller_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::Timelines::DirectController, type: :controller do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') } - - describe 'GET #show' do - it 'returns 200' do - allow(controller).to receive(:doorkeeper_token) { token } - get :show - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 33cc7108716d46f9adaf7ca1dfa7a291fadea746..da4a794cddc30d94adcdbc8c200e2cf01173987b 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -110,6 +110,7 @@ describe ApplicationController, type: :controller do sign_in current_user allow(Setting).to receive(:[]).with('theme').and_return 'contrast' + allow(Setting).to receive(:[]).with('noindex').and_return false expect(controller.view_context.current_theme).to eq 'contrast' end @@ -187,10 +188,10 @@ describe ApplicationController, type: :controller do expect(response).to have_http_status(200) end - it 'returns http 403 if user who signed in is suspended' do + it 'redirects to account status page' do sign_in(Fabricate(:user, account: Fabricate(:account, suspended: true))) get 'success' - expect(response).to have_http_status(403) + expect(response).to redirect_to(edit_user_registration_path) end end @@ -360,9 +361,5 @@ describe ApplicationController, type: :controller do context 'Status' do include_examples 'cacheable', :status, Status end - - context 'StreamEntry' do - include_examples 'receives :with_includes', :stream_entry, StreamEntry - end end end diff --git a/spec/controllers/auth/challenges_controller_spec.rb b/spec/controllers/auth/challenges_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..2a6ca301ef702c246a988244182f5acc506c7257 --- /dev/null +++ b/spec/controllers/auth/challenges_controller_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Auth::ChallengesController, type: :controller do + render_views + + let(:password) { 'foobar12345' } + let(:user) { Fabricate(:user, password: password) } + + before do + sign_in user + end + + describe 'POST #create' do + let(:return_to) { edit_user_registration_path } + + context 'with correct password' do + before { post :create, params: { form_challenge: { return_to: return_to, current_password: password } } } + + it 'redirects back' do + expect(response).to redirect_to(return_to) + end + + it 'sets session' do + expect(session[:challenge_passed_at]).to_not be_nil + end + end + + context 'with incorrect password' do + before { post :create, params: { form_challenge: { return_to: return_to, current_password: 'hhfggjjd562' } } } + + it 'renders challenge' do + expect(response).to render_template('auth/challenges/new') + end + + it 'displays error' do + expect(response.body).to include 'Invalid password' + end + + it 'does not set session' do + expect(session[:challenge_passed_at]).to be_nil + end + end + end +end diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index e9a471fc5a9e2700beb7f462526bac3b0adbbfb7..0b6b74ff902c9f45c7915c1803ee05bc65d06aa2 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -50,45 +50,4 @@ describe Auth::ConfirmationsController, type: :controller do end end end - - describe 'GET #finish_signup' do - subject { get :finish_signup } - - let(:user) { Fabricate(:user) } - before do - sign_in user, scope: :user - @request.env['devise.mapping'] = Devise.mappings[:user] - end - - it 'renders finish_signup' do - is_expected.to render_template :finish_signup - expect(assigns(:user)).to have_attributes id: user.id - end - end - - describe 'PATCH #finish_signup' do - subject { patch :finish_signup, params: { user: { email: email } } } - - let(:user) { Fabricate(:user) } - before do - sign_in user, scope: :user - @request.env['devise.mapping'] = Devise.mappings[:user] - end - - context 'when email is valid' do - let(:email) { 'new_' + user.email } - - it 'redirects to root_path' do - is_expected.to redirect_to root_path - end - end - - context 'when email is invalid' do - let(:email) { '' } - - it 'renders finish_signup' do - is_expected.to render_template :finish_signup - end - end - end end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index a4337039e154f6c7993a9c6535f646dc7616abc5..3e11b34b5398fa7d155ac2c81f6a7d9207846366 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -46,6 +46,15 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :update expect(response).to have_http_status(200) end + + context 'when suspended' do + it 'returns http forbidden' do + request.env["devise.mapping"] = Devise.mappings[:user] + sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user) + post :update + expect(response).to have_http_status(403) + end + end end describe 'GET #new' do @@ -94,9 +103,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -120,9 +129,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -148,9 +157,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do @@ -176,9 +185,9 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code } } end - it 'redirects to login page' do + it 'redirects to setup' do subject - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to auth_setup_path end it 'creates user' do diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 71fcc1a6e3d2a65b63a313455436446d876670fc..1950c173af33595a1b18d9f9d452b54229aadffc 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -5,11 +5,11 @@ require 'rails_helper' RSpec.describe Auth::SessionsController, type: :controller do render_views - describe 'GET #new' do - before do - request.env['devise.mapping'] = Devise.mappings[:user] - end + before do + request.env['devise.mapping'] = Devise.mappings[:user] + end + describe 'GET #new' do it 'returns http success' do get :new expect(response).to have_http_status(200) @@ -19,10 +19,6 @@ RSpec.describe Auth::SessionsController, type: :controller do describe 'DELETE #destroy' do let(:user) { Fabricate(:user) } - before do - request.env['devise.mapping'] = Devise.mappings[:user] - end - context 'with a regular user' do it 'redirects to home after sign out' do sign_in(user, scope: :user) @@ -51,10 +47,6 @@ RSpec.describe Auth::SessionsController, type: :controller do end describe 'POST #create' do - before do - request.env['devise.mapping'] = Devise.mappings[:user] - end - context 'using PAM authentication', if: ENV['PAM_ENABLED'] == 'true' do context 'using a valid password' do before do @@ -88,7 +80,7 @@ RSpec.describe Auth::SessionsController, type: :controller do let(:user) do account = Fabricate.build(:account, username: 'pam_user1') account.save!(validate: false) - user = Fabricate(:user, email: 'pam@example.com', password: nil, account: account) + user = Fabricate(:user, email: 'pam@example.com', password: nil, account: account, external: true) user end @@ -160,8 +152,8 @@ RSpec.describe Auth::SessionsController, type: :controller do let(:unconfirmed_user) { user.tap { |u| u.update!(confirmed_at: nil) } } let(:accept_language) { 'fr' } - it 'shows a translated login error' do - expect(flash[:alert]).to eq(I18n.t('devise.failure.unconfirmed', locale: accept_language)) + it 'redirects to home' do + expect(response).to redirect_to(root_path) end end @@ -191,11 +183,11 @@ RSpec.describe Auth::SessionsController, type: :controller do end context 'using two-factor authentication' do - let(:user) do - Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', - otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + let!(:user) do + Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) end - let(:recovery_codes) do + + let!(:recovery_codes) do codes = user.generate_otp_backup_codes! user.save return codes diff --git a/spec/controllers/concerns/account_controller_concern_spec.rb b/spec/controllers/concerns/account_controller_concern_spec.rb index ea2b4a2a1deb1d62da709029b1a6ae95d48bc806..7ea214a7d3fb5fd243e1bfdc572d4527d834d14a 100644 --- a/spec/controllers/concerns/account_controller_concern_spec.rb +++ b/spec/controllers/concerns/account_controller_concern_spec.rb @@ -41,7 +41,7 @@ describe ApplicationController, type: :controller do it 'sets link headers' do account = Fabricate(:account, username: 'username', user: Fabricate(:user)) get 'success', params: { account_username: 'username' } - expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/xrd+xml", <http://test.host/users/username.atom>; rel="alternate"; type="application/atom+xml", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"' + expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/jrd+json", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"' end it 'returns http success' do diff --git a/spec/controllers/concerns/challengable_concern_spec.rb b/spec/controllers/concerns/challengable_concern_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..4db3b740db23ab775a1c27645327e2f67671f7ed --- /dev/null +++ b/spec/controllers/concerns/challengable_concern_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ChallengableConcern, type: :controller do + controller(ApplicationController) do + include ChallengableConcern + + before_action :require_challenge! + + def foo + render plain: 'foo' + end + + def bar + render plain: 'bar' + end + end + + before do + routes.draw do + get 'foo' => 'anonymous#foo' + post 'bar' => 'anonymous#bar' + end + end + + context 'with a no-password user' do + let(:user) { Fabricate(:user, external: true, password: nil) } + + before do + sign_in user + end + + context 'for GET requests' do + before { get :foo } + + it 'does not ask for password' do + expect(response.body).to eq 'foo' + end + end + + context 'for POST requests' do + before { post :bar } + + it 'does not ask for password' do + expect(response.body).to eq 'bar' + end + end + end + + context 'with recent challenge in session' do + let(:password) { 'foobar12345' } + let(:user) { Fabricate(:user, password: password) } + + before do + sign_in user + end + + context 'for GET requests' do + before { get :foo, session: { challenge_passed_at: Time.now.utc } } + + it 'does not ask for password' do + expect(response.body).to eq 'foo' + end + end + + context 'for POST requests' do + before { post :bar, session: { challenge_passed_at: Time.now.utc } } + + it 'does not ask for password' do + expect(response.body).to eq 'bar' + end + end + end + + context 'with a password user' do + let(:password) { 'foobar12345' } + let(:user) { Fabricate(:user, password: password) } + + before do + sign_in user + end + + context 'for GET requests' do + before { get :foo } + + it 'renders challenge' do + expect(response).to render_template('auth/challenges/new') + end + + # See Auth::ChallengesControllerSpec + end + + context 'for POST requests' do + before { post :bar } + + it 'renders challenge' do + expect(response).to render_template('auth/challenges/new') + end + + it 'accepts correct password' do + post :bar, params: { form_challenge: { current_password: password } } + expect(response.body).to eq 'bar' + expect(session[:challenge_passed_at]).to_not be_nil + end + + it 'rejects wrong password' do + post :bar, params: { form_challenge: { current_password: 'dddfff888123' } } + expect(response.body).to render_template('auth/challenges/new') + expect(session[:challenge_passed_at]).to be_nil + end + end + end +end diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb index 72069009716267e8b3b086f36c8c9b131705782a..1fa19f54d78df7d69b5d0b71268e1a3e492d30b5 100644 --- a/spec/controllers/concerns/signature_verification_spec.rb +++ b/spec/controllers/concerns/signature_verification_spec.rb @@ -38,7 +38,7 @@ describe ApplicationController, type: :controller do end context 'with signature header' do - let!(:author) { Fabricate(:account) } + let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') } context 'without body' do before do diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb index f43cf0c27ec510ab3d06d736f819430815ee73ef..941f1dd9146cb8ba461cb6172ee1eed466a659bc 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/home_controller_spec.rb @@ -27,16 +27,6 @@ RSpec.describe HomeController, type: :controller do subject expect(assigns(:body_classes)).to eq 'app-body' end - - it 'assigns @initial_state_json' do - subject - initial_state_json = json_str_to_hash(assigns(:initial_state_json)) - expect(initial_state_json[:meta]).to_not be_nil - expect(initial_state_json[:compose]).to_not be_nil - expect(initial_state_json[:accounts]).to_not be_nil - expect(initial_state_json[:settings]).to_not be_nil - expect(initial_state_json[:media_attachments]).to_not be_nil - end end end end diff --git a/spec/controllers/remote_follow_controller_spec.rb b/spec/controllers/remote_follow_controller_spec.rb index 5088c2e656bf8b508801cf4195de762cc07bb70b..d79dd294952cd55baccad59ffdbcc45d932b172b 100644 --- a/spec/controllers/remote_follow_controller_spec.rb +++ b/spec/controllers/remote_follow_controller_spec.rb @@ -66,9 +66,7 @@ describe RemoteFollowController do end it 'redirects to the remote location' do - address = "http://example.com/follow_me?acct=test_user%40#{Rails.configuration.x.local_domain}" - - expect(response).to redirect_to(address) + expect(response).to redirect_to("http://example.com/follow_me?acct=https%3A%2F%2F#{Rails.configuration.x.local_domain}%2Fusers%2Ftest_user") end end end diff --git a/spec/controllers/remote_unfollows_controller_spec.rb b/spec/controllers/remote_unfollows_controller_spec.rb deleted file mode 100644 index a1a55ede0b19906a74904fe5b5566409df7cd154..0000000000000000000000000000000000000000 --- a/spec/controllers/remote_unfollows_controller_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe RemoteUnfollowsController do - render_views - - describe '#create' do - subject { post :create, params: { acct: acct } } - - let(:current_user) { Fabricate(:user, account: current_account) } - let(:current_account) { Fabricate(:account) } - let(:remote_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } - before do - sign_in current_user - current_account.follow!(remote_account) - stub_request(:post, 'http://example.com/inbox') { { status: 200 } } - end - - context 'when successfully unfollow remote account' do - let(:acct) { "acct:#{remote_account.username}@#{remote_account.domain}" } - - it do - is_expected.to render_template :success - expect(current_account.following?(remote_account)).to be false - end - end - - context 'when fails to unfollow remote account' do - let(:acct) { "acct:#{remote_account.username + '_test'}@#{remote_account.domain}" } - - it do - is_expected.to render_template :error - expect(current_account.following?(remote_account)).to be true - end - end - end -end diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index 35fd64e9b9805329bf5a7af97bbaac10d2ceb027..996872efd1af639726dc957e2981fd7787559c7d 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -15,6 +15,15 @@ describe Settings::DeletesController do get :show expect(response).to have_http_status(200) end + + context 'when suspended' do + let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) } + + it 'returns http forbidden' do + get :show + expect(response).to have_http_status(403) + end + end end context 'when not signed in' do @@ -49,6 +58,14 @@ describe Settings::DeletesController do it 'marks account as suspended' do expect(user.account.reload).to be_suspended end + + context 'when suspended' do + let(:user) { Fabricate(:user, account_attributes: { username: 'alice', suspended_at: Time.now.utc }) } + + it 'returns http forbidden' do + expect(response).to have_http_status(403) + end + end end context 'with incorrect password' do diff --git a/spec/controllers/settings/identity_proofs_controller_spec.rb b/spec/controllers/settings/identity_proofs_controller_spec.rb index 2a0f91088a9bbc9df92f0cb9774ae3b71367c6b6..261e980d4bec766bc330421b1aaba661c07c1e73 100644 --- a/spec/controllers/settings/identity_proofs_controller_spec.rb +++ b/spec/controllers/settings/identity_proofs_controller_spec.rb @@ -8,8 +8,8 @@ describe Settings::IdentityProofsController do let(:valid_token) { '1'*66 } let(:kbname) { 'kbuser' } let(:provider) { 'keybase' } - let(:findable_id) { Faker::Number.number(5) } - let(:unfindable_id) { Faker::Number.number(5) } + let(:findable_id) { Faker::Number.number(digits: 5) } + let(:unfindable_id) { Faker::Number.number(digits: 5) } let(:new_proof_params) do { provider: provider, provider_username: kbname, token: valid_token, username: user.account.username } end diff --git a/spec/controllers/settings/migrations_controller_spec.rb b/spec/controllers/settings/migrations_controller_spec.rb index 4d814a45e2977013e2042f25f5b8d8a48a24715c..36e4ba86e467af182843ce8882dac4614268d728 100644 --- a/spec/controllers/settings/migrations_controller_spec.rb +++ b/spec/controllers/settings/migrations_controller_spec.rb @@ -21,6 +21,7 @@ describe Settings::MigrationsController do let(:user) { Fabricate(:user, account: account) } let(:account) { Fabricate(:account, moved_to_account: moved_to_account) } + before { sign_in user, scope: :user } context 'when user does not have moved to account' do @@ -32,7 +33,7 @@ describe Settings::MigrationsController do end end - context 'when user does not have moved to account' do + context 'when user has a moved to account' do let(:moved_to_account) { Fabricate(:account) } it 'renders show page' do @@ -43,21 +44,22 @@ describe Settings::MigrationsController do end end - describe 'PUT #update' do + describe 'POST #create' do context 'when user is not sign in' do - subject { put :update } + subject { post :create } it_behaves_like 'authenticate user' end context 'when user is sign in' do - subject { put :update, params: { migration: { acct: acct } } } + subject { post :create, params: { account_migration: { acct: acct, current_password: '12345678' } } } + + let(:user) { Fabricate(:user, password: '12345678') } - let(:user) { Fabricate(:user) } before { sign_in user, scope: :user } context 'when migration account is changed' do - let(:acct) { Fabricate(:account) } + let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) } it 'updates moved to account' do is_expected.to redirect_to settings_migration_path diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index 478f2458552a71f7d7e575ec3b7adcfa379ca77f..336f131279fc11a61269c69cd1463da922b8aa86 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -24,7 +24,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do context 'when signed in' do subject do sign_in user, scope: :user - get :new + get :new, session: { challenge_passed_at: Time.now.utc } end include_examples 'renders :new' @@ -37,7 +37,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do it 'redirects if user do not have otp_secret' do sign_in user_without_otp_secret, scope: :user - get :new + get :new, session: { challenge_passed_at: Time.now.utc } expect(response).to redirect_to('/settings/two_factor_authentication') end end @@ -50,7 +50,8 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do describe 'when form_two_factor_confirmation parameter is not provided' do it 'raises ActionController::ParameterMissing' do - expect { post :create, params: {} }.to raise_error(ActionController::ParameterMissing) + post :create, params: {}, session: { challenge_passed_at: Time.now.utc } + expect(response).to have_http_status(400) end end @@ -67,7 +68,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do true end - post :create, params: { form_two_factor_confirmation: { code: '123456' } } + post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, session: { challenge_passed_at: Time.now.utc } expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled' @@ -84,7 +85,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do false end - post :create, params: { form_two_factor_confirmation: { code: '123456' } } + post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, session: { challenge_passed_at: Time.now.utc } end it 'renders the new view' do @@ -98,7 +99,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do context 'when not signed in' do it 'redirects if not signed in' do - post :create, params: { form_two_factor_confirmation: { code: '123456' } } + post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } } expect(response).to redirect_to('/auth/sign_in') end end diff --git a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb index c04760e535efbe9c13e12c5f2f161cf0b63fd804..630cec428e7264298987568fa30f72a7b98711d9 100644 --- a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb @@ -15,7 +15,7 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do end sign_in user, scope: :user - post :create + post :create, session: { challenge_passed_at: Time.now.utc } expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Recovery codes successfully regenerated' diff --git a/spec/controllers/settings/two_factor_authentications_controller_spec.rb b/spec/controllers/settings/two_factor_authentications_controller_spec.rb index 9f27222ad37c2a55a2b3026388f40de84d4aacca..9df9763fd33bd848db890502e20d45da159b848e 100644 --- a/spec/controllers/settings/two_factor_authentications_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentications_controller_spec.rb @@ -58,7 +58,7 @@ describe Settings::TwoFactorAuthenticationsController do describe 'when creation succeeds' do it 'updates user secret' do before = user.otp_secret - post :create + post :create, session: { challenge_passed_at: Time.now.utc } expect(user.reload.otp_secret).not_to eq(before) expect(response).to redirect_to(new_settings_two_factor_authentication_confirmation_path) @@ -91,7 +91,7 @@ describe Settings::TwoFactorAuthenticationsController do true end - post :destroy, params: { form_two_factor_confirmation: { code: '123456' } } + post :destroy, params: { form_two_factor_confirmation: { otp_attempt: '123456' } } expect(response).to redirect_to(settings_two_factor_authentication_path) user.reload @@ -105,14 +105,15 @@ describe Settings::TwoFactorAuthenticationsController do false end - post :destroy, params: { form_two_factor_confirmation: { code: '057772' } } + post :destroy, params: { form_two_factor_confirmation: { otp_attempt: '057772' } } user.reload expect(user.otp_required_for_login).to eq(true) end it 'raises ActionController::ParameterMissing if code is missing' do - expect { post :destroy }.to raise_error(ActionController::ParameterMissing) + post :destroy + expect(response).to have_http_status(400) end end diff --git a/spec/controllers/shares_controller_spec.rb b/spec/controllers/shares_controller_spec.rb index a74e9af561cc66b81f26079db1cbbc60bc611023..d6de3016ae47c3a5234b70a9a2447c285b896ba5 100644 --- a/spec/controllers/shares_controller_spec.rb +++ b/spec/controllers/shares_controller_spec.rb @@ -7,15 +7,12 @@ describe SharesController do before { sign_in user } describe 'GTE #show' do - subject(:initial_state_json) { JSON.parse(assigns(:initial_state_json), symbolize_names: true) } subject(:body_classes) { assigns(:body_classes) } before { get :show, params: { title: 'test title', text: 'test text', url: 'url1 url2' } } - it 'assigns json' do + it 'returns http success' do expect(response).to have_http_status :ok - expect(initial_state_json[:compose][:text]).to eq 'test title test text url1 url2' - expect(initial_state_json[:meta][:me]).to eq user.account.id.to_s expect(body_classes).to eq 'modal-layout compose-standalone' end end diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 1bb6636c603e8827e4f3e5594fdd01dbc30bd3a3..6905dae10e3b12156b0268f0c8fe170e13cb1237 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -55,18 +55,6 @@ describe StatusesController do expect(assigns(:status)).to eq status end - it 'assigns @stream_entry' do - status = Fabricate(:status) - get :show, params: { account_username: status.account.username, id: status.id } - expect(assigns(:stream_entry)).to eq status.stream_entry - end - - it 'assigns @type' do - status = Fabricate(:status) - get :show, params: { account_username: status.account.username, id: status.id } - expect(assigns(:type)).to eq 'status' - end - it 'assigns @ancestors for ancestors of the status if it is a reply' do ancestor = Fabricate(:status) status = Fabricate(:status, in_reply_to_id: ancestor.id) @@ -104,7 +92,7 @@ describe StatusesController do end it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do - stub_const 'StatusesController::DESCENDANTS_LIMIT', 1 + stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1 status = Fabricate(:status) child = Fabricate(:status, in_reply_to_id: status.id) @@ -115,7 +103,7 @@ describe StatusesController do end it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do - stub_const 'StatusesController::DESCENDANTS_DEPTH_LIMIT', 2 + stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2 status = Fabricate(:status) child0 = Fabricate(:status, in_reply_to_id: status.id) child1 = Fabricate(:status, in_reply_to_id: child0.id) @@ -135,10 +123,10 @@ describe StatusesController do expect(response).to have_http_status(200) end - it 'renders stream_entries/show' do + it 'renders statuses/show' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.id } - expect(response).to render_template 'stream_entries/show' + expect(response).to render_template 'statuses/show' end end end diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb deleted file mode 100644 index eb7fdf9d7823d284c21ccfde3b9ad4248ba280dc..0000000000000000000000000000000000000000 --- a/spec/controllers/stream_entries_controller_spec.rb +++ /dev/null @@ -1,95 +0,0 @@ -require 'rails_helper' - -RSpec.describe StreamEntriesController, type: :controller do - render_views - - shared_examples 'before_action' do |route| - context 'when account is not suspended and stream_entry is available' do - it 'assigns instance variables' do - status = Fabricate(:status) - - get route, params: { account_username: status.account.username, id: status.stream_entry.id } - - expect(assigns(:account)).to eq status.account - expect(assigns(:stream_entry)).to eq status.stream_entry - expect(assigns(:type)).to eq 'status' - end - - it 'sets Link headers' do - alice = Fabricate(:account, username: 'alice') - status = Fabricate(:status, account: alice) - - get route, params: { account_username: alice.username, id: status.stream_entry.id } - - expect(response.headers['Link'].to_s).to eq "<http://test.host/users/alice/updates/#{status.stream_entry.id}.atom>; rel=\"alternate\"; type=\"application/atom+xml\", <https://cb6e6126.ngrok.io/users/alice/statuses/#{status.id}>; rel=\"alternate\"; type=\"application/activity+json\"" - end - end - - context 'when account is suspended' do - it 'returns http status 410' do - account = Fabricate(:account, suspended: true) - status = Fabricate(:status, account: account) - - get route, params: { account_username: account.username, id: status.stream_entry.id } - - expect(response).to have_http_status(410) - end - end - - context 'when activity is nil' do - it 'raises ActiveRecord::RecordNotFound' do - account = Fabricate(:account) - stream_entry = Fabricate.build(:stream_entry, account: account, activity: nil, activity_type: 'Status') - stream_entry.save!(validate: false) - - get route, params: { account_username: account.username, id: stream_entry.id } - - expect(response).to have_http_status(404) - end - end - - context 'when it is hidden and it is not permitted' do - it 'raises ActiveRecord::RecordNotFound' do - status = Fabricate(:status) - user = Fabricate(:user) - status.account.block!(user.account) - status.stream_entry.update!(hidden: true) - - sign_in(user) - get route, params: { account_username: status.account.username, id: status.stream_entry.id } - - expect(response).to have_http_status(404) - end - end - end - - describe 'GET #show' do - include_examples 'before_action', :show - - it 'redirects to status page' do - status = Fabricate(:status) - - get :show, params: { account_username: status.account.username, id: status.stream_entry.id } - - expect(response).to redirect_to(short_account_status_url(status.account, status)) - end - - it 'returns http success with Atom' do - status = Fabricate(:status) - get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom' - expect(response).to have_http_status(200) - end - end - - describe 'GET #embed' do - include_examples 'before_action', :embed - - it 'redirects to new embed page' do - status = Fabricate(:status) - - get :embed, params: { account_username: status.account.username, id: status.stream_entry.id } - - expect(response).to redirect_to(embed_short_account_status_url(status.account, status)) - end - end -end diff --git a/spec/controllers/well_known/nodeinfo_controller_spec.rb b/spec/controllers/well_known/nodeinfo_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..12e1fa4159ce0113fadf73d27e19ba9620dd8c60 --- /dev/null +++ b/spec/controllers/well_known/nodeinfo_controller_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper' + +describe WellKnown::NodeInfoController, type: :controller do + render_views + + describe 'GET #index' do + it 'returns json document pointing to node info' do + get :index + + expect(response).to have_http_status(200) + expect(response.content_type).to eq 'application/json' + + json = body_as_json + + expect(json[:links]).to be_an Array + expect(json[:links][0][:rel]).to eq 'http://nodeinfo.diaspora.software/ns/schema/2.0' + expect(json[:links][0][:href]).to include 'nodeinfo/2.0' + end + end + + describe 'GET #show' do + it 'returns json document with node info properties' do + get :show + + expect(response).to have_http_status(200) + expect(response.content_type).to eq 'application/json' + + json = body_as_json + + expect(json[:version]).to eq '2.0' + expect(json[:usage]).to be_a Hash + expect(json[:software]).to be_a Hash + expect(json[:protocols]).to be_an Array + end + end +end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index b05745ea3bf0448536d6aa4f26adc8fa1cd36ce2..20275aa63bc33f958a2e3cfe2575c44800067fad 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -56,17 +56,6 @@ PEM expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') end - it 'returns JSON when account can be found' do - get :show, params: { resource: alice.to_webfinger_s }, format: :xml - - xml = Nokogiri::XML(response.body) - - expect(response).to have_http_status(200) - expect(response.content_type).to eq 'application/xrd+xml' - expect(xml.at_xpath('//xmlns:Subject').content).to eq 'acct:alice@cb6e6126.ngrok.io' - expect(xml.xpath('//xmlns:Alias').map(&:content)).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') - end - it 'returns http not found when account cannot be found' do get :show, params: { resource: 'acct:not@existing.com' }, format: :json diff --git a/spec/fabricators/account_alias_fabricator.rb b/spec/fabricators/account_alias_fabricator.rb new file mode 100644 index 0000000000000000000000000000000000000000..94dde9bb8c1b4c1815edf5a4fd53a255f472bad5 --- /dev/null +++ b/spec/fabricators/account_alias_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:account_alias) do + account + acct 'test@example.com' + uri 'https://example.com/users/test' +end diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index f12464ef3e675a6728d867d4c41735f09c9f0128..ab900c5fa03edbb5499bacf173bdcc440f21b440 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -4,7 +4,7 @@ private_key = keypair.to_pem Fabricator(:account) do transient :suspended, :silenced - username { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } } + username { sequence(:username) { |i| "#{Faker::Internet.user_name(separators: %w(_))}#{i}" } } last_webfingered_at { Time.now.utc } public_key { public_key } private_key { private_key } diff --git a/spec/fabricators/account_identity_proof_fabricator.rb b/spec/fabricators/account_identity_proof_fabricator.rb index 94f40dfd6b7cdf31f4e7e88c8df51588cc980a4b..7b932fa968792347974219f1b28536f67f8194ce 100644 --- a/spec/fabricators/account_identity_proof_fabricator.rb +++ b/spec/fabricators/account_identity_proof_fabricator.rb @@ -1,7 +1,7 @@ Fabricator(:account_identity_proof) do account provider 'keybase' - provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(15)}" } } + provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(number: 15)}" } } token { sequence(:token) { |i| "#{i}#{Faker::Crypto.sha1()*2}"[0..65] } } verified false live false diff --git a/spec/fabricators/account_migration_fabricator.rb b/spec/fabricators/account_migration_fabricator.rb new file mode 100644 index 0000000000000000000000000000000000000000..3b3fc20773a0c4ebddecc6c4197038596e0a5a3b --- /dev/null +++ b/spec/fabricators/account_migration_fabricator.rb @@ -0,0 +1,6 @@ +Fabricator(:account_migration) do + account + target_account + followers_count 1234 + acct 'test@example.com' +end diff --git a/spec/fabricators/custom_emoji_category_fabricator.rb b/spec/fabricators/custom_emoji_category_fabricator.rb new file mode 100644 index 0000000000000000000000000000000000000000..f593b95ed444d7e7d01112dc605febfe8652c72e --- /dev/null +++ b/spec/fabricators/custom_emoji_category_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:custom_emoji_category) do + name "MyString" +end diff --git a/spec/fabricators/domain_allow_fabricator.rb b/spec/fabricators/domain_allow_fabricator.rb new file mode 100644 index 0000000000000000000000000000000000000000..6226b1e20b265e8efe87f2a13693c5ffc3f72aad --- /dev/null +++ b/spec/fabricators/domain_allow_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator(:domain_allow) do + domain "MyString" +end diff --git a/spec/fabricators/marker_fabricator.rb b/spec/fabricators/marker_fabricator.rb new file mode 100644 index 0000000000000000000000000000000000000000..0c94150e0dc651b90db28d6d3eda4a71babb94b3 --- /dev/null +++ b/spec/fabricators/marker_fabricator.rb @@ -0,0 +1,6 @@ +Fabricator(:marker) do + user + timeline 'home' + last_read_id 0 + lock_version 0 +end diff --git a/spec/fabricators/stream_entry_fabricator.rb b/spec/fabricators/stream_entry_fabricator.rb deleted file mode 100644 index f33822c7c593a520062582014034b7c3ba2ff979..0000000000000000000000000000000000000000 --- a/spec/fabricators/stream_entry_fabricator.rb +++ /dev/null @@ -1,5 +0,0 @@ -Fabricator(:stream_entry) do - account - activity { Fabricate(:status) } - hidden { [true, false].sample } -end diff --git a/spec/fabricators/subscription_fabricator.rb b/spec/fabricators/subscription_fabricator.rb deleted file mode 100644 index 347dab5dfdbbc2cf4e38cc1fd1deec9f3bf2e390..0000000000000000000000000000000000000000 --- a/spec/fabricators/subscription_fabricator.rb +++ /dev/null @@ -1,7 +0,0 @@ -Fabricator(:subscription) do - account - callback_url "http://example.com/callback" - secret "foobar" - expires_at "2016-11-28 11:30:07" - confirmed false -end diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb index 53a1f9b126daa13f11bf7347890667163fc8e19d..f6c26cd0f13b869dfb1ecc3e4a0bb662c0b59fee 100644 --- a/spec/features/log_in_spec.rb +++ b/spec/features/log_in_spec.rb @@ -31,12 +31,12 @@ feature "Log in" do context do given(:confirmed_at) { nil } - scenario "A unconfirmed user is not able to log in" do + scenario "A unconfirmed user is able to log in" do fill_in "user_email", with: email fill_in "user_password", with: password click_on I18n.t('auth.login') - is_expected.to have_css(".flash-message", text: failure_message("unconfirmed")) + is_expected.to have_css("div.admin-wrapper") end end diff --git a/spec/fixtures/requests/webfinger.txt b/spec/fixtures/requests/webfinger.txt index edb8a2dbb563d7233f817b5d5fd52db12a2a8b15..f337ecae6f150131905920e4b96943e8785c3a96 100644 --- a/spec/fixtures/requests/webfinger.txt +++ b/spec/fixtures/requests/webfinger.txt @@ -8,4 +8,4 @@ Access-Control-Allow-Origin: * Vary: Accept-Encoding,Cookie Strict-Transport-Security: max-age=31536000; includeSubdomains; -{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]} \ No newline at end of file +{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]} diff --git a/spec/helpers/admin/account_moderation_notes_helper_spec.rb b/spec/helpers/admin/account_moderation_notes_helper_spec.rb index c07f6c4b886b37492c52b8ec0376ccc7ec380949..ddfe8b46f9f1fbd7cccee8266e75f236e0116111 100644 --- a/spec/helpers/admin/account_moderation_notes_helper_spec.rb +++ b/spec/helpers/admin/account_moderation_notes_helper_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do - include StreamEntriesHelper + include StatusesHelper describe '#admin_account_link_to' do context 'account is nil' do diff --git a/spec/helpers/stream_entries_helper_spec.rb b/spec/helpers/statuses_helper_spec.rb similarity index 94% rename from spec/helpers/stream_entries_helper_spec.rb rename to spec/helpers/statuses_helper_spec.rb index 845b9974ea1b5ce7446f7869443fbb3343f96227..510955a2ff5349cd460965ee20c60efcbf8dfa8a 100644 --- a/spec/helpers/stream_entries_helper_spec.rb +++ b/spec/helpers/statuses_helper_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe StreamEntriesHelper, type: :helper do +RSpec.describe StatusesHelper, type: :helper do describe '#display_name' do it 'uses the display name when it exists' do account = Account.new(display_name: "Display", username: "Username") @@ -70,13 +70,13 @@ RSpec.describe StreamEntriesHelper, type: :helper do end def set_not_embedded_view - params[:controller] = "not_#{StreamEntriesHelper::EMBEDDED_CONTROLLER}" - params[:action] = "not_#{StreamEntriesHelper::EMBEDDED_ACTION}" + params[:controller] = "not_#{StatusesHelper::EMBEDDED_CONTROLLER}" + params[:action] = "not_#{StatusesHelper::EMBEDDED_ACTION}" end def set_embedded_view - params[:controller] = StreamEntriesHelper::EMBEDDED_CONTROLLER - params[:action] = StreamEntriesHelper::EMBEDDED_ACTION + params[:controller] = StatusesHelper::EMBEDDED_CONTROLLER + params[:action] = StatusesHelper::EMBEDDED_ACTION end describe '#style_classes' do diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index 6bbacdbe644d2c2f2085651e7cfbe2b6a760373c..05112cc184f2a65aa2437b2aa7276b251f48f711 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -31,6 +31,36 @@ RSpec.describe ActivityPub::Activity::Follow do end end + context 'silenced account following an unlocked account' do + before do + sender.touch(:silenced_at) + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be false + end + + it 'creates a follow request' do + expect(sender.requested?(recipient)).to be true + end + end + + context 'unlocked account muting the sender' do + before do + recipient.mute!(sender) + subject.perform + end + + it 'creates a follow from sender to recipient' do + expect(sender.following?(recipient)).to be true + end + + it 'does not create a follow request' do + expect(sender.requested?(recipient)).to be false + end + end + context 'locked account' do before do recipient.update(locked: true) diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb index fbfc585cf9736ce991dd4b3c071f7f25c101358d..42da2986066417dce5bd7bd3c6f752178f3883da 100644 --- a/spec/lib/activitypub/activity/update_spec.rb +++ b/spec/lib/activitypub/activity/update_spec.rb @@ -19,7 +19,7 @@ RSpec.describe ActivityPub::Activity::Update do end let(:actor_json) do - ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, key_transform: :camel_lower).as_json + ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter).as_json end let(:json) do diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 6d246629e786d3b6dfacc407af4f5bb602ce55bb..1c5c6f0edd3bdd67b396620f959a6ca77a17050b 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -143,12 +143,6 @@ RSpec.describe ActivityPub::TagManager do expect(subject.uri_to_resource(OStatus::TagManager.instance.uri_for(status), Status)).to eq status end - it 'returns the local status for OStatus StreamEntry URL' do - status = Fabricate(:status) - stream_entry_url = account_stream_entry_url(status.account, status.stream_entry) - expect(subject.uri_to_resource(stream_entry_url, Status)).to eq status - end - it 'returns the remote status by matching URI without fragment part' do status = Fabricate(:status, uri: 'https://example.com/123') expect(subject.uri_to_resource('https://example.com/123#456', Status)).to eq status diff --git a/spec/lib/connection_pool/shared_connection_pool_spec.rb b/spec/lib/connection_pool/shared_connection_pool_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1144645580b5ffbaa2db47c0e754a0d1e440365e --- /dev/null +++ b/spec/lib/connection_pool/shared_connection_pool_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ConnectionPool::SharedConnectionPool do + class MiniConnection + attr_reader :site + + def initialize(site) + @site = site + end + end + + subject { described_class.new(size: 5, timeout: 5) { |site| MiniConnection.new(site) } } + + describe '#with' do + it 'runs a block with a connection' do + block_run = false + + subject.with('foo') do |connection| + expect(connection).to be_a MiniConnection + block_run = true + end + + expect(block_run).to be true + end + end +end diff --git a/spec/lib/connection_pool/shared_timed_stack_spec.rb b/spec/lib/connection_pool/shared_timed_stack_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f680c596670fcabe8ff765a9e3357b3a1fd7e1b3 --- /dev/null +++ b/spec/lib/connection_pool/shared_timed_stack_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ConnectionPool::SharedTimedStack do + class MiniConnection + attr_reader :site + + def initialize(site) + @site = site + end + end + + subject { described_class.new(5) { |site| MiniConnection.new(site) } } + + describe '#push' do + it 'keeps the connection in the stack' do + subject.push(MiniConnection.new('foo')) + expect(subject.size).to eq 1 + end + end + + describe '#pop' do + it 'returns a connection' do + expect(subject.pop('foo')).to be_a MiniConnection + end + + it 'returns the same connection that was pushed in' do + connection = MiniConnection.new('foo') + subject.push(connection) + expect(subject.pop('foo')).to be connection + end + + it 'does not create more than maximum amount of connections' do + expect { 6.times { subject.pop('foo', 0) } }.to raise_error Timeout::Error + end + + it 'repurposes a connection for a different site when maximum amount is reached' do + 5.times { subject.push(MiniConnection.new('foo')) } + expect(subject.pop('bar')).to be_a MiniConnection + end + end + + describe '#empty?' do + it 'returns true when no connections on the stack' do + expect(subject.empty?).to be true + end + + it 'returns false when there are connections on the stack' do + subject.push(MiniConnection.new('foo')) + expect(subject.empty?).to be false + end + end + + describe '#size' do + it 'returns the number of connections on the stack' do + 2.times { subject.push(MiniConnection.new('foo')) } + expect(subject.size).to eq 2 + end + end +end diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb deleted file mode 100644 index 891871c1c7fabbabd3ed938348951e87bdd55316..0000000000000000000000000000000000000000 --- a/spec/lib/ostatus/atom_serializer_spec.rb +++ /dev/null @@ -1,1560 +0,0 @@ -require 'rails_helper' - -RSpec.describe OStatus::AtomSerializer do - shared_examples 'follow request salmon' do - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'username') - follow_request = Fabricate(:follow_request, account: account) - - follow_request_salmon = serialize(follow_request) - - expect(follow_request_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with activity type' do - follow_request = Fabricate(:follow_request) - - follow_request_salmon = serialize(follow_request) - - object_type = follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with request_friend type' do - follow_request = Fabricate(:follow_request) - - follow_request_salmon = serialize(follow_request) - - verb = follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:request_friend] - end - - it 'appends activity:object with target account' do - target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') - follow_request = Fabricate(:follow_request, target_account: target_account) - - follow_request_salmon = serialize(follow_request) - - object = follow_request_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain.test/id' - end - end - - shared_examples 'namespaces' do - it 'adds namespaces' do - element = serialize - - expect(element['xmlns']).to eq OStatus::TagManager::XMLNS - expect(element['xmlns:thr']).to eq OStatus::TagManager::THR_XMLNS - expect(element['xmlns:activity']).to eq OStatus::TagManager::AS_XMLNS - expect(element['xmlns:poco']).to eq OStatus::TagManager::POCO_XMLNS - expect(element['xmlns:media']).to eq OStatus::TagManager::MEDIA_XMLNS - expect(element['xmlns:ostatus']).to eq OStatus::TagManager::OS_XMLNS - expect(element['xmlns:mastodon']).to eq OStatus::TagManager::MTDN_XMLNS - end - end - - shared_examples 'no namespaces' do - it 'does not add namespaces' do - expect(serialize['xmlns']).to eq nil - end - end - - shared_examples 'status attributes' do - it 'appends summary element with spoiler text if present' do - status = Fabricate(:status, language: :ca, spoiler_text: 'spoiler text') - - element = serialize(status) - - summary = element.summary - expect(summary['xml:lang']).to eq 'ca' - expect(summary.text).to eq 'spoiler text' - end - - it 'does not append summary element with spoiler text if not present' do - status = Fabricate(:status, spoiler_text: '') - element = serialize(status) - element.nodes.each { |node| expect(node.name).not_to eq 'summary' } - end - - it 'appends content element with formatted status' do - status = Fabricate(:status, language: :ca, text: 'text') - - element = serialize(status) - - content = element.content - expect(content[:type]).to eq 'html' - expect(content['xml:lang']).to eq 'ca' - expect(content.text).to eq '<p>text</p>' - end - - it 'appends link elements for mentioned accounts' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status) - Fabricate(:mention, account: account, status: status) - - element = serialize(status) - - mentioned = element.nodes.find do |node| - node.name == 'link' && - node[:rel] == 'mentioned' && - node['ostatus:object-type'] == OStatus::TagManager::TYPES[:person] - end - - expect(mentioned[:href]).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends link elements for emojis' do - Fabricate(:custom_emoji) - - status = Fabricate(:status, text: ':coolcat:') - element = serialize(status) - emoji = element.nodes.find { |node| node.name == 'link' && node[:rel] == 'emoji' } - - expect(emoji[:name]).to eq 'coolcat' - expect(emoji[:href]).to_not be_blank - end - end - - describe 'render' do - it 'returns XML with emojis' do - element = Ox::Element.new('tag') - element << '💩' - xml = OStatus::AtomSerializer.render(element) - - expect(xml).to eq "<?xml version=\"1.0\"?>\n<tag>💩</tag>\n" - end - - it 'returns XML, stripping invalid characters like \b and \v' do - element = Ox::Element.new('tag') - element << "im l33t\b haxo\b\vr" - xml = OStatus::AtomSerializer.render(element) - - expect(xml).to eq "<?xml version=\"1.0\"?>\n<tag>im l33t haxor</tag>\n" - end - end - - describe '#author' do - context 'when note is present' do - it 'appends poco:note element with note for local account' do - account = Fabricate(:account, domain: nil, note: '<p>note</p>') - - author = OStatus::AtomSerializer.new.author(account) - - note = author.nodes.find { |node| node.name == 'poco:note' } - expect(note.text).to eq '<p>note</p>' - end - - it 'appends poco:note element with tags-stripped note for remote account' do - account = Fabricate(:account, domain: 'remote', note: '<p>note</p>') - - author = OStatus::AtomSerializer.new.author(account) - - note = author.nodes.find { |node| node.name == 'poco:note' } - expect(note.text).to eq 'note' - end - - it 'appends summary element with type attribute and simplified note if present' do - account = Fabricate(:account, note: 'note') - author = OStatus::AtomSerializer.new.author(account) - expect(author.summary.text).to eq '<p>note</p>' - expect(author.summary[:type]).to eq 'html' - end - end - - context 'when note is not present' do - it 'does not append poco:note element' do - account = Fabricate(:account, note: '') - author = OStatus::AtomSerializer.new.author(account) - author.nodes.each { |node| expect(node.name).not_to eq 'poco:note' } - end - - it 'does not append summary element' do - account = Fabricate(:account, note: '') - author = OStatus::AtomSerializer.new.author(account) - author.nodes.each { |node| expect(node.name).not_to eq 'summary' } - end - end - - it 'returns author element' do - account = Fabricate(:account) - author = OStatus::AtomSerializer.new.author(account) - expect(author.name).to eq 'author' - end - - it 'appends activity:object-type element with person type' do - account = Fabricate(:account, domain: nil, username: 'username') - - author = OStatus::AtomSerializer.new.author(account) - - object_type = author.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:person] - end - - it 'appends email element with username and domain for local account' do - account = Fabricate(:account, username: 'username') - author = OStatus::AtomSerializer.new.author(account) - expect(author.email.text).to eq 'username@cb6e6126.ngrok.io' - end - - it 'appends email element with username and domain for remote user' do - account = Fabricate(:account, domain: 'domain', username: 'username') - author = OStatus::AtomSerializer.new.author(account) - expect(author.email.text).to eq 'username@domain' - end - - it 'appends link element for an alternative' do - account = Fabricate(:account, domain: nil, username: 'username') - - author = OStatus::AtomSerializer.new.author(account) - - link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' } - expect(link[:type]).to eq 'text/html' - expect(link[:rel]).to eq 'alternate' - expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/@username' - end - - it 'has link element for avatar if present' do - account = Fabricate(:account, avatar: attachment_fixture('avatar.gif')) - - author = OStatus::AtomSerializer.new.author(account) - - link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'avatar' } - expect(link[:type]).to eq 'image/gif' - expect(link['media:width']).to eq '120' - expect(link['media:height']).to eq '120' - expect(link[:href]).to match /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/avatars\/.+\/original\/avatar.gif/ - end - - it 'does not have link element for avatar if not present' do - account = Fabricate(:account, avatar: nil) - - author = OStatus::AtomSerializer.new.author(account) - - author.nodes.each do |node| - expect(node[:rel]).not_to eq 'avatar' if node.name == 'link' - end - end - - it 'appends link element for header if present' do - account = Fabricate(:account, header: attachment_fixture('avatar.gif')) - - author = OStatus::AtomSerializer.new.author(account) - - link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'header' } - expect(link[:type]).to eq 'image/gif' - expect(link['media:width']).to eq '700' - expect(link['media:height']).to eq '335' - expect(link[:href]).to match /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/headers\/.+\/original\/avatar.gif/ - end - - it 'does not append link element for header if not present' do - account = Fabricate(:account, header: nil) - - author = OStatus::AtomSerializer.new.author(account) - - author.nodes.each do |node| - expect(node[:rel]).not_to eq 'header' if node.name == 'link' - end - end - - it 'appends poco:displayName element with display name if present' do - account = Fabricate(:account, display_name: 'display name') - - author = OStatus::AtomSerializer.new.author(account) - - display_name = author.nodes.find { |node| node.name == 'poco:displayName' } - expect(display_name.text).to eq 'display name' - end - - it 'does not append poco:displayName element with display name if not present' do - account = Fabricate(:account, display_name: '') - author = OStatus::AtomSerializer.new.author(account) - author.nodes.each { |node| expect(node.name).not_to eq 'poco:displayName' } - end - - it "appends mastodon:scope element with 'private' if locked" do - account = Fabricate(:account, locked: true) - - author = OStatus::AtomSerializer.new.author(account) - - scope = author.nodes.find { |node| node.name == 'mastodon:scope' } - expect(scope.text).to eq 'private' - end - - it "appends mastodon:scope element with 'public' if unlocked" do - account = Fabricate(:account, locked: false) - - author = OStatus::AtomSerializer.new.author(account) - - scope = author.nodes.find { |node| node.name == 'mastodon:scope' } - expect(scope.text).to eq 'public' - end - - it 'includes URI' do - account = Fabricate(:account, domain: nil, username: 'username') - - author = OStatus::AtomSerializer.new.author(account) - - expect(author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - expect(author.uri.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'includes username' do - account = Fabricate(:account, username: 'username') - - author = OStatus::AtomSerializer.new.author(account) - - name = author.nodes.find { |node| node.name == 'name' } - username = author.nodes.find { |node| node.name == 'poco:preferredUsername' } - expect(name.text).to eq 'username' - expect(username.text).to eq 'username' - end - end - - describe '#entry' do - shared_examples 'not root' do - include_examples 'no namespaces' do - def serialize - subject - end - end - - it 'does not append author element' do - subject.nodes.each { |node| expect(node.name).not_to eq 'author' } - end - end - - context 'it is root' do - include_examples 'namespaces' do - def serialize - stream_entry = Fabricate(:stream_entry) - OStatus::AtomSerializer.new.entry(stream_entry, true) - end - end - - it 'appends author element' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry, true) - - expect(entry.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - end - - context 'if status is present' do - include_examples 'status attributes' do - def serialize(status) - OStatus::AtomSerializer.new.entry(status.stream_entry, true) - end - end - - it 'appends link element for the public collection if status is publicly visible' do - status = Fabricate(:status, visibility: :public) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - mentioned_person = entry.nodes.find do |node| - node.name == 'link' && - node[:rel] == 'mentioned' && - node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection] - end - expect(mentioned_person[:href]).to eq OStatus::TagManager::COLLECTIONS[:public] - end - - it 'does not append link element for the public collection if status is not publicly visible' do - status = Fabricate(:status, visibility: :private) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - entry.nodes.each do |node| - if node.name == 'link' && - node[:rel] == 'mentioned' && - node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection] - expect(mentioned_collection[:href]).not_to eq OStatus::TagManager::COLLECTIONS[:public] - end - end - end - - it 'appends category elements for tags' do - tag = Fabricate(:tag, name: 'tag') - status = Fabricate(:status, tags: [ tag ]) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - expect(entry.category[:term]).to eq 'tag' - end - - it 'appends link elements for media attachments' do - file = attachment_fixture('attachment.jpg') - media_attachment = Fabricate(:media_attachment, file: file) - status = Fabricate(:status, media_attachments: [ media_attachment ]) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - enclosure = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'enclosure' } - expect(enclosure[:type]).to eq 'image/jpeg' - expect(enclosure[:href]).to match /^https:\/\/cb6e6126.ngrok.io\/system\/media_attachments\/files\/.+\/original\/attachment.jpg$/ - end - - it 'appends mastodon:scope element with visibility' do - status = Fabricate(:status, visibility: :public) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - scope = entry.nodes.find { |node| node.name == 'mastodon:scope' } - expect(scope.text).to eq 'public' - end - - it 'returns element whose rendered view triggers creation when processed' do - remote_account = Account.create!(username: 'username') - remote_status = Fabricate(:status, account: remote_account, created_at: '2000-01-01T00:00:00Z') - - entry = OStatus::AtomSerializer.new.entry(remote_status.stream_entry, true) - entry.nodes.delete_if { |node| node[:type] == 'application/activity+json' } # Remove ActivityPub link to simplify test - xml = OStatus::AtomSerializer.render(entry).gsub('cb6e6126.ngrok.io', 'remote.test') - - remote_status.destroy! - remote_account.destroy! - - account = Account.create!( - domain: 'remote.test', - username: 'username', - last_webfingered_at: Time.now.utc - ) - - ProcessFeedService.new.call(xml, account) - - expect(Status.find_by(uri: "https://remote.test/users/#{remote_status.account.to_param}/statuses/#{remote_status.id}")).to be_instance_of Status - end - end - - context 'if status is not present' do - it 'appends content element saying status is deleted' do - status = Fabricate(:status) - status.destroy! - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - expect(entry.content.text).to eq 'Deleted status' - end - - it 'appends title element saying the status is deleted' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - status.destroy! - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - expect(entry.title.text).to eq 'username deleted status' - end - end - - context 'it is not root' do - let(:stream_entry) { Fabricate(:stream_entry) } - subject { OStatus::AtomSerializer.new.entry(stream_entry, false) } - include_examples 'not root' - end - - context 'without root parameter' do - let(:stream_entry) { Fabricate(:stream_entry) } - subject { OStatus::AtomSerializer.new.entry(stream_entry) } - include_examples 'not root' - end - - it 'returns entry element' do - stream_entry = Fabricate(:stream_entry) - entry = OStatus::AtomSerializer.new.entry(stream_entry) - expect(entry.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - status = Fabricate(:status, reblog_of_id: nil, created_at: '2000-01-01T00:00:00Z') - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - expect(entry.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - end - - it 'appends published element with created date' do - stream_entry = Fabricate(:stream_entry, created_at: '2000-01-01T00:00:00Z') - entry = OStatus::AtomSerializer.new.entry(stream_entry) - expect(entry.published.text).to eq '2000-01-01T00:00:00Z' - end - - it 'appends updated element with updated date' do - stream_entry = Fabricate(:stream_entry, updated_at: '2000-01-01T00:00:00Z') - entry = OStatus::AtomSerializer.new.entry(stream_entry) - expect(entry.updated.text).to eq '2000-01-01T00:00:00Z' - end - - it 'appends title element with status title' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account, reblog_of_id: nil) - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - expect(entry.title.text).to eq 'New status by username' - end - - it 'appends activity:object-type element with object type' do - status = Fabricate(:status) - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - object_type = entry.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:note] - end - - it 'appends activity:verb element with object type' do - status = Fabricate(:status) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - object_type = entry.nodes.find { |node| node.name == 'activity:verb' } - expect(object_type.text).to eq OStatus::TagManager::VERBS[:post] - end - - it 'appends activity:object element with target if present' do - reblogged = Fabricate(:status, created_at: '2000-01-01T00:00:00Z') - reblog = Fabricate(:status, reblog: reblogged) - - entry = OStatus::AtomSerializer.new.entry(reblog.stream_entry) - - object = entry.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{reblogged.account.to_param}/statuses/#{reblogged.id}" - end - - it 'does not append activity:object element if target is not present' do - status = Fabricate(:status, reblog_of_id: nil) - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - entry.nodes.each { |node| expect(node.name).not_to eq 'activity:object' } - end - - it 'appends link element for an alternative' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' } - expect(link[:type]).to eq 'text/html' - expect(link[:href]).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}" - end - - it 'appends link element for itself' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'self' } - expect(link[:type]).to eq 'application/atom+xml' - expect(link[:href]).to eq "https://cb6e6126.ngrok.io/users/username/updates/#{status.stream_entry.id}.atom" - end - - it 'appends thr:in-reply-to element if threaded' do - in_reply_to_status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reblog_of_id: nil) - reply_status = Fabricate(:status, in_reply_to_id: in_reply_to_status.id) - - entry = OStatus::AtomSerializer.new.entry(reply_status.stream_entry) - - in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' } - expect(in_reply_to[:ref]).to eq "https://cb6e6126.ngrok.io/users/#{in_reply_to_status.account.to_param}/statuses/#{in_reply_to_status.id}" - end - - it 'does not append thr:in-reply-to element if not threaded' do - status = Fabricate(:status) - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - entry.nodes.each { |node| expect(node.name).not_to eq 'thr:in-reply-to' } - end - - it 'appends ostatus:conversation if conversation id is present' do - status = Fabricate(:status) - status.conversation.update!(created_at: '2000-01-01T00:00:00Z') - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - conversation = entry.nodes.find { |node| node.name == 'ostatus:conversation' } - expect(conversation[:ref]).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.conversation_id}:objectType=Conversation" - end - - it 'does not append ostatus:conversation if conversation id is not present' do - status = Fabricate.build(:status, conversation_id: nil) - status.save!(validate: false) - - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - - entry.nodes.each { |node| expect(node.name).not_to eq 'ostatus:conversation' } - end - end - - describe '#feed' do - include_examples 'namespaces' do - def serialize - account = Fabricate(:account) - OStatus::AtomSerializer.new.feed(account, []) - end - end - - it 'returns feed element' do - account = Fabricate(:account) - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.name).to eq 'feed' - end - - it 'appends id element with account Atom URL' do - account = Fabricate(:account, username: 'username') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.id.text).to eq 'https://cb6e6126.ngrok.io/users/username.atom' - end - - it 'appends title element with account display name if present' do - account = Fabricate(:account, display_name: 'display name') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.title.text).to eq 'display name' - end - - it 'does not append title element with account username if account display name is not present' do - account = Fabricate(:account, display_name: '', username: 'username') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.title.text).to eq 'username' - end - - it 'appends subtitle element with account note' do - account = Fabricate(:account, note: 'note') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.subtitle.text).to eq 'note' - end - - it 'appends updated element with date account got updated' do - account = Fabricate(:account, updated_at: '2000-01-01T00:00:00Z') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.updated.text).to eq '2000-01-01T00:00:00Z' - end - - it 'appends logo element with full asset URL for original account avatar' do - account = Fabricate(:account, avatar: attachment_fixture('avatar.gif')) - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.logo.text).to match /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/avatars\/.+\/original\/avatar.gif/ - end - - it 'appends author element' do - account = Fabricate(:account, username: 'username') - feed = OStatus::AtomSerializer.new.feed(account, []) - expect(feed.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends link element for an alternative' do - account = Fabricate(:account, username: 'username') - - feed = OStatus::AtomSerializer.new.feed(account, []) - - link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' } - expect(link[:type]).to eq 'text/html' - expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/@username' - end - - it 'appends link element for itself' do - account = Fabricate(:account, username: 'username') - - feed = OStatus::AtomSerializer.new.feed(account, []) - - link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'self' } - expect(link[:type]).to eq 'application/atom+xml' - expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/users/username.atom' - end - - it 'appends link element for the next if it has 20 stream entries' do - account = Fabricate(:account, username: 'username') - stream_entry = Fabricate(:stream_entry) - - feed = OStatus::AtomSerializer.new.feed(account, Array.new(20, stream_entry)) - - link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'next' } - expect(link[:type]).to eq 'application/atom+xml' - expect(link[:href]).to eq "https://cb6e6126.ngrok.io/users/username.atom?max_id=#{stream_entry.id}" - end - - it 'does not append link element for the next if it does not have 20 stream entries' do - account = Fabricate(:account, username: 'username') - - feed = OStatus::AtomSerializer.new.feed(account, []) - - feed.nodes.each do |node| - expect(node[:rel]).not_to eq 'next' if node.name == 'link' - end - end - - it 'appends link element for hub' do - account = Fabricate(:account, username: 'username') - - feed = OStatus::AtomSerializer.new.feed(account, []) - - link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'hub' } - expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/api/push' - end - - it 'appends link element for Salmon' do - account = Fabricate(:account, username: 'username') - - feed = OStatus::AtomSerializer.new.feed(account, []) - - link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'salmon' } - expect(link[:href]).to start_with 'https://cb6e6126.ngrok.io/api/salmon/' - end - - it 'appends stream entries' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - feed = OStatus::AtomSerializer.new.feed(account, [status.stream_entry]) - - expect(feed.entry.title.text).to eq 'New status by username' - end - end - - describe '#block_salmon' do - include_examples 'namespaces' do - def serialize - block = Fabricate(:block) - OStatus::AtomSerializer.new.block_salmon(block) - end - end - - it 'returns entry element' do - block = Fabricate(:block) - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - expect(block_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - block = Fabricate(:block) - - time_before = Time.zone.now - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - time_after = Time.zone.now - - expect(block_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) - .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) - ) - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - block = Fabricate(:block, account: account, target_account: target_account) - - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - - expect(block_salmon.title.text).to eq 'account no longer wishes to interact with target_account@remote' - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'account') - block = Fabricate(:block, account: account) - - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - - expect(block_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/account' - end - - it 'appends activity:object-type element with activity type' do - block = Fabricate(:block) - - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - - object_type = block_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with block' do - block = Fabricate(:block) - - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - - verb = block_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:block] - end - - it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') - block = Fabricate(:block, target_account: target_account) - - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - - object = block_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain.test/id' - end - - it 'returns element whose rendered view triggers block when processed' do - block = Fabricate(:block) - block_salmon = OStatus::AtomSerializer.new.block_salmon(block) - xml = OStatus::AtomSerializer.render(block_salmon) - envelope = OStatus2::Salmon.new.pack(xml, block.account.keypair) - block.destroy! - - ProcessInteractionService.new.call(envelope, block.target_account) - - expect(block.account.blocking?(block.target_account)).to be true - end - end - - describe '#unblock_salmon' do - include_examples 'namespaces' do - def serialize - block = Fabricate(:block) - OStatus::AtomSerializer.new.unblock_salmon(block) - end - end - - it 'returns entry element' do - block = Fabricate(:block) - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - expect(unblock_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - block = Fabricate(:block) - - time_before = Time.zone.now - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - time_after = Time.zone.now - - expect(unblock_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block')) - .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block'))) - ) - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - block = Fabricate(:block, account: account, target_account: target_account) - - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - - expect(unblock_salmon.title.text).to eq 'account no longer blocks target_account@remote' - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'account') - block = Fabricate(:block, account: account) - - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - - expect(unblock_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/account' - end - - it 'appends activity:object-type element with activity type' do - block = Fabricate(:block) - - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - - object_type = unblock_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with block' do - block = Fabricate(:block) - - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - - verb = unblock_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:unblock] - end - - it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') - block = Fabricate(:block, target_account: target_account) - - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - - object = unblock_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain.test/id' - end - - it 'returns element whose rendered view triggers block when processed' do - block = Fabricate(:block) - unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) - xml = OStatus::AtomSerializer.render(unblock_salmon) - envelope = OStatus2::Salmon.new.pack(xml, block.account.keypair) - - ProcessInteractionService.new.call(envelope, block.target_account) - - expect { block.reload }.to raise_error ActiveRecord::RecordNotFound - end - end - - describe '#favourite_salmon' do - include_examples 'namespaces' do - def serialize - favourite = Fabricate(:favourite) - OStatus::AtomSerializer.new.favourite_salmon(favourite) - end - end - - it 'returns entry element' do - favourite = Fabricate(:favourite) - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - expect(favourite_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - favourite = Fabricate(:favourite, created_at: '2000-01-01T00:00:00Z') - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - expect(favourite_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{favourite.id}:objectType=Favourite" - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'username') - favourite = Fabricate(:favourite, account: account) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - expect(favourite_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with activity type' do - favourite = Fabricate(:favourite) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - object_type = favourite_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq 'http://activitystrea.ms/schema/1.0/activity' - end - - it 'appends activity:verb element with favorite' do - favourite = Fabricate(:favourite) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - verb = favourite_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:favorite] - end - - it 'appends activity:object element with status' do - status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z') - favourite = Fabricate(:favourite, status: status) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - object = favourite_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - end - - it 'appends thr:in-reply-to element for status' do - status_account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: status_account, created_at: '2000-01-01T00:00:00Z') - favourite = Fabricate(:favourite, status: status) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - in_reply_to = favourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' } - expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}" - end - - it 'includes description' do - account = Fabricate(:account, domain: nil, username: 'account') - status_account = Fabricate(:account, domain: 'remote', username: 'status_account') - status = Fabricate(:status, account: status_account) - favourite = Fabricate(:favourite, account: account, status: status) - - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - - expect(favourite_salmon.title.text).to eq 'account favourited a status by status_account@remote' - expect(favourite_salmon.content.text).to eq 'account favourited a status by status_account@remote' - end - - it 'returns element whose rendered view triggers favourite when processed' do - favourite = Fabricate(:favourite) - favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite) - xml = OStatus::AtomSerializer.render(favourite_salmon) - envelope = OStatus2::Salmon.new.pack(xml, favourite.account.keypair) - favourite.destroy! - - ProcessInteractionService.new.call(envelope, favourite.status.account) - expect(favourite.account.favourited?(favourite.status)).to be true - end - end - - describe '#unfavourite_salmon' do - include_examples 'namespaces' do - def serialize - favourite = Fabricate(:favourite) - OStatus::AtomSerializer.new.favourite_salmon(favourite) - end - end - - it 'returns entry element' do - favourite = Fabricate(:favourite) - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - expect(unfavourite_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - favourite = Fabricate(:favourite) - - time_before = Time.zone.now - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - time_after = Time.zone.now - - expect(unfavourite_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite')) - .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite'))) - ) - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'username') - favourite = Fabricate(:favourite, account: account) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - expect(unfavourite_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with activity type' do - favourite = Fabricate(:favourite) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - object_type = unfavourite_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq 'http://activitystrea.ms/schema/1.0/activity' - end - - it 'appends activity:verb element with favorite' do - favourite = Fabricate(:favourite) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - verb = unfavourite_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:unfavorite] - end - - it 'appends activity:object element with status' do - status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z') - favourite = Fabricate(:favourite, status: status) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - object = unfavourite_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - end - - it 'appends thr:in-reply-to element for status' do - status_account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: status_account, created_at: '2000-01-01T00:00:00Z') - favourite = Fabricate(:favourite, status: status) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - in_reply_to = unfavourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' } - expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}" - end - - it 'includes description' do - account = Fabricate(:account, domain: nil, username: 'account') - status_account = Fabricate(:account, domain: 'remote', username: 'status_account') - status = Fabricate(:status, account: status_account) - favourite = Fabricate(:favourite, account: account, status: status) - - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - - expect(unfavourite_salmon.title.text).to eq 'account no longer favourites a status by status_account@remote' - expect(unfavourite_salmon.content.text).to eq 'account no longer favourites a status by status_account@remote' - end - - it 'returns element whose rendered view triggers unfavourite when processed' do - favourite = Fabricate(:favourite) - unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite) - xml = OStatus::AtomSerializer.render(unfavourite_salmon) - envelope = OStatus2::Salmon.new.pack(xml, favourite.account.keypair) - - ProcessInteractionService.new.call(envelope, favourite.status.account) - expect { favourite.reload }.to raise_error ActiveRecord::RecordNotFound - end - end - - describe '#follow_salmon' do - include_examples 'namespaces' do - def serialize - follow = Fabricate(:follow) - OStatus::AtomSerializer.new.follow_salmon(follow) - end - end - - it 'returns entry element' do - follow = Fabricate(:follow) - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - expect(follow_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - follow = Fabricate(:follow, created_at: '2000-01-01T00:00:00Z') - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - expect(follow_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{follow.id}:objectType=Follow" - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'username') - follow = Fabricate(:follow, account: account) - - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - - expect(follow_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with activity type' do - follow = Fabricate(:follow) - - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - - object_type = follow_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with follow' do - follow = Fabricate(:follow) - - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - - verb = follow_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:follow] - end - - it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') - follow = Fabricate(:follow, target_account: target_account) - - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - - object = follow_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain.test/id' - end - - it 'includes description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - follow = Fabricate(:follow, account: account, target_account: target_account) - - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - - expect(follow_salmon.title.text).to eq 'account started following target_account@remote' - expect(follow_salmon.content.text).to eq 'account started following target_account@remote' - end - - it 'returns element whose rendered view triggers follow when processed' do - follow = Fabricate(:follow) - follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) - xml = OStatus::AtomSerializer.render(follow_salmon) - follow.destroy! - envelope = OStatus2::Salmon.new.pack(xml, follow.account.keypair) - - ProcessInteractionService.new.call(envelope, follow.target_account) - - expect(follow.account.following?(follow.target_account)).to be true - end - end - - describe '#unfollow_salmon' do - include_examples 'namespaces' do - def serialize - follow = Fabricate(:follow) - follow.destroy! - OStatus::AtomSerializer.new.unfollow_salmon(follow) - end - end - - it 'returns entry element' do - follow = Fabricate(:follow) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - expect(unfollow_salmon.name).to eq 'entry' - end - - it 'appends id element with unique tag' do - follow = Fabricate(:follow) - follow.destroy! - - time_before = Time.zone.now - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - time_after = Time.zone.now - - expect(unfollow_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow')) - .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow'))) - ) - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - follow = Fabricate(:follow, account: account, target_account: target_account) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - expect(unfollow_salmon.title.text).to eq 'account is no longer following target_account@remote' - end - - it 'appends content element with description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - follow = Fabricate(:follow, account: account, target_account: target_account) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - expect(unfollow_salmon.content.text).to eq 'account is no longer following target_account@remote' - end - - it 'appends author element with account' do - account = Fabricate(:account, domain: nil, username: 'username') - follow = Fabricate(:follow, account: account) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - expect(unfollow_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with activity type' do - follow = Fabricate(:follow) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - object_type = unfollow_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with follow' do - follow = Fabricate(:follow) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - verb = unfollow_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:unfollow] - end - - it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') - follow = Fabricate(:follow, target_account: target_account) - follow.destroy! - - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - - object = unfollow_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain.test/id' - end - - it 'returns element whose rendered view triggers unfollow when processed' do - follow = Fabricate(:follow) - follow.destroy! - unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) - xml = OStatus::AtomSerializer.render(unfollow_salmon) - follow.account.follow!(follow.target_account) - envelope = OStatus2::Salmon.new.pack(xml, follow.account.keypair) - - ProcessInteractionService.new.call(envelope, follow.target_account) - - expect(follow.account.following?(follow.target_account)).to be false - end - end - - describe '#follow_request_salmon' do - include_examples 'namespaces' do - def serialize - follow_request = Fabricate(:follow_request) - OStatus::AtomSerializer.new.follow_request_salmon(follow_request) - end - end - - context do - def serialize(follow_request) - OStatus::AtomSerializer.new.follow_request_salmon(follow_request) - end - - it_behaves_like 'follow request salmon' - - it 'appends id element with unique tag' do - follow_request = Fabricate(:follow_request, created_at: '2000-01-01T00:00:00Z') - follow_request_salmon = serialize(follow_request) - expect(follow_request_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{follow_request.id}:objectType=FollowRequest" - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: nil, username: 'account') - target_account = Fabricate(:account, domain: 'remote', username: 'target_account') - follow_request = Fabricate(:follow_request, account: account, target_account: target_account) - follow_request_salmon = serialize(follow_request) - expect(follow_request_salmon.title.text).to eq 'account requested to follow target_account@remote' - end - - it 'returns element whose rendered view triggers follow request when processed' do - follow_request = Fabricate(:follow_request) - follow_request_salmon = serialize(follow_request) - xml = OStatus::AtomSerializer.render(follow_request_salmon) - envelope = OStatus2::Salmon.new.pack(xml, follow_request.account.keypair) - follow_request.destroy! - - ProcessInteractionService.new.call(envelope, follow_request.target_account) - - expect(follow_request.account.requested?(follow_request.target_account)).to eq true - end - end - end - - describe '#authorize_follow_request_salmon' do - include_examples 'namespaces' do - def serialize - follow_request = Fabricate(:follow_request) - OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - end - end - - it_behaves_like 'follow request salmon' do - def serialize(follow_request) - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object' } - end - end - - it 'appends id element with unique tag' do - follow_request = Fabricate(:follow_request) - - time_before = Time.zone.now - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - time_after = Time.zone.now - - expect(authorize_follow_request_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) - .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))) - ) - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: 'remote', username: 'account') - target_account = Fabricate(:account, domain: nil, username: 'target_account') - follow_request = Fabricate(:follow_request, account: account, target_account: target_account) - - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - - expect(authorize_follow_request_salmon.title.text).to eq 'target_account authorizes follow request by account@remote' - end - - it 'appends activity:object-type element with activity type' do - follow_request = Fabricate(:follow_request) - - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - - object_type = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with authorize' do - follow_request = Fabricate(:follow_request) - - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - - verb = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:authorize] - end - - it 'returns element whose rendered view creates follow from follow request when processed' do - follow_request = Fabricate(:follow_request) - authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request) - xml = OStatus::AtomSerializer.render(authorize_follow_request_salmon) - envelope = OStatus2::Salmon.new.pack(xml, follow_request.target_account.keypair) - - ProcessInteractionService.new.call(envelope, follow_request.account) - - expect(follow_request.account.following?(follow_request.target_account)).to eq true - expect { follow_request.reload }.to raise_error ActiveRecord::RecordNotFound - end - end - - describe '#reject_follow_request_salmon' do - include_examples 'namespaces' do - def serialize - follow_request = Fabricate(:follow_request) - OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - end - end - - it_behaves_like 'follow request salmon' do - def serialize(follow_request) - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object' } - end - end - - it 'appends id element with unique tag' do - follow_request = Fabricate(:follow_request) - - time_before = Time.zone.now - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - time_after = Time.zone.now - - expect(reject_follow_request_salmon.id.text).to( - eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest')) - .or(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')) - ) - end - - it 'appends title element with description' do - account = Fabricate(:account, domain: 'remote', username: 'account') - target_account = Fabricate(:account, domain: nil, username: 'target_account') - follow_request = Fabricate(:follow_request, account: account, target_account: target_account) - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - expect(reject_follow_request_salmon.title.text).to eq 'target_account rejects follow request by account@remote' - end - - it 'appends activity:object-type element with activity type' do - follow_request = Fabricate(:follow_request) - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - object_type = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity] - end - - it 'appends activity:verb element with authorize' do - follow_request = Fabricate(:follow_request) - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - verb = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' } - expect(verb.text).to eq OStatus::TagManager::VERBS[:reject] - end - - it 'returns element whose rendered view deletes follow request when processed' do - follow_request = Fabricate(:follow_request) - reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request) - xml = OStatus::AtomSerializer.render(reject_follow_request_salmon) - envelope = OStatus2::Salmon.new.pack(xml, follow_request.target_account.keypair) - - ProcessInteractionService.new.call(envelope, follow_request.account) - - expect(follow_request.account.following?(follow_request.target_account)).to eq false - expect { follow_request.reload }.to raise_error ActiveRecord::RecordNotFound - end - end - - describe '#object' do - include_examples 'status attributes' do - def serialize(status) - OStatus::AtomSerializer.new.object(status) - end - end - - it 'returns activity:object element' do - status = Fabricate(:status) - object = OStatus::AtomSerializer.new.object(status) - expect(object.name).to eq 'activity:object' - end - - it 'appends id element with URL for status' do - status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z') - object = OStatus::AtomSerializer.new.object(status) - expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}" - end - - it 'appends published element with created date' do - status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z') - object = OStatus::AtomSerializer.new.object(status) - expect(object.published.text).to eq '2000-01-01T00:00:00Z' - end - - it 'appends updated element with updated date' do - status = Fabricate(:status) - status.updated_at = '2000-01-01T00:00:00Z' - object = OStatus::AtomSerializer.new.object(status) - expect(object.updated.text).to eq '2000-01-01T00:00:00Z' - end - - it 'appends title element with title' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - object = OStatus::AtomSerializer.new.object(status) - - expect(object.title.text).to eq 'New status by username' - end - - it 'appends author element with account' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - entry = OStatus::AtomSerializer.new.object(status) - - expect(entry.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username' - end - - it 'appends activity:object-type element with object type' do - status = Fabricate(:status) - - entry = OStatus::AtomSerializer.new.object(status) - - object_type = entry.nodes.find { |node| node.name == 'activity:object-type' } - expect(object_type.text).to eq OStatus::TagManager::TYPES[:note] - end - - it 'appends activity:verb element with verb' do - status = Fabricate(:status) - - entry = OStatus::AtomSerializer.new.object(status) - - object_type = entry.nodes.find { |node| node.name == 'activity:verb' } - expect(object_type.text).to eq OStatus::TagManager::VERBS[:post] - end - - it 'appends link element for an alternative' do - account = Fabricate(:account, username: 'username') - status = Fabricate(:status, account: account) - - entry = OStatus::AtomSerializer.new.object(status) - - link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' } - expect(link[:type]).to eq 'text/html' - expect(link[:href]).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}" - end - - it 'appends thr:in-reply-to element if it is a reply and thread is not nil' do - account = Fabricate(:account, username: 'username') - thread = Fabricate(:status, account: account, created_at: '2000-01-01T00:00:00Z') - reply = Fabricate(:status, thread: thread) - - entry = OStatus::AtomSerializer.new.object(reply) - - in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' } - expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{thread.account.to_param}/statuses/#{thread.id}" - expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{thread.id}" - end - - it 'does not append thr:in-reply-to element if thread is nil' do - status = Fabricate(:status, thread: nil) - entry = OStatus::AtomSerializer.new.object(status) - entry.nodes.each { |node| expect(node.name).not_to eq 'thr:in-reply-to' } - end - - it 'does not append ostatus:conversation element if conversation_id is nil' do - status = Fabricate.build(:status, conversation_id: nil) - status.save!(validate: false) - - entry = OStatus::AtomSerializer.new.object(status) - - entry.nodes.each { |node| expect(node.name).not_to eq 'ostatus:conversation' } - end - - it 'appends ostatus:conversation element if conversation_id is not nil' do - status = Fabricate(:status) - status.conversation.update!(created_at: '2000-01-01T00:00:00Z') - - entry = OStatus::AtomSerializer.new.object(status) - - conversation = entry.nodes.find { |node| node.name == 'ostatus:conversation' } - expect(conversation[:ref]).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.conversation.id}:objectType=Conversation" - end - end -end diff --git a/spec/lib/request_pool_spec.rb b/spec/lib/request_pool_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..4a144d7c7f7402e64e7321dbfec4a6969819d1c7 --- /dev/null +++ b/spec/lib/request_pool_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RequestPool do + subject { described_class.new } + + describe '#with' do + it 'returns a HTTP client for a host' do + subject.with('http://example.com') do |http_client| + expect(http_client).to be_a HTTP::Client + end + end + + it 'returns the same instance of HTTP client within the same thread for the same host' do + test_client = nil + + subject.with('http://example.com') { |http_client| test_client = http_client } + expect(test_client).to_not be_nil + subject.with('http://example.com') { |http_client| expect(http_client).to be test_client } + end + + it 'returns different HTTP clients for different hosts' do + test_client = nil + + subject.with('http://example.com') { |http_client| test_client = http_client } + expect(test_client).to_not be_nil + subject.with('http://example.org') { |http_client| expect(http_client).to_not be test_client } + end + + it 'grows to the number of threads accessing it' do + stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'Hello!') + + subject + + threads = 20.times.map do |i| + Thread.new do + 20.times do + subject.with('http://example.com') do |http_client| + http_client.get('/').flush + end + end + end + end + + threads.map(&:join) + + expect(subject.size).to be > 1 + end + + it 'closes idle connections' do + stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'Hello!') + + subject.with('http://example.com') do |http_client| + http_client.get('/').flush + end + + expect(subject.size).to eq 1 + sleep RequestPool::MAX_IDLE_TIME + 30 + 1 + expect(subject.size).to eq 0 + end + end +end diff --git a/spec/lib/spam_check_spec.rb b/spec/lib/spam_check_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d4d66a49930737df8fe489c48871d5151e400b4d --- /dev/null +++ b/spec/lib/spam_check_spec.rb @@ -0,0 +1,192 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe SpamCheck do + let!(:sender) { Fabricate(:account) } + let!(:alice) { Fabricate(:account, username: 'alice') } + let!(:bob) { Fabricate(:account, username: 'bob') } + + def status_with_html(text, options = {}) + status = PostStatusService.new.call(sender, { text: text }.merge(options)) + status.update_columns(text: Formatter.instance.format(status), local: false) + status + end + + describe '#hashable_text' do + it 'removes mentions from HTML for remote statuses' do + status = status_with_html('@alice Hello') + expect(described_class.new(status).hashable_text).to eq 'hello' + end + + it 'removes mentions from text for local statuses' do + status = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?") + expect(described_class.new(status).hashable_text).to eq 'hey , how are you?' + end + end + + describe '#insufficient_data?' do + it 'returns true when there is no text' do + status = status_with_html('@alice') + expect(described_class.new(status).insufficient_data?).to be true + end + + it 'returns false when there is text' do + status = status_with_html('@alice h') + expect(described_class.new(status).insufficient_data?).to be false + end + end + + describe '#digest' do + it 'returns a string' do + status = status_with_html('@alice Hello world') + expect(described_class.new(status).digest).to be_a String + end + end + + describe '#spam?' do + it 'returns false for a unique status' do + status = status_with_html('@alice Hello') + expect(described_class.new(status).spam?).to be false + end + + it 'returns false for different statuses to the same recipient' do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + status2 = status_with_html('@alice Are you available to talk?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for statuses with different content warnings' do + status1 = status_with_html('@alice Are you available to talk?') + described_class.new(status1).remember! + status2 = status_with_html('@alice Are you available to talk?', spoiler_text: 'This is a completely different matter than what I was talking about previously, I swear!') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for different statuses to different recipients' do + status1 = status_with_html('@alice How is it going?') + described_class.new(status1).remember! + status2 = status_with_html('@bob Are you okay?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for very short different statuses to different recipients' do + status1 = status_with_html('@alice 🙄') + described_class.new(status1).remember! + status2 = status_with_html('@bob Huh?') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns false for statuses with no text' do + status1 = status_with_html('@alice') + described_class.new(status1).remember! + status2 = status_with_html('@bob') + expect(described_class.new(status2).spam?).to be false + end + + it 'returns true for duplicate statuses to the same recipient' do + described_class::THRESHOLD.times do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + end + + status2 = status_with_html('@alice Hello') + expect(described_class.new(status2).spam?).to be true + end + + it 'returns true for duplicate statuses to different recipients' do + described_class::THRESHOLD.times do + status1 = status_with_html('@alice Hello') + described_class.new(status1).remember! + end + + status2 = status_with_html('@bob Hello') + expect(described_class.new(status2).spam?).to be true + end + + it 'returns true for nearly identical statuses with random numbers' do + source_text = 'Sodium, atomic number 11, was first isolated by Humphry Davy in 1807. A chemical component of salt, he named it Na in honor of the saltiest region on earth, North America.' + + described_class::THRESHOLD.times do + status1 = status_with_html('@alice ' + source_text + ' 1234') + described_class.new(status1).remember! + end + + status2 = status_with_html('@bob ' + source_text + ' 9568') + expect(described_class.new(status2).spam?).to be true + end + end + + describe '#skip?' do + it 'returns true when the sender is already silenced' do + status = status_with_html('@alice Hello') + sender.silence! + expect(described_class.new(status).skip?).to be true + end + + it 'returns true when the mentioned person follows the sender' do + status = status_with_html('@alice Hello') + alice.follow!(sender) + expect(described_class.new(status).skip?).to be true + end + + it 'returns false when even one mentioned person doesn\'t follow the sender' do + status = status_with_html('@alice @bob Hello') + alice.follow!(sender) + expect(described_class.new(status).skip?).to be false + end + + it 'returns true when the sender is replying to a status that mentions the sender' do + parent = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?") + status = status_with_html('@alice @bob Hello', thread: parent) + expect(described_class.new(status).skip?).to be true + end + end + + describe '#remember!' do + let(:status) { status_with_html('@alice') } + let(:spam_check) { described_class.new(status) } + let(:redis_key) { spam_check.send(:redis_key) } + + it 'remembers' do + expect(Redis.current.exists(redis_key)).to be true + spam_check.remember! + expect(Redis.current.exists(redis_key)).to be true + end + end + + describe '#reset!' do + let(:status) { status_with_html('@alice') } + let(:spam_check) { described_class.new(status) } + let(:redis_key) { spam_check.send(:redis_key) } + + before do + spam_check.remember! + end + + it 'resets' do + expect(Redis.current.exists(redis_key)).to be true + spam_check.reset! + expect(Redis.current.exists(redis_key)).to be false + end + end + + describe '#flag!' do + let!(:status1) { status_with_html('@alice General Kenobi you are a bold one') } + let!(:status2) { status_with_html('@alice @bob General Kenobi, you are a bold one') } + + before do + described_class.new(status1).remember! + described_class.new(status2).flag! + end + + it 'creates a report about the account' do + expect(sender.targeted_reports.unresolved.count).to eq 1 + end + + it 'attaches both matching statuses to the report' do + expect(sender.targeted_reports.first.status_ids).to include(status1.id, status2.id) + end + end +end diff --git a/spec/lib/status_finder_spec.rb b/spec/lib/status_finder_spec.rb index 6b4ee434f3cff813c963b0f33425a90ab9c425a4..61483f4bfe9bc2e6116f84adb0d2a55e7e37ea88 100644 --- a/spec/lib/status_finder_spec.rb +++ b/spec/lib/status_finder_spec.rb @@ -25,15 +25,6 @@ describe StatusFinder do end end - context 'with a stream entry url' do - let(:stream_entry) { Fabricate(:stream_entry) } - let(:url) { account_stream_entry_url(stream_entry.account, stream_entry) } - - it 'finds the stream entry' do - expect(subject.status).to eq(stream_entry.status) - end - end - context 'with a remote url even if id exists on local' do let(:status) { Fabricate(:status) } let(:url) { "https://example.com/users/test/statuses/#{status.id}" } diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb index 3a804ac0f0aebcdbd0a83c3d65d49edbe7ab894b..e9a7aa93441ddec877a26c7baff92a4763040e53 100644 --- a/spec/lib/tag_manager_spec.rb +++ b/spec/lib/tag_manager_spec.rb @@ -119,46 +119,4 @@ RSpec.describe TagManager do expect(TagManager.instance.same_acct?('username', 'incorrect@Cb6E6126.nGrOk.Io')).to eq false end end - - describe '#url_for' do - let(:alice) { Fabricate(:account, username: 'alice') } - - subject { TagManager.instance.url_for(target) } - - context 'activity object' do - let(:target) { Fabricate(:status, account: alice, reblog: Fabricate(:status)).stream_entry } - - it 'returns the unique tag for status' do - expect(target.object_type).to eq :activity - is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}" - end - end - - context 'comment object' do - let(:target) { Fabricate(:status, account: alice, reply: true) } - - it 'returns the unique tag for status' do - expect(target.object_type).to eq :comment - is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}" - end - end - - context 'note object' do - let(:target) { Fabricate(:status, account: alice, reply: false, thread: nil) } - - it 'returns the unique tag for status' do - expect(target.object_type).to eq :note - is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}" - end - end - - context 'person object' do - let(:target) { alice } - - it 'returns the URL for account' do - expect(target.object_type).to eq :person - is_expected.to eq 'https://cb6e6126.ngrok.io/@alice' - end - end - end end diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 53c8364944579c9ef74eca4fa4ce9ddca9206597..464f177d0e6008caf76f02d423a09f5ea3a530b1 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -18,6 +18,21 @@ class UserMailerPreview < ActionMailer::Preview UserMailer.password_change(User.first) end + # Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_disabled + def two_factor_disabled + UserMailer.two_factor_disabled(User.first) + end + + # Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_enabled + def two_factor_enabled + UserMailer.two_factor_enabled(User.first) + end + + # Preview this email at http://localhost:3000/rails/mailers/user_mailer/two_factor_recovery_codes_changed + def two_factor_recovery_codes_changed + UserMailer.two_factor_recovery_codes_changed(User.first) + end + # Preview this email at http://localhost:3000/rails/mailers/user_mailer/reconfirmation_instructions def reconfirmation_instructions user = User.first @@ -42,6 +57,6 @@ class UserMailerPreview < ActionMailer::Preview # Preview this email at http://localhost:3000/rails/mailers/user_mailer/warning def warning - UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence)) + UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence), [Status.first.id]) end end diff --git a/spec/models/account_alias_spec.rb b/spec/models/account_alias_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..27ec215aa48eb0af604fed038daeebecc55241d2 --- /dev/null +++ b/spec/models/account_alias_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AccountAlias, type: :model do + +end diff --git a/spec/models/account_migration_spec.rb b/spec/models/account_migration_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8461b4b28ec71372164dc740957f9718ea7cd540 --- /dev/null +++ b/spec/models/account_migration_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe AccountMigration, type: :model do + +end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index ce9ea250d1329602110666e4be933c51c7380b5f..b2f6234cb9f0e6b7955ba67591803ead896e2cf0 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -126,8 +126,8 @@ RSpec.describe Account, type: :model do end it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do - expect(account.avatar_remote_url).to eq '' - expect(account.header_remote_url).to eq '' + expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar' + expect(account.header_remote_url).to eq expectation.header_remote_url expect(account.avatar_file_name).to eq nil expect(account.header_file_name).to eq nil end @@ -450,7 +450,7 @@ RSpec.describe Account, type: :model do describe '.domains' do it 'returns domains' do Fabricate(:account, domain: 'domain') - expect(Account.domains).to match_array(['domain']) + expect(Account.remote.domains).to match_array(['domain']) end end @@ -583,26 +583,43 @@ RSpec.describe Account, type: :model do expect(account.valid?).to be true end + it 'is valid if we are creating an instance actor account with a period' do + account = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com') + expect(account.valid?).to be true + end + + it 'is valid if we are creating a possibly-conflicting instance actor account' do + account_1 = Fabricate(:account, username: 'examplecom') + account_2 = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com') + expect(account_2.valid?).to be true + end + it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do account = Fabricate.build(:account, username: 'the-doctor') account.valid? expect(account).to model_have_error_on_field(:username) end + it 'is invalid if the username contains a period' do + account = Fabricate.build(:account, username: 'the.doctor') + account.valid? + expect(account).to model_have_error_on_field(:username) + end + it 'is invalid if the username is longer then 30 characters' do - account = Fabricate.build(:account, username: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31)) account.valid? expect(account).to model_have_error_on_field(:username) end it 'is invalid if the display name is longer than 30 characters' do - account = Fabricate.build(:account, display_name: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31)) account.valid? expect(account).to model_have_error_on_field(:display_name) end it 'is invalid if the note is longer than 500 characters' do - account = Fabricate.build(:account, note: Faker::Lorem.characters(501)) + account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501)) account.valid? expect(account).to model_have_error_on_field(:note) end @@ -636,19 +653,19 @@ RSpec.describe Account, type: :model do end it 'is valid even if the username is longer then 30 characters' do - account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31)) account.valid? expect(account).not_to model_have_error_on_field(:username) end it 'is valid even if the display name is longer than 30 characters' do - account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(31)) + account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31)) account.valid? expect(account).not_to model_have_error_on_field(:display_name) end it 'is valid even if the note is longer than 500 characters' do - account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(501)) + account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501)) account.valid? expect(account).not_to model_have_error_on_field(:note) end @@ -665,7 +682,7 @@ RSpec.describe Account, type: :model do { username: 'b', domain: 'b' }, ].map(&method(:Fabricate).curry(2).call(:account)) - expect(Account.alphabetic).to eq matches + expect(Account.where('id > 0').alphabetic).to eq matches end end @@ -732,7 +749,7 @@ RSpec.describe Account, type: :model do 2.times { Fabricate(:account, domain: 'example.com') } Fabricate(:account, domain: 'example2.com') - results = Account.by_domain_accounts + results = Account.where('id > 0').by_domain_accounts expect(results.length).to eq 2 expect(results.first.domain).to eq 'example.com' expect(results.first.accounts_count).to eq 2 @@ -745,7 +762,7 @@ RSpec.describe Account, type: :model do it 'returns an array of accounts who do not have a domain' do account_1 = Fabricate(:account, domain: nil) account_2 = Fabricate(:account, domain: 'example.com') - expect(Account.local).to match_array([account_1]) + expect(Account.where('id > 0').local).to match_array([account_1]) end end @@ -756,14 +773,14 @@ RSpec.describe Account, type: :model do matches[index] = Fabricate(:account, domain: matches[index]) end - expect(Account.partitioned).to match_array(matches) + expect(Account.where('id > 0').partitioned).to match_array(matches) end end describe 'recent' do it 'returns a relation of accounts sorted by recent creation' do matches = 2.times.map { Fabricate(:account) } - expect(Account.recent).to match_array(matches) + expect(Account.where('id > 0').recent).to match_array(matches) end end @@ -787,7 +804,7 @@ RSpec.describe Account, type: :model do context 'when is local' do # Test disabled because test environment omits autogenerating keys for performance xit 'generates keys' do - account = Account.create!(domain: nil, username: Faker::Internet.user_name(nil, ['_'])) + account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_'])) expect(account.keypair.private?).to eq true end end @@ -795,12 +812,12 @@ RSpec.describe Account, type: :model do context 'when is remote' do it 'does not generate keys' do key = OpenSSL::PKey::RSA.new(1024).public_key - account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(nil, ['_']), public_key: key.to_pem) + account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem) expect(account.keypair.params).to eq key.params end it 'normalizes domain' do - account = Account.create!(domain: 'ã«ã‚ƒã‚“', username: Faker::Internet.user_name(nil, ['_'])) + account = Account.create!(domain: 'ã«ã‚ƒã‚“', username: Faker::Internet.user_name(separators: ['_'])) expect(account.domain).to eq 'xn--r9j5b5b' end end diff --git a/spec/models/account_stat_spec.rb b/spec/models/account_stat_spec.rb index a94185109c774d8701e914b1d2fe6d4b945f8447..8adc0d1d638d8bf46dbb025754d4759753f3dc7f 100644 --- a/spec/models/account_stat_spec.rb +++ b/spec/models/account_stat_spec.rb @@ -1,4 +1,57 @@ require 'rails_helper' RSpec.describe AccountStat, type: :model do + describe '#increment_count!' do + it 'increments the count' do + account_stat = AccountStat.create(account: Fabricate(:account)) + expect(account_stat.followers_count).to eq 0 + account_stat.increment_count!(:followers_count) + expect(account_stat.followers_count).to eq 1 + end + + it 'increments the count in multi-threaded an environment' do + account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 0) + increment_by = 15 + wait_for_start = true + + threads = Array.new(increment_by) do + Thread.new do + true while wait_for_start + AccountStat.find(account_stat.id).increment_count!(:statuses_count) + end + end + + wait_for_start = false + threads.each(&:join) + + expect(account_stat.reload.statuses_count).to eq increment_by + end + end + + describe '#decrement_count!' do + it 'decrements the count' do + account_stat = AccountStat.create(account: Fabricate(:account), followers_count: 15) + expect(account_stat.followers_count).to eq 15 + account_stat.decrement_count!(:followers_count) + expect(account_stat.followers_count).to eq 14 + end + + it 'decrements the count in multi-threaded an environment' do + account_stat = AccountStat.create(account: Fabricate(:account), statuses_count: 15) + decrement_by = 10 + wait_for_start = true + + threads = Array.new(decrement_by) do + Thread.new do + true while wait_for_start + AccountStat.find(account_stat.id).decrement_count!(:statuses_count) + end + end + + wait_for_start = false + threads.each(&:join) + + expect(account_stat.reload.statuses_count).to eq 5 + end + end end diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb index a3db60cfc85c95585a9713b9fbe9d2c90f084477..87fc285007867a129ec9a1baae59194cff9194e8 100644 --- a/spec/models/admin/account_action_spec.rb +++ b/spec/models/admin/account_action_spec.rb @@ -58,8 +58,8 @@ RSpec.describe Admin::AccountAction, type: :model do end.to change { Admin::ActionLog.count }.by 1 end - it 'calls queue_email!' do - expect(account_action).to receive(:queue_email!) + it 'calls process_email!' do + expect(account_action).to receive(:process_email!) subject end diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb index a4289cc45e79aca0e8e251eb1b6113007a4eabeb..99a60cbf652334804b256b255bb03cce017c2380 100644 --- a/spec/models/concerns/remotable_spec.rb +++ b/spec/models/concerns/remotable_spec.rb @@ -18,6 +18,8 @@ RSpec.describe Remotable do def hoge=(arg); end + def hoge_file_name; end + def hoge_file_name=(arg); end def has_attribute?(arg); end @@ -109,12 +111,21 @@ RSpec.describe Remotable do end context 'foo[attribute_name] == url' do - it 'makes no request' do + it 'makes no request if file is saved' do allow(foo).to receive(:[]).with(attribute_name).and_return(url) + allow(foo).to receive(:hoge_file_name).and_return('foo.jpg') foo.hoge_remote_url = url expect(request).not_to have_been_requested end + + it 'makes request if file is not saved' do + allow(foo).to receive(:[]).with(attribute_name).and_return(url) + allow(foo).to receive(:hoge_file_name).and_return(nil) + + foo.hoge_remote_url = url + expect(request).to have_been_requested + end end context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do diff --git a/spec/models/concerns/streamable_spec.rb b/spec/models/concerns/streamable_spec.rb deleted file mode 100644 index b5f2d51923e95b3dc863541e30b64397862a9974..0000000000000000000000000000000000000000 --- a/spec/models/concerns/streamable_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Streamable do - class Parent - def title; end - - def target; end - - def thread; end - - def self.has_one(*); end - - def self.after_create; end - end - - class Child < Parent - include Streamable - end - - child = Child.new - - describe '#title' do - it 'calls Parent#title' do - expect_any_instance_of(Parent).to receive(:title) - child.title - end - end - - describe '#content' do - it 'calls #title' do - expect_any_instance_of(Parent).to receive(:title) - child.content - end - end - - describe '#target' do - it 'calls Parent#target' do - expect_any_instance_of(Parent).to receive(:target) - child.target - end - end - - describe '#object_type' do - it 'returns :activity' do - expect(child.object_type).to eq :activity - end - end - - describe '#thread' do - it 'calls Parent#thread' do - expect_any_instance_of(Parent).to receive(:thread) - child.thread - end - end - - describe '#hidden?' do - it 'returns false' do - expect(child.hidden?).to be false - end - end -end diff --git a/spec/models/custom_emoji_category_spec.rb b/spec/models/custom_emoji_category_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..160033f4d489b86de9d82297dee48c842f84ae8c --- /dev/null +++ b/spec/models/custom_emoji_category_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe CustomEmojiCategory, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/domain_allow_spec.rb b/spec/models/domain_allow_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..e65435127d5d17dc176410bed0574e6582a52cfc --- /dev/null +++ b/spec/models/domain_allow_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe DomainAllow, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/form/status_batch_spec.rb b/spec/models/form/status_batch_spec.rb index 00c790a11f936a12b25eb806588f38bd43c66285..68d84a7379b8c6104c2a40ccd498c909d6a2c446 100644 --- a/spec/models/form/status_batch_spec.rb +++ b/spec/models/form/status_batch_spec.rb @@ -41,12 +41,12 @@ describe Form::StatusBatch do it 'call RemovalWorker' do form.save - expect(RemovalWorker).to have_received(:perform_async).with(status.id) + expect(RemovalWorker).to have_received(:perform_async).with(status.id, immediate: true) end it 'do not call RemovalWorker' do form.save - expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id) + expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id, immediate: true) end end end diff --git a/spec/models/home_feed_spec.rb b/spec/models/home_feed_spec.rb index 3acb997f1faba7ab3056dd4f53714a43a9987d2d..ee7a83960a05271044680fc8e75a4163bacedd64 100644 --- a/spec/models/home_feed_spec.rb +++ b/spec/models/home_feed_spec.rb @@ -34,11 +34,10 @@ RSpec.describe HomeFeed, type: :model do Redis.current.set("account:#{account.id}:regeneration", true) end - it 'gets statuses with ids in the range from database' do + it 'returns nothing' do results = subject.get(3) - expect(results.map(&:id)).to eq [10, 3, 2] - expect(results.first.attributes.keys).to include('id', 'updated_at') + expect(results.map(&:id)).to eq [] end end end diff --git a/spec/models/marker_spec.rb b/spec/models/marker_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d716aa75c20cf9b351c2646f63f8a63f3db5a493 --- /dev/null +++ b/spec/models/marker_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Marker, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/poll_vote_spec.rb b/spec/models/poll_vote_spec.rb index 354afd5350bd24da958398323795ff8aaf098bb1..563f346993a3977cc9e91d8056312154917274b5 100644 --- a/spec/models/poll_vote_spec.rb +++ b/spec/models/poll_vote_spec.rb @@ -1,5 +1,13 @@ +# frozen_string_literal: true + require 'rails_helper' RSpec.describe PollVote, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + describe '#object_type' do + let(:poll_vote) { Fabricate.build(:poll_vote) } + + it 'returns :vote' do + expect(poll_vote.object_type).to eq :vote + end + end end diff --git a/spec/models/remote_follow_spec.rb b/spec/models/remote_follow_spec.rb index ed2667b28aad82eb4242543284033974ff57b1c5..5b4c19b5bbecc8efee1daaacf9088f6f5dcd8291 100644 --- a/spec/models/remote_follow_spec.rb +++ b/spec/models/remote_follow_spec.rb @@ -61,7 +61,7 @@ RSpec.describe RemoteFollow do subject { remote_follow.subscribe_address_for(account) } it 'returns subscribe address' do - is_expected.to eq 'https://quitter.no/main/ostatussub?profile=alice%40cb6e6126.ngrok.io' + is_expected.to eq 'https://quitter.no/main/ostatussub?profile=https%3A%2F%2Fcb6e6126.ngrok.io%2Fusers%2Falice' end end end diff --git a/spec/models/remote_profile_spec.rb b/spec/models/remote_profile_spec.rb deleted file mode 100644 index da5048f0a4ede4c7cf2ad1e78ce6ba00e0ba7567..0000000000000000000000000000000000000000 --- a/spec/models/remote_profile_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe RemoteProfile do - let(:remote_profile) { RemoteProfile.new(body) } - let(:body) do - <<-XML - <feed xmlns="http://www.w3.org/2005/Atom"> - <author>John</author> - XML - end - - describe '.initialize' do - it 'calls Nokogiri::XML.parse' do - expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8') - RemoteProfile.new(body) - end - - it 'sets document' do - remote_profile = RemoteProfile.new(body) - expect(remote_profile).not_to be nil - end - end - - describe '#root' do - let(:document) { remote_profile.document } - - it 'callse document.at_xpath' do - expect(document).to receive(:at_xpath).with( - '/atom:feed|/atom:entry', - atom: OStatus::TagManager::XMLNS - ) - - remote_profile.root - end - end - - describe '#author' do - let(:root) { remote_profile.root } - - it 'calls root.at_xpath' do - expect(root).to receive(:at_xpath).with( - './atom:author|./dfrn:owner', - atom: OStatus::TagManager::XMLNS, - dfrn: OStatus::TagManager::DFRN_XMLNS - ) - - remote_profile.author - end - end - - describe '#hub_link' do - let(:root) { remote_profile.root } - - it 'calls #link_href_from_xml' do - expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub') - remote_profile.hub_link - end - end - - describe '#display_name' do - let(:author) { remote_profile.author } - - it 'calls author.at_xpath.content' do - expect(author).to receive_message_chain(:at_xpath, :content).with( - './poco:displayName', - poco: OStatus::TagManager::POCO_XMLNS - ).with(no_args) - - remote_profile.display_name - end - end - - describe '#note' do - let(:author) { remote_profile.author } - - it 'calls author.at_xpath.content' do - expect(author).to receive_message_chain(:at_xpath, :content).with( - './atom:summary|./poco:note', - atom: OStatus::TagManager::XMLNS, - poco: OStatus::TagManager::POCO_XMLNS - ).with(no_args) - - remote_profile.note - end - end - - describe '#scope' do - let(:author) { remote_profile.author } - - it 'calls author.at_xpath.content' do - expect(author).to receive_message_chain(:at_xpath, :content).with( - './mastodon:scope', - mastodon: OStatus::TagManager::MTDN_XMLNS - ).with(no_args) - - remote_profile.scope - end - end - - describe '#avatar' do - let(:author) { remote_profile.author } - - it 'calls #link_href_from_xml' do - expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar') - remote_profile.avatar - end - end - - describe '#header' do - let(:author) { remote_profile.author } - - it 'calls #link_href_from_xml' do - expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header') - remote_profile.header - end - end - - describe '#locked?' do - before do - allow(remote_profile).to receive(:scope).and_return(scope) - end - - subject { remote_profile.locked? } - - context 'scope is private' do - let(:scope) { 'private' } - - it 'returns true' do - is_expected.to be true - end - end - - context 'scope is not private' do - let(:scope) { 'public' } - - it 'returns false' do - is_expected.to be false - end - end - end -end diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index a0cd0800da570a04f4025eeed664825170bc168e..312954c9dc9f4bdd27f740d134ef8eee1cdf7de2 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -125,7 +125,7 @@ describe Report do end it 'is invalid if comment is longer than 1000 characters' do - report = Fabricate.build(:report, comment: Faker::Lorem.characters(1001)) + report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001)) report.valid? expect(report).to model_have_error_on_field(:comment) end diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index e7229087ffd98cd7ffe0f04ae549b42783e2eaf2..0b6086978d890af4c93764d13a151d300734422e 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -296,99 +296,6 @@ RSpec.describe Status, type: :model do end end - describe '.as_home_timeline' do - let(:account) { Fabricate(:account) } - let(:followed) { Fabricate(:account) } - let(:not_followed) { Fabricate(:account) } - - before do - Fabricate(:follow, account: account, target_account: followed) - - @self_status = Fabricate(:status, account: account, visibility: :public) - @self_direct_status = Fabricate(:status, account: account, visibility: :direct) - @followed_status = Fabricate(:status, account: followed, visibility: :public) - @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct) - @not_followed_status = Fabricate(:status, account: not_followed, visibility: :public) - - @results = Status.as_home_timeline(account) - end - - it 'includes statuses from self' do - expect(@results).to include(@self_status) - end - - it 'does not include direct statuses from self' do - expect(@results).to_not include(@self_direct_status) - end - - it 'includes statuses from followed' do - expect(@results).to include(@followed_status) - end - - it 'does not include direct statuses mentioning recipient from followed' do - Fabricate(:mention, account: account, status: @followed_direct_status) - expect(@results).to_not include(@followed_direct_status) - end - - it 'does not include direct statuses not mentioning recipient from followed' do - expect(@results).not_to include(@followed_direct_status) - end - - it 'does not include statuses from non-followed' do - expect(@results).not_to include(@not_followed_status) - end - end - - describe '.as_direct_timeline' do - let(:account) { Fabricate(:account) } - let(:followed) { Fabricate(:account) } - let(:not_followed) { Fabricate(:account) } - - before do - Fabricate(:follow, account: account, target_account: followed) - - @self_public_status = Fabricate(:status, account: account, visibility: :public) - @self_direct_status = Fabricate(:status, account: account, visibility: :direct) - @followed_public_status = Fabricate(:status, account: followed, visibility: :public) - @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct) - @not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct) - - @results = Status.as_direct_timeline(account) - end - - it 'does not include public statuses from self' do - expect(@results).to_not include(@self_public_status) - end - - it 'includes direct statuses from self' do - expect(@results).to include(@self_direct_status) - end - - it 'does not include public statuses from followed' do - expect(@results).to_not include(@followed_public_status) - end - - it 'does not include direct statuses not mentioning recipient from followed' do - expect(@results).to_not include(@followed_direct_status) - end - - it 'does not include direct statuses not mentioning recipient from non-followed' do - expect(@results).to_not include(@not_followed_direct_status) - end - - it 'includes direct statuses mentioning recipient from followed' do - Fabricate(:mention, account: account, status: @followed_direct_status) - results2 = Status.as_direct_timeline(account) - expect(results2).to include(@followed_direct_status) - end - - it 'includes direct statuses mentioning recipient from non-followed' do - Fabricate(:mention, account: account, status: @not_followed_direct_status) - results2 = Status.as_direct_timeline(account) - expect(results2).to include(@not_followed_direct_status) - end - end - describe '.as_public_timeline' do it 'only includes statuses with public visibility' do public_status = Fabricate(:status, visibility: :public) diff --git a/spec/models/stream_entry_spec.rb b/spec/models/stream_entry_spec.rb deleted file mode 100644 index 8f8bfbd5837566711f583e6b86c33b1772c6e866..0000000000000000000000000000000000000000 --- a/spec/models/stream_entry_spec.rb +++ /dev/null @@ -1,192 +0,0 @@ -require 'rails_helper' - -RSpec.describe StreamEntry, type: :model do - let(:alice) { Fabricate(:account, username: 'alice') } - let(:bob) { Fabricate(:account, username: 'bob') } - let(:status) { Fabricate(:status, account: alice) } - let(:reblog) { Fabricate(:status, account: bob, reblog: status) } - let(:reply) { Fabricate(:status, account: bob, thread: status) } - let(:stream_entry) { Fabricate(:stream_entry, activity: activity) } - let(:activity) { reblog } - - describe '#object_type' do - before do - allow(stream_entry).to receive(:orphaned?).and_return(orphaned) - allow(stream_entry).to receive(:targeted?).and_return(targeted) - end - - subject { stream_entry.object_type } - - context 'orphaned? is true' do - let(:orphaned) { true } - let(:targeted) { false } - - it 'returns :activity' do - is_expected.to be :activity - end - end - - context 'targeted? is true' do - let(:orphaned) { false } - let(:targeted) { true } - - it 'returns :activity' do - is_expected.to be :activity - end - end - - context 'orphaned? and targeted? are false' do - let(:orphaned) { false } - let(:targeted) { false } - - context 'activity is reblog' do - let(:activity) { reblog } - - it 'returns :note' do - is_expected.to be :note - end - end - - context 'activity is reply' do - let(:activity) { reply } - - it 'returns :comment' do - is_expected.to be :comment - end - end - end - end - - describe '#verb' do - before do - allow(stream_entry).to receive(:orphaned?).and_return(orphaned) - end - - subject { stream_entry.verb } - - context 'orphaned? is true' do - let(:orphaned) { true } - - it 'returns :delete' do - is_expected.to be :delete - end - end - - context 'orphaned? is false' do - let(:orphaned) { false } - - context 'activity is reblog' do - let(:activity) { reblog } - - it 'returns :share' do - is_expected.to be :share - end - end - - context 'activity is reply' do - let(:activity) { reply } - - it 'returns :post' do - is_expected.to be :post - end - end - end - end - - describe '#mentions' do - before do - allow(stream_entry).to receive(:orphaned?).and_return(orphaned) - end - - subject { stream_entry.mentions } - - context 'orphaned? is true' do - let(:orphaned) { true } - - it 'returns []' do - is_expected.to eq [] - end - end - - context 'orphaned? is false' do - before do - reblog.mentions << Fabricate(:mention, account: alice) - reblog.mentions << Fabricate(:mention, account: bob) - end - - let(:orphaned) { false } - - it 'returns [Account] includes alice and bob' do - is_expected.to eq [alice, bob] - end - end - end - - describe '#targeted?' do - it 'returns true for a reblog' do - expect(reblog.stream_entry.targeted?).to be true - end - - it 'returns false otherwise' do - expect(status.stream_entry.targeted?).to be false - end - end - - describe '#threaded?' do - it 'returns true for a reply' do - expect(reply.stream_entry.threaded?).to be true - end - - it 'returns false otherwise' do - expect(status.stream_entry.threaded?).to be false - end - end - - describe 'delegated methods' do - context 'with a nil status' do - subject { described_class.new(status: nil) } - - it 'returns nil for target' do - expect(subject.target).to be_nil - end - - it 'returns nil for title' do - expect(subject.title).to be_nil - end - - it 'returns nil for content' do - expect(subject.content).to be_nil - end - - it 'returns nil for thread' do - expect(subject.thread).to be_nil - end - end - - context 'with a real status' do - let(:original) { Fabricate(:status, text: 'Test status') } - let(:status) { Fabricate(:status, reblog: original, thread: original) } - subject { described_class.new(status: status) } - - it 'delegates target' do - expect(status.target).not_to be_nil - expect(subject.target).to eq(status.target) - end - - it 'delegates title' do - expect(status.title).not_to be_nil - expect(subject.title).to eq(status.title) - end - - it 'delegates content' do - expect(status.content).not_to be_nil - expect(subject.content).to eq(status.content) - end - - it 'delegates thread' do - expect(status.thread).not_to be_nil - expect(subject.thread).to eq(status.thread) - end - end - end -end diff --git a/spec/models/subscription_spec.rb b/spec/models/subscription_spec.rb deleted file mode 100644 index b83979d13799f4d3134d2a0dfd2f62658d6daada..0000000000000000000000000000000000000000 --- a/spec/models/subscription_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'rails_helper' - -RSpec.describe Subscription, type: :model do - let(:alice) { Fabricate(:account, username: 'alice') } - - subject { Fabricate(:subscription, account: alice) } - - describe '#expired?' do - it 'return true when expires_at is past' do - subject.expires_at = 2.days.ago - expect(subject.expired?).to be true - end - - it 'return false when expires_at is future' do - subject.expires_at = 2.days.from_now - expect(subject.expired?).to be false - end - end - - describe 'lease_seconds' do - it 'returns the time remaining until expiration' do - datetime = 1.day.from_now - subscription = Subscription.new(expires_at: datetime) - travel_to(datetime - 12.hours) do - expect(subscription.lease_seconds).to eq(12.hours) - end - end - end - - describe 'lease_seconds=' do - it 'sets expires_at to min expiration when small value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - too_low = Subscription::MIN_EXPIRATION - 1000 - travel_to(datetime) do - subscription.lease_seconds = too_low - end - - expected = datetime + Subscription::MIN_EXPIRATION.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - - it 'sets expires_at to value when valid value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - valid = Subscription::MIN_EXPIRATION + 1000 - travel_to(datetime) do - subscription.lease_seconds = valid - end - - expected = datetime + valid.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - - it 'sets expires_at to max expiration when large value is provided' do - subscription = Subscription.new - datetime = 1.day.from_now - too_high = Subscription::MAX_EXPIRATION + 1000 - travel_to(datetime) do - subscription.lease_seconds = too_high - end - - expected = datetime + Subscription::MAX_EXPIRATION.seconds - expect(subscription.expires_at).to be_within(1.0).of(expected) - end - end -end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 9a30ceaa527d34e23d354b9467d1262fa5c147d0..df876593c9e026d9b1f0c103041a34719e112234 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -62,6 +62,10 @@ RSpec.describe Tag, type: :model do expect(subject.match('hello #one·two·three').to_s).to eq ' #one·two·three' end + it 'matches ZWNJ' do + expect(subject.match('just add #Ù†Ø±Ù…â€ŒØ§ÙØ²Ø§Ø± and').to_s).to eq ' #Ù†Ø±Ù…â€ŒØ§ÙØ²Ø§Ø±' + end + it 'does not match middle dots at the start' do expect(subject.match('hello #·one·two·three')).to be_nil end @@ -82,6 +86,40 @@ RSpec.describe Tag, type: :model do end end + describe '.find_normalized' do + it 'returns tag for a multibyte case-insensitive name' do + upcase_string = 'abcABCï½ï½‚cABCやゆよ' + downcase_string = 'abcabcï½ï½‚cï½ï½‚cやゆよ'; + + tag = Fabricate(:tag, name: downcase_string) + expect(Tag.find_normalized(upcase_string)).to eq tag + end + end + + describe '.matching_name' do + it 'returns tags for multibyte case-insensitive names' do + upcase_string = 'abcABCï½ï½‚cABCやゆよ' + downcase_string = 'abcabcï½ï½‚cï½ï½‚cやゆよ'; + + tag = Fabricate(:tag, name: downcase_string) + expect(Tag.matching_name(upcase_string)).to eq [tag] + end + end + + describe '.find_or_create_by_names' do + it 'runs a passed block once per tag regardless of duplicates' do + upcase_string = 'abcABCï½ï½‚cABCやゆよ' + downcase_string = 'abcabcï½ï½‚cï½ï½‚cやゆよ'; + count = 0 + + Tag.find_or_create_by_names([upcase_string, downcase_string]) do |tag| + count += 1 + end + + expect(count).to eq 1 + end + end + describe '.search_for' do it 'finds tag records with matching names' do tag = Fabricate(:tag, name: "match") @@ -102,8 +140,8 @@ RSpec.describe Tag, type: :model do end it 'finds the exact matching tag as the first item' do - similar_tag = Fabricate(:tag, name: "matchlater") - tag = Fabricate(:tag, name: "match") + similar_tag = Fabricate(:tag, name: "matchlater", reviewed_at: Time.now.utc) + tag = Fabricate(:tag, name: "match", reviewed_at: Time.now.utc) results = Tag.search_for("match") diff --git a/spec/models/trending_tags_spec.rb b/spec/models/trending_tags_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b6122c994817bb17fb551708bdaa0d19795eed98 --- /dev/null +++ b/spec/models/trending_tags_spec.rb @@ -0,0 +1,68 @@ +require 'rails_helper' + +RSpec.describe TrendingTags do + describe '.record_use!' do + pending + end + + describe '.update!' do + let!(:at_time) { Time.now.utc } + let!(:tag1) { Fabricate(:tag, name: 'Catstodon') } + let!(:tag2) { Fabricate(:tag, name: 'DogsOfMastodon') } + let!(:tag3) { Fabricate(:tag, name: 'OCs') } + + before do + allow(Redis.current).to receive(:pfcount) do |key| + case key + when "activity:tags:#{tag1.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts" + 2 + when "activity:tags:#{tag1.id}:#{at_time.beginning_of_day.to_i}:accounts" + 16 + when "activity:tags:#{tag2.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts" + 0 + when "activity:tags:#{tag2.id}:#{at_time.beginning_of_day.to_i}:accounts" + 4 + when "activity:tags:#{tag3.id}:#{(at_time - 1.day).beginning_of_day.to_i}:accounts" + 13 + end + end + + Redis.current.zadd('trending_tags', 0.9, tag3.id) + Redis.current.sadd("trending_tags:used:#{at_time.beginning_of_day.to_i}", [tag1.id, tag2.id]) + + tag3.update(max_score: 0.9, max_score_at: (at_time - 1.day).beginning_of_day + 12.hours) + + described_class.update!(at_time) + end + + it 'calculates and re-calculates scores' do + expect(described_class.get(10, filtered: false)).to eq [tag1, tag3] + end + + it 'omits hashtags below threshold' do + expect(described_class.get(10, filtered: false)).to_not include(tag2) + end + + it 'decays scores' do + expect(Redis.current.zscore('trending_tags', tag3.id)).to be < 0.9 + end + end + + describe '.trending?' do + let(:tag) { Fabricate(:tag) } + + before do + 10.times { |i| Redis.current.zadd('trending_tags', i + 1, Fabricate(:tag).id) } + end + + it 'returns true if the hashtag is within limit' do + Redis.current.zadd('trending_tags', 11, tag.id) + expect(described_class.trending?(tag)).to be true + end + + it 'returns false if the hashtag is outside the limit' do + Redis.current.zadd('trending_tags', 0, tag.id) + expect(described_class.trending?(tag)).to be false + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 856254ce4b6acd1908b5ace92e3e0681c0145b94..d7c0b5359338969d5a27e8eb4fe117a628dca2d4 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -506,7 +506,7 @@ RSpec.describe User, type: :model do context 'when user is not confirmed' do let(:confirmed_at) { nil } - it { is_expected.to be false } + it { is_expected.to be true } end end @@ -522,7 +522,7 @@ RSpec.describe User, type: :model do context 'when user is not confirmed' do let(:confirmed_at) { nil } - it { is_expected.to be false } + it { is_expected.to be true } end end end diff --git a/spec/policies/subscription_policy_spec.rb b/spec/policies/subscription_policy_spec.rb deleted file mode 100644 index 21d60c15fc0c95d55c15828ae23d98e65cd3a868..0000000000000000000000000000000000000000 --- a/spec/policies/subscription_policy_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'pundit/rspec' - -RSpec.describe SubscriptionPolicy do - let(:subject) { described_class } - let(:admin) { Fabricate(:user, admin: true).account } - let(:john) { Fabricate(:user).account } - - permissions :index? do - context 'admin?' do - it 'permits' do - expect(subject).to permit(admin, Subscription) - end - end - - context '!admin?' do - it 'denies' do - expect(subject).to_not permit(john, Subscription) - end - end - end -end diff --git a/spec/policies/tag_policy_spec.rb b/spec/policies/tag_policy_spec.rb index c7afaa7c93d0f6eab29832da761cb4812a5861ee..c63875dc0888a6724db99043eb3537936d3ae55a 100644 --- a/spec/policies/tag_policy_spec.rb +++ b/spec/policies/tag_policy_spec.rb @@ -8,7 +8,7 @@ RSpec.describe TagPolicy do let(:admin) { Fabricate(:user, admin: true).account } let(:john) { Fabricate(:user).account } - permissions :index?, :hide?, :unhide? do + permissions :index?, :show?, :update? do context 'staff?' do it 'permits' do expect(subject).to permit(admin, Tag) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3a5e7491e515198888d2bc25733fab67fbec2a97..6fbceca539606821c22f4d0b7ec5688d28a63884 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -12,7 +12,7 @@ require 'capybara/rspec' Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } ActiveRecord::Migration.maintain_test_schema! -WebMock.disable_net_connect! +WebMock.disable_net_connect!(allow: Chewy.settings[:host]) Redis.current = Redis::Namespace.new("mastodon_test#{ENV['TEST_ENV_NUMBER']}", redis: Redis.current) Sidekiq::Testing.inline! Sidekiq::Logging.logger = nil diff --git a/spec/requests/link_headers_spec.rb b/spec/requests/link_headers_spec.rb index 3dc408d9270dd8ff8b55085d1e645fffe31a9013..712ee262b80ca4b5cc9a7af5fecdc8c8ca719a25 100644 --- a/spec/requests/link_headers_spec.rb +++ b/spec/requests/link_headers_spec.rb @@ -11,16 +11,16 @@ describe 'Link headers' do end it 'contains webfinger url in link header' do - link_header = link_header_with_type('application/xrd+xml') + link_header = link_header_with_type('application/jrd+json') expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io' expect(link_header.attr_pairs.first).to eq %w(rel lrdd) end - it 'contains atom url in link header' do - link_header = link_header_with_type('application/atom+xml') + it 'contains activitypub url in link header' do + link_header = link_header_with_type('application/activity+json') - expect(link_header.href).to eq 'http://www.example.com/users/test.atom' + expect(link_header.href).to eq 'https://cb6e6126.ngrok.io/users/test' expect(link_header.attr_pairs.first).to eq %w(rel alternate) end diff --git a/spec/requests/webfinger_request_spec.rb b/spec/requests/webfinger_request_spec.rb index 7f9e1162e9c43c6bd50e7f44318c89bfd3fde4ca..48823714e2087b545719c0f5235ebb3b6ccf6c93 100644 --- a/spec/requests/webfinger_request_spec.rb +++ b/spec/requests/webfinger_request_spec.rb @@ -12,23 +12,6 @@ describe 'The webfinger route' do end end - describe 'asking for xml format' do - it 'returns an xml response for xml format' do - get webfinger_url(resource: alice.to_webfinger_s, format: :xml) - - expect(response).to have_http_status(200) - expect(response.content_type).to eq 'application/xrd+xml' - end - - it 'returns an xml response for xml accept header' do - headers = { 'HTTP_ACCEPT' => 'application/xrd+xml' } - get webfinger_url(resource: alice.to_webfinger_s), headers: headers - - expect(response).to have_http_status(200) - expect(response.content_type).to eq 'application/xrd+xml' - end - end - describe 'asking for json format' do it 'returns a json response for json format' do get webfinger_url(resource: alice.to_webfinger_s, format: :json) diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb index 7b071b378e04b06fe6894ad4090205982f85e7bf..5b7182586c5f2f4406eb180ffa0eadfb35f29814 100644 --- a/spec/services/account_search_service_spec.rb +++ b/spec/services/account_search_service_spec.rb @@ -1,126 +1,56 @@ require 'rails_helper' describe AccountSearchService, type: :service do - describe '.call' do - describe 'with a query to ignore' do + describe '#call' do + context 'with a query to ignore' do it 'returns empty array for missing query' do results = subject.call('', nil, limit: 10) expect(results).to eq [] end - it 'returns empty array for hashtag query' do - results = subject.call('#tag', nil, limit: 10) - expect(results).to eq [] - end it 'returns empty array for limit zero' do Fabricate(:account, username: 'match') + results = subject.call('match', nil, limit: 0) expect(results).to eq [] end end - describe 'searching for a simple term that is not an exact match' do + context 'searching for a simple term that is not an exact match' do it 'does not return a nil entry in the array for the exact match' do - match = Fabricate(:account, username: 'matchingusername') - + account = Fabricate(:account, username: 'matchingusername') results = subject.call('match', nil, limit: 5) - expect(results).to eq [match] - end - end - describe 'searching local and remote users' do - describe "when only '@'" do - before do - allow(Account).to receive(:find_local) - allow(Account).to receive(:search_for) - subject.call('@', nil, limit: 10) - end - - it 'uses find_local with empty query to look for local accounts' do - expect(Account).to have_received(:find_local).with('') - end - end - - describe 'when no domain' do - before do - allow(Account).to receive(:find_local) - allow(Account).to receive(:search_for) - subject.call('one', nil, limit: 10) - end - - it 'uses find_local to look for local accounts' do - expect(Account).to have_received(:find_local).with('one') - end - - it 'uses search_for to find matches' do - expect(Account).to have_received(:search_for).with('one', 10, 0) - end - end - - describe 'when there is a domain' do - before do - allow(Account).to receive(:find_remote) - end - - it 'uses find_remote to look for remote accounts' do - subject.call('two@example.com', nil, limit: 10) - expect(Account).to have_received(:find_remote).with('two', 'example.com') - end - - describe 'and there is no account provided' do - it 'uses search_for to find matches' do - allow(Account).to receive(:search_for) - subject.call('two@example.com', nil, limit: 10, resolve: false) - - expect(Account).to have_received(:search_for).with('two example.com', 10, 0) - end - end - - describe 'and there is an account provided' do - it 'uses advanced_search_for to find matches' do - account = Fabricate(:account) - allow(Account).to receive(:advanced_search_for) - subject.call('two@example.com', account, limit: 10, resolve: false) - - expect(Account).to have_received(:advanced_search_for).with('two example.com', account, 10, nil, 0) - end - end + expect(results).to eq [account] end end - describe 'with an exact match' do - it 'returns exact match first, and does not return duplicates' do - partial = Fabricate(:account, username: 'exactness') - exact = Fabricate(:account, username: 'exact') - - results = subject.call('exact', nil, limit: 10) - expect(results.size).to eq 2 - expect(results).to eq [exact, partial] - end - end - - describe 'when there is a local domain' do + context 'when there is a local domain' do around do |example| before = Rails.configuration.x.local_domain + example.run + Rails.configuration.x.local_domain = before end it 'returns exact match first' do remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e') remote_too = Fabricate(:account, username: 'b', domain: 'remote', display_name: 'e') - exact = Fabricate(:account, username: 'e') + exact = Fabricate(:account, username: 'e') + Rails.configuration.x.local_domain = 'example.com' results = subject.call('e@example.com', nil, limit: 2) + expect(results.size).to eq 2 expect(results).to eq([exact, remote]).or eq([exact, remote_too]) end end - describe 'when there is a domain but no exact match' do + context 'when there is a domain but no exact match' do it 'follows the remote account when resolve is true' do service = double(call: nil) allow(ResolveAccountService).to receive(:new).and_return(service) @@ -138,23 +68,21 @@ describe AccountSearchService, type: :service do end end - describe 'should not include suspended accounts' do - it 'returns the fuzzy match first, and does not return suspended exacts' do - partial = Fabricate(:account, username: 'exactness') - exact = Fabricate(:account, username: 'exact', suspended: true) + it 'returns the fuzzy match first, and does not return suspended exacts' do + partial = Fabricate(:account, username: 'exactness') + exact = Fabricate(:account, username: 'exact', suspended: true) + results = subject.call('exact', nil, limit: 10) - results = subject.call('exact', nil, limit: 10) - expect(results.size).to eq 1 - expect(results).to eq [partial] - end + expect(results.size).to eq 1 + expect(results).to eq [partial] + end - it "does not return suspended remote accounts" do - remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true) + it "does not return suspended remote accounts" do + remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true) + results = subject.call('a@example.com', nil, limit: 2) - results = subject.call('a@example.com', nil, limit: 2) - expect(results.size).to eq 0 - expect(results).to eq [] - end + expect(results.size).to eq 0 + expect(results).to eq [] end end end diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 9ae4099969405194c47006a7c3313a6afb5ab869..78dd59e3b6ccb743afc2d07988025462338b0ea1 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -71,6 +71,39 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do end end + context 'with Audio object' do + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234", + type: 'Audio', + name: 'Nyan Cat 10 hours remix', + attributedTo: ActivityPub::TagManager.instance.uri_for(sender), + url: [ + { + type: 'Link', + mimeType: 'application/x-bittorrent', + href: "https://#{valid_domain}/12345.torrent", + }, + + { + type: 'Link', + mimeType: 'text/html', + href: "https://#{valid_domain}/watch?v=12345", + }, + ], + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.url).to eq "https://#{valid_domain}/watch?v=12345" + expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix https://#{valid_domain}/watch?v=12345" + end + end + context 'with wrong id' do let(:note) do { diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb index 7948bb53be10e4546129e1bd3fd6674f2f422a66..e7c7f3ba15e6bfcb69df2996297f7e556be44cb7 100644 --- a/spec/services/app_sign_up_service_spec.rb +++ b/spec/services/app_sign_up_service_spec.rb @@ -38,6 +38,15 @@ RSpec.describe AppSignUpService, type: :service do user = User.find_by(id: access_token.resource_owner_id) expect(user).to_not be_nil expect(user.account).to_not be_nil + expect(user.invite_request).to be_nil + end + + it 'creates an account with invite request text' do + access_token = subject.call(app, good_params.merge(reason: 'Foo bar')) + expect(access_token).to_not be_nil + user = User.find_by(id: access_token.resource_owner_id) + expect(user).to_not be_nil + expect(user.invite_request&.text).to eq 'Foo bar' end end end diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb index 562ef004124712f11ae7342b8fbc1998f758f294..ce56d57a6217673bd0ffa5b37bc1b260b048a720 100644 --- a/spec/services/authorize_follow_service_spec.rb +++ b/spec/services/authorize_follow_service_spec.rb @@ -38,13 +38,6 @@ RSpec.describe AuthorizeFollowService, type: :service do it 'creates follow relation' do expect(bob.following?(sender)).to be true end - - it 'sends a follow request authorization salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:authorize]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index e53623449f78d56abce08e5169cd53c72a2558a5..f84256f187e17238b4b95026b65d9de32d9a005a 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -14,11 +14,8 @@ RSpec.describe BatchedRemoveStatusService, type: :service do before do allow(Redis.current).to receive_messages(publish: nil) - stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {}) - stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200) - Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now) jeff.user.update(current_sign_in_at: Time.zone.now) jeff.follow!(alice) hank.follow!(alice) @@ -49,19 +46,6 @@ RSpec.describe BatchedRemoveStatusService, type: :service do expect(Redis.current).to have_received(:publish).with('timeline:public', any_args).at_least(:once) end - it 'sends PuSH update to PuSH subscribers' do - expect(a_request(:post, 'http://example.com/push').with { |req| - matches = req.body.match(OStatus::TagManager::VERBS[:delete]) - }).to have_been_made.at_least_once - end - - it 'sends Salmon slap to previously mentioned users' do - expect(a_request(:post, "http://example.com/salmon").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:delete]) - }).to have_been_made.once - end - it 'sends delete activity to followers' do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.at_least_once end diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb index 6584bb90ed64c78bd8806ac80f8c1fa182594727..de20dd026502ec95a271bd0c24d1a4d0a798d314 100644 --- a/spec/services/block_service_spec.rb +++ b/spec/services/block_service_spec.rb @@ -28,13 +28,6 @@ RSpec.describe BlockService, type: :service do it 'creates a blocking relation' do expect(sender.blocking?(bob)).to be true end - - it 'sends a block salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:block]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/bootstrap_timeline_service_spec.rb b/spec/services/bootstrap_timeline_service_spec.rb index a765de79127baf61314f396a6af3679066a48c98..a28d2407c1cf7ca25b778c3a6558c3925d1e7a70 100644 --- a/spec/services/bootstrap_timeline_service_spec.rb +++ b/spec/services/bootstrap_timeline_service_spec.rb @@ -22,9 +22,10 @@ RSpec.describe BootstrapTimelineService, type: :service do context 'when setting is set' do let!(:alice) { Fabricate(:account, username: 'alice') } let!(:bob) { Fabricate(:account, username: 'bob') } + let!(:eve) { Fabricate(:account, username: 'eve', suspended: true) } before do - Setting.bootstrap_timeline_accounts = 'alice, bob' + Setting.bootstrap_timeline_accounts = 'alice, @bob, eve, unknown' subject.call(source_account) end @@ -32,6 +33,10 @@ RSpec.describe BootstrapTimelineService, type: :service do expect(source_account.following?(alice)).to be true expect(source_account.following?(bob)).to be true end + + it 'does not follow suspended account' do + expect(source_account.following?(eve)).to be false + end end end end diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb index 0a20ccf6e4c4713472cab6275c3bf8eeea1a862e..4c29ea77b72c6d6beb7ddd4497886ad3a8a79316 100644 --- a/spec/services/favourite_service_spec.rb +++ b/spec/services/favourite_service_spec.rb @@ -30,13 +30,6 @@ RSpec.describe FavouriteService, type: :service do it 'creates a favourite' do expect(status.favourites.first).to_not be_nil end - - it 'sends a salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:favorite]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 50c60aafd1d6dec13d8b5cd06482da34ca14bd4a..9761c5f0649623ec3193fa8ccaf35075f20f14a3 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -4,20 +4,13 @@ RSpec.describe FetchLinkCardService, type: :service do subject { FetchLinkCardService.new } before do - stub_request(:head, 'http://example.xn--fiqs8s/').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.xn--fiqs8s/').to_return(request_fixture('idn.txt')) - stub_request(:head, 'http://example.com/sjis').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.com/sjis').to_return(request_fixture('sjis.txt')) - stub_request(:head, 'http://example.com/sjis_with_wrong_charset').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.com/sjis_with_wrong_charset').to_return(request_fixture('sjis_with_wrong_charset.txt')) - stub_request(:head, 'http://example.com/koi8-r').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt')) - stub_request(:head, 'http://example.com/日本語').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt')) - stub_request(:head, 'https://github.com/qbi/WannaCry').to_return(status: 404) - stub_request(:head, 'http://example.com/test-').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) + stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404) stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt')) - stub_request(:head, 'http://example.com/windows-1251').to_return(status: 200, headers: { 'Content-Type' => 'text/html' }) stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt')) subject.call(status) @@ -90,11 +83,11 @@ RSpec.describe FetchLinkCardService, type: :service do let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a> ') } it 'parses out URLs' do - expect(a_request(:head, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once + expect(a_request(:get, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once end it 'ignores URLs to hashtags' do - expect(a_request(:head, 'https://quitter.se/tag/wannacry')).to_not have_been_made + expect(a_request(:get, 'https://quitter.se/tag/wannacry')).to_not have_been_made end end end diff --git a/spec/services/fetch_remote_account_service_spec.rb b/spec/services/fetch_remote_account_service_spec.rb index 3cd86708be4339746ce8f8ca9a63783e1bd2bc90..ee7325be28671b9c0fd61f34388d23e736e77b2a 100644 --- a/spec/services/fetch_remote_account_service_spec.rb +++ b/spec/services/fetch_remote_account_service_spec.rb @@ -4,6 +4,7 @@ RSpec.describe FetchRemoteAccountService, type: :service do let(:url) { 'https://example.com/alice' } let(:prefetched_body) { nil } let(:protocol) { :ostatus } + subject { FetchRemoteAccountService.new.call(url, prefetched_body, protocol) } let(:actor) do @@ -36,36 +37,6 @@ RSpec.describe FetchRemoteAccountService, type: :service do include_examples 'return Account' end - context 'protocol is :ostatus' do - let(:prefetched_body) { xml } - let(:protocol) { :ostatus } - - before do - stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt')) - stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt')) - end - - include_examples 'return Account' - - it 'does not update account information if XML comes from an unverified domain' do - feed_xml = <<-XML.squish - <?xml version="1.0" encoding="UTF-8"?> - <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/"> - <author> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>http://kickass.zone/users/localhost</uri> - <name>localhost</name> - <poco:preferredUsername>localhost</poco:preferredUsername> - <poco:displayName>Villain!!!</poco:displayName> - </author> - </feed> - XML - - returned_account = described_class.new.call('https://real-fake-domains.com/alice', feed_xml, :ostatus) - expect(returned_account.display_name).to_not eq 'Villain!!!' - end - end - context 'when prefetched_body is nil' do context 'protocol is :activitypub' do before do @@ -75,15 +46,5 @@ RSpec.describe FetchRemoteAccountService, type: :service do include_examples 'return Account' end - - context 'protocol is :ostatus' do - before do - stub_request(:get, url).to_return(status: 200, body: xml, headers: { 'Content-Type' => 'application/atom+xml' }) - stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt')) - stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt')) - end - - include_examples 'return Account' - end end end diff --git a/spec/services/fetch_atom_service_spec.rb b/spec/services/fetch_resource_service_spec.rb similarity index 50% rename from spec/services/fetch_atom_service_spec.rb rename to spec/services/fetch_resource_service_spec.rb index 495540004e9504d9d782283f7e91dd6379647991..f836147d354f3d13aff473705c8830a0983ba59c 100644 --- a/spec/services/fetch_atom_service_spec.rb +++ b/spec/services/fetch_resource_service_spec.rb @@ -1,73 +1,80 @@ require 'rails_helper' -RSpec.describe FetchAtomService, type: :service do +RSpec.describe FetchResourceService, type: :service do describe '#call' do let(:url) { 'http://example.com' } - subject { FetchAtomService.new.call(url) } - context 'url is blank' do + subject { described_class.new.call(url) } + + context 'with blank url' do let(:url) { '' } it { is_expected.to be_nil } end - context 'request failed' do + context 'when request fails' do before do - WebMock.stub_request(:get, url).to_return(status: 500, body: '', headers: {}) + stub_request(:get, url).to_return(status: 500, body: '', headers: {}) end it { is_expected.to be_nil } end - context 'raise OpenSSL::SSL::SSLError' do + context 'when OpenSSL::SSL::SSLError is raised' do before do - allow(Request).to receive_message_chain(:new, :add_headers, :perform).and_raise(OpenSSL::SSL::SSLError) + allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(OpenSSL::SSL::SSLError) end - it 'output log and return nil' do - expect_any_instance_of(ActiveSupport::Logger).to receive(:debug).with('SSL error: OpenSSL::SSL::SSLError') - is_expected.to be_nil - end + it { is_expected.to be_nil } end - context 'raise HTTP::ConnectionError' do + context 'when HTTP::ConnectionError is raised' do before do - allow(Request).to receive_message_chain(:new, :add_headers, :perform).and_raise(HTTP::ConnectionError) + allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(HTTP::ConnectionError) end - it 'output log and return nil' do - expect_any_instance_of(ActiveSupport::Logger).to receive(:debug).with('HTTP ConnectionError: HTTP::ConnectionError') - is_expected.to be_nil - end + it { is_expected.to be_nil } end - context 'response success' do + context 'when request succeeds' do let(:body) { '' } - let(:headers) { { 'Content-Type' => content_type } } - let(:json) { - { id: 1, + + let(:content_type) { 'application/json' } + + let(:headers) do + { 'Content-Type' => content_type } + end + + let(:json) do + { + id: 1, '@context': ActivityPub::TagManager::CONTEXT, type: 'Note', }.to_json - } + end before do - WebMock.stub_request(:get, url).to_return(status: 200, body: body, headers: headers) + stub_request(:get, url).to_return(status: 200, body: body, headers: headers) + end + + it 'signs request' do + subject + expect(a_request(:get, url).with(headers: { 'Signature' => /keyId="#{Regexp.escape(ActivityPub::TagManager.instance.uri_for(Account.representative) + '#main-key')}"/ })).to have_been_made end - context 'content type is application/atom+xml' do + context 'when content type is application/atom+xml' do let(:content_type) { 'application/atom+xml' } - it { is_expected.to eq [url, { :prefetched_body => "" }, :ostatus] } + it { is_expected.to eq nil } end - context 'content_type is activity+json' do + context 'when content type is activity+json' do let(:content_type) { 'application/activity+json; charset=utf-8' } let(:body) { json } it { is_expected.to eq [1, { prefetched_body: body, id: true }, :activitypub] } end - context 'content_type is ld+json with profile' do + context 'when content type is ld+json with profile' do let(:content_type) { 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' } let(:body) { json } @@ -75,17 +82,17 @@ RSpec.describe FetchAtomService, type: :service do end before do - WebMock.stub_request(:get, url).to_return(status: 200, body: body, headers: headers) - WebMock.stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' }) + stub_request(:get, url).to_return(status: 200, body: body, headers: headers) + stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' }) end - context 'has link header' do + context 'when link header is present' do let(:headers) { { 'Link' => '<http://example.com/foo>; rel="alternate"; type="application/activity+json"', } } it { is_expected.to eq [1, { prefetched_body: json, id: true }, :activitypub] } end - context 'content type is text/html' do + context 'when content type is text/html' do let(:content_type) { 'text/html' } let(:body) { '<html><head><link rel="alternate" href="http://example.com/foo" type="application/activity+json"/></head></html>' } diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb index 3c4ec59be07a8cc45cf3f132bd27c7456731de24..ae863a9f0e98451ce3dc04e683fd6e9162ad67fb 100644 --- a/spec/services/follow_service_spec.rb +++ b/spec/services/follow_service_spec.rb @@ -30,6 +30,33 @@ RSpec.describe FollowService, type: :service do end end + describe 'unlocked account, from silenced account' do + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + + before do + sender.touch(:silenced_at) + subject.call(sender, bob.acct) + end + + it 'creates a follow request with reblogs' do + expect(FollowRequest.find_by(account: sender, target_account: bob, show_reblogs: true)).to_not be_nil + end + end + + describe 'unlocked account, from a muted account' do + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + + before do + bob.mute!(sender) + subject.call(sender, bob.acct) + end + + it 'creates a following relation with reblogs' do + expect(sender.following?(bob)).to be true + expect(sender.muting_reblogs?(bob)).to be false + end + end + describe 'unlocked account' do let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } @@ -96,74 +123,6 @@ RSpec.describe FollowService, type: :service do end end - context 'remote OStatus account' do - describe 'locked account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, locked: true, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account } - - before do - stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {}) - subject.call(sender, bob.acct) - end - - it 'creates a follow request' do - expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil - end - - it 'sends a follow request salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:request_friend]) - }).to have_been_made.once - end - end - - describe 'unlocked account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account } - - before do - stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:post, "http://hub.example.com/").to_return(status: 202) - subject.call(sender, bob.acct) - end - - it 'creates a following relation' do - expect(sender.following?(bob)).to be true - end - - it 'sends a follow salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:follow]) - }).to have_been_made.once - end - - it 'subscribes to PuSH' do - expect(a_request(:post, "http://hub.example.com/")).to have_been_made.once - end - end - - describe 'already followed account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account } - - before do - sender.follow!(bob) - subject.call(sender, bob.acct) - end - - it 'keeps a following relation' do - expect(sender.following?(bob)).to be true - end - - it 'does not send a follow salmon slap' do - expect(a_request(:post, "http://salmon.example.com/")).not_to have_been_made - end - - it 'does not subscribe to PuSH' do - expect(a_request(:post, "http://hub.example.com/")).not_to have_been_made - end - end - end - context 'remote ActivityPub account' do let(:bob) { Fabricate(:user, account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account } diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb index 5cf2dadf06c9c22396549327a7bcbcc2671bb6a4..5355133f4000826dcca7529f38015d7c0dc5abf1 100644 --- a/spec/services/import_service_spec.rb +++ b/spec/services/import_service_spec.rb @@ -3,7 +3,11 @@ require 'rails_helper' RSpec.describe ImportService, type: :service do let!(:account) { Fabricate(:account, locked: false) } let!(:bob) { Fabricate(:account, username: 'bob', locked: false) } - let!(:eve) { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false) } + let!(:eve) { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false, protocol: :activitypub, inbox_url: 'https://example.com/inbox') } + + before do + stub_request(:post, "https://example.com/inbox").to_return(status: 200) + end context 'import old-style list of muted users' do subject { ImportService.new } @@ -95,7 +99,8 @@ RSpec.describe ImportService, type: :service do let(:import) { Import.create(account: account, type: 'following', data: csv) } it 'follows the listed accounts, including boosts' do subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true end end @@ -106,7 +111,8 @@ RSpec.describe ImportService, type: :service do it 'follows the listed accounts, including notifications' do account.follow!(bob, reblogs: false) subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true end end @@ -117,7 +123,8 @@ RSpec.describe ImportService, type: :service do it 'mutes the listed accounts, including notifications' do account.follow!(bob, reblogs: false) subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true end end @@ -136,9 +143,10 @@ RSpec.describe ImportService, type: :service do let(:import) { Import.create(account: account, type: 'following', data: csv) } it 'follows the listed accounts, respecting boosts' do subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true - expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false + expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false end end @@ -148,9 +156,10 @@ RSpec.describe ImportService, type: :service do it 'mutes the listed accounts, respecting notifications' do account.follow!(bob, reblogs: true) subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true - expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false + expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false end end @@ -160,9 +169,10 @@ RSpec.describe ImportService, type: :service do it 'mutes the listed accounts, respecting notifications' do account.follow!(bob, reblogs: true) subject.call(import) - expect(account.following.count).to eq 2 + expect(account.following.count).to eq 1 + expect(account.follow_requests.count).to eq 1 expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true - expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false + expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false end end end diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index facbe977f2333096f345c8b9e27ad082ae3bb63f..bf06f50e9bd3dc68563181b8eda13175a36c0d7f 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -144,7 +144,6 @@ RSpec.describe PostStatusService, type: :service do it 'gets distributed' do allow(DistributionWorker).to receive(:perform_async) - allow(Pubsubhubbub::DistributionWorker).to receive(:perform_async) allow(ActivityPub::DistributionWorker).to receive(:perform_async) account = Fabricate(:account) @@ -152,7 +151,6 @@ RSpec.describe PostStatusService, type: :service do status = subject.call(account, text: "test status update") expect(DistributionWorker).to have_received(:perform_async).with(status.id) - expect(Pubsubhubbub::DistributionWorker).to have_received(:perform_async).with(status.stream_entry.id) expect(ActivityPub::DistributionWorker).to have_received(:perform_async).with(status.id) end diff --git a/spec/services/process_feed_service_spec.rb b/spec/services/process_feed_service_spec.rb deleted file mode 100644 index 9d3465f3f8ddac3eb9ce125d41b92fc0681a7507..0000000000000000000000000000000000000000 --- a/spec/services/process_feed_service_spec.rb +++ /dev/null @@ -1,252 +0,0 @@ -require 'rails_helper' - -RSpec.describe ProcessFeedService, type: :service do - subject { ProcessFeedService.new } - - describe 'processing a feed' do - let(:body) { File.read(Rails.root.join('spec', 'fixtures', 'xml', 'mastodon.atom')) } - let(:account) { Fabricate(:account, username: 'localhost', domain: 'kickass.zone') } - - before do - stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {}) - stub_request(:head, "http://kickass.zone/media/2").to_return(:status => 404) - stub_request(:head, "http://kickass.zone/media/3").to_return(:status => 404) - stub_request(:get, "http://kickass.zone/system/accounts/avatars/000/000/001/large/eris.png").to_return(request_fixture('avatar.txt')) - stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/002/original/morpheus_linux.jpg?1476059910").to_return(request_fixture('attachment1.txt')) - stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/003/original/gizmo.jpg?1476060065").to_return(request_fixture('attachment2.txt')) - end - - context 'when domain does not reject media' do - before do - subject.call(body, account) - end - - it 'updates remote user\'s account information' do - account.reload - expect(account.display_name).to eq '::1' - expect(account).to have_attached_file(:avatar) - expect(account.avatar_file_name).not_to be_nil - end - - it 'creates posts' do - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil - end - - it 'marks replies as replies' do - status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status') - expect(status.reply?).to be true - end - - it 'sets account being replied to when possible' do - status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status') - expect(status.in_reply_to_account_id).to eq status.account_id - end - - it 'ignores delete statuses unless they existed before' do - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Status')).to be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=12:objectType=Status')).to be_nil - end - - it 'does not create statuses for follows' do - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow')).to be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow')).to be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow')).to be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow')).to be_nil - end - - it 'does not create statuses for favourites' do - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite')).to be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite')).to be_nil - end - - it 'creates posts with media' do - status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status') - - expect(status).to_not be_nil - expect(status.media_attachments.first).to have_attached_file(:file) - expect(status.media_attachments.first.image?).to be true - expect(status.media_attachments.first.file_file_name).not_to be_nil - end - end - - context 'when domain is set to reject media' do - let!(:domain_block) { Fabricate(:domain_block, domain: 'kickass.zone', reject_media: true) } - - before do - subject.call(body, account) - end - - it 'updates remote user\'s account information' do - account.reload - expect(account.display_name).to eq '::1' - end - - it 'rejects remote user\'s avatar' do - account.reload - expect(account.display_name).to eq '::1' - expect(account.avatar_file_name).to be_nil - end - - it 'creates posts' do - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil - expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil - end - - it 'creates posts with remote-only media' do - status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status') - - expect(status).to_not be_nil - expect(status.media_attachments.first.file_file_name).to be_nil - expect(status.media_attachments.first.unknown?).to be true - end - end - end - - it 'does not accept tampered reblogs' do - good_actor = Fabricate(:account, username: 'tracer', domain: 'overwatch.com') - - real_body = <<XML -<?xml version="1.0"?> -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> - <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id> - <published>2017-04-27T13:49:25Z</published> - <updated>2017-04-27T13:49:25Z</updated> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> - <content type="html">Overwatch rocks</content> -</entry> -XML - - stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 200, body: real_body, headers: { 'Content-Type' => 'application/atom+xml' }) - - bad_actor = Fabricate(:account, username: 'sombra', domain: 'talon.xyz') - - body = <<XML -<?xml version="1.0"?> -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> - <id>tag:talon.xyz,2017-04-27:objectId=4467137:objectType=Status</id> - <published>2017-04-27T13:49:25Z</published> - <updated>2017-04-27T13:49:25Z</updated> - <author> - <id>https://talon.xyz/users/sombra</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://talon.xyz/users/sombra</uri> - <name>sombra</name> - </author> - <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb> - <content type="html">Overwatch SUCKS AHAHA</content> - <activity:object> - <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> - <content type="html">Overwatch SUCKS AHAHA</content> - <link rel="alternate" type="text/html" href="https://overwatch.com/users/tracer/updates/1" /> - </activity:object> -</entry> -XML - created_statuses = subject.call(body, bad_actor) - - expect(created_statuses.first.reblog?).to be true - expect(created_statuses.first.account_id).to eq bad_actor.id - expect(created_statuses.first.reblog.account_id).to eq good_actor.id - expect(created_statuses.first.reblog.text).to eq 'Overwatch rocks' - end - - it 'ignores reblogs if it failed to retrieve reblogged statuses' do - stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 404) - - actor = Fabricate(:account, username: 'tracer', domain: 'overwatch.com') - - body = <<XML -<?xml version="1.0"?> -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> - <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id> - <published>2017-04-27T13:49:25Z</published> - <updated>2017-04-27T13:49:25Z</updated> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> - <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb> - <content type="html">Overwatch rocks</content> - <activity:object> - <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> - <content type="html">Overwatch rocks</content> - <link rel="alternate" type="text/html" href="https://overwatch.com/users/tracer/updates/1" /> - </activity:object> -XML - - created_statuses = subject.call(body, actor) - - expect(created_statuses).to eq [] - end - - it 'ignores statuses with an out-of-order delete' do - sender = Fabricate(:account, username: 'tracer', domain: 'overwatch.com') - - delete_body = <<XML -<?xml version="1.0"?> -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> - <id>tag:overwatch.com,2017-04-27:objectId=4487555:objectType=Status</id> - <published>2017-04-27T13:49:25Z</published> - <updated>2017-04-27T13:49:25Z</updated> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> -</entry> -XML - - status_body = <<XML -<?xml version="1.0"?> -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> - <id>tag:overwatch.com,2017-04-27:objectId=4487555:objectType=Status</id> - <published>2017-04-27T13:49:25Z</published> - <updated>2017-04-27T13:49:25Z</updated> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> - <author> - <id>https://overwatch.com/users/tracer</id> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <uri>https://overwatch.com/users/tracer</uri> - <name>tracer</name> - </author> - <content type="html">Overwatch rocks</content> -</entry> -XML - - subject.call(delete_body, sender) - created_statuses = subject.call(status_body, sender) - - expect(created_statuses).to be_empty - end -end diff --git a/spec/services/process_interaction_service_spec.rb b/spec/services/process_interaction_service_spec.rb deleted file mode 100644 index b858c19d0f94eec99d9f9524c9c51cb249f93dfe..0000000000000000000000000000000000000000 --- a/spec/services/process_interaction_service_spec.rb +++ /dev/null @@ -1,151 +0,0 @@ -require 'rails_helper' - -RSpec.describe ProcessInteractionService, type: :service do - let(:receiver) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account } - let(:sender) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } - let(:remote_sender) { Fabricate(:account, username: 'carol', domain: 'localdomain.com', uri: 'https://webdomain.com/users/carol') } - - subject { ProcessInteractionService.new } - - describe 'status delete slap' do - let(:remote_status) { Fabricate(:status, account: remote_sender) } - let(:envelope) { OStatus2::Salmon.new.pack(payload, sender.keypair) } - let(:payload) { - <<~XML - <entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/"> - <author> - <email>carol@localdomain.com</email> - <name>carol</name> - <uri>https://webdomain.com/users/carol</uri> - </author> - - <id>#{remote_status.id}</id> - <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb> - </entry> - XML - } - - before do - receiver.update(locked: true) - remote_sender.update(private_key: sender.private_key, public_key: remote_sender.public_key) - end - - it 'deletes a record' do - expect(RemovalWorker).to receive(:perform_async).with(remote_status.id) - subject.call(envelope, receiver) - end - end - - describe 'follow request slap' do - before do - receiver.update(locked: true) - - payload = <<XML -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/"> - <author> - <name>bob</name> - <uri>https://cb6e6126.ngrok.io/users/bob</uri> - </author> - - <id>someIdHere</id> - <activity:verb>http://activitystrea.ms/schema/1.0/request-friend</activity:verb> -</entry> -XML - - envelope = OStatus2::Salmon.new.pack(payload, sender.keypair) - subject.call(envelope, receiver) - end - - it 'creates a record' do - expect(FollowRequest.find_by(account: sender, target_account: receiver)).to_not be_nil - end - end - - describe 'follow request slap from known remote user identified by email' do - before do - receiver.update(locked: true) - # Copy already-generated key - remote_sender.update(private_key: sender.private_key, public_key: remote_sender.public_key) - - payload = <<XML -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/"> - <author> - <email>carol@localdomain.com</email> - <name>carol</name> - <uri>https://webdomain.com/users/carol</uri> - </author> - - <id>someIdHere</id> - <activity:verb>http://activitystrea.ms/schema/1.0/request-friend</activity:verb> -</entry> -XML - - envelope = OStatus2::Salmon.new.pack(payload, remote_sender.keypair) - subject.call(envelope, receiver) - end - - it 'creates a record' do - expect(FollowRequest.find_by(account: remote_sender, target_account: receiver)).to_not be_nil - end - end - - describe 'follow request authorization slap' do - before do - receiver.update(locked: true) - FollowRequest.create(account: sender, target_account: receiver) - - payload = <<XML -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/"> - <author> - <name>alice</name> - <uri>https://cb6e6126.ngrok.io/users/alice</uri> - </author> - - <id>someIdHere</id> - <activity:verb>http://activitystrea.ms/schema/1.0/authorize</activity:verb> -</entry> -XML - - envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair) - subject.call(envelope, sender) - end - - it 'creates a follow relationship' do - expect(Follow.find_by(account: sender, target_account: receiver)).to_not be_nil - end - - it 'removes the follow request' do - expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil - end - end - - describe 'follow request rejection slap' do - before do - receiver.update(locked: true) - FollowRequest.create(account: sender, target_account: receiver) - - payload = <<XML -<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/"> - <author> - <name>alice</name> - <uri>https://cb6e6126.ngrok.io/users/alice</uri> - </author> - - <id>someIdHere</id> - <activity:verb>http://activitystrea.ms/schema/1.0/reject</activity:verb> -</entry> -XML - - envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair) - subject.call(envelope, sender) - end - - it 'does not create a follow relationship' do - expect(Follow.find_by(account: sender, target_account: receiver)).to be_nil - end - - it 'removes the follow request' do - expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil - end - end -end diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb index 8a6bb44acaad6ba4abf6ae4aa241a125512bc8ad..b1abd79b0f2b5ceb8d12ca374f3038428df067b2 100644 --- a/spec/services/process_mentions_service_spec.rb +++ b/spec/services/process_mentions_service_spec.rb @@ -15,12 +15,8 @@ RSpec.describe ProcessMentionsService, type: :service do subject.call(status) end - it 'creates a mention' do - expect(remote_user.mentions.where(status: status).count).to eq 1 - end - - it 'posts to remote user\'s Salmon end point' do - expect(a_request(:post, remote_user.salmon_url)).to have_been_made.once + it 'does not create a mention' do + expect(remote_user.mentions.where(status: status).count).to eq 0 end end diff --git a/spec/services/pubsubhubbub/subscribe_service_spec.rb b/spec/services/pubsubhubbub/subscribe_service_spec.rb deleted file mode 100644 index 01c956230a73bd52be20c0c03f245f2f44bb2fcd..0000000000000000000000000000000000000000 --- a/spec/services/pubsubhubbub/subscribe_service_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Pubsubhubbub::SubscribeService, type: :service do - describe '#call' do - subject { described_class.new } - let(:user_account) { Fabricate(:account) } - - context 'with a nil account' do - it 'returns the invalid topic status results' do - result = service_call(account: nil) - - expect(result).to eq invalid_topic_status - end - end - - context 'with an invalid callback url' do - it 'returns invalid callback status when callback is blank' do - result = service_call(callback: '') - - expect(result).to eq invalid_callback_status - end - it 'returns invalid callback status when callback is not a URI' do - result = service_call(callback: 'invalid-hostname') - - expect(result).to eq invalid_callback_status - end - end - - context 'with a blocked domain in the callback' do - it 'returns callback not allowed' do - Fabricate(:domain_block, domain: 'test.host', severity: :suspend) - result = service_call(callback: 'https://test.host/api') - - expect(result).to eq not_allowed_callback_status - end - end - - context 'with a valid account and callback' do - it 'returns success status and confirms subscription' do - allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil) - subscription = Fabricate(:subscription, account: user_account) - - result = service_call(callback: subscription.callback_url) - expect(result).to eq success_status - expect(Pubsubhubbub::ConfirmationWorker).to have_received(:perform_async).with(subscription.id, 'subscribe', 'asdf', 3600) - end - end - end - - def service_call(account: user_account, callback: 'https://callback.host', secret: 'asdf', lease_seconds: 3600) - subject.call(account, callback, secret, lease_seconds) - end - - def invalid_topic_status - ['Invalid topic URL', 422] - end - - def invalid_callback_status - ['Invalid callback URL', 422] - end - - def not_allowed_callback_status - ['Callback URL not allowed', 403] - end - - def success_status - ['', 202] - end -end diff --git a/spec/services/pubsubhubbub/unsubscribe_service_spec.rb b/spec/services/pubsubhubbub/unsubscribe_service_spec.rb deleted file mode 100644 index 7ed9fc5af5fcdd2f64656d0f795848626cdc01cb..0000000000000000000000000000000000000000 --- a/spec/services/pubsubhubbub/unsubscribe_service_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Pubsubhubbub::UnsubscribeService, type: :service do - describe '#call' do - subject { described_class.new } - - context 'with a nil account' do - it 'returns an invalid topic status' do - result = subject.call(nil, 'callback.host') - - expect(result).to eq invalid_topic_status - end - end - - context 'with a valid account' do - let(:account) { Fabricate(:account) } - - it 'returns a valid topic status and does not run confirm when no subscription' do - allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil) - result = subject.call(account, 'callback.host') - - expect(result).to eq valid_topic_status - expect(Pubsubhubbub::ConfirmationWorker).not_to have_received(:perform_async) - end - - it 'returns a valid topic status and does run confirm when there is a subscription' do - subscription = Fabricate(:subscription, account: account, callback_url: 'callback.host') - allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil) - result = subject.call(account, 'callback.host') - - expect(result).to eq valid_topic_status - expect(Pubsubhubbub::ConfirmationWorker).to have_received(:perform_async).with(subscription.id, 'unsubscribe') - end - end - - def invalid_topic_status - ['Invalid topic URL', 422] - end - - def valid_topic_status - ['', 202] - end - end -end diff --git a/spec/services/reblog_service_spec.rb b/spec/services/reblog_service_spec.rb index 9d84c41d5eaadeabf18f0180ebbf693c4c442d96..58fb46f0ff1bb7f1bd7e1105632d30b69d8c67b6 100644 --- a/spec/services/reblog_service_spec.rb +++ b/spec/services/reblog_service_spec.rb @@ -46,10 +46,6 @@ RSpec.describe ReblogService, type: :service do it 'creates a reblog' do expect(status.reblogs.count).to eq 1 end - - it 'sends a Salmon slap for a remote reblog' do - expect(a_request(:post, 'http://salmon.example.com')).to have_been_made - end end context 'ActivityPub' do diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb index e5ac37ed9037a091e38ea2f395ced23598dba662..1aec060db7a8f9e20dba5c0d1140bd282eda0861 100644 --- a/spec/services/reject_follow_service_spec.rb +++ b/spec/services/reject_follow_service_spec.rb @@ -38,13 +38,6 @@ RSpec.describe RejectFollowService, type: :service do it 'does not create follow relation' do expect(bob.following?(sender)).to be false end - - it 'sends a follow request rejection salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:reject]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 7bba83a60213f7edd345580834bb9f5b2534837c..06676ec45e20dd4120ab40fa7e2c9f749010ae02 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -10,12 +10,9 @@ RSpec.describe RemoveStatusService, type: :service do let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') } before do - stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {}) - stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example2.com/inbox').to_return(status: 200) - Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now) jeff.follow!(alice) hank.follow!(alice) @@ -32,23 +29,10 @@ RSpec.describe RemoveStatusService, type: :service do expect(HomeFeed.new(jeff).get(10)).to_not include(@status.id) end - it 'sends PuSH update to PuSH subscribers' do - expect(a_request(:post, 'http://example.com/push').with { |req| - req.body.match(OStatus::TagManager::VERBS[:delete]) - }).to have_been_made - end - it 'sends delete activity to followers' do expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.twice end - it 'sends Salmon slap to previously mentioned users' do - expect(a_request(:post, "http://example.com/salmon").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:delete]) - }).to have_been_made.once - end - it 'sends delete activity to rebloggers' do expect(a_request(:post, 'http://example2.com/inbox')).to have_been_made end diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 27a85af7c9778bd1469fc585af837396d8d41609..cea942e39cb8b0aab2d7b628819fd6b65268b827 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -6,19 +6,13 @@ RSpec.describe ResolveAccountService, type: :service do before do stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt')) stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404) - stub_request(:get, "https://redirected.com/.well-known/host-meta").to_return(request_fixture('redirected.host-meta.txt')) stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404) - stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt')) - stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:gargron@redirected.com").to_return(request_fixture('webfinger.txt')) - stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker1@redirected.com").to_return(request_fixture('webfinger-hacker1.txt')) - stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker2@redirected.com").to_return(request_fixture('webfinger-hacker2.txt')) - stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404) - stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt')) stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt')) - stub_request(:get, "https://localdomain.com/.well-known/host-meta").to_return(request_fixture('localdomain-hostmeta.txt')) - stub_request(:get, "https://localdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(status: 404) - stub_request(:get, "https://webdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(request_fixture('localdomain-webfinger.txt')) - stub_request(:get, "https://webdomain.com/users/foo.atom").to_return(request_fixture('localdomain-feed.txt')) + stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404) + stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt')) + stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt')) + stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt')) + stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404) end it 'raises error if no such user can be resolved via webfinger' do @@ -29,74 +23,7 @@ RSpec.describe ResolveAccountService, type: :service do expect(subject.call('catsrgr8@example.com')).to be_nil end - it 'prevents hijacking existing accounts' do - account = subject.call('hacker1@redirected.com') - expect(account.salmon_url).to_not eq 'https://hacker.com/main/salmon/user/7477' - end - - it 'prevents hijacking inexisting accounts' do - expect(subject.call('hacker2@redirected.com')).to be_nil - end - - context 'with an OStatus account' do - it 'returns an already existing remote account' do - old_account = Fabricate(:account, username: 'gargron', domain: 'quitter.no') - returned_account = subject.call('gargron@quitter.no') - - expect(old_account.id).to eq returned_account.id - end - - it 'returns a new remote account' do - account = subject.call('gargron@quitter.no') - - expect(account.username).to eq 'gargron' - expect(account.domain).to eq 'quitter.no' - expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom' - end - - it 'follows a legitimate account redirection' do - account = subject.call('gargron@redirected.com') - - expect(account.username).to eq 'gargron' - expect(account.domain).to eq 'quitter.no' - expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom' - end - - it 'returns a new remote account' do - account = subject.call('foo@localdomain.com') - - expect(account.username).to eq 'foo' - expect(account.domain).to eq 'localdomain.com' - expect(account.remote_url).to eq 'https://webdomain.com/users/foo.atom' - end - end - context 'with an ActivityPub account' do - before do - stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt')) - stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt')) - stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt')) - stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404) - end - - it 'fallback to OStatus if actor json could not be fetched' do - stub_request(:get, "https://ap.example.com/users/foo").to_return(status: 404) - - account = subject.call('foo@ap.example.com') - - expect(account.ostatus?).to eq true - expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom' - end - - it 'fallback to OStatus if actor json did not have inbox_url' do - stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-noinbox.txt')) - - account = subject.call('foo@ap.example.com') - - expect(account.ostatus?).to eq true - expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom' - end - it 'returns new remote account' do account = subject.call('foo@ap.example.com') @@ -124,13 +51,19 @@ RSpec.describe ResolveAccountService, type: :service do it 'processes one remote account at a time using locks' do wait_for_start = true fail_occurred = false - return_values = [] + return_values = Concurrent::Array.new + + # Preload classes that throw circular dependency errors in threads + Account + TagManager + DomainBlock threads = Array.new(5) do Thread.new do true while wait_for_start + begin - return_values << described_class.new.call('foo@localdomain.com') + return_values << described_class.new.call('foo@ap.example.com') rescue ActiveRecord::RecordNotUnique fail_occurred = true end diff --git a/spec/services/resolve_url_service_spec.rb b/spec/services/resolve_url_service_spec.rb index 7bb5d1940242df65ff49924213d6fec67524eba8..aa4204637187d980ef2fc705fc4b442d63ff1375 100644 --- a/spec/services/resolve_url_service_spec.rb +++ b/spec/services/resolve_url_service_spec.rb @@ -6,48 +6,14 @@ describe ResolveURLService, type: :service do subject { described_class.new } describe '#call' do - it 'returns nil when there is no atom url' do - url = 'http://example.com/missing-atom' + it 'returns nil when there is no resource url' do + url = 'http://example.com/missing-resource' service = double - allow(FetchAtomService).to receive(:new).and_return service - allow(service).to receive(:call).with(url).and_return(nil) - - result = subject.call(url) - expect(result).to be_nil - end - - it 'fetches remote accounts for feed types' do - url = 'http://example.com/atom-feed' - service = double - allow(FetchAtomService).to receive(:new).and_return service - feed_url = 'http://feed-url' - feed_content = '<feed>contents</feed>' - allow(service).to receive(:call).with(url).and_return([feed_url, { prefetched_body: feed_content }]) - - account_service = double - allow(FetchRemoteAccountService).to receive(:new).and_return(account_service) - allow(account_service).to receive(:call) - - _result = subject.call(url) - expect(account_service).to have_received(:call).with(feed_url, feed_content, nil) - end - - it 'fetches remote statuses for entry types' do - url = 'http://example.com/atom-entry' - service = double - allow(FetchAtomService).to receive(:new).and_return service - feed_url = 'http://feed-url' - feed_content = '<entry>contents</entry>' - allow(service).to receive(:call).with(url).and_return([feed_url, { prefetched_body: feed_content }]) - - account_service = double - allow(FetchRemoteStatusService).to receive(:new).and_return(account_service) - allow(account_service).to receive(:call) - - _result = subject.call(url) + allow(FetchResourceService).to receive(:new).and_return service + allow(service).to receive(:call).with(url).and_return(nil) - expect(account_service).to have_received(:call).with(feed_url, feed_content, nil) + expect(subject.call(url)).to be_nil end end end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index d064cd9b85d5f682135364fbd8c674b8304d6e25..739bb9cf5d8db78183a604f38a043a4aa78e9e66 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -27,7 +27,7 @@ describe SearchService, type: :service do it 'returns the empty results' do service = double(call: nil) allow(ResolveURLService).to receive(:new).and_return(service) - results = subject.call(@query, nil, 10) + results = subject.call(@query, nil, 10, resolve: true) expect(service).to have_received(:call).with(@query, on_behalf_of: nil) expect(results).to eq empty_results @@ -40,7 +40,7 @@ describe SearchService, type: :service do service = double(call: account) allow(ResolveURLService).to receive(:new).and_return(service) - results = subject.call(@query, nil, 10) + results = subject.call(@query, nil, 10, resolve: true) expect(service).to have_received(:call).with(@query, on_behalf_of: nil) expect(results).to eq empty_results.merge(accounts: [account]) end @@ -52,7 +52,7 @@ describe SearchService, type: :service do service = double(call: status) allow(ResolveURLService).to receive(:new).and_return(service) - results = subject.call(@query, nil, 10) + results = subject.call(@query, nil, 10, resolve: true) expect(service).to have_received(:call).with(@query, on_behalf_of: nil) expect(results).to eq empty_results.merge(statuses: [status]) end @@ -77,10 +77,10 @@ describe SearchService, type: :service do it 'includes the tag in the results' do query = '#tag' tag = Tag.new - allow(Tag).to receive(:search_for).with('tag', 10, 0).and_return([tag]) + allow(Tag).to receive(:search_for).with('tag', 10, 0, exclude_unreviewed: nil).and_return([tag]) results = subject.call(query, nil, 10) - expect(Tag).to have_received(:search_for).with('tag', 10, 0) + expect(Tag).to have_received(:search_for).with('tag', 10, 0, exclude_unreviewed: nil) expect(results).to eq empty_results.merge(hashtags: [tag]) end it 'does not include tag when starts with @ character' do diff --git a/spec/services/send_interaction_service_spec.rb b/spec/services/send_interaction_service_spec.rb deleted file mode 100644 index 710d8184cfc38fe367dc81f2b65facdaf76344d7..0000000000000000000000000000000000000000 --- a/spec/services/send_interaction_service_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rails_helper' - -RSpec.describe SendInteractionService, type: :service do - subject { SendInteractionService.new } - - it 'sends an XML envelope to the Salmon end point of remote user' -end diff --git a/spec/services/subscribe_service_spec.rb b/spec/services/subscribe_service_spec.rb deleted file mode 100644 index 10bdb1ba8f757e907d32d58ca3ba64e29f8a0839..0000000000000000000000000000000000000000 --- a/spec/services/subscribe_service_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'rails_helper' - -RSpec.describe SubscribeService, type: :service do - let(:account) { Fabricate(:account, username: 'bob', domain: 'example.com', hub_url: 'http://hub.example.com') } - subject { SubscribeService.new } - - it 'sends subscription request to PuSH hub' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 202) - subject.call(account) - expect(a_request(:post, 'http://hub.example.com/')).to have_been_made.once - end - - it 'generates and keeps PuSH secret on successful call' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 202) - subject.call(account) - expect(account.secret).to_not be_blank - end - - it 'fails silently if PuSH hub forbids subscription' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 403) - subject.call(account) - end - - it 'fails silently if PuSH hub is not found' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 404) - subject.call(account) - end - - it 'fails loudly if there is a network error' do - stub_request(:post, 'http://hub.example.com/').to_raise(HTTP::Error) - expect { subject.call(account) }.to raise_error HTTP::Error - end - - it 'fails loudly if PuSH hub is unavailable' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 503) - expect { subject.call(account) }.to raise_error Mastodon::UnexpectedResponseError - end - - it 'fails loudly if rate limited' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 429) - expect { subject.call(account) }.to raise_error Mastodon::UnexpectedResponseError - end -end diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index 6f45762aa3fb6d99a2d41d20c63e331069b5bfb5..eebbbc12ad8b95feff8beff8de5e7eac33f955be 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -18,7 +18,6 @@ RSpec.describe SuspendAccountService, type: :service do let!(:favourite) { Fabricate(:favourite, account: account) } let!(:active_relationship) { Fabricate(:follow, account: account) } let!(:passive_relationship) { Fabricate(:follow, target_account: account) } - let!(:subscription) { Fabricate(:subscription, account: account) } let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) } let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } @@ -27,14 +26,12 @@ RSpec.describe SuspendAccountService, type: :service do [ account.statuses, account.media_attachments, - account.stream_entries, account.notifications, account.favourites, account.active_relationships, account.passive_relationships, - account.subscriptions ].map(&:count) - }.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + }.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0]) end it 'sends a delete actor activity to all known inboxes' do @@ -63,21 +60,18 @@ RSpec.describe SuspendAccountService, type: :service do let!(:favourite) { Fabricate(:favourite, account: remote_bob) } let!(:active_relationship) { Fabricate(:follow, account: remote_bob, target_account: account) } let!(:passive_relationship) { Fabricate(:follow, target_account: remote_bob) } - let!(:subscription) { Fabricate(:subscription, account: remote_bob) } it 'deletes associated records' do is_expected.to change { [ remote_bob.statuses, remote_bob.media_attachments, - remote_bob.stream_entries, remote_bob.notifications, remote_bob.favourites, remote_bob.active_relationships, remote_bob.passive_relationships, - remote_bob.subscriptions ].map(&:count) - }.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + }.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0]) end it 'sends a reject follow to follwer inboxes' do diff --git a/spec/services/unblock_domain_service_spec.rb b/spec/services/unblock_domain_service_spec.rb index 619aefb5c0c535f291b09b2950a2437662419201..27dbc92ada08e7e9ce88adacf9c810abfec675be 100644 --- a/spec/services/unblock_domain_service_spec.rb +++ b/spec/services/unblock_domain_service_spec.rb @@ -31,7 +31,7 @@ describe UnblockDomainService, type: :service do subject.call(@domain_block) expect_deleted_domain_block expect(@suspended.reload.suspended?).to be false - expect(@silenced.reload.silenced?).to be true + expect(@silenced.reload.silenced?).to be false expect(@independently_suspended.reload.suspended?).to be true expect(@independently_silenced.reload.silenced?).to be true end diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb index 5835b912ba489c97b0f168af14423b33137d8dfc..6350c683441cd95676cf320466bbcc78a9b53c0a 100644 --- a/spec/services/unblock_service_spec.rb +++ b/spec/services/unblock_service_spec.rb @@ -30,13 +30,6 @@ RSpec.describe UnblockService, type: :service do it 'destroys the blocking relation' do expect(sender.blocking?(bob)).to be false end - - it 'sends an unblock salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:unblock]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb index 8a2881ab17f29e26811411d397fadce0fb4d6265..84b5dafbc4f2dd12c494f27554afcbeedbdcd0da 100644 --- a/spec/services/unfollow_service_spec.rb +++ b/spec/services/unfollow_service_spec.rb @@ -30,13 +30,6 @@ RSpec.describe UnfollowService, type: :service do it 'destroys the following relation' do expect(sender.following?(bob)).to be false end - - it 'sends an unfollow salmon slap' do - expect(a_request(:post, "http://salmon.example.com/").with { |req| - xml = OStatus2::Salmon.new.unpack(req.body) - xml.match(OStatus::TagManager::VERBS[:unfollow]) - }).to have_been_made.once - end end describe 'remote ActivityPub' do diff --git a/spec/services/unsubscribe_service_spec.rb b/spec/services/unsubscribe_service_spec.rb deleted file mode 100644 index 54d4b1b53cfd51f9137accbf8d418ac01b486bd2..0000000000000000000000000000000000000000 --- a/spec/services/unsubscribe_service_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'rails_helper' - -RSpec.describe UnsubscribeService, type: :service do - let(:account) { Fabricate(:account, username: 'bob', domain: 'example.com', hub_url: 'http://hub.example.com') } - subject { UnsubscribeService.new } - - it 'removes the secret and resets expiration on account' do - stub_request(:post, 'http://hub.example.com/').to_return(status: 204) - subject.call(account) - account.reload - - expect(account.secret).to be_blank - expect(account.subscription_expires_at).to be_blank - end - - it 'logs error on subscription failure' do - logger = stub_logger - stub_request(:post, 'http://hub.example.com/').to_return(status: 404) - subject.call(account) - - expect(logger).to have_received(:debug).with(/unsubscribe for bob@example.com failed/) - end - - it 'logs error on connection failure' do - logger = stub_logger - stub_request(:post, 'http://hub.example.com/').to_raise(HTTP::Error) - subject.call(account) - - expect(logger).to have_received(:debug).with(/unsubscribe for bob@example.com failed/) - end - - def stub_logger - double(debug: nil).tap do |logger| - allow(Rails).to receive(:logger).and_return(logger) - end - end -end diff --git a/spec/services/update_account_service_spec.rb b/spec/services/update_account_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..960b26891f78780f18f10c3cd8de59dc5738827d --- /dev/null +++ b/spec/services/update_account_service_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +RSpec.describe UpdateAccountService, type: :service do + subject { UpdateAccountService.new } + + describe 'switching form locked to unlocked accounts' do + let(:account) { Fabricate(:account, locked: true) } + let(:alice) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account } + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:eve) { Fabricate(:user, email: 'eve@example.com', account: Fabricate(:account, username: 'eve')).account } + + before do + bob.touch(:silenced_at) + account.mute!(eve) + + FollowService.new.call(alice, account) + FollowService.new.call(bob, account) + FollowService.new.call(eve, account) + + subject.call(account, { locked: false }) + end + + it 'auto-accepts pending follow requests' do + expect(alice.following?(account)).to be true + expect(alice.requested?(account)).to be false + end + + it 'does not auto-accept pending follow requests from silenced users' do + expect(bob.following?(account)).to be false + expect(bob.requested?(account)).to be true + end + + it 'auto-accepts pending follow requests from muted users so as to not leak mute' do + expect(eve.following?(account)).to be true + expect(eve.requested?(account)).to be false + end + end +end diff --git a/spec/services/update_remote_profile_service_spec.rb b/spec/services/update_remote_profile_service_spec.rb deleted file mode 100644 index f3ea70b801b7ff943d08c95241e1b0a1435351dc..0000000000000000000000000000000000000000 --- a/spec/services/update_remote_profile_service_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'rails_helper' - -RSpec.describe UpdateRemoteProfileService, type: :service do - let(:xml) { File.read(Rails.root.join('spec', 'fixtures', 'push', 'feed.atom')) } - - subject { UpdateRemoteProfileService.new } - - before do - stub_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png').to_return(request_fixture('avatar.txt')) - end - - context 'with updated details' do - let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com') } - - before do - subject.call(xml, remote_account) - end - - it 'downloads new avatar' do - expect(a_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png')).to have_been_made - end - - it 'sets the avatar remote url' do - expect(remote_account.reload.avatar_remote_url).to eq 'https://quitter.no/avatar/7477-300-20160211190340.png' - end - - it 'sets display name' do - expect(remote_account.reload.display_name).to eq 'DIGITAL CAT' - end - - it 'sets note' do - expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes' - end - end - - context 'with unchanged details' do - let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com', display_name: 'DIGITAL CAT', note: 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes', avatar_remote_url: 'https://quitter.no/avatar/7477-300-20160211190340.png') } - - before do - subject.call(xml, remote_account) - end - - it 'does not re-download avatar' do - expect(a_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png')).to have_been_made.once - end - - it 'sets the avatar remote url' do - expect(remote_account.reload.avatar_remote_url).to eq 'https://quitter.no/avatar/7477-300-20160211190340.png' - end - - it 'sets display name' do - expect(remote_account.reload.display_name).to eq 'DIGITAL CAT' - end - - it 'sets note' do - expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes' - end - end - - context 'with updated details from a domain set to reject media' do - let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com') } - let!(:domain_block) { Fabricate(:domain_block, domain: 'example.com', reject_media: true) } - - before do - subject.call(xml, remote_account) - end - - it 'does not the avatar remote url' do - expect(remote_account.reload.avatar_remote_url).to be_nil - end - - it 'sets display name' do - expect(remote_account.reload.display_name).to eq 'DIGITAL CAT' - end - - it 'sets note' do - expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes' - end - - it 'does not set store the avatar' do - expect(remote_account.reload.avatar_file_name).to be_nil - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0cd1f91d021521f7756efc86d8270bbe826551b3..45ba1bbd9bd976ecdc4b59b6814f0832b82be51f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,7 @@ RSpec.configure do |config| end config.before :suite do + Rails.application.load_seed Chewy.strategy(:bypass) end diff --git a/spec/validators/disallowed_hashtags_validator_spec.rb b/spec/validators/disallowed_hashtags_validator_spec.rb index 8ec1302ab9f6b6bd13613eaeb38cbf7a5da15e93..9deec0bb90e2b7d5bf642da67817b28363bdc89d 100644 --- a/spec/validators/disallowed_hashtags_validator_spec.rb +++ b/spec/validators/disallowed_hashtags_validator_spec.rb @@ -3,42 +3,44 @@ require 'rails_helper' RSpec.describe DisallowedHashtagsValidator, type: :validator do + let(:disallowed_tags) { [] } + describe '#validate' do before do - allow_any_instance_of(described_class).to receive(:select_tags) { tags } + disallowed_tags.each { |name| Fabricate(:tag, name: name, usable: false) } described_class.new.validate(status) end - let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: '') } + let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: disallowed_tags.map { |x| '#' + x }.join(' ')) } let(:errors) { double(add: nil) } - context 'unless status.local? && !status.reblog?' do + context 'for a remote reblog' do let(:local) { false } let(:reblog) { true } - it 'not calls errors.add' do + it 'does not add errors' do expect(errors).not_to have_received(:add).with(:text, any_args) end end - context 'status.local? && !status.reblog?' do + context 'for a local original status' do let(:local) { true } let(:reblog) { false } - context 'tags.empty?' do - let(:tags) { [] } + context 'when does not contain any disallowed hashtags' do + let(:disallowed_tags) { [] } - it 'not calls errors.add' do + it 'does not add errors' do expect(errors).not_to have_received(:add).with(:text, any_args) end end - context '!tags.empty?' do - let(:tags) { %w(a b c) } + context 'when contains disallowed hashtags' do + let(:disallowed_tags) { %w(a b c) } - it 'calls errors.add' do + it 'adds an error' do expect(errors).to have_received(:add) - .with(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size)) + .with(:text, I18n.t('statuses.disallowed_hashtags', tags: disallowed_tags.join(', '), count: disallowed_tags.size)) end end end diff --git a/spec/views/stream_entries/show.html.haml_spec.rb b/spec/views/statuses/show.html.haml_spec.rb similarity index 83% rename from spec/views/stream_entries/show.html.haml_spec.rb rename to spec/views/statuses/show.html.haml_spec.rb index 93f0adb991c399082f7f34f8182117347352b8a0..dbda3b66550075a55769233a8f1538cbffe254dc 100644 --- a/spec/views/stream_entries/show.html.haml_spec.rb +++ b/spec/views/statuses/show.html.haml_spec.rb @@ -2,10 +2,9 @@ require 'rails_helper' -describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true do +describe 'statuses/show.html.haml', without_verify_partial_doubles: true do before do double(:api_oembed_url => '') - double(:account_stream_entry_url => '') allow(view).to receive(:show_landing_strip?).and_return(true) allow(view).to receive(:site_title).and_return('example site') allow(view).to receive(:site_hostname).and_return('example.com') @@ -23,9 +22,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d reply = Fabricate(:status, account: bob, thread: status, text: 'Hello Alice') assign(:status, status) - assign(:stream_entry, status.stream_entry) assign(:account, alice) - assign(:type, status.stream_entry.activity_type.downcase) assign(:descendant_threads, []) render @@ -46,11 +43,9 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d comment = Fabricate(:status, account: carl, thread: reply, text: 'Hello Bob') assign(:status, reply) - assign(:stream_entry, reply.stream_entry) assign(:account, alice) - assign(:type, reply.stream_entry.activity_type.downcase) - assign(:ancestors, reply.stream_entry.activity.ancestors(1, bob)) - assign(:descendant_threads, [{ statuses: reply.stream_entry.activity.descendants(1) }]) + assign(:ancestors, reply.ancestors(1, bob)) + assign(:descendant_threads, [{ statuses: reply.descendants(1) }]) render @@ -71,9 +66,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d status = Fabricate(:status, account: alice, text: 'Hello World') assign(:status, status) - assign(:stream_entry, status.stream_entry) assign(:account, alice) - assign(:type, status.stream_entry.activity_type.downcase) assign(:descendant_threads, []) render diff --git a/spec/workers/after_remote_follow_request_worker_spec.rb b/spec/workers/after_remote_follow_request_worker_spec.rb deleted file mode 100644 index bd623cca508eb05a96cc4f9c37601abebf5dfa92..0000000000000000000000000000000000000000 --- a/spec/workers/after_remote_follow_request_worker_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe AfterRemoteFollowRequestWorker do - subject { described_class.new } - let(:follow_request) { Fabricate(:follow_request) } - describe 'perform' do - context 'when the follow_request does not exist' do - it 'catches a raise and returns true' do - allow(FollowService).to receive(:new) - result = subject.perform('aaa') - - expect(result).to eq(true) - expect(FollowService).not_to have_received(:new) - end - end - - context 'when the account cannot be updated' do - it 'returns nil and does not call service when account is nil' do - allow(FollowService).to receive(:new) - service = double(call: nil) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow_request.id) - - expect(result).to be_nil - expect(FollowService).not_to have_received(:new) - end - - it 'returns nil and does not call service when account is locked' do - allow(FollowService).to receive(:new) - service = double(call: double(locked?: true)) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow_request.id) - - expect(result).to be_nil - expect(FollowService).not_to have_received(:new) - end - end - - context 'when the account is updated' do - it 'calls the follow service and destroys the follow' do - follow_service = double(call: nil) - allow(FollowService).to receive(:new).and_return(follow_service) - account = Fabricate(:account, locked: false) - service = double(call: account) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow_request.id) - - expect(result).to be_nil - expect(follow_service).to have_received(:call).with(follow_request.account, account.acct) - expect { follow_request.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - end - end -end diff --git a/spec/workers/after_remote_follow_worker_spec.rb b/spec/workers/after_remote_follow_worker_spec.rb deleted file mode 100644 index d93c469f9d0505c221a1df086ba4de4c66eb9166..0000000000000000000000000000000000000000 --- a/spec/workers/after_remote_follow_worker_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe AfterRemoteFollowWorker do - subject { described_class.new } - let(:follow) { Fabricate(:follow) } - describe 'perform' do - context 'when the follow does not exist' do - it 'catches a raise and returns true' do - allow(FollowService).to receive(:new) - result = subject.perform('aaa') - - expect(result).to eq(true) - expect(FollowService).not_to have_received(:new) - end - end - - context 'when the account cannot be updated' do - it 'returns nil and does not call service when account is nil' do - allow(FollowService).to receive(:new) - service = double(call: nil) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow.id) - - expect(result).to be_nil - expect(FollowService).not_to have_received(:new) - end - - it 'returns nil and does not call service when account is not locked' do - allow(FollowService).to receive(:new) - service = double(call: double(locked?: false)) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow.id) - - expect(result).to be_nil - expect(FollowService).not_to have_received(:new) - end - end - - context 'when the account is updated' do - it 'calls the follow service and destroys the follow' do - follow_service = double(call: nil) - allow(FollowService).to receive(:new).and_return(follow_service) - account = Fabricate(:account, locked: true) - service = double(call: account) - allow(FetchRemoteAccountService).to receive(:new).and_return(service) - - result = subject.perform(follow.id) - - expect(result).to be_nil - expect(follow_service).to have_received(:call).with(follow.account, account.acct) - expect { follow.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - end - end -end diff --git a/spec/workers/domain_block_worker_spec.rb b/spec/workers/domain_block_worker_spec.rb index c4138501ffc9848d5ee04dae25ee4e243acc578d..48b3e38c40cef21580a345439eeb5fbd294ca326 100644 --- a/spec/workers/domain_block_worker_spec.rb +++ b/spec/workers/domain_block_worker_spec.rb @@ -14,7 +14,7 @@ describe DomainBlockWorker do result = subject.perform(domain_block.id) expect(result).to be_nil - expect(service).to have_received(:call).with(domain_block) + expect(service).to have_received(:call).with(domain_block, false) end it 'calls domain block service for relevant domain block' do diff --git a/spec/workers/pubsubhubbub/confirmation_worker_spec.rb b/spec/workers/pubsubhubbub/confirmation_worker_spec.rb deleted file mode 100644 index 1eecdd2b5b622f944f35a51da8d6fb0fb5e9dade..0000000000000000000000000000000000000000 --- a/spec/workers/pubsubhubbub/confirmation_worker_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Pubsubhubbub::ConfirmationWorker do - include RoutingHelper - - subject { described_class.new } - - let!(:alice) { Fabricate(:account, username: 'alice') } - let!(:subscription) { Fabricate(:subscription, account: alice, callback_url: 'http://example.com/api', confirmed: false, expires_at: 3.days.from_now, secret: nil) } - - describe 'perform' do - describe 'with subscribe mode' do - it 'confirms and updates subscription when challenge matches' do - stub_random_value - stub_request(:get, url_for_mode('subscribe')) - .with(headers: http_headers) - .to_return(status: 200, body: challenge_value, headers: {}) - - seconds = 10.days.seconds.to_i - subject.perform(subscription.id, 'subscribe', 'asdf', seconds) - - subscription.reload - expect(subscription.secret).to eq 'asdf' - expect(subscription.confirmed).to eq true - expect(subscription.expires_at).to be_within(5).of(10.days.from_now) - end - - it 'does not update subscription when challenge does not match' do - stub_random_value - stub_request(:get, url_for_mode('subscribe')) - .with(headers: http_headers) - .to_return(status: 200, body: 'wrong value', headers: {}) - - seconds = 10.days.seconds.to_i - subject.perform(subscription.id, 'subscribe', 'asdf', seconds) - - subscription.reload - expect(subscription.secret).to be_blank - expect(subscription.confirmed).to eq false - expect(subscription.expires_at).to be_within(5).of(3.days.from_now) - end - end - - describe 'with unsubscribe mode' do - it 'confirms and destroys subscription when challenge matches' do - stub_random_value - stub_request(:get, url_for_mode('unsubscribe')) - .with(headers: http_headers) - .to_return(status: 200, body: challenge_value, headers: {}) - - seconds = 10.days.seconds.to_i - subject.perform(subscription.id, 'unsubscribe', 'asdf', seconds) - - expect { subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - - it 'does not destroy subscription when challenge does not match' do - stub_random_value - stub_request(:get, url_for_mode('unsubscribe')) - .with(headers: http_headers) - .to_return(status: 200, body: 'wrong value', headers: {}) - - seconds = 10.days.seconds.to_i - subject.perform(subscription.id, 'unsubscribe', 'asdf', seconds) - - expect { subscription.reload }.not_to raise_error - end - end - end - - def url_for_mode(mode) - "http://example.com/api?hub.challenge=#{challenge_value}&hub.lease_seconds=863999&hub.mode=#{mode}&hub.topic=https://#{Rails.configuration.x.local_domain}/users/alice.atom" - end - - def stub_random_value - allow(SecureRandom).to receive(:hex).and_return(challenge_value) - end - - def challenge_value - '1a2s3d4f' - end - - def http_headers - { 'Connection' => 'close', 'Host' => 'example.com' } - end -end diff --git a/spec/workers/pubsubhubbub/delivery_worker_spec.rb b/spec/workers/pubsubhubbub/delivery_worker_spec.rb deleted file mode 100644 index c0e0d51866f4926f2e79f7e21bf041d117b46115..0000000000000000000000000000000000000000 --- a/spec/workers/pubsubhubbub/delivery_worker_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Pubsubhubbub::DeliveryWorker do - include RoutingHelper - subject { described_class.new } - - let(:payload) { 'test' } - - describe 'perform' do - it 'raises when subscription does not exist' do - expect { subject.perform 123, payload }.to raise_error(ActiveRecord::RecordNotFound) - end - - it 'does not attempt to deliver when domain blocked' do - _domain_block = Fabricate(:domain_block, domain: 'example.com', severity: :suspend) - subscription = Fabricate(:subscription, callback_url: 'https://example.com/api', last_successful_delivery_at: 2.days.ago) - - subject.perform(subscription.id, payload) - - expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(2.days.ago) - end - - it 'raises when request fails' do - subscription = Fabricate(:subscription) - - stub_request_to_respond_with(subscription, 500) - expect { subject.perform(subscription.id, payload) }.to raise_error Mastodon::UnexpectedResponseError - end - - it 'updates subscriptions when delivery succeeds' do - subscription = Fabricate(:subscription) - - stub_request_to_respond_with(subscription, 200) - subject.perform(subscription.id, payload) - - expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(Time.now.utc) - end - - it 'updates subscription without a secret when delivery succeeds' do - subscription = Fabricate(:subscription, secret: nil) - - stub_request_to_respond_with(subscription, 200) - subject.perform(subscription.id, payload) - - expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(Time.now.utc) - end - - def stub_request_to_respond_with(subscription, code) - stub_request(:post, 'http://example.com/callback') - .with(body: payload, headers: expected_headers(subscription)) - .to_return(status: code, body: '', headers: {}) - end - - def expected_headers(subscription) - { - 'Connection' => 'close', - 'Content-Type' => 'application/atom+xml', - 'Host' => 'example.com', - 'Link' => "<https://#{Rails.configuration.x.local_domain}/api/push>; rel=\"hub\", <https://#{Rails.configuration.x.local_domain}/users/#{subscription.account.username}.atom>; rel=\"self\"", - }.tap do |basic| - known_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret.to_s, payload) - basic.merge('X-Hub-Signature' => "sha1=#{known_digest}") if subscription.secret? - end - end - end -end diff --git a/spec/workers/pubsubhubbub/distribution_worker_spec.rb b/spec/workers/pubsubhubbub/distribution_worker_spec.rb deleted file mode 100644 index 5844850795fcf76da55b8fca4f569a106a285a77..0000000000000000000000000000000000000000 --- a/spec/workers/pubsubhubbub/distribution_worker_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'rails_helper' - -describe Pubsubhubbub::DistributionWorker do - subject { Pubsubhubbub::DistributionWorker.new } - - let!(:alice) { Fabricate(:account, username: 'alice') } - let!(:bob) { Fabricate(:account, username: 'bob', domain: 'example2.com') } - let!(:anonymous_subscription) { Fabricate(:subscription, account: alice, callback_url: 'http://example1.com', confirmed: true, lease_seconds: 3600) } - let!(:subscription_with_follower) { Fabricate(:subscription, account: alice, callback_url: 'http://example2.com', confirmed: true, lease_seconds: 3600) } - - before do - bob.follow!(alice) - end - - describe 'with public status' do - let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :public) } - - it 'delivers payload to all subscriptions' do - allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk) - subject.perform(status.stream_entry.id) - expect(Pubsubhubbub::DeliveryWorker).to have_received(:push_bulk).with([anonymous_subscription.id, subscription_with_follower.id]) - end - end - - context 'when OStatus privacy is not used' do - describe 'with private status' do - let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :private) } - - it 'does not deliver anything' do - allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk) - subject.perform(status.stream_entry.id) - expect(Pubsubhubbub::DeliveryWorker).to_not have_received(:push_bulk) - end - end - - describe 'with direct status' do - let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :direct) } - - it 'does not deliver payload' do - allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk) - subject.perform(status.stream_entry.id) - expect(Pubsubhubbub::DeliveryWorker).to_not have_received(:push_bulk) - end - end - end -end diff --git a/spec/workers/scheduler/subscriptions_scheduler_spec.rb b/spec/workers/scheduler/subscriptions_scheduler_spec.rb deleted file mode 100644 index a7d1046de614855c1dd2414e975ad4cd5abd398b..0000000000000000000000000000000000000000 --- a/spec/workers/scheduler/subscriptions_scheduler_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'rails_helper' - -describe Scheduler::SubscriptionsScheduler do - subject { Scheduler::SubscriptionsScheduler.new } - - let!(:expiring_account1) { Fabricate(:account, subscription_expires_at: 20.minutes.from_now, domain: 'example.com', followers_count: 1, hub_url: 'http://hub.example.com') } - let!(:expiring_account2) { Fabricate(:account, subscription_expires_at: 4.hours.from_now, domain: 'example.org', followers_count: 1, hub_url: 'http://hub.example.org') } - - before do - stub_request(:post, 'http://hub.example.com/').to_return(status: 202) - stub_request(:post, 'http://hub.example.org/').to_return(status: 202) - end - - it 're-subscribes for all expiring accounts' do - subject.perform - expect(a_request(:post, 'http://hub.example.com/')).to have_been_made.once - expect(a_request(:post, 'http://hub.example.org/')).to have_been_made.once - end -end diff --git a/streaming/index.js b/streaming/index.js index 68e19621aed813d12e3e5e3b025823f746bd25db..f743f574451e233ed7250aff0543db5966f9a332 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -12,6 +12,7 @@ const uuid = require('uuid'); const fs = require('fs'); const env = process.env.NODE_ENV || 'development'; +const alwaysRequireAuth = process.env.WHITELIST_MODE === 'true' || process.env.AUTHORIZED_FETCH === 'true'; dotenv.config({ path: env === 'production' ? '.env.production' : '.env', @@ -271,7 +272,7 @@ const startWorker = (workerId) => { const wsVerifyClient = (info, cb) => { const location = url.parse(info.req.url, true); - const authRequired = !PUBLIC_STREAMS.some(stream => stream === location.query.stream); + const authRequired = alwaysRequireAuth || !PUBLIC_STREAMS.some(stream => stream === location.query.stream); const allowedScopes = []; if (authRequired) { @@ -306,7 +307,7 @@ const startWorker = (workerId) => { return; } - const authRequired = !PUBLIC_ENDPOINTS.some(endpoint => endpoint === req.path); + const authRequired = alwaysRequireAuth || !PUBLIC_ENDPOINTS.some(endpoint => endpoint === req.path); const allowedScopes = []; if (authRequired) { diff --git a/yarn.lock b/yarn.lock index b9b47c18552dd931b8e0328ade104b3a9c157853..74323bf5bb7f2685e91ff79d1300f8a0d5a84290 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" - integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== dependencies: "@babel/highlight" "^7.0.0" @@ -60,7 +60,7 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@^7.2.2", "@babel/generator@^7.4.4": +"@babel/generator@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041" integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ== @@ -71,6 +71,17 @@ source-map "^0.5.0" trim-right "^1.0.1" +"@babel/generator@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" + integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== + dependencies: + "@babel/types" "^7.5.5" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + trim-right "^1.0.1" + "@babel/helper-annotate-as-pure@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" @@ -103,26 +114,26 @@ "@babel/traverse" "^7.4.4" "@babel/types" "^7.4.4" -"@babel/helper-create-class-features-plugin@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz#fc3d690af6554cc9efc607364a82d48f58736dba" - integrity sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA== +"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4" + integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg== dependencies: "@babel/helper-function-name" "^7.1.0" - "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-member-expression-to-functions" "^7.5.5" "@babel/helper-optimise-call-expression" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.4.4" + "@babel/helper-replace-supers" "^7.5.5" "@babel/helper-split-export-declaration" "^7.4.4" -"@babel/helper-define-map@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a" - integrity sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg== +"@babel/helper-define-map@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" + integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== dependencies: "@babel/helper-function-name" "^7.1.0" - "@babel/types" "^7.4.4" - lodash "^4.17.11" + "@babel/types" "^7.5.5" + lodash "^4.17.13" "@babel/helper-explode-assignable-expression@^7.1.0": version "7.1.0" @@ -155,12 +166,12 @@ dependencies: "@babel/types" "^7.4.4" -"@babel/helper-member-expression-to-functions@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz#8cd14b0a0df7ff00f009e7d7a436945f47c7a16f" - integrity sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg== +"@babel/helper-member-expression-to-functions@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" + integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.5.5" "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.0.0-beta.49": version "7.0.0" @@ -211,15 +222,15 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27" - integrity sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg== +"@babel/helper-replace-supers@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" + integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-member-expression-to-functions" "^7.5.5" "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.4.4" - "@babel/types" "^7.4.4" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" "@babel/helper-simple-access@^7.1.0": version "7.1.0" @@ -229,7 +240,7 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-split-export-declaration@^7.0.0", "@babel/helper-split-export-declaration@^7.4.4": +"@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== @@ -273,20 +284,10 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" - integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== - -"@babel/parser@^7.1.0", "@babel/parser@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c" - integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== - -"@babel/parser@^7.2.2", "@babel/parser@^7.2.3", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872" - integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4", "@babel/parser@^7.4.4", "@babel/parser@^7.4.5", "@babel/parser@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" + integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" @@ -297,12 +298,12 @@ "@babel/helper-remap-async-to-generator" "^7.1.0" "@babel/plugin-syntax-async-generators" "^7.2.0" -"@babel/plugin-proposal-class-properties@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz#93a6486eed86d53452ab9bab35e368e9461198ce" - integrity sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg== +"@babel/plugin-proposal-class-properties@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" + integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== dependencies: - "@babel/helper-create-class-features-plugin" "^7.4.4" + "@babel/helper-create-class-features-plugin" "^7.5.5" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-decorators@^7.4.4": @@ -314,6 +315,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-decorators" "^7.2.0" +"@babel/plugin-proposal-dynamic-import@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz#e532202db4838723691b10a67b8ce509e397c506" + integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-dynamic-import" "^7.2.0" + "@babel/plugin-proposal-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" @@ -322,10 +331,10 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005" - integrity sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g== +"@babel/plugin-proposal-object-rest-spread@^7.4.4", "@babel/plugin-proposal-object-rest-spread@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" + integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" @@ -403,10 +412,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-async-to-generator@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894" - integrity sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA== +"@babel/plugin-transform-async-to-generator@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" + integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -419,25 +428,25 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoping@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d" - integrity sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA== +"@babel/plugin-transform-block-scoping@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz#c49e21228c4bbd4068a35667e6d951c75439b1dc" + integrity sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.11" + lodash "^4.17.13" -"@babel/plugin-transform-classes@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6" - integrity sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw== +"@babel/plugin-transform-classes@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" + integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-define-map" "^7.4.4" + "@babel/helper-define-map" "^7.5.5" "@babel/helper-function-name" "^7.1.0" "@babel/helper-optimise-call-expression" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.4.4" + "@babel/helper-replace-supers" "^7.5.5" "@babel/helper-split-export-declaration" "^7.4.4" globals "^11.1.0" @@ -448,10 +457,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-destructuring@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f" - integrity sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ== +"@babel/plugin-transform-destructuring@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz#44bbe08b57f4480094d57d9ffbcd96d309075ba6" + integrity sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -464,10 +473,10 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" -"@babel/plugin-transform-duplicate-keys@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3" - integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw== +"@babel/plugin-transform-duplicate-keys@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" + integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -508,30 +517,33 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-amd@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6" - integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw== +"@babel/plugin-transform-modules-amd@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" + integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-commonjs@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e" - integrity sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw== +"@babel/plugin-transform-modules-commonjs@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz#39dfe957de4420445f1fcf88b68a2e4aa4515486" + integrity sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g== dependencies: "@babel/helper-module-transforms" "^7.4.4" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" + babel-plugin-dynamic-import-node "^2.3.0" -"@babel/plugin-transform-modules-systemjs@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405" - integrity sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ== +"@babel/plugin-transform-modules-systemjs@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" + integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg== dependencies: "@babel/helper-hoist-variables" "^7.4.4" "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" "@babel/plugin-transform-modules-umd@^7.2.0": version "7.2.0" @@ -541,12 +553,12 @@ "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-named-capturing-groups-regex@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" - integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg== +"@babel/plugin-transform-named-capturing-groups-regex@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.0.tgz#1e6e663097813bb4f53d42df0750cf28ad3bb3f1" + integrity sha512-jem7uytlmrRl3iCAuQyw8BpB4c4LWvSpvIeXKpMb+7j84lkx4m4mYr5ErAcmN5KM7B6BqrAvRGjBIbbzqCczew== dependencies: - regexp-tree "^0.1.6" + regexp-tree "^0.1.13" "@babel/plugin-transform-new-target@^7.4.4": version "7.4.4" @@ -555,13 +567,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-object-super@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" - integrity sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg== +"@babel/plugin-transform-object-super@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" + integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.1.0" + "@babel/helper-replace-supers" "^7.5.5" "@babel/plugin-transform-parameters@^7.4.4": version "7.4.4" @@ -602,10 +614,10 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-jsx" "^7.2.0" -"@babel/plugin-transform-react-jsx-source@^7.0.0", "@babel/plugin-transform-react-jsx-source@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz#20c8c60f0140f5dd3cd63418d452801cf3f7180f" - integrity sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g== +"@babel/plugin-transform-react-jsx-source@^7.0.0", "@babel/plugin-transform-react-jsx-source@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz#583b10c49cf057e237085bcbd8cc960bd83bd96b" + integrity sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-jsx" "^7.2.0" @@ -633,10 +645,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-runtime@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz#a50f5d16e9c3a4ac18a1a9f9803c107c380bce08" - integrity sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q== +"@babel/plugin-transform-runtime@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" + integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -689,43 +701,45 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" -"@babel/preset-env@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58" - integrity sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w== +"@babel/preset-env@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.0.tgz#aae4141c506100bb2bfaa4ac2a5c12b395619e50" + integrity sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-dynamic-import" "^7.5.0" "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.4.4" + "@babel/plugin-proposal-object-rest-spread" "^7.5.5" "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-dynamic-import" "^7.2.0" "@babel/plugin-syntax-json-strings" "^7.2.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" "@babel/plugin-transform-arrow-functions" "^7.2.0" - "@babel/plugin-transform-async-to-generator" "^7.4.4" + "@babel/plugin-transform-async-to-generator" "^7.5.0" "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.4.4" - "@babel/plugin-transform-classes" "^7.4.4" + "@babel/plugin-transform-block-scoping" "^7.6.0" + "@babel/plugin-transform-classes" "^7.5.5" "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.4.4" + "@babel/plugin-transform-destructuring" "^7.6.0" "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-duplicate-keys" "^7.5.0" "@babel/plugin-transform-exponentiation-operator" "^7.2.0" "@babel/plugin-transform-for-of" "^7.4.4" "@babel/plugin-transform-function-name" "^7.4.4" "@babel/plugin-transform-literals" "^7.2.0" "@babel/plugin-transform-member-expression-literals" "^7.2.0" - "@babel/plugin-transform-modules-amd" "^7.2.0" - "@babel/plugin-transform-modules-commonjs" "^7.4.4" - "@babel/plugin-transform-modules-systemjs" "^7.4.4" + "@babel/plugin-transform-modules-amd" "^7.5.0" + "@babel/plugin-transform-modules-commonjs" "^7.6.0" + "@babel/plugin-transform-modules-systemjs" "^7.5.0" "@babel/plugin-transform-modules-umd" "^7.2.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.6.0" "@babel/plugin-transform-new-target" "^7.4.4" - "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-object-super" "^7.5.5" "@babel/plugin-transform-parameters" "^7.4.4" "@babel/plugin-transform-property-literals" "^7.2.0" "@babel/plugin-transform-regenerator" "^7.4.5" @@ -736,7 +750,7 @@ "@babel/plugin-transform-template-literals" "^7.4.4" "@babel/plugin-transform-typeof-symbol" "^7.2.0" "@babel/plugin-transform-unicode-regex" "^7.4.4" - "@babel/types" "^7.4.4" + "@babel/types" "^7.6.0" browserslist "^4.6.0" core-js-compat "^3.1.1" invariant "^2.2.2" @@ -768,10 +782,10 @@ dependencies: regenerator-runtime "^0.12.0" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" - integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== +"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" + integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== dependencies: regenerator-runtime "^0.13.2" @@ -793,82 +807,34 @@ "@babel/parser" "^7.4.4" "@babel/types" "^7.4.4" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.5": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" - integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.3.4", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5", "@babel/traverse@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" + integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.2.3" - "@babel/types" "^7.2.2" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.10" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.4.5": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.5.tgz#4e92d1728fd2f1897dafdd321efbff92156c3216" - integrity sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.4.4" + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.5.5" "@babel/helper-function-name" "^7.1.0" "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.4.5" - "@babel/types" "^7.4.4" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.11" - -"@babel/traverse@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06" - integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.3.4" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.3.4" - "@babel/types" "^7.3.4" + "@babel/parser" "^7.5.5" + "@babel/types" "^7.5.5" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.11" - -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0" - integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ== - dependencies: - esutils "^2.0.2" - lodash "^4.17.11" - to-fast-properties "^2.0.0" + lodash "^4.17.13" -"@babel/types@^7.0.0-beta.49": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e" - integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== +"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.4", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648" + integrity sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g== dependencies: esutils "^2.0.2" - lodash "^4.17.10" + lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.3.0", "@babel/types@^7.3.4": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed" - integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== - dependencies: - esutils "^2.0.2" - lodash "^4.17.11" - to-fast-properties "^2.0.0" - -"@clusterws/cws@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.14.0.tgz#242824b6884454001340222a836db6f6c5e62bfb" - integrity sha512-knZj3KZNHIAGsX7TUc/0Q5gcx2bKMMcTPsAOZomLKdK5a4o/umKFlttWRH84Yr1nVlQy+UMO23qfDR8gRZ/4cw== +"@clusterws/cws@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@clusterws/cws/-/cws-0.15.0.tgz#1d585927252d1cd92e676c952fa6d69df14a0d07" + integrity sha512-41QpCngw86n41hIdU5Nx2QJmmxZuA9FPtDkjONrYpk27L7HjL1kj6J5oWEjbr14yXLfigZit3VY+oACDCGbiHw== "@cnakazawa/watch@^1.0.3": version "1.0.3" @@ -934,67 +900,77 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/core@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.8.0.tgz#fbbdcd42a41d0d39cddbc9f520c8bab0c33eed5b" - integrity sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A== +"@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== dependencies: "@jest/console" "^24.7.1" - "@jest/reporters" "^24.8.0" - "@jest/test-result" "^24.8.0" - "@jest/transform" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" ansi-escapes "^3.0.0" chalk "^2.0.1" exit "^0.1.2" graceful-fs "^4.1.15" - jest-changed-files "^24.8.0" - jest-config "^24.8.0" - jest-haste-map "^24.8.0" - jest-message-util "^24.8.0" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" jest-regex-util "^24.3.0" - jest-resolve-dependencies "^24.8.0" - jest-runner "^24.8.0" - jest-runtime "^24.8.0" - jest-snapshot "^24.8.0" - jest-util "^24.8.0" - jest-validate "^24.8.0" - jest-watcher "^24.8.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" micromatch "^3.1.10" p-each-series "^1.0.0" - pirates "^4.0.1" realpath-native "^1.1.0" rimraf "^2.5.4" + slash "^2.0.0" strip-ansi "^5.0.0" -"@jest/environment@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.8.0.tgz#0342261383c776bdd652168f68065ef144af0eac" - integrity sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw== - dependencies: - "@jest/fake-timers" "^24.8.0" - "@jest/transform" "^24.8.0" - "@jest/types" "^24.8.0" - jest-mock "^24.8.0" - -"@jest/fake-timers@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.8.0.tgz#2e5b80a4f78f284bcb4bd5714b8e10dd36a8d3d1" - integrity sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw== - dependencies: - "@jest/types" "^24.8.0" - jest-message-util "^24.8.0" - jest-mock "^24.8.0" - -"@jest/reporters@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.8.0.tgz#075169cd029bddec54b8f2c0fc489fd0b9e05729" - integrity sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw== - dependencies: - "@jest/environment" "^24.8.0" - "@jest/test-result" "^24.8.0" - "@jest/transform" "^24.8.0" - "@jest/types" "^24.8.0" +"@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + +"@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" chalk "^2.0.1" exit "^0.1.2" glob "^7.1.2" @@ -1002,13 +978,13 @@ istanbul-lib-instrument "^3.0.1" istanbul-lib-report "^2.0.4" istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.1.1" - jest-haste-map "^24.8.0" - jest-resolve "^24.8.0" - jest-runtime "^24.8.0" - jest-util "^24.8.0" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" jest-worker "^24.6.0" - node-notifier "^5.2.1" + node-notifier "^5.4.2" slash "^2.0.0" source-map "^0.6.0" string-length "^2.0.0" @@ -1022,54 +998,64 @@ graceful-fs "^4.1.15" source-map "^0.6.0" -"@jest/test-result@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.8.0.tgz#7675d0aaf9d2484caa65e048d9b467d160f8e9d3" - integrity sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng== +"@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== dependencies: - "@jest/console" "^24.7.1" - "@jest/types" "^24.8.0" + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-sequencer@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz#2f993bcf6ef5eb4e65e8233a95a3320248cf994b" - integrity sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg== +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== dependencies: - "@jest/test-result" "^24.8.0" - jest-haste-map "^24.8.0" - jest-runner "^24.8.0" - jest-runtime "^24.8.0" + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" -"@jest/transform@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.8.0.tgz#628fb99dce4f9d254c6fd9341e3eea262e06fef5" - integrity sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA== +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" babel-plugin-istanbul "^5.1.0" chalk "^2.0.1" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.1.15" - jest-haste-map "^24.8.0" - jest-regex-util "^24.3.0" - jest-util "^24.8.0" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" micromatch "^3.1.10" + pirates "^4.0.1" realpath-native "^1.1.0" slash "^2.0.0" source-map "^0.6.1" write-file-atomic "2.4.1" -"@jest/types@^24.8.0": - version "24.8.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad" - integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg== +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^12.0.9" + "@types/yargs" "^13.0.0" "@types/babel__core@^7.1.0": version "7.1.0" @@ -1144,9 +1130,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + version "12.7.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.8.tgz#cb1bf6800238898bc2ff6ffa5702c3cadd350708" + integrity sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A== "@types/q@^1.5.1": version "1.5.1" @@ -1165,10 +1151,17 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== -"@types/yargs@^12.0.2", "@types/yargs@^12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0" - integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA== +"@types/yargs-parser@*": + version "13.1.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" + integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== + +"@types/yargs@^13.0.0": + version "13.0.2" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.2.tgz#a64674fc0149574ecd90ba746e932b5a5f7b3653" + integrity sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ== + dependencies: + "@types/yargs-parser" "*" "@webassemblyjs/ast@1.8.5": version "1.8.5" @@ -1344,11 +1337,6 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-dynamic-import@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" - integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== - acorn-globals@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103" @@ -1364,16 +1352,21 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" -acorn-jsx@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" - integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== +acorn-jsx@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" + integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== -acorn-walk@^6.0.1, acorn-walk@^6.1.1: +acorn-walk@^6.0.1: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== +acorn-walk@^6.1.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" @@ -1384,10 +1377,20 @@ acorn@^5.5.0, acorn@^5.5.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.5, acorn@^6.0.7: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" - integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== +acorn@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.0.tgz#67f0da2fc339d6cfb5d6fb244fd449f33cd8bbe3" + integrity sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw== + +acorn@^6.0.7, acorn@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" + integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== + +acorn@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" + integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== airbnb-prop-types@^2.13.2: version "2.13.2" @@ -1415,10 +1418,10 @@ ajv-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= -ajv-keywords@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" - integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" + integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== ajv@^4.7.0: version "4.11.8" @@ -1428,10 +1431,10 @@ ajv@^4.7.0: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^6.1.0, ajv@^6.5.3, ajv@^6.5.5, ajv@^6.6.1: - version "6.6.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d" - integrity sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g== +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -1444,9 +1447,9 @@ alphanum-sort@^1.0.0: integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= ansi-colors@^3.0.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== ansi-escapes@^1.1.0: version "1.4.0" @@ -1458,6 +1461,11 @@ ansi-escapes@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -1478,6 +1486,11 @@ ansi-regex@^4.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -1498,6 +1511,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09" + integrity sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1635,10 +1656,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== dependencies: + object-assign "^4.1.1" util "0.10.3" assign-symbols@^1.0.0: @@ -1662,22 +1684,15 @@ async-each@^1.0.1: integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" - integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== - dependencies: - lodash "^4.17.10" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1688,18 +1703,18 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.0.tgz#0111c6bde2ad20c6f17995a33fad7cf6854b4c87" - integrity sha512-kuip9YilBqhirhHEGHaBTZKXL//xxGnzvsD0FtBQa6z+A69qZD6s/BAX9VzDF1i9VKDquTJDQaPLSEhOnL6FvQ== +autoprefixer@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" + integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== dependencies: - browserslist "^4.6.1" - caniuse-lite "^1.0.30000971" + browserslist "^4.6.3" + caniuse-lite "^1.0.30000980" chalk "^2.4.2" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^7.0.16" - postcss-value-parser "^3.3.1" + postcss "^7.0.17" + postcss-value-parser "^4.0.0" aws-sign2@~0.7.0: version "0.7.0" @@ -1711,6 +1726,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + axios@^0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" @@ -1726,40 +1749,47 @@ axobject-query@^2.0.2: dependencies: ast-types-flow "0.0.7" -babel-eslint@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" - integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== +babel-eslint@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== dependencies: "@babel/code-frame" "^7.0.0" "@babel/parser" "^7.0.0" "@babel/traverse" "^7.0.0" "@babel/types" "^7.0.0" - eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" -babel-jest@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589" - integrity sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw== +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== dependencies: - "@jest/transform" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" "@types/babel__core" "^7.1.0" babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.6.0" + babel-preset-jest "^24.9.0" chalk "^2.4.2" slash "^2.0.0" -babel-loader@^8.0.5: - version "8.0.5" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.5.tgz#225322d7509c2157655840bba52e46b6c2f2fe33" - integrity sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw== +babel-loader@^8.0.6: + version "8.0.6" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" + integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== dependencies: find-cache-dir "^2.0.0" loader-utils "^1.0.2" mkdirp "^0.5.1" - util.promisify "^1.0.0" + pify "^4.0.1" + +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" babel-plugin-emotion@^9.2.11: version "9.2.11" @@ -1788,10 +1818,10 @@ babel-plugin-istanbul@^5.1.0: istanbul-lib-instrument "^3.0.0" test-exclude "^5.0.0" -babel-plugin-jest-hoist@^24.6.0: - version "24.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz#f7f7f7ad150ee96d7a5e8e2c5da8319579e78019" - integrity sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w== +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== dependencies: "@types/babel__traverse" "^7.0.6" @@ -1849,13 +1879,13 @@ babel-plugin-transform-react-remove-prop-types@^0.4.24: resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== -babel-preset-jest@^24.6.0: - version "24.6.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984" - integrity sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw== +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== dependencies: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.6.0" + babel-plugin-jest-hoist "^24.9.0" babel-runtime@^6.26.0: version "6.26.0" @@ -1878,9 +1908,9 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== base@^0.11.1: version "0.11.2" @@ -1908,14 +1938,14 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" bfj@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48" - integrity sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ== + version "6.1.2" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" + integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== dependencies: - bluebird "^3.5.1" - check-types "^7.3.0" - hoopy "^0.1.2" - tryer "^1.0.0" + bluebird "^3.5.5" + check-types "^8.0.3" + hoopy "^0.1.4" + tryer "^1.0.1" big.js@^3.1.3: version "3.2.0" @@ -1928,20 +1958,35 @@ big.js@^5.2.2: integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" - integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== -bluebird@^3.5.1, bluebird@^3.5.3: +bluebird@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== +bluebird@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== + blurhash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.0.0.tgz#9087bc5cc4d482f1305059d7410df4133adcab2e" integrity sha512-x6fpZnd6AWde4U9m7xhUB44qIvGV4W6OdTAXGabYm4oZUOOGh5K1HAEoGAQn3iG4gbbPn9RSGce3VfNgGsX/Vw== +bmp-js@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" + integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" @@ -2004,6 +2049,13 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + bricks.js@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/bricks.js/-/bricks.js-1.8.0.tgz#8fdeb3c0226af251f4d5727a7df7f9ac0092b4b2" @@ -2087,14 +2139,14 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.1: - version "4.6.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.2.tgz#574c665950915c2ac73a4594b8537a9eba26203f" - integrity sha512-2neU/V0giQy9h3XMPwLhEY3+Ao0uHSwHvU8Q1Ea6AgLVL1sXbX3dzPrJ8NWe5Hi4PoTkCYXOtVR9rfRLI0J/8Q== +browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.3: + version "4.6.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" + integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== dependencies: - caniuse-lite "^1.0.30000974" - electron-to-chromium "^1.3.150" - node-releases "^1.1.23" + caniuse-lite "^1.0.30000984" + electron-to-chromium "^1.3.191" + node-releases "^1.1.25" bser@^2.0.0: version "2.0.0" @@ -2152,7 +2204,7 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^11.2.0, cacache@^11.3.2: +cacache@^11.2.0: version "11.3.2" resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg== @@ -2172,6 +2224,47 @@ cacache@^11.2.0, cacache@^11.3.2: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^11.3.3: + version "11.3.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" + integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^12.0.2: + version "12.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" + integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2223,15 +2316,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== -camelcase@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== - -camelcase@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.2.0.tgz#e7522abda5ed94cc0489e1b8466610e88404cf45" - integrity sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ== +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-api@^3.0.0: version "3.0.0" @@ -2243,10 +2331,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000971, caniuse-lite@^1.0.30000974: - version "1.0.30000974" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000974.tgz#b7afe14ee004e97ce6dc73e3f878290a12928ad8" - integrity sha512-xc3rkNS/Zc3CmpMKuczWEdY2sZgx09BkAxfvkxlAEBTqcMHeL8QnPqhKse+5sRTi3nrw2pJwToD2WvKn1Uhvww== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984: + version "1.0.30000986" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000986.tgz#f34350e367cc900509511574817ac092112bf7ab" + integrity sha512-pM+LnkoAX0+QnIH3tpW5EnkmfpEoqOD8FAcoBvsl3Xh6DXkgctiCxeCbXphP/k3XJtJzm+zOAJbi6U6IVkpWZQ== capture-exit@^1.2.0: version "1.2.0" @@ -2260,6 +2348,15 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +chalk@2.4.2, chalk@^2.0, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2271,25 +2368,21 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-types@^7.3.0: +check-types@^7.4.0: version "7.4.0" resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" integrity sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg== +check-types@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" + integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== + cheerio@^1.0.0-rc.2: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" @@ -2302,10 +2395,25 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" - integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== +"chokidar@>=2.0.0 <4.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681" + integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA== + dependencies: + anymatch "^3.0.1" + braces "^3.0.2" + glob-parent "^5.0.0" + is-binary-path "^2.1.0" + is-glob "^4.0.1" + normalize-path "^3.0.0" + readdirp "^3.1.1" + optionalDependencies: + fsevents "^2.0.6" + +chokidar@^2.0.2, chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -2322,14 +2430,14 @@ chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.1.6: fsevents "^1.2.7" chownr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" - integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== -chrome-trace-event@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" - integrity sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A== +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== dependencies: tslib "^1.9.0" @@ -2394,6 +2502,15 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + clone-deep@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" @@ -2476,15 +2593,15 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.11.0, commander@^2.18.0, commander@^2.19.0, commander@^2.8.1: +commander@^2.11.0, commander@^2.19.0, commander@^2.8.1, commander@~2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== -commander@~2.17.1: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@^2.18.0, commander@^2.20.0: + version "2.20.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9" + integrity sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg== commondir@^1.0.1: version "1.0.1" @@ -2492,9 +2609,9 @@ commondir@^1.0.1: integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== compressible@~2.0.16: version "2.0.17" @@ -2503,13 +2620,13 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.40.0 < 2" -compression-webpack-plugin@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-2.0.0.tgz#46476350c1eb27f783dccc79ac2f709baa2cffbc" - integrity sha512-bDgd7oTUZC8EkRx8j0sjyCfeiO+e5sFcfgaFcjVhfQf5lLya7oY2BczxcJ7IUuVjz5m6fy8IECFmVFew3xLk8Q== +compression-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-3.0.0.tgz#097d2e4d95c3a14cb5c8ed20899009ab5b9bbca0" + integrity sha512-ls+oKw4eRbvaSv/hj9NmctihhBcR26j76JxV0bLRLcWhrUBdQFgd06z/Kgg7exyQvtWWP484wZxs0gIUX3NO0Q== dependencies: cacache "^11.2.0" - find-cache-dir "^2.0.0" + find-cache-dir "^3.0.0" neo-async "^2.5.0" schema-utils "^1.0.0" serialize-javascript "^1.4.0" @@ -2616,6 +2733,24 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-webpack-plugin@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" + integrity sha512-YBuYGpSzoCHSSDGyHy6VJ7SHojKp6WHT4D7ItcQFNAYx2hrwkMe56e97xfVR0/ovDuMTrMffXUiltvQljtAGeg== + dependencies: + cacache "^11.3.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.0" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + webpack-log "^2.0.0" + core-js-compat@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.1.3.tgz#0cc3ba4c7f62928c2837e1cffbe8dc78b4f1ae14" @@ -2727,7 +2862,7 @@ cross-env@^5.1.4: cross-spawn "^6.0.5" is-windows "^1.0.0" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -2800,22 +2935,23 @@ css-list-helpers@^1.0.1: dependencies: tcomb "^2.5.0" -css-loader@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" - integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== +css-loader@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2" + integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ== dependencies: - camelcase "^5.2.0" - icss-utils "^4.1.0" + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" loader-utils "^1.2.3" normalize-path "^3.0.0" - postcss "^7.0.14" + postcss "^7.0.17" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^2.0.6" + postcss-modules-local-by-default "^3.0.2" postcss-modules-scope "^2.1.0" - postcss-modules-values "^2.0.0" - postcss-value-parser "^3.3.0" - schema-utils "^1.0.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.0.0" + schema-utils "^2.0.0" css-select-base-adapter@~0.1.0: version "0.1.1" @@ -2985,17 +3121,18 @@ csstype@^2.5.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.5.tgz#1cd1dff742ebf4d7c991470ae71e12bb6751e034" integrity sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA== -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8= +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== dependencies: - es5-ext "^0.10.9" + es5-ext "^0.10.50" + type "^1.0.1" damerau-levenshtein@^1.0.4: version "1.0.4" @@ -3023,7 +3160,7 @@ date-now@^0.1.4: resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= -debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3037,7 +3174,7 @@ debug@=3.1.0: dependencies: ms "2.0.0" -debug@^3.2.5: +debug@^3.0.0, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3062,9 +3199,16 @@ decode-uri-component@^0.2.0: integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= deep-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + version "1.1.0" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.0.tgz#3103cdf8ab6d32cf4a8df7865458f2b8d33f3745" + integrity sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" deep-extend@^0.5.1: version "0.5.1" @@ -3184,10 +3328,10 @@ detect-passive-events@^1.0.2: resolved "https://registry.yarnpkg.com/detect-passive-events/-/detect-passive-events-1.0.4.tgz#6ed477e6e5bceb79079735dcd357789d37f9a91a" integrity sha1-btR35uW863kHlzXc01d4nTf5qRo= -diff-sequences@^24.3.0: - version "24.3.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" - integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw== +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== diffie-hellman@^5.0.0: version "5.0.3" @@ -3198,6 +3342,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + discontinuous-range@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" @@ -3238,6 +3389,13 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-helpers@^3.2.1, dom-helpers@^3.3.1: version "3.4.0" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" @@ -3321,9 +3479,9 @@ duplexer@^0.1.1: integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= duplexify@^3.4.2, duplexify@^3.6.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.1.tgz#b1a7a29c4abfd639585efaecce80d666b1e34125" - integrity sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA== + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -3343,20 +3501,25 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^2.3.4, ejs@^2.6.1: +ejs@^2.3.4: version "2.6.1" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== -electron-to-chromium@^1.3.150: - version "1.3.163" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.163.tgz#7fc3d637f5d8fa4ca4a052cad0de7675bd98b911" - integrity sha512-uCEoqQeKrjlhUSUudY0XvyNVDhWR5XmnCIV0WXr2Qo9PVzEVXI75LHGtzwro9Qh8NNetRjSitrm8AfQvPGaSqA== +ejs@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.1.tgz#5b5ab57f718b79d4aca9254457afecd36fa80228" + integrity sha512-kS/gEPzZs3Y1rRsbGX4UOSjtP/CeJP0CxSNZHYxGfVM/VgLcv0ZqM7C45YyTj2DI2g7+P9Dd24C+IMIg6D0nYQ== + +electron-to-chromium@^1.3.191: + version "1.3.203" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.203.tgz#76de1b76eaaf7208e587a26b8e45407535a00abd" + integrity sha512-Z1FjJKEBhYrCNmnususVk8khiBabVI/bSJB/295V4ghVt4MFmtbP+mXgRZLQZinEBI469U6FtiGgpXnlLs6qiQ== elliptic@^6.0.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" - integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== + version "6.5.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b" + integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -3367,10 +3530,10 @@ elliptic@^6.0.0: minimalistic-crypto-utils "^1.0.0" emoji-mart@Gargron/emoji-mart#build: - version "2.6.2" - resolved "https://codeload.github.com/Gargron/emoji-mart/tar.gz/ff00dc470b5b2d9f145a6d6e977a54de5df2b4c9" + version "2.6.3" + resolved "https://codeload.github.com/Gargron/emoji-mart/tar.gz/934f314fd8322276765066e8a2a6be5bac61b1cf" -emoji-regex@^7.0.2: +emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -3401,13 +3564,13 @@ encoding@^0.1.11: iconv-lite "~0.4.13" end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: +enhanced-resolve@4.1.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -3520,24 +3683,15 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.14: - version "0.10.50" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778" - integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw== +es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.51, es5-ext@~0.10.14: + version "0.10.51" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.51.tgz#ed2d7d9d48a12df86e0299287e93a09ff478842f" + integrity sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" next-tick "^1.0.0" -es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.46" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572" - integrity sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - next-tick "1" - es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" @@ -3570,13 +3724,21 @@ es6-set@~0.1.5: es6-symbol "3.1.1" event-emitter "~0.3.5" -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: +es6-symbol@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= dependencies: - d "1" - es5-ext "~0.10.14" + d "1" + es5-ext "~0.10.14" + +es6-symbol@^3.1.1, es6-symbol@^3.1.2, es6-symbol@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.2.tgz#859fdd34f32e905ff06d752e7171ddd4444a7ed1" + integrity sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.51" es6-weak-map@^2.0.1: version "2.0.2" @@ -3636,10 +3798,10 @@ eslint-module-utils@^2.4.0: debug "^2.6.8" pkg-dir "^2.0.0" -eslint-plugin-import@~2.17.3: - version "2.17.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189" - integrity sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q== +eslint-plugin-import@~2.18.2: + version "2.18.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" + integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ== dependencies: array-includes "^3.0.3" contains-path "^0.1.0" @@ -3648,16 +3810,17 @@ eslint-plugin-import@~2.17.3: eslint-import-resolver-node "^0.3.2" eslint-module-utils "^2.4.0" has "^1.0.3" - lodash "^4.17.11" minimatch "^3.0.4" + object.values "^1.1.0" read-pkg-up "^2.0.0" resolve "^1.11.0" -eslint-plugin-jsx-a11y@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz#4ebba9f339b600ff415ae4166e3e2e008831cf0c" - integrity sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w== +eslint-plugin-jsx-a11y@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" + integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== dependencies: + "@babel/runtime" "^7.4.5" aria-query "^3.0.0" array-includes "^3.0.3" ast-types-flow "^0.0.7" @@ -3665,51 +3828,55 @@ eslint-plugin-jsx-a11y@~6.2.1: damerau-levenshtein "^1.0.4" emoji-regex "^7.0.2" has "^1.0.3" - jsx-ast-utils "^2.0.1" + jsx-ast-utils "^2.2.1" -eslint-plugin-promise@~4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.1.1.tgz#1e08cb68b5b2cd8839f8d5864c796f56d82746db" - integrity sha512-faAHw7uzlNPy7b45J1guyjazw28M+7gJokKUjC5JSFoYfUEyy6Gw/i7YQvmv2Yk00sUjWcmzXQLpU1Ki/C2IZQ== +eslint-plugin-promise@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-plugin-react@~7.12.1: - version "7.12.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.1.tgz#b9c4639f72469ff317ac31e3bd630d22d0dbf8f4" - integrity sha512-1YyXVhp6KSB+xRC1BWzmlA4BH9Wp9jMMBE6AJizxuk+bg/KUJpQGRwsU1/q1pV8rM6oEdLCxunXn7Nfh2BOWBg== +eslint-plugin-react@~7.14.3: + version "7.14.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" + integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA== dependencies: array-includes "^3.0.3" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.0.1" + jsx-ast-utils "^2.1.0" + object.entries "^1.1.0" object.fromentries "^2.0.0" - prop-types "^15.6.2" - resolve "^1.9.0" + object.values "^1.1.0" + prop-types "^15.7.2" + resolve "^1.10.1" -eslint-scope@3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" - integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" - integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== +eslint-utils@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" + integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== + dependencies: + eslint-visitor-keys "^1.0.0" -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" - integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== eslint@^2.7.0: version "2.13.1" @@ -3750,48 +3917,48 @@ eslint@^2.7.0: text-table "~0.2.0" user-home "^2.0.0" -eslint@^5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.11.1.tgz#8deda83db9f354bf9d3f53f9677af7e0e13eadda" - integrity sha512-gOKhM8JwlFOc2acbOrkYR05NW8M6DCMSvfcJiBB5NDxRE1gv8kbvxKaC9u69e6ZGEMWXcswA/7eKR229cEIpvg== +eslint@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.0.tgz#304623eec903969dd5c9f2d61c6ce3d6ecec8750" + integrity sha512-IIbSW+vKOqMatPmS9ayyku4tvWxHY2iricSRtOz6+ZA5IPRlgXzEL0u/j6dr4eha0ugmhMwDTqxtmNu3kj9O4w== dependencies: "@babel/code-frame" "^7.0.0" - ajv "^6.5.3" + ajv "^6.10.0" chalk "^2.1.0" cross-spawn "^6.0.5" debug "^4.0.1" - doctrine "^2.1.0" - eslint-scope "^4.0.0" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.0" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.2" + eslint-visitor-keys "^1.1.0" + espree "^6.1.1" esquery "^1.0.1" esutils "^2.0.2" - file-entry-cache "^2.0.0" + file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" - glob "^7.1.2" + glob-parent "^5.0.0" globals "^11.7.0" ignore "^4.0.6" + import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.1.0" - js-yaml "^3.12.0" + inquirer "^6.4.1" + is-glob "^4.0.0" + js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.17.5" + lodash "^4.17.14" minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" progress "^2.0.0" regexpp "^2.0.1" - require-uncached "^1.0.3" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.0.2" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" text-table "^0.2.0" + v8-compile-cache "^2.0.3" espree@^3.1.6: version "3.5.4" @@ -3801,14 +3968,14 @@ espree@^3.1.6: acorn "^5.5.0" acorn-jsx "^3.0.0" -espree@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c" - integrity sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA== +espree@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" + integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== dependencies: - acorn "^6.0.2" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" + acorn "^7.0.0" + acorn-jsx "^5.0.2" + eslint-visitor-keys "^1.1.0" esprima@^3.1.3: version "3.1.3" @@ -3835,9 +4002,9 @@ esrecurse@^4.1.0: estraverse "^4.1.0" estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" @@ -3857,15 +4024,15 @@ event-emitter@~0.3.5: d "1" es5-ext "~0.10.14" -eventemitter3@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" - integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== eventsource@^1.0.7: version "1.0.7" @@ -3935,17 +4102,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.8.0.tgz#471f8ec256b7b6129ca2524b2a62f030df38718d" - integrity sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA== +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" ansi-styles "^3.2.0" - jest-get-type "^24.8.0" - jest-matcher-utils "^24.8.0" - jest-message-util "^24.8.0" - jest-regex-util "^24.3.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" express@^4.16.3, express@^4.17.1: version "4.17.1" @@ -4003,7 +4170,7 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.0: +external-editor@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== @@ -4059,9 +4226,9 @@ faye-websocket@^0.10.0: websocket-driver ">=0.5.1" faye-websocket@~0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" - integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg= + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== dependencies: websocket-driver ">=0.5.1" @@ -4113,21 +4280,25 @@ file-entry-cache@^1.1.1: flat-cache "^1.2.1" object-assign "^4.0.1" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" + flat-cache "^2.0.1" -file-loader@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.0.0.tgz#c3570783fefb6e1bc0978a856f4bf5825b966c2a" - integrity sha512-roAbL6IdSGczwfXxhMi6Zq+jD4IfUpL0jWHD7fvmjdOVb7xBfdRUHe4LpBgO23VtVK5AW1OlWZo0p34Jvx3iWg== +file-loader@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.2.0.tgz#5fb124d2369d7075d70a9a5abecd12e60a95215e" + integrity sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ== dependencies: - loader-utils "^1.2.2" - schema-utils "^1.0.0" + loader-utils "^1.2.3" + schema-utils "^2.0.0" + +file-type@^10.5.0: + version "10.11.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890" + integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== filesize@^3.6.1: version "3.6.1" @@ -4144,6 +4315,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -4157,15 +4335,24 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" - integrity sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA== +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" - make-dir "^1.0.0" + make-dir "^2.0.0" pkg-dir "^3.0.0" +find-cache-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.0.0.tgz#cd4b7dd97b7185b7e17dbfe2d6e4115ee3eeb8fc" + integrity sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.0" + pkg-dir "^4.1.0" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -4185,13 +4372,21 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +findup-sync@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== dependencies: detect-file "^1.0.0" - is-glob "^3.1.0" + is-glob "^4.0.0" micromatch "^3.0.4" resolve-dir "^1.0.1" @@ -4205,13 +4400,27 @@ flat-cache@^1.2.1: rimraf "~2.6.2" write "^0.2.1" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== + flush-write-stream@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" - integrity sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw== + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== dependencies: - inherits "^2.0.1" - readable-stream "^2.0.4" + inherits "^2.0.3" + readable-stream "^2.3.6" follow-redirects@1.5.10: version "1.5.10" @@ -4221,11 +4430,11 @@ follow-redirects@1.5.10: debug "=3.1.0" follow-redirects@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.0.tgz#d12452c031e8c67eb6637d861bfc7a8090167933" - integrity sha512-4Oh4eI3S9OueVV41AgJ1oLjpaJUhbJ7JDGOMhe0AFqoSejl5Q2nn3eGglAzRUKVKZE8jG5MNn66TjCJMAnpsWA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" + integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== dependencies: - debug "=3.1.0" + debug "^3.0.0" font-awesome@^4.7.0: version "4.7.0" @@ -4314,11 +4523,11 @@ fs-extra@^8.0.1: universalify "^0.1.0" fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: - minipass "^2.2.1" + minipass "^2.6.0" fs-write-stream-atomic@^1.0.8: version "1.0.10" @@ -4343,6 +4552,11 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +fsevents@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a" + integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ== + function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4400,6 +4614,11 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4427,7 +4646,14 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.0.0, glob@~7.1.1: +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + dependencies: + is-glob "^4.0.1" + +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== @@ -4439,17 +4665,12 @@ glob@^7.0.0, glob@~7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + global-prefix "^3.0.0" global-modules@^1.0.0: version "1.0.0" @@ -4471,16 +4692,20 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" -globals@^11.1.0: +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0, globals@^11.7.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^11.7.0: - version "11.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249" - integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg== - globals@^9.2.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4497,6 +4722,18 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -4513,7 +4750,12 @@ gonzales-pe-sl@^4.2.3: dependencies: minimist "1.1.x" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + +graceful-fs@^4.1.6: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== @@ -4524,24 +4766,24 @@ growly@^1.3.0: integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= gzip-size@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80" - integrity sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA== + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== dependencies: duplexer "^0.1.1" - pify "^3.0.0" + pify "^4.0.1" handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" - integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== +handlebars@^4.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.2.0.tgz#57ce8d2175b9bbb3d8b3cf3e4217b1aec8ddcb2e" + integrity sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw== dependencies: - async "^2.5.0" + neo-async "^2.6.0" optimist "^0.6.1" source-map "^0.6.1" optionalDependencies: @@ -4646,6 +4888,18 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +history@^4.10.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + history@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" @@ -4685,7 +4939,7 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hoopy@^0.1.2: +hoopy@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== @@ -4756,7 +5010,7 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@1.7.2, http-errors@~1.7.2: +http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== @@ -4777,15 +5031,26 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-link-header@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/http-link-header/-/http-link-header-1.0.2.tgz#bea50f02e1c7996021f1013b428c63f77e0f4e11" integrity sha512-z6YOZ8ZEnejkcCWlGZzYXNa6i+ZaTfiTg3WhlV/YvnNya3W/RbX1bMVUMTuCrg/DrtTCQxaFCkXCz4FtLpcebg== -http-parser-js@>=0.4.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8" - integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w== +"http-parser-js@>=0.4.0 <0.4.11": + version "0.4.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" + integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= http-proxy-middleware@^0.19.1: version "0.19.1" @@ -4798,11 +5063,11 @@ http-proxy-middleware@^0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + version "1.18.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== dependencies: - eventemitter3 "^3.0.0" + eventemitter3 "^4.0.0" follow-redirects "^1.0.0" requires-port "^1.0.0" @@ -4827,22 +5092,22 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= - -icss-utils@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.0.tgz#339dbbffb9f8729a243b701e1c29d4cc58c52f0e" - integrity sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ== +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== dependencies: postcss "^7.0.14" +idb-keyval@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.2.0.tgz#cbbf354deb5684b6cdc84376294fc05932845bd6" + integrity sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ== + ieee754@^1.1.4: - version "1.1.12" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" - integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== iferr@^0.1.5: version "0.1.5" @@ -4850,13 +5115,13 @@ iferr@^0.1.5: integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + version "3.0.2" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.2.tgz#99d83a246c196ea5c93ef9315ad7b0819c35069b" + integrity sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw== dependencies: minimatch "^3.0.4" -ignore@^3.1.2: +ignore@^3.1.2, ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -4886,6 +5151,14 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + integrity sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-from@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" @@ -4893,7 +5166,7 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" -import-local@^2.0.0: +import-local@2.0.0, import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== @@ -4919,10 +5192,10 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" @@ -4932,17 +5205,22 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= -ini@^1.3.4, ini@~1.3.0: +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -4966,23 +5244,23 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.1.tgz#9943fc4882161bdb0b0c9276769c75b32dbfcd52" - integrity sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg== +inquirer@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" + integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" + ansi-escapes "^3.2.0" + chalk "^2.4.2" cli-cursor "^2.1.0" cli-width "^2.0.0" - external-editor "^3.0.0" + external-editor "^3.0.3" figures "^2.0.0" - lodash "^4.17.10" + lodash "^4.17.12" mute-stream "0.0.7" run-async "^2.2.0" - rxjs "^6.1.0" + rxjs "^6.4.0" string-width "^2.1.0" - strip-ansi "^5.0.0" + strip-ansi "^5.1.0" through "^2.3.6" internal-ip@^4.3.0: @@ -4993,7 +5271,7 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -interpret@^1.1.0: +interpret@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== @@ -5025,13 +5303,18 @@ intl-messageformat@^2.0.0, intl-messageformat@^2.1.0, intl-messageformat@^2.2.0: dependencies: intl-messageformat-parser "1.4.0" -intl-relativeformat@^2.1.0, intl-relativeformat@^2.2.0: +intl-relativeformat@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz#6aca95d019ec8d30b6c5653b6629f9983ea5b6c5" integrity sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw== dependencies: intl-messageformat "^2.0.0" +intl-relativeformat@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-6.4.3.tgz#cb5559e1e257cc2e763583502012a354bb777efe" + integrity sha512-VxZXZfhuX/zBVfxzE/J6kPUpsyWKYjqtZ3jVGZwr6wzK5BOLVpe1vSlwCQX56w5UjlpL63fS8Nxq0kgTyf1gJA== + intl@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/intl/-/intl-1.2.5.tgz#82244a2190c4e419f8371f5aa34daa3420e2abde" @@ -5059,16 +5342,26 @@ ip@^1.1.0, ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.0, ipaddr.js@^1.9.0: +ipaddr.js@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== +ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-absolute-url@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -5083,6 +5376,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -5100,6 +5398,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" @@ -5229,10 +5534,10 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" @@ -5271,15 +5576,20 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= is-path-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.1.0.tgz#2e0c7e463ff5b7a0eb60852d851a6809347a124c" - integrity sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw== + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== is-path-in-cwd@^2.0.0: version "2.1.0" @@ -5295,7 +5605,12 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -5358,6 +5673,16 @@ is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-url@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.2.tgz#498905a593bf47cc2d9e7f738372bbf7696c7f26" + integrity sha1-SYkFpZO/R8wtnn9zg3K792lsfyY= + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -5446,73 +5771,73 @@ istanbul-lib-source-maps@^3.0.1: rimraf "^2.6.2" source-map "^0.6.1" -istanbul-reports@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.1.1.tgz#72ef16b4ecb9a4a7bd0e2001e00f95d1eec8afa9" - integrity sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw== +istanbul-reports@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" + integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== dependencies: - handlebars "^4.1.0" + handlebars "^4.1.2" -jest-changed-files@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b" - integrity sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug== +jest-changed-files@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" execa "^1.0.0" throat "^4.0.0" -jest-cli@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.8.0.tgz#b075ac914492ed114fa338ade7362a301693e989" - integrity sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA== +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== dependencies: - "@jest/core" "^24.8.0" - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" chalk "^2.0.1" exit "^0.1.2" import-local "^2.0.0" is-ci "^2.0.0" - jest-config "^24.8.0" - jest-util "^24.8.0" - jest-validate "^24.8.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" prompts "^2.0.1" realpath-native "^1.1.0" - yargs "^12.0.2" + yargs "^13.3.0" -jest-config@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.8.0.tgz#77db3d265a6f726294687cbbccc36f8a76ee0f4f" - integrity sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw== +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.8.0" - "@jest/types" "^24.8.0" - babel-jest "^24.8.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" chalk "^2.0.1" glob "^7.1.1" - jest-environment-jsdom "^24.8.0" - jest-environment-node "^24.8.0" - jest-get-type "^24.8.0" - jest-jasmine2 "^24.8.0" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" jest-regex-util "^24.3.0" - jest-resolve "^24.8.0" - jest-util "^24.8.0" - jest-validate "^24.8.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" micromatch "^3.1.10" - pretty-format "^24.8.0" + pretty-format "^24.9.0" realpath-native "^1.1.0" -jest-diff@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.8.0.tgz#146435e7d1e3ffdf293d53ff97e193f1d1546172" - integrity sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g== +jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== dependencies: chalk "^2.0.1" - diff-sequences "^24.3.0" - jest-get-type "^24.8.0" - pretty-format "^24.8.0" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" jest-docblock@^24.3.0: version "24.3.0" @@ -5521,123 +5846,124 @@ jest-docblock@^24.3.0: dependencies: detect-newline "^2.1.0" -jest-each@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.8.0.tgz#a05fd2bf94ddc0b1da66c6d13ec2457f35e52775" - integrity sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA== +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" chalk "^2.0.1" - jest-get-type "^24.8.0" - jest-util "^24.8.0" - pretty-format "^24.8.0" - -jest-environment-jsdom@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz#300f6949a146cabe1c9357ad9e9ecf9f43f38857" - integrity sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ== - dependencies: - "@jest/environment" "^24.8.0" - "@jest/fake-timers" "^24.8.0" - "@jest/types" "^24.8.0" - jest-mock "^24.8.0" - jest-util "^24.8.0" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" jsdom "^11.5.1" -jest-environment-node@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.8.0.tgz#d3f726ba8bc53087a60e7a84ca08883a4c892231" - integrity sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q== +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== dependencies: - "@jest/environment" "^24.8.0" - "@jest/fake-timers" "^24.8.0" - "@jest/types" "^24.8.0" - jest-mock "^24.8.0" - jest-util "^24.8.0" + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" -jest-get-type@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.8.0.tgz#a7440de30b651f5a70ea3ed7ff073a32dfe646fc" - integrity sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ== +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== -jest-haste-map@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.8.0.tgz#51794182d877b3ddfd6e6d23920e3fe72f305800" - integrity sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ== +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" anymatch "^2.0.0" fb-watchman "^2.0.0" graceful-fs "^4.1.15" invariant "^2.2.4" - jest-serializer "^24.4.0" - jest-util "^24.8.0" - jest-worker "^24.6.0" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" micromatch "^3.1.10" sane "^4.0.3" walker "^1.0.7" optionalDependencies: fsevents "^1.2.7" -jest-jasmine2@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz#a9c7e14c83dd77d8b15e820549ce8987cc8cd898" - integrity sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong== +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.8.0" - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" chalk "^2.0.1" co "^4.6.0" - expect "^24.8.0" + expect "^24.9.0" is-generator-fn "^2.0.0" - jest-each "^24.8.0" - jest-matcher-utils "^24.8.0" - jest-message-util "^24.8.0" - jest-runtime "^24.8.0" - jest-snapshot "^24.8.0" - jest-util "^24.8.0" - pretty-format "^24.8.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" throat "^4.0.0" -jest-leak-detector@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz#c0086384e1f650c2d8348095df769f29b48e6980" - integrity sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g== +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== dependencies: - pretty-format "^24.8.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" -jest-matcher-utils@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz#2bce42204c9af12bde46f83dc839efe8be832495" - integrity sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw== +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== dependencies: chalk "^2.0.1" - jest-diff "^24.8.0" - jest-get-type "^24.8.0" - pretty-format "^24.8.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" -jest-message-util@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.8.0.tgz#0d6891e72a4beacc0292b638685df42e28d6218b" - integrity sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g== +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" "@types/stack-utils" "^1.0.1" chalk "^2.0.1" micromatch "^3.1.10" slash "^2.0.0" stack-utils "^1.0.1" -jest-mock@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.8.0.tgz#2f9d14d37699e863f1febf4e4d5a33b7fdbbde56" - integrity sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A== +jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" jest-pnp-resolver@^1.2.1: version "1.2.1" @@ -5649,113 +5975,119 @@ jest-regex-util@^24.3.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.3.0.tgz#d5a65f60be1ae3e310d5214a0307581995227b36" integrity sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg== -jest-resolve-dependencies@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz#19eec3241f2045d3f990dba331d0d7526acff8e0" - integrity sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw== +jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" jest-regex-util "^24.3.0" - jest-snapshot "^24.8.0" + jest-snapshot "^24.9.0" -jest-resolve@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.8.0.tgz#84b8e5408c1f6a11539793e2b5feb1b6e722439f" - integrity sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw== +jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" browser-resolve "^1.11.3" chalk "^2.0.1" jest-pnp-resolver "^1.2.1" realpath-native "^1.1.0" -jest-runner@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.8.0.tgz#4f9ae07b767db27b740d7deffad0cf67ccb4c5bb" - integrity sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow== +jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== dependencies: "@jest/console" "^24.7.1" - "@jest/environment" "^24.8.0" - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" chalk "^2.4.2" exit "^0.1.2" graceful-fs "^4.1.15" - jest-config "^24.8.0" + jest-config "^24.9.0" jest-docblock "^24.3.0" - jest-haste-map "^24.8.0" - jest-jasmine2 "^24.8.0" - jest-leak-detector "^24.8.0" - jest-message-util "^24.8.0" - jest-resolve "^24.8.0" - jest-runtime "^24.8.0" - jest-util "^24.8.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" jest-worker "^24.6.0" source-map-support "^0.5.6" throat "^4.0.0" -jest-runtime@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.8.0.tgz#05f94d5b05c21f6dc54e427cd2e4980923350620" - integrity sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA== +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== dependencies: "@jest/console" "^24.7.1" - "@jest/environment" "^24.8.0" + "@jest/environment" "^24.9.0" "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.8.0" - "@jest/types" "^24.8.0" - "@types/yargs" "^12.0.2" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" chalk "^2.0.1" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.1.15" - jest-config "^24.8.0" - jest-haste-map "^24.8.0" - jest-message-util "^24.8.0" - jest-mock "^24.8.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" jest-regex-util "^24.3.0" - jest-resolve "^24.8.0" - jest-snapshot "^24.8.0" - jest-util "^24.8.0" - jest-validate "^24.8.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" realpath-native "^1.1.0" slash "^2.0.0" strip-bom "^3.0.0" - yargs "^12.0.2" + yargs "^13.3.0" -jest-serializer@^24.4.0: - version "24.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.4.0.tgz#f70c5918c8ea9235ccb1276d232e459080588db3" - integrity sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q== +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== -jest-snapshot@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.8.0.tgz#3bec6a59da2ff7bc7d097a853fb67f9d415cb7c6" - integrity sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg== +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" chalk "^2.0.1" - expect "^24.8.0" - jest-diff "^24.8.0" - jest-matcher-utils "^24.8.0" - jest-message-util "^24.8.0" - jest-resolve "^24.8.0" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" mkdirp "^0.5.1" natural-compare "^1.4.0" - pretty-format "^24.8.0" - semver "^5.5.0" - -jest-util@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.8.0.tgz#41f0e945da11df44cc76d64ffb915d0716f46cd1" - integrity sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA== - dependencies: - "@jest/console" "^24.7.1" - "@jest/fake-timers" "^24.8.0" - "@jest/source-map" "^24.3.0" - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" + pretty-format "^24.9.0" + semver "^6.2.0" + +jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" callsites "^3.0.0" chalk "^2.0.1" graceful-fs "^4.1.15" @@ -5764,29 +6096,29 @@ jest-util@^24.8.0: slash "^2.0.0" source-map "^0.6.0" -jest-validate@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.8.0.tgz#624c41533e6dfe356ffadc6e2423a35c2d3b4849" - integrity sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA== +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== dependencies: - "@jest/types" "^24.8.0" - camelcase "^5.0.0" + "@jest/types" "^24.9.0" + camelcase "^5.3.1" chalk "^2.0.1" - jest-get-type "^24.8.0" - leven "^2.1.0" - pretty-format "^24.8.0" - -jest-watcher@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.8.0.tgz#58d49915ceddd2de85e238f6213cef1c93715de4" - integrity sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw== - dependencies: - "@jest/test-result" "^24.8.0" - "@jest/types" "^24.8.0" - "@types/yargs" "^12.0.9" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + +jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" ansi-escapes "^3.0.0" chalk "^2.0.1" - jest-util "^24.8.0" + jest-util "^24.9.0" string-length "^2.0.0" jest-worker@^24.6.0: @@ -5797,13 +6129,21 @@ jest-worker@^24.6.0: merge-stream "^1.0.1" supports-color "^6.1.0" -jest@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.8.0.tgz#d5dff1984d0d1002196e9b7f12f75af1b2809081" - integrity sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg== +jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== dependencies: import-local "^2.0.0" - jest-cli "^24.8.0" + jest-cli "^24.9.0" js-base64@^2.1.9: version "2.5.0" @@ -5913,9 +6253,9 @@ json-stringify-safe@~5.0.1: integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json3@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" - integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== json5@^0.5.0: version "0.5.1" @@ -5970,12 +6310,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" - integrity sha1-6AGxs5mF4g//yHtA43SAgOLcrH8= +jsx-ast-utils@^2.1.0, jsx-ast-utils@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" + integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== dependencies: array-includes "^3.0.3" + object.assign "^4.1.0" keycode@^2.1.7: version "2.2.0" @@ -6038,10 +6379,10 @@ left-pad@^1.3.0: resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== -leven@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" - integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.3.0, levn@~0.3.0: version "0.3.0" @@ -6071,10 +6412,10 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -loader-runner@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" - integrity sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw== +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== loader-utils@0.2.x: version "0.2.17" @@ -6086,7 +6427,7 @@ loader-utils@0.2.x: json5 "^0.5.0" object-assign "^4.0.1" -loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.2, loader-utils@^1.2.3: +loader-utils@1.2.3, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== @@ -6111,6 +6452,13 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + lodash.capitalize@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" @@ -6181,15 +6529,15 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.7.11, lodash@~4.17.10: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.3.0, lodash@~4.17.10: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -loglevel@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.2.tgz#668c77948a03dbd22502a3513ace1f62a80cc372" - integrity sha512-Jt2MHrCNdtIe1W6co3tF5KXGRkzF+TYffiQstfXa04mrss9IKXzAAXYWak8LbZseAQY03sH2GzMCMU0ZOUc9bg== +loglevel@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" + integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" @@ -6205,13 +6553,28 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -make-dir@^1.0.0, make-dir@^1.3.0: +make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" + integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw== + dependencies: + semver "^6.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -6273,20 +6636,20 @@ media-typer@0.3.0: integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= mem@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" - integrity sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA== + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== dependencies: map-age-cleaner "^0.1.1" - mimic-fn "^1.0.0" - p-is-promise "^1.1.0" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" memoize-one@^5.0.0: version "5.0.4" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc" integrity sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA== -memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: +memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -6306,6 +6669,11 @@ merge-stream@^1.0.1: dependencies: readable-stream "^2.0.1" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" @@ -6316,7 +6684,7 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -6343,24 +6711,29 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": +mime-db@1.40.0: version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== +"mime-db@>= 1.40.0 < 2": + version "1.42.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" + integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== + mime-db@~1.37.0: version "1.37.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.21" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== dependencies: mime-db "~1.37.0" -mime-types@~2.1.24: +mime-types@~2.1.17, mime-types@~2.1.24: version "2.1.24" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== @@ -6372,22 +6745,28 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.2: - version "2.4.3" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" - integrity sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw== +mime@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mini-css-extract-plugin@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" - integrity sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw== +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mini-css-extract-plugin@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz#81d41ec4fe58c713a96ad7c723cdb2d0bd4d70e1" + integrity sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw== dependencies: loader-utils "^1.1.0" + normalize-url "1.9.1" schema-utils "^1.0.0" webpack-sources "^1.1.0" @@ -6428,20 +6807,20 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -minipass@^2.2.1, minipass@^2.3.4: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" -minizlib@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: - minipass "^2.2.1" + minipass "^2.9.0" mississippi@^3.0.0: version "3.0.0" @@ -6460,9 +6839,9 @@ mississippi@^3.0.0: through2 "^2.0.0" mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" @@ -6475,7 +6854,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mkdirp@0.5.x, mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.x, mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -6509,11 +6888,16 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1, ms@^2.1.1: +ms@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -6576,11 +6960,11 @@ nearley@^2.7.10: semver "^5.4.1" needle@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" - integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== dependencies: - debug "^2.1.2" + debug "^3.2.6" iconv-lite "^0.4.4" sax "^1.2.4" @@ -6589,12 +6973,12 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" - integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== -next-tick@1, next-tick@^1.0.0: +next-tick@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= @@ -6612,20 +6996,20 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-forge@0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" - integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== +node-forge@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.2.tgz#b4bcc59fb12ce77a8825fc6a783dfe3182499c5a" + integrity sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg== node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-libs-browser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" - integrity sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg== +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" @@ -6634,10 +7018,10 @@ node-libs-browser@^2.0.0: constants-browserify "^1.0.0" crypto-browserify "^3.11.0" domain-browser "^1.1.1" - events "^1.0.0" + events "^3.0.0" https-browserify "^1.0.0" os-browserify "^0.3.0" - path-browserify "0.0.0" + path-browserify "0.0.1" process "^0.11.10" punycode "^1.2.4" querystring-es3 "^0.2.0" @@ -6648,20 +7032,21 @@ node-libs-browser@^2.0.0: timers-browserify "^2.0.4" tty-browserify "0.0.0" url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" + util "^0.11.0" + vm-browserify "^1.0.1" node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^5.2.1: - version "5.3.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.3.0.tgz#c77a4a7b84038733d5fb351aafd8a268bfe19a01" - integrity sha512-AhENzCSGZnZJgBARsUjnQ7DnZbzyP+HxlVXuD0xqAnvL8q+OqtSX7lGg9e8nHzwXkMMXNdVeqq4E2M3EUAqX6Q== +node-notifier@^5.4.2: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== dependencies: growly "^1.3.0" + is-wsl "^1.1.0" semver "^5.5.0" shellwords "^0.1.1" which "^1.3.0" @@ -6682,10 +7067,10 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.23: - version "1.1.23" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.23.tgz#de7409f72de044a2fa59c097f436ba89c39997f0" - integrity sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w== +node-releases@^1.1.25: + version "1.1.26" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.26.tgz#f30563edc5c7dc20cf524cc8652ffa7be0762937" + integrity sha512-fZPsuhhUHMTlfkhDLGtfY80DSJTjOcx+qD1j5pqPkuhUHVS7xHZIg9EE4DHK8O3f0zTxXHX5VIkDG8pu98/wfQ== dependencies: semver "^5.3.0" @@ -6731,20 +7116,30 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + normalize-url@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== npm-bundled@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" - integrity sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g== + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-packlist@^1.1.6: - version "1.1.12" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a" - integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g== + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -6832,6 +7227,11 @@ object-keys@^1.0.11, object-keys@^1.0.12: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -6941,6 +7341,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opencollective-postinstall@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + opener@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" @@ -6990,7 +7395,7 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^3.0.0: +os-locale@^3.0.0, os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== @@ -7029,10 +7434,10 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== p-limit@^1.1.0: version "1.3.0" @@ -7042,9 +7447,16 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68" - integrity sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g== + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== + dependencies: + p-try "^2.0.0" + +p-limit@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== dependencies: p-try "^2.0.0" @@ -7062,6 +7474,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -7072,15 +7491,22 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" - integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== packet-reader@0.3.1: version "0.3.1" @@ -7088,29 +7514,37 @@ packet-reader@0.3.1: integrity sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc= pako@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.7.tgz#2473439021b57f1516c82f58be7275ad8ef1bb27" - integrity sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ== + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== dependencies: - cyclist "~0.2.2" + cyclist "^1.0.1" inherits "^2.0.3" readable-stream "^2.1.5" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" - integrity sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw== + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" parse-css-font@^2.0.2: version "2.0.2" @@ -7169,10 +7603,10 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-complete-extname@^1.0.0: version "1.0.0" @@ -7189,6 +7623,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -7306,6 +7745,11 @@ pgpass@1.*: dependencies: split "^1.0.0" +picomatch@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -7354,25 +7798,27 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -portfinder@^1.0.20: - version "1.0.20" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" - integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw== +portfinder@^1.0.24: + version "1.0.24" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.24.tgz#11efbc6865f12f37624b6531ead1d809ed965cfa" + integrity sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg== dependencies: async "^1.5.2" debug "^2.2.0" @@ -7527,14 +7973,15 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" - integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== +postcss-modules-local-by-default@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" + integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-value-parser "^3.3.1" + icss-utils "^4.1.1" + postcss "^7.0.16" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.0" postcss-modules-scope@^2.1.0: version "2.1.0" @@ -7544,12 +7991,12 @@ postcss-modules-scope@^2.1.0: postcss "^7.0.6" postcss-selector-parser "^6.0.0" -postcss-modules-values@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" - integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== dependencies: - icss-replace-symbols "^1.1.0" + icss-utils "^4.0.0" postcss "^7.0.6" postcss-normalize-charset@^4.0.1: @@ -7689,7 +8136,7 @@ postcss-selector-parser@^5.0.0-rc.4: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0: +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== @@ -7717,11 +8164,16 @@ postcss-unique-selectors@^4.0.1: postcss "^7.0.0" uniqs "^2.0.0" -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== +postcss-value-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d" + integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ== + postcss@^5.0.16: version "5.2.18" resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" @@ -7732,7 +8184,7 @@ postcss@^5.0.16: source-map "^0.5.6" supports-color "^3.2.3" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.5, postcss@^7.0.6: version "7.0.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== @@ -7773,17 +8225,17 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^1.17.0: - version "1.18.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" - integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= -pretty-format@^24.8.0: - version "24.8.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" - integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== dependencies: - "@jest/types" "^24.8.0" + "@jest/types" "^24.9.0" ansi-regex "^4.0.0" ansi-styles "^3.2.0" react-is "^16.8.4" @@ -7794,9 +8246,9 @@ private@^0.1.6: integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.10: version "0.11.10" @@ -7944,6 +8396,14 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7954,10 +8414,10 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.0.tgz#7ded8dfbf7879dcc60d0a644ac6754b283ad17ef" - integrity sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg== +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== quote@^0.4.0: version "0.4.0" @@ -7990,9 +8450,9 @@ randexp@0.4.6: ret "~0.1.10" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" - integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" @@ -8106,10 +8566,10 @@ react-intl@^2.9.0: intl-relativeformat "^2.1.0" invariant "^2.1.1" -react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2, react-is@^16.8.4, react-is@^16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" - integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== +react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" + integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" @@ -8161,17 +8621,17 @@ react-redux-loading-bar@^4.0.8: prop-types "^15.6.2" react-lifecycles-compat "^3.0.2" -react-redux@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d" - integrity sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ== +react-redux@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.1.tgz#ce6eee1b734a7a76e0788b3309bf78ff6b34fa0a" + integrity sha512-QsW0vcmVVdNQzEkrgzh2W3Ksvr8cqpAv5FhEk7tNEft+5pp7rXxAudTz3VOPawRkLIepItpkEIyLcN/VVXzjTg== dependencies: - "@babel/runtime" "^7.3.1" + "@babel/runtime" "^7.5.5" hoist-non-react-statics "^3.3.0" invariant "^2.2.4" loose-envify "^1.4.0" prop-types "^15.7.2" - react-is "^16.8.2" + react-is "^16.9.0" react-router-dom@^4.1.1: version "4.3.1" @@ -8337,7 +8797,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -8351,9 +8811,9 @@ read-pkg@^3.0.0: util-deprecate "~1.0.1" readable-stream@^3.0.6: - version "3.1.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" - integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -8368,6 +8828,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.1.tgz#b158123ac343c8b0f31d65680269cc0fc1025db1" + integrity sha512-XXdSXZrQuvqoETj50+JAitxz1UPdt5dupjT6T5nVB+WvjMv2XKYj+s7hPeAVCXvmJrL36O4YYyWlIC3an2ePiQ== + dependencies: + picomatch "^2.0.4" + readline2@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" @@ -8413,10 +8880,10 @@ redux-thunk@^2.2.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== -redux@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" - integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== +redux@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796" + integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q== dependencies: loose-envify "^1.4.0" symbol-observable "^1.2.0" @@ -8468,10 +8935,17 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp-tree@^0.1.6: - version "0.1.10" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.10.tgz#d837816a039c7af8a8d64d7a7c3cf6a1d93450bc" - integrity sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ== +regexp-tree@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.13.tgz#5b19ab9377edc68bc3679256840bb29afc158d7f" + integrity sha512-hwdV/GQY5F8ReLZWO+W1SRoN5YfpOKY6852+tBFcma72DKBIcHjPRIlIvQN35bCOljuAfP2G2iB0FC/w236mUw== + +regexp.prototype.flags@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" + integrity sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA== + dependencies: + define-properties "^1.1.2" regexpp@^2.0.1: version "2.0.1" @@ -8584,12 +9058,17 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + require-package-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" integrity sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk= -require-uncached@^1.0.2, require-uncached@^1.0.3: +require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= @@ -8632,11 +9111,21 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-pathname@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg== +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -8647,10 +9136,10 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.10.0, resolve@^1.11.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" - integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw== +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" + integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== dependencies: path-parse "^1.0.6" @@ -8675,6 +9164,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + rgb-regex@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" @@ -8685,13 +9179,27 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: +rimraf@2.6.3, rimraf@^2.6.2, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: glob "^7.1.3" +rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.0.tgz#614176d4b3010b75e5c390eb0ee96f6dc0cebb9b" + integrity sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -8739,18 +9247,23 @@ rx-lite@^3.1.2: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -rxjs@^6.1.0: - version "6.3.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" - integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== +rxjs@^6.4.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" + integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== dependencies: tslib "^1.9.0" -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -8810,12 +9323,12 @@ sass-loader@^7.0.3: pify "^3.0.0" semver "^5.5.0" -sass@^1.20.3: - version "1.20.3" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.20.3.tgz#18284a7bac6eab9cbb80453288473194f29efb84" - integrity sha512-kvf+w5XT7FrmFrCKz1gPHqegufG+gxifC8oQesX/s8gkShdeiTqiuvP0c8TvfBwMAuI1YGOgobZQ2KIJGn//jA== +sass@^1.22.12: + version "1.22.12" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.22.12.tgz#5cbdd38720ffd1857da695331faee9f634bcb5d7" + integrity sha512-u5Rxn+dKTPCW5/11kMNxtmqKsxCjcpnqj9CaJoru1NqeJ0DOa9rOM00e0HqmseTAatGkKoLY+jaNecMYevu1gg== dependencies: - chokidar "^2.0.0" + chokidar ">=2.0.0 <4.0.0" sax@^1.2.4, sax@~1.2.4: version "1.2.4" @@ -8839,6 +9352,14 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.0.1.tgz#1eec2e059556af841b7f3a83b61af13d7a3f9196" + integrity sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A== + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + scroll-behavior@^0.9.1: version "0.9.9" resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.9.tgz#ebfe0658455b82ad885b66195215416674dacce2" @@ -8852,27 +9373,27 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selfsigned@^1.10.4: - version "1.10.4" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" - integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== +selfsigned@^1.10.6: + version "1.10.6" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.6.tgz#7b3cd37ed9c2034261a173af1a1aae27d8169b67" + integrity sha512-i3+CeqxL7DpAazgVpAGdKMwHuL63B5nhJMh9NQ7xmChGkA3jNFflq6Jyo1LLJYcr3idWiNOPWHCrm4zMayLG4w== dependencies: - node-forge "0.7.5" + node-forge "0.8.2" "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" - integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= -semver@^6.1.0, semver@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" - integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== +semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== send@0.17.1: version "0.17.1" @@ -8894,15 +9415,15 @@ send@0.17.1: statuses "~1.5.0" serialize-javascript@^1.4.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" - integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== - -serialize-javascript@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== +serialize-javascript@^1.7.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" + integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A== + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -8931,20 +9452,10 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -9037,10 +9548,10 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slice-ansi@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.0.0.tgz#5373bdb8559b45676e8541c66916cdd6251612e7" - integrity sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== dependencies: ansi-styles "^3.2.0" astral-regex "^1.0.0" @@ -9076,10 +9587,10 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sockjs-client@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" - integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg== +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== dependencies: debug "^3.2.5" eventsource "^1.0.7" @@ -9096,6 +9607,13 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -9120,10 +9638,10 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.10: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== +source-map-support@~0.5.12: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -9186,10 +9704,10 @@ spdy-transport@^3.0.0: readable-stream "^3.0.6" wbuf "^1.7.3" -spdy@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52" - integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q== +spdy@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" + integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== dependencies: debug "^4.1.0" handle-thing "^2.0.0" @@ -9267,9 +9785,9 @@ stealthy-require@^1.1.0: integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" - integrity sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds= + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== dependencies: inherits "~2.0.1" readable-stream "^2.0.2" @@ -9298,6 +9816,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -9323,6 +9846,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string.prototype.trim@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" @@ -9333,11 +9865,11 @@ string.prototype.trim@^1.1.2: function-bind "^1.0.2" string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" - integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: - safe-buffer "~5.1.0" + safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" @@ -9346,10 +9878,10 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringz@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stringz/-/stringz-1.0.0.tgz#d2acba994e4ce3c725ee15c86fff4281280d2025" - integrity sha512-oaqFaIAmw1MJmdPNiBqocHHrC0VzJTL3CI1z5uXm3NQSE3AyDU152ZPTSJSOKk+9z1Cm3LZzgLFjCTb8SXZvag== +stringz@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/stringz/-/stringz-2.0.0.tgz#0a092bc64ed9b42eff2936d0401d2398393d54e9" + integrity sha512-pRWc5RGpedKEDvQ/ukYs8kS8tKj+cKu5ayOoyOvsavbpiLBcm1dGX6p1o5IagaN11cbfN8tKGpgQ4fHdEq5LBA== dependencies: unicode-astral-regex "^1.0.1" @@ -9367,12 +9899,12 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" - integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow== +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: - ansi-regex "^4.0.0" + ansi-regex "^4.1.0" strip-bom@^3.0.0: version "3.0.0" @@ -9384,16 +9916,21 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== strip-json-comments@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + stylehacks@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.1.tgz#3186595d047ab0df813d213e51c8b94e0b9010f2" @@ -9418,6 +9955,13 @@ substring-trie@^1.0.2: resolved "https://registry.yarnpkg.com/substring-trie/-/substring-trie-1.0.2.tgz#7b42592391628b4f2cb17365c6cce4257c7b7af5" integrity sha1-e0JZI5Fii08ssXNlxszkJXx7evU= +supports-color@6.1.0, supports-color@^6.0.0, supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -9430,20 +9974,13 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^6.0.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - svgo@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985" @@ -9486,63 +10023,92 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" -table@^5.0.2: - version "5.1.1" - resolved "https://registry.yarnpkg.com/table/-/table-5.1.1.tgz#92030192f1b7b51b6eeab23ed416862e47b70837" - integrity sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw== +table@^5.2.3: + version "5.4.1" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.1.tgz#0691ae2ebe8259858efb63e550b6d5f9300171e8" + integrity sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w== dependencies: - ajv "^6.6.1" + ajv "^6.9.1" lodash "^4.17.11" - slice-ansi "2.0.0" - string-width "^2.1.1" + slice-ansi "^2.1.0" + string-width "^3.0.0" -tapable@^1.0.0, tapable@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e" - integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA== +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== tar@^4: - version "4.4.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" - integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.4" - minizlib "^1.1.1" + minipass "^2.8.6" + minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" - yallist "^3.0.2" + yallist "^3.0.3" tcomb@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0" integrity sha1-ENYpWAQWaaXVNWe5pO6M3iKxwrA= -terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz#69aa22426299f4b5b3775cbed8cb2c5d419aa1d4" - integrity sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg== +terser-webpack-plugin@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4" + integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg== dependencies: - cacache "^11.3.2" - find-cache-dir "^2.0.0" + cacache "^12.0.2" + find-cache-dir "^2.1.0" is-wsl "^1.1.0" - loader-utils "^1.2.3" schema-utils "^1.0.0" serialize-javascript "^1.7.0" source-map "^0.6.1" - terser "^4.0.0" - webpack-sources "^1.3.0" + terser "^4.1.2" + webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.0.0.tgz#ef356f6f359a963e2cc675517f21c1c382877374" - integrity sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA== +terser@^4.1.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.4.tgz#ad91bade95619e3434685d69efa621a5af5f877d" + integrity sha512-Kcrn3RiW8NtHBP0ssOAzwa2MsIRQ8lJWiBG/K7JgqPlomA3mtb2DEmp4/hrUA+Jujx+WZ02zqd7GYD+QRBB/2Q== dependencies: - commander "^2.19.0" + commander "^2.20.0" source-map "~0.6.1" - source-map-support "~0.5.10" + source-map-support "~0.5.12" + +tesseract.js-core@^2.0.0-beta.12: + version "2.0.0-beta.13" + resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.0.0-beta.13.tgz#a21d798e88098898a9bdd935d0553215e03274f8" + integrity sha512-GboWV/aV5h+Whito6L6Q3WCFZ2+lgxZGgjY84wSpWbTLEkkZgHsU+dz1or+3rWSABH/nuzHDco1bZRk5+f94mw== + +tesseract.js-utils@^1.0.0-beta.8: + version "1.0.0-beta.8" + resolved "https://registry.yarnpkg.com/tesseract.js-utils/-/tesseract.js-utils-1.0.0-beta.8.tgz#d1ef25c12609a337c3e0ac12a33f9903f3145a68" + integrity sha512-qjHBfWfzo2o1ZY9XI0Wh2hmpp38+mIgCMOk60W5Yyie/pBl421VLBKOZUEwQgpbLnOJ24VU6Q8yXsVgtFFHcFg== + dependencies: + axios "^0.18.0" + bmp-js "^0.1.0" + file-type "^10.5.0" + idb-keyval "^3.1.0" + is-url "^1.2.4" + zlibjs "^0.3.1" + +tesseract.js@^2.0.0-alpha.16: + version "2.0.0-alpha.16" + resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-2.0.0-alpha.16.tgz#1e17717234a1464481abe12283f2c3ac79603d2e" + integrity sha512-8g3je2Kl8rkAFtpmwilGGj+8rCiPClNQaCjW6IafOPNn7hzFnVdL6fU6rG1Xsrc4Twv0HOa75kbpx5u70/WbTA== + dependencies: + axios "^0.18.0" + check-types "^7.4.0" + is-url "1.2.2" + opencollective-postinstall "^2.0.2" + resolve-url "^0.2.1" + tesseract.js-core "^2.0.0-beta.12" + tesseract.js-utils "^1.0.0-beta.8" test-exclude@^5.0.0: version "5.1.0" @@ -9590,9 +10156,9 @@ thunky@^1.0.2: integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== dependencies: setimmediate "^1.0.4" @@ -9601,11 +10167,21 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-invariant@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73" + integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA== + tiny-queue@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= +tiny-warning@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -9643,6 +10219,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -9693,15 +10276,15 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -tryer@^1.0.0: +tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== tslib@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== tty-browserify@0.0.0: version "0.0.0" @@ -9735,6 +10318,11 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179" + integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -9746,11 +10334,11 @@ ua-parser-js@^0.7.18: integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ== uglify-js@^3.1.4: - version "3.4.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" - integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== + version "3.6.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" + integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== dependencies: - commander "~2.17.1" + commander "~2.20.0" source-map "~0.6.1" unicode-astral-regex@^1.0.1: @@ -9782,14 +10370,14 @@ unicode-property-aliases-ecmascript@^1.0.4: integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" - set-value "^0.4.3" + set-value "^2.0.1" uniq@^1.0.1: version "1.0.1" @@ -9809,9 +10397,9 @@ unique-filename@^1.1.1: unique-slug "^2.0.0" unique-slug@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" - integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" @@ -9839,9 +10427,9 @@ unset-value@^1.0.0: isobject "^3.0.0" upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== uri-js@^4.2.2: version "4.2.2" @@ -9856,11 +10444,11 @@ urix@^0.1.0: integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-parse@^1.4.3: - version "1.4.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8" - integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg== + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== dependencies: - querystringify "^2.0.0" + querystringify "^2.1.1" requires-port "^1.0.0" url@^0.11.0: @@ -9910,20 +10498,32 @@ util@^0.10.3: dependencies: inherits "2.0.3" +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: +uuid@^3.0.1, uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +uuid@^3.1.0: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -v8-compile-cache@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" - integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== +v8-compile-cache@2.0.3, v8-compile-cache@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" + integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== validate-npm-package-license@^3.0.1: version "3.0.4" @@ -9938,6 +10538,11 @@ value-equal@^0.4.0: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" integrity sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw== +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -9957,12 +10562,10 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= - dependencies: - indexof "0.0.1" +vm-browserify@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" + integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== w3c-hr-time@^1.0.1: version "1.0.1" @@ -9992,7 +10595,7 @@ warning@^4.0.1: dependencies: loose-envify "^1.0.0" -watchpack@^1.5.0: +watchpack@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== @@ -10001,6 +10604,11 @@ watchpack@^1.5.0: graceful-fs "^4.1.2" neo-async "^2.5.0" +wavesurfer.js@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/wavesurfer.js/-/wavesurfer.js-3.0.0.tgz#35f36d76d59c749dca453cf4e10ee0ec49f454f8" + integrity sha512-DANu206c6gb9pSUbYFevsSiXMy8+Ri+CNtqm0UsouUdsn9fVQRtYs8uxzBtXK+rUPlIc6FlO54DU8uWeW3lDzw== + wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" @@ -10027,9 +10635,9 @@ webpack-assets-manifest@^3.1.1: webpack-sources "^1.0.0" webpack-bundle-analyzer@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.3.2.tgz#3da733a900f515914e729fcebcd4c40dde71fc6f" - integrity sha512-7qvJLPKB4rRWZGjVp5U1KEjwutbDHSKboAl0IfafnrdXMrgC0tOtZbQD6Rw0u4cmpgRN4O02Fc0t8eAT+FgGzA== + version "3.5.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.2.tgz#ac02834f4b31de8e27d71e6c7a612301ebddb79f" + integrity sha512-g9spCNe25QYUVqHRDkwG414GTok2m7pTTP0wr6l0J50Z3YLS04+BGodTqqoVBL7QfU/U/9p/oiI5XFOyfZ7S/A== dependencies: acorn "^6.0.7" acorn-walk "^6.1.1" @@ -10040,47 +10648,47 @@ webpack-bundle-analyzer@^3.3.2: express "^4.16.3" filesize "^3.6.1" gzip-size "^5.0.0" - lodash "^4.17.10" + lodash "^4.17.15" mkdirp "^0.5.1" opener "^1.5.1" ws "^6.0.0" -webpack-cli@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.4.tgz#de27e281c48a897b8c219cb093e261d5f6afe44a" - integrity sha512-ubJGQEKMtBSpT+LiL5hXvn2GIOWiRWItR1DGUqJRhwRBeGhpRXjvF5f0erqdRJLErkfqS5/Ldkkedh4AL5Q1ZQ== - dependencies: - chalk "^2.4.1" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.0" - findup-sync "^2.0.0" - global-modules "^1.0.0" - import-local "^2.0.0" - interpret "^1.1.0" - loader-utils "^1.1.0" - prettier "^1.17.0" - supports-color "^5.5.0" - v8-compile-cache "^2.0.2" - yargs "^12.0.5" - -webpack-dev-middleware@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff" - integrity sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA== +webpack-cli@^3.3.7: + version "3.3.9" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.9.tgz#79c27e71f94b7fe324d594ab64a8e396b9daa91a" + integrity sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A== + dependencies: + chalk "2.4.2" + cross-spawn "6.0.5" + enhanced-resolve "4.1.0" + findup-sync "3.0.0" + global-modules "2.0.0" + import-local "2.0.0" + interpret "1.2.0" + loader-utils "1.2.3" + supports-color "6.1.0" + v8-compile-cache "2.0.3" + yargs "13.2.4" + +webpack-dev-middleware@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== dependencies: memory-fs "^0.4.1" - mime "^2.4.2" + mime "^2.4.4" + mkdirp "^0.5.1" range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.5.1.tgz#4290ac709bb989dc7382c912899f79fd5677dabf" - integrity sha512-0IdMGddJcnK9zesZOeHWl4uAOVfypn7DSrdNWtclROkVBXy/TcBN+6eEG1wNfLT9dXVfaRZZsLTJt0mJtgTQgw== +webpack-dev-server@^3.8.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.1.tgz#485b64c4aadc23f601e72114b40c1b1fea31d9f1" + integrity sha512-9F5DnfFA9bsrhpUCAfQic/AXBVHvq+3gQS+x6Zj0yc1fVVE0erKh2MV4IV12TBewuTrYeeTIRwCH9qLMvdNvTw== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" - chokidar "^2.1.6" + chokidar "^2.1.8" compression "^1.7.4" connect-history-api-fallback "^1.6.0" debug "^4.1.1" @@ -10091,22 +10699,25 @@ webpack-dev-server@^3.5.1: import-local "^2.0.0" internal-ip "^4.3.0" ip "^1.1.5" + is-absolute-url "^3.0.2" killable "^1.0.1" - loglevel "^1.6.2" + loglevel "^1.6.4" opn "^5.5.0" - portfinder "^1.0.20" + p-retry "^3.0.1" + portfinder "^1.0.24" schema-utils "^1.0.0" - selfsigned "^1.10.4" - semver "^6.1.1" + selfsigned "^1.10.6" + semver "^6.3.0" serve-index "^1.9.1" sockjs "0.3.19" - sockjs-client "1.3.0" - spdy "^4.0.0" + sockjs-client "1.4.0" + spdy "^4.0.1" strip-ansi "^3.0.1" supports-color "^6.1.0" url "^0.11.0" - webpack-dev-middleware "^3.7.0" + webpack-dev-middleware "^3.7.1" webpack-log "^2.0.0" + ws "^6.2.1" yargs "12.0.5" webpack-log@^2.0.0: @@ -10118,56 +10729,56 @@ webpack-log@^2.0.0: uuid "^3.3.2" webpack-merge@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4" - integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw== + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== dependencies: - lodash "^4.17.5" + lodash "^4.17.15" -webpack-sources@^1.0.0, webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" - integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== +webpack-sources@^1.0.0, webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== dependencies: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.34.0: - version "4.34.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.34.0.tgz#a4c30129482f7b4ece4c0842002dedf2b56fab58" - integrity sha512-ry2IQy1wJjOefLe1uJLzn5tG/DdIKzQqNlIAd2L84kcaADqNvQDTBlo8UcCNyDaT5FiaB+16jhAkb63YeG3H8Q== +webpack@^4.35.3: + version "4.41.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" + integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5" "@webassemblyjs/wasm-edit" "1.8.5" "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.0.5" - acorn-dynamic-import "^4.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - chrome-trace-event "^1.0.0" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" enhanced-resolve "^4.1.0" - eslint-scope "^4.0.0" + eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - micromatch "^3.1.8" - mkdirp "~0.5.0" - neo-async "^2.5.0" - node-libs-browser "^2.0.0" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" schema-utils "^1.0.0" - tapable "^1.1.0" - terser-webpack-plugin "^1.1.0" - watchpack "^1.5.0" - webpack-sources "^1.3.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.1" + watchpack "^1.6.0" + webpack-sources "^1.4.1" websocket-driver@>=0.5.1: - version "0.7.0" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" - integrity sha1-DK+dLXVdk67gSdS90NP+LMoqJOs= + version "0.7.3" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" + integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== dependencies: - http-parser-js ">=0.4.0" + http-parser-js ">=0.4.0 <0.4.11" + safe-buffer ">=5.1.0" websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: @@ -10222,7 +10833,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.14, which@^1.2.9, which@^1.3.0: +which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -10261,6 +10872,15 @@ wrap-ansi@^2.0.0: string-width "^1.0.1" strip-ansi "^3.0.1" +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -10275,6 +10895,13 @@ write-file-atomic@2.4.1: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + write@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" @@ -10289,10 +10916,10 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" -ws@^6.0.0: - version "6.1.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.2.tgz#3cc7462e98792f0ac679424148903ded3b9c3ad8" - integrity sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw== +ws@^6.0.0, ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== dependencies: async-limiter "~1.0.0" @@ -10302,19 +10929,19 @@ xml-name-validator@^3.0.0: integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xtend@^4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@^11.1.1: version "11.1.1" @@ -10324,7 +10951,15 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@12.0.5, yargs@^12.0.2, yargs@^12.0.5: +yargs-parser@^13.1.0, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@12.0.5: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== @@ -10341,3 +10976,41 @@ yargs@12.0.5, yargs@^12.0.2, yargs@^12.0.5: which-module "^2.0.0" y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" + +yargs@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + +yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +zlibjs@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/zlibjs/-/zlibjs-0.3.1.tgz#50197edb28a1c42ca659cc8b4e6a9ddd6d444554" + integrity sha1-UBl+2yihxCymWcyLTmqd3W1ERVQ=