Change interaction modal in web UI (#26075)

Co-authored-by: Eugen Rochko <eugen@zeonfederated.com>
local
Claire 10 months ago committed by GitHub
parent 1e4ccc655a
commit b4e739ff0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      app/chewy/instances_index.rb
  2. 2
      app/controllers/api/v1/instances/peers_controller.rb
  3. 45
      app/controllers/api/v1/peers/search_controller.rb
  4. 21
      app/controllers/authorize_interactions_controller.rb
  5. 43
      app/controllers/remote_interaction_helper_controller.rb
  6. 1
      app/controllers/well_known/webfinger_controller.rb
  7. 2
      app/javascript/mastodon/containers/status_container.jsx
  8. 2
      app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
  9. 4
      app/javascript/mastodon/features/compose/components/search.jsx
  10. 298
      app/javascript/mastodon/features/interaction_modal/index.jsx
  11. 6
      app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
  12. 6
      app/javascript/mastodon/features/status/index.jsx
  13. 8
      app/javascript/mastodon/locales/en.json
  14. 172
      app/javascript/packs/remote_interaction_helper.ts
  15. 111
      app/javascript/styles/mastodon/components.scss
  16. 2
      app/javascript/styles/mastodon/variables.scss
  17. 1
      app/javascript/types/resources.ts
  18. 26
      app/lib/importer/instances_index_importer.rb
  19. 9
      app/lib/webfinger_resource.rb
  20. 1
      app/models/instance.rb
  21. 6
      app/serializers/rest/account_serializer.rb
  22. 1
      app/serializers/webfinger_serializer.rb
  23. 4
      app/views/authorize_interactions/_post_follow_actions.html.haml
  24. 3
      app/views/authorize_interactions/error.html.haml
  25. 24
      app/views/authorize_interactions/show.html.haml
  26. 13
      app/views/authorize_interactions/success.html.haml
  27. 8
      app/views/layouts/helper_frame.html.haml
  28. 4
      app/views/remote_interaction_helper/index.html.haml
  29. 1
      app/workers/scheduler/instance_refresh_scheduler.rb
  30. 12
      config/locales/an.yml
  31. 12
      config/locales/ar.yml
  32. 9
      config/locales/ast.yml
  33. 12
      config/locales/be.yml
  34. 12
      config/locales/bg.yml
  35. 5
      config/locales/br.yml
  36. 12
      config/locales/ca.yml
  37. 12
      config/locales/ckb.yml
  38. 12
      config/locales/co.yml
  39. 12
      config/locales/cs.yml
  40. 12
      config/locales/cy.yml
  41. 12
      config/locales/da.yml
  42. 12
      config/locales/de.yml
  43. 12
      config/locales/el.yml
  44. 12
      config/locales/en-GB.yml
  45. 12
      config/locales/en.yml
  46. 12
      config/locales/eo.yml
  47. 12
      config/locales/es-AR.yml
  48. 12
      config/locales/es-MX.yml
  49. 12
      config/locales/es.yml
  50. 12
      config/locales/et.yml
  51. 12
      config/locales/eu.yml
  52. 12
      config/locales/fa.yml
  53. 12
      config/locales/fi.yml
  54. 12
      config/locales/fo.yml
  55. 12
      config/locales/fr-QC.yml
  56. 12
      config/locales/fr.yml
  57. 12
      config/locales/fy.yml
  58. 5
      config/locales/ga.yml
  59. 12
      config/locales/gd.yml
  60. 12
      config/locales/gl.yml
  61. 12
      config/locales/he.yml
  62. 4
      config/locales/hr.yml
  63. 12
      config/locales/hu.yml
  64. 11
      config/locales/hy.yml
  65. 12
      config/locales/id.yml
  66. 12
      config/locales/io.yml
  67. 12
      config/locales/is.yml
  68. 12
      config/locales/it.yml
  69. 12
      config/locales/ja.yml
  70. 11
      config/locales/ka.yml
  71. 8
      config/locales/kab.yml
  72. 11
      config/locales/kk.yml
  73. 12
      config/locales/ko.yml
  74. 12
      config/locales/ku.yml
  75. 11
      config/locales/lt.yml
  76. 12
      config/locales/lv.yml
  77. 10
      config/locales/ml.yml
  78. 8
      config/locales/ms.yml
  79. 12
      config/locales/my.yml
  80. 12
      config/locales/nl.yml
  81. 12
      config/locales/nn.yml
  82. 12
      config/locales/no.yml
  83. 11
      config/locales/oc.yml
  84. 12
      config/locales/pl.yml
  85. 12
      config/locales/pt-BR.yml
  86. 12
      config/locales/pt-PT.yml
  87. 12
      config/locales/ro.yml
  88. 12
      config/locales/ru.yml
  89. 12
      config/locales/sc.yml
  90. 12
      config/locales/sco.yml
  91. 12
      config/locales/si.yml
  92. 11
      config/locales/sk.yml
  93. 12
      config/locales/sl.yml
  94. 12
      config/locales/sq.yml
  95. 12
      config/locales/sr-Latn.yml
  96. 12
      config/locales/sr.yml
  97. 12
      config/locales/sv.yml
  98. 2
      config/locales/ta.yml
  99. 12
      config/locales/th.yml
  100. 12
      config/locales/tr.yml
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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

@ -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

@ -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

@ -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

@ -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

@ -19,6 +19,7 @@ module WellKnown
def set_account
username = username_from_resource
@account = begin
if username == Rails.configuration.x.local_domain
Account.representative

@ -278,7 +278,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
modalProps: {
type,
accountId: status.getIn(['account', 'id']),
url: status.get('url'),
url: status.get('uri'),
},
}));
},

