~cytrogen/masto-fe

b4e739ff0f64c601973762ac986c0e63092d2d7e — Claire 2 years ago 1e4ccc6
Change interaction modal in web UI (#26075)

Co-authored-by: Eugen Rochko <eugen@zeonfederated.com>
111 files changed, 679 insertions(+), 1088 deletions(-)

A app/chewy/instances_index.rb
M app/controllers/api/v1/instances/peers_controller.rb
A app/controllers/api/v1/peers/search_controller.rb
M app/controllers/authorize_interactions_controller.rb
A app/controllers/remote_interaction_helper_controller.rb
M app/controllers/well_known/webfinger_controller.rb
M app/javascript/mastodon/containers/status_container.jsx
M app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
M app/javascript/mastodon/features/compose/components/search.jsx
M app/javascript/mastodon/features/interaction_modal/index.jsx
M app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
M app/javascript/mastodon/features/status/index.jsx
M app/javascript/mastodon/locales/en.json
A app/javascript/packs/remote_interaction_helper.ts
M app/javascript/styles/mastodon/components.scss
M app/javascript/styles/mastodon/variables.scss
M app/javascript/types/resources.ts
A app/lib/importer/instances_index_importer.rb
M app/lib/webfinger_resource.rb
M app/models/instance.rb
M app/serializers/rest/account_serializer.rb
M app/serializers/webfinger_serializer.rb
D app/views/authorize_interactions/_post_follow_actions.html.haml
D app/views/authorize_interactions/error.html.haml
D app/views/authorize_interactions/show.html.haml
D app/views/authorize_interactions/success.html.haml
A app/views/layouts/helper_frame.html.haml
A app/views/remote_interaction_helper/index.html.haml
M app/workers/scheduler/instance_refresh_scheduler.rb
M config/locales/an.yml
M config/locales/ar.yml
M config/locales/ast.yml
M config/locales/be.yml
M config/locales/bg.yml
M config/locales/br.yml
M config/locales/ca.yml
M config/locales/ckb.yml
M config/locales/co.yml
M config/locales/cs.yml
M config/locales/cy.yml
M config/locales/da.yml
M config/locales/de.yml
M config/locales/el.yml
M config/locales/en-GB.yml
M config/locales/en.yml
M config/locales/eo.yml
M config/locales/es-AR.yml
M config/locales/es-MX.yml
M config/locales/es.yml
M config/locales/et.yml
M config/locales/eu.yml
M config/locales/fa.yml
M config/locales/fi.yml
M config/locales/fo.yml
M config/locales/fr-QC.yml
M config/locales/fr.yml
M config/locales/fy.yml
M config/locales/ga.yml
M config/locales/gd.yml
M config/locales/gl.yml
M config/locales/he.yml
M config/locales/hr.yml
M config/locales/hu.yml
M config/locales/hy.yml
M config/locales/id.yml
M config/locales/io.yml
M config/locales/is.yml
M config/locales/it.yml
M config/locales/ja.yml
M config/locales/ka.yml
M config/locales/kab.yml
M config/locales/kk.yml
M config/locales/ko.yml
M config/locales/ku.yml
M config/locales/lt.yml
M config/locales/lv.yml
M config/locales/ml.yml
M config/locales/ms.yml
M config/locales/my.yml
M config/locales/nl.yml
M config/locales/nn.yml
M config/locales/no.yml
M config/locales/oc.yml
M config/locales/pl.yml
M config/locales/pt-BR.yml
M config/locales/pt-PT.yml
M config/locales/ro.yml
M config/locales/ru.yml
M config/locales/sc.yml
M config/locales/sco.yml
M config/locales/si.yml
M config/locales/sk.yml
M config/locales/sl.yml
M config/locales/sq.yml
M config/locales/sr-Latn.yml
M config/locales/sr.yml
M config/locales/sv.yml
M config/locales/ta.yml
M config/locales/th.yml
M config/locales/tr.yml
M config/locales/tt.yml
M config/locales/uk.yml
M config/locales/vi.yml
M config/locales/zgh.yml
M config/locales/zh-CN.yml
M config/locales/zh-HK.yml
M config/locales/zh-TW.yml
M config/routes.rb
M config/routes/api.rb
M lib/mastodon/cli/search.rb
M spec/controllers/authorize_interactions_controller_spec.rb
A app/chewy/instances_index.rb => app/chewy/instances_index.rb +12 -0
@@ 0,0 1,12 @@
# frozen_string_literal: true

class InstancesIndex < Chewy::Index
  settings index: { refresh_interval: '30s' }

  index_scope ::Instance.searchable

  root date_detection: false do
    field :domain, type: 'text', index_prefixes: { min_chars: 1 }
    field :accounts_count, type: 'long'
  end
end

M app/controllers/api/v1/instances/peers_controller.rb => app/controllers/api/v1/instances/peers_controller.rb +1 -1
@@ 15,7 15,7 @@ class Api::V1::Instances::PeersController < Api::BaseController

  def index
    cache_even_if_authenticated!
    render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
    render_with_cache(expires_in: 1.day) { Instance.searchable.pluck(:domain) }
  end

  private

A app/controllers/api/v1/peers/search_controller.rb => app/controllers/api/v1/peers/search_controller.rb +45 -0
@@ 0,0 1,45 @@
# frozen_string_literal: true

class Api::V1::Peers::SearchController < Api::BaseController
  before_action :require_enabled_api!
  before_action :set_domains

  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
  skip_around_action :set_locale

  vary_by ''

  def index
    cache_even_if_authenticated!
    render json: @domains
  end

  private

  def require_enabled_api!
    head 404 unless Setting.peers_api_enabled && !whitelist_mode?
  end

  def set_domains
    return if params[:q].blank?

    if Chewy.enabled?
      @domains = InstancesIndex.query(function_score: {
        query: {
          prefix: {
            domain: params[:q],
          },
        },

        field_value_factor: {
          field: 'accounts_count',
          modifier: 'log2p',
        },
      }).limit(10).pluck(:domain)
    else
      domain = params[:q].strip
      domain = TagManager.instance.normalize_domain(domain)
      @domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain)
    end
  end
end

M app/controllers/authorize_interactions_controller.rb => app/controllers/authorize_interactions_controller.rb +2 -19
@@ 3,32 3,19 @@
class AuthorizeInteractionsController < ApplicationController
  include Authorization

  layout 'modal'

  before_action :authenticate_user!
  before_action :set_body_classes
  before_action :set_resource

  def show
    if @resource.is_a?(Account)
      render :show
      redirect_to web_url("@#{@resource.pretty_acct}")
    elsif @resource.is_a?(Status)
      redirect_to web_url("@#{@resource.account.pretty_acct}/#{@resource.id}")
    else
      render :error
      not_found
    end
  end

  def create
    if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource, with_rate_limit: true)
      render :success
    else
      render :error
    end
  rescue ActiveRecord::RecordNotFound
    render :error
  end

  private

  def set_resource


@@ 61,8 48,4 @@ class AuthorizeInteractionsController < ApplicationController
  def uri_param
    params[:uri] || params.fetch(:acct, '').delete_prefix('acct:')
  end

  def set_body_classes
    @body_classes = 'modal-layout'
  end
end

A app/controllers/remote_interaction_helper_controller.rb => app/controllers/remote_interaction_helper_controller.rb +43 -0
@@ 0,0 1,43 @@
# frozen_string_literal: true

class RemoteInteractionHelperController < ApplicationController
  vary_by ''

  skip_before_action :require_functional!
  skip_around_action :set_locale
  skip_before_action :update_user_sign_in

  content_security_policy do |p|
    # We inherit the normal `script-src`

    # Set every directive that does not have a fallback
    p.default_src :none
    p.form_action :none
    p.base_uri :none

    # Disable every directive with a fallback to cut on response size
    p.base_uri false
    p.font_src false
    p.img_src false
    p.style_src false
    p.media_src false
    p.frame_src false
    p.manifest_src false
    p.connect_src false
    p.child_src false
    p.worker_src false

    # Widen the directives that we do need
    p.frame_ancestors :self
    p.connect_src :https
  end

  def index
    expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day)

    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    response.headers['Referrer-Policy'] = 'no-referrer'

    render layout: 'helper_frame'
  end
end

M app/controllers/well_known/webfinger_controller.rb => app/controllers/well_known/webfinger_controller.rb +1 -0
@@ 19,6 19,7 @@ module WellKnown

    def set_account
      username = username_from_resource

      @account = begin
        if username == Rails.configuration.x.local_domain
          Account.representative

M app/javascript/mastodon/containers/status_container.jsx => app/javascript/mastodon/containers/status_container.jsx +1 -1
@@ 278,7 278,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
      modalProps: {
        type,
        accountId: status.getIn(['account', 'id']),
        url: status.get('url'),
        url: status.get('uri'),
      },
    }));
  },

M app/javascript/mastodon/features/account_timeline/containers/header_container.jsx => app/javascript/mastodon/features/account_timeline/containers/header_container.jsx +1 -1
@@ 83,7 83,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
      modalProps: {
        type: 'follow',
        accountId: account.get('id'),
        url: account.get('url'),
        url: account.get('uri'),
      },
    }));
  },

