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

Conflicts:
	app/controllers/application_controller.rb

Changed instance theme selection by instance flavour selection.
This commit is contained in:
Thibaut Girka 2018-08-24 13:34:51 +02:00
commit 246c313d45
52 changed files with 534 additions and 282 deletions

View file

@ -16,6 +16,8 @@ module Admin
timeline_preview timeline_preview
show_staff_badge show_staff_badge
bootstrap_timeline_accounts bootstrap_timeline_accounts
flavour
skin
thumbnail thumbnail
hero hero
min_invite_role min_invite_role
@ -23,6 +25,7 @@ module Admin
peers_api_enabled peers_api_enabled
show_known_fediverse_at_about_page show_known_fediverse_at_about_page
preview_sensitive_media preview_sensitive_media
custom_css
).freeze ).freeze
BOOLEAN_SETTINGS = %w( BOOLEAN_SETTINGS = %w(

View file

@ -14,7 +14,7 @@ module Admin
@suspension = Form::AdminSuspensionConfirmation.new(suspension_params) @suspension = Form::AdminSuspensionConfirmation.new(suspension_params)
if suspension_params[:acct] == @account.acct if suspension_params[:acct] == @account.acct
resolve_report! if suspension_params[:report_id] resolve_report! if suspension_params[:report_id].present?
perform_suspend! perform_suspend!
mark_reports_resolved! mark_reports_resolved!
redirect_to admin_accounts_path redirect_to admin_accounts_path

View file

@ -7,6 +7,8 @@ class Api::BaseController < ApplicationController
include RateLimitHeaders include RateLimitHeaders
skip_before_action :store_current_location skip_before_action :store_current_location
skip_before_action :check_user_permissions
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|

View file

@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base
rescue_from Mastodon::NotPermittedError, with: :forbidden rescue_from Mastodon::NotPermittedError, with: :forbidden
before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
before_action :check_suspension, if: :user_signed_in? before_action :check_user_permissions, if: :user_signed_in?
def raise_not_found def raise_not_found
raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}" raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}"
@ -49,8 +49,8 @@ class ApplicationController < ActionController::Base
forbidden unless current_user&.staff? forbidden unless current_user&.staff?
end end
def check_suspension def check_user_permissions
forbidden if current_user.account.suspended? forbidden if current_user.disabled? || current_user.account.suspended?
end end
def after_sign_out_path_for(_resource_or_scope) def after_sign_out_path_for(_resource_or_scope)
@ -165,12 +165,12 @@ class ApplicationController < ActionController::Base
end end
def current_flavour def current_flavour
return Setting.default_settings['flavour'] unless Themes.instance.flavours.include? current_user&.setting_flavour return Setting.flavour unless Themes.instance.flavours.include? current_user&.setting_flavour
current_user.setting_flavour current_user.setting_flavour
end end
def current_skin def current_skin
return 'default' unless Themes.instance.skins_for(current_flavour).include? current_user&.setting_skin return Setting.skin unless Themes.instance.skins_for(current_flavour).include? current_user&.setting_skin
current_user.setting_skin current_user.setting_skin
end end

View file

@ -6,7 +6,7 @@ class Auth::SessionsController < Devise::SessionsController
layout 'auth' layout 'auth'
skip_before_action :require_no_authentication, only: [:create] skip_before_action :require_no_authentication, only: [:create]
skip_before_action :check_suspension, only: [:destroy] skip_before_action :check_user_permissions, only: [:destroy]
prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
prepend_before_action :set_pack prepend_before_action :set_pack
before_action :set_instance_presenter, only: [:new] before_action :set_instance_presenter, only: [:new]

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class CustomCssController < ApplicationController
before_action :set_cache_headers
def show
skip_session!
render plain: Setting.custom_css || '', content_type: 'text/css'
end
end

View file

@ -130,7 +130,7 @@ export function submitCompose() {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
}, },
}).then(function (response) { }).then(function (response) {
dispatch(insertIntoTagHistory(response.data.tags)); dispatch(insertIntoTagHistory(response.data.tags, status));
dispatch(submitComposeSuccess({ ...response.data })); dispatch(submitComposeSuccess({ ...response.data }));
// To make the app more responsive, immediately get the status into the columns // To make the app more responsive, immediately get the status into the columns
@ -390,13 +390,13 @@ export function hydrateCompose() {
}; };
} }
function insertIntoTagHistory(tags) { function insertIntoTagHistory(recognizedTags, text) {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();
const oldHistory = state.getIn(['compose', 'tagHistory']); const oldHistory = state.getIn(['compose', 'tagHistory']);
const me = state.getIn(['meta', 'me']); const me = state.getIn(['meta', 'me']);
const names = tags.map(({ name }) => name); const names = recognizedTags.map(tag => text.match(new RegExp(`#${tag.name}`, 'i'))[0].slice(1));
const intersectedOldHistory = oldHistory.filter(name => !names.includes(name)); const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1);
names.push(...intersectedOldHistory.toJS()); names.push(...intersectedOldHistory.toJS());

View file

@ -7,6 +7,7 @@ export default class Column extends React.PureComponent {
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
label: PropTypes.string,
}; };
scrollTop () { scrollTop () {
@ -40,10 +41,10 @@ export default class Column extends React.PureComponent {
} }
render () { render () {
const { children } = this.props; const { label, children } = this.props;
return ( return (
<div role='region' className='column' ref={this.setRef}> <div role='region' aria-label={label} className='column' ref={this.setRef}>
{children} {children}
</div> </div>
); );

View file

@ -226,6 +226,12 @@ export default class Dropdown extends React.PureComponent {
return this.target; return this.target;
} }
componentWillUnmount = () => {
if (this.state.id === this.props.openDropdownId) {
this.handleClose();
}
}
render () { render () {
const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId } = this.props; const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId } = this.props;
const open = this.state.id === openDropdownId; const open = this.state.id === openDropdownId;

View file

@ -109,7 +109,7 @@ export default class IntersectionObserverArticle extends React.Component {
return ( return (
<article <article
ref={this.handleRef} ref={this.handleRef}
aria-posinset={index} aria-posinset={index + 1}
aria-setsize={listLength} aria-setsize={listLength}
style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }} style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
data-id={id} data-id={id}
@ -121,7 +121,7 @@ export default class IntersectionObserverArticle extends React.Component {
} }
return ( return (
<article ref={this.handleRef} aria-posinset={index} aria-setsize={listLength} data-id={id} tabIndex='0'> <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex='0'>
{children && React.cloneElement(children, { hidden: false })} {children && React.cloneElement(children, { hidden: false })}
</article> </article>
); );

View file

@ -8,7 +8,7 @@ import DisplayName from './display_name';
import StatusContent from './status_content'; import StatusContent from './status_content';
import StatusActionBar from './status_action_bar'; import StatusActionBar from './status_action_bar';
import AttachmentList from './attachment_list'; import AttachmentList from './attachment_list';
import { FormattedMessage } from 'react-intl'; import { injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { MediaGallery, Video } from '../features/ui/util/async-components'; import { MediaGallery, Video } from '../features/ui/util/async-components';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
@ -18,6 +18,24 @@ import classNames from 'classnames';
// to use the progress bar to show download progress // to use the progress bar to show download progress
import Bundle from '../features/ui/components/bundle'; import Bundle from '../features/ui/components/bundle';
export const textForScreenReader = (intl, status, rebloggedByText = false, expanded = false) => {
const displayName = status.getIn(['account', 'display_name']);
const values = [
displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName,
status.get('spoiler_text') && !expanded ? status.get('spoiler_text') : status.get('search_index').slice(status.get('spoiler_text').length),
intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }),
status.getIn(['account', 'acct']),
];
if (rebloggedByText) {
values.push(rebloggedByText);
}
return values.join(', ');
};
@injectIntl
export default class Status extends ImmutablePureComponent { export default class Status extends ImmutablePureComponent {
static contextTypes = { static contextTypes = {
@ -138,9 +156,9 @@ export default class Status extends ImmutablePureComponent {
render () { render () {
let media = null; let media = null;
let statusAvatar, prepend; let statusAvatar, prepend, rebloggedByText;
const { hidden, featured } = this.props; const { intl, hidden, featured } = this.props;
let { status, account, ...other } = this.props; let { status, account, ...other } = this.props;
@ -189,6 +207,8 @@ export default class Status extends ImmutablePureComponent {
</div> </div>
); );
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
account = status.get('account'); account = status.get('account');
status = status.get('reblog'); status = status.get('reblog');
} }
@ -248,7 +268,7 @@ export default class Status extends ImmutablePureComponent {
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null}> <div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}>
{prepend} {prepend}
<div className={classNames('status', `status-${status.get('visibility')}`, { muted: this.props.muted })} data-id={status.get('id')}> <div className={classNames('status', `status-${status.get('visibility')}`, { muted: this.props.muted })} data-id={status.get('id')}>

View file

@ -105,7 +105,7 @@ export default class CommunityTimeline extends React.PureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='users' icon='users'
active={hasUnread} active={hasUnread}

View file

@ -22,6 +22,7 @@ const messages = defineMessages({
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new toot' },
}); });
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
@ -95,7 +96,7 @@ export default class Compose extends React.PureComponent {
} }
return ( return (
<div className='drawer'> <div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}>
{header} {header}
{(multiColumn || isSearchPage) && <SearchContainer /> } {(multiColumn || isSearchPage) && <SearchContainer /> }

View file

@ -76,7 +76,7 @@ export default class DirectTimeline extends React.PureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='envelope' icon='envelope'
active={hasUnread} active={hasUnread}

View file

@ -72,7 +72,7 @@ export default class Favourites extends ImmutablePureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.heading)}>
<ColumnHeader <ColumnHeader
icon='star' icon='star'
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}

View file

