Translate some profile blocks

This commit is contained in:
MartinBraquet
2026-01-02 15:13:51 +02:00
parent dc7be2d334
commit bc672db79a
7 changed files with 144 additions and 49 deletions

View File

@@ -5,6 +5,7 @@ import { withTracking } from 'web/lib/service/analytics'
import { toast } from 'react-hot-toast'
import { PrivateUser, User } from 'common/user'
import { api } from 'web/lib/api'
import { useT } from 'web/lib/locale'
export const BlockUser = (props: {
user: User
@@ -13,6 +14,7 @@ export const BlockUser = (props: {
}) => {
const { user, currentUser, closeModal } = props
const { id: userId } = user
const t = useT()
const isBlocked = currentUser.blockedUserIds?.includes(userId)
@@ -20,16 +22,19 @@ export const BlockUser = (props: {
const onBlock = async () => {
await toast.promise(api('user/by-id/:id/block', { id: user.id }), {
loading: 'Blocking...',
success: `You'll no longer see content from this user`,
error: 'Error blocking user',
loading: t('block_user.toast.loading', 'Blocking...'),
success: t(
'block_user.toast.success',
"You'll no longer see content from this user"
),
error: t('block_user.toast.error', 'Error blocking user'),
})
}
return (
<Col>
<Row className={'justify-between'}>
<Button onClick={closeModal} color={'gray-white'}>
Cancel
{t('settings.action.cancel', 'Cancel')}
</Button>
<Row className={'gap-4'}>
{isBlocked ? (
@@ -39,7 +44,7 @@ export const BlockUser = (props: {
className="my-auto"
onClick={withTracking(onUnblock, 'unblock')}
>
Unblock {user.name}
{t('block_user.unblock', 'Unblock')} {user.name}
</Button>
) : (
<Button
@@ -48,7 +53,7 @@ export const BlockUser = (props: {
className="my-auto"
onClick={withTracking(onBlock, 'block')}
>
Block {user.name}
{t('block_user.block', 'Block')} {user.name}
</Button>
)}
</Row>

View File

@@ -7,34 +7,37 @@ import {Col} from '../layout/col'
import {Input} from '../widgets/input'
import {Title} from '../widgets/title'
import {deleteAccount} from "web/lib/util/delete";
import {useT} from 'web/lib/locale'
export function DeleteYourselfButton() {
const [deleteAccountConfirmation, setDeleteAccountConfirmation] = useState('')
const t = useT()
const confirmPhrase = t('deleteyourself.confirm_phrase', 'delete my account')
return (
<ConfirmationButton
openModalBtn={{
className: 'p-2',
label: 'Permanently delete this account',
label: t('deleteyourself.open_label', 'Permanently delete this account'),
icon: <TrashIcon className="mr-1 h-5 w-5"/>,
color: 'red',
}}
submitBtn={{
label: 'Delete account',
label: t('deleteyourself.submit', 'Delete account'),
color:
deleteAccountConfirmation == 'delete my account' ? 'red' : 'gray',
deleteAccountConfirmation == confirmPhrase ? 'red' : 'gray',
}}
onSubmitWithSuccess={async () => {
if (deleteAccountConfirmation == 'delete my account') {
if (deleteAccountConfirmation == confirmPhrase) {
toast
.promise(deleteAccount(), {
loading: 'Deleting account...',
loading: t('deleteyourself.toast.loading', 'Deleting account...'),
success: () => {
router.push('/')
return 'Your account has been deleted.'
return t('deleteyourself.toast.success', 'Your account has been deleted.')
},
error: () => {
return 'Failed to delete account.'
return t('deleteyourself.toast.error', 'Failed to delete account.')
},
})
.then(() => {
@@ -48,14 +51,16 @@ export function DeleteYourselfButton() {
}}
>
<Col>
<Title>Are you sure?</Title>
<Title>{t('deleteyourself.title', 'Are you sure?')}</Title>
<div>
Deleting your account means you will no longer be able to use your
account. You will lose access to all of your data.
{t(
'deleteyourself.description',
'Deleting your account means you will no longer be able to use your account. You will lose access to all of your data.'
)}
</div>
<Input
type="text"
placeholder="Type 'delete my account' to confirm"
placeholder={t('deleteyourself.input_placeholder', "Type 'delete my account' to confirm")}
className="w-full"
value={deleteAccountConfirmation}
onChange={(e) => setDeleteAccountConfirmation(e.target.value)}

View File

@@ -21,6 +21,7 @@ import {VisibilityConfirmationModal} from './visibility-confirmation-modal'
import toast from "react-hot-toast";
import {StarButton} from "web/components/widgets/star-button";
import {disableProfile} from "web/lib/util/disable";
import {useT} from 'web/lib/locale'
export default function ProfileHeader(props: {
user: User
@@ -46,6 +47,7 @@ export default function ProfileHeader(props: {
const isCurrentUser = currentUser?.id === user.id
const [showVisibilityModal, setShowVisibilityModal] = useState(false)
const disabled = profile.disabled
const t = useT()
console.debug('ProfileProfileHeader', {user, profile, userActivity, currentUser})
@@ -55,7 +57,7 @@ export default function ProfileHeader(props: {
<Row className="items-center gap-1">
<Col className="gap-1">
{currentUser && isCurrentUser && disabled &&
<div className="text-red-500">You disabled your profile, so no one else can access it.</div>}
<div className="text-red-500">{t('profile.header.disabled_notice', 'You disabled your profile, so no one else can access it.')}</div>}
<Row className="items-center gap-1 text-xl">
{/*{!isCurrentUser && <OnlineIcon last_online_time={userActivity?.last_online_time}/>}*/}
<span>
@@ -98,8 +100,8 @@ export default function ProfileHeader(props: {
{
name:
profile.visibility === 'member'
? 'List Profile Publicly'
: 'Limit to Members Only',
? t('profile.header.menu.list_public', 'List Profile Publicly')
: t('profile.header.menu.limit_members', 'Limit to Members Only'),
icon:
profile.visibility === 'member' ? (
<EyeIcon className="h-4 w-4"/>
@@ -109,7 +111,7 @@ export default function ProfileHeader(props: {
onClick: () => setShowVisibilityModal(true),
},
{
name: disabled ? 'Enable profile' : 'Disable profile',
name: disabled ? t('profile.header.menu.enable_profile', 'Enable profile') : t('profile.header.menu.disable_profile', 'Disable profile'),
icon: null,
onClick: async () => {
const confirmed = true // confirm(
@@ -118,12 +120,18 @@ export default function ProfileHeader(props: {
if (confirmed) {
toast
.promise(disableProfile(!disabled), {
loading: disabled ? 'Enabling profile...' : 'Disabling profile...',
loading: disabled
? t('profile.header.toast.enabling', 'Enabling profile...')
: t('profile.header.toast.disabling', 'Disabling profile...'),
success: () => {
return `Profile ${disabled ? 'enabled' : 'disabled'}`
return disabled
? t('profile.header.toast.enabled', 'Profile enabled')
: t('profile.header.toast.disabled', 'Profile disabled')
},
error: () => {
return `Failed to ${disabled ? 'enable' : 'disable'} profile`
return disabled
? t('profile.header.toast.failed_enable', 'Failed to enable profile')
: t('profile.header.toast.failed_disable', 'Failed to disable profile')
},
})
.then(() => {

View File

@@ -16,6 +16,7 @@ import {Content} from "web/components/widgets/editor";
import {JSONContent} from "@tiptap/core";
import {useUserActivity} from 'web/hooks/use-user-activity'
import {UserActivity} from "common/user";
import {useT} from 'web/lib/locale'
export function ProfileInfo(props: {
profile: Profile
@@ -28,6 +29,7 @@ export function ProfileInfo(props: {
console.debug('Rendering Profile for', user.username, user.name, props)
const currentUser = useUser()
const t = useT()
// const currentProfile = useProfile()
// const isCurrentUser = currentUser?.id === user.id
@@ -103,7 +105,7 @@ export function ProfileInfo(props: {
<div className="from-canvas-0 absolute bottom-0 h-12 w-full bg-gradient-to-t to-transparent"/>
</Col>
<Row className="gap-2">
<SignUpButton text="Sign up to see profile"/>
<SignUpButton text={t('profile.info.signup_to_see', 'Sign up to see profile')}/>
</Row>
</Col>
)}

View File

@@ -8,15 +8,20 @@ import Textarea from 'react-expanding-textarea'
import { toast } from 'react-hot-toast'
import { api } from 'web/lib/api'
import {randomString} from "common/util/random";
import { useT } from 'web/lib/locale'
export const ReportUser = (props: { user: User; closeModal: () => void }) => {
const { user, closeModal } = props
const t = useT()
const reportTypes = [
'Spam',
'Inappropriate or objectionable content',
'Violence or threats',
'Fraudulent activity',
'Other',
t('report.user.type.spam', 'Spam'),
t(
'report.user.type.inappropriate',
'Inappropriate or objectionable content'
),
t('report.user.type.violence', 'Violence or threats'),
t('report.user.type.fraud', 'Fraudulent activity'),
t('report.user.type.other', 'Other'),
]
const [selectedReportTypes, setSelectedReportTypes] = useState<string[]>([])
const [otherReportType, setOtherReportType] = useState('')
@@ -38,9 +43,9 @@ export const ReportUser = (props: { user: User; closeModal: () => void }) => {
'Reasons: ' + [...selectedReportTypes, otherReportType].join(', '),
}),
{
loading: 'Reporting...',
success: 'Reported',
error: 'Error reporting user',
loading: t('report.user.toast.loading', 'Reporting...'),
success: t('report.user.toast.success', 'Reported'),
error: t('report.user.toast.error', 'Error reporting user'),
}
)
.then(() => {
@@ -53,18 +58,25 @@ export const ReportUser = (props: { user: User; closeModal: () => void }) => {
<Col>
{hasSubmitted ? (
<Col className={'gap-2'}>
<span>Thank you for your report.</span>
<span>We'll review the user and take action if necessary.</span>
<span>{t('report.user.thanks', 'Thank you for your report.')}</span>
<span>
{t(
'report.user.review_message',
"We'll review the user and take action if necessary."
)}
</span>
<Row className={'mt-2 justify-end'}>
<Button onClick={closeModal}>Close</Button>
<Button onClick={closeModal}>{t('common.close', 'Close')}</Button>
</Row>
</Col>
) : (
<>
<Row className={'mb-4'}>
<span>
Please select the reason(s) for reporting this user and a link to
the content.
{t(
'report.user.instructions',
'Please select the reason(s) for reporting this user and a link to the content.'
)}
</span>
</Row>
<Col className={'mb-4 ml-4 gap-3'}>
@@ -86,9 +98,10 @@ export const ReportUser = (props: { user: User; closeModal: () => void }) => {
))}
<Textarea
placeholder={
placeholder={t(
'report.user.placeholder',
'Add more context and/or provide a link to the content'
}
)}
rows={2}
className={
'border-ink-300 bg-canvas-0 -ml-2 rounded-md border p-2'
@@ -99,10 +112,10 @@ export const ReportUser = (props: { user: User; closeModal: () => void }) => {
</Col>
<Row className={'justify-between'}>
<Button color={'gray-white'} onClick={closeModal}>
Cancel
{t('settings.action.cancel', 'Cancel')}
</Button>
<Button disabled={!canSubmit} color={'red'} onClick={reportUser}>
Report User
{t('report.user.submit', 'Report User')}
</Button>
</Row>
</>

View File

@@ -3,6 +3,7 @@ import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row'
import { Button } from 'web/components/buttons/button'
import { EyeIcon, LockClosedIcon } from '@heroicons/react/outline'
import { useT } from 'web/lib/locale'
export function VisibilityConfirmationModal(props: {
open: boolean
@@ -12,6 +13,7 @@ export function VisibilityConfirmationModal(props: {
}) {
const { open, setOpen, currentVisibility, onConfirm } = props
const isMakingPublic = currentVisibility === 'member'
const t = useT()
return (
<Modal open={open} setOpen={setOpen}>
@@ -24,20 +26,32 @@ export function VisibilityConfirmationModal(props: {
)}
<span>
{isMakingPublic
? 'Make profile visible publicly?'
: 'Limit profile to members only?'}
? t(
'profile.visibility.question.public',
'Make profile visible publicly?'
)
: t(
'profile.visibility.question.member',
'Limit profile to members only?'
)}
</span>
</Row>
<div className="text-ink-600">
{isMakingPublic
? 'Your profile will be visible to any visitor without logging in.'
: 'Your profile will only be visible to members. Visitors will have to log in to view your profile.'}
? t(
'profile.visibility.desc.public',
'Your profile will be visible to any visitor without logging in.'
)
: t(
'profile.visibility.desc.member',
'Your profile will only be visible to members. Visitors will have to log in to view your profile.'
)}
</div>
<Row className="w-full justify-end gap-4">
<Button color="gray-white" onClick={() => setOpen(false)}>
Cancel
{t('settings.action.cancel', 'Cancel')}
</Button>
<Button
color={isMakingPublic ? 'blue' : 'gray'}
@@ -46,7 +60,9 @@ export function VisibilityConfirmationModal(props: {
setOpen(false)
}}
>
{isMakingPublic ? 'Make Public' : 'Limit to Members'}
{isMakingPublic
? t('profile.visibility.make_public', 'Make Public')
: t('profile.visibility.limit_to_members', 'Limit to Members')}
</Button>
</Row>
</Col>

View File

@@ -435,5 +435,51 @@
"messages.toast.send_failed": "Échec de l'envoi du message. Veuillez réessayer plus tard ou contacter le support si le problème persiste.",
"aboutsettings.copied": "Copié !",
"aboutsettings.copy_info": "Copier les infos",
"profiles.title": "Personnes"
"profiles.title": "Personnes",
"profile.visibility.question.public": "Rendre le profil visible publiquement ?",
"profile.visibility.question.member": "Limiter le profil aux membres uniquement ?",
"profile.visibility.desc.public": "Votre profil sera visible par tout visiteur sans connexion.",
"profile.visibility.desc.member": "Votre profil ne sera visible que par les membres. Les visiteurs devront se connecter pour voir votre profil.",
"profile.visibility.make_public": "Rendre public",
"profile.visibility.limit_to_members": "Limiter aux membres",
"profile.info.signup_to_see": "Inscrivez-vous pour voir le profil",
"profile.header.disabled_notice": "Vous avez désactivé votre profil, personne d'autre ne peut y accéder.",
"profile.header.menu.list_public": "Lister le profil publiquement",
"profile.header.menu.limit_members": "Limiter aux membres uniquement",
"profile.header.menu.enable_profile": "Activer le profil",
"profile.header.menu.disable_profile": "Désactiver le profil",
"profile.header.toast.enabling": "Activation du profil...",
"profile.header.toast.disabling": "Désactivation du profil...",
"profile.header.toast.enabled": "Profil activé",
"profile.header.toast.disabled": "Profil désactivé",
"profile.header.toast.failed_enable": "Échec de l'activation du profil",
"profile.header.toast.failed_disable": "Échec de la désactivation du profil",
"report.user.type.spam": "Spam",
"report.user.type.inappropriate": "Contenu inapproprié ou choquant",
"report.user.type.violence": "Violence ou menaces",
"report.user.type.fraud": "Activité frauduleuse",
"report.user.type.other": "Autre",
"report.user.toast.loading": "Signalement en cours...",
"report.user.toast.success": "Signalé",
"report.user.toast.error": "Erreur lors du signalement de l'utilisateur",
"report.user.thanks": "Merci pour votre signalement.",
"report.user.review_message": "Nous examinerons cet utilisateur et prendrons les mesures nécessaires si besoin.",
"report.user.instructions": "Veuillez sélectionner la ou les raisons de signaler cet utilisateur et ajouter un lien vers le contenu.",
"report.user.placeholder": "Ajoutez plus de contexte et/ou fournissez un lien vers le contenu",
"report.user.submit": "Signaler l'utilisateur",
"common.close": "Fermer",
"deleteyourself.confirm_phrase": "delete my account",
"deleteyourself.open_label": "Supprimer définitivement ce compte",
"deleteyourself.submit": "Supprimer le compte",
"deleteyourself.toast.loading": "Suppression du compte...",
"deleteyourself.toast.success": "Votre compte a été supprimé.",
"deleteyourself.toast.error": "Échec de la suppression du compte.",
"deleteyourself.title": "Êtes-vous sûr ?",
"deleteyourself.description": "Supprimer votre compte signifie que vous ne pourrez plus l'utiliser. Vous perdrez l'accès à toutes vos données.",
"deleteyourself.input_placeholder": "Tapez 'delete my account' pour confirmer",
"block_user.toast.loading": "Blocage en cours...",
"block_user.toast.success": "Vous ne verrez plus le contenu de cet utilisateur",
"block_user.toast.error": "Erreur lors du blocage de l'utilisateur",
"block_user.unblock": "Débloquer",
"block_user.block": "Bloquer"
}