Merge branch 'master' of github.com:coralproject/talk into featured-comments

This commit is contained in:
Belen Curcio
2017-07-05 12:19:09 -03:00
20 changed files with 620 additions and 146 deletions
@@ -1,11 +1,41 @@
.Reply {
position: relative;
.root {
margin-top: 16px;
margin-left: 20px;
margin-bottom: 15px;
position: relative;
}
.Comment {
margin-bottom: 15px;
position: relative;
.rootLevel0 {
margin-left: 0px;
}
.comment {
padding-left: 20px;
}
.commentLevel0 {
padding-left: 0px;
}
.commentLevel1 {
border-left: 3px #212121 solid;
}
.commentLevel2 {
border-left: 2px #6a6a6a solid;
}
.commentLevel3 {
border-left: 2px #9e9e9e solid;
}
.commentLevel4 {
border-left: 2px #c1c1c1 solid;
}
.highlightedComment {
padding-left: 20px;
border-left: 3px solid rgb(35,118,216);
}
.pendingComment {
@@ -10,6 +10,9 @@ import {can} from 'coral-framework/services/perms';
import {TransitionGroup} from 'react-transition-group';
import cn from 'classnames';
import styles from './Comment.css';
import {THREADING_LEVEL} from '../constants/stream';
import merge from 'lodash/merge';
import mapValues from 'lodash/mapValues';
import LoadMore from './LoadMore';
import {getEditableUntilDate} from './util';
@@ -304,6 +307,7 @@ export default class Comment extends React.Component {
postDontAgree,
setActiveReplyBox,
activeReplyBox,
loadMore,
deleteAction,
disableReply,
maxCharCount,
@@ -317,9 +321,12 @@ export default class Comment extends React.Component {
const view = this.getVisibileReplies();
const {loadingState} = this.state;
const isReply = !!parentId;
const isPending = comment.id.indexOf('pending') >= 0;
const isHighlighted = highlighted === comment.id;
const hasMoreComments = comment.replies && (comment.replies.hasNextPage || comment.replies.nodes.length > view.length);
const replyCount = this.hasIgnoredReplies() ? '' : comment.replyCount;
const moreRepliesCount = this.hasIgnoredReplies() ? -1 : comment.replyCount - view.length;
const flagSummary = getActionSummary('FlagActionSummary', comment);
const dontAgreeSummary = getActionSummary(
'DontAgreeActionSummary',
@@ -332,13 +339,8 @@ export default class Comment extends React.Component {
myFlag = dontAgreeSummary.find((s) => s.current_user);
}
let commentClass = parentId
? `reply ${styles.Reply}`
: `comment ${styles.Comment}`;
commentClass += comment.id.indexOf('pending') >= 0 ? ` ${styles.pendingComment}` : '';
/**
* classNamesToAdd
* conditionClassNames
* adds classNames based on condition
* classnames is an array of objects with key as classnames and value as conditions
* i.e:
@@ -348,34 +350,44 @@ export default class Comment extends React.Component {
*
* This will add myClassName to comments tagged with STAFF TAG.
* **/
const classNamesToAdd = commentClassNames.reduce((acc, className) => {
let res = [];
// Adding classNames based on tags
Object.keys(className).forEach((cn) => {
const condition = className[cn];
condition.tags.forEach((tag) => {
if (hasTag(comment.tags, tag)) {
res = [...res, cn];
}
});
const conditionalClassNames =
mapValues(merge({}, ...commentClassNames), (condition) => {
if (condition.tags) {
return condition.tags.some((tag) => hasTag(comment.tags, tag));
}
return false;
});
// TODO: Compare rest of the comment obj to the condition if needed
return [...acc, ...res];
}, []);
const rootClassNames = [
'talk-stream-comment-wrapper',
`talk-stream-comment-wrapper-level-${depth}`,
styles.root,
styles[`rootLevel${depth}`],
{
...conditionalClassNames,
[styles.enter]: this.state.animateEnter,
},
];
return (
<div
className={cn(commentClass, 'talk-stream-comment-wrapper', classNamesToAdd, {[styles.enter]: this.state.animateEnter})}
className={cn(...rootClassNames)}
id={`c_${comment.id}`}
style={{marginLeft: depth * 30}}
>
<hr aria-hidden={true} />
{!isReply && <hr aria-hidden={true} />}
<div
className={highlighted === comment.id ? 'highlighted-comment' : ''}
className={cn(
'talk-stream-comment',
`talk-stream-comment-level-${depth}`,
styles.comment,
styles[`commentLevel${depth}`],
{
[styles.pendingComment]: isPending,
[styles.highlightedComment]: isHighlighted,
'talk-stream-pending-comment': isPending,
'talk-stream-highlighted-comment': isHighlighted,
}
)}
>
<AuthorName author={comment.user} className={'talk-stream-comment-user-name'} />
{isStaff(comment.tags) ? <TagLabel>Staff</TagLabel> : null}
@@ -493,7 +505,7 @@ export default class Comment extends React.Component {
charCountEnable={charCountEnable}
maxCharCount={maxCharCount}
setActiveReplyBox={setActiveReplyBox}
parentId={parentId || comment.id}
parentId={(depth < THREADING_LEVEL) ? comment.id : parentId}
addNotification={addNotification}
postComment={postComment}
currentUser={currentUser}
@@ -536,7 +548,7 @@ export default class Comment extends React.Component {
<div className="talk-load-more-replies">
<LoadMore
topLevel={false}
replyCount={replyCount}
replyCount={moreRepliesCount}
moreComments={hasMoreComments}
loadMore={this.loadNewReplies}
loadingState={loadingState}
@@ -5,27 +5,15 @@ import t from 'coral-framework/services/i18n';
import cn from 'classnames';
class LoadMore extends React.Component {
initialState = true;
replyCountFormat = (count) => {
if (!count) {
return t('framework.view_all_replies_unknown_number');
return t('framework.show_all_replies');
}
if (count === 1) {
return t('framework.view_reply');
return t('framework.show_1_more_reply');
}
if (this.initialState) {
return t('framework.view_all_replies_initial', count);
} else {
return t('framework.view_all_replies', count);
}
}
componentWillReceiveProps(nextProps) {
if (['success', 'error'].indexOf(nextProps.loadingState) >= 0) {
this.initialState = false;
}
return t('framework.show_x_more_replies', count);
}
render () {
@@ -3,7 +3,7 @@ import React, {PropTypes} from 'react';
import t from 'coral-framework/services/i18n';
const NewCount = ({count, loadMore}) => {
return <div className='coral-new-comments coral-load-more'>
return <div className='talk-new-comments talk-load-more'>
{
count ?
<button onClick={loadMore}>
@@ -16,6 +16,7 @@ import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import NewCount from './NewCount';
import {TransitionGroup} from 'react-transition-group';
import {forEachError} from 'coral-framework/utils';
import {getTopLevelParent} from '../graphql/utils';
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
@@ -167,9 +168,7 @@ class Stream extends React.Component {
const open = asset.closedAt === null;
// even though the permalinked comment is the highlighted one, we're displaying its parent + replies
const highlightedComment = comment && comment.parent
? comment.parent
: comment;
const highlightedComment = comment && getTopLevelParent(comment);
const banned = user && user.status === 'BANNED';
const temporarilySuspended =
@@ -3,3 +3,4 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10;
export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS';
export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME';
export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME';
export const THREADING_LEVEL = process.env.TALK_THREADING_LEVEL;
@@ -2,7 +2,7 @@ import React from 'react';
import {gql, compose} from 'react-apollo';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {ADDTL_COMMENTS_ON_LOAD_MORE} from '../constants/stream';
import {ADDTL_COMMENTS_ON_LOAD_MORE, THREADING_LEVEL} from '../constants/stream';
import {
withPostComment, withPostFlag, withPostDontAgree,
withDeleteAction, withIgnoreUser, withEditComment
@@ -91,9 +91,7 @@ class StreamContainer extends React.Component {
}
loadNewReplies = (parent_id) => {
const comment = this.props.root.comment
? this.props.root.comment.parent || this.props.root.comment // highlighted comment.
: this.props.root.asset.comments.nodes.filter((comment) => comment.id === parent_id)[0];
const comment = findCommentInEmbedQuery(this.props.root, parent_id);
return this.props.data.fetchMore({
query: LOAD_MORE_QUERY,
@@ -153,20 +151,35 @@ class StreamContainer extends React.Component {
}
}
const nest = (def, level) => {
let result = '';
for (let x = 0; x < level; x++) {
if (x === 0) {
result += def;
continue;
}
result = result.replace('...nest', def);
}
return result.replace('...nest', '');
};
const commentFragment = gql`
fragment CoralEmbedStream_Stream_comment on Comment {
id
...${getDefinitionName(Comment.fragments.comment)}
replyCount(excludeIgnored: $excludeIgnored)
replies(excludeIgnored: $excludeIgnored) {
nodes {
id
...${getDefinitionName(Comment.fragments.comment)}
${nest(`
replyCount(excludeIgnored: $excludeIgnored)
replies(excludeIgnored: $excludeIgnored) {
nodes {
id
...${getDefinitionName(Comment.fragments.comment)}
...nest
}
hasNextPage
startCursor
endCursor
}
hasNextPage
startCursor
endCursor
}
`, THREADING_LEVEL)}
}
${Comment.fragments.comment}
`;
@@ -205,16 +218,19 @@ const LOAD_MORE_QUERY = gql`
nodes {
id
...${getDefinitionName(Comment.fragments.comment)}
replyCount(excludeIgnored: $excludeIgnored)
replies(limit: 3, excludeIgnored: $excludeIgnored) {
nodes {
id
...${getDefinitionName(Comment.fragments.comment)}
${nest(`
replyCount(excludeIgnored: $excludeIgnored)
replies(limit: 3, excludeIgnored: $excludeIgnored) {
nodes {
id
...${getDefinitionName(Comment.fragments.comment)}
...nest
}
hasNextPage
startCursor
endCursor
}
hasNextPage
startCursor
endCursor
}
`, THREADING_LEVEL)}
}
hasNextPage
startCursor
@@ -229,9 +245,12 @@ const fragments = {
fragment CoralEmbedStream_Stream_root on RootQuery {
comment(id: $commentId) @include(if: $hasComment) {
...CoralEmbedStream_Stream_comment
parent {
...CoralEmbedStream_Stream_comment
}
${nest(`
parent {
...CoralEmbedStream_Stream_comment
...nest
}
`, THREADING_LEVEL)}
}
asset(id: $assetId, url: $assetUrl) {
id
+23 -19
View File
@@ -1,21 +1,23 @@
import update from 'immutability-helper';
import {THREADING_LEVEL} from '../constants/stream';
function applyToCommentsOrigin(root, callback) {
if (root.comment) {
if (root.comment.parent) {
return update(root, {
comment: {
parent: {
$apply: (node) => callback(node),
},
},
});
let comment = root.comment;
console.log(comment);
for (let depth = 0; depth <= THREADING_LEVEL; depth++) {
let changes = {$apply: (node) => node ? callback(node) : node};
for (let i = 0; i < depth; i++) {
changes = {parent: changes};
}
console.log(changes);
comment = update(comment, changes);
}
return update(root, {
comment: {
$apply: (node) => callback(node),
},
});
return {
...root,
comment,
};
}
return update(root, {
asset: {$apply: (asset) => callback(asset)},
@@ -97,6 +99,13 @@ export function removeCommentFromEmbedQuery(root, id) {
return applyToCommentsOrigin(root, (origin) => findAndRemoveComment(origin, id));
}
export function getTopLevelParent(comment) {
if (comment.parent) {
return getTopLevelParent(comment.parent);
}
return comment;
}
function findComment(nodes, callback) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
@@ -119,12 +128,7 @@ export function findCommentInEmbedQuery(root, callbackOrId) {
callback = (node) => node.id === callbackOrId;
}
if (root.comment) {
if (callback(root.comment)) {
return root.comment;
}
if (root.comment.parent && callback(root.comment.parent)) {
return root.comment.parent;
}
return findComment([getTopLevelParent(root.comment)], callback);
}
if (!root.asset.comments) {
return false;
+25 -19
View File
@@ -26,7 +26,7 @@ body {
}
button {
margin: 5px 10px 5px 0px;
margin: 5px 0px 5px 0px;
background: none;
padding: 0px;
border: none;
@@ -266,11 +266,6 @@ hr {
float: right;
}
.highlighted-comment {
padding-left: 10px;
border-left: 3px solid rgb(35,118,216);
}
/* Tag Labels */
.coral-plugin-tag-label {
@@ -282,26 +277,16 @@ hr {
border-radius: 2px;
}
/* Reply styles */
.comment .reply {
margin: 0px 0px 10px 20px;
}
/* Comment Action Styles */
.commentActionsRight, .replyActionsRight {
display: flex;
justify-content: flex-end;
width: 30%;
}
.commentActionsLeft, .replyActionsLeft {
display: flex;
justify-content: flex-start;
float: left;
width: 70%;
}
.comment__action-container .material-icons {
@@ -422,9 +407,11 @@ button.comment__action-button[disabled],
}
.talk-load-more button {
width: 100%;
text-align: center;
color: #FFF;
background-color: #2376D8;
border-radius: 2px;
cursor: pointer;
padding: 10px;
border-radius: 2px;
@@ -437,23 +424,42 @@ button.comment__action-button[disabled],
background-color: #4399FF;
}
.talk-load-more-replies, .coral-new-comments {
.talk-new-comments {
width: 100%;
display: flex;
justify-content: center;
cursor: pointer;
}
.coral-new-comments {
.talk-load-more-replies {
width: 100%;
padding-left: 20px;
box-sizing: border-box;
}
.talk-new-comments {
position: relative;
top: 1.8em;
z-index: 100;
}
.talk-load-more-replies button.talk-load-more, .coral-new-comments button.talk-load-more{
.talk-load-more-replies .talk-load-more-button {
background-color: transparent;
color: #979797;
border: #979797 solid 1px;
border-radius: 2px;
}
.talk-load-more-replies .talk-load-more:hover button {
background-color: #979797;
color: white;
}
.talk-new-comments button.talk-load-more{
width: initial;
}
@media (min-device-width : 300px) and (max-device-width : 420px) {
.commentActionsLeft.comment__action-container .coral-plugin-replies-reply-button {
visibility: collapse;
+4 -1
View File
@@ -4,13 +4,15 @@ import get from 'lodash/get';
import merge from 'lodash/merge';
import esTA from '../../../node_modules/timeago.js/locales/es';
import frTA from '../../../node_modules/timeago.js/locales/fr';
import en from '../../../locales/en.yml';
import es from '../../../locales/es.yml';
import fr from '../../../locales/fr.yml';
// Translations are happening at https://translate.lingohub.com/the-coral-project/dashboard
const defaultLanguage = 'en';
const translations = {...en, ...es};
const translations = {...en, ...es, ...fr};
let lang;
let timeagoInstance;
@@ -40,6 +42,7 @@ function init() {
}
ta.register('es', esTA);
ta.register('fr', frTA);
timeagoInstance = ta();
}
@@ -5,6 +5,8 @@ import {can} from 'coral-framework/services/perms';
import {PopupMenu, Button} from 'coral-ui';
import ClickOutside from 'coral-framework/components/ClickOutside';
import cn from 'classnames';
import styles from './styles.css';
const name = 'coral-plugin-flags';
@@ -146,14 +148,17 @@ export default class FlagButton extends Component {
<button
ref={(ref) => this.flagButton = ref}
onClick={!this.props.banned && !flaggedByCurrentUser && !localPost ? this.onReportClick : null}
className={`${name}-button`}>
className={cn(`${name}-button`, styles.button)}>
{
flagged
? <span className={`${name}-button-text`}>{t('reported')}</span>
: <span className={`${name}-button-text`}>{t('report')}</span>
}
<i className={`${name}-icon material-icons ${flagged && 'flaggedIcon'}`}
style={flagged ? styles.flaggedIcon : {}}
<i className={
cn(`${name}-icon`, 'material-icons', {
flaggedIcon: flagged,
[styles.flaggedIcon]: flagged,
})}
aria-hidden={true}>flag</i>
</button>
{
@@ -214,12 +219,3 @@ export default class FlagButton extends Component {
);
}
}
const styles = {
flaggedIcon: {
color: '#F00'
},
unflaggedIcon: {
color: 'inherit'
}
};
@@ -0,0 +1,7 @@
.button {
margin: 5px 0px 5px 10px;
}
.flaggedIcon {
color: #f00
}
+3 -3
View File
@@ -234,9 +234,9 @@ en:
success_bio_update: "Your biography has been updated"
success_name_update: "Your username has been updated"
success_update_settings: "The changes you have made have been applied to the comment stream on this article"
view_all_replies_unknown_number: "view all replies"
view_all_replies: "view {0} replies"
view_all_replies_initial: "view all {0} replies"
show_all_replies: Show all replies
show_1_more_reply: Show 1 more reply
show_x_more_replies: Show {0} more replies
view_more_comments: "view more comments"
view_reply: "view reply"
from_settings_page: "From the Profile Page you can see your comment history."
+3 -3
View File
@@ -231,9 +231,9 @@ es:
success_bio_update: "Tu biografia fue actualizada"
success_name_update: "Tu nombre de usuario ha sido actualizado"
success_update_settings: "La configuración de este articulo fue actualizada"
view_all_replies_unknown_number: "ver todas las respuestas"
view_all_replies: "ver {0} respuestas"
view_all_replies_initial: "ver todas las {0} respuestas"
show_all_replies: "Mostrar todas las respuestas"
show_1_more_reply: "Mostrar 1 respuestas"
show_x_more_replies: "Mostrar {0} respuestas más"
view_more_comments: "Ver más comentarios"
view_reply: "ver respuesta"
from_settings_page: "Desde la página de configuración puedes ver tu historia de comentarios."
+355
View File
@@ -0,0 +1,355 @@
fr:
bandialog:
are_you_sure: "Êtes-vous sûr de vouloir bannir {0}?"
ban_user: "Bannir l'utilisateur ?"
banned_user: "Utilisateur banni"
cancel: Annuler
note: "Remarque: bannir cet utilisateur rejettera également ce commentaire."
yes_ban_user: "Oui, bannir cet utilisateur"
bio_offensive: "Cette biographie est offensante"
cancel: Annuler
characters_remaining: "caractères restants"
comment:
anon: Anonyme
ban_user: "Bannir utilisateur"
comment: "Publier un commentaire"
flagged: signalé
view_context: "Afficher le contexte"
comment_box:
post: "Publier"
cancel: "Annuler"
reply: "Répondre"
comment: "Publier un commentaire"
name: "Nom"
comment_post_notif: "Votre commentaire a été publié."
comment_post_notif_premod: "Merci d'avoir envoyé un commentaire. Notre équipe de modération passera en revue votre commentaire sous peu."
comment_post_banned_word: "Votre commentaire contient un ou plusieurs mots qui ne sont pas autorisés, il ne sera pas publié. Si vous pensez qu'il y a une erreur, contactez notre équipe de modération."
characters_remaining: "caractères restants"
comment_is_best: "Ce commentaire est l'un des meilleurs"
comment_offensive: "Ce commentaire est offensant"
comment_plural: Commentaires
comment_post_banned_word: "Votre commentaire contient un ou plusieurs mots qui ne sont pas autorisés, il ne sera pas publié. Si vous pensez qu'il y a une erreur, contactez notre équipe de modération."
comment_post_notif: "Votre commentaire a été publié."
comment_post_notif_premod: "Merci d'avoir envoyé un commentaire. Notre équipe de modération passera en revue votre commentaire sous peu."
community:
account_creation_date: "Date de création du compte"
active: Actif
admin: Administrateur
ads_marketing: "Cela ressemble à du marketing / un message publicitaire"
are_you_sure: "Êtes-vous sûr de vouloir bannir {0} ?"
ban_user: "Bannir l'utilisateur ?"
banned: Banni
banned_user: "Utilisateur banni"
cancel: Signalé
dont_like_username: "Je n'aime pas ce nom d'utilisateur"
flaggedaccounts: "Noms d'utilisateurs signalés"
flags: Signalements
impersonating: "Cet utilisateur se fait passer pour quelqu'un d'autre"
loading: "Chargement des résultats"
moderator: Modérateur
newsroom_role: "Rôle de la salle de presse"
no_flagged_accounts: "La liste des comptes signalés est vide."
no_results: "Aucun utilisateur n'a été trouvé avec ce nom d'utilisateur ou cette adresse de messagerie. Ils se cachent !"
note: "Remarque: bannir cet utilisateur ne lui permettra pas de modifier les commentaires ou de supprimer quoi que ce soit."
offensive: "Ce commentaire est offensant"
other: Autre
people: Gens
role: "Sélectionnez le rôle ..."
select_status: "Sélectionnez l'état ..."
spam_ads: "Spam / Annonces"
staff: "Équipe"
status: Statut
username_and_email: "Nom d'utilisateur et e-mail"
yes_ban_user: "Oui bannir l'utilisateur"
configure:
apply: Appliquer
banned_word_text: "Les commentaires qui contiennent ces mots ou ces expressions (non sensibles à la casse) seront automatiquement supprimés du flux de commentaires. Tapez un mot et appuyez sur Entrée ou Tabulation pour ajouter. En option, envoyez une liste séparée par des virgules."
banned_words_title: "Liste des mots interdits"
close: "Fermer"
close_after: "Fermer les commentaires après"
close_stream: "Fermer le fil"
close_stream_configuration: "Ce fil de commentaires est actuellement fermé. En ouvrant ce fil commentaire, de nouveaux commentaires peuvent être soumis et publiés."
closed_comments_desc: "Écrivez un message à afficher lorsque votre fil de commentaires est fermé et n'accepte plus de nouveaux commentaires."
closed_comments_label: "Écrire un message..."
closed_stream_settings: "Message de fil de commentaires fermé"
comment_count_error: "S'il vous plait, entrez un nombre valide."
comment_count_header: "Limiter la longueur d'un commentaire"
comment_count_text_post: caractères
comment_count_text_pre: "Les commentaires seront limités à"
comment_settings: Paramètres
comment_stream: "Fil de commentaires"
comment_stream_will_close: "Le fil de commentaires va se fermer dans"
community: Communauté
configure: Configurer
copy_and_paste: "Copiez et collez le code ci-dessous dans votre CMS pour intégrer votre boîte de commentaires dans vos articles."
custom_css_url: "URL CSS personnalisée"
custom_css_url_desc: "URL d'une feuille de style CSS qui remplacera les styles par défaut d'intégration des commentaires. Peut être interne ou externe."
dashboard: Tableau de bord
days: Journées
description: "En tant qu'administrateur, vous pouvez personnaliser les paramètres du fil de commentaires pour cet élément."
domain_list_text: "Entrez les domaines que vous souhaitez autoriser pour Talk, par exemple vos environnements locaux de production et de production (ex : localhost: 3000 staging.domain.com domain.com)."
domain_list_title: "Domaines autorisés"
edit_comment_timeframe_heading: "Modifier l'horodatage des commentaires"
edit_comment_timeframe_text_pre: "Les commentateurs auront"
edit_comment_timeframe_text_post: "secondes pour modifier leurs commentaires."
embed_comment_stream: "Intégrer le fil"
enable_premod_links_text: "Les modérateurs doivent approuver tout commentaire contenant un lien avant sa publication."
enable_pre_moderation: "Activer modération a priori"
enable_pre_moderation_text: "Les modérateurs doivent approuver tout commentaire avant qu'il ne soit publié."
enable_premod_links: "Modérer les commentaires contenant des liens"
enable_premod: "Activer la modération a priori"
enable_premod_description: "Les modérateurs doivent approuver tout commentaire avant sa publication."
enable_premod_links_description: "Les modérateurs doivent approuver tout commentaire contenant un lien avant sa publication."
enable_questionbox: "Posez une question aux lecteurs."
enable_questionbox_description: "Cette question apparaîtra en haut de ce fil de commentaires. Demandez aux lecteurs de s'exprimer sur un sujet évoqué dans l'article ou posez les termes de la discussion, etc."
hours: Heures
include_comment_stream: "Inclure la description du fil de commentaires pour les lecteurs"
include_comment_stream_desc: "Écrivez un message à ajouter au haut de votre flux de commentaires. Publiez un sujet de discussion, avec des suggestion sur la tenue des débats, etc."
include_text: "Entrez votre texte ici."
include_question_here: "Écrivez votre question ici."
moderate: Modérer
moderation_settings: "Paramètres de la modération"
open: "Ouvert"
open_stream: "Ouvrir le fil"
open_stream_configuration: "Ce fil de commentaires est actuellement ouvert. En fermant ce fil de commentaires, aucun nouveau commentaire ne pourra être envoyé, les commentaires précédents seront toujours affichés."
require_email_verification: "Exiger une vérification de l'e-mail"
require_email_verification_text: "Les nouveaux utilisateurs doivent vérifier leur adresse e-mail avant de commenter."
save_changes: "Sauvegarder les changements"
shortcuts: Raccourcis
sign_out: "Se Déconnecter"
stories: Histoires
stream_settings: "Paramètres du fil"
suspect_word_title: "Liste des mots suspects"
suspect_word_text: "Les commentaires contenant ces mots ou expressions (non sensibles à la casse) seront mis en évidence dans le flux de commentaires. Tapez un mot et appuyez sur Entrée ou Tabulation pour ajouter. En option, entrez une liste séparée par des virgules."
tech_settings: "Paramètres techniques"
title: "Configurer le fil de commentaires"
weeks: Semaines
wordlist: "Mots interdits"
continue: Continuer
dashboard:
auto_update: "Les données sont automatiquement mises à jour toutes les cinq minutes ou lorsque vous rechargez."
comment_count: commentaires
flags: signalements
most_flags: "Articles avec le plus de signalements"
most_conversations: "Articles avec le plus de conversations"
next_update: "{0} minutes jusqu'à la prochaine mise à jour."
no_activity: "Il n'y a eu aucun commentaire nulle part dans les cinq dernières minutes."
no_flags: "Il n'y a pas eu aucun signalement au cours des 5 dernières minutes! Hooray !"
no_likes: "Il n'y a pas eu de \"J'aime\" au cours des 5 dernières minutes. Tout est calme."
done: Terminé
edit_comment:
body_input_label: "Modifier ce commentaire"
save_button: "Sauvegarder les changements"
edit_window_expired: "Vous ne pouvez plus modifier ce commentaire. La fenêtre de temps pour le faire a expiré. Pourquoi ne pas en publier un autre ?"
edit_window_expired_close: "Fermer"
edit_window_timer_prefix: "Fenêtre d'édition :"
second: "seconde"
seconds_plural: "secondes"
unexpected_error: "Erreur inattendue lors de l'enregistrement des modifications. Désolé !"
embedlink:
copy: "Copier dans le presse-papier"
error:
COMMENT_TOO_SHORT: "Votre commentaire doit contenir quelque chose"
NOT_AUTHORIZED: "Vous n'êtes pas autorisé à effectuer cette action."
NO_SPECIAL_CHARACTERS: "Les noms d'utilisateur ne peuvent contenir que des lettres, des chiffres et \"_\" seulement"
PASSWORD_LENGTH: "Le mot de passe est trop court"
PROFANITY_ERROR: "Les noms d'utilisateur ne doivent pas contenir de mots offensants. Veuillez contacter l'administrateur si vous pensez qu'il y a une erreur."
USERNAME_IN_USE: "Ce nom d'utilisateur est déjà pris"
USERNAME_REQUIRED: "Doit entrer un nom d'utilisateur"
EDIT_WINDOW_ENDED: "Vous ne pouvez plus modifier ce commentaire. La fenêtre de temps pour le faire a expiré."
EDIT_USERNAME_NOT_AUTHORIZED: "Vous n'avez pas la permission de mettre à jour votre nom d'utilisateur."
EMAIL_IN_USE: "Adresse e-mail déjà utilisée"
EMAIL_REQUIRED: "Une adresse email est requise"
LOGIN_MAXIMUM_EXCEEDED: "Vous avez effectué trop de tentatives infructueuses pour entrer votre mot de passe. S'il vous plaît, attendez."
PASSWORD_REQUIRED: "Doit entrer un mot de passe"
COMMENTING_CLOSED: "Les commentaires sont déjà fermés"
NOT_FOUND: "Ressource introuvable"
INVALID_ASSET_URL: "L'URL est invalide"
email: "Pas une adresse e-mail valide"
confirm_password: "Les mots de passe ne correspondent pas. Vérifiez à nouveau"
network_error: "Échec de connexion au serveur. Vérifiez votre connexion Internet et réessayez."
email_not_verified: "L'adresse e-mail {0} n'est pas vérifiée."
email_password: "Combinaison e-mail / mot de passe incorrecte."
organization_name: "Le nom de l'organisation ne peut contenir que des lettres ou des chiffres."
password: "Le mot de passe doit être d'au moins 8 caractères"
username: "Les noms d'utilisateur ne peuvent contenir que des chiffres, des lettres et \"_\""
flag_comment: "Signaler un commentaire"
flag_reason: "Motif du signalement (facultatif)"
flag_username: "Signaler un nom d'utilisateur"
framework:
banned_account_msg: "Votre compte est actuellement suspendu. Cela signifie que vous ne pouvez pas Aimer Signaler ou Écrire des commentaires. Contactez-nous si vous avez des questions."
comment: commentaire
comment_is_ignored: "Ce commentaire est caché car vous avez ignoré cet utilisateur."
comments: commentaires
configure_stream: "Configure le fil"
content_not_available: "Ce contenu n'est pas disponible"
edit_name:
button: Soumettre
error: "Les noms d'utilisateur ne peuvent contenir que des chiffres, des lettres et \"_\""
label: "Nouveau nom d'utilisateur"
msg: "Votre compte est actuellement suspendu car votre nom d'utilisateur a été jugé inapproprié. Pour restaurer votre compte, entrez un nouveau nom d'utilisateur. Contactez-nous si vous avez des questions."
ignored_users: "Utilisateurs ignorés"
my_comments: "Mes commentaires"
my_profile: "Mon profil"
new_count: "Voir {0} nouveau {1}"
profile: Profil
show_all_comments: "Afficher tous les commentaires"
success_bio_update: "Votre biographie a été mise à jour"
success_name_update: "Votre nom d'utilisateur a été mis à jour"
success_update_settings: "Les modifications que vous avez apportées ont été appliqué es au fil de commentaires sur cet article"
view_all_replies: "Voir {0} réponses"
view_all_replies_initial: "Voir toutes les {0} réponses"
view_more_comments: "Voir plus de commentaires"
view_reply: "Voir la réponse"
from_settings_page: "Dans la page Profil, vous pouvez voir l'historique de vos commentaires."
like: "J'aime"
loading_results: "Chargement des résultats"
marketing: "Cela ressemble à du marketing / une publicité"
moderate_this_stream: "Modifiez ce fil"
modqueue:
account: "Signalements du compte"
actions: Actions
all: tous
all_streams: "Tous les fils"
approve: "Approuver"
approved: "Approuvé"
ban_user: "Bannir"
billion: B
close: Fermer
dont_like_username: "N'aime pas le nom d'utilisateur"
empty_queue: "Plus de commentaires à modérer ! Vous avez terminé, allez prendre un ☕️"
flagged: signalé
impersonating: "problème d'identité"
less_detail: "Moins de détails"
likes: "J'aime"
million: M
mod_faster: "Modérer plus rapidement avec les raccourcis clavier"
moderate: "Modérer →"
more_detail: "Plus de détails"
newest_first: "Le plus récent d'abord"
navigation: Navigation
next_comment: "Aller au prochain commentaire"
offensive: Offensant
oldest_first: "Le plus ancien d'abord"
other: Autre
premod: Pre-modérer
prev_comment: "Aller au commentaire précédent"
reject: "Rejeter"
rejected: "Rejeté"
select_stream: "Sélectionnez le fil"
shift_key:
shortcuts: Raccourcis
show_shortcuts: "Afficher les raccourcis"
singleview: "Passer en mode d'édition de commentaire unique"
spam_ads: Spam / Publicités
thismenu: "Ouvrir ce menu"
thousand: k
try_these: "Essayez ces"
view_more_shortcuts: "Afficher plus de raccourcis"
my_comment_history: "Mon historique de commentaires"
name: Nom
no_agree_comment: "Je ne suis pas d'accord avec ce commentaire"
no_like_bio: "Je n'aime pas cette biographie"
no_like_username: "Je n'aime pas ce nom d'utilisateur"
other: Autre
permalink: Lien
personal_info: "Ce commentaire révèle des informations personnelles identifiables"
post: Publier
profile: Profil
profile_settings: "Paramètres du profil"
reply: Répondre
report: Signaler
report_notif: "Merci de signaler ce commentaire. Notre équipe de modération a é té informée."
report_notif_remove: "Votre signalement a été supprimé."
reported: Signalé
set_best: "Sélectionner comme le meilleur"
settings:
all_comments: "Tous les commentaires"
from_settings_page: "Dans la page Profil, vous pouvez voir l'historique de vos commentaires."
my_comment_history: "Mon historique de commentaires"
profile: Profil
profile_settings: "Paramètres du profil"
sign_in: "Se connecter"
to_access: "Accéder au profil"
user_no_comment: "Vous n'avez jamais laissé de commentaire. Rejoignez la conversation!"
stream:
temporarily_suspended: "Conformément à la charte d'utilisation des commentaires de {0}, votre compte a été temporairement suspendu. Merci de revenir dans la conversation {1}."
step_1_header: "Signaler un problème"
step_2_header: "Aidez-nous à comprendre"
step_3_header: "Merci pour votre participation"
streams:
all: Tous
article: Récit
closed: Fermé
empty_result: "Aucun contenu ne correspond à cette recherche. Peut-être essayer d'élargir votre recherche ?"
filter_streams: "Filtrer les flux"
newest: Le plus récent
oldest: Le plus ancien
open: Ouvrir
pubdate: "Date de publication"
search: Recherche
sort_by: "Trier par"
status: "Statut du fil"
stream_status: "Statut du fil"
suspenduser:
bio: Bio
cancel: "Annuler"
days: "{0} jours"
description_0: "Voulez-vous interdire temporairement cet utilisateur en raison de leur {0} ? Cela dissimulera temporairement leurs commentaires jusqu'à ce qu'ils réécrivent leurs {0}."
description_1: "Suspendre cet utilisateur désactivera temporairement son compte et cachera tous ses commentaires sur le site."
description_notify: "Suspendre cet utilisateur désactivera temporairement son compte et cachera tous ses commentaires sur le site."
description_reject: "Voulez-vous interdire temporairement cet utilisateur en raison de leur {0}? Cela dissimulera temporairement leurs commentaires jusqu'à ce qu'ils réécrivent leurs {0}."
description_suspend: "Vous suspendez {0}. Ce commentaire sera rejeté, et {0} ne sera pas autorisé à aimer, à signaler, à répondre ou à publier jusqu'à ce que la suspension soit levée."
email: "Un autre membre de la communauté a récemment signalé votre nom d'utilisateur pour examen. En raison de son contenu, votre utilisateur a été rejeté. Cela signifie que vous ne pouvez plus commenter ou signaler du contenu jusqu'à ce que vous changiez votre nom d'utilisateur. Veuillez nous envoyer un e-mail si vous avez des questions ou des préoccupations."
email_subject: "Votre compte a été suspendu"
email_message_reject: "Un autre membre de la communauté a récemment signalé votre nom d'utilisateur pour examen. En raison de son contenu, votre nom d'utilisateur a été rejeté. Cela signifie que vous ne pouvez plus commenter, ni signaler du contenu jusqu'à ce que vous changiez votre nom d'utilisateur. Veuillez nous envoyer un e-mail si vous avez des questions ou des préoccupations."
email_message_suspend: "Cher {0},\n\nConformément à la charte des commentaires de {1}, votre compte a été temporairement suspendu. Pendant cette période, vous ne pourrez pas commenter, signaler ou participer à d'autres commentaires. \n\nMerci de revenir dans la conversation {2}."
error_email_message_empty: "Vous devez spécifier un message dans l'e-mail."
hours: "{0} heures"
no_cancel: "Pas d'annulation"
notify_suspend_until: "L'utilisateur {0} a été temporairement suspendu. Cette suspension se terminera automatiquement {1}."
one_hour: "1 heure"
send: Envoyer
select_duration: "Sélectionnez la durée de la suspension"
suspend_user: "Suspendre l'utilisateur"
title: "Suspendre un utilisateur"
title_0: "Nous avons remarqué que vous avez rejeté un nom d'utilisateur"
title_1: "Aviser l'utilisateur de sa suspension temporaire"
title_notify: "Aviser l'utilisateur de sa suspension temporaire"
title_reject: "Nous avons remarqué que vous avez rejeté un nom d'utilisateur"
title_suspend: "Suspendre l'utilisateur"
username: "Nom d'utilisateur"
write_message: "Écrire un message"
yes_suspend: "Oui suspendre"
thank_you: "Nous apprécions vos commentaires. Un modérateur passera en revue votre signalement. Merci pour votre aide."
unset_best: "Déselectionner comme le meilleur"
user:
bio_flags: "Signaler pour cette biographie"
user_bio: "Bio de l'utilisateur"
username_flags: "Signaler pour ce nom d'utilisateur"
user_impersonating: "Cet utilisateur se fait passer pour quelqu'un d'autre"
user_no_comment: "Vous n'avez jamais laissé de commentaire. Rejoignez la conversation !"
username_offensive: "Ce nom d'utilisateur est offensant"
view_conversation: "Afficher la conversation"
install:
initial:
description: "Configurez votre communauté Talk en quelques petites étapes."
submit: "Commencer"
add_organization:
description: "Veuillez nous indiquer le nom de votre organisation. Cela apparaîtra dans les e-mails lors de l'invitation de nouveaux membres dans l'équipe."
label: "Nom de l'organisation"
save: "Sauvegarder"
create:
email: "Adresse e-mail"
username: "Nom d'utilisateur"
password: "Mot de passe"
confirm_password: "Confirmez Le mot de passe"
save: "Sauvegarder"
permitted_domains:
title: "Domaines autorisés"
description: "Entrez les domaines que vous souhaitez autoriser pour Talk, par ex. vos environnements locaux, de déploiement et de production (ex localhost:3000, staging.domain.com, domain.com)."
submit: "Terminer l'installation"
final:
description: "Merci d'avoir installé Talk ! Nous avons envoyé un e-mail pour vérifier votre adresse électronique. Pendant que vous terminez la configuration, vous pouvez commencer à engager vos lecteurs dès maintenant."
launch: "Lancer Talk"
close: "Fermez cet installateur"
@@ -89,3 +89,47 @@ es:
username: Nombre
write_your_username: "Edita tu nombre"
your_username: "Tu nombre aparece en cada comentario que publiques."
fr:
sign_in:
email_verify_cta: "Merci de vérifier votre adresse e-mail."
request_new_verify_email: "Demander un nouvel envoi d'e-mail :"
verify_email: "Merci d'avoir créé un compte ! Nous avons envoyé un courrier électronique à l'adresse que vous avez fournie pour vérifier votre adresse e-mail."
verify_email2: "Vous devez vérifier votre adresse e-mail avant de vous engager auprès de la communauté."
not_you: "Pas vous ?"
logged_in_as: "Connecté en tant que"
facebook_sign_in: "Connectez-vous avec Facebook"
facebook_sign_up: "Inscrivez-vous avec Facebook"
logout: "Se déconnecter"
sign_in: "Se connecter"
sign_in_to_join: "Connectez-vous pour participer à la conversation"
or: "Ou"
email: "Adresse e-mail"
password: "Mot de passe"
forgot_your_pass: "Mot de passe oublié ?"
need_an_account: "Besoin d'un compte ?"
register: "Inscription"
sign_up: "S'inscrire"
confirm_password: "Confirmez Le mot de passe"
username: "Nom d'utilisateur"
already_have_an_account: "Vous avez déjà un compte ?"
recover_password: "Récupérer le mot de passe"
email_in_use: "L'adresse e-mail est déjà utilisée"
email_or_username_in_use: "Adresse e-mail ou nom d'utilisateur déjà utilisé"
required_field: "Ce champ est obligatoire"
passwords_dont_match: "Les mots de passe ne correspondent pas."
special_characters: "Les noms d'utilisateur ne peuvent contenir que des lettres, des chiffres et \"_\" seulement"
sign_in_to_comment: "Connectez-vous pour commenter"
check_the_form: "Formulaire non valide. Veuillez vérifier les champs"
createdisplay:
check_the_form: "Formulaire non valide. Veuillez vérifier les champs"
continue: "Continuez avec le même nom d'utilisateur que sur Facebook"
error_create: "Erreur lors de la modification du nom d'utilisateur"
fake_comment_body: "Ceci est un exemple de commentaire. Les lecteurs peuvent partager leurs pensées et opinions avec les rédactions dans la section des commentaires."
fake_comment_date: "Il y a 1 minute"
if_you_dont_change_your_name: "Si vous ne modifiez pas votre nom d'utilisateur à cette étape, votre nom d'affichage Facebook apparaîtra à côté de tous vos commentaires."
required_field: "champs requis"
save: Sauvegarder
special_characters: "Les noms d'utilisateur ne peuvent contenir que des chiffres, des lettres et \"_\""
username: "Nom d'utilisateur"
write_your_username: "Modifier votre nom d'utilisateur"
your_username: "Votre nom d'utilisateur apparaît sur chaque commentaire que vous publiez."
@@ -66,11 +66,11 @@ export default class PermalinkButton extends React.Component {
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className={cn(`${name}-container`, styles.container)}>
<button
ref={(ref) => this.linkButton = ref}
onClick={this.toggle}
className={`${name}-button`}>
className={cn(`${name}-button`, styles.button)}>
{t('permalink')}
<Icon name="link" />
</button>
@@ -89,7 +89,7 @@ export default class PermalinkButton extends React.Component {
<Button
onClick={this.copyPermalink}
className={cn([
styles.button,
styles.copyButton,
`${name}-copy-button`, {
[styles.success]:copySuccessful,
[styles.failure]: copyFailure
@@ -98,7 +98,7 @@ export default class PermalinkButton extends React.Component {
{copySuccessful && 'Copied'}
{copyFailure && 'Not supported'}
</Button>
</div>
</div>
</ClickOutside>
@@ -42,7 +42,13 @@
}
.button {
margin: 5px 0px 5px 10px;
cursor: pointer;
}
.copyButton {
display: inline-block;
margin: 5px 10px 5px 0px;
float: right;
box-sizing: border-box;
margin: 0;
@@ -52,18 +58,19 @@
height: auto;
padding: 2px;
transition: background-color 0.4s ease;
cursor: pointer;
}
.button:hover{
.copyButton:hover{
color: black;
}
.button.success {
.copyButton.success {
background-color: #00897B;
color: white;
}
.button.failure {
.copyButton.failure {
background-color: #FF5252;
color: white;
}
@@ -74,4 +81,4 @@
.active {
display: block;
}
}
+4 -2
View File
@@ -2,17 +2,19 @@ const has = require('lodash/has');
const get = require('lodash/get');
const yaml = require('yamljs');
const es = yaml.load('./locales/es.yml');
const en = yaml.load('./locales/en.yml');
const fr = yaml.load('./locales/fr.yml');
const accepts = require('accepts');
// default language
let defaultLanguage = 'en';
let language = defaultLanguage;
const languages = ['en', 'es'];
const languages = ['en', 'es', 'fr'];
const translations = Object.assign(en, es);
const translations = Object.assign(en, es, fr);
/**
* Exposes a service object to allow translations.
+2 -1
View File
@@ -112,7 +112,8 @@ const config = {
}
}),
new webpack.EnvironmentPlugin({
'TALK_PLUGINS_JSON': '{}'
'TALK_PLUGINS_JSON': '{}',
'TALK_THREADING_LEVEL': '3'
})
],
resolveLoader: {