@ -31,6 +31,7 @@ const messages = defineMessages({
discover: { id: 'navigation_bar.discover', defaultMessage: 'Discover' }, discover: { id: 'navigation_bar.discover', defaultMessage: 'Discover' },
personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' }, personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' },
security: { id: 'navigation_bar.security', defaultMessage: 'Security' }, security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -115,7 +116,7 @@ export default class GettingStarted extends ImmutablePureComponent {
} }
return ( return (
<Column> <Column label={intl.formatMessage(messages.menu)}>
{multiColumn && <div className='column-header__wrapper'> {multiColumn && <div className='column-header__wrapper'>
<h1 className='column-header'> <h1 className='column-header'>
<button> <button>

View file

@ -89,7 +89,7 @@ export default class HashtagTimeline extends React.PureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={`#${id}`}>
<ColumnHeader <ColumnHeader
icon='hashtag' icon='hashtag'
active={hasUnread} active={hasUnread}

View file

@ -98,7 +98,7 @@ export default class HomeTimeline extends React.PureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='home' icon='home'
active={hasUnread} active={hasUnread}

View file

@ -137,7 +137,7 @@ export default class ListTimeline extends React.PureComponent {
} }
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={title}>
<ColumnHeader <ColumnHeader
icon='list-ul' icon='list-ul'
active={hasUnread} active={hasUnread}

View file

@ -165,7 +165,7 @@ export default class Notifications extends React.PureComponent {
); );
return ( return (
<Column ref={this.setColumnRef}> <Column ref={this.setColumnRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='bell' icon='bell'
active={isUnread} active={isUnread}

View file

@ -112,7 +112,7 @@ export default class PublicTimeline extends React.PureComponent {
const pinned = !!columnId; const pinned = !!columnId;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='globe' icon='globe'
active={hasUnread} active={hasUnread}

View file

@ -51,7 +51,7 @@ export default class CommunityTimeline extends React.PureComponent {
const { intl } = this.props; const { intl } = this.props;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='users' icon='users'
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}

View file

@ -51,7 +51,7 @@ export default class PublicTimeline extends React.PureComponent {
const { intl } = this.props; const { intl } = this.props;
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='globe' icon='globe'
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}

View file

@ -43,6 +43,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { boostModal, deleteModal } from '../../initial_state'; import { boostModal, deleteModal } from '../../initial_state';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
import { textForScreenReader } from '../../components/status';
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -52,6 +53,7 @@ const messages = defineMessages({
blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' }, revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' }, hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
}); });
const makeMapStateToProps = () => { const makeMapStateToProps = () => {
@ -404,7 +406,7 @@ export default class Status extends ImmutablePureComponent {
}; };
return ( return (
<Column> <Column label={intl.formatMessage(messages.detailedStatus)}>
<ColumnHeader <ColumnHeader
showBackButton showBackButton
extraButton={( extraButton={(
@ -417,7 +419,7 @@ export default class Status extends ImmutablePureComponent {
{ancestors} {ancestors}
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>
<div className='focusable' tabIndex='0'> <div className='focusable' tabIndex='0' aria-label={textForScreenReader(intl, status, false, !status.get('hidden'))}>
<DetailedStatus <DetailedStatus
status={status} status={status}
onOpenVideo={this.handleOpenVideo} onOpenVideo={this.handleOpenVideo}

View file

@ -1,66 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { injectIntl, defineMessages } from 'react-intl';
import Column from '../ui/components/column';
import ColumnHeader from '../../components/column_header';
import Hashtag from '../../components/hashtag';
import classNames from 'classnames';
import { fetchTrends } from '../../actions/trends';
const messages = defineMessages({
title: { id: 'trends.header', defaultMessage: 'Trending now' },
refreshTrends: { id: 'trends.refresh', defaultMessage: 'Refresh trends' },
});
const mapStateToProps = state => ({
trends: state.getIn(['trends', 'items']),
loading: state.getIn(['trends', 'isLoading']),
});
const mapDispatchToProps = dispatch => ({
fetchTrends: () => dispatch(fetchTrends()),
});
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
export default class Trends extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
trends: ImmutablePropTypes.list,
fetchTrends: PropTypes.func.isRequired,
loading: PropTypes.bool,
};
componentDidMount () {
this.props.fetchTrends();
}
handleRefresh = () => {
this.props.fetchTrends();
}
render () {
const { trends, loading, intl } = this.props;
return (
<Column>
<ColumnHeader
icon='fire'
title={intl.formatMessage(messages.title)}
extraButton={(
<button className='column-header__button' title={intl.formatMessage(messages.refreshTrends)} aria-label={intl.formatMessage(messages.refreshTrends)} onClick={this.handleRefresh}><i className={classNames('fa', 'fa-refresh', { 'fa-spin': loading })} /></button>
)}
/>
<div className='scrollable'>
{trends && trends.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
</div>
</Column>
);
}
}

View file

@ -22,7 +22,7 @@
"account.posts": "Pouets", "account.posts": "Pouets",
"account.posts_with_replies": "Pouets et réponses", "account.posts_with_replies": "Pouets et réponses",
"account.report": "Signaler", "account.report": "Signaler",
"account.requested": "En attente d'approbation. Cliquez pour annuler la requête", "account.requested": "En attente dapprobation. Cliquez pour annuler la requête",
"account.share": "Partager le profil de @{name}", "account.share": "Partager le profil de @{name}",
"account.show_reblogs": "Afficher les partages de @{name}", "account.show_reblogs": "Afficher les partages de @{name}",
"account.unblock": "Débloquer", "account.unblock": "Débloquer",
@ -32,7 +32,7 @@
"account.unmute": "Ne plus masquer", "account.unmute": "Ne plus masquer",
"account.unmute_notifications": "Réactiver les notifications de @{name}", "account.unmute_notifications": "Réactiver les notifications de @{name}",
"account.view_full_profile": "Afficher le profil complet", "account.view_full_profile": "Afficher le profil complet",
"alert.unexpected.message": "Une erreur non-attendue s'est produite.", "alert.unexpected.message": "Une erreur non attendue sest produite.",
"alert.unexpected.title": "Oups!", "alert.unexpected.title": "Oups!",
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois", "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois",
"bundle_column_error.body": "Une erreur sest produite lors du chargement de ce composant.", "bundle_column_error.body": "Une erreur sest produite lors du chargement de ce composant.",
@ -62,9 +62,9 @@
"column_header.unpin": "Retirer", "column_header.unpin": "Retirer",
"column_subheading.settings": "Paramètres", "column_subheading.settings": "Paramètres",
"community.column_settings.media_only": "Média uniquement", "community.column_settings.media_only": "Média uniquement",
"compose_form.direct_message_warning": "Ce pouet sera uniquement envoyé qu'aux personnes mentionnées. Cependant, l'administration de votre instance et des instances réceptrices pourront inspecter ce message.", "compose_form.direct_message_warning": "Ce pouet sera uniquement envoyé aux personnes mentionnées. Cependant, ladministration de votre instance et des instances réceptrices pourront inspecter ce message.",
"compose_form.direct_message_warning_learn_more": "En savoir plus", "compose_form.direct_message_warning_learn_more": "En savoir plus",
"compose_form.hashtag_warning": "Ce pouet ne sera pas listé dans les recherches par hashtag car sa visibilité est réglée sur \"non-listé\". Seuls les pouets avec une visibilité \"publique\" peuvent être recherchés par hashtag.", "compose_form.hashtag_warning": "Ce pouet ne sera pas listé dans les recherches par hashtag car sa visibilité est réglée sur \"non listé\". Seuls les pouets avec une visibilité \"publique\" peuvent être recherchés par hashtag.",
"compose_form.lock_disclaimer": "Votre compte nest pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.", "compose_form.lock_disclaimer": "Votre compte nest pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.",
"compose_form.lock_disclaimer.lock": "verrouillé", "compose_form.lock_disclaimer.lock": "verrouillé",
"compose_form.placeholder": "Quavez-vous en tête?", "compose_form.placeholder": "Quavez-vous en tête?",
@ -73,7 +73,7 @@
"compose_form.sensitive.marked": "Média marqué comme sensible", "compose_form.sensitive.marked": "Média marqué comme sensible",
"compose_form.sensitive.unmarked": "Média non marqué comme sensible", "compose_form.sensitive.unmarked": "Média non marqué comme sensible",
"compose_form.spoiler.marked": "Le texte est caché derrière un avertissement", "compose_form.spoiler.marked": "Le texte est caché derrière un avertissement",
"compose_form.spoiler.unmarked": "Le texte n'est pas caché", "compose_form.spoiler.unmarked": "Le texte nest pas caché",
"compose_form.spoiler_placeholder": "Écrivez ici votre avertissement", "compose_form.spoiler_placeholder": "Écrivez ici votre avertissement",
"confirmation_modal.cancel": "Annuler", "confirmation_modal.cancel": "Annuler",
"confirmations.block.confirm": "Bloquer", "confirmations.block.confirm": "Bloquer",
@ -83,11 +83,11 @@
"confirmations.delete_list.confirm": "Supprimer", "confirmations.delete_list.confirm": "Supprimer",
"confirmations.delete_list.message": "Êtes-vous sûr de vouloir supprimer définitivement cette liste?", "confirmations.delete_list.message": "Êtes-vous sûr de vouloir supprimer définitivement cette liste?",
"confirmations.domain_block.confirm": "Masquer le domaine entier", "confirmations.domain_block.confirm": "Masquer le domaine entier",
"confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables. Vous ne verrez plus de contenu provenant de ce domaine ni dans vos lignes de temps publiques, ni dans vos notifications. Vos suiveurs utilisant ce domaine seront retirés.", "confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables. Vous ne verrez plus de contenu provenant de ce domaine, ni dans fils publics, ni dans vos notifications. Vos abonné·e·s utilisant ce domaine seront retiré·e·s.",
"confirmations.mute.confirm": "Masquer", "confirmations.mute.confirm": "Masquer",
"confirmations.mute.message": "Confirmez-vous le masquage de {name}?", "confirmations.mute.message": "Confirmez-vous le masquage de {name}?",
"confirmations.redraft.confirm": "Effacer et ré-écrire", "confirmations.redraft.confirm": "Effacer et ré-écrire",
"confirmations.redraft.message": "Êtes vous sûr de vouloir effacer ce statut pour le ré-écrire ? Vous perdrez toutes ses réponses, ses repartages et ses mises en favori.", "confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer ce statut pour le ré-écrire? Vous perdrez toutes ses réponses, ses repartages et ses mises en favori.",
"confirmations.unfollow.confirm": "Ne plus suivre", "confirmations.unfollow.confirm": "Ne plus suivre",
"confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name}?", "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name}?",
"embed.instructions": "Intégrez ce statut à votre site en copiant le code ci-dessous.", "embed.instructions": "Intégrez ce statut à votre site en copiant le code ci-dessous.",
@ -98,7 +98,7 @@
"emoji_button.food": "Nourriture & Boisson", "emoji_button.food": "Nourriture & Boisson",
"emoji_button.label": "Insérer un émoji", "emoji_button.label": "Insérer un émoji",
"emoji_button.nature": "Nature", "emoji_button.nature": "Nature",
"emoji_button.not_found": "Pas d'emojis!! (╯°□°)╯︵ ┻━┻", "emoji_button.not_found": "Pas démoji!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "Objets", "emoji_button.objects": "Objets",
"emoji_button.people": "Personnages", "emoji_button.people": "Personnages",
"emoji_button.recent": "Fréquemment utilisés", "emoji_button.recent": "Fréquemment utilisés",
@ -107,11 +107,11 @@
"emoji_button.symbols": "Symboles", "emoji_button.symbols": "Symboles",
"emoji_button.travel": "Lieux & Voyages", "emoji_button.travel": "Lieux & Voyages",
"empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir!", "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir!",
"empty_column.direct": "Vous n'avez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il s'affichera ici.", "empty_column.direct": "Vous navez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il saffichera ici.",
"empty_column.hashtag": "Il ny a encore aucun contenu associé à ce hashtag.", "empty_column.hashtag": "Il ny a encore aucun contenu associé à ce hashtag.",
"empty_column.home": "Vous ne suivez personne. Visitez {public} ou utilisez la recherche pour trouver dautres personnes à suivre.", "empty_column.home": "Vous ne suivez personne. Visitez {public} ou utilisez la recherche pour trouver dautres personnes à suivre.",
"empty_column.home.public_timeline": "le fil public", "empty_column.home.public_timeline": "le fil public",
"empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publierons de nouveaux statuts, ils apparaîtront ici.", "empty_column.list": "Il ny a rien dans cette liste pour linstant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.",
"empty_column.notifications": "Vous navez pas encore de notification. Interagissez avec dautres personnes pour débuter la conversation.", "empty_column.notifications": "Vous navez pas encore de notification. Interagissez avec dautres personnes pour débuter la conversation.",
"empty_column.public": "Il ny a rien ici! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes dautres instances pour remplir le fil public", "empty_column.public": "Il ny a rien ici! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes dautres instances pour remplir le fil public",
"follow_request.authorize": "Accepter", "follow_request.authorize": "Accepter",
@ -129,7 +129,7 @@
"home.column_settings.show_replies": "Afficher les réponses", "home.column_settings.show_replies": "Afficher les réponses",
"keyboard_shortcuts.back": "revenir en arrière", "keyboard_shortcuts.back": "revenir en arrière",
"keyboard_shortcuts.boost": "partager", "keyboard_shortcuts.boost": "partager",
"keyboard_shortcuts.column": "focaliser un statut dans l'une des colonnes", "keyboard_shortcuts.column": "focaliser un statut dans lune des colonnes",
"keyboard_shortcuts.compose": "pour centrer la zone de rédaction", "keyboard_shortcuts.compose": "pour centrer la zone de rédaction",
"keyboard_shortcuts.description": "Description", "keyboard_shortcuts.description": "Description",
"keyboard_shortcuts.down": "pour descendre dans la liste", "keyboard_shortcuts.down": "pour descendre dans la liste",
@ -138,8 +138,8 @@
"keyboard_shortcuts.heading": "Raccourcis clavier", "keyboard_shortcuts.heading": "Raccourcis clavier",
"keyboard_shortcuts.hotkey": "Raccourci", "keyboard_shortcuts.hotkey": "Raccourci",
"keyboard_shortcuts.legend": "pour afficher cette légende", "keyboard_shortcuts.legend": "pour afficher cette légende",
"keyboard_shortcuts.mention": "pour mentionner l'auteur", "keyboard_shortcuts.mention": "pour mentionner lauteur·rice",
"keyboard_shortcuts.profile": "pour ouvrir le profil de l'auteur", "keyboard_shortcuts.profile": "pour ouvrir le profil de lauteur·rice",
"keyboard_shortcuts.reply": "pour répondre", "keyboard_shortcuts.reply": "pour répondre",
"keyboard_shortcuts.search": "pour cibler la recherche", "keyboard_shortcuts.search": "pour cibler la recherche",
"keyboard_shortcuts.toggle_hidden": "pour afficher/cacher un texte derrière CW", "keyboard_shortcuts.toggle_hidden": "pour afficher/cacher un texte derrière CW",
@ -202,12 +202,12 @@
"onboarding.next": "Suivant", "onboarding.next": "Suivant",
"onboarding.page_five.public_timelines": "Le fil public global affiche les messages de toutes les personnes suivies par les membres de {domain}. Le fil public local est identique, mais se limite aux membres de {domain}.", "onboarding.page_five.public_timelines": "Le fil public global affiche les messages de toutes les personnes suivies par les membres de {domain}. Le fil public local est identique, mais se limite aux membres de {domain}.",
"onboarding.page_four.home": "Laccueil affiche les messages des personnes que vous suivez.", "onboarding.page_four.home": "Laccueil affiche les messages des personnes que vous suivez.",
"onboarding.page_four.notifications": "La colonne de notification vous avertit lors d'une interaction avec vous.", "onboarding.page_four.notifications": "La colonne de notification vous avertit lors dune interaction avec vous.",
"onboarding.page_one.federation": "Mastodon est un réseau de serveurs indépendants qui se joignent pour former un réseau social plus vaste. Nous appelons ces serveurs des instances.", "onboarding.page_one.federation": "Mastodon est un réseau de serveurs indépendants qui se joignent pour former un réseau social plus vaste. Nous appelons ces serveurs des instances.",
"onboarding.page_one.full_handle": "Votre identifiant complet", "onboarding.page_one.full_handle": "Votre identifiant complet",
"onboarding.page_one.handle_hint": "C'est ce que vos amis devront rechercher.", "onboarding.page_one.handle_hint": "Cest ce que vos ami·e·s devront rechercher.",
"onboarding.page_one.welcome": "Bienvenue sur Mastodon!", "onboarding.page_one.welcome": "Bienvenue sur Mastodon!",
"onboarding.page_six.admin": "Ladministrateur⋅ice de votre instance est {admin}.", "onboarding.page_six.admin": "Votre instance est administrée par {admin}.",
"onboarding.page_six.almost_done": "Nous y sommes presque…", "onboarding.page_six.almost_done": "Nous y sommes presque…",
"onboarding.page_six.appetoot": "Bon appouétit!", "onboarding.page_six.appetoot": "Bon appouétit!",
"onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres.", "onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres.",
@ -220,14 +220,14 @@
"onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.", "onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.",
"onboarding.skip": "Passer", "onboarding.skip": "Passer",
"privacy.change": "Ajuster la confidentialité du message", "privacy.change": "Ajuster la confidentialité du message",
"privacy.direct.long": "N'envoyer qu'aux personnes mentionnées", "privacy.direct.long": "Nenvoyer quaux personnes mentionnées",
"privacy.direct.short": "Direct", "privacy.direct.short": "Direct",
"privacy.private.long": "Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts", "privacy.private.long": "Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts",
"privacy.private.short": "Abonné⋅e⋅s uniquement", "privacy.private.short": "Abonné⋅e⋅s uniquement",
"privacy.public.long": "Afficher dans les fils publics", "privacy.public.long": "Afficher dans les fils publics",
"privacy.public.short": "Public", "privacy.public.short": "Public",
"privacy.unlisted.long": "Ne pas afficher dans les fils publics", "privacy.unlisted.long": "Ne pas afficher dans les fils publics",
"privacy.unlisted.short": "Non-listé", "privacy.unlisted.short": "Non listé",
"regeneration_indicator.label": "Chargement…", "regeneration_indicator.label": "Chargement…",
"regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation!", "regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation!",
"relative_time.days": "{number} j", "relative_time.days": "{number} j",
@ -237,8 +237,8 @@
"relative_time.seconds": "{number} s", "relative_time.seconds": "{number} s",
"reply_indicator.cancel": "Annuler", "reply_indicator.cancel": "Annuler",
"report.forward": "Transférer à {target}", "report.forward": "Transférer à {target}",
"report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport?", "report.forward_hint": "Le compte provient dun autre serveur. Envoyez également une copie anonyme du rapport?",
"report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez le compte ci-dessous:", "report.hint": "Le rapport sera envoyé aux modérateur·rice·s de votre instance. Vous pouvez expliquer pourquoi vous signalez le compte ci-dessous:",
"report.placeholder": "Commentaires additionnels", "report.placeholder": "Commentaires additionnels",
"report.submit": "Envoyer", "report.submit": "Envoyer",
"report.target": "Signalement", "report.target": "Signalement",
@ -272,7 +272,7 @@
"status.pin": "Épingler sur le profil", "status.pin": "Épingler sur le profil",
"status.pinned": "Pouet épinglé", "status.pinned": "Pouet épinglé",
"status.reblog": "Partager", "status.reblog": "Partager",
"status.reblog_private": "Booster vers l'audience originale", "status.reblog_private": "Booster vers laudience originale",
"status.reblogged_by": "{name} a partagé:", "status.reblogged_by": "{name} a partagé:",
"status.redraft": "Effacer et ré-écrire", "status.redraft": "Effacer et ré-écrire",
"status.reply": "Répondre", "status.reply": "Répondre",
@ -292,16 +292,16 @@
"tabs_bar.local_timeline": "Fil public local", "tabs_bar.local_timeline": "Fil public local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Chercher", "tabs_bar.search": "Chercher",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} discutent", "trends.count_by_accounts": "{count} {rawCount, plural, one {personne} other {personnes}} discutent",
"ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
"upload_area.title": "Glissez et déposez pour envoyer", "upload_area.title": "Glissez et déposez pour envoyer",
"upload_button.label": "Joindre un média", "upload_button.label": "Joindre un média",
"upload_form.description": "Décrire pour les malvoyants", "upload_form.description": "Décrire pour les malvoyant·e·s",
"upload_form.focus": "Recadrer", "upload_form.focus": "Recadrer",
"upload_form.undo": "Supprimer", "upload_form.undo": "Supprimer",
"upload_progress.label": "Envoi en cours…", "upload_progress.label": "Envoi en cours…",
"video.close": "Fermer la vidéo", "video.close": "Fermer la vidéo",
"video.exit_fullscreen": "Quitter plein écran", "video.exit_fullscreen": "Quitter le plein écran",
"video.expand": "Agrandir la vidéo", "video.expand": "Agrandir la vidéo",
"video.fullscreen": "Plein écran", "video.fullscreen": "Plein écran",
"video.hide": "Masquer la vidéo", "video.hide": "Masquer la vidéo",

View file

@ -131,7 +131,7 @@ const updateSuggestionTags = (state, token) => {
return state.merge({ return state.merge({
suggestions: state.get('tagHistory') suggestions: state.get('tagHistory')
.filter(tag => tag.startsWith(prefix)) .filter(tag => tag.toLowerCase().startsWith(prefix.toLowerCase()))
.slice(0, 4) .slice(0, 4)
.map(tag => '#' + tag), .map(tag => '#' + tag),
suggestion_token: token, suggestion_token: token,

View file

@ -80,15 +80,7 @@ const handlePush = (event) => {
// Placeholder until more information can be loaded // Placeholder until more information can be loaded
event.waitUntil( event.waitUntil(
notify({ fetchFromApi(`/api/v1/notifications/${notification_id}`, 'get', access_token).then(notification => {
title,
body,
icon,
tag: notification_id,
timestamp: new Date(),
badge: '/badge.png',
data: { access_token, preferred_locale, url: '/web/notifications' },
}).then(() => fetchFromApi(`/api/v1/notifications/${notification_id}`, 'get', access_token)).then(notification => {
const options = {}; const options = {};
options.title = formatMessage(`notification.${notification.type}`, preferred_locale, { name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); options.title = formatMessage(`notification.${notification.type}`, preferred_locale, { name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
@ -112,6 +104,16 @@ const handlePush = (event) => {
} }
return notify(options); return notify(options);
}).catch(() => {
return notify({
title,
body,
icon,
tag: notification_id,
timestamp: new Date(),
badge: '/badge.png',
data: { access_token, preferred_locale, url: '/web/notifications' },
});
}) })
); );
}; };

View file

@ -169,6 +169,10 @@
color: $white; color: $white;
} }
.dropdown-menu__separator {
border-bottom-color: lighten($ui-base-color, 12%);
}
// Change the background colors of modals // Change the background colors of modals
.actions-modal, .actions-modal,
.boost-modal, .boost-modal,
@ -281,3 +285,87 @@
} }
} }
} }
.flash-message {
box-shadow: none;
&.notice {
background: rgba($success-green, 0.5);
color: lighten($success-green, 12%);
}
&.alert {
background: rgba($error-red, 0.5);
color: lighten($error-red, 12%);
}
}
.simple_form,
.table-form {
.warning {
box-shadow: none;
background: rgba($error-red, 0.5);
text-shadow: none;
}
}
.status__content,
.reply-indicator__content {
a {
color: $highlight-text-color;
}
}
.button.logo-button {
color: $white;
svg path:first-child {
fill: $white;
}
}
.public-layout {
.header,
.public-account-header,
.public-account-bio {
box-shadow: none;
}
.header {
background: lighten($ui-base-color, 12%);
}
.public-account-header {
&__image {
background: lighten($ui-base-color, 12%);
&::after {
box-shadow: none;
}
}
&__tabs {
&__name {
h1,
h1 small {
color: $white;
}
}
}
}
}
.account__section-headline a.active::after {
border-color: transparent transparent $white;
}
.hero-widget,
.box-widget,
.contact-widget,
.landing-page__information.contact-widget,
.moved-account-widget,
.memoriam-widget,
.activity-stream,
.nothing-here {
box-shadow: none;
}

View file

@ -621,3 +621,14 @@ code {
.scope-danger { .scope-danger {
color: $warning-red; color: $warning-red;
} }
.form_admin_settings_site_short_description,
.form_admin_settings_site_description,
.form_admin_settings_site_extended_description,
.form_admin_settings_site_terms,
.form_admin_settings_custom_css,
.form_admin_settings_closed_registrations_message {
textarea {
font-family: 'mastodon-font-monospace', monospace;
}
}

View file

@ -107,7 +107,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
updated = tag['updated'] updated = tag['updated']
emoji = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain) emoji = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain)
return unless emoji.nil? || emoji.updated_at >= updated return unless emoji.nil? || image_url != emoji.image_remote_url || (updated && emoji.updated_at >= updated)
emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri) emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri)
emoji.image_remote_url = image_url emoji.image_remote_url = image_url

View file

@ -2,6 +2,11 @@
module Settings module Settings
class ScopedSettings class ScopedSettings
DEFAULTING_TO_UNSCOPED = %w(
flavour
skin
).freeze
def initialize(object) def initialize(object)
@object = object @object = object
end end
@ -50,15 +55,22 @@ module Settings
Rails.cache.fetch(Setting.cache_key(key, @object)) do Rails.cache.fetch(Setting.cache_key(key, @object)) do
db_val = thing_scoped.find_by(var: key.to_s) db_val = thing_scoped.find_by(var: key.to_s)
if db_val if db_val
default_value = Setting.default_settings[key] default_value = ScopedSettings.default_settings[key]
return default_value.with_indifferent_access.merge!(db_val.value) if default_value.is_a?(Hash) return default_value.with_indifferent_access.merge!(db_val.value) if default_value.is_a?(Hash)
db_val.value db_val.value
else else
Setting.default_settings[key] ScopedSettings.default_settings[key]
end end
end end
end end
class << self
def default_settings
defaulting = DEFAULTING_TO_UNSCOPED.map { |k| [k, Setting[k]] }.to_h
Setting.default_settings.merge!(defaulting)
end
end
protected protected
def thing_scoped def thing_scoped

View file

@ -30,6 +30,10 @@ class Form::AdminSettings
:show_staff_badge=, :show_staff_badge=,
:bootstrap_timeline_accounts, :bootstrap_timeline_accounts,
:bootstrap_timeline_accounts=, :bootstrap_timeline_accounts=,
:flavour,
:flavour=,
:skin,
:skin=,
:min_invite_role, :min_invite_role,
:min_invite_role=, :min_invite_role=,
:activity_api_enabled, :activity_api_enabled,
@ -40,6 +44,8 @@ class Form::AdminSettings
:show_known_fediverse_at_about_page=, :show_known_fediverse_at_about_page=,
:preview_sensitive_media, :preview_sensitive_media,
:preview_sensitive_media=, :preview_sensitive_media=,
:custom_css,
:custom_css=,
to: Setting to: Setting
) )
end end

View file

@ -216,10 +216,6 @@ class User < ApplicationRecord
save! save!
end end
def active_for_authentication?
super && !disabled?
end
def setting_default_privacy def setting_default_privacy
settings.default_privacy || (account.locked? ? 'private' : 'public') settings.default_privacy || (account.locked? ? 'private' : 'public')
end end

View file

@ -18,11 +18,11 @@ class UserPolicy < ApplicationPolicy
end end
def enable? def enable?
admin? staff?
end end
def disable? def disable?
admin? && !record.admin? staff? && !record.admin?
end end
def promote? def promote?

View file

@ -14,7 +14,7 @@ class InstancePresenter
) )
def contact_account def contact_account
Account.find_local(Setting.site_contact_username) Account.find_local(Setting.site_contact_username.gsub(/\A@/, ''))
end end
def user_count def user_count

