i18n: translate the rest of Settings → General (encryption, ClubLog)
The Password-encryption, ClubLog-exceptions and ClubLog-Most-Wanted blocks in the General settings panel were still hardcoded English. Added ~30 gen.* keys (EN + FR) and wired every heading, checkbox label, description, button and status line through t(), plus the passphrase-mismatch error.
This commit is contained in:
@@ -1149,7 +1149,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
const refreshSecret = async () => { try { setSecret(await GetSecretStatus() as any); } catch {} };
|
||||
useEffect(() => { refreshSecret(); }, []);
|
||||
const applyPassphrase = async () => {
|
||||
if (ppNew !== ppConfirm) { setPpErr('Passphrases do not match'); return; }
|
||||
if (ppNew !== ppConfirm) { setPpErr(t('gen.pwMismatch')); return; }
|
||||
setPpBusy(true); setPpErr('');
|
||||
try { await SetPassphrase(ppNew); setPpNew(''); setPpConfirm(''); await refreshSecret(); }
|
||||
catch (e: any) { setPpErr(String(e?.message ?? e)); }
|
||||
@@ -4705,35 +4705,35 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
<MainViewPanes onChanged={onMainPaneChanged} flexAvailable={flexAvailable} icomAvailable={icomAvailable} />
|
||||
|
||||
<div className="border-t border-border/60 pt-4 space-y-2">
|
||||
<h4 className="text-sm font-semibold text-foreground">Password encryption</h4>
|
||||
<h4 className="text-sm font-semibold text-foreground">{t('gen.pwEnc')}</h4>
|
||||
{secret.has_passphrase ? (
|
||||
<>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Passwords encrypted{' '}
|
||||
{t('gen.pwEncrypted')}{' '}
|
||||
{secret.unlocked
|
||||
? <span className="text-success font-medium">— unlocked</span>
|
||||
: <span className="text-warning font-medium">— locked (unlock at launch)</span>}.
|
||||
? <span className="text-success font-medium">{t('gen.pwUnlocked')}</span>
|
||||
: <span className="text-warning font-medium">{t('gen.pwLocked')}</span>}.
|
||||
</p>
|
||||
{secret.unlocked && (
|
||||
<>
|
||||
<div className="flex items-center gap-2 max-w-md">
|
||||
<Input type="password" placeholder="New passphrase (to change)" value={ppNew} onChange={(e) => { setPpNew(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Input type="password" placeholder="Confirm" value={ppConfirm} onChange={(e) => { setPpConfirm(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Button size="sm" disabled={!ppNew || ppBusy} onClick={applyPassphrase}>Change</Button>
|
||||
<Input type="password" placeholder={t('gen.pwNewPh')} value={ppNew} onChange={(e) => { setPpNew(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Input type="password" placeholder={t('gen.pwConfirm')} value={ppConfirm} onChange={(e) => { setPpConfirm(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Button size="sm" disabled={!ppNew || ppBusy} onClick={applyPassphrase}>{t('gen.pwChange')}</Button>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" disabled={ppBusy} onClick={removePassphrase}>Remove encryption (store passwords in clear)</Button>
|
||||
<Button variant="outline" size="sm" disabled={ppBusy} onClick={removePassphrase}>{t('gen.pwRemove')}</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Encrypt saved passwords with a passphrase (asked at launch). Stays portable — re-enter it on another PC.
|
||||
{t('gen.pwHint')}
|
||||
</p>
|
||||
<div className="flex items-center gap-2 max-w-md">
|
||||
<Input type="password" placeholder="Passphrase" value={ppNew} onChange={(e) => { setPpNew(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Input type="password" placeholder="Confirm" value={ppConfirm} onChange={(e) => { setPpConfirm(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Button size="sm" disabled={!ppNew || ppNew !== ppConfirm || ppBusy} onClick={applyPassphrase}>Encrypt</Button>
|
||||
<Input type="password" placeholder={t('gen.pwPassphrase')} value={ppNew} onChange={(e) => { setPpNew(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Input type="password" placeholder={t('gen.pwConfirm')} value={ppConfirm} onChange={(e) => { setPpConfirm(e.target.value); setPpErr(''); }} className="h-8" />
|
||||
<Button size="sm" disabled={!ppNew || ppNew !== ppConfirm || ppBusy} onClick={applyPassphrase}>{t('gen.pwEncrypt')}</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -4741,7 +4741,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border/60 pt-4 space-y-2">
|
||||
<h4 className="text-sm font-semibold text-foreground">ClubLog exceptions (DXpedition overrides)</h4>
|
||||
<h4 className="text-sm font-semibold text-foreground">{t('gen.clubTitle')}</h4>
|
||||
<label className="flex items-start gap-2 text-sm cursor-pointer">
|
||||
<Checkbox
|
||||
checked={clubInfo.enabled}
|
||||
@@ -4762,30 +4762,28 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<span>
|
||||
Use ClubLog Country File for callsign resolution
|
||||
{t('gen.clubUse')}
|
||||
<span className="block text-xs text-muted-foreground mt-0.5">
|
||||
Applies ClubLog's date-ranged full-callsign <strong>exceptions</strong> that cty.dat lacks — e.g. VK2/SP9FIH
|
||||
resolves to Lord Howe Island (not Australia) for the DXpedition dates. Used on entry, import, UDP, and the
|
||||
right-click <em>Update from ClubLog</em>.
|
||||
{t('gen.clubDesc')}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex items-center gap-3 pl-6">
|
||||
<Button variant="outline" size="sm" className="h-8" disabled={clubBusy}
|
||||
onClick={() => { setClubBusy(true); setClubErr(''); DownloadClublogCty().then((i) => setClubInfo(i as ClubInfo)).catch((e: any) => setClubErr(String(e?.message ?? e))).finally(() => setClubBusy(false)); }}>
|
||||
{clubBusy ? 'Downloading…' : (clubInfo.loaded ? 'Update ClubLog data' : 'Download ClubLog data')}
|
||||
{clubBusy ? t('gen.downloading') : (clubInfo.loaded ? t('gen.clubUpdate') : t('gen.clubDownload'))}
|
||||
</Button>
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{clubInfo.loaded
|
||||
? `${clubInfo.count.toLocaleString()} exceptions${clubInfo.date ? ' · ' + clubInfo.date.slice(0, 10) : ''}`
|
||||
: 'not downloaded'}
|
||||
? t('gen.clubCount', { n: clubInfo.count.toLocaleString() }) + (clubInfo.date ? ' · ' + clubInfo.date.slice(0, 10) : '')
|
||||
: t('gen.notDownloaded')}
|
||||
</span>
|
||||
</div>
|
||||
{clubErr && <p className="text-[11px] text-destructive pl-6">{clubErr}</p>}
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border/60 pt-4 space-y-2">
|
||||
<h4 className="text-sm font-semibold text-foreground">ClubLog Most Wanted</h4>
|
||||
<h4 className="text-sm font-semibold text-foreground">{t('gen.mwTitle')}</h4>
|
||||
<label className="flex items-start gap-2 text-sm cursor-pointer">
|
||||
<Checkbox
|
||||
checked={mwInfo.enabled}
|
||||
@@ -4806,22 +4804,21 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<span>
|
||||
Show ClubLog "Most Wanted" rank in the entry matrix
|
||||
{t('gen.mwShow')}
|
||||
<span className="block text-xs text-muted-foreground mt-0.5">
|
||||
Displays a <strong>MW #rank</strong> pill next to the entity name (1 = the most wanted DXCC entity), fetched
|
||||
from ClubLog and <strong>personalised to your callsign</strong>. Refreshed daily.
|
||||
{t('gen.mwDesc')}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex items-center gap-3 pl-6">
|
||||
<Button variant="outline" size="sm" className="h-8" disabled={mwBusy}
|
||||
onClick={() => { setMwBusy(true); setMwErr(''); DownloadClublogMostWanted().then((i) => setMwInfo(i as MWInfo)).catch((e: any) => setMwErr(String(e?.message ?? e))).finally(() => setMwBusy(false)); }}>
|
||||
{mwBusy ? 'Downloading…' : (mwInfo.loaded ? 'Update Most Wanted' : 'Download Most Wanted')}
|
||||
{mwBusy ? t('gen.downloading') : (mwInfo.loaded ? t('gen.mwUpdate') : t('gen.mwDownload'))}
|
||||
</Button>
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{mwInfo.loaded
|
||||
? `${mwInfo.count.toLocaleString()} entities${mwInfo.callsign ? ' · ' + mwInfo.callsign : ''}${mwInfo.date ? ' · ' + mwInfo.date : ''}`
|
||||
: 'not downloaded'}
|
||||
? t('gen.mwCount', { n: mwInfo.count.toLocaleString() }) + (mwInfo.callsign ? ' · ' + mwInfo.callsign : '') + (mwInfo.date ? ' · ' + mwInfo.date : '')
|
||||
: t('gen.notDownloaded')}
|
||||
</span>
|
||||
</div>
|
||||
{mwErr && <p className="text-[11px] text-destructive pl-6">{mwErr}</p>}
|
||||
|
||||
@@ -170,6 +170,23 @@ const en: Dict = {
|
||||
'gen.showQsoRate': 'Show QSO rate in the header', 'gen.showQsoRateHint': '(QSOs/hour, projected from the last 10 / 60 min)',
|
||||
'gen.lookupOnBlur': 'Look up the callsign only after leaving the field', 'gen.lookupOnBlurHint': '(not while typing)', 'amp.hint': 'Configure one or several amplifiers — each panel card has a dropdown to pick which one it shows.', 'amp.none': 'No amplifier configured yet.', 'amp.namePh': 'Name (e.g. SPE left)', 'amp.remove': 'Remove this amplifier', 'amp.add': 'Add amplifier',
|
||||
'gen.groupDigital': 'Group digital modes as one (DXCC-style)', 'gen.groupDigitalHint': '(matrix badges + cluster: FT8/FT4/RTTY… count as a single Digital mode; off = each digital mode is its own slot)',
|
||||
// Password encryption
|
||||
'gen.pwEnc': 'Password encryption',
|
||||
'gen.pwEncrypted': 'Passwords encrypted', 'gen.pwUnlocked': '— unlocked', 'gen.pwLocked': '— locked (unlock at launch)',
|
||||
'gen.pwNewPh': 'New passphrase (to change)', 'gen.pwConfirm': 'Confirm', 'gen.pwChange': 'Change',
|
||||
'gen.pwRemove': 'Remove encryption (store passwords in clear)',
|
||||
'gen.pwHint': 'Encrypt saved passwords with a passphrase (asked at launch). Stays portable — re-enter it on another PC.',
|
||||
'gen.pwPassphrase': 'Passphrase', 'gen.pwEncrypt': 'Encrypt', 'gen.pwMismatch': 'Passphrases do not match',
|
||||
// ClubLog exceptions + Most Wanted
|
||||
'gen.clubTitle': 'ClubLog exceptions (DXpedition overrides)',
|
||||
'gen.clubUse': 'Use ClubLog Country File for callsign resolution',
|
||||
'gen.clubDesc': "Applies ClubLog's date-ranged full-callsign exceptions that cty.dat lacks — e.g. VK2/SP9FIH resolves to Lord Howe Island (not Australia) for the DXpedition dates. Used on entry, import, UDP, and the right-click Update from ClubLog.",
|
||||
'gen.clubUpdate': 'Update ClubLog data', 'gen.clubDownload': 'Download ClubLog data',
|
||||
'gen.clubCount': '{n} exceptions', 'gen.downloading': 'Downloading…', 'gen.notDownloaded': 'not downloaded',
|
||||
'gen.mwTitle': 'ClubLog Most Wanted',
|
||||
'gen.mwShow': 'Show ClubLog "Most Wanted" rank in the entry matrix',
|
||||
'gen.mwDesc': 'Displays a MW #rank pill next to the entity name (1 = the most wanted DXCC entity), fetched from ClubLog and personalised to your callsign. Refreshed daily.',
|
||||
'gen.mwUpdate': 'Update Most Wanted', 'gen.mwDownload': 'Download Most Wanted', 'gen.mwCount': '{n} entities',
|
||||
'gen.checkUpdates': 'Check for updates at startup', 'gen.checkUpdatesHint': '(notifies when a newer OpsLog is published)',
|
||||
'email.title': 'E-mail',
|
||||
// Station panel
|
||||
@@ -505,6 +522,23 @@ const fr: Dict = {
|
||||
'gen.showQsoRate': 'Afficher le rythme QSO dans la barre du haut', 'gen.showQsoRateHint': '(QSO/heure, projeté sur les 10 / 60 dernières min)',
|
||||
'gen.lookupOnBlur': 'Rechercher l\'indicatif seulement après avoir quitté le champ', 'gen.lookupOnBlurHint': '(pas pendant la saisie)', 'amp.hint': 'Configurez un ou plusieurs amplificateurs — chaque carte de panneau a une liste déroulante pour choisir lequel afficher.', 'amp.none': 'Aucun amplificateur configuré.', 'amp.namePh': 'Nom (p. ex. SPE gauche)', 'amp.remove': 'Supprimer cet amplificateur', 'amp.add': 'Ajouter un amplificateur',
|
||||
'gen.groupDigital': 'Regrouper les modes digitaux en un seul (style DXCC)', 'gen.groupDigitalHint': '(badges de la matrice + cluster : FT8/FT4/RTTY… comptent comme un seul mode Digital ; décoché = chaque mode digital est un slot distinct)',
|
||||
// Chiffrement des mots de passe
|
||||
'gen.pwEnc': 'Chiffrement des mots de passe',
|
||||
'gen.pwEncrypted': 'Mots de passe chiffrés', 'gen.pwUnlocked': '— déverrouillé', 'gen.pwLocked': '— verrouillé (à déverrouiller au lancement)',
|
||||
'gen.pwNewPh': 'Nouvelle phrase secrète (pour changer)', 'gen.pwConfirm': 'Confirmer', 'gen.pwChange': 'Changer',
|
||||
'gen.pwRemove': 'Retirer le chiffrement (stocker les mots de passe en clair)',
|
||||
'gen.pwHint': "Chiffre les mots de passe enregistrés avec une phrase secrète (demandée au lancement). Reste portable — à ressaisir sur un autre PC.",
|
||||
'gen.pwPassphrase': 'Phrase secrète', 'gen.pwEncrypt': 'Chiffrer', 'gen.pwMismatch': 'Les phrases secrètes ne correspondent pas',
|
||||
// Exceptions ClubLog + Most Wanted
|
||||
'gen.clubTitle': 'Exceptions ClubLog (overrides DXpédition)',
|
||||
'gen.clubUse': "Utiliser le Country File ClubLog pour la résolution d'indicatif",
|
||||
'gen.clubDesc': "Applique les exceptions ClubLog d'indicatif complet datées que cty.dat n'a pas — p. ex. VK2/SP9FIH devient Lord Howe Island (pas l'Australie) pendant les dates de la DXpédition. Utilisé à la saisie, à l'import, en UDP et via le clic droit « Mettre à jour depuis ClubLog ».",
|
||||
'gen.clubUpdate': 'Mettre à jour les données ClubLog', 'gen.clubDownload': 'Télécharger les données ClubLog',
|
||||
'gen.clubCount': '{n} exceptions', 'gen.downloading': 'Téléchargement…', 'gen.notDownloaded': 'non téléchargé',
|
||||
'gen.mwTitle': 'ClubLog Most Wanted',
|
||||
'gen.mwShow': 'Afficher le rang « Most Wanted » de ClubLog dans la matrice de saisie',
|
||||
'gen.mwDesc': "Affiche une pastille MW #rang à côté du nom de l'entité (1 = l'entité DXCC la plus recherchée), récupérée depuis ClubLog et personnalisée selon ton indicatif. Rafraîchie quotidiennement.",
|
||||
'gen.mwUpdate': 'Mettre à jour Most Wanted', 'gen.mwDownload': 'Télécharger Most Wanted', 'gen.mwCount': '{n} entités',
|
||||
'gen.checkUpdates': 'Vérifier les mises à jour au démarrage', 'gen.checkUpdatesHint': '(prévient quand une version plus récente est publiée)',
|
||||
'email.title': 'E-mail',
|
||||
'station.loading': 'Chargement du profil…',
|
||||
|
||||
Reference in New Issue
Block a user