Add translations for settings page (fr/en)
**Motivations:** - Translate settings page and all its components to French and English - Provide consistent multilingual experience **Root causes:** - Settings page and components were hardcoded in English - No translation support for key management and NIP-95 configuration **Correctifs:** - None (new feature) **Evolutions:** - Added translations for settings page title - Added translations for KeyManagementManager component: - Public keys display (npub and hex) - Import form and validation messages - Recovery phrase display - All buttons and warnings - Added translations for Nip95ConfigManager component: - Endpoint list and management - Add/edit/remove actions - Error messages - Updated both fr.txt and en.txt translation files - All text now uses t() function for i18n support **Pages affectées:** - pages/settings.tsx - components/KeyManagementManager.tsx - components/Nip95ConfigManager.tsx - public/locales/fr.txt - public/locales/en.txt - locales/fr.txt - locales/en.txt
This commit is contained in:
parent
d7a04dd8f8
commit
32b33d56a1
@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
||||
import { nostrAuthService } from '@/lib/nostrAuth'
|
||||
import { keyManagementService } from '@/lib/keyManagement'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { t } from '@/lib/i18n'
|
||||
|
||||
interface PublicKeys {
|
||||
publicKey: string
|
||||
@ -41,7 +42,7 @@ export function KeyManagementManager() {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to load keys'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.keyManagement.loading')
|
||||
setError(errorMessage)
|
||||
console.error('Error loading keys:', e)
|
||||
} finally {
|
||||
@ -79,14 +80,14 @@ export function KeyManagementManager() {
|
||||
|
||||
async function handleImport() {
|
||||
if (!importKey.trim()) {
|
||||
setError('Please enter a private key')
|
||||
setError(t('settings.keyManagement.import.error.required'))
|
||||
return
|
||||
}
|
||||
|
||||
// Extract key from URL or text
|
||||
const extractedKey = extractKeyFromUrl(importKey.trim())
|
||||
if (!extractedKey) {
|
||||
setError('Invalid key format. Please provide a nsec (nsec1...) or hex private key.')
|
||||
setError(t('settings.keyManagement.import.error.invalid'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -104,8 +105,7 @@ export function KeyManagementManager() {
|
||||
} catch (e) {
|
||||
// If decoding failed, assume it's hex, validate length (64 hex chars = 32 bytes)
|
||||
if (!/^[0-9a-f]{64}$/i.test(extractedKey)) {
|
||||
const errorMsg = e instanceof Error ? e.message : 'Invalid format'
|
||||
setError(`Invalid key format: ${errorMsg}. Please provide a nsec (nsec1...) or hex (64 characters) private key.`)
|
||||
setError(t('settings.keyManagement.import.error.invalid'))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -138,7 +138,7 @@ export function KeyManagementManager() {
|
||||
setShowImportForm(false)
|
||||
await loadKeys()
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to import key'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.keyManagement.import.error.failed')
|
||||
setError(errorMessage)
|
||||
console.error('Error importing key:', e)
|
||||
} finally {
|
||||
@ -194,7 +194,7 @@ export function KeyManagementManager() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-cyber-darker border border-neon-cyan/30 rounded-lg p-6">
|
||||
<p className="text-cyber-accent">Loading...</p>
|
||||
<p className="text-cyber-accent">{t('settings.keyManagement.loading')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -202,7 +202,7 @@ export function KeyManagementManager() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-cyber-darker border border-neon-cyan/30 rounded-lg p-6">
|
||||
<h2 className="text-2xl font-bold text-neon-cyan mb-4">Key Management</h2>
|
||||
<h2 className="text-2xl font-bold text-neon-cyan mb-4">{t('settings.keyManagement.title')}</h2>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-900/20 border border-red-400/50 rounded-lg p-4 mb-4">
|
||||
@ -215,14 +215,14 @@ export function KeyManagementManager() {
|
||||
<div className="space-y-4 mb-6">
|
||||
<div className="bg-neon-blue/10 border border-neon-blue/30 rounded-lg p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<p className="text-neon-blue font-semibold">Public Key (npub)</p>
|
||||
<p className="text-neon-blue font-semibold">{t('settings.keyManagement.publicKey.npub')}</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
void handleCopyNpub()
|
||||
}}
|
||||
className="px-3 py-1 text-xs bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded transition-colors"
|
||||
>
|
||||
{copiedNpub ? '✓ Copied' : 'Copy'}
|
||||
{copiedNpub ? t('settings.keyManagement.copied') : t('settings.keyManagement.copy')}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-neon-cyan text-sm font-mono break-all">{publicKeys.npub}</p>
|
||||
@ -230,14 +230,14 @@ export function KeyManagementManager() {
|
||||
|
||||
<div className="bg-neon-blue/10 border border-neon-blue/30 rounded-lg p-4">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<p className="text-neon-blue font-semibold">Public Key (hex)</p>
|
||||
<p className="text-neon-blue font-semibold">{t('settings.keyManagement.publicKey.hex')}</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
void handleCopyPublicKey()
|
||||
}}
|
||||
className="px-3 py-1 text-xs bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded transition-colors"
|
||||
>
|
||||
{copiedPublicKey ? '✓ Copied' : 'Copy'}
|
||||
{copiedPublicKey ? t('settings.keyManagement.copied') : t('settings.keyManagement.copy')}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-neon-cyan text-sm font-mono break-all">{publicKeys.publicKey}</p>
|
||||
@ -247,9 +247,9 @@ export function KeyManagementManager() {
|
||||
|
||||
{!publicKeys && !accountExists && (
|
||||
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
|
||||
<p className="text-yellow-400 font-semibold mb-2">No account found</p>
|
||||
<p className="text-yellow-400 font-semibold mb-2">{t('settings.keyManagement.noAccount.title')}</p>
|
||||
<p className="text-yellow-300/90 text-sm">
|
||||
Create a new account by importing a private key. The key will be encrypted using a two-level encryption system.
|
||||
{t('settings.keyManagement.noAccount.description')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@ -263,28 +263,23 @@ export function KeyManagementManager() {
|
||||
}}
|
||||
className="w-full py-3 px-6 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan"
|
||||
>
|
||||
{accountExists ? 'Replace Account (Import New Key)' : 'Import Private Key'}
|
||||
{accountExists ? t('settings.keyManagement.import.button.replace') : t('settings.keyManagement.import.button.new')}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{showImportForm && (
|
||||
<div className="space-y-4">
|
||||
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4">
|
||||
<p className="text-yellow-400 font-semibold mb-2">⚠️ Important</p>
|
||||
<p className="text-yellow-300/90 text-sm">
|
||||
After importing, you will receive <strong className="font-bold">4 recovery words</strong> (BIP39 dictionary) to secure your account.
|
||||
These words encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API, which then encrypts your private key stored in IndexedDB (two-level encryption system).
|
||||
</p>
|
||||
<p className="text-yellow-400 font-semibold mb-2">{t('settings.keyManagement.import.warning.title')}</p>
|
||||
<p className="text-yellow-300/90 text-sm" dangerouslySetInnerHTML={{ __html: t('settings.keyManagement.import.warning.description') }} />
|
||||
{accountExists && (
|
||||
<p className="text-yellow-300/90 text-sm mt-2">
|
||||
<strong className="font-bold">Warning:</strong> Importing a new key will replace your existing account. Make sure you have your recovery phrase saved before proceeding.
|
||||
</p>
|
||||
<p className="text-yellow-300/90 text-sm mt-2" dangerouslySetInnerHTML={{ __html: t('settings.keyManagement.import.warning.replace') }} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="importKey" className="block text-sm font-medium text-cyber-accent mb-2">
|
||||
Private Key (nsec URL, nsec1..., or hex)
|
||||
{t('settings.keyManagement.import.label')}
|
||||
</label>
|
||||
<textarea
|
||||
id="importKey"
|
||||
@ -293,20 +288,20 @@ export function KeyManagementManager() {
|
||||
setImportKey(e.target.value)
|
||||
setError(null)
|
||||
}}
|
||||
placeholder="nsec1... or nostr://nsec1... or hex key"
|
||||
placeholder={t('settings.keyManagement.import.placeholder')}
|
||||
className="w-full px-3 py-2 bg-cyber-dark border border-neon-cyan/30 rounded-lg font-mono text-sm text-neon-cyan focus:border-neon-cyan focus:outline-none"
|
||||
rows={4}
|
||||
/>
|
||||
<p className="text-sm text-cyber-accent/70 mt-2">
|
||||
You can paste a nsec key, a nostr:// URL containing a nsec, or a hex private key (64 characters).
|
||||
{t('settings.keyManagement.import.help')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{showReplaceWarning && (
|
||||
<div className="bg-red-900/20 border border-red-400/50 rounded-lg p-4">
|
||||
<p className="text-red-400 font-semibold mb-2">⚠️ Replace Existing Account?</p>
|
||||
<p className="text-red-400 font-semibold mb-2">{t('settings.keyManagement.replace.warning.title')}</p>
|
||||
<p className="text-red-300/90 text-sm mb-4">
|
||||
This will delete your current account and create a new one with the imported key. Make sure you have saved your recovery phrase for the current account.
|
||||
{t('settings.keyManagement.replace.warning.description')}
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
@ -315,7 +310,7 @@ export function KeyManagementManager() {
|
||||
}}
|
||||
className="flex-1 py-2 px-4 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg font-medium transition-colors"
|
||||
>
|
||||
Cancel
|
||||
{t('settings.keyManagement.replace.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@ -324,7 +319,7 @@ export function KeyManagementManager() {
|
||||
disabled={importing}
|
||||
className="flex-1 py-2 px-4 bg-red-600/20 hover:bg-red-600/30 text-red-400 rounded-lg font-medium transition-all border border-red-400/50 hover:shadow-glow-red disabled:opacity-50"
|
||||
>
|
||||
{importing ? 'Replacing...' : 'Replace Account'}
|
||||
{importing ? t('settings.keyManagement.replace.replacing') : t('settings.keyManagement.replace.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -340,7 +335,7 @@ export function KeyManagementManager() {
|
||||
}}
|
||||
className="flex-1 py-2 px-4 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg font-medium transition-colors"
|
||||
>
|
||||
Cancel
|
||||
{t('settings.keyManagement.import.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@ -349,7 +344,7 @@ export function KeyManagementManager() {
|
||||
disabled={importing}
|
||||
className="flex-1 py-2 px-4 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50"
|
||||
>
|
||||
{importing ? 'Importing...' : 'Import'}
|
||||
{importing ? t('settings.keyManagement.import.importing') : t('settings.keyManagement.import.import')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@ -360,16 +355,11 @@ export function KeyManagementManager() {
|
||||
{recoveryPhrase && newNpub && (
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4">
|
||||
<p className="text-yellow-400 font-semibold mb-2">⚠️ Important</p>
|
||||
<p className="text-yellow-300/90 text-sm">
|
||||
These <strong className="font-bold">4 recovery words</strong> are your only way to recover your account.
|
||||
<strong className="font-bold"> They will never be displayed again.</strong>
|
||||
</p>
|
||||
<p className="text-yellow-400 font-semibold mb-2">{t('settings.keyManagement.recovery.warning.title')}</p>
|
||||
<p className="text-yellow-300/90 text-sm" dangerouslySetInnerHTML={{ __html: t('settings.keyManagement.recovery.warning.part1') }} />
|
||||
<p className="text-yellow-300/90 text-sm mt-2" dangerouslySetInnerHTML={{ __html: t('settings.keyManagement.recovery.warning.part2') }} />
|
||||
<p className="text-yellow-300/90 text-sm mt-2">
|
||||
These words (BIP39 dictionary) are used with <strong>PBKDF2</strong> to encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API. This KEK then encrypts your private key stored in IndexedDB (two-level system).
|
||||
</p>
|
||||
<p className="text-yellow-300/90 text-sm mt-2">
|
||||
Save them in a safe place. Without these words, you will permanently lose access to your account.
|
||||
{t('settings.keyManagement.recovery.warning.part3')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -391,12 +381,12 @@ export function KeyManagementManager() {
|
||||
}}
|
||||
className="w-full py-2 px-4 bg-cyber-light border border-neon-cyan/30 hover:border-neon-cyan/50 hover:bg-cyber-dark text-cyber-accent hover:text-neon-cyan rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
{copiedRecoveryPhrase ? '✓ Copied!' : 'Copy Recovery Words'}
|
||||
{copiedRecoveryPhrase ? t('settings.keyManagement.recovery.copied') : t('settings.keyManagement.recovery.copy')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-neon-blue/10 border border-neon-blue/30 rounded-lg p-4">
|
||||
<p className="text-neon-blue font-semibold mb-2">Your new public key (npub)</p>
|
||||
<p className="text-neon-blue font-semibold mb-2">{t('settings.keyManagement.recovery.newNpub')}</p>
|
||||
<p className="text-neon-cyan text-sm font-mono break-all">{newNpub}</p>
|
||||
</div>
|
||||
|
||||
@ -408,7 +398,7 @@ export function KeyManagementManager() {
|
||||
}}
|
||||
className="w-full py-2 px-4 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan"
|
||||
>
|
||||
Done
|
||||
{t('settings.keyManagement.recovery.done')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { configStorage } from '@/lib/configStorage'
|
||||
import type { Nip95Config } from '@/lib/configStorageTypes'
|
||||
import { t } from '@/lib/i18n'
|
||||
|
||||
interface Nip95ConfigManagerProps {
|
||||
onConfigChange?: () => void
|
||||
@ -25,7 +26,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
const config = await configStorage.getConfig()
|
||||
setApis(config.nip95Apis.sort((a, b) => a.priority - b.priority))
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to load NIP-95 APIs'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.loadFailed')
|
||||
setError(errorMessage)
|
||||
console.error('Error loading NIP-95 APIs:', e)
|
||||
} finally {
|
||||
@ -39,7 +40,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
await loadApis()
|
||||
onConfigChange?.()
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to update API'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.updateFailed')
|
||||
setError(errorMessage)
|
||||
console.error('Error updating NIP-95 API:', e)
|
||||
}
|
||||
@ -51,7 +52,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
await loadApis()
|
||||
onConfigChange?.()
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to update priority'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.priorityFailed')
|
||||
setError(errorMessage)
|
||||
console.error('Error updating priority:', e)
|
||||
}
|
||||
@ -64,7 +65,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
setEditingId(null)
|
||||
onConfigChange?.()
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to update URL'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.urlFailed')
|
||||
setError(errorMessage)
|
||||
console.error('Error updating URL:', e)
|
||||
}
|
||||
@ -72,7 +73,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
|
||||
async function handleAddApi() {
|
||||
if (!newUrl.trim()) {
|
||||
setError('URL is required')
|
||||
setError(t('settings.nip95.error.urlRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -86,9 +87,9 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
onConfigChange?.()
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError && e.message.includes('Invalid URL')) {
|
||||
setError('Invalid URL format')
|
||||
setError(t('settings.nip95.error.invalidUrl'))
|
||||
} else {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to add API'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.addFailed')
|
||||
setError(errorMessage)
|
||||
}
|
||||
console.error('Error adding NIP-95 API:', e)
|
||||
@ -96,7 +97,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
}
|
||||
|
||||
async function handleRemoveApi(id: string) {
|
||||
if (!confirm('Are you sure you want to remove this endpoint?')) {
|
||||
if (!confirm(t('settings.nip95.remove.confirm'))) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -105,7 +106,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
await loadApis()
|
||||
onConfigChange?.()
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : 'Failed to remove API'
|
||||
const errorMessage = e instanceof Error ? e.message : t('settings.nip95.error.removeFailed')
|
||||
setError(errorMessage)
|
||||
console.error('Error removing NIP-95 API:', e)
|
||||
}
|
||||
@ -114,7 +115,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="text-center py-8 text-neon-cyan">
|
||||
<div>Loading...</div>
|
||||
<div>{t('settings.nip95.loading')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -134,12 +135,12 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
)}
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold text-neon-cyan">NIP-95 Upload Endpoints</h2>
|
||||
<h2 className="text-2xl font-bold text-neon-cyan">{t('settings.nip95.title')}</h2>
|
||||
<button
|
||||
onClick={() => setShowAddForm(!showAddForm)}
|
||||
className="px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan border border-neon-cyan/50 rounded transition-colors"
|
||||
>
|
||||
{showAddForm ? 'Cancel' : '+ Add Endpoint'}
|
||||
{showAddForm ? t('settings.nip95.add.cancel') : t('settings.nip95.addButton')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -147,13 +148,13 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
<div className="bg-cyber-dark border border-neon-cyan/30 rounded p-4 space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-cyber-accent mb-2">
|
||||
Endpoint URL
|
||||
{t('settings.nip95.add.url')}
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
value={newUrl}
|
||||
onChange={(e) => setNewUrl(e.target.value)}
|
||||
placeholder="https://example.com/upload"
|
||||
placeholder={t('settings.nip95.add.placeholder')}
|
||||
className="w-full px-4 py-2 bg-cyber-darker border border-cyber-accent/30 rounded text-cyber-light focus:border-neon-cyan focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
@ -162,7 +163,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
onClick={() => void handleAddApi()}
|
||||
className="px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan border border-neon-cyan/50 rounded transition-colors"
|
||||
>
|
||||
Add
|
||||
{t('settings.nip95.add.add')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@ -172,7 +173,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
}}
|
||||
className="px-4 py-2 bg-cyber-accent/20 hover:bg-cyber-accent/30 text-cyber-accent border border-cyber-accent/50 rounded transition-colors"
|
||||
>
|
||||
Cancel
|
||||
{t('settings.nip95.add.cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -181,7 +182,7 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
<div className="space-y-4">
|
||||
{apis.length === 0 ? (
|
||||
<div className="text-center py-8 text-cyber-accent">
|
||||
No NIP-95 endpoints configured
|
||||
{t('settings.nip95.empty')}
|
||||
</div>
|
||||
) : (
|
||||
apis.map((api) => (
|
||||
@ -236,21 +237,21 @@ export function Nip95ConfigManager({ onConfigChange }: Nip95ConfigManagerProps)
|
||||
className="w-4 h-4 text-neon-cyan bg-cyber-darker border-cyber-accent rounded focus:ring-neon-cyan"
|
||||
/>
|
||||
<span className="text-sm text-cyber-accent">
|
||||
{api.enabled ? 'Enabled' : 'Disabled'}
|
||||
{api.enabled ? t('settings.nip95.list.enabled') : t('settings.nip95.list.disabled')}
|
||||
</span>
|
||||
</label>
|
||||
<button
|
||||
onClick={() => void handleRemoveApi(api.id)}
|
||||
className="px-3 py-1 text-sm bg-red-900/30 hover:bg-red-900/50 text-red-300 border border-red-500/50 rounded transition-colors"
|
||||
title="Remove endpoint"
|
||||
title={t('settings.nip95.list.remove')}
|
||||
>
|
||||
Remove
|
||||
{t('settings.nip95.list.remove')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-2">
|
||||
<span className="text-sm text-cyber-accent">Priority:</span>
|
||||
<span className="text-sm text-cyber-accent">{t('settings.nip95.list.priority')}:</span>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
|
||||
@ -85,7 +85,7 @@ presentation.fallback.user=User
|
||||
|
||||
# Filters
|
||||
filters.clear=Clear all
|
||||
filters.author=By author
|
||||
filters.author=All authors
|
||||
filters.sort=Sort by
|
||||
filters.sort.newest=Newest
|
||||
filters.sort.oldest=Oldest
|
||||
@ -103,3 +103,69 @@ footer.privacy=Privacy Policy
|
||||
common.loading=Loading...
|
||||
common.error=Error
|
||||
common.back=Back
|
||||
common.open=Open
|
||||
|
||||
# Settings
|
||||
settings.title=Settings
|
||||
settings.keyManagement.title=Key Management
|
||||
settings.keyManagement.loading=Loading...
|
||||
settings.keyManagement.publicKey.npub=Public Key (npub)
|
||||
settings.keyManagement.publicKey.hex=Public Key (hex)
|
||||
settings.keyManagement.copy=Copy
|
||||
settings.keyManagement.copied=✓ Copied
|
||||
settings.keyManagement.noAccount.title=No account found
|
||||
settings.keyManagement.noAccount.description=Create a new account by importing a private key. The key will be encrypted using a two-level encryption system.
|
||||
settings.keyManagement.import.button.new=Import Private Key
|
||||
settings.keyManagement.import.button.replace=Replace Account (Import New Key)
|
||||
settings.keyManagement.import.warning.title=⚠️ Important
|
||||
settings.keyManagement.import.warning.description=After importing, you will receive <strong>4 recovery words</strong> (BIP39 dictionary) to secure your account. These words encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API, which then encrypts your private key stored in IndexedDB (two-level encryption system).
|
||||
settings.keyManagement.import.warning.replace=Warning: Importing a new key will replace your existing account. Make sure you have your recovery phrase saved before proceeding.
|
||||
settings.keyManagement.import.label=Private Key (nsec URL, nsec1..., or hex)
|
||||
settings.keyManagement.import.placeholder=nsec1... or nostr://nsec1... or hex key
|
||||
settings.keyManagement.import.help=You can paste a nsec key, a nostr:// URL containing a nsec, or a hex private key (64 characters).
|
||||
settings.keyManagement.import.error.required=Please enter a private key
|
||||
settings.keyManagement.import.error.invalid=Invalid key format. Please provide a nsec (nsec1...) or hex (64 characters) private key.
|
||||
settings.keyManagement.import.error.failed=Failed to import key
|
||||
settings.keyManagement.replace.warning.title=⚠️ Replace Existing Account?
|
||||
settings.keyManagement.replace.warning.description=This will delete your current account and create a new one with the imported key. Make sure you have saved your recovery phrase for the current account.
|
||||
settings.keyManagement.replace.cancel=Cancel
|
||||
settings.keyManagement.replace.confirm=Replace Account
|
||||
settings.keyManagement.replace.replacing=Replacing...
|
||||
settings.keyManagement.import.cancel=Cancel
|
||||
settings.keyManagement.import.importing=Importing...
|
||||
settings.keyManagement.import.import=Import
|
||||
settings.keyManagement.recovery.warning.title=⚠️ Important
|
||||
settings.keyManagement.recovery.warning.part1=These <strong>4 recovery words</strong> are your only way to recover your account. <strong>They will never be displayed again.</strong>
|
||||
settings.keyManagement.recovery.warning.part2=These words (BIP39 dictionary) are used with <strong>PBKDF2</strong> to encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API. This KEK then encrypts your private key stored in IndexedDB (two-level system).
|
||||
settings.keyManagement.recovery.warning.part3=Save them in a safe place. Without these words, you will permanently lose access to your account.
|
||||
settings.keyManagement.recovery.copy=Copy Recovery Words
|
||||
settings.keyManagement.recovery.copied=✓ Copied!
|
||||
settings.keyManagement.recovery.newNpub=Your new public key (npub)
|
||||
settings.keyManagement.recovery.done=Done
|
||||
settings.nip95.title=NIP-95 Upload Endpoints
|
||||
settings.nip95.loading=Loading...
|
||||
settings.nip95.error.loadFailed=Failed to load NIP-95 APIs
|
||||
settings.nip95.error.updateFailed=Failed to update API
|
||||
settings.nip95.error.priorityFailed=Failed to update priority
|
||||
settings.nip95.error.urlFailed=Failed to update URL
|
||||
settings.nip95.error.addFailed=Failed to add API
|
||||
settings.nip95.error.removeFailed=Failed to remove API
|
||||
settings.nip95.error.invalidUrl=Invalid URL format
|
||||
settings.nip95.error.urlRequired=URL is required
|
||||
settings.nip95.addButton=Add endpoint
|
||||
settings.nip95.add.url=Endpoint URL
|
||||
settings.nip95.add.placeholder=https://example.com/api/upload
|
||||
settings.nip95.add.add=Add
|
||||
settings.nip95.add.cancel=Cancel
|
||||
settings.nip95.add.adding=Adding...
|
||||
settings.nip95.list.enabled=Enabled
|
||||
settings.nip95.list.disabled=Disabled
|
||||
settings.nip95.list.priority=Priority
|
||||
settings.nip95.list.url=URL
|
||||
settings.nip95.list.actions=Actions
|
||||
settings.nip95.list.edit=Edit
|
||||
settings.nip95.list.save=Save
|
||||
settings.nip95.list.cancel=Cancel
|
||||
settings.nip95.list.remove=Remove
|
||||
settings.nip95.remove.confirm=Are you sure you want to remove this endpoint?
|
||||
settings.nip95.empty=No endpoints configured
|
||||
|
||||
@ -57,13 +57,35 @@ publish.publishing=Publication...
|
||||
# Presentation
|
||||
presentation.title=Créer votre article de présentation
|
||||
presentation.description=Cet article est obligatoire pour publier sur zapwall.fr. Il permet aux lecteurs de vous connaître et de vous sponsoriser.
|
||||
presentation.profileNote=Les données de ce profil sont spécifiques à zapwall.fr et peuvent différer de votre profil Nostr.
|
||||
presentation.success=Article de présentation créé !
|
||||
presentation.successMessage=Votre article de présentation a été créé avec succès. Vous pouvez maintenant publier des articles.
|
||||
presentation.profileNote=Les données de ce profil sont spécifiques à zapwall.fr et peuvent différer de votre profil Nostr.
|
||||
presentation.field.picture=Photo de profil
|
||||
presentation.field.picture.help=Image de profil pour votre page auteur (max 5Mo, formats: PNG, JPG, WebP)
|
||||
presentation.field.picture.change=Changer l'image
|
||||
presentation.field.picture.upload=Télécharger une image
|
||||
presentation.field.picture.uploading=Upload en cours...
|
||||
presentation.field.picture.remove=Supprimer
|
||||
presentation.field.picture.error.imagesOnly=Seules les images sont autorisées
|
||||
presentation.field.picture.error.uploadFailed=Erreur lors de l'upload
|
||||
presentation.field.authorName=Nom d'auteur
|
||||
presentation.field.authorName.placeholder=Votre nom d'auteur
|
||||
presentation.field.authorName.help=Ce nom sera affiché à la place de votre clé publique sur votre profil
|
||||
presentation.field.presentation=Présentation personnelle
|
||||
presentation.field.presentation.placeholder=Présentez-vous : qui êtes-vous, votre parcours, vos intérêts...
|
||||
presentation.field.presentation.help=Cette présentation sera visible par tous les lecteurs
|
||||
presentation.field.contentDescription=Description de votre contenu
|
||||
presentation.field.contentDescription.placeholder=Décrivez le type de contenu que vous publiez : science-fiction, recherche scientifique, thèmes abordés...
|
||||
presentation.field.contentDescription.help=Aidez les lecteurs à comprendre le type d'articles que vous publiez
|
||||
presentation.field.mainnetAddress=Adresse Bitcoin mainnet (pour le sponsoring)
|
||||
presentation.field.mainnetAddress.placeholder=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
|
||||
presentation.field.mainnetAddress.help=Adresse Bitcoin mainnet où vous recevrez les paiements de sponsoring (0.046 BTC hors frais par sponsoring)
|
||||
presentation.validation.invalidAddress=Adresse Bitcoin invalide (doit commencer par 1, 3 ou bc1)
|
||||
presentation.fallback.user=Utilisateur
|
||||
|
||||
# Filters
|
||||
filters.clear=Effacer tout
|
||||
filters.author=Par auteur
|
||||
filters.author=Tous les auteurs
|
||||
filters.sort=Trier par
|
||||
filters.sort.newest=Plus récent
|
||||
filters.sort.oldest=Plus ancien
|
||||
@ -81,3 +103,69 @@ footer.privacy=Politique de confidentialité
|
||||
common.loading=Chargement...
|
||||
common.error=Erreur
|
||||
common.back=Retour
|
||||
common.open=Ouvrir
|
||||
|
||||
# Settings
|
||||
settings.title=Paramètres
|
||||
settings.keyManagement.title=Gestion des clés
|
||||
settings.keyManagement.loading=Chargement...
|
||||
settings.keyManagement.publicKey.npub=Clé publique (npub)
|
||||
settings.keyManagement.publicKey.hex=Clé publique (hex)
|
||||
settings.keyManagement.copy=Copier
|
||||
settings.keyManagement.copied=✓ Copié
|
||||
settings.keyManagement.noAccount.title=Aucun compte trouvé
|
||||
settings.keyManagement.noAccount.description=Créez un nouveau compte en important une clé privée. La clé sera chiffrée à l'aide d'un système de chiffrement à deux niveaux.
|
||||
settings.keyManagement.import.button.new=Importer une clé privée
|
||||
settings.keyManagement.import.button.replace=Remplacer le compte (Importer une nouvelle clé)
|
||||
settings.keyManagement.import.warning.title=⚠️ Important
|
||||
settings.keyManagement.import.warning.description=Après l'import, vous recevrez <strong>4 mots de récupération</strong> (dictionnaire BIP39) pour sécuriser votre compte. Ces mots chiffrent une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur, qui chiffre ensuite votre clé privée stockée dans IndexedDB (système de chiffrement à deux niveaux).
|
||||
settings.keyManagement.import.warning.replace=Avertissement : L'importation d'une nouvelle clé remplacera votre compte existant. Assurez-vous d'avoir sauvegardé votre phrase de récupération avant de continuer.
|
||||
settings.keyManagement.import.label=Clé privée (URL nsec, nsec1..., ou hex)
|
||||
settings.keyManagement.import.placeholder=nsec1... ou nostr://nsec1... ou clé hex
|
||||
settings.keyManagement.import.help=Vous pouvez coller une clé nsec, une URL nostr:// contenant un nsec, ou une clé privée hex (64 caractères).
|
||||
settings.keyManagement.import.error.required=Veuillez entrer une clé privée
|
||||
settings.keyManagement.import.error.invalid=Format de clé invalide. Veuillez fournir un nsec (nsec1...) ou une clé privée hex (64 caractères).
|
||||
settings.keyManagement.import.error.failed=Échec de l'importation de la clé
|
||||
settings.keyManagement.replace.warning.title=⚠️ Remplacer le compte existant ?
|
||||
settings.keyManagement.replace.warning.description=Cela supprimera votre compte actuel et créera un nouveau compte avec la clé importée. Assurez-vous d'avoir sauvegardé votre phrase de récupération pour le compte actuel.
|
||||
settings.keyManagement.replace.cancel=Annuler
|
||||
settings.keyManagement.replace.confirm=Remplacer le compte
|
||||
settings.keyManagement.replace.replacing=Remplacement...
|
||||
settings.keyManagement.import.cancel=Annuler
|
||||
settings.keyManagement.import.importing=Importation...
|
||||
settings.keyManagement.import.import=Importer
|
||||
settings.keyManagement.recovery.warning.title=⚠️ Important
|
||||
settings.keyManagement.recovery.warning.part1=Ces <strong>4 mots de récupération</strong> sont votre seul moyen de récupérer votre compte. <strong>Ils ne seront jamais affichés à nouveau.</strong>
|
||||
settings.keyManagement.recovery.warning.part2=Ces mots (dictionnaire BIP39) sont utilisés avec <strong>PBKDF2</strong> pour chiffrer une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur. Cette KEK chiffre ensuite votre clé privée stockée dans IndexedDB (système à deux niveaux).
|
||||
settings.keyManagement.recovery.warning.part3=Enregistrez-les dans un endroit sûr. Sans ces mots, vous perdrez définitivement l'accès à votre compte.
|
||||
settings.keyManagement.recovery.copy=Copier les mots de récupération
|
||||
settings.keyManagement.recovery.copied=✓ Copié !
|
||||
settings.keyManagement.recovery.newNpub=Votre nouvelle clé publique (npub)
|
||||
settings.keyManagement.recovery.done=Terminé
|
||||
settings.nip95.title=NIP-95 Upload Endpoints
|
||||
settings.nip95.loading=Chargement...
|
||||
settings.nip95.error.loadFailed=Échec du chargement des API NIP-95
|
||||
settings.nip95.error.updateFailed=Échec de la mise à jour de l'API
|
||||
settings.nip95.error.priorityFailed=Échec de la mise à jour de la priorité
|
||||
settings.nip95.error.urlFailed=Échec de la mise à jour de l'URL
|
||||
settings.nip95.error.addFailed=Échec de l'ajout de l'API
|
||||
settings.nip95.error.removeFailed=Échec de la suppression de l'API
|
||||
settings.nip95.error.invalidUrl=Format d'URL invalide
|
||||
settings.nip95.error.urlRequired=L'URL est requise
|
||||
settings.nip95.addButton=Ajouter un endpoint
|
||||
settings.nip95.add.url=URL de l'endpoint
|
||||
settings.nip95.add.placeholder=https://example.com/api/upload
|
||||
settings.nip95.add.add=Ajouter
|
||||
settings.nip95.add.cancel=Annuler
|
||||
settings.nip95.add.adding=Ajout...
|
||||
settings.nip95.list.enabled=Activé
|
||||
settings.nip95.list.disabled=Désactivé
|
||||
settings.nip95.list.priority=Priorité
|
||||
settings.nip95.list.url=URL
|
||||
settings.nip95.list.actions=Actions
|
||||
settings.nip95.list.edit=Modifier
|
||||
settings.nip95.list.save=Enregistrer
|
||||
settings.nip95.list.cancel=Annuler
|
||||
settings.nip95.list.remove=Supprimer
|
||||
settings.nip95.remove.confirm=Êtes-vous sûr de vouloir supprimer cet endpoint ?
|
||||
settings.nip95.empty=Aucun endpoint configuré
|
||||
|
||||
@ -3,12 +3,13 @@ import { PageHeader } from '@/components/PageHeader'
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { Nip95ConfigManager } from '@/components/Nip95ConfigManager'
|
||||
import { KeyManagementManager } from '@/components/KeyManagementManager'
|
||||
import { t } from '@/lib/i18n'
|
||||
|
||||
export default function SettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Settings - zapwall.fr</title>
|
||||
<title>{t('settings.title')} - zapwall.fr</title>
|
||||
<meta name="description" content="Application settings and configuration" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
@ -16,7 +17,7 @@ export default function SettingsPage() {
|
||||
<main className="min-h-screen bg-cyber-darker">
|
||||
<PageHeader />
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<h1 className="text-3xl font-bold text-neon-cyan mb-8">Settings</h1>
|
||||
<h1 className="text-3xl font-bold text-neon-cyan mb-8">{t('settings.title')}</h1>
|
||||
<div className="space-y-8">
|
||||
<KeyManagementManager />
|
||||
<Nip95ConfigManager />
|
||||
|
||||
@ -104,3 +104,68 @@ common.loading=Loading...
|
||||
common.error=Error
|
||||
common.back=Back
|
||||
common.open=Open
|
||||
|
||||
# Settings
|
||||
settings.title=Settings
|
||||
settings.keyManagement.title=Key Management
|
||||
settings.keyManagement.loading=Loading...
|
||||
settings.keyManagement.publicKey.npub=Public Key (npub)
|
||||
settings.keyManagement.publicKey.hex=Public Key (hex)
|
||||
settings.keyManagement.copy=Copy
|
||||
settings.keyManagement.copied=✓ Copied
|
||||
settings.keyManagement.noAccount.title=No account found
|
||||
settings.keyManagement.noAccount.description=Create a new account by importing a private key. The key will be encrypted using a two-level encryption system.
|
||||
settings.keyManagement.import.button.new=Import Private Key
|
||||
settings.keyManagement.import.button.replace=Replace Account (Import New Key)
|
||||
settings.keyManagement.import.warning.title=⚠️ Important
|
||||
settings.keyManagement.import.warning.description=After importing, you will receive <strong>4 recovery words</strong> (BIP39 dictionary) to secure your account. These words encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API, which then encrypts your private key stored in IndexedDB (two-level encryption system).
|
||||
settings.keyManagement.import.warning.replace=Warning: Importing a new key will replace your existing account. Make sure you have your recovery phrase saved before proceeding.
|
||||
settings.keyManagement.import.label=Private Key (nsec URL, nsec1..., or hex)
|
||||
settings.keyManagement.import.placeholder=nsec1... or nostr://nsec1... or hex key
|
||||
settings.keyManagement.import.help=You can paste a nsec key, a nostr:// URL containing a nsec, or a hex private key (64 characters).
|
||||
settings.keyManagement.import.error.required=Please enter a private key
|
||||
settings.keyManagement.import.error.invalid=Invalid key format. Please provide a nsec (nsec1...) or hex (64 characters) private key.
|
||||
settings.keyManagement.import.error.failed=Failed to import key
|
||||
settings.keyManagement.replace.warning.title=⚠️ Replace Existing Account?
|
||||
settings.keyManagement.replace.warning.description=This will delete your current account and create a new one with the imported key. Make sure you have saved your recovery phrase for the current account.
|
||||
settings.keyManagement.replace.cancel=Cancel
|
||||
settings.keyManagement.replace.confirm=Replace Account
|
||||
settings.keyManagement.replace.replacing=Replacing...
|
||||
settings.keyManagement.import.cancel=Cancel
|
||||
settings.keyManagement.import.importing=Importing...
|
||||
settings.keyManagement.import.import=Import
|
||||
settings.keyManagement.recovery.warning.title=⚠️ Important
|
||||
settings.keyManagement.recovery.warning.part1=These <strong>4 recovery words</strong> are your only way to recover your account. <strong>They will never be displayed again.</strong>
|
||||
settings.keyManagement.recovery.warning.part2=These words (BIP39 dictionary) are used with <strong>PBKDF2</strong> to encrypt a Key Encryption Key (KEK) stored in the browser's Credentials API. This KEK then encrypts your private key stored in IndexedDB (two-level system).
|
||||
settings.keyManagement.recovery.warning.part3=Save them in a safe place. Without these words, you will permanently lose access to your account.
|
||||
settings.keyManagement.recovery.copy=Copy Recovery Words
|
||||
settings.keyManagement.recovery.copied=✓ Copied!
|
||||
settings.keyManagement.recovery.newNpub=Your new public key (npub)
|
||||
settings.keyManagement.recovery.done=Done
|
||||
settings.nip95.title=NIP-95 Upload Endpoints
|
||||
settings.nip95.loading=Loading...
|
||||
settings.nip95.error.loadFailed=Failed to load NIP-95 APIs
|
||||
settings.nip95.error.updateFailed=Failed to update API
|
||||
settings.nip95.error.priorityFailed=Failed to update priority
|
||||
settings.nip95.error.urlFailed=Failed to update URL
|
||||
settings.nip95.error.addFailed=Failed to add API
|
||||
settings.nip95.error.removeFailed=Failed to remove API
|
||||
settings.nip95.error.invalidUrl=Invalid URL format
|
||||
settings.nip95.error.urlRequired=URL is required
|
||||
settings.nip95.addButton=Add endpoint
|
||||
settings.nip95.add.url=Endpoint URL
|
||||
settings.nip95.add.placeholder=https://example.com/api/upload
|
||||
settings.nip95.add.add=Add
|
||||
settings.nip95.add.cancel=Cancel
|
||||
settings.nip95.add.adding=Adding...
|
||||
settings.nip95.list.enabled=Enabled
|
||||
settings.nip95.list.disabled=Disabled
|
||||
settings.nip95.list.priority=Priority
|
||||
settings.nip95.list.url=URL
|
||||
settings.nip95.list.actions=Actions
|
||||
settings.nip95.list.edit=Edit
|
||||
settings.nip95.list.save=Save
|
||||
settings.nip95.list.cancel=Cancel
|
||||
settings.nip95.list.remove=Remove
|
||||
settings.nip95.remove.confirm=Are you sure you want to remove this endpoint?
|
||||
settings.nip95.empty=No endpoints configured
|
||||
|
||||
@ -103,3 +103,69 @@ footer.privacy=Politique de confidentialité
|
||||
common.loading=Chargement...
|
||||
common.error=Erreur
|
||||
common.back=Retour
|
||||
common.open=Ouvrir
|
||||
|
||||
# Settings
|
||||
settings.title=Paramètres
|
||||
settings.keyManagement.title=Gestion des clés
|
||||
settings.keyManagement.loading=Chargement...
|
||||
settings.keyManagement.publicKey.npub=Clé publique (npub)
|
||||
settings.keyManagement.publicKey.hex=Clé publique (hex)
|
||||
settings.keyManagement.copy=Copier
|
||||
settings.keyManagement.copied=✓ Copié
|
||||
settings.keyManagement.noAccount.title=Aucun compte trouvé
|
||||
settings.keyManagement.noAccount.description=Créez un nouveau compte en important une clé privée. La clé sera chiffrée à l'aide d'un système de chiffrement à deux niveaux.
|
||||
settings.keyManagement.import.button.new=Importer une clé privée
|
||||
settings.keyManagement.import.button.replace=Remplacer le compte (Importer une nouvelle clé)
|
||||
settings.keyManagement.import.warning.title=⚠️ Important
|
||||
settings.keyManagement.import.warning.description=Après l'import, vous recevrez <strong>4 mots de récupération</strong> (dictionnaire BIP39) pour sécuriser votre compte. Ces mots chiffrent une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur, qui chiffre ensuite votre clé privée stockée dans IndexedDB (système de chiffrement à deux niveaux).
|
||||
settings.keyManagement.import.warning.replace=Avertissement : L'importation d'une nouvelle clé remplacera votre compte existant. Assurez-vous d'avoir sauvegardé votre phrase de récupération avant de continuer.
|
||||
settings.keyManagement.import.label=Clé privée (URL nsec, nsec1..., ou hex)
|
||||
settings.keyManagement.import.placeholder=nsec1... ou nostr://nsec1... ou clé hex
|
||||
settings.keyManagement.import.help=Vous pouvez coller une clé nsec, une URL nostr:// contenant un nsec, ou une clé privée hex (64 caractères).
|
||||
settings.keyManagement.import.error.required=Veuillez entrer une clé privée
|
||||
settings.keyManagement.import.error.invalid=Format de clé invalide. Veuillez fournir un nsec (nsec1...) ou une clé privée hex (64 caractères).
|
||||
settings.keyManagement.import.error.failed=Échec de l'importation de la clé
|
||||
settings.keyManagement.replace.warning.title=⚠️ Remplacer le compte existant ?
|
||||
settings.keyManagement.replace.warning.description=Cela supprimera votre compte actuel et créera un nouveau compte avec la clé importée. Assurez-vous d'avoir sauvegardé votre phrase de récupération pour le compte actuel.
|
||||
settings.keyManagement.replace.cancel=Annuler
|
||||
settings.keyManagement.replace.confirm=Remplacer le compte
|
||||
settings.keyManagement.replace.replacing=Remplacement...
|
||||
settings.keyManagement.import.cancel=Annuler
|
||||
settings.keyManagement.import.importing=Importation...
|
||||
settings.keyManagement.import.import=Importer
|
||||
settings.keyManagement.recovery.warning.title=⚠️ Important
|
||||
settings.keyManagement.recovery.warning.part1=Ces <strong>4 mots de récupération</strong> sont votre seul moyen de récupérer votre compte. <strong>Ils ne seront jamais affichés à nouveau.</strong>
|
||||
settings.keyManagement.recovery.warning.part2=Ces mots (dictionnaire BIP39) sont utilisés avec <strong>PBKDF2</strong> pour chiffrer une clé de chiffrement (KEK) stockée dans l'API Credentials du navigateur. Cette KEK chiffre ensuite votre clé privée stockée dans IndexedDB (système à deux niveaux).
|
||||
settings.keyManagement.recovery.warning.part3=Enregistrez-les dans un endroit sûr. Sans ces mots, vous perdrez définitivement l'accès à votre compte.
|
||||
settings.keyManagement.recovery.copy=Copier les mots de récupération
|
||||
settings.keyManagement.recovery.copied=✓ Copié !
|
||||
settings.keyManagement.recovery.newNpub=Votre nouvelle clé publique (npub)
|
||||
settings.keyManagement.recovery.done=Terminé
|
||||
settings.nip95.title=NIP-95 Upload Endpoints
|
||||
settings.nip95.loading=Chargement...
|
||||
settings.nip95.error.loadFailed=Échec du chargement des API NIP-95
|
||||
settings.nip95.error.updateFailed=Échec de la mise à jour de l'API
|
||||
settings.nip95.error.priorityFailed=Échec de la mise à jour de la priorité
|
||||
settings.nip95.error.urlFailed=Échec de la mise à jour de l'URL
|
||||
settings.nip95.error.addFailed=Échec de l'ajout de l'API
|
||||
settings.nip95.error.removeFailed=Échec de la suppression de l'API
|
||||
settings.nip95.error.invalidUrl=Format d'URL invalide
|
||||
settings.nip95.error.urlRequired=L'URL est requise
|
||||
settings.nip95.addButton=Ajouter un endpoint
|
||||
settings.nip95.add.url=URL de l'endpoint
|
||||
settings.nip95.add.placeholder=https://example.com/api/upload
|
||||
settings.nip95.add.add=Ajouter
|
||||
settings.nip95.add.cancel=Annuler
|
||||
settings.nip95.add.adding=Ajout...
|
||||
settings.nip95.list.enabled=Activé
|
||||
settings.nip95.list.disabled=Désactivé
|
||||
settings.nip95.list.priority=Priorité
|
||||
settings.nip95.list.url=URL
|
||||
settings.nip95.list.actions=Actions
|
||||
settings.nip95.list.edit=Modifier
|
||||
settings.nip95.list.save=Enregistrer
|
||||
settings.nip95.list.cancel=Annuler
|
||||
settings.nip95.list.remove=Supprimer
|
||||
settings.nip95.remove.confirm=Êtes-vous sûr de vouloir supprimer cet endpoint ?
|
||||
settings.nip95.empty=Aucun endpoint configuré
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user