View file

@ -93,11 +93,11 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
end end
def avatar_exists? def avatar_exists?
object.avatar.exists? object.avatar?
end end
def header_exists? def header_exists?
object.header.exists? object.header?
end end
def manually_approves_followers def manually_approves_followers

View file

@ -33,7 +33,7 @@ class Web::NotificationSerializer < ActiveModel::Serializer
end end
def body def body
str = truncate(strip_tags(object.target_status&.spoiler_text&.presence || object.target_status&.text || object.from_account.note), length: 140) str = strip_tags(object.target_status&.spoiler_text&.presence || object.target_status&.text || object.from_account.note)
HTMLEntities.new.decode(str.to_str) # Do not encode entities, since this value will not be used in HTML truncate(HTMLEntities.new.decode(str.to_str), length: 140) # Do not encode entities, since this value will not be used in HTML
end end
end end

View file

@ -226,7 +226,7 @@ class ActivityPub::ProcessAccountService < BaseService
updated = tag['updated'] updated = tag['updated']
emoji = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain) emoji = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain)
return unless emoji.nil? || emoji.updated_at >= updated return unless emoji.nil? || image_url != emoji.image_remote_url || (updated && emoji.updated_at >= updated)
emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri) emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri)
emoji.image_remote_url = image_url emoji.image_remote_url = image_url