@ -83,7 +83,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
modalProps: {
type: 'follow',
accountId: account.get('id'),
url: account.get('url'),
url: account.get('uri'),
},
}));
},

@ -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;

@ -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>
);
}

@ -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'),
},
}));
}

@ -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'),
},
}));
}

@ -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",

@ -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);
}
});

@ -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 {

@ -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%)};
}

@ -33,6 +33,7 @@ interface AccountApiResponseValues {
note: string;
statuses_count: number;
url: string;
uri: string;
username: string;
}

@ -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

@ -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]

@ -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}") }

@ -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

@ -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
[

@ -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')

@ -1,3 +0,0 @@
.form-container
.flash-message#error_explanation
= t('authorize_follow.error')

@ -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

@ -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'

@ -0,0 +1,8 @@
!!! 5
%html
%head
%meta{ charset: 'utf-8' }/
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= yield :header_tags

@ -0,0 +1,4 @@
- content_for :header_tags do
%meta{ name: 'robots', content: 'noindex' }/
= javascript_pack_tag 'remote_interaction_helper', crossorigin: 'anonymous'

@ -7,5 +7,6 @@ class Scheduler::InstanceRefreshScheduler
def perform
Instance.refresh
InstancesIndex.import if Chewy.enabled?
end
end

@ -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."

@ -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> لن نطلب منك ثانية كلمتك السرية في غضون الساعة اللاحقة."

@ -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."

@ -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> Мы не будзем запытваць ваш пароль зноўку на працягу наступнай гадзіны."

@ -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>: няма да ви питаме пак за паролата през следващия час."

@ -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

@ -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."

@ -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> ئیمە لە کاتژمێری داهاتوو تێپەروشەت لێداوا ناکەین."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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> Δεν θα σου ζητήσουμε τον κωδικό ασφαλείας σου ξανά για την επόμενη ώρα."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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."

@ -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> ما در یک ساعت آینده گذرواژهتان را از شما نخواهیم پرسید."

@ -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."

@ -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."

@ -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 suivezmaintenant :'
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."

@ -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 suivezmaintenant :'
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."

@ -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."

@ -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:

@ -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."

@ -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."

@ -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> לא נבקש את סיסמתך שוב בשעה הקרובה."

@ -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"

@ -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."

@ -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: Անվաւեր ծածկագիր

@ -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."

@ -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."

@ -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."

@ -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."

@ -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時間はパスワードの再入力を求めません

@ -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}სთ"

@ -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

@ -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> біз келесі сағат ішінде сізден құпия сөзді қайта сұрамаймыз."

@ -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> 한 시간 동안 다시 암호를 묻지 않을 것입니다."

@ -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."

@ -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"

@ -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."

@ -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: അറിികൾ

@ -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

@ -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> နက သငစကကက ထပမညမဟ။"

@ -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."

@ -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."

@ -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."

@ -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."

@ -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ę."

@ -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."

@ -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."

@ -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ă."

@ -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>: мы не будем спрашивать пароль повторно в течение часа."

@ -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."

@ -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."

@ -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> අප ඉදය සඳහවත ඔබගරපදය ඔබඉලලම."

@ -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."

@ -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."

@ -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ë."

@ -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."

@ -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> Нећемо Вас питати за лозинку поново у наредних сат времена."

@ -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."

@ -191,8 +191,6 @@ ta:
localization:
body: தனவலரகளியரகபபடிறத.
guide_link_text: அனவர பஙகளிகல.
authorize_follow:
already_requested: இககணகிடரிகள ஏறகனவ அனிிகள
crypto:
errors:
invalid_key: ஒரன Ed25519 அலலத Curve25519 key அல

@ -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> เราจะไมถามรหสผานของคณกบคณสำหรบชวโมงถดไป"

@ -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."

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

Loading…
Cancel
Save