fix: add i18n for EQ settings (#2174)

* fix: add i18n for EQ settings

* reuse common reset i18n labels
This commit is contained in:
York
2026-06-22 22:17:50 +08:00
committed by GitHub
parent 0ed68e8ebb
commit ecda4ef8bc
2 changed files with 82 additions and 51 deletions
+34
View File
@@ -845,6 +845,22 @@
"enableAutoTranslation": "Enable auto translation", "enableAutoTranslation": "Enable auto translation",
"enableFurigana_description": "Display pronunciation guides (furigana) over Japanese kanji lyrics.", "enableFurigana_description": "Display pronunciation guides (furigana) over Japanese kanji lyrics.",
"enableFurigana": "Enable furigana generation", "enableFurigana": "Enable furigana generation",
"equalizer_descriptionMpv": "Parametric equalizer via FFmpeg lavfi (MPV)",
"equalizer_descriptionWebAudio": "Parametric equalizer via Web Audio API",
"equalizer": "Equalizer",
"equalizerBands_description": "Per-band gain. Drag up/down or type a value. Range: -12 to +12 dB.",
"equalizerBands": "Bands",
"equalizerPreamp_description": "Input gain before EQ bands. Set negative when boosting bands to prevent clipping (MPV).",
"equalizerPreamp": "Preamp",
"equalizerPreset_description": "Apply a built-in or saved custom EQ curve",
"equalizerPreset": "Preset",
"equalizerPresetDeletePlaceholder": "Delete custom...",
"equalizerPresetGroupBuiltIn": "Built-in",
"equalizerPresetGroupCustom": "Custom",
"equalizerPresetNamePlaceholder": "Preset name...",
"equalizerPresetSelectPlaceholder": "Select preset",
"equalizerSavePreset_description": "Save the current EQ settings as a named preset",
"equalizerSavePreset": "Save preset",
"enableRemote_description": "Enables the remote control server to allow other devices to control the application", "enableRemote_description": "Enables the remote control server to allow other devices to control the application",
"enableRemote": "Enable remote control server", "enableRemote": "Enable remote control server",
"exitToTray_description": "Exit the application to the system tray", "exitToTray_description": "Exit the application to the system tray",
@@ -1026,6 +1042,24 @@
"showVisualizerInSidebar": "Show visualizer in player sidebar", "showVisualizerInSidebar": "Show visualizer in player sidebar",
"combinedLyricsAndVisualizer_description": "Combine lyrics and visualizer into the same panel", "combinedLyricsAndVisualizer_description": "Combine lyrics and visualizer into the same panel",
"combinedLyricsAndVisualizer": "Combine lyrics and visualizer in player sidebar", "combinedLyricsAndVisualizer": "Combine lyrics and visualizer in player sidebar",
"compressor_descriptionMpv": "Dynamic range compressor via FFmpeg acompressor (MPV)",
"compressor_descriptionWebAudio": "Dynamic range compressor via Web Audio API",
"compressor": "Compressor",
"compressorAttack_description": "How quickly the compressor engages after the signal exceeds the threshold.",
"compressorAttack": "Attack",
"compressorKnee_description": "Soft-knee width. Higher values make the transition into compression more gradual.",
"compressorKnee": "Knee",
"compressorMakeupGain_description": "Output gain applied after compression to restore loudness.",
"compressorMakeupGain": "Makeup Gain",
"compressorPreset_description": "Apply a built-in or saved custom compressor setting",
"compressorRatio_description": "Compression ratio, e.g. 4 = 4:1.",
"compressorRatio": "Ratio",
"compressorRelease_description": "How quickly the compressor releases after the signal drops below the threshold.",
"compressorRelease": "Release",
"compressorReset_description": "Restore all compressor parameters to their default values",
"compressorSavePreset_description": "Save the current compressor settings as a named preset",
"compressorThreshold_description": "Signal level above which compression begins.",
"compressorThreshold": "Threshold",
"preservePitch_description": "Preserves pitch when modifying playback speed", "preservePitch_description": "Preserves pitch when modifying playback speed",
"preservePitch": "Preserve pitch", "preservePitch": "Preserve pitch",
"audioFadeOnStatusChange": "Audio fade on status change", "audioFadeOnStatusChange": "Audio fade on status change",
@@ -1,6 +1,7 @@
import { useMove } from '@mantine/hooks'; import { useMove } from '@mantine/hooks';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { memo, useCallback, useContext, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
buildMpvAudioFilters, buildMpvAudioFilters,
@@ -257,6 +258,7 @@ function EqBandSlider({
// ─── Main component ─────────────────────────────────────────────────────────── // ─── Main component ───────────────────────────────────────────────────────────
export const EqSettings = memo(() => { export const EqSettings = memo(() => {
const { t } = useTranslation();
const settings = usePlaybackSettings(); const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions(); const { setSettings } = useSettingsStoreActions();
@@ -443,13 +445,13 @@ export const EqSettings = memo(() => {
// ── Preset select data ──────────────────────────────────────────────────── // ── Preset select data ────────────────────────────────────────────────────
const eqPresetSelectData = [ const eqPresetSelectData = [
{ {
group: 'Built-in', group: t('setting.equalizerPresetGroupBuiltIn'),
items: Object.keys(EQ_PRESETS).map((name) => ({ label: name, value: name })), items: Object.keys(EQ_PRESETS).map((name) => ({ label: name, value: name })),
}, },
...(Object.keys(customEqPresets).length > 0 ...(Object.keys(customEqPresets).length > 0
? [ ? [
{ {
group: 'Custom', group: t('setting.equalizerPresetGroupCustom'),
items: Object.keys(customEqPresets).map((name) => ({ items: Object.keys(customEqPresets).map((name) => ({
label: name, label: name,
value: name, value: name,
@@ -461,13 +463,13 @@ export const EqSettings = memo(() => {
const compPresetSelectData = [ const compPresetSelectData = [
{ {
group: 'Built-in', group: t('setting.equalizerPresetGroupBuiltIn'),
items: Object.keys(COMP_PRESETS).map((name) => ({ label: name, value: name })), items: Object.keys(COMP_PRESETS).map((name) => ({ label: name, value: name })),
}, },
...(Object.keys(customCompPresets).length > 0 ...(Object.keys(customCompPresets).length > 0
? [ ? [
{ {
group: 'Custom', group: t('setting.equalizerPresetGroupCustom'),
items: Object.keys(customCompPresets).map((name) => ({ items: Object.keys(customCompPresets).map((name) => ({
label: name, label: name,
value: name, value: name,
@@ -488,9 +490,9 @@ export const EqSettings = memo(() => {
), ),
description: description:
settings.type === PlayerType.LOCAL settings.type === PlayerType.LOCAL
? 'Parametric equalizer via FFmpeg lavfi (MPV)' ? t('setting.equalizer', { context: 'descriptionMpv' })
: 'Parametric equalizer via Web Audio API', : t('setting.equalizer', { context: 'descriptionWebAudio' }),
title: 'Equalizer', title: t('setting.equalizer'),
}, },
...(settings.equalizer.enabled ...(settings.equalizer.enabled
? ([ ? ([
@@ -505,7 +507,7 @@ export const EqSettings = memo(() => {
const preset = customEqPresets[name] ?? EQ_PRESETS[name]; const preset = customEqPresets[name] ?? EQ_PRESETS[name];
if (preset) applyEqPreset(preset); if (preset) applyEqPreset(preset);
}} }}
placeholder="Select preset" placeholder={t('setting.equalizerPresetSelectPlaceholder')}
searchable searchable
value={null} value={null}
w={180} w={180}
@@ -521,15 +523,15 @@ export const EqSettings = memo(() => {
if (!name) return; if (!name) return;
handleDeleteEqPreset(name); handleDeleteEqPreset(name);
}} }}
placeholder="Delete custom..." placeholder={t('setting.equalizerPresetDeletePlaceholder')}
value={null} value={null}
w={160} w={160}
/> />
)} )}
</Group> </Group>
), ),
description: 'Apply a built-in or saved custom EQ curve', description: t('setting.equalizerPreset', { context: 'description' }),
title: 'Preset', title: t('setting.equalizerPreset'),
}, },
{ {
control: ( control: (
@@ -539,7 +541,7 @@ export const EqSettings = memo(() => {
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter') handleSaveEqPreset(); if (e.key === 'Enter') handleSaveEqPreset();
}} }}
placeholder="Preset name..." placeholder={t('setting.equalizerPresetNamePlaceholder')}
value={saveEqName} value={saveEqName}
w={180} w={180}
/> />
@@ -548,12 +550,12 @@ export const EqSettings = memo(() => {
onClick={handleSaveEqPreset} onClick={handleSaveEqPreset}
variant="subtle" variant="subtle"
> >
Save {t('common.save')}
</Button> </Button>
</Group> </Group>
), ),
description: 'Save the current EQ settings as a named preset', description: t('setting.equalizerSavePreset', { context: 'description' }),
title: 'Save preset', title: t('setting.equalizerSavePreset'),
}, },
{ {
control: ( control: (
@@ -600,13 +602,12 @@ export const EqSettings = memo(() => {
w={70} w={70}
/> />
<Button onClick={handleResetEq} variant="subtle"> <Button onClick={handleResetEq} variant="subtle">
Reset all {t('common.reset')}
</Button> </Button>
</Group> </Group>
), ),
description: description: t('setting.equalizerPreamp', { context: 'description' }),
'Input gain before EQ bands. Set negative when boosting bands to prevent clipping (MPV).', title: t('setting.equalizerPreamp'),
title: 'Preamp',
}, },
{ {
control: ( control: (
@@ -625,9 +626,8 @@ export const EqSettings = memo(() => {
))} ))}
</Group> </Group>
), ),
description: description: t('setting.equalizerBands', { context: 'description' }),
'Per-band gain. Drag up/down or type a value. Range: -12 to +12 dB.', title: t('setting.equalizerBands'),
title: 'Bands',
}, },
] as SettingOption[]) ] as SettingOption[])
: []), : []),
@@ -644,60 +644,57 @@ export const EqSettings = memo(() => {
unit: string; unit: string;
}[] = [ }[] = [
{ {
description: 'Signal level above which compression begins.', description: t('setting.compressorThreshold', { context: 'description' }),
key: 'threshold', key: 'threshold',
max: 0, max: 0,
min: -60, min: -60,
step: 1, step: 1,
title: 'Threshold', title: t('setting.compressorThreshold'),
unit: 'dB', unit: 'dB',
}, },
{ {
description: 'Compression ratio, e.g. 4 = 4:1.', description: t('setting.compressorRatio', { context: 'description' }),
key: 'ratio', key: 'ratio',
max: 20, max: 20,
min: 1, min: 1,
step: 0.5, step: 0.5,
title: 'Ratio', title: t('setting.compressorRatio'),
unit: ':1', unit: ':1',
}, },
{ {
description: description: t('setting.compressorAttack', { context: 'description' }),
'How quickly the compressor engages after the signal exceeds the threshold.',
key: 'attack', key: 'attack',
max: 2000, max: 2000,
min: 0.1, min: 0.1,
step: 1, step: 1,
title: 'Attack', title: t('setting.compressorAttack'),
unit: 'ms', unit: 'ms',
}, },
{ {
description: description: t('setting.compressorRelease', { context: 'description' }),
'How quickly the compressor releases after the signal drops below the threshold.',
key: 'release', key: 'release',
max: 9000, max: 9000,
min: 1, min: 1,
step: 10, step: 10,
title: 'Release', title: t('setting.compressorRelease'),
unit: 'ms', unit: 'ms',
}, },
{ {
description: 'Output gain applied after compression to restore loudness.', description: t('setting.compressorMakeupGain', { context: 'description' }),
key: 'makeup', key: 'makeup',
max: 30, max: 30,
min: 0, min: 0,
step: 0.5, step: 0.5,
title: 'Makeup Gain', title: t('setting.compressorMakeupGain'),
unit: 'dB', unit: 'dB',
}, },
{ {
description: description: t('setting.compressorKnee', { context: 'description' }),
'Soft-knee width. Higher values make the transition into compression more gradual.',
key: 'knee', key: 'knee',
max: 10, max: 10,
min: 1, min: 1,
step: 0.5, step: 0.5,
title: 'Knee', title: t('setting.compressorKnee'),
unit: 'dB', unit: 'dB',
}, },
]; ];
@@ -713,9 +710,9 @@ export const EqSettings = memo(() => {
), ),
description: description:
settings.type === PlayerType.LOCAL settings.type === PlayerType.LOCAL
? 'Dynamic range compressor via FFmpeg acompressor (MPV)' ? t('setting.compressor', { context: 'descriptionMpv' })
: 'Dynamic range compressor via Web Audio API', : t('setting.compressor', { context: 'descriptionWebAudio' }),
title: 'Compressor', title: t('setting.compressor'),
}, },
...(settings.compressor.enabled ...(settings.compressor.enabled
? ([ ? ([
@@ -730,7 +727,7 @@ export const EqSettings = memo(() => {
const preset = customCompPresets[name] ?? COMP_PRESETS[name]; const preset = customCompPresets[name] ?? COMP_PRESETS[name];
if (preset) applyCompPreset(preset); if (preset) applyCompPreset(preset);
}} }}
placeholder="Select preset" placeholder={t('setting.equalizerPresetSelectPlaceholder')}
searchable searchable
value={null} value={null}
w={180} w={180}
@@ -746,15 +743,15 @@ export const EqSettings = memo(() => {
if (!name) return; if (!name) return;
handleDeleteCompPreset(name); handleDeleteCompPreset(name);
}} }}
placeholder="Delete custom..." placeholder={t('setting.equalizerPresetDeletePlaceholder')}
value={null} value={null}
w={160} w={160}
/> />
)} )}
</Group> </Group>
), ),
description: 'Apply a built-in or saved custom compressor setting', description: t('setting.compressorPreset', { context: 'description' }),
title: 'Preset', title: t('setting.equalizerPreset'),
}, },
{ {
control: ( control: (
@@ -764,7 +761,7 @@ export const EqSettings = memo(() => {
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter') handleSaveCompPreset(); if (e.key === 'Enter') handleSaveCompPreset();
}} }}
placeholder="Preset name..." placeholder={t('setting.equalizerPresetNamePlaceholder')}
value={saveCompName} value={saveCompName}
w={180} w={180}
/> />
@@ -773,12 +770,12 @@ export const EqSettings = memo(() => {
onClick={handleSaveCompPreset} onClick={handleSaveCompPreset}
variant="subtle" variant="subtle"
> >
Save {t('common.save')}
</Button> </Button>
</Group> </Group>
), ),
description: 'Save the current compressor settings as a named preset', description: t('setting.compressorSavePreset', { context: 'description' }),
title: 'Save preset', title: t('setting.equalizerSavePreset'),
}, },
// One SettingOption per compressor parameter — Slider + NumberInput // One SettingOption per compressor parameter — Slider + NumberInput
...compParams.map(({ description, key, max, min, step, title, unit }) => ({ ...compParams.map(({ description, key, max, min, step, title, unit }) => ({
@@ -834,11 +831,11 @@ export const EqSettings = memo(() => {
{ {
control: ( control: (
<Button onClick={handleResetComp} variant="subtle"> <Button onClick={handleResetComp} variant="subtle">
Reset to defaults {t('common.resetToDefault')}
</Button> </Button>
), ),
description: 'Restore all compressor parameters to their default values', description: t('setting.compressorReset', { context: 'description' }),
title: 'Reset', title: t('common.reset'),
}, },
] as SettingOption[]) ] as SettingOption[])
: []), : []),