View file

@ -15,6 +15,7 @@
%hr/ %hr/
.fields-group .fields-group
= f.input :flavour, collection: Themes.instance.flavours, label_method: lambda { |flavour| I18n.t("flavours.#{flavour}.name", default: flavour) }, wrapper: :with_label, include_blank: false
= f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html') = f.input :thumbnail, as: :file, wrapper: :with_block_label, label: t('admin.settings.thumbnail.title'), hint: t('admin.settings.thumbnail.desc_html')
= f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html') = f.input :hero, as: :file, wrapper: :with_block_label, label: t('admin.settings.hero.title'), hint: t('admin.settings.hero.desc_html')
@ -48,7 +49,7 @@
.fields-group .fields-group
= f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 }
= f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 } = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
= f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
%hr/ %hr/
.fields-group .fields-group

View file

@ -3,7 +3,7 @@
%li= link_to t('auth.login'), new_session_path(resource_name) %li= link_to t('auth.login'), new_session_path(resource_name)
- if devise_mapping.registerable? && controller_name != 'registrations' - if devise_mapping.registerable? && controller_name != 'registrations'
%li= link_to t('auth.register'), new_registration_path(resource_name) %li= link_to t('auth.register'), open_registrations? ? new_registration_path(resource_name) : 'https://joinmastodon.org/#getting-started'
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
%li= link_to t('auth.forgot_password'), new_password_path(resource_name) %li= link_to t('auth.forgot_password'), new_password_path(resource_name)

View file

@ -21,6 +21,9 @@
= javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous'
= csrf_meta_tags = csrf_meta_tags
- if Setting.custom_css.present?
= stylesheet_link_tag custom_css_path, media: 'all'
= yield :header_tags = yield :header_tags
-# These must come after :header_tags to ensure our initial state has been defined. -# These must come after :header_tags to ensure our initial state has been defined.

View file

@ -11,7 +11,7 @@
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn' = link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
- else - else
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button' = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn nav-link nav-button'
= link_to t('auth.register'), new_user_registration_path, class: 'webapp-btn nav-link nav-button' = link_to t('auth.register'), open_registrations? ? new_user_registration_path : 'https://joinmastodon.org/#getting-started', class: 'webapp-btn nav-link nav-button'
.container= yield .container= yield

View file

@ -17,30 +17,30 @@ fr:
unconfirmed: Vous devez valider votre compte pour continuer. unconfirmed: Vous devez valider votre compte pour continuer.
mailer: mailer:
confirmation_instructions: confirmation_instructions:
action: Vérifier l'adresse courriel action: Vérifier ladresse courriel
explanation: Vous avez créé un compte sur %{host} avec cette adresse courriel. Vous êtes à un clic de l'activer. Si ce n'était pas vous, veuillez ignorer ce courriel. explanation: Vous avez créé un compte sur %{host} avec cette adresse courriel. Vous êtes à un clic de lactiver. Si ce nétait pas vous, veuillez ignorer ce courriel.
extra_html: S'il vous plaît, consultez également <a href="%{terms_path}"> 1les règles de l'instance</a> 2 et <a href="%{policy_path}">3nos termes de service</a> 4. extra_html: Merci de consultez également <a href="%{terms_path}">les règles de linstance</a> et <a href="%{policy_path}">nos conditions dutilisation</a>.
subject: Merci de confirmer votre inscription sur %{instance} subject: Merci de confirmer votre inscription sur %{instance}
title: Vérifier l'adresse courriel title: Vérifier ladresse courriel
email_changed: email_changed:
explanation: 'L''adresse courriel de votre compte est en cours de modification pour devenir :' explanation: 'Ladresse courriel de votre compte est en cours de modification pour devenir :'
extra: Si vous n'avez pas changé votre adresse courriel, il est probable que quelqu'un ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter l'administrateur de l'instance si vous êtes bloqué hors de votre compte. extra: Si vous navez pas changé votre adresse courriel, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice de linstance si vous êtes bloqué·e hors de votre compte.
subject: 'Mastodon : Courriel modifié' subject: 'Mastodon : Courriel modifié'
title: Nouvelle adresse courriel title: Nouvelle adresse courriel
password_change: password_change:
explanation: Le mot de passe de votre compte a été changé. explanation: Le mot de passe de votre compte a été changé.
extra: Si vous n'avez pas changé votre mot de passe, il est probable que quelqu'un ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter l'administrateur de l'instance si vous êtes bloqué hors de votre compte. extra: Si vous navez pas changé votre mot de passe, il est probable que quelquun ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter ladministrateur·rice de linstance si vous êtes bloqué·e hors de votre compte.
subject: Votre mot de passe a été modifié avec succès subject: Votre mot de passe a été modifié avec succès
title: Mot de passe modifié title: Mot de passe modifié
reconfirmation_instructions: reconfirmation_instructions:
explanation: Confirmez la nouvelle adresse pour changer votre courriel. explanation: Confirmez la nouvelle adresse pour changer votre courriel.
extra: Si ce changement n' a pas été initié par vous, veuillez ignorer ce courriel. L'adresse courriel du compte Mastodon ne changera pas tant que vous n'aurez pas cliqué sur le lien ci-dessus. extra: Si ce changement na pas été initié par vous, veuillez ignorer ce courriel. Ladresse courriel du compte Mastodon ne changera pas tant que vous naurez pas cliqué sur le lien ci-dessus.
subject: 'Mastodon : Confirmez l''email pour %{instance}' subject: 'Mastodon : Confirmez ladresse pour %{instance}'
title: Vérifier l'adresse courriel title: Vérifier ladresse courriel
reset_password_instructions: reset_password_instructions:
action: Modifier le mot de passe action: Modifier le mot de passe
explanation: Vous avez demandé un nouveau mot de passe pour votre compte. explanation: Vous avez demandé un nouveau mot de passe pour votre compte.
extra: Si vous ne l'avez pas demandé, veuillez ignorer ce courriel. Votre mot de passe ne changera pas tant que vous n'aurez pas cliqué sur le lien ci-dessus et que vous n'en aurez pas créé un nouveau. extra: Si vous ne lavez pas demandé, veuillez ignorer ce courriel. Votre mot de passe ne changera pas tant que vous naurez pas cliqué sur le lien ci-dessus et que vous nen aurez pas créé un nouveau.
subject: Instructions pour changer votre mot de passe subject: Instructions pour changer votre mot de passe
title: Réinitialisation du mot de passe title: Réinitialisation du mot de passe
unlock_instructions: unlock_instructions:

View file

@ -7,7 +7,7 @@ fr:
redirect_uri: LURL de redirection redirect_uri: LURL de redirection
scope: Portée scope: Portée
scopes: Étendues scopes: Étendues
website: Site web de l'application website: Site web de lapplication
errors: errors:
models: models:
doorkeeper/application: doorkeeper/application:
@ -64,7 +64,7 @@ fr:
prompt: Autoriser %{client_name} à utiliser votre compte? prompt: Autoriser %{client_name} à utiliser votre compte?
title: Autorisation requise title: Autorisation requise
show: show:
title: Copiez ce code d'autorisation et collez-le dans l'application. title: Copiez ce code dautorisation et collez-le dans lapplication.
authorized_applications: authorized_applications:
buttons: buttons:
revoke: Annuler revoke: Annuler
@ -119,12 +119,12 @@ fr:
push: recevoir vos notifications push: recevoir vos notifications
read: lire toutes les données de votre compte read: lire toutes les données de votre compte
read:accounts: voir les informations du compte read:accounts: voir les informations du compte
read:blocks: voir vos bloqués read:blocks: voir vos bloquages
read:favourites: voir vos favoris read:favourites: voir vos favoris
read:filters: voir vos filtres read:filters: voir vos filtres
read:follows: voir vos suivis read:follows: voir vos suivis
read:lists: voir vos listes read:lists: voir vos listes
read:mutes: voir vos silenciés read:mutes: voir vos masquages
read:notifications: voir vos notifications read:notifications: voir vos notifications
read:reports: voir vos rapports read:reports: voir vos rapports
read:search: rechercher en votre nom read:search: rechercher en votre nom
@ -137,7 +137,7 @@ fr:
write:follows: suivre les gens write:follows: suivre les gens
write:lists: créer des listes write:lists: créer des listes
write:media: téléverser des fichiers-média write:media: téléverser des fichiers-média
write:mutes: silencier des gens et des conversations write:mutes: masquer des gens et des conversations
write:notifications: nettoyer vos notifications write:notifications: nettoyer vos notifications
write:reports: rapporter d'autres personnes write:reports: rapporter dautres personnes
write:statuses: publier des statuts write:statuses: publier des statuts

View file