M app/javascript/mastodon/features/compose/components/search.jsx => app/javascript/mastodon/features/compose/components/search.jsx +0 -4
@@ 139,10 139,6 @@ class Search extends PureComponent {
    this.setState({ expanded: false, selectedOption: -1 });
  };

  findTarget = () => {
    return this.searchForm;
  };

  handleHashtagClick = () => {
    const { router } = this.context;
    const { value, onClickSearchResult } = this.props;

M app/javascript/mastodon/features/interaction_modal/index.jsx => app/javascript/mastodon/features/interaction_modal/index.jsx +245 -53
@@ 1,95 1,296 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import React from 'react';

import { FormattedMessage } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';

import classNames from 'classnames';

import { connect } from 'react-redux';

import { throttle, escapeRegExp } from 'lodash';

import { openModal, closeModal } from 'mastodon/actions/modal';
import api from 'mastodon/api';
import Button from 'mastodon/components/button';
import { Icon }  from 'mastodon/components/icon';
import { registrationsOpen } from 'mastodon/initial_state';

const messages = defineMessages({
  loginPrompt: { id: 'interaction_modal.login.prompt', defaultMessage: 'Domain of your home server, e.g. mastodon.social' },
});

const mapStateToProps = (state, { accountId }) => ({
  displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
  signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
});

const mapDispatchToProps = (dispatch) => ({
  onSignupClick() {
    dispatch(closeModal({
      modalType: undefined,
      ignoreFocus: false,
    }));
    dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
    dispatch(closeModal());
    dispatch(openModal('CLOSED_REGISTRATIONS'));
  },
});

class Copypaste extends PureComponent {
const PERSISTENCE_KEY = 'mastodon_home';

const isValidDomain = value => {
  const url = new URL('https:///path');
  url.hostname = value;
  return url.hostname === value;
};

const valueToDomain = value => {
  // If the user starts typing an URL
  if (/^https?:\/\//.test(value)) {
    try {
      const url = new URL(value);

      // Consider that if there is a path, the URL is more meaningful than a bare domain
      if (url.pathname.length > 1) {
        return '';
      }

      return url.host;
    } catch {
      return undefined;
    }
  // If the user writes their full handle including username
  } else if (value.includes('@')) {
    if (value.replace(/^@/, '').split('@').length > 2) {
      return undefined;
    }
    return '';
  }

  return value;
};

const addInputToOptions = (value, options) => {
  value = value.trim();

  if (value.includes('.') && isValidDomain(value)) {
    return [value].concat(options.filter((x) => x !== value));
  }

  return options;
};

class LoginForm extends React.PureComponent {

  static propTypes = {
    value: PropTypes.string,
    resourceUrl: PropTypes.string,
    intl: PropTypes.object.isRequired,
  };

  state = {
    copied: false,
    value: localStorage ? (localStorage.getItem(PERSISTENCE_KEY) || '') : '',
    expanded: false,
    selectedOption: -1,
    isLoading: false,
    isSubmitting: false,
    error: false,
    options: [],
    networkOptions: [],
  };

  setRef = c => {
    this.input = c;
  };

  handleInputClick = () => {
    this.setState({ copied: false });
    this.input.focus();
    this.input.select();
    this.input.setSelectionRange(0, this.input.value.length);
  handleChange = ({ target }) => {
    this.setState(state => ({ value: target.value, isLoading: true, error: false, options: addInputToOptions(target.value, state.networkOptions) }), () => this._loadOptions());
  };

  handleButtonClick = () => {
    const { value } = this.props;
    navigator.clipboard.writeText(value);
    this.input.blur();
    this.setState({ copied: true });
    this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
  handleMessage = (event) => {
    const { resourceUrl } = this.props;

    if (event.origin !== window.origin || event.source !== this.iframeRef.contentWindow) {
      return;
    }

    if (event.data?.type === 'fetchInteractionURL-failure') {
      this.setState({ isSubmitting: false, error: true });
    } else if (event.data?.type === 'fetchInteractionURL-success') {
      if (/^https?:\/\//.test(event.data.template)) {
        if (localStorage) {
          localStorage.setItem(PERSISTENCE_KEY, event.data.uri_or_domain);
        }

        window.location.href = event.data.template.replace('{uri}', encodeURIComponent(resourceUrl));
      } else {
        this.setState({ isSubmitting: false, error: true });
      }
    }
  };

  componentDidMount () {
    window.addEventListener('message', this.handleMessage);
  }

  componentWillUnmount () {
    if (this.timeout) clearTimeout(this.timeout);
    window.removeEventListener('message', this.handleMessage);
  }

  handleSubmit = () => {
    const { value } = this.state;

    this.setState({ isSubmitting: true });

    this.iframeRef.contentWindow.postMessage({
      type: 'fetchInteractionURL',
      uri_or_domain: value.trim(),
    }, window.origin);
  };

  setIFrameRef = (iframe) => {
    this.iframeRef = iframe;
  }

  handleFocus = () => {
    this.setState({ expanded: true });
  };

  handleBlur = () => {
    this.setState({ expanded: false });
  };

  handleKeyDown = (e) => {
    const { options, selectedOption } = this.state;

    switch(e.key) {
    case 'ArrowDown':
      e.preventDefault();

      if (options.length > 0) {
        this.setState({ selectedOption: Math.min(selectedOption + 1, options.length - 1) });
      }

      break;
    case 'ArrowUp':
      e.preventDefault();

      if (options.length > 0) {
        this.setState({ selectedOption: Math.max(selectedOption - 1, -1) });
      }

      break;
    case 'Enter':
      e.preventDefault();

      if (selectedOption === -1) {
        this.handleSubmit();
      } else if (options.length > 0) {
        this.setState({ value: options[selectedOption], error: false }, () => this.handleSubmit());
      }

      break;
    }
  };

  handleOptionClick = e => {
    const index  = Number(e.currentTarget.getAttribute('data-index'));
    const option = this.state.options[index];

    e.preventDefault();
    this.setState({ selectedOption: index, value: option, error: false }, () => this.handleSubmit());
  };

  _loadOptions = throttle(() => {
    const { value } = this.state;

    const domain = valueToDomain(value.trim());

    if (typeof domain === 'undefined') {
      this.setState({ options: [], networkOptions: [], isLoading: false, error: true });
      return;
    }

    if (domain.length === 0) {
      this.setState({ options: [], networkOptions: [], isLoading: false });
      return;
    }

    api().get('/api/v1/peers/search', { params: { q: domain } }).then(({ data }) => {
      if (!data) {
        data = [];
      }

      this.setState((state) => ({ networkOptions: data, options: addInputToOptions(state.value, data), isLoading: false }));
    }).catch(() => {
      this.setState({ isLoading: false });
    });
  }, 200, { leading: true, trailing: true });

  render () {
    const { value } = this.props;
    const { copied } = this.state;
    const { intl } = this.props;
    const { value, expanded, options, selectedOption, error, isSubmitting } = this.state;
    const domain = (valueToDomain(value) || '').trim();
    const domainRegExp = new RegExp(`(${escapeRegExp(domain)})`, 'gi');
    const hasPopOut = domain.length > 0 && options.length > 0;

    return (
      <div className={classNames('copypaste', { copied })}>
        <input
          type='text'
          ref={this.setRef}
          value={value}
          readOnly
          onClick={this.handleInputClick}
      <div className={classNames('interaction-modal__login', { focused: expanded, expanded: hasPopOut, invalid: error })}>

        <iframe
          ref={this.setIFrameRef}
          style={{display: 'none'}}
          src='/remote_interaction_helper'
          sandbox='allow-scripts allow-same-origin'
          title='remote interaction helper'
        />

        <button className='button' onClick={this.handleButtonClick}>
          {copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy' defaultMessage='Copy' />}
        </button>
        <div className='interaction-modal__login__input'>
          <input
            ref={this.setRef}
            type='text'
            value={value}
            placeholder={intl.formatMessage(messages.loginPrompt)}
            aria-label={intl.formatMessage(messages.loginPrompt)}
            autoFocus
            onChange={this.handleChange}
            onFocus={this.handleFocus}
            onBlur={this.handleBlur}
            onKeyDown={this.handleKeyDown}
          />

          <Button onClick={this.handleSubmit} disabled={isSubmitting}><FormattedMessage id='interaction_modal.login.action' defaultMessage='Take me home' /></Button>
        </div>

        {hasPopOut && (
          <div className='search__popout'>
            <div className='search__popout__menu'>
              {options.map((option, i) => (
                <button key={option} onMouseDown={this.handleOptionClick} data-index={i} className={classNames('search__popout__menu__item', { selected: selectedOption === i })}>
                  {option.split(domainRegExp).map((part, i) => (
                    part.toLowerCase() === domain.toLowerCase() ? (
                      <mark key={i}>
                        {part}
                      </mark>
                    ) : (
                      <span key={i}>
                        {part}
                      </span>
                    )
                  ))}
                </button>
              ))}
            </div>
          </div>
        )}
      </div>
    );
  }

}

class InteractionModal extends PureComponent {
const IntlLoginForm = injectIntl(LoginForm);

class InteractionModal extends React.PureComponent {

  static propTypes = {
    displayNameHtml: PropTypes.string,
    url: PropTypes.string,
    type: PropTypes.oneOf(['reply', 'reblog', 'favourite', 'follow']),
    onSignupClick: PropTypes.func.isRequired,
    signupUrl: PropTypes.string.isRequired,
  };

  handleSignupClick = () => {


@@ 97,7 298,7 @@ class InteractionModal extends PureComponent {
  };

  render () {
    const { url, type, displayNameHtml, signupUrl } = this.props;
    const { url, type, displayNameHtml } = this.props;

    const name = <bdi dangerouslySetInnerHTML={{ __html: displayNameHtml }} />;



@@ 130,13 331,13 @@ class InteractionModal extends PureComponent {

    if (registrationsOpen) {
      signupButton = (
        <a href={signupUrl} className='button button--block button-tertiary'>
        <a href='/auth/sign_up' className='link-button'>
          <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
        </a>
      );
    } else {
      signupButton = (
        <button className='button button--block button-tertiary' onClick={this.handleSignupClick}>
        <button className='link-button' onClick={this.handleSignupClick}>
          <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
        </button>
      );


@@ 146,22 347,13 @@ class InteractionModal extends PureComponent {
      <div className='modal-root__modal interaction-modal'>
        <div className='interaction-modal__lead'>
          <h3><span className='interaction-modal__icon'>{icon}</span> {title}</h3>
          <p>{actionDescription} <FormattedMessage id='interaction_modal.preamble' defaultMessage="Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one." /></p>
          <p>{actionDescription} <strong><FormattedMessage id='interaction_modal.sign_in' defaultMessage='You are not logged in to this server. Where is your account hosted?' /></strong></p>
        </div>

        <div className='interaction-modal__choices'>
          <div className='interaction-modal__choices__choice'>
            <h3><FormattedMessage id='interaction_modal.on_this_server' defaultMessage='On this server' /></h3>
            <a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
            {signupButton}
          </div>
        <IntlLoginForm resourceUrl={url} />

          <div className='interaction-modal__choices__choice'>
            <h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3>
            <p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.' /></p>
            <Copypaste value={url} />
          </div>
        </div>
        <p className='hint'><FormattedMessage id='interaction_modal.sign_in_hint' defaultMessage="Tip: That's the website where you signed up. If you don't remember, look for the welcome e-mail in your inbox. You can also enter your full username! (e.g. @Mastodon@mastodon.social)" /></p>
        <p><FormattedMessage id='interaction_modal.no_account_yet' defaultMessage='Not on Mastodon?' /> {signupButton}</p>
      </div>
    );
  }

M app/javascript/mastodon/features/picture_in_picture/components/footer.jsx => app/javascript/mastodon/features/picture_in_picture/components/footer.jsx +3 -3
@@ 91,7 91,7 @@ class Footer extends ImmutablePureComponent {
        modalProps: {
          type: 'reply',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }


@@ 113,7 113,7 @@ class Footer extends ImmutablePureComponent {
        modalProps: {
          type: 'favourite',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }


@@ 142,7 142,7 @@ class Footer extends ImmutablePureComponent {
        modalProps: {
          type: 'reblog',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }

M app/javascript/mastodon/features/status/index.jsx => app/javascript/mastodon/features/status/index.jsx +3 -3
@@ 252,7 252,7 @@ class Status extends ImmutablePureComponent {
        modalProps: {
          type: 'favourite',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }


@@ 289,7 289,7 @@ class Status extends ImmutablePureComponent {
        modalProps: {
          type: 'reply',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }


@@ 319,7 319,7 @@ class Status extends ImmutablePureComponent {
        modalProps: {
          type: 'reblog',
          accountId: status.getIn(['account', 'id']),
          url: status.get('url'),
          url: status.get('uri'),
        },
      }));
    }

M app/javascript/mastodon/locales/en.json => app/javascript/mastodon/locales/en.json +5 -3
@@ 191,7 191,6 @@
  "conversation.open": "View conversation",
  "conversation.with": "With {names}",
  "copypaste.copied": "Copied",
  "copypaste.copy": "Copy",
  "copypaste.copy_to_clipboard": "Copy to clipboard",
  "directory.federated": "From known fediverse",
  "directory.local": "From {domain} only",


@@ 311,10 310,13 @@
  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
  "interaction_modal.login.action": "Take me home",
  "interaction_modal.login.prompt": "Domain of your home server, e.g. mastodon.social",
  "interaction_modal.no_account_yet": "Not on Mastodon?",
  "interaction_modal.on_another_server": "On a different server",
  "interaction_modal.on_this_server": "On this server",
  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.",
  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
  "interaction_modal.sign_in": "You are not logged in to this server. Where is your account hosted?",
  "interaction_modal.sign_in_hint": "Tip: That's the website where you signed up. If you don't remember, look for the welcome e-mail in your inbox. You can also enter your full username! (e.g. @Mastodon@mastodon.social)",
  "interaction_modal.title.favourite": "Favorite {name}'s post",
  "interaction_modal.title.follow": "Follow {name}",
  "interaction_modal.title.reblog": "Boost {name}'s post",

A app/javascript/packs/remote_interaction_helper.ts => app/javascript/packs/remote_interaction_helper.ts +172 -0
@@ 0,0 1,172 @@
/*

This script is meant to to be used in an `iframe` with the sole purpose of doing webfinger queries
client-side without being restricted by a strict `connect-src` Content-Security-Policy directive.

It communicates with the parent window through message events that are authenticated by origin,
and performs no other task.

*/

import './public-path';

import axios from 'axios';

interface JRDLink {
  rel: string;
  template?: string;
  href?: string;
}

const isJRDLink = (link: unknown): link is JRDLink =>
  typeof link === 'object' &&
  link !== null &&
  'rel' in link &&
  typeof link.rel === 'string' &&
  (!('template' in link) || typeof link.template === 'string') &&
  (!('href' in link) || typeof link.href === 'string');

const findLink = (rel: string, data: unknown): JRDLink | undefined => {
  if (
    typeof data === 'object' &&
    data !== null &&
    'links' in data &&
    data.links instanceof Array
  ) {
    return data.links.find(
      (link): link is JRDLink => isJRDLink(link) && link.rel === rel,
    );
  } else {
    return undefined;
  }
};

const findTemplateLink = (data: unknown) =>
  findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template;

const fetchInteractionURLSuccess = (
  uri_or_domain: string,
  template: string,
) => {
  window.parent.postMessage(
    {
      type: 'fetchInteractionURL-success',
      uri_or_domain,
      template,
    },
    window.origin,
  );
};

const fetchInteractionURLFailure = () => {
  window.parent.postMessage(
    {
      type: 'fetchInteractionURL-failure',
    },
    window.origin,
  );
};

const isValidDomain = (value: string) => {
  const url = new URL('https:///path');
  url.hostname = value;
  return url.hostname === value;
};

// Attempt to find a remote interaction URL from a domain
const fromDomain = (domain: string) => {
  const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;

  axios
    .get(`https://${domain}/.well-known/webfinger`, {
      params: { resource: `https://${domain}` },
    })
    .then(({ data }) => {
      const template = findTemplateLink(data);
      fetchInteractionURLSuccess(domain, template ?? fallbackTemplate);
      return;
    })
    .catch(() => {
      fetchInteractionURLSuccess(domain, fallbackTemplate);
    });
};

// Attempt to find a remote interaction URL from an arbitrary URL
const fromURL = (url: string) => {
  const domain = new URL(url).host;
  const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;

  axios
    .get(`https://${domain}/.well-known/webfinger`, {
      params: { resource: url },
    })
    .then(({ data }) => {
      const template = findTemplateLink(data);
      fetchInteractionURLSuccess(url, template ?? fallbackTemplate);
      return;
    })
    .catch(() => {
      fromDomain(domain);
    });
};

// Attempt to find a remote interaction URL from a `user@domain` string
const fromAcct = (acct: string) => {
  acct = acct.replace(/^@/, '');

  const segments = acct.split('@');

  if (segments.length !== 2 || !segments[0] || !isValidDomain(segments[1])) {
    fetchInteractionURLFailure();
    return;
  }

  const domain = segments[1];
  const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;

  axios
    .get(`https://${domain}/.well-known/webfinger`, {
      params: { resource: `acct:${acct}` },
    })
    .then(({ data }) => {
      const template = findTemplateLink(data);
      fetchInteractionURLSuccess(acct, template ?? fallbackTemplate);
      return;
    })
    .catch(() => {
      // TODO: handle host-meta?
      fromDomain(domain);
    });
};

const fetchInteractionURL = (uri_or_domain: string) => {
  if (/^https?:\/\//.test(uri_or_domain)) {
    fromURL(uri_or_domain);
  } else if (uri_or_domain.includes('@')) {
    fromAcct(uri_or_domain);
  } else {
    fromDomain(uri_or_domain);
  }
};

window.addEventListener('message', (event: MessageEvent<unknown>) => {
  // Check message origin
  if (
    !window.origin ||
    window.parent !== event.source ||
    event.origin !== window.origin
  ) {
    return;
  }

  if (
    event.data &&
    typeof event.data === 'object' &&
    'type' in event.data &&
    event.data.type === 'fetchInteractionURL' &&
    'uri_or_domain' in event.data &&
    typeof event.data.uri_or_domain === 'string'
  ) {
    fetchInteractionURL(event.data.uri_or_domain);
  }
});

M app/javascript/styles/mastodon/components.scss => app/javascript/styles/mastodon/components.scss +74 -37
@@ 8356,13 8356,13 @@ noscript {
.interaction-modal {
  max-width: 90vw;
  width: 600px;
  background: $ui-base-color;
  background: var(--modal-background-color);
  border: 1px solid var(--modal-border-color);
  border-radius: 8px;
  overflow-x: hidden;
  overflow-y: auto;
  overflow: visible;
  position: relative;
  display: block;
  padding: 20px;
  padding: 40px;

  h3 {
    font-size: 22px;


@@ 8371,64 8371,101 @@ noscript {
    text-align: center;
  }

  p {
    font-size: 17px;
    line-height: 22px;
    color: $darker-text-color;

    strong {
      color: $primary-text-color;
      font-weight: 700;
    }
  }

  p.hint {
    margin-bottom: 14px;
    font-size: 14px;
  }

  &__icon {
    color: $highlight-text-color;
    margin: 0 5px;
  }

  &__lead {
    padding: 20px;
    text-align: center;
    margin-bottom: 20px;

    h3 {
      margin-bottom: 15px;
    }

    p {
      font-size: 17px;
      line-height: 22px;
      color: $darker-text-color;
    }
  }

  &__choices {
    display: flex;
  &__login {
    position: relative;
    margin-bottom: 20px;

    &__choice {
      flex: 0 0 auto;
      width: 50%;
      box-sizing: border-box;
      padding: 20px;
    &__input {
      @include search-input;

      h3 {
        margin-bottom: 20px;
      }
      border: 1px solid lighten($ui-base-color, 8%);
      padding: 4px 6px;
      color: $primary-text-color;
      font-size: 16px;
      line-height: 18px;
      display: flex;
      align-items: center;

      p {
        color: $darker-text-color;
        margin-bottom: 20px;
      }
      input {
        background: transparent;
        color: inherit;
        font: inherit;
        border: 0;
        padding: 15px - 4px 15px - 6px;
        flex: 1 1 auto;

      .button {
        margin-bottom: 10px;
        &::placeholder {
          color: lighten($darker-text-color, 4%);
        }

        &:last-child {
          margin-bottom: 0;
        &:focus {
          outline: 0;
        }
      }

      .button {
        flex: 0 0 auto;
      }
    }

    .search__popout {
      margin-top: -1px;
      padding-top: 5px;
      padding-bottom: 5px;
      border: 1px solid lighten($ui-base-color, 8%);
    }

    &.focused &__input {
      border-color: $highlight-text-color;
      background: lighten($ui-base-color, 4%);
    }

    &.invalid &__input {
      border-color: $error-red;
    }
  }

  @media screen and (max-width: $no-gap-breakpoint - 1px) {
    &__choices {
    &.expanded .search__popout {
      display: block;
    }

      &__choice {
        width: auto;
        margin-bottom: 20px;
      }
    &.expanded &__input {
      border-radius: 4px 4px 0 0;
    }
  }

  .link-button {
    font-size: inherit;
    display: inline;
  }
}

.copypaste {

M app/javascript/styles/mastodon/variables.scss => app/javascript/styles/mastodon/variables.scss +2 -0
@@ 96,4 96,6 @@ $font-monospace: 'mastodon-font-monospace' !default;
  --dropdown-background-color: #{lighten($ui-base-color, 4%)};
  --dropdown-shadow: 0 20px 25px -5px #{rgba($base-shadow-color, 0.25)},
    0 8px 10px -6px #{rgba($base-shadow-color, 0.25)};
  --modal-background-color: #{darken($ui-base-color, 4%)};
  --modal-border-color: #{lighten($ui-base-color, 4%)};
}

M app/javascript/types/resources.ts => app/javascript/types/resources.ts +1 -0
@@ 33,6 33,7 @@ interface AccountApiResponseValues {
  note: string;
  statuses_count: number;
  url: string;
  uri: string;
  username: string;
}


A app/lib/importer/instances_index_importer.rb => app/lib/importer/instances_index_importer.rb +26 -0
@@ 0,0 1,26 @@
# frozen_string_literal: true

class Importer::InstancesIndexImporter < Importer::BaseImporter
  def import!
    index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp|
      in_work_unit(tmp) do |instances|
        bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: instances).bulk_body

        indexed = bulk.count { |entry| entry[:index] }
        deleted = bulk.count { |entry| entry[:delete] }

        Chewy::Index::Import::BulkRequest.new(index).perform(bulk)

        [indexed, deleted]
      end
    end

    wait!
  end

  private

  def index
    InstancesIndex
  end
end

M app/lib/webfinger_resource.rb => app/lib/webfinger_resource.rb +9 -0
@@ 11,6 11,8 @@ class WebfingerResource

  def username
    case resource
    when %r{\A(https?://)?#{instance_actor_regexp}/?\Z}
      Rails.configuration.x.local_domain
    when /\Ahttps?/i
      username_from_url
    when /@/


@@ 22,6 24,13 @@ class WebfingerResource

  private

  def instance_actor_regexp
    hosts = [Rails.configuration.x.local_domain, Rails.configuration.x.web_domain]
    hosts.concat(Rails.configuration.x.alternate_domains) if Rails.configuration.x.alternate_domains.present?

    Regexp.union(hosts)
  end

  def username_from_url
    if account_show_page?
      path_params[:username]

M app/models/instance.rb => app/models/instance.rb +1 -0
@@ 21,6 21,7 @@ class Instance < ApplicationRecord
    belongs_to :unavailable_domain # skipcq: RB-RL1031
  end

  scope :searchable, -> { where.not(domain: DomainBlock.select(:domain)) }
  scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
  scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") }


M app/serializers/rest/account_serializer.rb => app/serializers/rest/account_serializer.rb +5 -1
@@ 5,7 5,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
  include FormattingHelper

  attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
             :note, :url, :avatar, :avatar_static, :header, :header_static,
             :note, :url, :uri, :avatar, :avatar_static, :header, :header_static,
             :followers_count, :following_count, :statuses_count, :last_status_at

  has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?


@@ 66,6 66,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
    ActivityPub::TagManager.instance.url_for(object)
  end

  def uri
    ActivityPub::TagManager.instance.uri_for(object)
  end

  def avatar
    full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_original_url)
  end

M app/serializers/webfinger_serializer.rb => app/serializers/webfinger_serializer.rb +1 -0
@@ 22,6 22,7 @@ class WebfingerSerializer < ActiveModel::Serializer
      [
        { 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 },
        { rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
      ]
    else
      [

D app/views/authorize_interactions/_post_follow_actions.html.haml => app/views/authorize_interactions/_post_follow_actions.html.haml +0 -4
@@ 1,4 0,0 @@
.post-follow-actions
  %div= link_to t('authorize_follow.post_follow.web'), web_url("@#{@resource.pretty_acct}"), 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')

D app/views/authorize_interactions/error.html.haml => app/views/authorize_interactions/error.html.haml +0 -3
@@ 1,3 0,0 @@
.form-container
  .flash-message#error_explanation
    = t('authorize_follow.error')

D app/views/authorize_interactions/show.html.haml => app/views/authorize_interactions/show.html.haml +0 -24
@@ 1,24 0,0 @@
- content_for :page_title do
  = t('authorize_follow.title', acct: @resource.pretty_acct)

.form-container
  .follow-prompt
    = render 'application/card', account: @resource

  - if current_account.following?(@resource)
    .flash-message
      %strong
        = t('authorize_follow.already_following')

    = render 'post_follow_actions'
  - elsif current_account.requested?(@resource)
    .flash-message
      %strong
        = t('authorize_follow.already_requested')

    = render 'post_follow_actions'
  - else
    = form_tag authorize_interaction_path, method: :post, class: 'simple_form' do
      = hidden_field_tag :action, :follow
      = hidden_field_tag :acct, @resource.acct
      = button_tag t('authorize_follow.follow'), type: :submit

D app/views/authorize_interactions/success.html.haml => app/views/authorize_interactions/success.html.haml +0 -13
@@ 1,13 0,0 @@
- content_for :page_title do
  = t('authorize_follow.title', acct: @resource.pretty_acct)

.form-container
  .follow-prompt
    - if @resource.locked?
      %h2= t('authorize_follow.follow_request')
    - else
      %h2= t('authorize_follow.following')

    = render 'application/card', account: @resource

  = render 'post_follow_actions'

A app/views/layouts/helper_frame.html.haml => app/views/layouts/helper_frame.html.haml +8 -0
@@ 0,0 1,8 @@
!!! 5
%html
  %head
    %meta{ charset: 'utf-8' }/

    = javascript_pack_tag 'common', crossorigin: 'anonymous'

    = yield :header_tags

A app/views/remote_interaction_helper/index.html.haml => app/views/remote_interaction_helper/index.html.haml +4 -0
@@ 0,0 1,4 @@
- content_for :header_tags do
  %meta{ name: 'robots', content: 'noindex' }/

  = javascript_pack_tag 'remote_interaction_helper', crossorigin: 'anonymous'

M app/workers/scheduler/instance_refresh_scheduler.rb => app/workers/scheduler/instance_refresh_scheduler.rb +1 -0
@@ 7,5 7,6 @@ class Scheduler::InstanceRefreshScheduler

  def perform
    Instance.refresh
    InstancesIndex.import if Chewy.enabled?
  end
end

M config/locales/an.yml => config/locales/an.yml +0 -12
@@ 982,18 982,6 @@ an:
      view_strikes: Veyer amonestacions pasadas contra la tuya cuenta
    too_fast: Formulario ninviau masiau rapido, lo intente de nuevo.
    use_security_key: Usar la clau de seguranza
  authorize_follow:
    already_following: Ya yes seguindo a esta cuenta
    already_requested: Ya has ninviau una solicitut de seguimiento a ixa cuenta
    error: Desafortunadament, ha ocurriu una error buscando la cuenta remota
    follow: Seguir
    follow_request: 'Tiens una solicitut de seguimiento de:'
    following: 'Exito! Agora yes seguindo a:'
    post_follow:
      close: U, puetz simplament zarrar esta finestra.
      return: Tornar ta lo perfil de l'usuario
      web: Ir ta lo puesto web
    title: Seguir a %{acct}
  challenge:
    confirm: Continar
    hint_html: "<strong>Tip:</strong> No tornaremos a preguntar-te per la clau entre la siguient hora."

M config/locales/ar.yml => config/locales/ar.yml +0 -12
@@ 1065,18 1065,6 @@ ar:
      view_strikes: عرض السجلات السابقة ضد حسابك
    too_fast: تم إرسال النموذج بسرعة كبيرة، حاول مرة أخرى.
    use_security_key: استخدام مفتاح الأمان
  authorize_follow:
    already_following: أنت تتابع بالفعل هذا الحساب
    already_requested: لقد قُمتَ بإرسال طلب متابَعة إلى هذا الحساب مِن قَبل
    error: يا للأسف، وقع هناك خطأ إثر عملية البحث عن الحساب عن بعد
    follow: اتبع
    follow_request: 'لقد قمت بإرسال طلب متابعة إلى:'
    following: 'مرحى! أنت الآن تتبع:'
    post_follow:
      close: أو يمكنك إغلاق هذه النافذة.
      return: اظهر الملف التعريفي للمستخدم
      web: واصل إلى الويب
    title: إتباع %{acct}
  challenge:
    confirm: واصل
    hint_html: "<strong>توصية:</strong> لن نطلب منك ثانية كلمتك السرية في غضون الساعة اللاحقة."

M config/locales/ast.yml => config/locales/ast.yml +0 -9
@@ 481,15 481,6 @@ ast:
      functional: La cuenta ta completamente operativa.
      pending: La to solicitú ta pendiente de que la revise'l nuesu personal ya ye posible que tarde tiempu. Vas recibir un mensaxe si s'aprueba.
    too_fast: El formulariu xubióse mui rápido, volvi tentalo.
  authorize_follow:
    already_following: Xá tas siguiendo a esta cuenta
    already_requested: Yá unviesti una solicitú de siguimientu a esa cuenta
    error: Desafortunadamente, hebo un error al buscar la cuenta remota
    follow_request: 'Unviesti una solicitú de siguimientu a:'
    post_follow:
      close: O pues zarrar esta ventana.
      return: Amosar el perfil de la cuenta
      web: Dir a la web
  challenge:
    confirm: Siguir
    hint_html: "<strong>Conseyu:</strong> nun vamos volver pidite la contraseña hasta dientro d'una hora."

M config/locales/be.yml => config/locales/be.yml +0 -12
@@ 1095,18 1095,6 @@ be:
      view_strikes: Праглядзець мінулыя папярэджанні для вашага ўліковага запісу
    too_fast: Форма адпраўлена занадта хутка, паспрабуйце яшчэ раз.
    use_security_key: Выкарыстаеце ключ бяспекі
  authorize_follow:
    already_following: Вы ўжо падпісаныя на гэты ўліковы запіс
    already_requested: Вы ўжо адправілі запыт на гэты ўліковы запіс
    error: На жаль, падчас пошуку аддаленага ўліковага запісу здарылася памылка
    follow: Падпісацца
    follow_request: 'Вы адправілі запыт на падпіску:'
    following: 'Поспех! Цяпер вы падпісаны на:'
    post_follow:
      close: Або, вы можаце проста закрыць гэтае акно.
      return: Паказаць профіль карыстальніка
      web: Перайсці ў вэб-версію
    title: Падпісацца на %{acct}
  challenge:
    confirm: Працягнуць
    hint_html: "<strong>Парада:</strong> Мы не будзем запытваць ваш пароль зноўку на працягу наступнай гадзіны."

M config/locales/bg.yml => config/locales/bg.yml +0 -12
@@ 1059,18 1059,6 @@ bg:
      view_strikes: Преглед на предишните предупреждения против акаунта ви
    too_fast: Образецът подаден пребързо, опитайте пак.
    use_security_key: Употреба на ключ за сигурност
  authorize_follow:
    already_following: Вече следвате този акаунт
    already_requested: Вече сте изпратили заявка за последване до този акаунт
    error: Възникна грешка, търсейки отдалечения акаунт
    follow: Последвай
    follow_request: 'Изпратихте следната заявка до:'
    following: 'Успешно! Сега сте последвали:'
    post_follow:
      close: Или просто затворете този прозорец.
      return: Показване на профила на потребителя
      web: Към мрежата
    title: Последвай %{acct}
  challenge:
    confirm: Продължаване
    hint_html: "<strong>Съвет</strong>: няма да ви питаме пак за паролата през следващия час."

M config/locales/br.yml => config/locales/br.yml +0 -5
@@ 299,11 299,6 @@ br:
    security: Diogelroez
    status:
      account_status: Statud ar gont
  authorize_follow:
    follow: Heuliañ
    post_follow:
      web: Distreiñ d'an etrefas web
    title: Heuliañ %{acct}
  challenge:
    confirm: Kenderc' hel
    invalid_password: Ger-tremen diwiriek

M config/locales/ca.yml => config/locales/ca.yml +0 -12
@@ 1060,18 1060,6 @@ ca:
      view_strikes: Veure accions del passat contra el teu compte
    too_fast: Formulari enviat massa ràpid, torna a provar-ho.
    use_security_key: Usa clau de seguretat
  authorize_follow:
    already_following: Ja estàs seguint aquest compte
    already_requested: Ja has enviat una sol·licitud de seguiment a aquest usuari
    error: Malauradament, ha ocorregut un error cercant el compte remot
    follow: Segueix
    follow_request: 'Has enviat una sol·licitud de seguiment a:'
    following: 'Perfecte! Ara segueixes:'
    post_follow:
      close: O bé, pots tancar aquesta finestra.
      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."

M config/locales/ckb.yml => config/locales/ckb.yml +0 -12
@@ 620,18 620,6 @@ ckb:
      view_strikes: بینینی لێدانەکانی ڕابردوو لە دژی ئەکاونتەکەت
    too_fast: فۆڕم زۆر خێرا پێشکەش کراوە، دووبارە هەوڵبدەرەوە.
    use_security_key: کلیلی ئاسایش بەکاربهێنە
  authorize_follow:
    already_following: ئێوە ئێستا شوێن کەوتووی ئەم هەژمارەیەی
    already_requested: تۆ پێشتر داواکاری بەدواداچوت ناردوە بۆ ئەو هەژمارە
    error: بەداخەوە هەڵەیەک هەبوو لە کاتی گەڕان بەدوای ئەو هەژمارەیە
    follow: شوێن کەوە
    follow_request: 'تۆ داواکاری شوێنکەوتنت ناردووە بۆ:'
    following: 'ئەنجام بوو! تۆ ئێستا بەدوای ئەم بەکارهێنەرە دەکەویت:'
    post_follow:
      close: یان، دەتوانیت ئەم پەنجەرەیە دابخەیت.
      return: پرۆفایلی بەکارهێنەر نیشان بدە
      web: بڕۆ بۆ وێب
    title: دوای %{acct} بکەوە
  challenge:
    confirm: بەردەوام بە
    hint_html: "<strong>خاڵ:</strong> ئیمە لە کاتژمێری داهاتوو تێپەروشەت لێداوا ناکەین."

M config/locales/co.yml => config/locales/co.yml +0 -12
@@ 583,18 583,6 @@ co:
      redirecting_to: U vostru contu hè inattivu perchè riindirizza versu %{acct}.
    too_fast: Furmulariu mandatu troppu prestu, ripruvate.
    use_security_key: Utilizà a chjave di sicurità
  authorize_follow:
    already_following: Site digià abbunatu·a à stu contu
    already_requested: Avete digià mandatu una dumanda d'abbunamentu à stu contu
    error: Peccatu, c’hè statu un prublemu ricercandu u contu
    follow: Siguità
    follow_request: 'Avete dumandatu di siguità:'
    following: 'Eccu! Avà seguitate:'
    post_follow:
      close: O pudete ancu chjude sta finestra.
      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."

M config/locales/cs.yml => config/locales/cs.yml +0 -12
@@ 1075,18 1075,6 @@ cs:
      view_strikes: Zobrazit minulé prohřešky vašeho účtu
    too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu.
    use_security_key: Použít bezpečnostní klíč
  authorize_follow:
    already_following: Tento účet již sledujete
    already_requested: Tomuto účtu už jste žádost o sledování zaslali
    error: Při hledání vzdáleného účtu bohužel nastala chyba
    follow: Sledovat
    follow_request: 'Poslali jste žádost o sledování uživateli:'
    following: 'Podařilo se! Nyní sledujete uživatele:'
    post_follow:
      close: Nebo můžete toto okno klidně zavřít.
      return: Zobrazit profil uživatele
      web: Přejít na web
    title: Sledovat %{acct}
  challenge:
    confirm: Pokračovat
    hint_html: "<strong>Tip:</strong> Po dobu jedné hodiny vás o heslo nebudeme znovu žádat."

M config/locales/cy.yml => config/locales/cy.yml +0 -12
@@ 1132,18 1132,6 @@ cy:
      view_strikes: Gweld rybuddion y gorffennol yn erbyn eich cyfrif
    too_fast: Cafodd y ffurflen ei chyflwyno'n rhy gyflym, ceisiwch eto.
    use_security_key: Defnyddiwch allwedd diogelwch
  authorize_follow:
    already_following: Rydych yn dilyn y cyfrif hwn yn barod
    already_requested: Rydych chi eisoes wedi anfon cais i ddilyn y cyfrif hwnnw
    error: Yn anffodus, roedd gwall tra'n edrych am y cyfrif pell
    follow: Dilyn
    follow_request: 'Rydych wedi anfon cais dilyn at:'
    following: 'Llwyddiant! Rydych nawr yn dilyn:'
    post_follow:
      close: Neu, gallwch gau'r ffenest hon.
      return: Dangos proffil y defnyddiwr
      web: Ewch i'r we
    title: Dilyn %{acct}
  challenge:
    confirm: Parhau
    hint_html: "<strong>Awgrym:</strong> Fyddwn ni ddim yn gofyn i chi am eich cyfrinair eto am yr awr nesaf."

M config/locales/da.yml => config/locales/da.yml +0 -12
@@ 1060,18 1060,6 @@ da:
      view_strikes: Se tidligere anmeldelser af din konto
    too_fast: Formularen indsendt for hurtigt, forsøg igen.
    use_security_key: Brug sikkerhedsnøgle
  authorize_follow:
    already_following: Du følger allerede denne konto
    already_requested: Du har allerede sendt en følgeanmodning til den konto
    error: Desværre opstod en fejl under søgning af fjernkontoen
    follow: Følg
    follow_request: 'Du har sendt en følgeanmodning til:'
    following: 'Accepteret! Du følger nu:'
    post_follow:
      close: Du kan også bare lukke dette vindue.
      return: Vis brugerens profil
      web: Gå til web
    title: Følg %{acct}
  challenge:
    confirm: Fortsæt
    hint_html: "<strong>Tip:</strong> Du bliver ikke anmodet om din adgangskode igen den næste time."

M config/locales/de.yml => config/locales/de.yml +0 -12
@@ 1060,18 1060,6 @@ de:
      view_strikes: Vorherige Verstöße deines Kontos ansehen
    too_fast: Formular zu schnell übermittelt. Bitte versuche es erneut.
    use_security_key: Sicherheitsschlüssel verwenden
  authorize_follow:
    already_following: Du folgst diesem Konto bereits
    already_requested: Du hast bereits eine Anfrage zum Folgen an dieses Konto gestellt
    error: Bedauerlicherweise konnte das externe Konto nicht geladen werden
    follow: Folgen
    follow_request: 'Du hast eine Anfrage zum Folgen gestellt an:'
    following: 'Erfolg! Du folgst nun:'
    post_follow:
      close: Oder du schließt einfach dieses Fenster.
      return: Profil anzeigen
      web: Im Webinterface ö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."

M config/locales/el.yml => config/locales/el.yml +0 -12
@@ 1039,18 1039,6 @@ el:
      view_strikes: Προβολή προηγούμενων ποινών εναντίον του λογαριασμού σας
    too_fast: Η φόρμα υποβλήθηκε πολύ γρήγορα, προσπαθήστε ξανά.
    use_security_key: Χρήση κλειδιού ασφαλείας
  authorize_follow:
    already_following: Ήδη ακολουθείς αυτό το λογαριασμό
    already_requested: Έχετε ήδη στείλει ένα αίτημα ακολούθησης σε αυτόν τον λογαριασμό
    error: Δυστυχώς παρουσιάστηκε ένα σφάλμα κατά την αναζήτηση του απομακρυσμένου λογαριασμού
    follow: Ακολούθησε
    follow_request: 'Έστειλες αίτημα παρακολούθησης προς:'
    following: 'Επιτυχία! Πλέον ακολουθείς τον/την:'
    post_follow:
      close: Ή, μπορείς απλά να κλείσεις αυτό το παράθυρο.
      return: Δείξε το προφίλ του χρήστη
      web: Πήγαινε στο δίκτυο
    title: Ακολούθησε %{acct}
  challenge:
    confirm: Συνέχεια
    hint_html: "<strong>Συμβουλή:</strong> Δεν θα σου ζητήσουμε τον κωδικό ασφαλείας σου ξανά για την επόμενη ώρα."

M config/locales/en-GB.yml => config/locales/en-GB.yml +0 -12
@@ 1060,18 1060,6 @@ en-GB:
      view_strikes: View past strikes against your account
    too_fast: Form submitted too fast, try again.
    use_security_key: Use security key
  authorize_follow:
    already_following: You are already following this account
    already_requested: You have already sent a follow request to that account
    error: Unfortunately, there was an error looking up the remote account
    follow: Follow
    follow_request: 'You have sent a follow request to:'
    following: 'Success! You are now following:'
    post_follow:
      close: Or, you can just close this window.
      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."

M config/locales/en.yml => config/locales/en.yml +0 -12
@@ 1060,18 1060,6 @@ en:
      view_strikes: View past strikes against your account
    too_fast: Form submitted too fast, try again.
    use_security_key: Use security key
  authorize_follow:
    already_following: You are already following this account
    already_requested: You have already sent a follow request to that account
    error: Unfortunately, there was an error looking up the remote account
    follow: Follow
    follow_request: 'You have sent a follow request to:'
    following: 'Success! You are now following:'
    post_follow:
      close: Or, you can just close this window.
      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."

M config/locales/eo.yml => config/locales/eo.yml +0 -12
@@ 1056,18 1056,6 @@ eo:
      view_strikes: Vidi antauaj admonoj kontra via konto
    too_fast: Formularo sendita tro rapide, klopodu denove.
    use_security_key: Uzi sekurecan ŝlosilon
  authorize_follow:
    already_following: Vi jam sekvas tiun konton
    already_requested: Vi jam sendis peton de sekvado al ĉi tiu konto
    error: Bedaŭrinde, estis eraro en la serĉado de la fora konto
    follow: Sekvi
    follow_request: 'Vi sendis peton de sekvado al:'
    following: 'Sukceson! Vi nun sekvas:'
    post_follow:
      close: Aŭ, vi povas simple fermi ĉi tiun fenestron.
      return: Montri la profilon de la uzanto
      web: Iri al reto
    title: Sekvi %{acct}
  challenge:
    confirm: Daŭrigi
    hint_html: "<strong>Konsileto:</strong> Ni ne demandos pri via pasvorto ĝis 1 horo."

M config/locales/es-AR.yml => config/locales/es-AR.yml +0 -12
@@ 1060,18 1060,6 @@ es-AR:
      view_strikes: Ver incumplimientos pasados contra tu cuenta
    too_fast: Formulario enviado demasiado rápido, probá de nuevo.
    use_security_key: Usar la llave de seguridad
  authorize_follow:
    already_following: Ya estás siguiendo a esta cuenta
    already_requested: Ya enviaste una solicitud de seguimiento a esa cuenta
    error: Lamentablemente, ocurrió un error buscando la cuenta remota
    follow: Seguir
    follow_request: 'Enviaste una solicitud de seguimiento a:'
    following: "¡Listo! Ahora estás siguiendo a:"
    post_follow:
      close: O simplemente podés cerrar esta ventana.
      return: Mostrar el perfil del usuario
      web: Ir a la web
    title: Seguir a %{acct}
  challenge:
    confirm: Continuar
    hint_html: "<strong>Dato:</strong> No volveremos a preguntarte por la contraseña durante la siguiente hora."

M config/locales/es-MX.yml => config/locales/es-MX.yml +0 -12
@@ 1060,18 1060,6 @@ es-MX:
      view_strikes: Ver amonestaciones pasadas contra tu cuenta
    too_fast: Formulario enviado demasiado rápido, inténtelo de nuevo.
    use_security_key: Usar la clave de seguridad
  authorize_follow:
    already_following: Ya estás siguiendo a esta cuenta
    already_requested: Ya has enviado una solicitud de seguimiento a esa cuenta
    error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota
    follow: Seguir
    follow_request: 'Tienes una solicitud de seguimiento de:'
    following: "¡Éxito! Ahora estás siguiendo a:"
    post_follow:
      close: O, puedes simplemente cerrar esta ventana.
      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."

M config/locales/es.yml => config/locales/es.yml +0 -12
@@ 1060,18 1060,6 @@ es:
      view_strikes: Ver amonestaciones pasadas contra tu cuenta
    too_fast: Formulario enviado demasiado rápido, inténtelo de nuevo.
    use_security_key: Usar la clave de seguridad
  authorize_follow:
    already_following: Ya estás siguiendo a esta cuenta
    already_requested: Ya has enviado una solicitud de seguimiento a esa cuenta
    error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota
    follow: Seguir
    follow_request: 'Tienes una solicitud de seguimiento de:'
    following: "¡Éxito! Ahora estás siguiendo a:"
    post_follow:
      close: O, puedes simplemente cerrar esta ventana.
      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."

M config/locales/et.yml => config/locales/et.yml +0 -12
@@ 1060,18 1060,6 @@ et:
      view_strikes: Vaata enda eelnevaid juhtumeid
    too_fast: Vorm esitatud liiga kiirelt, proovi uuesti.
    use_security_key: Kasuta turvavõtit
  authorize_follow:
    already_following: Juba jälgid seda kontot
    already_requested: Saatsid juba sellele kontole jälgimistaotluse
    error: Kahjuks ilmus viga kasutaja kaugserverist otsimisel
    follow: Jälgi
    follow_request: 'Oled saatnud jälgimistaotluse kasutajale:'
    following: 'Õnnestus! Jälgid nüüd kasutajat:'
    post_follow:
      close: Või sulge lihtsalt see aken.
      return: Näita kasutaja profiili
      web: Mine veebi
    title: Jälgi %{acct}
  challenge:
    confirm: Jätka
    hint_html: "<strong>Nõuanne:</strong> Me ei küsi salasõna uuesti järgmise tunni jooksul."

M config/locales/eu.yml => config/locales/eu.yml +0 -12
@@ 1051,18 1051,6 @@ eu:
      view_strikes: Ikusi zure kontuaren aurkako neurriak
    too_fast: Formularioa azkarregi bidali duzu, saiatu berriro.
    use_security_key: Erabili segurtasun gakoa
  authorize_follow:
    already_following: Kontu hau aurretik jarraitzen duzu
    already_requested: Bidali duzu dagoeneko kontu hori jarraitzeko eskaera bat
    error: Zoritxarrez, urruneko kontua bilatzean errore bat gertatu da
    follow: Jarraitu
    follow_request: 'Jarraitzeko eskari bat bidali duzu hona:'
    following: 'Ongi! Orain jarraitzen duzu:'
    post_follow:
      close: Edo, leiho hau besterik gabe itxi dezakezu.
      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."

M config/locales/fa.yml => config/locales/fa.yml +0 -12
@@ 889,18 889,6 @@ fa:
      view_strikes: دیدن شکایت‌های گذشته از حسابتان
    too_fast: فرم با سرعت بسیار زیادی فرستاده شد، دوباره تلاش کنید.
    use_security_key: استفاده از کلید امنیتی
  authorize_follow:
    already_following: شما همین الان هم این حساب را پی‌می‌گیرید
    already_requested: درخواست پی‌گیری‌ای برای آن حساب فرستاده‌ بودید
    error: متأسفانه حین یافتن آن حساب خطایی رخ داد
    follow: پی بگیرید
    follow_request: 'شما درخواست پیگیری فرستاده‌اید به:'
    following: 'انجام شد! شما هم‌اینک پیگیر این کاربر هستید:'
    post_follow:
      close: یا این پنجره را ببندید.
      return: نمایهٔ این کاربر را نشان بده
      web: رفتن به وب
    title: پیگیری %{acct}
  challenge:
    confirm: ادامه
    hint_html: "<strong>نکته:</strong> ما در یک ساعت آینده گذرواژه‌تان را از شما نخواهیم پرسید."

M config/locales/fi.yml => config/locales/fi.yml +0 -12
@@ 1060,18 1060,6 @@ fi:
      view_strikes: Näytä tiliäsi koskevia aiempia varoituksia
    too_fast: Lomake lähetettiin liian nopeasti, yritä uudelleen.
    use_security_key: Käytä suojausavainta
  authorize_follow:
    already_following: Sinä seuraat jo tätä tiliä
    already_requested: Olet jo lähettänyt seurantapyynnön tälle tilille
    error: Valitettavasti etätilin haussa tapahtui virhe
    follow: Seuraa
    follow_request: 'Olet lähettänyt seuraamispyynnön käyttäjälle:'
    following: 'Onnistui! Seuraat käyttäjää:'
    post_follow:
      close: Tai voit sulkea tämän ikkunan.
      return: Palaa käyttäjän profiiliin
      web: Siirry verkkosivulle
    title: Seuraa käyttäjää %{acct}
  challenge:
    confirm: Jatka
    hint_html: "<strong>Vihje:</strong> Emme pyydä sinulta salasanaa uudelleen seuraavan tunnin aikana."

M config/locales/fo.yml => config/locales/fo.yml +0 -12
@@ 1060,18 1060,6 @@ fo:
      view_strikes: Vís eldri atsóknir móti tíni kontu
    too_fast: Oyðublaðið innsent ov skjótt, royn aftur.
    use_security_key: Brúka trygdarlykil
  authorize_follow:
    already_following: Tú fylgir longu hesi kontuni
    already_requested: Tú hevur longu sent eina fylgiumbøn til hasa kontuna
    error: Tíverri kom ein feilur, tá vit royndu at finna fjarkontuna
    follow: Fylg
    follow_request: 'Tú hevur sent eina fylgjaraumbøn til:'
    following: 'Góðkent! Tú fylgir nú:'
    post_follow:
      close: Ella kanst tú bara lata hetta vindeygað aftur.
      return: Vís vangan hjá brúkaranum
      web: Far á vevið
    title: Fylg %{acct}
  challenge:
    confirm: Hald á
    hint_html: "<strong>Góð ráð:</strong> vit spyrja teg ikki aftur um loyniorðið næsta tíman."

M config/locales/fr-QC.yml => config/locales/fr-QC.yml +0 -12
@@ 1060,18 1060,6 @@ fr-QC:
      view_strikes: Voir les sanctions précédemment appliquées à votre compte
    too_fast: Formulaire envoyé trop rapidement, veuillez réessayer.
    use_security_key: Utiliser la clé de sécurité
  authorize_follow:
    already_following: Vous suivez déjà ce compte
    already_requested: Vous avez déjà envoyé une demande d’abonnement à ce compte
    error: Malheureusement, une erreur s'est produite lors de la recherche du compte distant
    follow: Suivre
    follow_request: 'Vous avez demandé à suivre :'
    following: 'Youpi ! Vous suivez maintenant  :'
    post_follow:
      close: Ou bien, vous pouvez fermer cette fenêtre.
      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."

M config/locales/fr.yml => config/locales/fr.yml +0 -12
@@ 1060,18 1060,6 @@ fr:
      view_strikes: Voir les sanctions précédemment appliquées à votre compte
    too_fast: Formulaire envoyé trop rapidement, veuillez réessayer.
    use_security_key: Utiliser la clé de sécurité
  authorize_follow:
    already_following: Vous suivez déjà ce compte
    already_requested: Vous avez déjà envoyé une demande d’abonnement à ce compte
    error: Malheureusement, une erreur s'est produite lors de la recherche du compte distant
    follow: Suivre
    follow_request: 'Vous avez demandé à suivre :'
    following: 'Youpi ! Vous suivez maintenant  :'
    post_follow:
      close: Ou bien, vous pouvez fermer cette fenêtre.
      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."

M config/locales/fy.yml => config/locales/fy.yml +0 -12
@@ 1060,18 1060,6 @@ fy:
      view_strikes: Besjoch de earder troch moderatoaren fêststelde skeiningen dy’t jo makke hawwe
    too_fast: Formulier is te fluch yntsjinne. Probearje it nochris.
    use_security_key: Befeiligingskaai brûke
  authorize_follow:
    already_following: Jo folgje dizze account al
    already_requested: Jo hawwe al in folchfersyk nei dat account ferstjoerd
    error: Spitiger, der is in flater bard by it opsykjen fan de eksterne account
    follow: Folgje
    follow_request: 'Jo hawwe in folchfersyk yntsjinne by:'
    following: 'Slagge! Jo folgje no:'
    post_follow:
      close: Of jo kinne dit finster gewoan slute.
      return: Profyl fan dizze brûker toane
      web: Gean nei de webapp
    title: "%{acct} folgje"
  challenge:
    confirm: Trochgean
    hint_html: "<strong>Tip:</strong> Wy freegje jo it kommende oere net mear nei jo wachtwurd."

M config/locales/ga.yml => config/locales/ga.yml +0 -5
@@ 331,11 331,6 @@ ga:
    status:
      account_status: Stádas cuntais
    too_fast: Cuireadh an fhoirm isteach róthapa, triail arís.
  authorize_follow:
    follow: Lean
    post_follow:
      return: Taispeáin próifíl an úsáideora
    title: Lean %{acct}
  challenge:
    confirm: Lean ar aghaidh
  datetime:

M config/locales/gd.yml => config/locales/gd.yml +0 -12
@@ 1095,18 1095,6 @@ gd:
      view_strikes: Seall na rabhaidhean a fhuair an cunntas agad roimhe
    too_fast: Chaidh am foirm a chur a-null ro luath, feuch ris a-rithist.
    use_security_key: Cleachd iuchair tèarainteachd
  authorize_follow:
    already_following: Tha thu a’ leantainn a’ chunntais seo mu thràth
    already_requested: Chuir thu iarrtas leantainn dhan chunntas seo mu thràth
    error: Gu mì-fhortanach, thachair mearachd le lorg a’ chunntais chèin
    follow: Lean
    follow_request: 'Chuir thu iarrtas leantainn gu:'
    following: 'Taghta! Chaidh leat a’ leantainn:'
    post_follow:
      close: Air neo dùin an uinneag seo.
      return: Seall pròifil a’ chleachdaiche
      web: Tadhail air an duilleag-lìn
    title: Lean %{acct}
  challenge:
    confirm: Lean air adhart
    hint_html: "<strong>Gliocas:</strong> Chan iarr sinn am facal-faire agad ort a-rithist fad uair a thìde."

M config/locales/gl.yml => config/locales/gl.yml +0 -12
@@ 1060,18 1060,6 @@ gl:
      view_strikes: Ver avisos anteriores respecto da túa conta
    too_fast: Formulario enviado demasiado rápido, inténtao outra vez.
    use_security_key: Usa chave de seguridade
  authorize_follow:
    already_following: Xa está a seguir esta conta
    already_requested: Xa tes enviada unha solicitude de seguimento a esa conta
    error: Desgraciadamente, algo fallou ao buscar a conta remota
    follow: Seguir
    follow_request: 'Enviaches unha petición de seguimento a:'
    following: 'Parabéns! Agora segues a:'
    post_follow:
      close: Ou, podes pechar esta ventá.
      return: Mostrar o perfil da usuaria
      web: Ir á web
    title: Seguir %{acct}
  challenge:
    confirm: Continuar
    hint_html: "<strong>Nota:</strong> Non che pediremos o contrasinal na seguinte hora."

M config/locales/he.yml => config/locales/he.yml +0 -12
@@ 1096,18 1096,6 @@ he:
      view_strikes: צפיה בעברות קודמות שנרשמו נגד חשבונך
    too_fast: הטופס הוגש מהר מדי, נסה/י שוב.
    use_security_key: שימוש במפתח אבטחה
  authorize_follow:
    already_following: את/ה כבר עוקב/ת אחרי חשבון זה
    already_requested: כבר נשלחה בקשת מעקב לחשבון זה
    error: למרבה הצער, היתה שגיאה בחיפוש החשבון המרוחק
    follow: לעקוב
    follow_request: 'שלחת בקשת מעקב ל:'
    following: 'הצלחה! הינך עוקב עכשיו אחרי:'
    post_follow:
      close: או, פשוט לסגור חלון זה.
      return: הצג את פרופיל המשתמש
      web: מעבר לווב
    title: לעקוב אחרי %{acct}
  challenge:
    confirm: המשך
    hint_html: "<strong>טיפ:</strong> לא נבקש את סיסמתך שוב בשעה הקרובה."

M config/locales/hr.yml => config/locales/hr.yml +0 -4
@@ 64,10 64,6 @@ hr:
    reset_password: Ponovno postavi lozinku
    security: Sigurnost
    set_new_password: Postavi novu lozinku
  authorize_follow:
    error: Nažalost, došlo je do greške tijekom traženja udaljenog računa
    follow: Prati
    title: Prati %{acct}
  datetime:
    distance_in_words:
      about_x_months: "%{count}mj"

M config/locales/hu.yml => config/locales/hu.yml +0 -12
@@ 1060,18 1060,6 @@ hu:
      view_strikes: Fiókod ellen felrótt korábbi vétségek megtekintése
    too_fast: Túl gyorsan küldted el az űrlapot, próbáld később.
    use_security_key: Biztonsági kulcs használata
  authorize_follow:
    already_following: Már követed ezt a felhasználót
    already_requested: Már küldtél követési kérelmet ennek a fióknak
    error: Hiba történt a távoli felhasználó keresésekor
    follow: Követés
    follow_request: 'Engedélyt kértél az alábbi felhasználó követésére:'
    following: 'Siker! Mostantól követed az alábbi felhasználót:'
    post_follow:
      close: Akár be is zárhatod ezt az ablakot.
      return: A felhasználó profiljának mutatása
      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."

M config/locales/hy.yml => config/locales/hy.yml +0 -11
@@ 481,17 481,6 @@ hy:
      account_status: Հաշուի կարգավիճակ
      pending: Դիմումը պէտք է քննուի մեր անձնակազմի կողմից, ինչը կարող է մի փոքր ժամանակ խլել։ Դիմումի հաստատուելու դէպքում, կտեղեկացնենք նամակով։
    use_security_key: Օգտագործել անվտանգութեան բանալի
  authorize_follow:
    already_following: Դու արդէն հետեւում ես այս հաշուին
    already_requested: Դու արդէն ուղարկել ես հետեւմանն յայտ այս հաշուին
    follow: Հետևել
    follow_request: Դու ուղարկել ես հետեւելու հայց՝
    following: Յաջողութի՜ւն։ Դու այժմ հետեւում ես․
    post_follow:
      close: Կամ, կարող ես պարզապէս փակել այս պատուհանը։
      return: Ցուցադրել օգտատիրոջ էջը
      web: Անցնել վէբին
    title: Հետեւել %{acct}
  challenge:
    confirm: Շարունակել
    invalid_password: Անվաւեր ծածկագիր

M config/locales/id.yml => config/locales/id.yml +0 -12
@@ 959,18 959,6 @@ id:
      view_strikes: Lihat hukuman lalu yang pernah terjadi kepada akun Anda
    too_fast: Formulir dikirim terlalu cepat, coba lagi.
    use_security_key: Gunakan kunci keamanan
  authorize_follow:
    already_following: Anda sudah mengikuti akun ini
    already_requested: Anda sudah mengirimkan permintaan untuk mengikuti akun tersebut
    error: Sayangnya, ada error saat melihat akun remote
    follow: Ikuti
    follow_request: 'Anda telah mengirim permintaan untuk mengikuti ke:'
    following: 'Berhasil! Anda sekarang mengikuti:'
    post_follow:
      close: Atau Anda dapat menutup jendela ini.
      return: Tampilkan profil pengguna
      web: Ke web
    title: Mengikuti %{acct}
  challenge:
    confirm: Lanjut
    hint_html: "<strong>Tip:</strong> Kami tidak akan meminta kata sandi Anda lagi untuk beberapa jam ke depan."

M config/locales/io.yml => config/locales/io.yml +0 -12
@@ 935,18 935,6 @@ io:
      view_strikes: Videz antea streki kontre vua konto
    too_fast: Formulario sendesis tro rapide, probez itere.
    use_security_key: Uzes sekuresklefo
  authorize_follow:
    already_following: Vu ja sequis ca konto
    already_requested: Vu ja sendis sequodemando a ta konto
    error: Regretinde, eventis eraro probante konsultar la fora konto
    follow: Sequar
    follow_request: 'Vu sendis sequodemando a:'
    following: 'Suceso! Vu nun sequas:'
    post_follow:
      close: O, vu volas jus klozar ca panelo.
      return: Montrez priflo de uzanti
      web: Irez a interreto
    title: Sequar %{acct}
  challenge:
    confirm: Durez
    hint_html: "<strong>Guidilo:</strong> Ni ne demandos vua pasvorto itere til 1 horo."

M config/locales/is.yml => config/locales/is.yml +0 -12
@@ 1064,18 1064,6 @@ is:
      view_strikes: Skoða fyrri bönn notandaaðgangsins þíns
    too_fast: Innfyllingarform sent inn of hratt, prófaðu aftur.
    use_security_key: Nota öryggislykil
  authorize_follow:
    already_following: Þú ert að þegar fylgjast með þessum aðgangi
    already_requested: Þú ert þegar búin/n að senda fylgjendabeiðni á þennan notanda
    error: Því miður, það kom upp villa við að fletta upp fjartengda notandaaðgangnum
    follow: Fylgjast með
    follow_request: 'Þú sendir beiðni um að fylgjast með til:'
    following: 'Tókst! Þú ert núna að fylgjast með:'
    post_follow:
      close: Eða að þú getur lokað þessum glugga.
      return: Birta notandasnið notandans
      web: Fara á vefinn
    title: Fylgjast með %{acct}
  challenge:
    confirm: Halda áfram
    hint_html: "<strong>Ábending:</strong> Við munum ekki spyrja þig um lykilorðið aftur næstu klukkustundina."

M config/locales/it.yml => config/locales/it.yml +0 -12
@@ 1062,18 1062,6 @@ it:
      view_strikes: Visualizza le sanzioni precedenti prese nei confronti del tuo account
    too_fast: Modulo inviato troppo velocemente, riprova.
    use_security_key: Usa la chiave di sicurezza
  authorize_follow:
    already_following: Stai già seguendo questo account
    already_requested: Hai già mandato una richiesta di seguire questo account
    error: Sfortunatamente c'è stato un errore nel consultare l'account remoto
    follow: Segui
    follow_request: 'Hai mandato una richiesta di seguire:'
    following: 'Accettato! Ora stai seguendo:'
    post_follow:
      close: Oppure puoi chiudere questa finestra.
      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."

M config/locales/ja.yml => config/locales/ja.yml +0 -12
@@ 1042,18 1042,6 @@ ja:
      view_strikes: 過去のストライクを表示
    too_fast: フォームの送信が速すぎます。もう一度やり直してください。
    use_security_key: セキュリティキーを使用
  authorize_follow:
    already_following: あなたは既にこのアカウントをフォローしています
    already_requested: 既にこのアカウントへフォローリクエストを送信しています
    error: 残念ながら、リモートアカウント情報の取得中にエラーが発生しました
    follow: フォロー
    follow_request: 'あなたは以下のアカウントにフォローリクエストを送信しました:'
    following: '成功! あなたは現在以下のアカウントをフォローしています:'
    post_follow:
      close: またはこのウィンドウを閉じます。
      return: ユーザーのプロフィールを見る
      web: Webを開く
    title: "%{acct}さんをフォロー"
  challenge:
    confirm: 続ける
    hint_html: 以後1時間はパスワードの再入力を求めません

M config/locales/ka.yml => config/locales/ka.yml +0 -11
@@ 232,17 232,6 @@ ka:
    reset_password: პაროლის გადატვირთვა
    security: უსაფრთხოება
    set_new_password: ახალი პაროლის დაყენება
  authorize_follow:
    already_following: უკვე მიჰყვებით ამ ანგარიშს
    error: სამწუხაროთ, დისტანციური სერვერის წაკითხვამ გამოიწვია შეცდომა
    follow: გაყევი
    follow_request: 'დადევნების მოთხონვა გაეგზავნა:'
    following: 'წარმატება! ახლა მიჰყვებით:'
    post_follow:
      close: ან შეგიძლიათ დახუროთ ეს ფანჯარა.
      return: მომხმარებლის პროფილის ჩვენება
      web: ვებზე გადასვლა
    title: გაყევი %{acct}-ს
  datetime:
    distance_in_words:
      about_x_hours: "%{count}სთ"

M config/locales/kab.yml => config/locales/kab.yml +0 -8
@@ 461,14 461,6 @@ kab:
    status:
      account_status: Addad n umiḍan
    use_security_key: Seqdec tasarut n teɣlist
  authorize_follow:
    already_following: Teṭafareḍ ya kan amiḍan-a
    follow: Ḍfeṛ
    following: 'Igerrez! Aqlik teṭafareḍ tura:'
    post_follow:
      return: Ssken-d amaɣnu n useqdac
      web: Ddu γer Web
    title: Ḍfeṛ %{acct}
  challenge:
    confirm: Kemmel
    invalid_password: Yir awal uffir

M config/locales/kk.yml => config/locales/kk.yml +0 -11
@@ 360,17 360,6 @@ kk:
      confirming: Электрондық поштаны растау аяқталуын күтуде.
      pending: Сіздің өтінішіңіз біздің қызметкерлеріміздің қарауында. Бұл біраз уақыт алуы мүмкін. Өтінішіңіз мақұлданса, сізге электрондық пошта хабарламасы келеді.
      redirecting_to: Сіздің есептік жазбаңыз белсенді емес, себебі ол %{acct} жүйесіне қайта бағытталуда.
  authorize_follow:
    already_following: Бұл аккаунтқа жазылғансыз
    error: Өкінішке орай, қашықтағы тіркелгіні іздеуде қате пайда болды
    follow: Жазылу
    follow_request: 'Сіз жазылуға өтініш жібердіңіз:'
    following: 'Керемет! Сіз енді жазылдыңыз:'
    post_follow:
      close: Немесе терезені жаба салыңыз.
      return: Қолданушы профилін көрсет
      web: Вебте ашу
    title: Жазылу %{acct}
  challenge:
    confirm: Жалғастыру
    hint_html: "<strong> Кеңес: </strong> біз келесі сағат ішінде сізден құпия сөзді қайта сұрамаймыз."

M config/locales/ko.yml => config/locales/ko.yml +0 -12
@@ 1044,18 1044,6 @@ ko:
      view_strikes: 내 계정에 대한 과거 중재 기록 보기
    too_fast: 너무 빠르게 양식이 제출되었습니다, 다시 시도하세요.
    use_security_key: 보안 키 사용
  authorize_follow:
    already_following: 이미 이 계정을 팔로우 하고 있습니다
    already_requested: 이미 이 계정에게 팔로우 요청을 보냈습니다
    error: 리모트 계정을 확인하는 도중 오류가 발생했습니다
    follow: 팔로우
    follow_request: '팔로우 요청을 보냄:'
    following: '성공! 당신은 다음 계정을 팔로우 하고 있습니다:'
    post_follow:
      close: 혹은, 그저 이 창을 닫을 수도 있습니다.
      return: 사용자 프로필 보기
      web: 웹으로 가기
    title: "%{acct} 를 팔로우"
  challenge:
    confirm: 계속
    hint_html: "<strong>팁:</strong> 한 시간 동안 다시 암호를 묻지 않을 것입니다."

M config/locales/ku.yml => config/locales/ku.yml +0 -12
@@ 979,18 979,6 @@ ku:
      view_strikes: Binpêkirinên berê yên dijî ajimêrê xwe bibîne
    too_fast: Form pir zû hat şandin, dîsa biceribîne.
    use_security_key: Kilîteke ewlehiyê bi kar bîne
  authorize_follow:
    already_following: Jixwe tu vê ajimêrê dişopînî
    already_requested: Jixwe te ji vê ajimêrê re daxwazîya şopandinê şandi bû
    error: Mixabin, dema ajimêr hat gerandin çewtiyek çêbû
    follow: Bişopîne
    follow_request: 'Te ji vê kesê re daxwazîya şopandinê şand:'
    following: 'Serkeftin! Tu êdî dikarî bişopînî:'
    post_follow:
      close: An jî, tu dikarî tenê ev çarçoveyê bigirî.
      return: Profîla vê bikarhênerê nîşan bike
      web: Biçe tevneyê
    title: "%{acct} bişopîne"
  challenge:
    confirm: Bidomîne
    hint_html: "<strong>Nîşe:</strong>Ji bo demjimêreke din em ê borînpeyva te careke din ji te nexwazin."

M config/locales/lt.yml => config/locales/lt.yml +0 -11
@@ 267,17 267,6 @@ lt:
    reset_password: Atstatyti slaptažodį
    security: Apsauga
    set_new_password: Nustatyti naują slaptažodį
  authorize_follow:
    already_following: Jūs jau sekate šią paskyrą
    error: Dėja, aptikta klaida ieškant tolimosios paskyros
    follow: Sekti
    follow_request: 'Jūs išsiuntėte sekimo prašymą:'
    following: 'Puiku! Jūs pradėjote sekti:'
    post_follow:
      close: Arba, Jūs galite uždaryti šį langą.
      return: Rodyti vartotojo paskyrą
      web: Eiti į
    title: Sekti %{acct}
  datetime:
    distance_in_words:
      about_x_hours: "%{count} val"

M config/locales/lv.yml => config/locales/lv.yml +0 -12
@@ 1057,18 1057,6 @@ lv:
      view_strikes: Skati iepriekšējos brīdinājumus par savu kontu
    too_fast: Veidlapa ir iesniegta pārāk ātri, mēģini vēlreiz.
    use_security_key: Lietot drošības atslēgu
  authorize_follow:
    already_following: Tu jau seko šim kontam
    already_requested: Tu jau esi nosūtījis sekošanas pieteikumu šim kontam
    error: Diemžēl, meklējot attālināto kontu, radās kļūda
    follow: Sekot
    follow_request: 'Tu esi nosūtījis sekošanas pieteikumu:'
    following: 'Veiksmīgi! Tu tagad seko:'
    post_follow:
      close: Vai vienkārši aizver šo logu.
      return: Parādīt lietotāja profilu
      web: Doties uz tīmekli
    title: Sekot %{acct}
  challenge:
    confirm: Turpināt
    hint_html: "<strong>Padoms:</strong> Nākamās stundas laikā mēs tev vairs neprasīsim paroli."

M config/locales/ml.yml => config/locales/ml.yml +0 -10
@@ 76,16 76,6 @@ ml:
    invites:
      filter:
        all: എല്ലാം
  authorize_follow:
    following: 'വിജയകരം! നിങ്ങൾ ഇപ്പോൾ പിന്തുടരുന്നു:'
  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.
    '429': Too many requests
    '503': The page could not be served due to a temporary server failure.
  filters:
    contexts:
      notifications: അറിയിപ്പുകൾ

M config/locales/ms.yml => config/locales/ms.yml +0 -8
@@ 767,14 767,6 @@ ms:
      account_status: Status akaun
      view_strikes: Lihat pelanggaran yang lepas terhadap akaun anda
    use_security_key: Gunakan kunci keselamatan
  authorize_follow:
    follow: Ikut
    follow_request: 'Anda telah menghantar permintaan mengikut kepada:'
    post_follow:
      close: Atau anda boleh tutup tetingkap ini.
      return: Tunjukkan profil pengguna
      web: Pergi ke web
    title: Ikuti %{acct}
  challenge:
    confirm: Teruskan
    invalid_password: Kata laluan tidak sah

M config/locales/my.yml => config/locales/my.yml +0 -12
@@ 1042,18 1042,6 @@ my:
      view_strikes: သင့်အကောင့်ကို ဆန့်ကျင်သည့် ယခင်ကလုပ်ဆောင်ချက်များကို ကြည့်ပါ
    too_fast: ဖောင်တင်သည်မှာ မြန်နေပါသည်။ ထပ်စမ်းကြည့်ပါ။
    use_security_key: လုံခြုံရေးကီးကို သုံးပါ
  authorize_follow:
    already_following: သင်သည် ဤအကောင့်ကို စောင့်ကြည့်နေပြီဖြစ်ပါသည်
    already_requested: သင်သည် ထိုအကောင့်စောင့်ကြည့်ရန် တောင်းဆိုမှုတစ်ခု ပေးပို့ခဲ့ပြီးပါပြီ
    error: ကံမကောင်းစွာဖြင့် အဝေးမှထိန်းချုပ်သောအကောင့်ရှာဖွေရာတွင် အမှားအယွင်းတစ်ခုရှိခဲ့သည်
    follow: စောင့်ကြည့်မယ်
    follow_request: သင်သည် စောင့်ကြည့်မည် တောင်းဆိုချက်တစ်ခု ပေးပို့ထားသည်-
    following: သင် ယခု အောက်ပါအတိုင်း လုပ်ဆောင်နေပါသည် -
    post_follow:
      close: သို့မဟုတ် သင်သည် ဤဝင်းဒိုးကို ပိတ်နိုင်သည်
      return: အသုံးပြုသူ၏ ပရိုဖိုင်ကိုပြရန်
      web: ဝဘ်သို့ သွားပါ
    title: "%{acct} ကို စောင့်ကြည့်မယ်"
  challenge:
    confirm: ဆက်လုပ်မည်
    hint_html: "<strong>အကြံပြုချက် -</strong> နောက်နာရီများတွင် သင့်စကားဝှက်ကို ထပ်မံတောင်းဆိုမည်မဟုတ်ပါ။"

M config/locales/nl.yml => config/locales/nl.yml +0 -12
@@ 1060,18 1060,6 @@ nl:
      view_strikes: Bekijk de eerder door moderatoren vastgestelde overtredingen die je hebt gemaakt
    too_fast: Formulier is te snel ingediend. Probeer het nogmaals.
    use_security_key: Beveiligingssleutel gebruiken
  authorize_follow:
    already_following: Je volgt dit account al
    already_requested: Je hebt al een volgverzoek naar dat account verstuurd
    error: Helaas, er is een fout opgetreden bij het opzoeken van de externe account
    follow: Volgen
    follow_request: 'Jij hebt een volgverzoek ingediend bij:'
    following: 'Succes! Jij volgt nu:'
    post_follow:
      close: Of je kunt dit venster gewoon sluiten.
      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."

M config/locales/nn.yml => config/locales/nn.yml +0 -12
@@ 1049,18 1049,6 @@ nn:
      view_strikes: Vis tidligere advarsler mot kontoen din
    too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt.
    use_security_key: Bruk sikkerhetsnøkkel
  authorize_follow:
    already_following: Du fylgjer allereie denne kontoen
    already_requested: Du har allereie sendt ein fylgjespurnad til den kontoen
    error: Uheldigvis skjedde det en feil da vi prøvde å få tak i en bruker fra en annen instans
    follow: Fylg
    follow_request: 'Du har sendt ein fylgjeførespurnad til:'
    following: 'Suksess! No fylgjer du:'
    post_follow:
      close: Eller så kan du berre lukka att dette vindauget.
      return: Vis brukarprofilen
      web: Gå til nettet
    title: Fylg %{acct}
  challenge:
    confirm: Hald fram
    hint_html: "<strong>Tips:</strong> Vi skal ikkje spørja deg om passordet ditt igjen i laupet av den neste timen."

M config/locales/no.yml => config/locales/no.yml +0 -12
@@ 988,18 988,6 @@
      view_strikes: Vis tidligere advarsler mot kontoen din
    too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt.
    use_security_key: Bruk sikkerhetsnøkkel
  authorize_follow:
    already_following: Du følger allerede denne kontoen
    already_requested: Du har allerede sendt en følgeforespørsel til denne kontoen
    error: Dessverre oppstod det en feil da vi prøvde å få tak i brukeren fra tjeneren
    follow: Følg
    follow_request: 'Du har sendt en følgeforespørsel til:'
    following: 'Suksess! Nå følger du:'
    post_follow:
      close: Eller så kan du lukke dette vinduet.
      return: Gå tilbake til brukerens profil
      web: Gå til nettsiden
    title: Følg %{acct}
  challenge:
    confirm: Fortsett
    hint_html: "<strong>Tips:</strong> Vi ber deg ikke om passordet ditt igjen i løpet av neste time."

M config/locales/oc.yml => config/locales/oc.yml +0 -11
@@ 502,17 502,6 @@ oc:
      account_status: Estat del compte
      functional: Vòstre compte es complètament foncional.
    use_security_key: Utilizar clau de seguretat
  authorize_follow:
    already_following: Seguètz ja aqueste compte
    error: O planhèm, i a agut una error al moment de cercar lo compte
    follow: Sègre
    follow_request: 'Avètz demandat de sègre :'
    following: 'Felicitacion ! Seguètz ara :'
    post_follow:
      close: O podètz tampar aquesta fenèstra.
      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."

M config/locales/pl.yml => config/locales/pl.yml +0 -12
@@ 1096,18 1096,6 @@ pl:
      view_strikes: Zobacz dawne ostrzeżenia nałożone na twoje konto
    too_fast: Zbyt szybko przesłano formularz, spróbuj ponownie.
    use_security_key: Użyj klucza bezpieczeństwa
  authorize_follow:
    already_following: Już obserwujesz to konto
    already_requested: Już wysłałeś(-aś) prośbę o możliwość obserwowania tego konta
    error: Niestety, podczas sprawdzania zdalnego konta wystąpił błąd
    follow: Obserwuj
    follow_request: 'Wysłano prośbę o możliwość obserwowania:'
    following: 'Pomyślnie! Od teraz obserwujesz:'
    post_follow:
      close: Ewentualnie, możesz po prostu zamknąć tę stronę.
      return: Pokaż stronę użytkownika
      web: Przejdź do sieci
    title: Obserwuj %{acct}
  challenge:
    confirm: Kontynuuj
    hint_html: "<strong>Informacja:</strong> Nie będziemy prosić Cię o ponowne podanie hasła przez następną godzinę."

M config/locales/pt-BR.yml => config/locales/pt-BR.yml +0 -12
@@ 1059,18 1059,6 @@ pt-BR:
      view_strikes: Veja os avisos anteriores em relação à sua conta
    too_fast: O formulário foi enviado muito rapidamente, tente novamente.
    use_security_key: Usar chave de segurança
  authorize_follow:
    already_following: Você já segue
    already_requested: Você já enviou uma solicitação para seguir esta conta
    error: Infelizmente, ocorreu um erro ao buscar a conta remota
    follow: Seguir
    follow_request: 'Você mandou solicitação para seguir para:'
    following: 'Sucesso! Agora você está seguindo:'
    post_follow:
      close: Ou você pode simplesmente fechar esta janela.
      return: Mostrar o perfil do usuário
      web: Voltar à página inicial
    title: Seguir %{acct}
  challenge:
    confirm: Continuar
    hint_html: "<strong>Dica:</strong> Não pediremos novamente sua senha pela próxima hora."

M config/locales/pt-PT.yml => config/locales/pt-PT.yml +0 -12
@@ 1060,18 1060,6 @@ pt-PT:
      view_strikes: Veja as reprimendas anteriores sobre a sua conta
    too_fast: Formulário enviado demasiado rapidamente, tente novamente.
    use_security_key: Usar chave de segurança
  authorize_follow:
    already_following: Tu já estás a seguir esta conta
    already_requested: Já enviou anteriormente um pedido para seguir esta conta
    error: Infelizmente, ocorreu um erro ao buscar a conta remota
    follow: Seguir
    follow_request: 'Enviaste uma solicitação de seguidor para:'
    following: 'Sucesso! Agora estás a seguir a:'
    post_follow:
      close: Ou podes simplesmente fechar esta janela.
      return: Mostrar perfil do utilizador
      web: Ir para a página na teia
    title: Seguir %{acct}
  challenge:
    confirm: Continuar
    hint_html: "<strong>Dica:</strong> Não vamos pedir novamente a sua palavra-passe durante a próxima hora."

M config/locales/ro.yml => config/locales/ro.yml +0 -12
@@ 445,18 445,6 @@ ro:
      confirming: Se așteaptă finalizarea confirmării prin e-mail.
      pending: Cererea dvs. este în curs de revizuire de către personalul nostru. Este posibil să dureze ceva timp. Veți primi un e-mail dacă cererea dvs. este aprobată.
      redirecting_to: Contul dvs. este inactiv deoarece în prezent se redirecționează către %{acct}.
  authorize_follow:
    already_following: Urmărești deja acest cont
    already_requested: Ați trimis deja o cerere de urmărire către acel cont
    error: Din păcate a apărut o eroare
    follow: Urmărește
    follow_request: 'Ai trimis o cerere de urmărire către:'
    following: 'Gata! De acum urmărești:'
    post_follow:
      close: Sau, poți închide această fereastră.
      return: Arată profilul utilizatorului
      web: Mergi la web
    title: Urmărește %{acct}
  challenge:
    confirm: Continuă
    hint_html: "<strong>Sfat:</strong> Nu vă vom mai cere parola pentru următoarea oră."

M config/locales/ru.yml => config/locales/ru.yml +0 -12
@@ 1094,18 1094,6 @@ ru:
      view_strikes: Просмотр предыдущих замечаний в адрес вашей учетной записи
    too_fast: Форма отправлена слишком быстро, попробуйте еще раз.
    use_security_key: Использовать ключ безопасности
  authorize_follow:
    already_following: Вы уже подписаны на эту учётную запись
    already_requested: Вы уже отправили запрос на подписку на эту учётную запись
    error: К сожалению, при поиске удалённой учётной записи возникла ошибка
    follow: Подписаться
    follow_request: 'Вы отправили запрос на подписку:'
    following: 'Готово! Вы подписались на:'
    post_follow:
      close: Или просто закройте это окно.
      return: Вернуться к профилю пользователя
      web: Открыть в веб-версии
    title: Подписаться на %{acct}
  challenge:
    confirm: Продолжить
    hint_html: "<strong>Подсказка</strong>: мы не будем спрашивать пароль повторно в течение часа."

M config/locales/sc.yml => config/locales/sc.yml +0 -12
@@ 537,18 537,6 @@ sc:
      redirecting_to: Su contu tuo est inativu pro ite in die de oe est torrende a indiritzare a %{acct}.
    too_fast: Formulàriu imbiadu tropu a lestru, torra a proare.
    use_security_key: Imprea una crae de seguresa
  authorize_follow:
    already_following: Ses giai sighende custu contu
    already_requested: As giai imbiadu una dimanda de sighidura a custa persone
    error: Faddina in sa chirca de su contu remotu
    follow: Sighi
    follow_request: 'As imbiadu una dimanda de sighidura a:'
    following: 'Fatu! Immoe ses sighende a:'
    post_follow:
      close: O, podes serrare custa ventana.
      return: Ammustra su profilu de custa persone
      web: Bae a su situ web
    title: Sighi a %{acct}
  challenge:
    confirm: Sighi
    hint_html: "<strong>Cussìgiu:</strong> No t'amus a torrare a dimandare sa crae in s'ora imbeniente."

M config/locales/sco.yml => config/locales/sco.yml +0 -12
@@ 969,18 969,6 @@ sco:
      view_strikes: Luik at past strikes aginst yer accoont
    too_fast: Form submittit ower fast, try again.
    use_security_key: Uise security key
  authorize_follow:
    already_following: Ye'r awriddy follaein this accoont
    already_requested: Ye'v awriddy sent a follae request tae that accoont
    error: Unfortunately, there wis a error luikin up the remote accoont
    follow: Follae
    follow_request: 'Ye hae sent a follae request tae:'
    following: 'Success! Ye''r noo follaein:'
    post_follow:
      close: Or, ye kin juist shut this windae.
      return: Shaw the uiser's profile
      web: Gang tae the wab
    title: Follae %{acct}
  challenge:
    confirm: Continue
    hint_html: "<strong>wee tip:</strong> We wullnae ask ye fir yer passwird again fir the neist oor."

M config/locales/si.yml => config/locales/si.yml +0 -12
@@ 803,18 803,6 @@ si:
      view_strikes: ඔබගේ ගිණුමට එරෙහිව පසුගිය වර්ජන බලන්න
    too_fast: පෝරමය ඉතා වේගයෙන් ඉදිරිපත් කර ඇත, නැවත උත්සාහ කරන්න.
    use_security_key: ආරක්ෂක යතුර භාවිතා කරන්න
  authorize_follow:
    already_following: ඔබ දැනටමත් මෙම ගිණුම අනුගමනය කරයි
    already_requested: ඔබ දැනටමත් එම ගිණුමට අනුගමනය ඉල්ලීමක් යවා ඇත
    error: අවාසනාවකට, දුරස්ථ ගිණුම සෙවීමේදී දෝෂයක් ඇති විය
    follow: අනුගමනය
    follow_request: 'ඔබ පහත ඉල්ලීමක් යවා ඇත:'
    following: 'සාර්ථකත්වය! ඔබ දැන් පහත දැක්වේ:'
    post_follow:
      close: හෝ ඔබට මෙම කවුළුව වසාදැමිය හැකිය.
      return: පරිශීලකගේ පැතිකඩ පෙන්වන්න
      web: වියමන ට යන්න
    title: "%{acct} අනුගමනය"
  challenge:
    confirm: ඉදිරියට
    hint_html: "<strong>ඉඟිය:</strong> අපි ඉදිරි පැය සඳහා නැවත ඔබගේ මුරපදය ඔබෙන් නොඉල්ලමු."

M config/locales/sk.yml => config/locales/sk.yml +0 -11
@@ 737,17 737,6 @@ sk:
      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}.
    use_security_key: Použi bezpečnostný kľúč
  authorize_follow:
    already_following: Tento účet už nasleduješ
    error: Naneštastie nastala chyba pri hľadaní vzdialeného účtu
    follow: Nasleduj
    follow_request: 'Poslal/a si žiadosť nasledovať užívateľa:'
    following: 'Podarilo sa! Teraz nasleduješ užívateľa:'
    post_follow:
      close: Alebo môžeš iba zatvoriť toto okno.
      return: Ukáž užívateľov profil
      web: Prejdi do siete
    title: Nasleduj %{acct}
  challenge:
    confirm: Pokračuj
    hint_html: "<strong>Tip:</strong> Hodinu nebudeme znovu vyžadovať tvoje heslo."

M config/locales/sl.yml => config/locales/sl.yml +0 -12
@@ 1084,18 1084,6 @@ sl:
      view_strikes: Pokaži pretekle ukrepe proti mojemu računu
    too_fast: Obrazec oddan prehitro, poskusite znova.
    use_security_key: Uporabi varnostni ključ
  authorize_follow:
    already_following: Temu računu že sledite
    already_requested: Temu računu ste že poslali zahtevo po sledenju
    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}
  challenge:
    confirm: Nadaljuj
    hint_html: "<strong>Namig:</strong> naslednjo uro vas ne bomo več vprašali po vašem geslu."

M config/locales/sq.yml => config/locales/sq.yml +0 -12
@@ 1052,18 1052,6 @@ sq:
      view_strikes: Shihni paralajmërime të dikurshme kundër llogarisë tuaj
    too_fast: Formulari u parashtrua shumë shpejt, riprovoni.
    use_security_key: Përdor kyç sigurie
  authorize_follow:
    already_following: E ndiqni tashmë këtë llogari
    already_requested: Keni dërguar tashmë një kërkesë ndjekjeje te ajo llogari
    error: Mjerisht, pati një gabim gjatë kërkimit të llogarisë së largët
    follow: Ndiqeni
    follow_request: 'Keni dërguar një kërkesë ndjekjeje te:'
    following: 'Sukses! Tani e ndiqni:'
    post_follow:
      close: Ose, thjesht mund të mbyllni këtë dritare.
      return: Shfaq profilin e përdoruesit
      web: Kalo në web
    title: Ndiq %{acct}
  challenge:
    confirm: Vazhdo
    hint_html: "<strong>Ndihmëz:</strong> S’do t’ju pyesim për fjalëkalimin tuaj sërish, për një orë."

M config/locales/sr-Latn.yml => config/locales/sr-Latn.yml +0 -12
@@ 1078,18 1078,6 @@ sr-Latn:
      view_strikes: Pogledajte prethodne prestupe upisane na Vaše ime
    too_fast: Formular je podnet prebrzo, pokušajte ponovo.
    use_security_key: Koristite sigurnosni ključ
  authorize_follow:
    already_following: Već pratite ovaj nalog
    already_requested: Već ste poslali zahtev za praćenje tom nalogu
    error: Nažalost, desila se greška pri traženju udaljenog naloga
    follow: Zaprati
    follow_request: 'Poslali ste zahtev za praćenjen za:'
    following: 'Sjajno! Sada pratite:'
    post_follow:
      close: Ili možete zatvoriti ovaj prozor.
      return: Vrati se na nalog ovog korisnika
      web: Idi na veb
    title: Zaprati %{acct}
  challenge:
    confirm: Nastavi
    hint_html: "<strong>Savet:</strong> Nećemo Vas pitati za lozinku ponovo u narednih sat vremena."

M config/locales/sr.yml => config/locales/sr.yml +0 -12
@@ 1078,18 1078,6 @@ sr:
      view_strikes: Погледајте претходне преступе уписане на Ваше име
    too_fast: Формулар је поднет пребрзо, покушајте поново.
    use_security_key: Користите сигурносни кључ
  authorize_follow:
    already_following: Већ пратите овај налог
    already_requested: Већ сте послали захтев за праћење том налогу
    error: Нажалост, десила се грешка при тражењу удаљеног налога
    follow: Запрати
    follow_request: 'Послали сте захтев за праћењен за:'
    following: 'Сјајно! Сада пратите:'
    post_follow:
      close: Или можете затворити овај прозор.
      return: Врати се на налог овог корисника
      web: Иди на веб
    title: Запрати %{acct}
  challenge:
    confirm: Настави
    hint_html: "<strong>Савет:</strong> Нећемо Вас питати за лозинку поново у наредних сат времена."

M config/locales/sv.yml => config/locales/sv.yml +0 -12
@@ 1042,18 1042,6 @@ sv:
      view_strikes: Visa tidigare prickar på ditt konto
    too_fast: Formuläret har skickats för snabbt, försök igen.
    use_security_key: Använd säkerhetsnyckel
  authorize_follow:
    already_following: Du följer redan detta konto
    already_requested: Du har redan skickat en vänförfrågan till det kontot
    error: Tyvärr inträffade ett fel när vi kontrollerade fjärrkontot
    follow: Följ
    follow_request: 'Du har skickat en följaförfrågan till:'
    following: 'Succé! Du följer nu:'
    post_follow:
      close: Eller så kan du stänga detta fönster.
      return: Visa användarens profil
      web: Gå till webb
    title: Följ %{acct}
  challenge:
    confirm: Fortsätt
    hint_html: "<strong>Tips:</strong> Vi frågar dig inte efter ditt lösenord igen under nästkommande timme."

M config/locales/ta.yml => config/locales/ta.yml +0 -2
@@ 191,8 191,6 @@ ta:
    localization:
      body: மாஸ்டோடான் தன்னார்வலர்களால் மொழிபெயர்க்கப்படுகிறது.
      guide_link_text: அனைவரும் பங்களிக்கலாம்.
  authorize_follow:
    already_requested: இக்கணக்கைப் பின்தொடரும் கோரிக்கையை நீங்கள் ஏற்கனவே அனுப்பிவிட்டீர்கள்
  crypto:
    errors:
      invalid_key: ஒரு முறையான Ed25519 அல்லது Curve25519 key அல்ல

M config/locales/th.yml => config/locales/th.yml +0 -12
@@ 1042,18 1042,6 @@ th:
      view_strikes: ดูการดำเนินการที่ผ่านมาต่อบัญชีของคุณ
    too_fast: ส่งแบบฟอร์มเร็วเกินไป ลองอีกครั้ง
    use_security_key: ใช้กุญแจความปลอดภัย
  authorize_follow:
    already_following: คุณกำลังติดตามบัญชีนี้อยู่แล้ว
    already_requested: คุณได้ส่งคำขอติดตามไปยังบัญชีนั้นไปแล้ว
    error: น่าเสียดาย มีข้อผิดพลาดในการมองหาบัญชีระยะไกล
    follow: ติดตาม
    follow_request: 'คุณได้ส่งคำขอติดตามไปยัง:'
    following: 'สำเร็จ! ตอนนี้คุณกำลังติดตาม:'
    post_follow:
      close: หรือคุณสามารถปิดหน้าต่างนี้
      return: แสดงโปรไฟล์ของผู้ใช้
      web: ไปยังเว็บ
    title: ติดตาม %{acct}
  challenge:
    confirm: ดำเนินการต่อ
    hint_html: "<strong>เคล็ดลับ:</strong> เราจะไม่ถามรหัสผ่านของคุณกับคุณสำหรับชั่วโมงถัดไป"

M config/locales/tr.yml => config/locales/tr.yml +0 -12
@@ 1060,18 1060,6 @@ tr:
      view_strikes: Hesabınıza yönelik eski eylemleri görüntüleyin
    too_fast: Form çok hızlı gönderildi, tekrar deneyin.
    use_security_key: Güvenlik anahtarını kullan
  authorize_follow:
    already_following: Bu hesabı zaten takip ediyorsunuz
    already_requested: Bu hesaba zaten takip isteği gönderdiniz
    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} takip et"
  challenge:
    confirm: Devam et
    hint_html: "<strong>İpucu:</strong> Önümüzdeki saat boyunca sana parolanı sormayacağız."

M config/locales/tt.yml => config/locales/tt.yml +0 -6
@@ 337,12 337,6 @@ tt:
      accept: Кабул итү
      back: Кире
    security: Хәвефсезлек
    status:
      account_status: Хисап халәте
  authorize_follow:
    follow: Язылу
    post_follow:
      web: Вебга күчү
  challenge:
    confirm: Дәвам итү
  date:

M config/locales/uk.yml => config/locales/uk.yml +0 -12
@@ 1096,18 1096,6 @@ uk:
      view_strikes: Переглянути попередні попередження вашому обліковому запису
    too_fast: Форму подано занадто швидко, спробуйте ще раз.
    use_security_key: Використовувати ключ безпеки
  authorize_follow:
    already_following: Ви вже слідкуєте за цим обліковим записом
    already_requested: Ви вже надіслали запит на підписку до цього облікового запису
    error: На жаль, під час пошуку віддаленого облікового запису сталася помилка
    follow: Підписатися
    follow_request: 'Вам надіслали запит на підписку:'
    following: 'Ура! Ви тепер підписані на:'
    post_follow:
      close: Або, ви можете просто закрити вікно.
      return: Перейти до профілю користувача
      web: Перейти до вебу
    title: Підписатися на %{acct}
  challenge:
    confirm: Далі
    hint_html: "<strong>Підказка:</strong> ми не будемо запитувати ваш пароль впродовж наступної години."

M config/locales/vi.yml => config/locales/vi.yml +0 -12
@@ 1042,18 1042,6 @@ vi:
      view_strikes: Xem những lần cảnh cáo cũ
    too_fast: Nghi vấn đăng ký spam, xin thử lại.
    use_security_key: Dùng khóa bảo mật
  authorize_follow:
    already_following: Bạn đang theo dõi người này
    already_requested: Bạn vừa gửi một yêu cầu theo dõi tới người này
    error: Rất tiếc, đã xảy ra lỗi khi tìm kiếm tài khoản từ nơi khác
    follow: Theo dõi
    follow_request: Bạn đã gửi yêu cầu theo dõi tới
    following: Chúc mừng! Bạn đã trở thành người theo dõi
    post_follow:
      close: Bạn có thể đóng cửa sổ này rồi.
      return: Xem trang hồ sơ
      web: Mở trong Mastodon
    title: Theo dõi %{acct}
  challenge:
    confirm: Tiếp tục
    hint_html: "<strong>Mẹo:</strong> Chúng tôi sẽ không hỏi lại mật khẩu của bạn sau này."

M config/locales/zgh.yml => config/locales/zgh.yml +0 -3
@@ 77,9 77,6 @@ zgh:
    register: ⵣⵎⵎⴻⵎ
    status:
      account_status: ⴰⴷⴷⴰⴷ ⵏ ⵓⵎⵉⴹⴰⵏ
  authorize_follow:
    follow: ⴹⴼⵕ
    title: ⴹⴼⵕ %{acct}
  deletes:
    proceed: ⴽⴽⵙ ⴰⵎⵉⴹⴰⵏ
  errors:

M config/locales/zh-CN.yml => config/locales/zh-CN.yml +0 -12
@@ 1042,18 1042,6 @@ zh-CN:
      view_strikes: 查看针对你账号的记录
    too_fast: 表单提交过快,请重试。
    use_security_key: 使用安全密钥
  authorize_follow:
    already_following: 你已经在关注此用户了
    already_requested: 你已经向那个账户发送过关注请求了
    error: 对不起,寻找这个跨站用户时出错
    follow: 关注
    follow_request: 关注请求已发送给:
    following: 成功!你正在关注:
    post_follow:
      close: 你也可以直接关闭这个窗口。
      return: 查看用户个人资料
      web: 返回本站
    title: 关注 %{acct}
  challenge:
    confirm: 继续
    hint_html: "<strong>注意:</strong>接下来一小时内我们不会再次要求你输入密码。"

M config/locales/zh-HK.yml => config/locales/zh-HK.yml +0 -12
@@ 1005,18 1005,6 @@ zh-HK:
      view_strikes: 查看針對你的帳戶的過往警告
    too_fast: 你太快遞交了,請再試一次。
    use_security_key: 使用安全密鑰裝置
  authorize_follow:
    already_following: 你已經關注了這個帳號
    already_requested: 你先前已向該帳號發送關注請求
    error: 對不起,尋找這個跨站用戶的過程發生錯誤
    follow: 關注
    follow_request: 關注請求已發送给:
    following: 成功!你正在關注:
    post_follow:
      close: 你也可以直接關閉這個頁面。
      return: 顯示個人資料頁
      web: 返回本站
    title: 關注 %{acct}
  challenge:
    confirm: 繼續
    hint_html: "<strong>温馨提示</strong> 我們在未來一小時內不會再要求你填寫密碼。"

M config/locales/zh-TW.yml => config/locales/zh-TW.yml +0 -12
@@ 1046,18 1046,6 @@ zh-TW:
      view_strikes: 檢視針對您帳號過去的警示
    too_fast: 送出表單的速度太快跟不上,請稍後再試。
    use_security_key: 使用安全金鑰
  authorize_follow:
    already_following: 您已經跟隨這個使用者
    already_requested: 您早已向該帳號寄送跟隨請求
    error: 對不起,搜尋其他站點使用者出現錯誤
    follow: 跟隨
    follow_request: 跟隨請求已發送給:
    following: 成功!您正在跟隨:
    post_follow:
      close: 您可以直接關閉此頁面。
      return: 顯示個人檔案
      web: 返回本站
    title: 跟隨 %{acct}
  challenge:
    confirm: 繼續
    hint_html: "<strong>温馨小提醒:</strong> 我們在接下來一小時內不會再要求您輸入密碼。"

M config/routes.rb => config/routes.rb +4 -1
@@ 54,6 54,7 @@ Rails.application.routes.draw do
  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/proxy', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }

  get '/nodeinfo/2.0', to: 'well_known/nodeinfo#show', as: :nodeinfo_schema



@@ 61,6 62,8 @@ Rails.application.routes.draw do
  get 'intent', to: 'intents#show'
  get 'custom.css', to: 'custom_css#show', as: :custom_css

  get 'remote_interaction_helper', to: 'remote_interaction_helper#index'

  resource :instance_actor, path: 'actor', only: [:show] do
    resource :inbox, only: [:create], module: :activitypub
    resource :outbox, only: [:show], module: :activitypub


@@ 163,7 166,7 @@ Rails.application.routes.draw do
  get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false
  get '/backups/:id/download', to: 'backups#download', as: :download_backup, format: false

  resource :authorize_interaction, only: [:show, :create]
  resource :authorize_interaction, only: [:show]
  resource :share, only: [:show]

  draw(:admin)

M config/routes/api.rb => config/routes/api.rb +4 -0
@@ 123,6 123,10 @@ namespace :api, format: false do
      resource :activity, only: [:show], controller: 'instances/activity'
    end

    namespace :peers do
      get :search, to: 'search#index'
    end

    resource :domain_blocks, only: [:show, :create, :destroy]

    resource :directory, only: [:show]

M lib/mastodon/cli/search.rb => lib/mastodon/cli/search.rb +1 -0
@@ 7,6 7,7 @@ module Mastodon::CLI
    # Indices are sorted by amount of data to be expected in each, so that
    # smaller indices can go online sooner
    INDICES = [
      InstancesIndex,
      AccountsIndex,
      TagsIndex,
      StatusesIndex,

M spec/controllers/authorize_interactions_controller_spec.rb => spec/controllers/authorize_interactions_controller_spec.rb +4 -47
@@ 24,7 24,7 @@ describe AuthorizeInteractionsController do
      it 'renders error without acct param' do
        get :show

        expect(response).to render_template(:error)
        expect(response).to have_http_status(404)
      end

      it 'renders error when account cant be found' do


@@ 34,7 34,7 @@ describe AuthorizeInteractionsController do

        get :show, params: { acct: 'acct:missing@hostname' }

        expect(response).to render_template(:error)
        expect(response).to have_http_status(404)
        expect(service).to have_received(:call).with('missing@hostname')
      end



@@ 46,7 46,7 @@ describe AuthorizeInteractionsController do

        get :show, params: { acct: 'http://example.com' }

        expect(response).to have_http_status(200)
        expect(response).to have_http_status(302)
        expect(assigns(:resource)).to eq account
      end



@@ 58,52 58,9 @@ describe AuthorizeInteractionsController do

        get :show, params: { acct: 'acct:found@hostname' }

        expect(response).to have_http_status(200)
        expect(response).to have_http_status(302)
        expect(assigns(:resource)).to eq account
      end
    end
  end

  describe 'POST #create' do
    describe 'when signed out' do
      it 'redirects to sign in page' do
        post :create

        expect(response).to redirect_to(new_user_session_path)
      end
    end

    describe 'when signed in' do
      let!(:user) { Fabricate(:user) }
      let(:account) { user.account }

      before do
        sign_in(user)
      end

      it 'shows error when account not found' do
        service = instance_double(ResolveAccountService)

        allow(ResolveAccountService).to receive(:new).and_return(service)
        allow(service).to receive(:call).with('user@hostname').and_return(nil)

        post :create, params: { acct: 'acct:user@hostname' }

        expect(response).to render_template(:error)
      end

      it 'follows account when found' do
        target_account = Fabricate(:account)
        service = instance_double(ResolveAccountService)

        allow(ResolveAccountService).to receive(:new).and_return(service)
        allow(service).to receive(:call).with('user@hostname').and_return(target_account)

        post :create, params: { acct: 'acct:user@hostname' }

        expect(account.following?(target_account)).to be true
        expect(response).to render_template(:success)
      end
    end
  end
end