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>}
|
||||
|
||||
Reference in New Issue
Block a user