@ -4,7 +4,7 @@ fr:
about_hashtag_html: Figurent ci-dessous les pouets tagués avec <strong>#%{hashtag}</strong>. Vous pouvez interagir avec eux si vous avez un compte nimporte où dans le Fediverse. about_hashtag_html: Figurent ci-dessous les pouets tagués avec <strong>#%{hashtag}</strong>. Vous pouvez interagir avec eux si vous avez un compte nimporte où dans le Fediverse.
about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé. about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé.
about_this: À propos about_this: À propos
administered_by: 'Administré par :' administered_by: 'Administrée par:'
api: API api: API
apps: Applications mobiles apps: Applications mobiles
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. Cependant, vous pouvez trouver une autre instance sur laquelle vous créer un compte et à partir de laquelle vous pourrez accéder au même réseau. closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. Cependant, vous pouvez trouver une autre instance sur laquelle vous créer un compte et à partir de laquelle vous pourrez accéder au même réseau.
@ -20,7 +20,7 @@ fr:
humane_approach_title: Une approche plus humaine humane_approach_title: Une approche plus humaine
not_a_product_body: Mastodon nest pas un réseau commercial. Ici, pas de publicités, pas de prospection de données et pas denvironnements fermés. Il ny existe aucune autorité centrale. not_a_product_body: Mastodon nest pas un réseau commercial. Ici, pas de publicités, pas de prospection de données et pas denvironnements fermés. Il ny existe aucune autorité centrale.
not_a_product_title: Vous êtes une personne, pas un produit not_a_product_title: Vous êtes une personne, pas un produit
real_conversation_body: Avec 500 caractères à votre disposition, une grande granularité en termes de diffusion et la possibilité de masquer vos messages derrières des avertissements, vous êtes libre de vous exprimer de la manière qui vous plaît. real_conversation_body: Avec 500 caractères à votre disposition, une grande granularité en termes de diffusion et la possibilité de masquer vos messages derrière des avertissements, vous êtes libre de vous exprimer de la manière qui vous plaît.
real_conversation_title: Construit pour de vraies conversations real_conversation_title: Construit pour de vraies conversations
within_reach_body: Grâce à lexistence dun environnement API accueillant pour les développeur·se·s, de multiples applications pour iOS, Android et dautres plateformes vous permettent de rester en contact avec vos ami·e·s où que vous soyez. within_reach_body: Grâce à lexistence dun environnement API accueillant pour les développeur·se·s, de multiples applications pour iOS, Android et dautres plateformes vous permettent de rester en contact avec vos ami·e·s où que vous soyez.
within_reach_title: Toujours à portée de main within_reach_title: Toujours à portée de main
@ -37,16 +37,19 @@ fr:
user_count_before: Abrite user_count_before: Abrite
what_is_mastodon: Quest-ce que Mastodon? what_is_mastodon: Quest-ce que Mastodon?
accounts: accounts:
choices_html: 'Sélection de %{name} :' choices_html: "%{name}recommande:"
follow: Suivre follow: Suivre
followers: Abonné⋅e⋅s followers: Abonné⋅e⋅s
following: Abonnements following: Abonnements
joined: Inscrit·e en %{date}
media: Médias media: Médias
moved_html: "%{name} a changé de compte pour %{new_profile_link} :" moved_html: "%{name} a changé de compte pour %{new_profile_link}:"
network_hidden: Cette information nest pas disponible network_hidden: Cette information nest pas disponible
nothing_here: Rien à voir ici! nothing_here: Rien à voir ici!
people_followed_by: Personnes suivies par %{name} people_followed_by: Personnes suivies par %{name}
people_who_follow: Personnes qui suivent %{name} people_who_follow: Personnes qui suivent %{name}
pin_errors:
following: Vous devez être déjà abonné·e à la personne que vous désirez recommander
posts: Statuts posts: Statuts
posts_with_replies: Statuts & réponses posts_with_replies: Statuts & réponses
reserved_username: Ce nom dutilisateur⋅ice est réservé reserved_username: Ce nom dutilisateur⋅ice est réservé
@ -66,7 +69,7 @@ fr:
avatar: Avatar avatar: Avatar
by_domain: Domaine by_domain: Domaine
change_email: change_email:
changed_msg: Courriel du compte modifié avec succès ! changed_msg: Courriel du compte modifié avec succès!
current_email: Courriel actuel current_email: Courriel actuel
label: Modifier le courriel label: Modifier le courriel
new_email: Nouveau courriel new_email: Nouveau courriel
@ -121,11 +124,11 @@ fr:
public: Publique public: Publique
push_subscription_expires: Expiration de labonnement PuSH push_subscription_expires: Expiration de labonnement PuSH
redownload: Rafraîchir les avatars redownload: Rafraîchir les avatars
remove_avatar: Supprimer l'avatar remove_avatar: Supprimer lavatar
resend_confirmation: resend_confirmation:
already_confirmed: Cet utilisateur est déjà confirmé already_confirmed: Cet·te utilisateur·ice est déjà confirmé·e
send: Renvoyer un courriel de confirmation send: Renvoyer un courriel de confirmation
success: Email de confirmation envoyé avec succès ! success: Courriel de confirmation envoyé avec succès!
reset: Réinitialiser reset: Réinitialiser
reset_password: Réinitialiser le mot de passe reset_password: Réinitialiser le mot de passe
resubscribe: Se réabonner resubscribe: Se réabonner
@ -154,35 +157,36 @@ fr:
web: Web web: Web
action_logs: action_logs:
actions: actions:
assigned_to_self_report: "%{name} s'est assigné le signalement de %{target} à eux-même" assigned_to_self_report: "%{name} sest assigné·e le signalement de %{target}"
change_email_user: "%{name} a modifié l'adresse de courriel de l'utilisateur %{target}" change_email_user: "%{name} a modifié ladresse de courriel de lutilisateur·rice %{target}"
confirm_user: "%{name} adresse courriel confirmée de l'utilisateur %{target}" confirm_user: "%{name} adresse courriel confirmée de lutilisateur·ice %{target}"
create_custom_emoji: "%{name} a importé de nouveaux emoji %{target}" create_custom_emoji: "%{name} a importé de nouveaux émojis %{target}"
create_domain_block: "%{name} a bloqué le domaine %{target}" create_domain_block: "%{name} a bloqué le domaine %{target}"
create_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste noire" create_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste noire"
demote_user: "%{name} a rétrogradé l'utilisateur %{target}" demote_user: "%{name} a rétrogradé lutilisateur·ice %{target}"
destroy_domain_block: "%{name} a débloqué le domaine %{target}" destroy_domain_block: "%{name} a débloqué le domaine %{target}"
destroy_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste blanche" destroy_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste blanche"
destroy_status: "%{name} a enlevé le statut de %{target}" destroy_status: "%{name} a enlevé le statut de %{target}"
disable_2fa_user: "%{name} a désactivé l'authentification à deux facteurs pour l'utilisateur %{target}" disable_2fa_user: "%{name} a désactivé lauthentification à deux facteurs pour lutilisateur·ice %{target}"
disable_custom_emoji: "%{name} a désactivé l'emoji %{target}" disable_custom_emoji: "%{name} a désactivé l’émoji %{target}"
disable_user: "%{name} a désactivé le login pour l'utilisateur %{target}" disable_user: "%{name} a désactivé le login pour lutilisateur·ice %{target}"
enable_custom_emoji: "%{name} a activé l'emoji %{target}" enable_custom_emoji: "%{name} a activé l’émoji %{target}"
enable_user: "%{name} a activé le login pour l'utilisateur %{target}" enable_user: "%{name} a activé le login pour lutilisateur·ice %{target}"
memorialize_account: "%{name} a transformé le compte de %{target} en une page de mémorial" memorialize_account: "%{name} a transformé le compte de %{target} en une page de mémorial"
promote_user: "%{name} a promu l'utilisateur %{target}" promote_user: "%{name} a promu lutilisateur·ice %{target}"
remove_avatar_user: "%{name} a supprimé l'avatar de %{target}'s" remove_avatar_user: "%{name} a supprimé lavatar de %{target}"
reopen_report: "%{name} a ré-ouvert le signalement %{target}" reopen_report: "%{name} a rouvert le signalement %{target}"
reset_password_user: "%{name} a réinitialisé le mot de passe de %{target}" reset_password_user: "%{name} a réinitialisé le mot de passe de %{target}"
resolve_report: "%{name} a résolu la dénonciation de %{target}" resolve_report: "%{name} a résolu la dénonciation de %{target}"
silence_account: "%{name} a mis le compte %{target} en mode silence" silence_account: "%{name} a mis le compte %{target} en mode silence"
suspend_account: "%{name} a suspendu le compte %{target}" suspend_account: "%{name} a suspendu le compte %{target}"
unassigned_report: "%{name} a dés-assigné le signalement %{target}" unassigned_report: "%{name} a désassigné le signalement %{target}"
unsilence_account: "%{name} a mis fin au mode silence de %{target}" unsilence_account: "%{name} a mis fin au mode silence de %{target}"
unsuspend_account: "%{name} a réactivé le compte de %{target}" unsuspend_account: "%{name} a réactivé le compte de %{target}"
update_custom_emoji: "%{name} a mis à jour l'emoji %{target}" update_custom_emoji: "%{name} a mis à jour l’émoji %{target}"
update_status: "%{name} a mis à jour le statut de %{target}" update_status: "%{name} a mis à jour le statut de %{target}"
title: Journal d'audit deleted_status: "(statut supprimé)"
title: Journal daudit
custom_emojis: custom_emojis:
by_domain: Domaine by_domain: Domaine
copied_msg: Copie locale de lémoji créée avec succès! copied_msg: Copie locale de lémoji créée avec succès!
@ -203,29 +207,32 @@ fr:
overwrite: Réécrire overwrite: Réécrire
shortcode: Raccourci shortcode: Raccourci
shortcode_hint: Au moins deux caractères, seulement des caractères alphanumériques ou des tirets bas shortcode_hint: Au moins deux caractères, seulement des caractères alphanumériques ou des tirets bas
title: Émoji personnalisés title: Émojis personnalisés
unlisted: Délisté unlisted: Délisté
update_failed_msg: N'a pas pu mettre à jour cet emoji update_failed_msg: Na pas pu mettre à jour cet émoji
updated_msg: Emoji mis à jour avec succès ! updated_msg: Émoji mis à jour avec succès!
upload: Téléverser upload: Téléverser
dashboard: dashboard:
backlog: tâches en attente
config: Configuration config: Configuration
feature_invites: Liens d'invitation feature_deletions: Suppressions de comptes
feature_invites: Liens dinvitation
feature_registrations: Inscriptions feature_registrations: Inscriptions
feature_relay: Relais de fédération feature_relay: Relais de fédération
features: Fonctionnalités features: Fonctionnalités
hidden_service: Fédération avec des services cachés hidden_service: Fédération avec des services cachés
recent_users: Utilisateurs récents open_reports: signalements non résolus
search: Recherche texte-plein recent_users: Utilisateur·rice·s récent·e·s
single_user_mode: Mode utilisateur unique search: Recherche plein texte
single_user_mode: Mode utilisateur·ice unique
software: Logiciel software: Logiciel
space: Utilisation d'espace space: Espace utilisé
title: Tableau de bord title: Tableau de bord
total_users: utilisateurs au total total_users: utilisateur·rice·s au total
trends: Tendances trends: Tendances
week_interactions: interactions cette semaine week_interactions: interactions cette semaine
week_users_active: actif cette semaine week_users_active: actif·ve·s cette semaine
week_users_new: utilisateurs cette semaine week_users_new: utilisateur·rice·s cette semaine
domain_blocks: domain_blocks:
add_new: Ajouter add_new: Ajouter
created_msg: Le blocage de domaine est désormais activé created_msg: Le blocage de domaine est désormais activé
@ -284,20 +291,22 @@ fr:
title: Invitations title: Invitations
relays: relays:
add_new: Ajouter un nouveau relais add_new: Ajouter un nouveau relais
description_html: Un <strong>relai de fédération</strong> est un serveur intermédiaire qui échange de grandes quantités de pouets entre les serveurs qui publient dessus et ceux qui y sont abonnés. <strong>Il peut aider les petites et moyennes instances à découvrir du contenu sur le fediverse</strong>, ce qui normalement nécessiterait que les membres locaux suivent des gens inscrits sur des serveurs distants.
enable_hint: Une fois activé, votre serveur souscrira à tous les pouets publics présents sur ce relais et y enverra ses propres pouets publics.
inbox_url: URL de relais inbox_url: URL de relais
setup: Paramétrer une connexion de relais setup: Paramétrer une connexion de relais
status: Statut status: Statut
title: Relais title: Relais
report_notes: report_notes:
created_msg: Note de signalement créée avec succès ! created_msg: Note de signalement créée avec succès!
destroyed_msg: Note de signalement effacée avec succès ! destroyed_msg: Note de signalement effacée avec succès!
reports: reports:
account: account:
note: note note: note
report: signaler report: signaler
action_taken_by: Intervention de action_taken_by: Intervention de
are_you_sure: Êtes vous certain⋅e? are_you_sure: Êtes vous certain⋅e?
assign_to_self: Me l'assigner assign_to_self: Me lassigner
assigned: Modérateur assigné assigned: Modérateur assigné
comment: comment:
none: Aucun none: Aucun
@ -317,7 +326,7 @@ fr:
reported_account: Compte signalé reported_account: Compte signalé
reported_by: Signalé par reported_by: Signalé par
resolved: Résolus resolved: Résolus
resolved_msg: Signalement résolu avec succès ! resolved_msg: Signalement résolu avec succès!
silence_account: Masquer le compte silence_account: Masquer le compte
status: Statut status: Statut
suspend_account: Suspendre le compte suspend_account: Suspendre le compte
@ -329,20 +338,23 @@ fr:
view: Voir view: Voir
settings: settings:
activity_api_enabled: activity_api_enabled:
desc_html: Nombre de statuts affichés localement, d'utilisateurs actifs et de nouveaux enregistrements dans les registres hebdomadaires desc_html: Nombre de statuts affichés localement, dutilisateur·ice·s actif·ve·s et de nouveaux enregistrements dans les registres hebdomadaires
title: Publier des statistiques agrégées sur l'activité des utilisateurs title: Publier des statistiques agrégées sur lactivité des utilisateur·ice·s
bootstrap_timeline_accounts: bootstrap_timeline_accounts:
desc_html: Séparez les noms dutilisateur·ice par des virgules. Ne fonctionne quavec des comptes locaux et non-verrouillés. Si laissé vide, tous les administrateur⋅ice⋅s locaux sont sélectionné⋅e⋅s. desc_html: Séparez les noms dutilisateur·ice par des virgules. Ne fonctionne quavec des comptes locaux et non verrouillés. Si laissé vide, tous les administrateur⋅ice⋅s locaux sont sélectionné⋅e⋅s.
title: Abonnements par défaut pour les nouveaux·elles utilisateur·ice·s title: Abonnements par défaut pour les nouveaux·elles utilisateur·ice·s
contact_information: contact_information:
email: Entrez une adresse courriel publique email: Entrez une adresse courriel publique
username: Entrez un nom dutilisateur⋅ice username: Entrez un nom dutilisateur⋅ice
hero: hero:
desc_html: Affichée sur la page d'accueil. Au moins 600x100px recommandé. Lorsqu'elle n'est pas définie, se rabat sur la vignette de l'instance desc_html: Affichée sur la page daccueil. Au moins 600x100px recommandé. Lorsquelle nest pas définie, se rabat sur la vignette de linstance
title: Image d'en-tête title: Image den-tête
peers_api_enabled: peers_api_enabled:
desc_html: Noms des domaines que cette instance a découvert dans le fediverse desc_html: Noms des domaines que cette instance a découvert dans le fediverse
title: Publier la liste des instances découvertes title: Publier la liste des instances découvertes
preview_sensitive_media:
desc_html: Les liens de prévisualisation sur les autres sites web afficheront une vignette même si le média est sensible
title: Afficher les médias sensibles dans les prévisualisations OpenGraph
registrations: registrations:
closed_message: closed_message:
desc_html: Affiché sur la page daccueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML desc_html: Affiché sur la page daccueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML
@ -357,19 +369,20 @@ fr:
desc_html: Autoriser tout le monde à créer un compte desc_html: Autoriser tout le monde à créer un compte
title: Ouvrir les inscriptions title: Ouvrir les inscriptions
show_known_fediverse_at_about_page: show_known_fediverse_at_about_page:
desc_html: Lorsque l'option est activée, les pouets provenant de toutes les instances connues sont affichés dans la prévisualisation. Si non, seuls les pouets locaux sont affichés. desc_html: Lorsque loption est activée, les pouets provenant de toutes les instances connues sont affichés dans la prévisualisation. Sinon, seuls les pouets locaux sont affichés.
title: Afficher le fediverse connu dans la prévisualisation du fil title: Afficher le fediverse connu dans la prévisualisation du fil
show_staff_badge: show_staff_badge:
desc_html: Montrer un badge de responsable sur une page utilisateur desc_html: Montrer un badge de responsable sur une page utilisateur·ice
title: Montrer un badge de responsable title: Montrer un badge de responsable
site_description: site_description:
desc_html: Paragraphe introductif sur la page d'accueil. Décrivez ce qui rend spécifique ce serveur Mastodon et toute autre chose importante. Vous pouvez utiliser des balises HTML, en particulier <code>&lt;a&gt;</code> et <code>&lt;em&gt;</code>. desc_html: Paragraphe introductif sur la page daccueil. Décrivez ce qui rend spécifique ce serveur Mastodon et toute autre chose importante. Vous pouvez utiliser des balises HTML, en particulier <code>&lt;a&gt;</code> et <code>&lt;em&gt;</code>.
title: Description du site title: Description du site
site_description_extended: site_description_extended:
desc_html: Affichée sur la page dinformations complémentaires du site<br>Vous pouvez utiliser des balises HTML desc_html: Affichée sur la page dinformations complémentaires du site<br>Vous pouvez utiliser des balises HTML
title: Description étendue du site title: Description étendue du site
site_short_description: site_short_description:
title: Description courte de l'instance desc_html: Affichée dans la barre latérale et dans les méta-tags. Décrivez ce qui rend spécifique ce serveur Mastodon en un seul paragraphe. Si laissée vide, la description de linstance sera affiché par défaut.
title: Description courte de linstance
site_terms: site_terms:
desc_html: Affichée sur la page des conditions dutilisation du site<br>Vous pouvez utiliser des balises HTML desc_html: Affichée sur la page des conditions dutilisation du site<br>Vous pouvez utiliser des balises HTML
title: Politique de confidentialité title: Politique de confidentialité
@ -391,6 +404,7 @@ fr:
media: media:
title: Médias title: Médias
no_media: Aucun média no_media: Aucun média
no_status_selected: Aucun statut na été modifié car aucun na été sélectionné
title: État du compte title: État du compte
with_media: avec médias with_media: avec médias
subscriptions: subscriptions:
@ -404,13 +418,13 @@ fr:
admin_mailer: admin_mailer:
new_report: new_report:
body: "%{reporter} a signalé %{target}" body: "%{reporter} a signalé %{target}"
body_remote: Quelqu'un de %{domain} a signalé %{target} body_remote: Quelquun de %{domain} a signalé %{target}
subject: Nouveau signalement sur %{instance} (#%{id}) subject: Nouveau signalement sur %{instance} (#%{id})
application_mailer: application_mailer:
notification_preferences: Modifier les préférences de courriel notification_preferences: Modifier les préférences de courriel
salutation: "%{name}," salutation: "%{name},"
settings: 'Changer les préférences courriel: %{link}' settings: 'Changer les préférences courriel: %{link}'
view: 'Voir :' view: 'Voir:'
view_profile: Voir le profil view_profile: Voir le profil
view_status: Afficher le statut view_status: Afficher le statut
applications: applications:
@ -440,7 +454,7 @@ fr:
cas: CAS cas: CAS
saml: SAML saml: SAML
register: Sinscrire register: Sinscrire
register_elsewhere: S'inscrire sur un autre serveur register_elsewhere: Sinscrire sur un autre serveur
resend_confirmation: Envoyer à nouveau les consignes de confirmation resend_confirmation: Envoyer à nouveau les consignes de confirmation
reset_password: Réinitialiser le mot de passe reset_password: Réinitialiser le mot de passe
security: Sécurité security: Sécurité
@ -449,8 +463,8 @@ fr:
already_following: Vous suivez déjà ce compte already_following: Vous suivez déjà ce compte
error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant
follow: Suivre follow: Suivre
follow_request: 'Vous avez demandé à suivre :' follow_request: 'Vous avez demandé à suivre:'
following: 'Youpi! Vous suivez :' following: 'Youpi! Vous suivez:'
post_follow: post_follow:
close: Ou bien, vous pouvez fermer cette fenêtre. close: Ou bien, vous pouvez fermer cette fenêtre.
return: Afficher le profil de lutilisateur⋅ice return: Afficher le profil de lutilisateur⋅ice
@ -476,7 +490,7 @@ fr:
description_html: Cela va supprimer votre compte et le désactiver de manière <strong>permanente et irréversible</strong>. Votre nom dutilisateur⋅ice restera réservé afin déviter la confusion. description_html: Cela va supprimer votre compte et le désactiver de manière <strong>permanente et irréversible</strong>. Votre nom dutilisateur⋅ice restera réservé afin déviter la confusion.
proceed: Supprimer compte proceed: Supprimer compte
success_msg: Votre compte a été supprimé avec succès success_msg: Votre compte a été supprimé avec succès
warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-lignes ainsi que ceux nétant plus abonnés à vos publications ne mettront pas leur base de données à jour. warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-ligne ainsi que ceux nétant plus abonnés à vos publications ne mettront pas leur base de données à jour.
warning_title: Disponibilité du contenu disséminé warning_title: Disponibilité du contenu disséminé
errors: errors:
'403': Vous navez pas accès à cette page. '403': Vous navez pas accès à cette page.
@ -489,13 +503,13 @@ fr:
'500': '500':
content: Nous sommes désolé·e·s, mais quelque chose sest mal passé de notre côté. content: Nous sommes désolé·e·s, mais quelque chose sest mal passé de notre côté.
title: Cette page nest pas correcte title: Cette page nest pas correcte
noscript_html: Pour utiliser Mastodon, veuillez activer JavaScript. Sinon, essayez l'une des <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">applications natives</a> pour Mastodon pour votre plate-forme. noscript_html: Pour utiliser Mastodon, veuillez activer JavaScript. Sinon, essayez lune des <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">applications natives</a> pour Mastodon pour votre plate-forme.
exports: exports:
archive_takeout: archive_takeout:
date: Date date: Date
download: Télécharger votre archive download: Télécharger votre archive
hint_html: Vous pouvez demander une archive de vos <strong>pouets et médias téléversés</strong>. Les données exportées seront au format ActivityPub, lisible par tout logiciel compatible. Vous pouvez demander une archive tous les 7 jours. hint_html: Vous pouvez demander une archive de vos <strong>pouets et médias téléversés</strong>. Les données exportées seront au format ActivityPub, lisible par tout logiciel compatible. Vous pouvez demander une archive tous les 7 jours.
in_progress: Élaboration de votre archive.... in_progress: Création de votre archive…
request: Demandez vos archives request: Demandez vos archives
size: Taille size: Taille
blocks: Vous bloquez blocks: Vous bloquez
@ -505,12 +519,15 @@ fr:
storage: Médias stockés storage: Médias stockés
filters: filters:
contexts: contexts:
home: Ligne de temps personnelle home: Accueil
notifications: Notifications notifications: Notifications
public: Lignes de temps public public: Fils publics
thread: Conversations thread: Conversations
edit: edit:
title: Filtre d'édition title: Éditer le filtre
errors:
invalid_context: Contexte invalide ou insuffisant
invalid_irreversible: Le filtrage irréversible ne fonctionne que pour laccueil et les notifications
index: index:
delete: Effacer delete: Effacer
title: Filtres title: Filtres
@ -559,7 +576,7 @@ fr:
'86400': 1 jour '86400': 1 jour
expires_in_prompt: Jamais expires_in_prompt: Jamais
generate: Générer generate: Générer
invited_by: 'Vous avez été invité par :' invited_by: 'Vous avez été invité·e par:'
max_uses: max_uses:
one: 1 usage one: 1 usage
other: "%{count} usages" other: "%{count} usages"
@ -577,26 +594,26 @@ fr:
images_and_video: Impossible de joindre une vidéo à un statut contenant déjà des images images_and_video: Impossible de joindre une vidéo à un statut contenant déjà des images
too_many: Impossible de joindre plus de 4 fichiers too_many: Impossible de joindre plus de 4 fichiers
migrations: migrations:
acct: utilisateur@domaine du nouveau compte acct: profil@domaine du nouveau compte
currently_redirecting: 'Votre profile va être redirigé vers :' currently_redirecting: 'Votre profile va être redirigé vers:'
proceed: Enregistrer proceed: Enregistrer
updated_msg: Les paramètres de votre migration de compte ont été mis à jour avec succès ! updated_msg: Les paramètres de votre migration de compte ont été mis à jour avec succès!
moderation: moderation:
title: Modération title: Modération
notification_mailer: notification_mailer:
digest: digest:
action: Voir toutes les notifications action: Voir toutes les notifications
body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite le %{since} :' body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite le %{since}:'
mention: "%{name} vous a mentionné⋅e dans :" mention: "%{name} vous a mentionné⋅e dans:"
new_followers_summary: new_followers_summary:
one: Vous avez un⋅e nouvel⋅le abonné⋅e! Youpi! one: Vous avez un⋅e nouvel⋅le abonné⋅e! Youpi!
other: Vous avez %{count} nouveaux⋅elles abonné⋅e·s! Incroyable! other: Vous avez %{count} nouveaux⋅elles abonné⋅e·s! Incroyable!
subject: subject:
one: "Une nouvelle notification depuis votre dernière visite \U0001F418" one: "Une nouvelle notification depuis votre dernière visite \U0001F418"
other: "%{count} nouvelles notifications depuis votre dernière visite \U0001F418" other: "%{count} nouvelles notifications depuis votre dernière visite \U0001F418"
title: Pendant votre absence... title: Pendant votre absence
favourite: favourite:
body: "%{name} a ajouté votre post à ses favoris :" body: "%{name} a ajouté votre post à ses favoris:"
subject: "%{name} a ajouté votre post à ses favoris" subject: "%{name} a ajouté votre post à ses favoris"
title: Nouveau favori title: Nouveau favori
follow: follow:
@ -604,17 +621,17 @@ fr:
subject: "%{name} vous suit" subject: "%{name} vous suit"
title: Nouvel·le abonné·e title: Nouvel·le abonné·e
follow_request: follow_request:
action: Gérer les demandes d'abonnement action: Gérer les demandes dabonnement
body: "%{name} a demandé à vous suivre" body: "%{name} a demandé à vous suivre"
subject: 'Abonné⋅es en attente: %{name}' subject: 'Abonné⋅es en attente: %{name}'
title: Nouvelle demande d'abonnement title: Nouvelle demande dabonnement
mention: mention:
action: Répondre action: Répondre
body: "%{name} vous a mentionné⋅e dans :" body: "%{name} vous a mentionné⋅e dans:"
subject: "%{name} vous a mentionné·e" subject: "%{name} vous a mentionné·e"
title: Nouvelle mention title: Nouvelle mention
reblog: reblog:
body: "%{name} a partagé votre statut :" body: "%{name} a partagé votre statut:"
subject: "%{name} a partagé votre statut" subject: "%{name} a partagé votre statut"
title: Nouveau partage title: Nouveau partage
number: number:
@ -640,10 +657,14 @@ fr:
publishing: Publication publishing: Publication
web: Web web: Web
remote_follow: remote_follow:
acct: Entrez votre pseudo@instance depuis lequel vous voulez suivre cet·te utilisateur⋅ice acct: Entrez ladresse profil@instance depuis laquelle vous voulez vous abonner
missing_resource: LURL de redirection na pas pu être trouvée missing_resource: LURL de redirection na pas pu être trouvée
proceed: Continuez pour suivre no_account_html: Vous navez pas de compte? Vous pouvez <a href='%{sign_up_path}' target='_blank'>vous inscrire ici</a>
prompt: 'Vous allez suivre :' proceed: Confirmer labonnement
prompt: 'Vous allez suivre:'
remote_interaction:
proceed: Confirmer linteraction
prompt: 'Vous désirez interagir avec ce pouet:'
remote_unfollow: remote_unfollow:
error: Erreur error: Erreur
title: Titre title: Titre
@ -706,7 +727,7 @@ fr:
your_apps: Vos applications your_apps: Vos applications
statuses: statuses:
attached: attached:
description: 'Attaché : %{attached}' description: 'Attaché: %{attached}'
image: image:
one: "%{count} image" one: "%{count} image"
other: "%{count} images" other: "%{count} images"
@ -714,10 +735,10 @@ fr:
one: "%{count} vidéo" one: "%{count} vidéo"
other: "%{count} vidéos" other: "%{count} vidéos"
boosted_from_html: Repartagé depuis %{acct_link} boosted_from_html: Repartagé depuis %{acct_link}
content_warning: 'Attention au contenu : %{warning}' content_warning: 'Avertissement sur le contenu: %{warning}'
disallowed_hashtags: disallowed_hashtags:
one: 'contient un hashtag désactivé : %{tags}' one: 'contient un hashtag désactivé: %{tags}'
other: 'contient les hashtag désactivés : %{tags}' other: 'contient les hashtags désactivés: %{tags}'
language_detection: Détecter automatiquement la langue language_detection: Détecter automatiquement la langue
open_in_web: Ouvrir sur le web open_in_web: Ouvrir sur le web
over_character_limit: limite de caractères dépassée de %{max} caractères over_character_limit: limite de caractères dépassée de %{max} caractères
@ -727,6 +748,7 @@ fr:
private: Les statuts non-publics ne peuvent pas être épinglés private: Les statuts non-publics ne peuvent pas être épinglés
reblog: Un partage ne peut pas être épinglé reblog: Un partage ne peut pas être épinglé
show_more: Afficher plus show_more: Afficher plus
sign_in_to_participate: Inscrivez-vous pour prendre part à la conversation
title: '%{name}: "%{quote}"' title: '%{name}: "%{quote}"'
visibilities: visibilities:
private: Abonné⋅e⋅s uniquement private: Abonné⋅e⋅s uniquement
@ -740,7 +762,88 @@ fr:
reblogged: a partagé reblogged: a partagé
sensitive_content: Contenu sensible sensitive_content: Contenu sensible
terms: terms:
title: "%{instance} Conditions dutilisations et politique de confidentialité" body_html: |
<h2>Politique de confidentialité</h2>
<h3 id="collect">Quelles informations collectons-nous?</h3>
<ul>
<li><em>Informations de base sur votre compte</em>: Si vous vous inscrivez sur ce serveur, il vous sera demandé de rentrer un identifiant, une adresse électronique et un mot de passe. Vous pourrez également ajouter des informations additionnelles sur votre profil, telles quun nom public et une biographie, ainsi que téléverser une image de profil et une image den-tête. Vos identifiant, nom public, biographie, image de profil et image den-tête seront toujours affichés publiquement.</li>
<li><em>Posts, liste dabonnements et autres informations publiques</em>: La liste de vos abonnements ainsi que la liste de vos abonné·e·s sont publiques. Quand vous postez un message, la date et lheure denvoi ainsi que le nom de lapplication utilisée pour sa transmission sont enregistré·e·s. Des médias, tels que des images ou des vidéos, peuvent être joints aux messages. Les posts publics et non listés sont affichés publiquement. Quand vous mettez en avant un post sur votre profil, ce post est également affiché publiquement. Vos messages sont délivrés à vos abonné·e·s, ce qui, dans certains cas, signifie quils sont délivrés à des serveurs tiers et que ces derniers en stockent une copie. Quand vous supprimer un post, il est probable que vos abonné·e·s en soient informé·e·s. Partager un message ou le marquer comme favori est toujours une action publique.</li>
<li><em>Posts directs et abonné·e·s uniquement</em>: Tous les posts sont stockés et traités par le serveur. Les messages abonné·e·s uniquement ne sont transmis quà vos abonné·e·s et aux personnes mentionnées dans le corps du message, tandis que les messages directs ne sont transmis quaux personnes mentionnées. Dans certains cas, cela signifie quils sont délivrés à des serveurs tiers et que ces derniers en stockent une copie. Nous faisons un effort de bonne fois pour en limiter laccès uniquement aux personnes autorisées, mais ce nest pas nécessairement le cas des autres serveurs. Il est donc très important que vous vérifiiez les serveurs auxquels appartiennent vos abonné·e·s. Il vous est possible dactiver une option dans les paramètres afin dapprouver et de rejeter manuellement les nouveaux·lles abonné·e·s. <em>Gardez sil-vous-plaît en mémoire que les opérateur·rice·s du serveur ainsi que celles et ceux de nimporte quel serveur récepteur peuvent voir ces messages</em> et quil est possible pour les destinataires de faire des captures décran, de copier et plus généralement de repartager ces messages. <em>Ne partager aucune information sensible à laide de Mastodon.</em></li>
<li><em>IP et autres métadonnées</em>: Quand vous vous connectez, nous enregistrons votre adresse IP ainsi que le nom de votre navigateur web. Toutes les sessions enregistrées peuvent être consultées dans les paramètres, afin que vous puissiez les surveiller et éventuellement les révoquer. La dernière adresse IP utilisée est conservée pour une durée de 12 mois. Nous sommes également susceptibles de conserver les journaux du serveur, ce qui inclut ladresse IP de chaque requête reçue.</li>
</ul>
<hr class="spacer" />
<h3 id="use">Que faisons-nous des informations que nous collectons?</h3>
<p>Toutes les informations que nous collectons sur vous peuvent être utilisées dune des manières suivantes:</p>
<ul>
<li>Pour vous fournir les fonctionnalités de base de Mastodon. Vous ne pouvez interagir avec le contenu des autres et poster votre propre contenu que lorsque vous êtes connecté·e. Par exemple, vous pouvez vous abonner à plusieurs autres comptes pour voir lensemble de leurs posts dans votre fil daccueil personnalisé.</li>
<li>Pour aider à la modération de la communauté, par exemple, comparer votre adresse IP à dautres afin de déterminer si un bannissement a été contourné ou si une autre violation aux règles a été commise.</li>
<li>Ladresse électronique que vous nous avez fournie peut être utilisée pour vous envoyez des informations, des notifications lorsque dautres personnes interagissent avec votre contenu ou vous envoient des messages, pour répondre à des demandes de votre part ainsi que pour tout autres requêtes ou questions.</li>
</ul>
<hr class="spacer" />
<h3 id="protect">Comment protégeons-nous vos informations?</h3>
<p>Nous mettons en œuvre une variété de mesures de sécurité afin de garantir la sécurité de vos informations personnelles quand vous les saisissez, les soumettez et les consultez. Entre autres choses, votre session de navigation ainsi que le trafic entre votre application et lAPI sont sécurisés à laide de TLS tandis que votre mot de passe est haché en utilisant un puissant algorithme à sens unique. Vous pouvez également activer lauthentification à deux facteurs pour sécuriser encore plus laccès à votre compte.</p>
<hr class="spacer" />
<h3 id="data-retention">Quelle est notre politique de conservation des données?</h3>
<p>Nous ferons un effort de bonne foi:</p>
<ul>
<li>Pour ne pas conserver plus de 90 jours les journaux systèmes contenant les adresses IP de toutes les requêtes reçues par ce serveur.</li>
<li>Pour ne pas conserver plus de 12 mois les adresses IP associées aux utilisateur·ice·s enregistré·e·s.</li>
</ul>
<p>Vous pouvez demander une archive de votre contenu, incluant vos posts, vos médias joints, votre image de profil et votre image den-tête.</p>
<p>Vous pouvez, à nimporte quel moment, supprimer votre compte de manière définitive.</p>
<hr class="spacer"/>
<h3 id="cookies">Utilisons-nous des témoins de connexion?</h3>
<p>Oui. Les témoins de connexion sont de petits fichiers quun site ou un service transféres sur le disque dur de votre ordinateur via votre navigateur web (si vous lavez autorisé). Ces témoins permettent au site de reconnaître votre navigateur et de, dans le cas où vous possédez un compte, de vous associer avec ce dernier.</p>
<p>Nous utilisons les témoins de connexion comme un moyen de comprendre et de nous souvenir de vos préférences pour vos prochaines visites.</p>
<hr class="spacer" />
<h3 id="disclose">Divulguons-nous des informations à des tierces parties?</h3>
<p>Nous ne vendons, néchangeons ou ne transférons dune quelque manière que soit des informations permettant de vous identifier personnellement. Cela ninclut pas les tierces parties de confiance qui nous aident à opérer ce site, à conduire nos activités commerciales ou à vous servir, tant quelles acceptent de garder ces informations confidentielles. Nous sommes également susceptibles de partager vos informations quand nous pensons que cest nécessaire pour nous conformer à la loi, pour appliquer les politiques de notre site ainsi que pour défendre nos droits, notre propriété, notre sécurité et celles et ceux dautres personnes.</p>
<p>Votre contenu public peut être téléchargé par dautres serveurs du réseau. Dans le cas où vos abonné·e·s et vos destinataires résideraient sur des serveurs différents du vôtre, vos posts publics et abonné·e·s uniquement peuvent être délivrés vers les serveurs de vos abonné·e·s tandis que vos messages directs sont délivrés aux serveurs de vos destinataires.</p>
<p>Quand vous autorisez une application à utiliser votre compte, en fonction de létendue des permissions que vous approuvez, il est possible quelle puisse accéder aux informations publiques de votre profil, votre liste dabonnements, votre liste dabonné·e·s, vos listes, tout vos posts et vos favoris. Les applications ne peuvent en aucun cas accéder à votre adresse électronique et à votre mot de passe.</p>
<hr class="spacer" />
<h3 id="children">Utilisation de ce site par les enfants</h3>
<p>Si ce serveur est situé dans dans lUE ou lEEE: Notre site, produits et services sont tous destinés à des personnes âgées de 16 ans ou plus. Si vous avez moins de 16 ans, en application du RGPD (<a href="https://fr.wikipedia.org/wiki/R%C3%A8glement_g%C3%A9n%C3%A9ral_sur_la_protection_des_donn%C3%A9es">Règlement Général sur la Protection des Données</a>), merci de ne pas utiliser ce site.</p>
<p>Si ce serveur est situé dans aux États-Unis dAmérique: Notre site, produits et services sont tous destinés à des personnes âgées de 13 ans ou plus. Si vous avez moins de 13 ans, en application du COPPA (<a href="https://fr.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>), merci de ne pas utiliser ce site.</p>
<p>Les exigences légales peuvent être différentes si ce serveur se trouve dans une autre juridiction.</p>
<hr class="spacer" />
<h3 id="changes">Modifications de notre politique de confidentialité</h3>
<p>Dans le cas où nous déciderions de changer notre politique de confidentialité, nous posterons les modifications sur cette page.</p>
<p>Ce document est publié sous lincence CC-BY-SA. Il a été mis à jours pour la dernière fois le 7 mars 2018.</p>
<p>Originellement adapté de la <a href="https://github.com/discourse/discourse">politique de confidentialité de Discourse</a>.</p>
title: "%{instance} Conditions dutilisation et politique de confidentialité"
themes: themes:
contrast: Contraste élevé contrast: Contraste élevé
default: Mastodon default: Mastodon
@ -758,8 +861,8 @@ fr:
enabled_success: Identification à deux facteurs activée avec succès enabled_success: Identification à deux facteurs activée avec succès
generate_recovery_codes: Générer les codes de récupération generate_recovery_codes: Générer les codes de récupération
instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application génèrera des jetons que vous devrez saisir à chaque connexion." instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application génèrera des jetons que vous devrez saisir à chaque connexion."
lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre comptre si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés. lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre compte si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés.
manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez lentrer manuellement, voici le secret en clair :' manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez lentrer manuellement, voici le secret en clair:'
recovery_codes: Codes de récupération recovery_codes: Codes de récupération
recovery_codes_regenerated: Codes de récupération régénérés avec succès recovery_codes_regenerated: Codes de récupération régénérés avec succès
recovery_instructions_html: Si vous perdez laccès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour retrouver laccès à votre compte. <strong>Conservez les codes de récupération en sécurité</strong>. Par exemple, en les imprimant et en les stockant avec vos autres documents importants. recovery_instructions_html: Si vous perdez laccès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour retrouver laccès à votre compte. <strong>Conservez les codes de récupération en sécurité</strong>. Par exemple, en les imprimant et en les stockant avec vos autres documents importants.
@ -767,30 +870,30 @@ fr:
wrong_code: Les codes entrés sont incorrects! Lheure du serveur et celle de votre appareil sont-elles correctes? wrong_code: Les codes entrés sont incorrects! Lheure du serveur et celle de votre appareil sont-elles correctes?
user_mailer: user_mailer:
backup_ready: backup_ready:
explanation: Vous avez demandé une sauvegarde complète de votre compte Mastodon. Elle est maintenant prête à être téléchargée ! explanation: Vous avez demandé une sauvegarde complète de votre compte Mastodon. Elle est maintenant prête à être téléchargée!
subject: Votre archive est prête à être téléchargée subject: Votre archive est prête à être téléchargée
title: Retrait de l'archive title: Récupération de larchive
welcome: welcome:
edit_profile_action: Configuration du profil edit_profile_action: Configuration du profil
edit_profile_step: Vous pouvez personnaliser votre profil en téléchargeant un avatar, une image d'en-tête, en changeant votre pseudo et plus encore. Si vous souhaitez examiner les nouveaux abonnés avant qu'ils ne soient autorisés à vous suivre, vous pouvez verrouiller votre compte. edit_profile_step: Vous pouvez personnaliser votre profil en téléchargeant un avatar, une image den-tête, en changeant votre pseudo et plus encore. Si vous souhaitez examiner les nouveaux·lles abonné·e·s avant quil·elle·s ne soient autorisé·e·s à vous suivre, vous pouvez verrouiller votre compte.
explanation: Voici quelques conseils pour vous aider à démarrer explanation: Voici quelques conseils pour vous aider à démarrer
final_action: Commencer à publier final_action: Commencer à publier
final_step: 'Commencez à poster ! Même sans abonné·es, vos messages publics peuvent être vus par d''autres, par exemple sur la chronologie locale et dans les hashtags. Vous pouvez vous présenter sur le hashtag #introductions.' final_step: 'Commencez à poster! Même sans abonné·e·s, vos messages publics peuvent être vus par dautres, par exemple sur le fil public local et dans les hashtags. Vous pouvez vous présenter sur le hashtag #introductions.'
full_handle: Votre pleine maîtrise full_handle: Votre identifiant complet
full_handle_hint: C'est ce que vous diriez à vos amis pour qu'ils puissent vous envoyer un message ou vous suivre à partir d'une autre instance. full_handle_hint: Cest ce que vous diriez à vos ami·e·s pour quil·elle·s puissent vous envoyer un message ou vous suivre à partir dune autre instance.
review_preferences_action: Modifier les préférences review_preferences_action: Modifier les préférences
review_preferences_step: Assurez-vous de définir vos préférences, telles que les courriels que vous aimeriez recevoir ou le niveau de confidentialité auquel vous aimeriez que vos messages soient soumis par défaut. Si vous n'avez pas le mal des transports, vous pouvez choisir d'activer la lecture automatique GIF. review_preferences_step: Assurez-vous de définir vos préférences, telles que les courriels que vous aimeriez recevoir ou le niveau de confidentialité auquel vous aimeriez que vos messages soient soumis par défaut. Si vous navez pas le mal des transports, vous pouvez choisir dactiver la lecture automatique des GIF.
subject: Bienvenue sur Mastodon subject: Bienvenue sur Mastodon
tip_bridge_html: Si vous venez de Twitter, vous pouvez retrouver vos amis sur Mastodon en utilisant le <a href="%{bridge_url}">bridge app</a>. Cela ne fonctionne que s'ils ont aussi utilisé cette application ! tip_bridge_html: Si vous venez de Twitter, vous pouvez retrouver vos ami·e·s sur Mastodon en utilisant l<a href="%{bridge_url}">application de mise en relation</a>. Cela ne fonctionne que sil·elle·s ont aussi utilisé cette application!
tip_federated_timeline: La chronologie fédérée est une vue en direct du réseau Mastodon. Mais elle n'inclut que les personnes auxquelles vos voisin·es sont abonné·es, donc elle n'est pas complète. tip_federated_timeline: La fil public global est une vue en direct du réseau Mastodon. Mais elle ninclut que les personnes auxquelles vos voisin·es sont abonné·e·s, donc elle nest pas complète.
tip_following: Vous suivez les administrateurs et administratrices de votre serveur par défaut. Pour trouver d'autres personnes intéressantes, consultez les chronologies locales et fédérées. tip_following: Vous suivez les administrateur·rice·s de votre serveur par défaut. Pour trouver dautres personnes intéressantes, consultez les fils publics local et global.
tip_local_timeline: La chronologie locale est une vue des personnes sur %{instance}. Ce sont vos voisines et voisins immédiats ! tip_local_timeline: Le fil public local est une vue des personnes sur %{instance}. Ce sont vos voisines et voisins immédiats!
tip_mobile_webapp: Si votre navigateur mobile vous propose d'ajouter Mastodon à votre écran d'accueil, vous pouvez recevoir des notifications. Il agit comme une application native de bien des façons ! tip_mobile_webapp: Si votre navigateur mobile vous propose dajouter Mastodon à votre écran daccueil, vous pouvez recevoir des notifications. Il agit comme une application native de bien des façons!
tips: Astuces tips: Astuces
title: Bienvenue à bord, %{name} ! title: Bienvenue à bord, %{name}!
users: users:
invalid_email: Ladresse courriel est invalide invalid_email: Ladresse courriel est invalide
invalid_otp_token: Le code dauthentification à deux facteurs est invalide invalid_otp_token: Le code dauthentification à deux facteurs est invalide
otp_lost_help_html: Si vous perdez accès aux deux, vous pouvez contacter %{email} otp_lost_help_html: Si vous perdez accès aux deux, vous pouvez contacter %{email}
seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles. seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles.
signed_in_as: 'Connecté·e en tant que :' signed_in_as: 'Connecté·e en tant que:'

View file

@ -30,10 +30,16 @@ pl:
other_instances: Lista instancji other_instances: Lista instancji
privacy_policy: Polityka prywatności privacy_policy: Polityka prywatności
source_code: Kod źródłowy source_code: Kod źródłowy
status_count_after: wpisów status_count_after:
few: wpisów
many: wpisów
one: wpisu
status_count_before: Są autorami status_count_before: Są autorami
terms: Zasady użytkowania terms: Zasady użytkowania
user_count_after: użytkowników user_count_after:
few: użytkowników
many: użytkowników
one: użytkownik
user_count_before: Z serwera korzysta user_count_before: Z serwera korzysta
what_is_mastodon: Czym jest Mastodon? what_is_mastodon: Czym jest Mastodon?
accounts: accounts:
@ -413,6 +419,12 @@ pl:
last_delivery: Ostatnio doręczono last_delivery: Ostatnio doręczono
title: WebSub title: WebSub
topic: Temat topic: Temat
suspensions:
bad_acct_msg: Zawartość potwierdzenia nie zgadza się. Czy próbujesz zawiesić właściwe konto?
hint_html: 'Aby potwierdzić zawieszenie konta, wprowadź %{value} w poniższe pole:'
proceed: Przejdź
title: Zawieś %{acct}
warning_html: 'Zawieszenie konta będzie skutkowało <strong>nieodwracalnym</strong> usunięciem danych z tego konta, wliczając:'
title: Administracja title: Administracja
admin_mailer: admin_mailer:
new_report: new_report:

View file

@ -3,34 +3,34 @@ fr:
simple_form: simple_form:
hints: hints:
defaults: defaults:
autofollow: Les personnes qui s'inscrivent grâce à l'invitation vous suivront automatiquement autofollow: Les personnes qui sinscrivent grâce à linvitation vous suivront automatiquement
avatar: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à %{dimensions}px avatar: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à %{dimensions}px
bot: Ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé bot: Ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé
context: Un ou plusieurs contextes où le filtre devrait s'appliquer context: Un ou plusieurs contextes où le filtre devrait sappliquer
digest: Uniquement envoyé après une longue période dinactivité et uniquement si vous avez reçu des messages personnels pendant votre absence digest: Uniquement envoyé après une longue période dinactivité et uniquement si vous avez reçu des messages personnels pendant votre absence
display_name: display_name:
one: <span class="name-counter">1</span> caractère restant one: <span class="name-counter">1</span> caractère restant
other: <span class="name-counter">%{count}</span> caractères restants other: <span class="name-counter">%{count}</span> caractères restants
fields: Vous pouvez avoir jusqu'à 4 éléments affichés en tant que tableau sur votre profil fields: Vous pouvez avoir jusquà 4 éléments affichés en tant que tableau sur votre profil
header: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à %{dimensions}px header: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à %{dimensions}px
inbox_url: Copiez l'URL depuis la page d'accueil du relais que vous souhaitez utiliser inbox_url: Copiez lURL depuis la page daccueil du relais que vous souhaitez utiliser
irreversible: Les pouets filtrés disparaîtront irrémédiablement, même si le filtre est supprimé plus tard irreversible: Les pouets filtrés disparaîtront irrémédiablement, même si le filtre est supprimé plus tard
locale: La langue de l'interface-utilisateur, des courriels, et des notifications locale: La langue de linterface, des courriels et des notifications
locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne safficheront quà vos abonné⋅es locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne safficheront quà vos abonné⋅es
note: note:
one: <span class="note-counter">1</span> caractère restant one: <span class="note-counter">1</span> caractère restant
other: <span class="note-counter">%{count}</span> caractères restants other: <span class="note-counter">%{count}</span> caractères restants
phrase: Sera trouvé sans que la case ou l'avertissement de contenu du pouet soit pris en compte phrase: Sera trouvé sans que la case ou lavertissement de contenu du pouet soit pris en compte
scopes: À quelles APIs l'application sera autorisée à accéder. Si vous sélectionnez un périmètre de haut-niveau, vous n'avez pas besoin de sélectionner les individuels. scopes: À quelles APIs lapplication sera autorisée à accéder. Si vous sélectionnez un périmètre de haut-niveau, vous navez pas besoin de sélectionner les individuels.
setting_default_language: La langue de vos pouets peut être détectée automatiquement, mais ça n'est pas toujours pertinent setting_default_language: La langue de vos pouets peut être détectée automatiquement, mais ça nest pas toujours pertinent
setting_hide_network: Ceux que vous suivez et ceux qui vous suivent ne seront pas affichés sur votre profil setting_hide_network: Ceux que vous suivez et ceux qui vous suivent ne seront pas affichés sur votre profil
setting_noindex: Affecte votre profil public ainsi que vos statuts setting_noindex: Affecte votre profil public ainsi que vos statuts
setting_theme: Affecte lapparence de Mastodon quand vous êtes connecté·e depuis nimporte quel appareil. setting_theme: Affecte lapparence de Mastodon quand vous êtes connecté·e depuis nimporte quel appareil.
whole_word: Lorsque le mot-clef ou la phrase-clef est uniquement alphanumérique, ça sera uniquement appliqué s'il correspond au mot entier whole_word: Lorsque le mot-clef ou la phrase-clef est uniquement alphanumérique, ça sera uniquement appliqué sil correspond au mot entier
imports: imports:
data: Un fichier CSV généré par une autre instance de Mastodon data: Un fichier CSV généré par une autre instance de Mastodon
sessions: sessions:
otp: 'Entrez le code dauthentification à deux facteurs généré par votre téléphone ou utilisez un de vos codes de récupération :' otp: 'Entrez le code dauthentification à deux facteurs généré par votre téléphone ou utilisez un de vos codes de récupération:'
user: user:
chosen_languages: Lorsque coché, seuls les pouets dans les langues sélectionnées seront affichés sur les fils publics chosen_languages: Lorsque coché, seuls les pouets dans les langues sélectionnées seront affichés sur les fils publics
labels: labels:
@ -55,9 +55,9 @@ fr:
header: Image den-tête header: Image den-tête
inbox_url: URL de la boîte de relais inbox_url: URL de la boîte de relais
irreversible: Supprimer plutôt que de cacher irreversible: Supprimer plutôt que de cacher
locale: Langue de l'interface locale: Langue de linterface
locked: Verrouiller le compte locked: Verrouiller le compte
max_uses: Nombre maximum d'utilisations max_uses: Nombre maximum dutilisations
new_password: Nouveau mot de passe new_password: Nouveau mot de passe
note: Présentation note: Présentation
otp_attempt: Code didentification à deux facteurs otp_attempt: Code didentification à deux facteurs
@ -79,7 +79,7 @@ fr:
severity: Sévérité severity: Sévérité
type: Type dimport type: Type dimport
username: Identifiant username: Identifiant
username_or_email: Nom d'utilisateur ou courriel username_or_email: Nom dutilisateur·ice ou courriel
whole_word: Mot entier whole_word: Mot entier
interactions: interactions:
must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas

View file

@ -23,6 +23,7 @@ Rails.application.routes.draw do
get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger
get 'manifest', to: 'manifests#show', defaults: { format: 'json' } get 'manifest', to: 'manifests#show', defaults: { format: 'json' }
get 'intent', to: 'intents#show' get 'intent', to: 'intents#show'
get 'custom.css', to: 'custom_css#show', as: :custom_css
devise_scope :user do devise_scope :user do
get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite

View file

@ -9,11 +9,11 @@ module Mastodon
end end
def minor def minor
4 5
end end
def patch def patch
3 0
end end
def pre def pre
@ -21,7 +21,7 @@ module Mastodon
end end
def flags def flags
'' 'rc1'
end end
def to_a def to_a

View file

@ -92,6 +92,43 @@ describe ApplicationController, type: :controller do
end end
end end
describe 'helper_method :current_flavour' do
it 'returns "glitch" when theme wasn\'t changed in admin settings' do
allow(Setting).to receive(:default_settings).and_return({'skin' => 'default'})
allow(Setting).to receive(:default_settings).and_return({'flavour' => 'glitch'})
expect(controller.view_context.current_flavour).to eq 'glitch'
end
it 'returns instances\'s flavour when user is not signed in' do
allow(Setting).to receive(:[]).with('skin').and_return 'default'
allow(Setting).to receive(:[]).with('flavour').and_return 'vanilla'
expect(controller.view_context.current_flavour).to eq 'vanilla'
end
it 'returns instances\'s default flavour when user didn\'t set theme' do
current_user = Fabricate(:user)
sign_in current_user
allow(Setting).to receive(:[]).with('skin').and_return 'default'
allow(Setting).to receive(:[]).with('flavour').and_return 'vanilla'
expect(controller.view_context.current_flavour).to eq 'vanilla'
end
it 'returns user\'s flavour when it is set' do
current_user = Fabricate(:user)
current_user.settings['flavour'] = 'glitch'
sign_in current_user
allow(Setting).to receive(:[]).with('skin').and_return 'default'
allow(Setting).to receive(:[]).with('flavour').and_return 'vanilla'
expect(controller.view_context.current_flavour).to eq 'glitch'
end
end
context 'ActionController::RoutingError' do context 'ActionController::RoutingError' do
subject do subject do
routes.draw { get 'routing_error' => 'anonymous#routing_error' } routes.draw { get 'routing_error' => 'anonymous#routing_error' }

View file

@ -512,7 +512,7 @@ RSpec.describe User, type: :model do
context 'when user is confirmed' do context 'when user is confirmed' do
let(:confirmed_at) { Time.zone.now } let(:confirmed_at) { Time.zone.now }
it { is_expected.to be false } it { is_expected.to be true }
end end
context 'when user is not confirmed' do context 'when user is not confirmed' do