Swap boosts, favourites

This commit is contained in:
Ernest Wiśniewski 2023-06-07 17:10:43 +02:00
parent 2172d3e749
commit d592ef2439
25 changed files with 805 additions and 56 deletions

View file

@ -69,6 +69,7 @@ federation.
* English
* Polish
* Dutch ([Vistaus](https://github.com/Vistaus))
* Japanese ([@dannekrose@brioco.social](https://brioco.social/@dannekrose))
## Credits

View file

@ -118,6 +118,12 @@ entry_comment_favourite:
path: /ecf/{id}
methods: [ POST ]
entry_comment_boost:
controller: App\Controller\BoostController
defaults: { entityClass: App\Entity\EntryComment }
path: /ecb/{id}
methods: [ POST ]
entry_create:
controller: App\Controller\Entry\EntryCreateController
defaults: { type: link }
@ -240,6 +246,12 @@ entry_favourite:
path: /ef/{id}
methods: [ POST ]
entry_boost:
controller: App\Controller\BoostController
defaults: { entityClass: App\Entity\Entry }
path: /eb/{id}
methods: [ POST ]
entry_cti:
controller: App\Controller\Entry\EntryCardanoTxInitController
defaults: { entityClass: App\Entity\Entry }

View file

@ -82,6 +82,12 @@ post_comment_favourite:
path: /pcf/{id}
methods: [ POST ]
post_comment_boost:
controller: App\Controller\BoostController
defaults: { entityClass: App\Entity\PostComment }
path: /pcb/{id}
methods: [ POST ]
posts_front:
controller: App\Controller\Post\PostFrontController::front
defaults: { sortBy: hot, time: ~ }
@ -211,3 +217,9 @@ post_favourite:
defaults: { entityClass: App\Entity\Post }
path: /pf/{id}
methods: [ POST ]
post_boost:
controller: App\Controller\BoostController
defaults: { entityClass: App\Entity\Post }
path: /pb/{id}
methods: [ POST ]

View file

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Entity\Contracts\FavouriteInterface;
use App\Entity\Contracts\VotableInterface;
use App\Service\GenerateHtmlClassService;
use App\Service\VoteManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class BoostController extends AbstractController
{
public function __construct(
private readonly GenerateHtmlClassService $classService,
private readonly VoteManager $manager
) {
}
#[IsGranted('ROLE_USER')]
public function __invoke(VotableInterface $subject, Request $request): Response
{
$this->validateCsrf('boost', $request->request->get('token'));
$this->manager->vote(VotableInterface::VOTE_UP, $subject, $this->getUserOrThrow());
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
[
'html' => $this->renderView(
'components/_ajax.html.twig',
[
'component' => 'boost',
'attributes' => [
'subject' => $subject,
'path' => $request->attributes->get('_route'),
],
]
),
]
);
}
return $this->redirectToRefererOrHome($request, ($this->classService)->fromEntity($subject));
}
}

View file

@ -28,13 +28,11 @@ class FavouriteController extends AbstractController
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
[
'html' => $this->renderView(
'components/_ajax.html.twig',
[
'component' => 'favourite',
'html' => $this->renderView('components/_ajax.html.twig', [
'component' => 'vote',
'attributes' => [
'subject' => $subject,
'path' => $request->attributes->get('_route'),
'showDownvote' => str_contains(get_class($subject), 'Entry'),
],
]
),

View file

@ -11,6 +11,8 @@ use App\Event\FavouriteEvent;
use App\Message\ActivityPub\Outbox\LikeMessage;
use App\Message\Notification\FavouriteNotificationMessage;
use App\Service\CacheService;
use App\Service\FavouriteManager;
use App\Service\VoteManager;
use Doctrine\Common\Util\ClassUtils;
use JetBrains\PhpStorm\ArrayShape;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -22,7 +24,8 @@ class FavouriteHandleSubscriber implements EventSubscriberInterface
public function __construct(
private readonly MessageBusInterface $bus,
private readonly CacheInterface $cache,
private readonly CacheService $cacheService
private readonly CacheService $cacheService,
private readonly VoteManager $voteManager
) {
}
@ -38,6 +41,10 @@ class FavouriteHandleSubscriber implements EventSubscriberInterface
{
$subject = $event->subject;
if (FavouriteManager::TYPE_UNLIKE && $subject->isFavored($event->user)) {
$this->voteManager->removeVote($subject, $event->user);
}
$this->bus->dispatch(
new FavouriteNotificationMessage(
$subject->getId(),

View file

@ -11,6 +11,7 @@ use App\Event\VoteEvent;
use App\Message\ActivityPub\Outbox\AnnounceMessage;
use App\Message\Notification\VoteNotificationMessage;
use App\Service\CacheService;
use App\Service\FavouriteManager;
use Doctrine\Common\Util\ClassUtils;
use JetBrains\PhpStorm\ArrayShape;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@ -22,7 +23,8 @@ class VoteHandleSubscriber implements EventSubscriberInterface
public function __construct(
private readonly MessageBusInterface $bus,
private readonly CacheService $cacheService,
private readonly CacheInterface $cache
private readonly CacheInterface $cache,
private readonly FavouriteManager $favouriteManager,
) {
}
@ -36,6 +38,10 @@ class VoteHandleSubscriber implements EventSubscriberInterface
public function onVote(VoteEvent $event): void
{
if (VotableInterface::VOTE_DOWN === $event->vote->choice) {
$this->favouriteManager->toggle($event->vote->user, $event->votable, FavouriteManager::TYPE_UNLIKE);
}
$this->clearCache($event->votable);
$this->bus->dispatch(
@ -45,7 +51,7 @@ class VoteHandleSubscriber implements EventSubscriberInterface
)
);
if (!$event->vote->user->apId && 1 === $event->vote->choice && !$event->votedAgain) {
if (!$event->vote->user->apId && VotableInterface::VOTE_UP === $event->vote->choice && !$event->votedAgain) {
$this->bus->dispatch(
new AnnounceMessage(
$event->vote->user->getId(),

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Service;
use App\Entity\Contracts\FavouriteInterface;
use App\Entity\Contracts\VotableInterface;
use App\Entity\Favourite;
use App\Entity\User;
use App\Event\FavouriteEvent;

View file

@ -114,4 +114,24 @@ class VoteManager
return $vote;
}
public function removeVote(VotableInterface $votable, User $user): ?Vote
{
// @todo save activity pub object id
$vote = $votable->getUserVote($user);
if (!$vote) {
return null;
}
$vote->choice = VotableInterface::VOTE_NONE;
$votable->updateVoteCounts();
$this->entityManager->flush();
$this->dispatcher->dispatch(new VoteEvent($votable, $vote, false));
return $vote;
}
}

View file

@ -3,11 +3,30 @@
namespace App\Twig\Components;
use App\Entity\Contracts\ContentInterface;
use App\Entity\Entry;
use App\Entity\EntryComment;
use App\Entity\Post;
use App\Entity\PostComment;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\PostMount;
#[AsTwigComponent('boost')]
final class BoostComponent
{
public string $path;
public string $formDest;
public ContentInterface $subject;
#[PostMount]
public function postMount(array $attr): array
{
$this->formDest = match (true) {
$this->subject instanceof Entry => 'entry',
$this->subject instanceof EntryComment => 'entry_comment',
$this->subject instanceof Post => 'post',
$this->subject instanceof PostComment => 'post_comment',
default => throw new \LogicException(),
};
return $attr;
}
}

View file

@ -3,11 +3,30 @@
namespace App\Twig\Components;
use App\Entity\Contracts\ContentInterface;
use App\Entity\Entry;
use App\Entity\EntryComment;
use App\Entity\Post;
use App\Entity\PostComment;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\PostMount;
#[AsTwigComponent('favourite')]
final class FavouriteComponent
{
public string $path;
public string $formDest;
public ContentInterface $subject;
#[PostMount]
public function postMount(array $attr): array
{
$this->formDest = match (true) {
$this->subject instanceof Entry => 'entry',
$this->subject instanceof EntryComment => 'entry_comment',
$this->subject instanceof Post => 'post',
$this->subject instanceof PostComment => 'post_comment',
default => throw new \LogicException(),
};
return $attr;
}
}

View file

@ -15,17 +15,16 @@ final class VoteComponent
{
public VotableInterface $subject;
public string $formDest;
public bool $showDownvote = true;
#[PostMount]
public function postMount(array $attr): array
{
$this->formDest = match (true) {
$this->subject instanceof Entry => 'entry_vote',
$this->subject instanceof EntryComment => 'entry_comment_vote',
$this->subject instanceof Post => 'post_vote',
$this->subject instanceof PostComment => 'post_comment_vote',
$this->subject instanceof Entry => 'entry',
$this->subject instanceof EntryComment => 'entry_comment',
$this->subject instanceof Post => 'post',
$this->subject instanceof PostComment => 'post_comment',
default => throw new \LogicException(),
};

View file

@ -0,0 +1,13 @@
{%- set VOTE_UP = constant('App\\Entity\\Contracts\\VotableInterface::VOTE_UP') -%}
{%- set user_choice = is_granted('ROLE_USER') ? subject.userChoice(app.user) : null -%}
<form method="post"
action="{{ path(formDest~'_boost', {id: subject.id}) }}">
<input type="hidden" name="token" value="{{ csrf_token('boost') }}">
<button class="{{ html_classes('stretched-link', {'active': app.user and user_choice is same as(VOTE_UP) }) }}"
type="submit"
data-action="subject#favourite">
{{ 'up_vote'|trans|lower }} <span class="{{ html_classes({'hidden': not subject.countUpvotes}) }}">
(<span data-subject-target="favCounter">{{ subject.countUpvotes }}</span>)
</span>
</button>
</form>

View file

@ -103,7 +103,6 @@
{% if entry.visibility in ['visible', 'private'] %}
{{ component('vote', {
subject: entry,
formDest: 'entry_vote'
}) }}
{% endif %}
<footer>
@ -137,9 +136,8 @@
</a>
</li>
<li>
{{ component('favourite', {
subject: entry,
path: 'entry_favourite'
{{ component('boost', {
subject: entry
}) }}
</li>
<li class="dropdown">

View file

@ -66,7 +66,7 @@
data-action="subject#getForm">{{ 'reply'|trans|lower }}</a>
</li>
<li>
{{ component('favourite', {subject: comment, path: 'entry_comment_favourite'}) }}
{{ component('boost', {subject: comment}) }}
</li>
<li class="dropdown">
<button class="stretched-link" data-subject-target="more">{{ 'more'|trans|lower }}</button>

View file

@ -1,5 +1,5 @@
<form method="post"
action="{{ path(path, {id: subject.id}) }}">
action="{{ path(formDest~'_favourite', {id: subject.id}) }}">
<input type="hidden" name="token" value="{{ csrf_token('favourite') }}">
<button class="{{ html_classes('stretched-link', {'active': app.user and subject.isFavored(app.user) }) }}"
type="submit"

View file

@ -81,9 +81,8 @@
</li>
{% endif %}
<li>
{{ component('favourite', {
subject: post,
path: 'post_favourite'
{{ component('boost', {
subject: post
}) }}
</li>
<li class="dropdown">

View file

@ -70,9 +70,8 @@
data-action="subject#getForm">{{ 'reply'|trans|lower }}</a>
</li>
<li>
{{ component('favourite', {
subject: comment,
path: 'post_comment_favourite'
{{ component('boost', {
subject: comment
}) }}
</li>
<li class="dropdown">

View file

@ -4,8 +4,8 @@
{% if app.user %}
{%- set user_choice = is_granted('ROLE_USER') ? subject.userChoice(app.user) : null -%}
{% set upUrl = path(formDest, {id: subject.id, choice: VOTE_UP}) %}
{% set downUrl = path(formDest, {id: subject.id, choice: VOTE_DOWN}) %}
{% set upUrl = path(formDest~'_favourite', {id: subject.id, choice: VOTE_UP}) %}
{% set downUrl = path(formDest~'_vote', {id: subject.id, choice: VOTE_DOWN}) %}
{% if(user_choice is same as(VOTE_UP)) %}
{% set choice = VOTE_UP %}
@ -16,23 +16,23 @@
{% endif %}
{% else %}
{% set choice = VOTE_NONE %}
{% set upUrl = path(formDest, {id: subject.id, choice: VOTE_NONE}) %}
{% set downUrl = path(formDest, {id: subject.id, choice: VOTE_NONE}) %}
{% set upUrl = path(formDest~'_favourite', {id: subject.id, choice: VOTE_NONE}) %}
{% set downUrl = path(formDest~'_vote', {id: subject.id, choice: VOTE_NONE}) %}
{% endif %}
<aside{{ attributes.defaults({class: 'vote'}) }}>
<form method="post"
action="{{ upUrl }}"
class="{{ html_classes('vote__up',{
'active': choice is same as(VOTE_UP),
'active': app.user and subject.isFavored(app.user),
}) }}">
<button type="submit"
title="{{ 'up_vote'|trans }}"
aria-label="{{ 'up_vote'|trans }}"
title="{{ 'favourite'|trans }}"
aria-label="{{ 'favourite'|trans }}"
data-action="subject#vote"
{{ html_classes({'disabled': app.user and subject.isAuthor(app.user)}) }}>
{{ subject.countUpvotes }} <span><i class="fa-solid fa-arrow-up"></i></span>
{{ subject.favouriteCount }} <span><i class="fa-solid fa-arrow-up"></i></span>
</button>
<input type="hidden" name="token" value="{{ csrf_token('vote') }}">
<input type="hidden" name="token" value="{{ csrf_token('favourite') }}">
</form>
{% if showDownvote %}
<form method="post"

View file

@ -473,3 +473,4 @@ header_logo: Header logo
browsing_one_thread: You are only browsing one thread in the discussion! All comments
are available on the post page.
return: Return
boost: Boost

View file

@ -1 +1,37 @@
{}
type.link: Enlace
type.article: Artículo
type.photo: Fotografía
type.video: Vídeo
type.smart_contract: Contrato inteligente
type.magazine: Magazin
thread: Hilo
threads: Hilos
microblog: Microblog
people: Gente
events: Eventos
magazine: Magazin
magazines: Magazines
search: Búsqueda
add: Agregar
select_channel: Elegir un canal
login: Entrar
top: Mejores
hot: Popular
active: Activo
newest: Más recientes
oldest: Más antiguos
commented: Comentado
change_view: Cambiar vista
filter_by_time: Ordenar por tiempo
filter_by_type: Ordenar por tipo
comments_count: '{0}comentarios|{1}comentario|]1,Inf[ comentarios'
favourites: Favoritos
favourite: Favorito
more: Otros
avatar: Avatar
added: Agregado
general: General
views: Visualizaciones
created_at: Creado
owner: Propietaria/o
subscribers: Suscriptores/as

View file

@ -179,7 +179,7 @@ are_you_sure: Êtes-vous sûr(e)?
reset_check_email_desc2: Si vous ne recevez pas d'email, veuillez vérifier votre dossier
spam.
messages: Messages directs
in: en
in: dans
email_confirm_header: Salut ! Confirmez votre addresse email.
image: Image
edit: Modifier
@ -187,3 +187,250 @@ go_to_search: Vers la recherche
privacy_policy: Politique de confidentialité
subscriptions: Abonnements
edit_article: Modifier l'article
type.smart_contract: Contrat intelligent
hot: Dernière minute
agree_terms: Consentir aux %terms_link_start%conditions d'utilisation%terms_link_end%
et à la %policy_link_start%politique de confidentialité%policy_link_end%
reset_check_email_desc: Si un compte correspondant à votre adresse email existe, un
email vient d'être envoyé contenant un lien que vous pouvez utiliser pour réinitialiser
votre mot de passe. Ce lien expirera dans %expire%.
joined: Inscrit(e)
show_profile_subscriptions: Voir les abonnements aux magazines
show_profile_followings: Voir les utilisateurs suivants
notify_on_new_entry_reply: Avertissez-moi des commentaires dans mes fils
notify_on_new_entry_comment_reply: Avertissez-moi des réponses à mes commentaires
dans les fils
notify_on_new_post_reply: Avertissez-moi des réponses à mes messages
notify_on_new_post_comment_reply: Avertissez-moi des réponses à mes commentaires dans
les messages
notify_on_new_entry: Avertissez-moi des nouveaux fils dans les magazines abonnés
notify_on_new_posts: Avertissez-moi des nouveaux messages dans les magazines abonnés
save: Enregistrer
about: À propos
old_email: Ancien email
new_email: Nouvel email
new_email_repeat: Confirmer le nouvel email
current_password: Mot de passe actuel
new_password: Nouveau mot de passe
new_password_repeat: Confirmer le nouveau mot de passe
change_email: Changer l'email
change_password: Changer le mot de passe
expand: Élargir
collapse: Plier
domains: Domaines
error: Erreur
votes: Votes
theme: Thème
dark: Sombre
light: Clair
font_size: Taille de police
size: Taille
he_unbanned: débloquer
boosts: Partages
yes: Oui
no: Non
show_thumbnails: Afficher les vignettes
show_users_avatars: Afficher les avatars des utilisateurs
ban_expired: L'interdiction a expiré
ban: Bannir
firstname: Prénom
bans: Interdictions
add_ban: Ajouter une interdiction
return: Retour
header_logo: Logo d'entête
captcha_enabled: Captcha activé
kbin_promo_title: Créer votre propre instance
kbin_intro_title: Explorer le Fédivers
dynamic_lists: Listes dynamiques
auto_preview: Aperçu automatique des médias
sidebar: Barre latérale
random_magazines: Magazines aléatoires
related_magazines: Magazines reliés
delete_account_request_send: Votre demande de supprimer le compte a été soumise.
unban_account: Débloquer le compte
delete_account: Supprimer le compte
related_entries: Fils reliés
random_entries: Fils aléatoires
active_users: Personnes actives
banned_instances: Instances bannis
ban_account: Bannir le compte
send: Envoyer
he_banned: banni
Your account has been banned: Votre compte a été banni.
banned: Vous a banni
show_magazines_icons: Afficher les icônes des magazines
solarized_light: Clair solarisé
solarized_dark: Sombre solarisé
rounded_edges: Bords arrondis
removed_thread_by: a enlevé un fil par
restored_thread_by: a rétabli un fil par
removed_comment_by: a enlevé un commentaire par
restored_comment_by: a rétabli un commentaire par
removed_post_by: a enlevé un message par
restored_post_by: a rétabli un message par
read_all: Lire tou(te)s
show_all: Afficher tout
ranking_disabled: Le classement est inactif
flash_register_success: Votre compte a été enregistré. Vérifiez votre email, nous
avons envoyé un message avec un lien d'activation.
flash_thread_new_success: Le fil a été créé correctement et est maintenant visible
aux autres usagers.
flash_thread_edit_success: Le fil a été modifié correctement.
flash_thread_delete_success: Le fil a été supprimé correctement.
flash_thread_pin_success: Le fil a été fixé correctement.
flash_thread_unpin_success: Le fil a été défixé correctement.
flash_magazine_edit_success: Le magazine a été modifié correctement.
too_many_requests: Limite dépassée, veuillez réessayer plus tard.
set_magazines_bar: Barre des magazines
gold: Or
silver: Argent
bronze: Bronze
bronze_autobiographer: Autobiographe
bronze_personality: Personnalité bronze
bronze_personality_desc: 20 suiveurs
bronze_autobiographer_desc: Pour avoir complété tous les champs du profil
bronze_commentator: Commentateur/trice bronze
bronze_commentator_desc: 10 bons commentaires
bronze_scout: Scout(e) bronze
bronze_scout_desc: 10 bons liens
bronze_redactor_desc: 10 bons articles
set_magazines_bar_desc: ajoutez les noms de magazines après la virgule
bronze_poster_desc: 10 bons messages
bronze_link: Bon lien
bronze_link_desc: Le message a été partagé 5 fois
bronze_article: Article bronze
bronze_photo: Bonne photo
bronze_photo_desc: La photo a été partagée 5 fois
bronze_comment: Bon commentaire
bronze_comment_desc: Le commentaire a été partagé 5 fois
bronze_post: Bon message
bronze_post_desc: Le message a été partagé 5 fois
bronze_ranking: Numéro 3
bronze_ranking_desc: Troisième rang du classement
bronze_popular_entry: Fil populaire
silver_personality: Personnalité argent
silver_personality_desc: 50 suiveurs
silver_commentator: Commentateur/trice argent
silver_scout: Scout(e) argent
silver_scout_desc: 10 très bons liens
silver_redactor_desc: 10 très bons articles
silver_poster: Messager argent
silver_poster_desc: 10 très bons messages
silver_link: Très bon lien
silver_article: Article argent
silver_article_desc: L'article a été partagé 10 fois
silver_photo: Très bonne photo
silver_photo_desc: La photo a été partagée 10 fois
silver_comment: Très bon commentaire
silver_post: Très bon message
silver_post_desc: Le message a été partagé 10 fois
silver_ranking: Numéro 2
silver_ranking_desc: Deuxième rang du classement
silver_popular_entry: Fil populaire
silver_popular_entry_desc: Fil visité 5000 fois
silver_magazine: Magazine populaire
silver_entry_week: Fil de la semaine
silver_comment_week: Commentaire de la semaine
silver_comment_week_desc: Meilleur commentaire de la semaine
silver_post_week: Message de la semaine
silver_post_week_desc: Meilleur message de la semaine
gold_personality: Personnalité or
gold_personality_desc: 100 suiveurs
gold_commentator_desc: 10 commentaires excellents
gold_scout: Scout(e) or
gold_scout_desc: 10 liens excellents
silver_redactor: Rédacteur/trice argent
gold_redactor: Rédacteur/trice or
gold_redactor_desc: 10 articles excellents
gold_poster: Messager or
gold_link: Lien excellent
gold_link_desc: Le message a été partagé 15 fois
gold_article: Article or
gold_article_desc: L'article a été partagé 15 fois
gold_photo: Photo excellente
gold_comment: Commentaire excellent
gold_comment_desc: Le commentaire a été partagé 15 fois
bronze_magazine_desc: 20 suiveurs ont créé un magazine
silver_magazine_desc: 50 suiveurs ont créé un magazine
gold_post: Message excellent
gold_ranking: Numéro 1
gold_ranking_desc: Premier rang du classement
gold_popular_entry: Fil populaire
gold_popular_entry_desc: Fil visité 10000 fois
gold_magazine: Magazine populaire
gold_entry_month_desc: Meilleur fil du mois
gold_comment_month: Commentaire du mois
gold_comment_month_desc: Meilleur commentaire du mois
gold_post_month: Message du mois
gold_post_month_desc: Meilleur message du mois
gold_magazine_desc: 100 suiveurs ont créé un magazine
edited_thread: Fil modifié
added_new_thread: a ajouté un nouveau fil
added_new_comment: A ajouté un nouveau commentaire
edited_comment: A modifié un commentaire
replied_to_your_comment: A répondu à un commentaire
mod_deleted_your_comment: Un(e) modérateur/trice a supprimé votre commentaire
mod_remove_your_thread: Un(e) modérateur/trice a retiré votre fil
added_new_post: A ajouté un nouveau message
edited_post: A modifié un message
mod_remove_your_post: Un(e) modérateur/trice a retiré votre message
added_new_reply: A ajouté une nouvelle réponse
removed: Retiré par un(e) modérateur/trice
deleted: Supprimé par l'auteur
mentioned_you: vous a mentionné
comment: Commentaire
post: Message
purge: Purge
message: Message direct
infinite_scroll: Défilement infini
show_top_bar: Afficher la barre supérieure
sticky_navbar: Barre de navigation collante
subject_reported: Le contenu a été signalé.
left: Gauche
right: Droite
federation: Fédération
status: Statut
on: Actif
off: Inactif
instances: Instances
upload_file: Téléverser un fichier
from_url: Depuis l'URL
magazine_panel: Panneau de magazine
reject: Refuser
approve: Approuver
filters: Filtres
approved: Approuvé
rejected: Rejeté
add_moderator: Ajouter un(e) modérateur/trice
add_badge: Ajouter un badge
created: Créé
expires: Expire
perm: Perm
expired_at: Expiré le
trash: Corbeille
icon: Icône
done: Terminé
pin: Épingle
bronze_redactor: Rédacteur/trice bronze
bronze_poster: Messager bronze
flash_magazine_new_success: Le magazine a été créé correctement. Vous pouvez désormais
ajouter du nouveau contenu ou explorer le panneau d'administration du magazine.
bronze_article_desc: L'article a été partagé 5 fois
bronze_popular_entry_desc: Fil visité 1000 fois
bronze_magazine: Magazine fameux
silver_commentator_desc: 10 très bons commentaires
silver_link_desc: Le lien a été partagé 10 fois
silver_comment_desc: Le commentaire a été partagé 10 fois
silver_entry_week_desc: Meilleur fil de la semaine
gold_commentator: Commentateur/trice or
gold_poster_desc: 10 messages excellents
gold_photo_desc: La photo a été partagée 15 fois
gold_entry_month: Fil du mois
set_magazines_bar_empty_desc: si le champ est vide, les magazines actifs seront affichés
sur la barre.
wrote_message: A écrit un message
send_message: Envoyer un message
gold_post_desc: Le message a été partagé 15 fois
sidebar_position: Position de la barre latérale
mod_log_alert: Dans le journal de modération vous pourriez trouver du contenu choquant
enlevé par les modérateurs. Assurez-vous de savoir ce que vous faites.

View file

@ -7,7 +7,7 @@ type.magazine: 雑誌
thread: スレッド
threads: スレッド
microblog: ミニブログ
people: ユーザ
people: ユーザ
events: イベント
magazine: 雑誌
magazines: 雑誌
@ -16,11 +16,11 @@ add: 追加
select_channel: チャンネルを選択する
login: ログイン
top: トップ
hot: 熱中
active: 活発
newest: 新更
oldest: い更新順
commented: コメントありの
hot: ホット
active: 活発
newest:
oldest:
commented: コメント
change_view: 表示変更
filter_by_time: 時間でフィルター
filter_by_type: 投稿の種類でフィルター
@ -48,7 +48,7 @@ add_media: メディアを添付する
markdown_howto: エディターの使え方は?
enter_your_post: 投稿を入力してください
activity: アクティビティ
cover: カバー映像
cover: プロフィールのバナー
related_posts: 関連投稿
random_posts: ランダムの投稿
federated_user_info: 連合のサーバーのプロフィールの全データが受信されていない可能性があります。
@ -65,7 +65,7 @@ you_cant_login: ログインできない?
already_have_account: すでにアカウントを持っていますか?
register: 新規登録する
show_more: もっと表示する
to: 当て
to: 雑誌
in: 中に
username: ユーザー名
email: メールアドレス
@ -158,7 +158,7 @@ delete: 削除する
edit_post: 投稿を編集する
settings: 設定
profile: プロフィール
blocked: ブロックされた
blocked: ブロック
reports: 通報
notifications: 通知
messages: メッセージ
@ -174,7 +174,7 @@ create_new_magazine: 新しい雑誌を作成する
federated_magazine_info: 連合のサーバーの雑誌から受信されていない投稿の可能性があります。
add_new_photo: 画像をアップする
image_alt: 画像の代替テキスト
moderated: モデレーターである雑誌
moderated: モデレーターである雑誌
check_email: メールをチェックしてください
unfollow: アンフォローする
reset_check_email_desc: あなたのメールアドレスで登録されたアカウントがありましたら、そのメールアドレスにパスワードのリセットのリンクを送信しました。そのリンクの有効期限は
@ -191,8 +191,8 @@ general: 一般
hide_adult: R-18・NSFWを非表示する
featured_magazines: おすすめ雑誌
privacy: プライバシー
show_profile_subscriptions: 雑誌の購読を
show_profile_followings: フォローしているユーザーを
show_profile_subscriptions: 雑誌の購読を表示す
show_profile_followings: フォローしているユーザーを表示す
notify_on_new_post_reply: 自分の投稿のリプライを通知する
notify_on_new_entry_reply: 自分のスレッドのコメントを通知する
notify_on_new_entry_comment_reply: 自分のスレッドのコメントのリプライを通知する
@ -255,7 +255,7 @@ restored_thread_by: はこのアカウントが作成したスレッドを復旧
removed_post_by: はこのアカウントが作成した投稿を削除しました:
flash_thread_unpin_success: スレッドの固定を外しました。
flash_magazine_new_success: 雑誌の作成ができました。新しいコンテンツを追加でき、また雑誌管理パネルを見られます。
bronze_personality: ブロンズ人気者
bronze_personality: ブロンズ人気者
bronze_personality_desc: 20人のフォロワー達成
bronze_commentator: ブロンズ帯コメント者
bronze_scout: ブロンズ帯斥候
@ -415,10 +415,10 @@ writing: 著作
users: ユーザー
content: コンテンツ
weeks: 週間
week: 週間
month: 月間
week: 週間
month: 1ヶ月間
months: 月間
year: 年間
year: 1年間
local: ローカル
admin_panel: 管理パネル
dashboard: ダッシュボード
@ -449,7 +449,7 @@ sidebar: サイドバー
auto_preview: メディアを自動表示
dynamic_lists: ダイナミックリスト
banned_instances: バンされたインスタンス
kbin_intro_title: フェディバースをブラウズする
kbin_intro_title: フェディバースに参加しましょう
kbin_promo_title: 自分のインスタンスを立つ
kbin_promo_desc: '%link_start% リポジトリをクローンして %link_end% フェディバースを広げましょう'
captcha_enabled: Captchaは有効
@ -461,5 +461,5 @@ add_mentions_entries: タグを自動的に追加する
Your account has been banned: あなたのアカウントはバンされました。
unban_account: アカウントのバンを解除する
magazine_panel_tags_info: この雑誌に連合しているサーバーから受信したコンテンツを自動的に追加したい場合、関連のタグを入力してください
kbin_intro_desc: フェディバースに連合している分散型のコンテンツ収取とミニブログができるソフトです。
kbin_intro_desc: フェディバースに連合している分散型のコンテンツ収取とミニブログができるソフトです。
browsing_one_thread: この議論に一つだけのスレッドを見ています!投稿ページですべてのコメントを見られます。

View file

@ -475,3 +475,4 @@ header_logo: Header logo
return: Wróć
browsing_one_thread: Przeglądasz tylko jeden wątek w dyskusji! Wszystkie Komentarze
dostępne są na stronie posta.
boost: Podbij

View file

@ -0,0 +1,311 @@
type.link: Ligação
type.article: Artigo
type.photo: Foto
type.video: Video
type.magazine: Magazine
thread: Tópico
threads: Tópicos
microblog: Microblog
people: Pessoas
events: Eventos
magazine: Magazine
magazines: Magazines
search: Procura
add: Adicionar
select_channel: Selecionar um canal
login: Log in
top: Topo
hot: Quente
newest: Novo
oldest: Antigo
commented: Comentado
change_view: Alterar vista
filter_by_time: Filtrar por tempo
filter_by_type: Filtrar por tipo
favourites: Favoritos
favourite: Favorito
more: Mais
avatar: Avatar
added: Adicionado
views: Visualisações
up_votes: Upvoto
down_votes: Downvoto
no_comments: Sem comentários
created_at: Criado
owner: Dono
subscribers: Subscritores
online: Online
comments: Comentários
posts: Postagens
moderators: Moderadores
mod_log: Log de Moderação
add_comment: Adicionar comentário
add_post: Adicionar postagem
add_media: Adicionar media
enter_your_comment: Introduza o seu comentário
enter_your_post: Introduza a sua postagem
activity: Atividade
cover: Capa
related_posts: Postagens relacionadas
random_posts: Postagens aleatórias
federated_user_info: O perfil do servidor federado pode estar incompleto.
go_to_original_instance: Navega mais na instância federada.
empty: Vazio
subscribe: Subscrever
unsubscribe: Cancelar subscrição
follow: Seguir
unfollow: Deixar de seguir
reply: Resposta
login_or_email: Login ou email
password: Password
dont_have_account: Não tem uma conta?
you_cant_login: Não consegue fazer login?
already_have_account: Já tem uma conta?
register: Registar
reset_password: Redefinir password
show_more: Mostrar mais
to: para
in: em
email: Email
repeat_password: Repetir password
terms: Termos do serviço
privacy_policy: Política de Privacidade
about_instance: Sobre
all_magazines: Todas as magazines
stats: Estatísticas
fediverse: Fediverse
awards: Prémios
ranking: Ranking
create_new_magazine: Criar uma nova magazine
add_new_article: Adicionar um novo artigo
add_new_link: Adicionar um novo link
add_new_photo: Adicionar uma nova foto
add_new_post: Adicionar uma nova postagem
add_new_video: Adicionar um novo video
contact: Contato
faq: FAQ
rss: RSS
change_theme: Alterar tema
useful: Útil
help: Ajuda
reset_check_email_desc2: Se não receber um email, verifique a pasta de spam.
try_again: Tente de novo
up_vote: Upvoto
down_vote: Downvoto
email_confirm_header: Olá! Confirme o seu endereço de email.
email_confirm_content: 'Se quer ativar a sua conta clique no link seguinte:'
email_verify: Confirme endereço de email
email_confirm_title: Confirme o seu endereço de email.
select_magazine: Selecione uma magazine
go_to_search: Ir para a procura
go_to_footer: Ir para o rodapé
subscribed: Subscrito
all: Tudo
logout: Terminar sessão
classic_view: Vista clássica
compact_view: Vista compacta
chat_view: Vista de chat
tree_view: Vista de árvore
cards_view: Vista de cartão
3h: 3h
6h: 6h
1d: 1d
1m: 1m
links: Links
articles: Artigos
1w: 1s
photos: Fotos
videos: Videos
report: Reportar
share: Partilhar
copy_url_to_fediverse: Copiar url para o Fediverso
share_on_fediverse: Partilhar no Fediverso
edit: Editar
are_you_sure: Tem a certeza?
moderate: Moderar
reason: Motivo
edit_link: Editar link
edit_article: Editar artigo
delete: Apagar
edit_post: Editar postagem
edit_comment: Editar comentário
settings: Definições
general: Geral
profile: Perfil
blocked: Bloqueado
reports: Relatórios
notifications: Notificações
messages: Mensagens
homepage: Página principal
hide_adult: Esconder conteúdo adulto
featured_magazines: Magazines em destaque
privacy: Privacidade
show_profile_followings: Mostrar utilizadores seguidos
notify_on_new_entry_reply: Notificar-me de comentários nos meus tópicos
notify_on_new_post_reply: Notificar-me de respostas às minhas postagens
notify_on_new_post_comment_reply: Notificar-me de respostas aos meus comentários em
postagens
notify_on_new_entry: Notificar-me de novos tópicos em magazines subscritos
save: Guardar
about: Sobre
old_email: Email antigo
new_email: Novo email
new_email_repeat: Confirmar o novo email
current_password: Password atual
new_password: Nova password
new_password_repeat: Confirmar nova password
change_email: Alterar email
change_password: Alterar password
expand: Expandir
collapse: Colapsar
domains: Domínios
error: Erro
votes: Votos
dark: Escuro
light: Claro
solarized_light: Claro solarizado
solarized_dark: Escuro solarizado
font_size: Tamanho da letra
size: Tamanho
boosts: Upvotos
yes: Sim
no: Não
show_magazines_icons: Mostrar os ícones das magazines
show_thumbnails: Mostrar miniaturas
rounded_edges: Cantos redondos
removed_thread_by: removeu tópico por
removed_comment_by: removeu tópico por
restored_comment_by: restaurou comentário por
removed_post_by: removeu a postagem por
restored_post_by: restaurou postagem por
he_banned: banir
he_unbanned: desbanir
read_all: Ler tudo
show_all: Mostrar tudo
gold_popular_entry: Tópico popular
gold_popular_entry_desc: Tópico visitado 10000 vezes
gold_magazine: Magazine popular
gold_magazine_desc: Magazine com 100 seguidores
gold_entry_month_desc: O melhor tópico do mês
gold_comment_month: Comentário do mês
gold_comment_month_desc: O melhor comentário do mês
gold_post_month: Postagem do mês
gold_post_month_desc: A melhor postagem do mês
added_new_thread: Adicionado um novo tópico
edited_thread: Editado um tópico
mod_remove_your_thread: Um moderador removeu o seu tópico
added_new_comment: Adicionado um novo comentário
edited_comment: Editado um comentário
replied_to_your_comment: Respondeu ao seu comentário
mod_deleted_your_comment: Um moderador apagou o seu comentário
added_new_post: Adicionado uma nova postagem
mod_remove_your_post: Um moderador removeu a sua postagem
added_new_reply: Adicionada uma nova resposta
wrote_message: Escreva uma mensagem
banned: Baniu-o
removed: Removido por um moderador
mentioned_you: Mencionou-o
comment: Comentário
post: Postagem
ban_expired: O ban expirou
purge: Limpar
send_message: Enviar mensagem
message: Mensagem
infinite_scroll: Scroll infinito
show_top_bar: Mostrar barra do topo
subject_reported: O conteúdo foi reportado.
sidebar_position: Posição da barra lateral
left: Esquerda
right: Direita
federation: Federação
status: Estado
on: Ligado
instances: Instâncias
upload_file: Carregar ficheiro
from_url: Do url
magazine_panel: Painel da magazine
reject: Rejeitar
approve: Aprovar
ban: Banir
filters: Filtros
approved: Aprovado
add_moderator: Adicionar moderador
add_badge: Adicionar distintivo
bans: Expulsões
created: Criado
expires: Expira
perm: Perm
expired_at: Expirou em
add_ban: Adicionar expulsão
trash: Lixo
icon: Ícone
pin: Fixar
unpin: Desafixar
change_magazine: Alterar magazine
change_language: Alterar idioma
change: Alterar
pinned: Fixado
preview: Previsualizar
article: Artigo
reputation: Reputação
note: Nota
users: Utilizadores
content: Conteúdo
week: Semana
weeks: Semanas
month: Mês
months: Meses
year: Ano
federated: Federado
local: Local
dashboard: Painel
contact_email: Email de contato
meta: Meta
instance: Instância
pages: Páginas
FAQ: FAQ
type_search_term: Introduza termo de pesquisa
federation_enabled: Federação ativada
registrations_enabled: Registos ativados
registration_disabled: Registos desativados
restore: Restaurar
type.smart_contract: Contrato inteligente
active: Ativo
agree_terms: Consentimento dos %terms_link_start%Termos e Condições%terms_link_end%
e %policy_link_start%Política de Privacidade%policy_link_end%
check_email: Verifique o seu email
comments_count: '{0}comentários|{1}comentário|]1,Inf[ comentários'
reset_check_email_desc: Se uma conta com o seu email existir, então um email será
enviado com um link para redefinir a sua password. Este link irá expirar em %expire%.
replies: Respostas
markdown_howto: Como funciona o editor?
email_confirm_expire: O link irá expirar numa hora.
federated_magazine_info: A magazine do servidor federado pode estar incompleta.
remember_me: Lembrar-me
username: Username
table_view: Vista de tabela
gold_entry_month: Tópico do mês
edited_post: Editada uma postagem
12h: 12h
gold_ranking_desc: 1º lugar no ranking
deleted: Apagado pelo autor
1y: 1a
mod_log_alert: No Modlog pode encontrar conteúdo removido drasticamente por moderadores.
Por favor saiba o que está a fazer.
sticky_navbar: Barra de navegação fixa
copy_url: Copiar url
off: Desligado
edit_photo: Editar foto
rejected: Rejeitado
appearance: Aparência
done: Feito
show_profile_subscriptions: Mostrar subscrições de magazines
writing: Escrita
notify_on_new_entry_comment_reply: Notificar-me de respostas ao meus comentários em
tópicos
admin_panel: Painel de administração
notify_on_new_posts: Notificar-me de novas postagens numa magazine subscrita
theme: Tema
show_users_avatars: Mostrar os avatars dos utilizadores
restored_thread_by: restaurou tópico por
ranking_disabled: Ranking está desativado