Commit 4444d8f9 authored by Thibaut Girka's avatar Thibaut Girka

Merge branch 'master' into glitch-soc/merge-upstream

parents b6fa5008 51625d34
This diff is collapsed.
This diff is collapsed.
......@@ -108,7 +108,7 @@ group :production, :test do
end
group :test do
gem 'capybara', '~> 3.15'
gem 'capybara', '~> 3.16'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 1.9'
gem 'microformats', '~> 4.1'
......
......@@ -127,7 +127,7 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
capybara (3.15.0)
capybara (3.16.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
......@@ -672,7 +672,7 @@ DEPENDENCIES
capistrano-rails (~> 1.4)
capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0)
capybara (~> 3.15)
capybara (~> 3.16)
charlock_holmes (~> 0.7.6)
chewy (~> 5.0)
cld3 (~> 3.2.3)
......
......@@ -37,6 +37,7 @@ export function submitSearch() {
params: {
q: value,
resolve: true,
limit: 5,
},
}).then(response => {
if (response.data.accounts) {
......
......@@ -94,15 +94,15 @@ class Header extends ImmutablePureComponent {
let menu = [];
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
info.push(<span key='blocked' className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
}
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
info.push(<span key='muted' className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
info.push(<span className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>);
info.push(<span key='domain_blocked' className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>);
}
if (me !== account.get('id')) {
......@@ -111,7 +111,7 @@ class Header extends ImmutablePureComponent {
} else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
} else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
}
......
......@@ -14,14 +14,17 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
const emptyList = ImmutableList();
const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
const path = withReplies ? `${accountId}:with_replies` : accountId;
return {
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
};
};
......@@ -37,6 +40,7 @@ class AccountTimeline extends ImmutablePureComponent {
isLoading: PropTypes.bool,
hasMore: PropTypes.bool,
withReplies: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
......@@ -44,9 +48,11 @@ class AccountTimeline extends ImmutablePureComponent {
this.props.dispatch(fetchAccount(accountId));
this.props.dispatch(fetchAccountIdentityProofs(accountId));
if (!withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
}
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
}
......@@ -54,9 +60,11 @@ class AccountTimeline extends ImmutablePureComponent {
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
this.props.dispatch(fetchAccount(nextProps.params.accountId));
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
if (!nextProps.withReplies) {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
}
this.props.dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
}
}
......@@ -66,7 +74,7 @@ class AccountTimeline extends ImmutablePureComponent {
}
render () {
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore } = this.props;
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy } = this.props;
if (!statusIds && isLoading) {
return (
......@@ -76,6 +84,8 @@ class AccountTimeline extends ImmutablePureComponent {
);
}
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
return (
<Column>
<ColumnBackButton />
......@@ -84,13 +94,13 @@ class AccountTimeline extends ImmutablePureComponent {
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
alwaysPrepend
scrollKey='account_timeline'
statusIds={statusIds}
statusIds={blockedBy ? emptyList : statusIds}
featuredStatusIds={featuredStatusIds}
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
emptyMessage={emptyMessage}
/>
</Column>
);
......
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import SettingText from '../../../components/setting_text';
const messages = defineMessages({
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
settings: { id: 'home.settings', defaultMessage: 'Column settings' },
});
export default @injectIntl
class ColumnSettings extends React.PureComponent {
static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
render () {
const { settings, onChange, intl } = this.props;
return (
<div>
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
<div className='column-settings__row'>
<SettingText settings={settings} settingKey={['regex', 'body']} onChange={onChange} label={intl.formatMessage(messages.filter_regex)} />
</div>
</div>
);
}
}
import { connect } from 'react-redux';
import ColumnSettings from '../components/column_settings';
import { changeSetting } from '../../../actions/settings';
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'direct']),
});
const mapDispatchToProps = dispatch => ({
onChange (key, checked) {
dispatch(changeSetting(['direct', ...key], checked));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
......@@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});
export default @connect(mapStateToProps)
......@@ -31,6 +32,7 @@ class Followers extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
......@@ -50,7 +52,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props;
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) {
return (
......@@ -60,7 +62,7 @@ class Followers extends ImmutablePureComponent {
);
}
const emptyMessage = <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />;
return (
<Column>
......@@ -75,7 +77,7 @@ class Followers extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{accountIds.map(id =>
{blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>
......
......@@ -20,6 +20,7 @@ import ScrollableList from '../../components/scrollable_list';
const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
});
export default @connect(mapStateToProps)
......@@ -31,6 +32,7 @@ class Following extends ImmutablePureComponent {
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
};
componentWillMount () {
......@@ -50,7 +52,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
const { shouldUpdateScroll, accountIds, hasMore } = this.props;
const { shouldUpdateScroll, accountIds, hasMore, blockedBy } = this.props;
if (!accountIds) {
return (
......@@ -60,7 +62,7 @@ class Following extends ImmutablePureComponent {
);
}
const emptyMessage = <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_timeline_blocked' defaultMessage='You are blocked' /> : <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
return (
<Column>
......@@ -75,7 +77,7 @@ class Following extends ImmutablePureComponent {
alwaysPrepend
emptyMessage={emptyMessage}
>
{accountIds.map(id =>
{blockedBy ? [] : accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />
)}
</ScrollableList>
......
......@@ -83,7 +83,7 @@
"compose_form.spoiler.unmarked": "Testu micca piattatu",
"compose_form.spoiler_placeholder": "Scrive u vostr'avertimentu quì",
"confirmation_modal.cancel": "Annullà",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "Bluccà è signalà",
"confirmations.block.confirm": "Bluccà",
"confirmations.block.message": "Site sicuru·a che vulete bluccà @{name}?",
"confirmations.delete.confirm": "Toglie",
......@@ -136,7 +136,7 @@
"follow_request.reject": "Righjittà",
"getting_started.developers": "Sviluppatori",
"getting_started.directory": "Annuariu di i prufili",
"getting_started.documentation": "Documentation",
"getting_started.documentation": "Ducumentazione",
"getting_started.heading": "Per principià",
"getting_started.invite": "Invità ghjente",
"getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}.",
......@@ -238,7 +238,7 @@
"navigation_bar.lists": "Liste",
"navigation_bar.logout": "Scunnettassi",
"navigation_bar.mutes": "Utilizatori piattati",
"navigation_bar.personal": "Personal",
"navigation_bar.personal": "Persunale",
"navigation_bar.pins": "Statuti puntarulati",
"navigation_bar.preferences": "Preferenze",
"navigation_bar.public_timeline": "Linea pubblica glubale",
......
......@@ -26,7 +26,7 @@
"account.posts": "Tooty",
"account.posts_with_replies": "Tooty a odpovědi",
"account.report": "Nahlásit uživatele @{name}",
"account.requested": "Požadavek čeká na schválení. Kliknutím zrušíte požadavek o sledování",
"account.requested": "Čekám na schválení. Kliknutím zrušíte požadavek o sledování",
"account.share": "Sdílet profil uživatele @{name}",
"account.show_reblogs": "Zobrazit boosty od uživatele @{name}",
"account.unblock": "Odblokovat uživatele @{name}",
......
......@@ -83,7 +83,7 @@
"compose_form.spoiler.unmarked": "نوشته پنهان نیست",
"compose_form.spoiler_placeholder": "هشدار محتوا",
"confirmation_modal.cancel": "بی‌خیال",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "مسدودسازی و گزارش",
"confirmations.block.confirm": "مسدود کن",
"confirmations.block.message": "آیا واقعاً می‌خواهید {name} را مسدود کنید؟",
"confirmations.delete.confirm": "پاک کن",
......
......@@ -83,7 +83,7 @@
"compose_form.spoiler.unmarked": "열람주의가 설정 되어 있지 않습니다",
"compose_form.spoiler_placeholder": "경고",
"confirmation_modal.cancel": "취소",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "차단하고 신고하기",
"confirmations.block.confirm": "차단",
"confirmations.block.message": "정말로 {name}를 차단하시겠습니까?",
"confirmations.delete.confirm": "삭제",
......
......@@ -5308,6 +5308,7 @@ noscript {
margin-left: -2px;
.account__avatar {
background: darken($ui-base-color, 8%);
border: 2px solid lighten($ui-base-color, 4%);
}
}
......
......@@ -99,9 +99,9 @@
}
}
&:active,
&:focus,
&:hover {
&:active:not(:disabled),
&:focus:not(:disabled),
&:hover:not(:disabled) {
background: lighten($ui-highlight-color, 10%);
svg path:last-child {
......
......@@ -352,6 +352,7 @@
border-radius: 50%;
position: relative;
margin-left: -10px;
background: darken($ui-base-color, 8%);
border: 2px solid $ui-base-color;
&:nth-child(1) {
......
......@@ -18,6 +18,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' },
conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' },
focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } },
identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' },
}.freeze
def self.default_key_transform
......
......@@ -28,7 +28,8 @@ class ProofProvider::Keybase
return
end
return if @proof.provider_username.blank?
# Do not perform synchronous validation for remote accounts
return if @proof.provider_username.blank? || !@proof.account.local?
if verifier.valid?
@proof.verified = true
......
......@@ -6,7 +6,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
context :security
context_extensions :manually_approves_followers, :featured, :also_known_as,
:moved_to, :property_value, :hashtag, :emoji
:moved_to, :property_value, :hashtag, :emoji, :identity_proof
attributes :id, :type, :following, :followers,
:inbox, :outbox, :featured,
......@@ -115,7 +115,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
end
def virtual_attachments
object.fields
object.fields + object.identity_proofs.active
end
def moved_to
......@@ -158,4 +158,24 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
Formatter.instance.format_field(object.account, object.value)
end
end
class AccountIdentityProofSerializer < ActivityPub::Serializer
attributes :type, :name, :signature_algorithm, :signature_value
def type
'IdentityProof'
end
def name
object.provider_username
end
def signature_algorithm
object.provider
end
def signature_value
object.token
end
end
end
......@@ -24,6 +24,7 @@ class ActivityPub::ProcessAccountService < BaseService
create_account if @account.nil?
update_account
process_tags
process_attachments
else
raise Mastodon::RaceConditionError
end
......@@ -151,7 +152,7 @@ class ActivityPub::ProcessAccountService < BaseService
def property_values
return unless @json['attachment'].is_a?(Array)
@json['attachment'].select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
as_array(@json['attachment']).select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
end
def mismatching_origin?(url)
......@@ -231,6 +232,23 @@ class ActivityPub::ProcessAccountService < BaseService
end
end
def process_attachments
return if @json['attachment'].blank?
previous_proofs = @account.identity_proofs.to_a
current_proofs = []
as_array(@json['attachment']).each do |attachment|
next unless equals_or_includes?(attachment['type'], 'IdentityProof')
current_proofs << process_identity_proof(attachment)
end
previous_proofs.each do |previous_proof|
next if current_proofs.any? { |current_proof| current_proof.id == previous_proof.id }
previous_proof.delete
end
end
def process_emoji(tag)
return if skip_download?
return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?
......@@ -247,4 +265,12 @@ class ActivityPub::ProcessAccountService < BaseService
emoji.image_remote_url = image_url
emoji.save
end
def process_identity_proof(attachment)
provider = attachment['signatureAlgorithm']
provider_username = attachment['name']
token = attachment['signatureValue']
@account.identity_proofs.where(provider: provider, provider_username: provider_username).find_or_create_by(provider: provider, provider_username: provider_username, token: token)
end
end
......@@ -2,8 +2,9 @@
co:
activerecord:
attributes:
status:
owned_poll: Scandagliu
poll:
expires_at: Fine
options: Scelte
errors:
models:
account:
......
......@@ -245,6 +245,7 @@ co:
feature_profile_directory: Annuariu di i prufili
feature_registrations: Arregistramenti
feature_relay: Ripetitore di federazione
feature_timeline_preview: Vista di a linea pubblica
features: Funziunalità
hidden_service: Federazione cù servizii piattati
open_reports: signalamenti aperti
......@@ -586,6 +587,9 @@ co:
content: Scusate, mà c’hè statu un prublemu cù u nostru servore.
title: Sta pagina ùn hè curretta
noscript_html: Mastodon nant’à u web hà bisognu di JavaScript per funziunà. Pudete ancu pruvà <a href="%{apps_path}">l’applicazione native</a> per a vostra piattaforma.
existing_username_validator:
not_found: ùn si pudeva micca truvà un'utilizatore lucale cù stu cugnome
not_found_multiple: ùn si pudeva micca truvà %{usernames}
exports:
archive_takeout:
date: Data
......@@ -629,10 +633,31 @@ co:
all: Tuttu
changes_saved_msg: Cambiamenti salvati!
copy: Cupià
order_by: Urdinà per
save_changes: Salvà e mudificazione
validation_errors:
one: Qualcosa ùn và bè! Verificate u prublemu quì sottu
other: Qualcosa ùn và bè! Verificate %{count} prublemi quì sottu
html_validator:
invalid_markup: 'cuntene codice HTML invalidu: %{error}'
identity_proofs:
active: Attiva
authorize: Ié, auturizà
authorize_connection_prompt: Auturizà sta cunnessione crittograffica?
errors:
failed: A cunnessione crittograffica s'hè fiascata. Ripruvate da %{provider}.
keybase:
invalid_token: E fiscie Keybase sò hash di firme è duvenu fà 66 caratteri esadecimali (0-9 A-F)
verification_failed: Keybase ùn ricunosce micca sta fiscia cum'una firma di l'utilizatore Keybase %{kb_username}. Ripruvate da Keybase.
wrong_user: Ùn si pò micca creà una prova per %{proving} mentre chì site cunnettatu·a cum'è %{current}. Cunnettatevi cum'è %{proving} è ripruvate.
explanation_html: Quì pudete cunnettà crittografficamente e vostre altre identità, cum'è per esempiu un prufile Keybase. Quessu permette à d'altre persone di mandà vi missaghji crittati, è d'affiducià i cuntinuti chì mandate.
i_am_html: Sò %{username} nant'à %{service}.
identity: Identità
inactive: Inattiva
publicize_checkbox: 'È mandà stu statutu:'
publicize_toot: ' pruvata! %{username} nant’à %{service}: %{url}'
status: Statutu di a verificazione
view_proof: Vede a prova
imports:
modes:
merge: Unisce
......@@ -753,6 +778,8 @@ co:
relationships:
activity: Attività di u contu
dormant: Inattivu
last_active: Ultima attività
most_recent: Più ricente
moved: Spiazzatu
mutual: Mutuale
primary: Primariu
......@@ -835,6 +862,7 @@ co:
edit_profile: Mudificà u prufile
export: Spurtazione d’infurmazione
featured_tags: Hashtag in vista
identity_proofs: Prove d'identità
import: Impurtazione
migrate: Migrazione di u contu
notifications: Nutificazione
......
......@@ -650,16 +650,19 @@ cs:
identity_proofs:
active: Aktivní
authorize: Ano, autorizovat
authorize_connection_prompt: Autorizovat tohle kryptografické spojení?
authorize_connection_prompt: Autorizovat toto kryptografické spojení?
errors:
failed: Kryptografické spojení selhalo. Prosím zkuste to znovu z %{provider}.
keybase:
invalid_token: Tokeny Keybase jsou hashe podpisů a musí být 66 znaků dlouhé
verification_failed: Keybase nerozpoznává tento token jako podpis uživatele %{kb_username} na Keybase. Prosím zkuste to znovu z Keybase.
wrong_user: Nelze vytvořit důkaz pro uživatele %{proving}, zatímco jste přihlášen/a jako %{current}. Přihlaste se jako %{proving} a zkuste to znovu.
explanation_html: Zde můžete kryptograficky připojit vaše ostatní identity, například profil Keybase. To dovolí jiným lidem vám posílat šifrované zprávy a důvěřovat obsahu, který jim pošlete.
i_am_html: Na %{service} jsem %{username}.
identity: Identita
inactive: Neaktivní
publicize_checkbox: 'A tootnout tohle:'
publicize_toot: 'Je to dokázáno! Na %{service} jsem %{username}: %{url}'
status: Stav ověření
view_proof: Zobrazit důkaz
imports:
......@@ -784,6 +787,8 @@ cs:
relationships:
activity: Aktivita účtu
dormant: Nečinné
last_active: Naposledy aktivní
most_recent: Nedávno přidaní
moved: Přesunuté
mutual: Vzájemné
primary: Primární
......@@ -1026,7 +1031,7 @@ cs:
recovery_codes_regenerated: Záložní kódy byly úspěšně znovu vygenerované
recovery_instructions_html: Ztratíte-li někdy přístup k vašemu telefonu, můžete k získání přístupu k účtu použít jeden ze záložních kódů. <strong>Uchovávejte tyto kódy v bezpečí</strong>. Můžete si je například vytisknout a uložit je mezi jiné důležité dokumenty.
setup: Nastavit
wrong_code: Zadaný kód byl neplatný! Je serverový čas a čas na zařízení správný?
wrong_code: Zadaný kód byl neplatný! Je čas na serveru a na zařízení správný?
user_mailer:
backup_ready:
explanation: Vyžádal/a jste si úplnou zálohu svého účtu Mastodon. Nyní je připravena ke stažení!
......
......@@ -245,6 +245,7 @@ ko:
feature_profile_directory: 프로필 디렉토리
feature_registrations: 가입
feature_relay: 연합 릴레이
feature_timeline_preview: 타임라인 미리보기
features: 기능
hidden_service: 히든 서비스와의 연합
open_reports: 미해결 신고
......@@ -634,6 +635,7 @@ ko:
all: 모두
changes_saved_msg: 정상적으로 변경되었습니다!
copy: 복사
order_by: 순서
save_changes: 변경 사항을 저장
validation_errors:
one: 오류가 발생했습니다. 아래 오류를 확인해 주십시오
......@@ -649,10 +651,13 @@ ko:
keybase:
invalid_token: 키베이스 토큰은 서명의 해시이며 66자의 16진수 문자여야 합니다
verification_failed: 키베이스가 이 토큰을 키베이스 유저 %{kb_username}의 서명으로 인식하지 못했습니다. 키베이스에서 다시 시도하세요.
wrong_user: "%{current}로 로그인 상태에서는 %{proving}에 대한 증명을 없습니다. %{proving}으로 로그인 다시 시도하세요."
explanation_html: 키베이스와 같은 다른 명의에 대한 암호화 연결을 할 수 있습니다. 이것으로 다른 사람들이 당신에게 암호화 된 메시지를 보낼 수 있고 당신의 메시지를 믿을 수 있습니다.
i_am_html: 나는 %{service}의 %{username} 입니다.
identity: 신원
inactive: 비활성
publicize_checkbox: '그리고 이것을 하세요:'
publicize_toot: '증명되었습니다! 저는 %{service}에 있는 %{username}입니다: %{url}'
status: 인증 상태
view_proof: 증명 보기
imports:
......@@ -775,6 +780,8 @@ ko:
relationships:
activity: 계정 활동
dormant: 휴면
last_active: 마지막 활동
most_recent: 가장 최근
moved: 이동함
mutual: 상호 팔로우
primary: 주 계정
......