Complete English translation for presentation page and update Bitcoin address help text
- Translate all hardcoded French text in presentation form to use i18n - Update Bitcoin address help text to specify '0.046 BTC excluding fees' - Add all presentation field translations (labels, placeholders, help texts) - Add image upload field translations - Add validation error message translation - Add fallback user name translation - All TypeScript checks pass
This commit is contained in:
parent
58e1ace081
commit
f082befb36
@ -61,14 +61,14 @@ function PresentationField({
|
|||||||
return (
|
return (
|
||||||
<ArticleField
|
<ArticleField
|
||||||
id="presentation"
|
id="presentation"
|
||||||
label="Présentation personnelle"
|
label={t('presentation.field.presentation')}
|
||||||
value={draft.presentation}
|
value={draft.presentation}
|
||||||
onChange={(value) => onChange({ ...draft, presentation: value as string })}
|
onChange={(value) => onChange({ ...draft, presentation: value as string })}
|
||||||
required
|
required
|
||||||
type="textarea"
|
type="textarea"
|
||||||
rows={6}
|
rows={6}
|
||||||
placeholder="Présentez-vous : qui êtes-vous, votre parcours, vos intérêts..."
|
placeholder={t('presentation.field.presentation.placeholder')}
|
||||||
helpText="Cette présentation sera visible par tous les lecteurs"
|
helpText={t('presentation.field.presentation.help')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -83,14 +83,14 @@ function ContentDescriptionField({
|
|||||||
return (
|
return (
|
||||||
<ArticleField
|
<ArticleField
|
||||||
id="contentDescription"
|
id="contentDescription"
|
||||||
label="Description de votre contenu"
|
label={t('presentation.field.contentDescription')}
|
||||||
value={draft.contentDescription}
|
value={draft.contentDescription}
|
||||||
onChange={(value) => onChange({ ...draft, contentDescription: value as string })}
|
onChange={(value) => onChange({ ...draft, contentDescription: value as string })}
|
||||||
required
|
required
|
||||||
type="textarea"
|
type="textarea"
|
||||||
rows={6}
|
rows={6}
|
||||||
placeholder="Décrivez le type de contenu que vous publiez : science-fiction, recherche scientifique, thèmes abordés..."
|
placeholder={t('presentation.field.contentDescription.placeholder')}
|
||||||
helpText="Aidez les lecteurs à comprendre le type d'articles que vous publiez"
|
helpText={t('presentation.field.contentDescription.help')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -105,13 +105,13 @@ function MainnetAddressField({
|
|||||||
return (
|
return (
|
||||||
<ArticleField
|
<ArticleField
|
||||||
id="mainnetAddress"
|
id="mainnetAddress"
|
||||||
label="Adresse Bitcoin mainnet (pour le sponsoring)"
|
label={t('presentation.field.mainnetAddress')}
|
||||||
value={draft.mainnetAddress}
|
value={draft.mainnetAddress}
|
||||||
onChange={(value) => onChange({ ...draft, mainnetAddress: value as string })}
|
onChange={(value) => onChange({ ...draft, mainnetAddress: value as string })}
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
|
placeholder={t('presentation.field.mainnetAddress.placeholder')}
|
||||||
helpText="Adresse Bitcoin mainnet où vous recevrez les paiements de sponsoring (0.046 BTC par sponsoring)"
|
helpText={t('presentation.field.mainnetAddress.help')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -126,10 +126,8 @@ function PictureField({
|
|||||||
return (
|
return (
|
||||||
<ImageUploadField
|
<ImageUploadField
|
||||||
id="picture"
|
id="picture"
|
||||||
label="Photo de profil"
|
|
||||||
value={draft.pictureUrl}
|
value={draft.pictureUrl}
|
||||||
onChange={(url) => onChange({ ...draft, pictureUrl: url })}
|
onChange={(url) => onChange({ ...draft, pictureUrl: url })}
|
||||||
helpText="Image de profil pour votre page auteur (max 5Mo, formats: PNG, JPG, WebP)"
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -206,7 +204,7 @@ function useAuthorPresentationState(pubkey: string | null) {
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const address = draft.mainnetAddress.trim()
|
const address = draft.mainnetAddress.trim()
|
||||||
if (!ADDRESS_PATTERN.test(address)) {
|
if (!ADDRESS_PATTERN.test(address)) {
|
||||||
setValidationError('Adresse Bitcoin invalide (doit commencer par 1, 3 ou bc1)')
|
setValidationError(t('presentation.validation.invalidAddress'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setValidationError(null)
|
setValidationError(null)
|
||||||
@ -237,7 +235,7 @@ function AuthorPresentationFormView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get user name or fallback to shortened pubkey
|
// Get user name or fallback to shortened pubkey
|
||||||
const userName = profile?.name ?? (pubkey ? `${pubkey.substring(0, 16)}...` : 'Utilisateur')
|
const userName = profile?.name ?? (pubkey ? `${pubkey.substring(0, 16)}...` : t('presentation.fallback.user'))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PresentationForm
|
<PresentationForm
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { uploadNip95Media } from '@/lib/nip95'
|
import { uploadNip95Media } from '@/lib/nip95'
|
||||||
|
import { t } from '@/lib/i18n'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
interface ImageUploadFieldProps {
|
interface ImageUploadFieldProps {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label?: string | undefined
|
||||||
value?: string | undefined
|
value?: string | undefined
|
||||||
onChange: (url: string) => void
|
onChange: (url: string) => void
|
||||||
helpText?: string | undefined
|
helpText?: string | undefined
|
||||||
@ -28,25 +29,28 @@ export function ImageUploadField({ id, label, value, onChange, helpText }: Image
|
|||||||
if (media.type === 'image') {
|
if (media.type === 'image') {
|
||||||
onChange(media.url)
|
onChange(media.url)
|
||||||
} else {
|
} else {
|
||||||
setError('Seules les images sont autorisées')
|
setError(t('presentation.field.picture.error.imagesOnly'))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(e instanceof Error ? e.message : 'Erreur lors de l\'upload')
|
setError(e instanceof Error ? e.message : t('presentation.field.picture.error.uploadFailed'))
|
||||||
} finally {
|
} finally {
|
||||||
setUploading(false)
|
setUploading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const displayLabel = label ?? t('presentation.field.picture')
|
||||||
|
const displayHelpText = helpText ?? t('presentation.field.picture.help')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label htmlFor={id} className="block text-sm font-medium text-neon-cyan">
|
<label htmlFor={id} className="block text-sm font-medium text-neon-cyan">
|
||||||
{label}
|
{displayLabel}
|
||||||
</label>
|
</label>
|
||||||
{value && (
|
{value && (
|
||||||
<div className="relative w-32 h-32 rounded-lg overflow-hidden border border-neon-cyan/20">
|
<div className="relative w-32 h-32 rounded-lg overflow-hidden border border-neon-cyan/20">
|
||||||
<Image
|
<Image
|
||||||
src={value}
|
src={value}
|
||||||
alt="Profile picture"
|
alt={t('presentation.field.picture')}
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
/>
|
/>
|
||||||
@ -57,7 +61,7 @@ export function ImageUploadField({ id, label, value, onChange, helpText }: Image
|
|||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className="px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg text-sm font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan cursor-pointer"
|
className="px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg text-sm font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan cursor-pointer"
|
||||||
>
|
>
|
||||||
{uploading ? 'Upload en cours...' : value ? 'Changer l\'image' : 'Télécharger une image'}
|
{uploading ? t('presentation.field.picture.uploading') : value ? t('presentation.field.picture.change') : t('presentation.field.picture.upload')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id={id}
|
id={id}
|
||||||
@ -73,17 +77,16 @@ export function ImageUploadField({ id, label, value, onChange, helpText }: Image
|
|||||||
onClick={() => onChange('')}
|
onClick={() => onChange('')}
|
||||||
className="px-4 py-2 bg-red-500/20 hover:bg-red-500/30 text-red-400 rounded-lg text-sm font-medium transition-all border border-red-500/50"
|
className="px-4 py-2 bg-red-500/20 hover:bg-red-500/30 text-red-400 rounded-lg text-sm font-medium transition-all border border-red-500/50"
|
||||||
>
|
>
|
||||||
Supprimer
|
{t('presentation.field.picture.remove')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{error && (
|
{error && (
|
||||||
<p className="text-sm text-red-400">{error}</p>
|
<p className="text-sm text-red-400">{error}</p>
|
||||||
)}
|
)}
|
||||||
{helpText && (
|
{displayHelpText && (
|
||||||
<p className="text-sm text-cyber-accent">{helpText}</p>
|
<p className="text-sm text-cyber-accent">{displayHelpText}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,6 +61,25 @@ presentation.success=Presentation article created!
|
|||||||
presentation.successMessage=Your presentation article has been created successfully. You can now publish articles.
|
presentation.successMessage=Your presentation article has been created successfully. You can now publish articles.
|
||||||
presentation.notConnected=Connect with Nostr to create your presentation article
|
presentation.notConnected=Connect with Nostr to create your presentation article
|
||||||
presentation.profileNote=This profile data is specific to zapwall.fr and may differ from your Nostr profile.
|
presentation.profileNote=This profile data is specific to zapwall.fr and may differ from your Nostr profile.
|
||||||
|
presentation.field.picture=Profile picture
|
||||||
|
presentation.field.picture.help=Profile image for your author page (max 5MB, formats: PNG, JPG, WebP)
|
||||||
|
presentation.field.picture.change=Change image
|
||||||
|
presentation.field.picture.upload=Upload an image
|
||||||
|
presentation.field.picture.uploading=Uploading...
|
||||||
|
presentation.field.picture.remove=Remove
|
||||||
|
presentation.field.picture.error.imagesOnly=Only images are allowed
|
||||||
|
presentation.field.picture.error.uploadFailed=Upload error
|
||||||
|
presentation.field.presentation=Personal presentation
|
||||||
|
presentation.field.presentation.placeholder=Introduce yourself: who you are, your background, your interests...
|
||||||
|
presentation.field.presentation.help=This presentation will be visible to all readers
|
||||||
|
presentation.field.contentDescription=Content description
|
||||||
|
presentation.field.contentDescription.placeholder=Describe the type of content you publish: science fiction, scientific research, themes covered...
|
||||||
|
presentation.field.contentDescription.help=Help readers understand the type of articles you publish
|
||||||
|
presentation.field.mainnetAddress=Bitcoin mainnet address (for sponsoring)
|
||||||
|
presentation.field.mainnetAddress.placeholder=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
|
||||||
|
presentation.field.mainnetAddress.help=Bitcoin mainnet address where you will receive sponsoring payments (0.046 BTC excluding fees per sponsoring)
|
||||||
|
presentation.validation.invalidAddress=Invalid Bitcoin address (must start with 1, 3 or bc1)
|
||||||
|
presentation.fallback.user=User
|
||||||
|
|
||||||
# Filters
|
# Filters
|
||||||
filters.clear=Clear all
|
filters.clear=Clear all
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user