From 517ae96ab137262036fa3449fc581fb638d533e4 Mon Sep 17 00:00:00 2001 From: eafonyang Date: Fri, 12 Jun 2026 19:50:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=86=E4=BA=AB=20EQ=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=85=B7=E4=BD=93=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E8=BF=98=E6=B2=A1=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=85=88=E7=94=A8?= =?UTF-8?q?=E4=BC=AA=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/locales/data-de.json | 274 --------------- client/src/locales/data-en.json | 23 +- client/src/locales/data-es.json | 274 --------------- client/src/locales/data-fr.json | 274 --------------- client/src/locales/data-zh-HK.json | 23 +- client/src/locales/data-zh.json | 23 +- client/src/pages/EQPage.tsx | 523 ++++++++++++++++++++++++++++- 7 files changed, 586 insertions(+), 828 deletions(-) delete mode 100644 client/src/locales/data-de.json delete mode 100644 client/src/locales/data-es.json delete mode 100644 client/src/locales/data-fr.json diff --git a/client/src/locales/data-de.json b/client/src/locales/data-de.json deleted file mode 100644 index 7ad806c..0000000 --- a/client/src/locales/data-de.json +++ /dev/null @@ -1,274 +0,0 @@ -{ - "home": { "audioSet": "Audioeinstellungen" }, - "source": { - "label": "Eingang & Ausgang", - "inputOptions": ["USB-B", "USB-C", "Koaxial", "Optisch", "Bluetooth", "HDMI-EARC", "RCA", "USB-Treiber"], - "input": { - "coaxial": "Koaxial", - "optical": "Optisch", - "bluetooth": "Bluetooth", - "rca": "Analog RCA" - }, - "output": { "headset": "Kopfhörer" } - }, - "effect": { - "lowBass": "Tiefton", - "enterBass": "Mittlerer Ton", - "highBass": "Hochton", - "tone": "Tonhöhe", - "loudness": "Lautheit", - "threshold": "Schwellenwert", - "delayed": "Verzögerung", - "mainframeCase": "Hauptgehäuse", - "subwooferUnit": "Subwoofer", - "style": { - "label": "Stil", - "options": [ - { "index": 0, "label": "Klassisch" }, - { "index": 1, "label": "Dance" }, - { "index": 2, "label": "Pop" }, - { "index": 3, "label": "Reggae" }, - { "index": 4, "label": "Live" }, - { "index": 5, "label": "Rock" }, - { "index": 6, "label": "Sanft" }, - { "index": 7, "label": "Elektronisch" }, - { "index": 8, "label": "Club" }, - { "index": 9, "label": "Nur Bass" }, - { "index": 10, "label": "Nur Höhen" }, - { "index": 11, "label": "Kopfhörer" }, - { "index": 12, "label": "Halle" }, - { "index": 13, "label": "Fusion" }, - { "index": 14, "label": "Ska" }, - { "index": 15, "label": "Slow Dance" } - ] - }, - "stereoWidth": { "label": "Stereobreite" }, - "crossfeed": { "label": "Crossfeed" }, - "subwoofer": { - "label": "Subwoofer-Ausgang", - "freq": "Tiefpassfrequenz", - "attenuation": "Dämpfungsrate", - "gain": "Bassverstärkung", - "output": { - "label": "Ausgangsmodus", - "options": [ - { "label": "Mono", "index": 0 }, - { "label": "Stereo", "index": 1 } - ] - } - } - }, - "peq": { - "Standard": "Standard-Equalizer-Voreinstellung", - "edit": "Mehrfachbearbeitung", - "headset": "Kopfhörermodell hinzufügen", - "preamp": "Vorverstärkung", - "filters": { - "label": "Filter", - "options": [ - { - "index": 0, - "title": "LPF", - "desc1": "Tiefpassfilter lässt nur Signale unterhalb", - "desc2": "einer bestimmten Grenzfrequenz durch,", - "desc3": "Hochfrequente Signale werden abgeschwächt.", - "img": "LPF.png" - }, - { - "index": 1, - "title": "HPF", - "desc1": "Hochpassfilter lässt nur Signale oberhalb", - "desc2": "einer bestimmten Grenzfrequenz durch,", - "desc3": "Niederfrequente Signale werden abgeschwächt.", - "img": "HPF.png" - }, - { - "index": 2, - "title": "BPF", - "img": "BPF.png", - "desc1": "Bandpassfilter lässt nur Signale in einem", - "desc2": "bestimmten Frequenzbereich durch,", - "desc3": "andere Frequenzen werden abgeschwächt." - }, - { - "index": 3, - "title": "NOTCH", - "img": "NOTCH.png", - "desc1": "Notch-Filter zur Beseitigung von", - "desc2": "Resonanzen oder Störungen bei bestimmten Frequenzen." - }, - { - "index": 4, - "title": "PEAK", - "img": "PEAK.png", - "desc1": "Peak-Filter verstärken oder dämpfen", - "desc2": "Signale um eine bestimmte Frequenz herum." - }, - { - "index": 5, - "title": "LSHELF", - "img": "LSHELF.png", - "desc1": "Low-Shelf-Filter zur Verstärkung oder", - "desc2": "Dämpfung tiefer Frequenzen." - }, - { - "index": 5, - "title": "HSHELF", - "img": "HSHELF.png", - "desc1": "High-Shelf-Filter zur Verstärkung oder", - "desc2": "Dämpfung hoher Frequenzen." - }, - { - "index": 6, - "title": "APF", - "img": "APF.png", - "desc1": "Allpassfilter verändern nur", - "desc2": "die Phase des Signals, nicht die Amplitude." - } - ] - }, - "freq": "FREQ", - "gain": "GAIN", - "q": "Q-Wert", - "loadDeviceBtn": "Laden bestätigen", - "peqNoneItem": "Nicht konfiguriert" - }, - "dac": { - "balance": "Balance Links/Rechts", - "filters": { - "label": "Filtercharakteristik", - "options": [ - { "index": 0, "label": "Schneller Abfall" }, - { "index": 1, "label": "Langsamer Abfall" }, - { "index": 2, "label": "Kurze Verzögerung, schneller Abfall" }, - { "index": 3, "label": "Kurze Verzögerung, langsamer Abfall" }, - { "index": 4, "label": "Sehr langsamer Abfall" }, - { "index": 5, "label": "Geringe Dispersion, kurze Verzögerung" } - ] - }, - "headPhoneGain": { - "label": "Kopfhörerverstärkung", - "options": [ - { "index": 0, "label": "Niedrig" }, - { "index": 1, "label": "Mittel" }, - { "index": 2, "label": "Hoch" } - ] - }, - "volumeStep": "Lautstärkeschritt", - "bootVolume": "Startlautstärke", - "xlr": { - "label": "XLR-Polarität", - "options": [ - { "index": 0, "label": "Normal" }, - { "index": 1, "label": "Invertiert" } - ] - }, - "arc": { - "label": "ARC-Modus", - "options": [ - { "index": 0, "label": "ARC" }, - { "index": 1, "label": "EARC" } - ] - } - }, - "settings": { - "vu": "Einstellungen", - "vuMeter": "VU-Meter", - "screenBrightness": { - "label": "Bildschirmhelligkeit", - "options": [ - { "index": 0, "label": "Hell" }, - { "index": 1, "label": "Mittel" }, - { "index": 2, "label": "Dunkel" } - ] - }, - "sleep": { - "label": "Ruhezustand", - "options": [ - { "index": 0, "label": "Aus" }, - { "index": 1, "label": "Kein Signal 1 Min" }, - { "index": 2, "label": "Kein Signal 5 Min" }, - { "index": 3, "label": "Kein Signal 10 Min" }, - { "index": 4, "label": "Kein Signal 15 Min" } - ] - }, - "turnOffScreen": { - "label": "Bildschirm ausschalten", - "options": [ - { "index": 0, "label": "Immer an" }, - { "index": 1, "label": "Keine Aktion 30 Sek" }, - { "index": 2, "label": "Keine Aktion 1 Min" }, - { "index": 3, "label": "Keine Aktion 3 Min" }, - { "index": 4, "label": "Keine Aktion 5 Min" } - ] - }, - "knobBrightness": { - "label": "Drehknopfhelligkeit", - "options": [ - { "index": 0, "label": "Aus" }, - { "index": 1, "label": "Hell" }, - { "index": 2, "label": "Mittel" }, - { "index": 3, "label": "Dunkel" } - ] - }, - "sleepTime": { - "label": "Ruhezustand", - "options": [ - { "index": 0, "label": "Aus" }, - { "index": 1, "label": "Kein Signal nach 1 Min." }, - { "index": 2, "label": "Kein Signal nach 5 Min." }, - { "index": 3, "label": "Kein Signal nach 10 Min." } - ] - }, - "buttonLight": { - "label": "Drehknopf-Standby-Atemlicht", - "options": [ - { "index": 0, "label": "Ein" }, - { "index": 1, "label": "Aus" } - ] - }, - "language": { - "label": "Sprache", - "options": [ - { "index": 0, "label": "Englisch" }, - { "index": 1, "label": "Chinesisch (traditionell)" }, - { "index": 2, "label": "Chinesisch (vereinfacht)" }, - { "index": 3, "label": "Deutsch" }, - { "index": 4, "label": "Spanisch" }, - { "index": 5, "label": "Französisch" } - ] - }, - "Automatic": { - "label": "Automatisch zur Startseite zurückkehren", - "options": [ - { "index": 0, "label": "Aus" }, - { "index": 1, "label": "Keine Aktion 20 Sek" }, - { "index": 2, "label": "Keine Aktion 40 Sek" }, - { "index": 3, "label": "Keine Aktion 60 Sek" } - ] - } - }, - "prompt": { - "prompt": "Hinweis", - "NotAdded": "Kopfhörer nicht hinzugefügt. Möchten Sie hinzufügen?", - "confirm": "Bestätigen", - "cancel": "Abbrechen", - "save": "Speichern", - "reset": "Zurücksetzen", - "delete": "Löschen", - "add": "Hinzufügen", - "edit": "Bearbeiten", - "deleteConfirm": "Möchten Sie löschen?", - "saveConfirm": "Speichern bestätigen?", - "HP-EQ": "HP-EQ ist deaktiviert. Aktivieren?", - "Effect": "Effekt ist deaktiviert. Aktivieren?", - "EnableEffectY": "Bestätigen", - "EnableEffectN": "Abbrechen", - "noDelete": "Dieser Eintrag kann nicht gelöscht werden", - "SavePreset": "Neue Voreinstellung speichern", - "default": "Standard", - "invalidData": "Ungültige Daten", - "range": "Bereich", - "selectedYet": "Sie haben keine Zielkurve ausgewählt" - } -} diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json index 22af690..a6fad27 100644 --- a/client/src/locales/data-en.json +++ b/client/src/locales/data-en.json @@ -210,6 +210,7 @@ "Standard": "Standard equalizer preset", "edit": "Batch Edit", "headset": "Add HeadSet", + "share": "Share EQ", "preamp": "Preamp", "filters": { "label": "Filters", @@ -352,7 +353,27 @@ "paramInputInvalid": "Enter a valid number", "paramInputOutOfRangeFreq": "Frequency must be between {{min}} and {{max}} Hz", "paramInputOutOfRangeGain": "Gain must be between {{min}} and {{max}} dB", - "paramInputOutOfRangeQ": "Q must be between {{min}} and {{max}}" + "paramInputOutOfRangeQ": "Q must be between {{min}} and {{max}}", + "shareTitle": "Share EQ", + "shareSelectHint": "Select the EQ preset to share", + "shareConfirm": "Generate Code", + "shareCodeLabel": "Share Code", + "shareCopied": "Code copied", + "shareGenerating": "Generating…", + "importTab": "Import EQ", + "importCodeHint": "Enter or paste the 5-character share code", + "importQuery": "Query", + "importQuerying": "Querying…", + "importCodeNotFound": "Share code not found", + "importEqName": "EQ Name", + "importButton": "Import", + "importSuccess": "EQ imported successfully", + "mySharesTab": "My Shares", + "mySharesLoading": "Loading…", + "mySharesEmpty": "No shared EQ yet", + "mySharesCode": "Code", + "mySharesName": "EQ Name", + "mySharesLoadFail": "Failed to load share list" } }, "dac": { diff --git a/client/src/locales/data-es.json b/client/src/locales/data-es.json deleted file mode 100644 index c4cfa0d..0000000 --- a/client/src/locales/data-es.json +++ /dev/null @@ -1,274 +0,0 @@ -{ - "home": { "audioSet": "Configuración de audio" }, - "source": { - "label": "Entrada/Salida", - "inputOptions": ["USB-B", "USB-C", "Coaxial", "Óptico", "Bluetooth", "HDMI-EARC", "RCA", "Controlador USB"], - "input": { - "coaxial": "Coaxial", - "optical": "Óptico", - "bluetooth": "Bluetooth", - "rca": "RCA analógico" - }, - "output": { "headset": "Auriculares" } - }, - "effect": { - "lowBass": "Bajo", - "enterBass": "Medio", - "highBass": "Agudo", - "tone": "Tono", - "loudness": "Sonoridad", - "threshold": "Umbral", - "delayed": "Retardo", - "mainframeCase": "Caja principal", - "subwooferUnit": "Subwoofer", - "style": { - "label": "Estilo", - "options": [ - { "index": 0, "label": "Clásico" }, - { "index": 1, "label": "Dance" }, - { "index": 2, "label": "Pop" }, - { "index": 3, "label": "Reggae" }, - { "index": 4, "label": "En vivo" }, - { "index": 5, "label": "Rock" }, - { "index": 6, "label": "Suave" }, - { "index": 7, "label": "Electrónica" }, - { "index": 8, "label": "Club" }, - { "index": 9, "label": "Todo Bajo" }, - { "index": 10, "label": "Todo Agudo" }, - { "index": 11, "label": "Auriculares" }, - { "index": 12, "label": "Salón" }, - { "index": 13, "label": "Fusión" }, - { "index": 14, "label": "Ska" }, - { "index": 15, "label": "Slow Shake" } - ] - }, - "stereoWidth": { "label": "Ancho del escenario sonoro" }, - "crossfeed": { "label": "Realimentación cruzada" }, - "subwoofer": { - "label": "Salida de subwoofer", - "freq": "Frecuencia de corte", - "attenuation": "Pendiente de atenuación", - "gain": "Realce de bajos", - "output": { - "label": "Modo de salida", - "options": [ - { "label": "Mono", "index": 0 }, - { "label": "Estéreo", "index": 1 } - ] - } - } - }, - "peq": { - "Standard": "Preajuste de ecualizador estándar", - "edit": "Edición en lote", - "headset": "Agregar modelo de auriculares", - "preamp": "Ganancia total", - "filters": { - "label": "Filtros", - "options": [ - { - "index": 0, - "title": "LPF", - "desc1": "Filtro paso bajo, solo permite el paso de", - "desc2": "señales por debajo de cierta frecuencia de corte,", - "desc3": "las señales altas se atenúan.", - "img": "LPF.png" - }, - { - "index": 1, - "title": "HPF", - "desc1": "Filtro paso alto, solo permite el paso de", - "desc2": "señales por encima de cierta frecuencia de corte,", - "desc3": "las señales bajas se atenúan.", - "img": "HPF.png" - }, - { - "index": 2, - "title": "BPF", - "img": "BPF.png", - "desc1": "Filtro paso banda, solo permite señales", - "desc2": "dentro de un cierto rango de frecuencias,", - "desc3": "otras frecuencias se atenúan." - }, - { - "index": 3, - "title": "NOTCH", - "img": "NOTCH.png", - "desc1": "Filtro de muesca, elimina señales de", - "desc2": "frecuencias específicas." - }, - { - "index": 4, - "title": "PEAK", - "img": "PEAK.png", - "desc1": "Filtro de pico, realza o atenúa", - "desc2": "señales cerca de una frecuencia específica." - }, - { - "index": 5, - "title": "LSHELF", - "img": "LSHELF.png", - "desc1": "Filtro de estantería baja, usado para realzar o", - "desc2": "atenuar señales de baja frecuencia." - }, - { - "index": 5, - "title": "HSHELF", - "img": "HSHELF.png", - "desc1": "Filtro de estantería alta, usado para realzar o", - "desc2": "atenuar señales de alta frecuencia." - }, - { - "index": 6, - "title": "APF", - "img": "APF.png", - "desc1": "Filtro de paso total, no cambia la amplitud", - "desc2": "pero sí la fase de la señal." - } - ] - }, - "freq": "FREQ", - "gain": "GANANCIA", - "q": "Valor Q", - "loadDeviceBtn": "Confirmar carga", - "peqNoneItem": "No configurado" - }, - "dac": { - "balance": "Balance izquierdo/derecho", - "filters": { - "label": "Características de filtrado", - "options": [ - { "index": 0, "label": "Descenso rápido" }, - { "index": 1, "label": "Descenso lento" }, - { "index": 2, "label": "Descenso rápido con retardo corto" }, - { "index": 3, "label": "Descenso lento con retardo corto" }, - { "index": 4, "label": "Descenso ultra lento" }, - { "index": 5, "label": "Poca dispersión con retardo corto" } - ] - }, - "headPhoneGain": { - "label": "Ganancia de auriculares", - "options": [ - { "index": 0, "label": "Baja" }, - { "index": 1, "label": "Media" }, - { "index": 2, "label": "Alta" } - ] - }, - "volumeStep": "Incremento de volumen", - "bootVolume": "Volumen de arranque", - "xlr": { - "label": "Polaridad del puerto XLR", - "options": [ - { "index": 0, "label": "Normal" }, - { "index": 1, "label": "Invertido" } - ] - }, - "arc": { - "label": "Modo ARC", - "options": [ - { "index": 0, "label": "ARC" }, - { "index": 1, "label": "EARC" } - ] - } - }, - "settings": { - "vu": "Configuración", - "vuMeter": "Medidor VU", - "screenBrightness": { - "label": "Brillo de pantalla", - "options": [ - { "index": 0, "label": "Brillante" }, - { "index": 1, "label": "Medio" }, - { "index": 2, "label": "Oscuro" } - ] - }, - "sleep": { - "label": "Modo de reposo", - "options": [ - { "index": 0, "label": "Apagado" }, - { "index": 1, "label": "Sin señal 1 min" }, - { "index": 2, "label": "Sin señal 5 min" }, - { "index": 3, "label": "Sin señal 10 min" }, - { "index": 4, "label": "Sin señal 15 min" } - ] - }, - "turnOffScreen": { - "label": "Apagar pantalla", - "options": [ - { "index": 0, "label": "Siempre encendida" }, - { "index": 1, "label": "Sin operación 30 s" }, - { "index": 2, "label": "Sin operación 1 min" }, - { "index": 3, "label": "Sin operación 3 min" }, - { "index": 4, "label": "Sin operación 5 min" } - ] - }, - "knobBrightness": { - "label": "Brillo del mando", - "options": [ - { "index": 0, "label": "Apagado" }, - { "index": 1, "label": "Brillante" }, - { "index": 2, "label": "Medio" }, - { "index": 3, "label": "Oscuro" } - ] - }, - "sleepTime": { - "label": "Reposo", - "options": [ - { "index": 0, "label": "Apagado" }, - { "index": 1, "label": "Sin señal tras 1 min" }, - { "index": 2, "label": "Sin señal tras 5 min" }, - { "index": 3, "label": "Sin señal tras 10 min" } - ] - }, - "buttonLight": { - "label": "Luz de respiración del mando (pantalla apagada)", - "options": [ - { "index": 0, "label": "Encendido" }, - { "index": 1, "label": "Apagado" } - ] - }, - "language": { - "label": "Idioma", - "options": [ - { "index": 0, "label": "Inglés" }, - { "index": 1, "label": "Chino Tradicional" }, - { "index": 2, "label": "Chino Simplificado" }, - { "index": 3, "label": "Alemán" }, - { "index": 4, "label": "Español" }, - { "index": 5, "label": "Francés" } - ] - }, - "Automatic": { - "label": "Regresar automáticamente al inicio", - "options": [ - { "index": 0, "label": "Desactivado" }, - { "index": 1, "label": "Sin operación 20 s" }, - { "index": 2, "label": "Sin operación 40 s" }, - { "index": 3, "label": "Sin operación 60 s" } - ] - } - }, - "prompt": { - "prompt": "Aviso", - "NotAdded": "Auriculares no agregados. ¿Desea agregarlos?", - "confirm": "Confirmar", - "cancel": "Cancelar", - "save": "Guardar", - "reset": "Restablecer", - "delete": "Eliminar", - "add": "Agregar", - "edit": "Editar", - "deleteConfirm": "¿Desea eliminar?", - "saveConfirm": "¿Confirmar guardado?", - "HP-EQ": "HP-EQ no está activado. ¿Desea activarlo?", - "Effect": "Effect no está activado. ¿Desea activarlo?", - "EnableEffectY": "Sí", - "EnableEffectN": "No", - "noDelete": "Este elemento no puede eliminarse", - "SavePreset": "Nuevo preajuste", - "default": "Predeterminado", - "invalidData": "Datos no válidos", - "range": "Rango", - "selectedYet": "Aún no has seleccionado una curva objetivo" - } -} diff --git a/client/src/locales/data-fr.json b/client/src/locales/data-fr.json deleted file mode 100644 index e6bea05..0000000 --- a/client/src/locales/data-fr.json +++ /dev/null @@ -1,274 +0,0 @@ -{ - "home": { "audioSet": "Paramètres audio" }, - "source": { - "label": "Entrée/Sortie", - "inputOptions": ["USB-B", "USB-C", "Coaxial", "Optique", "Bluetooth", "HDMI-EARC", "RCA", "Clé USB"], - "input": { - "coaxial": "Coaxial", - "optical": "Optique", - "bluetooth": "Bluetooth", - "rca": "RCA analogique" - }, - "output": { "headset": "Casque" } - }, - "effect": { - "lowBass": "Basses", - "enterBass": "Médiums", - "highBass": "Aigus", - "tone": "Ton", - "delayed": "Retard", - "loudness": "Sonie", - "threshold": "Seuil", - "mainframeCase": "Châssis principal", - "subwooferUnit": "Caisson de basses", - "style": { - "label": "Style", - "options": [ - { "index": 0, "label": "Classique" }, - { "index": 1, "label": "Dance" }, - { "index": 2, "label": "Pop" }, - { "index": 3, "label": "Reggae" }, - { "index": 4, "label": "Live" }, - { "index": 5, "label": "Rock" }, - { "index": 6, "label": "Doux" }, - { "index": 7, "label": "Électronique" }, - { "index": 8, "label": "Club" }, - { "index": 9, "label": "Tout basses" }, - { "index": 10, "label": "Tout aigus" }, - { "index": 11, "label": "Casque" }, - { "index": 12, "label": "Hall" }, - { "index": 13, "label": "Aggrégé" }, - { "index": 14, "label": "Ska" }, - { "index": 15, "label": "Slow" } - ] - }, - "stereoWidth": { "label": "Largeur stéréo" }, - "crossfeed": { "label": "Crossfeed" }, - "subwoofer": { - "label": "Sortie caisson de basses", - "freq": "Fréquence de coupure", - "attenuation": "Pente d'atténuation", - "gain": "Renforcement basses", - "output": { - "label": "Mode de sortie", - "options": [ - { "label": "Mono", "index": 0 }, - { "label": "Stéréo", "index": 1 } - ] - } - } - }, - "peq": { - "Standard": "Préréglages EQ standard", - "edit": "Édition groupée", - "headset": "Ajouter modèle casque", - "preamp": "Gain global", - "filters": { - "label": "Filtres", - "options": [ - { - "index": 0, - "title": "LPF", - "desc1": "Filtre passe-bas, ne laisse passer que", - "desc2": "les signaux en dessous d'une certaine", - "desc3": "fréquence de coupure, atténue les aigus.", - "img": "LPF.png" - }, - { - "index": 1, - "title": "HPF", - "desc1": "Filtre passe-haut, ne laisse passer que", - "desc2": "les signaux au-dessus d'une certaine", - "desc3": "fréquence de coupure, atténue les basses.", - "img": "HPF.png" - }, - { - "index": 2, - "title": "BPF", - "img": "BPF.png", - "desc1": "Filtre passe-bande, ne laisse passer", - "desc2": "qu'une plage de fréquences spécifique,", - "desc3": "atténue les autres fréquences." - }, - { - "index": 3, - "title": "NOTCH", - "img": "NOTCH.png", - "desc1": "Filtre coupe-bande, élimine les", - "desc2": "résonances ou interférences spécifiques." - }, - { - "index": 4, - "title": "PEAK", - "img": "PEAK.png", - "desc1": "Filtre peak, amplifie ou atténue", - "desc2": "les signaux autour d'une fréquence." - }, - { - "index": 5, - "title": "LSHELF", - "img": "LSHELF.png", - "desc1": "Étage bas, renforce ou atténue", - "desc2": "les fréquences basses." - }, - { - "index": 5, - "title": "HSHELF", - "img": "HSHELF.png", - "desc1": "Étage haut, renforce ou atténue", - "desc2": "les fréquences hautes." - }, - { - "index": 6, - "title": "APF", - "img": "APF.png", - "desc1": "Filtre passe-tout, conserve l'amplitude", - "desc2": "mais modifie la phase du signal." - } - ] - }, - "freq": "FREQ", - "gain": "GAIN", - "q": "Facteur Q", - "loadDeviceBtn": "Charger", - "peqNoneItem": "Non configuré" - }, - "dac": { - "balance": "Balance L/R", - "filters": { - "label": "Caractéristiques filtre", - "options": [ - { "index": 0, "label": "Déclin rapide" }, - { "index": 1, "label": "Déclin lent" }, - { "index": 2, "label": "Déclin rapide court délai" }, - { "index": 3, "label": "Déclin lent court délai" }, - { "index": 4, "label": "Déclin très lent" }, - { "index": 5, "label": "Court délai faible dispersion" } - ] - }, - "headPhoneGain": { - "label": "Gain casque", - "options": [ - { "index": 0, "label": "Faible" }, - { "index": 1, "label": "Moyen" }, - { "index": 2, "label": "Élevé" } - ] - }, - "volumeStep": "Pas volume", - "bootVolume": "Volume démarrage", - "xlr": { - "label": "Polarité XLR", - "options": [ - { "index": 0, "label": "Normal" }, - { "index": 1, "label": "Inversé" } - ] - }, - "arc": { - "label": "Mode ARC", - "options": [ - { "index": 0, "label": "ARC" }, - { "index": 1, "label": "eARC" } - ] - } - }, - "settings": { - "vu": "Paramètres", - "vuMeter": "VU-mètre", - "screenBrightness": { - "label": "Luminosité écran", - "options": [ - { "index": 0, "label": "Élevée" }, - { "index": 1, "label": "Moyenne" }, - { "index": 2, "label": "Faible" } - ] - }, - "sleep": { - "label": "Veille", - "options": [ - { "index": 0, "label": "Désactivée" }, - { "index": 1, "label": "1 min sans signal" }, - { "index": 2, "label": "5 min sans signal" }, - { "index": 3, "label": "10 min sans signal" }, - { "index": 4, "label": "15 min sans signal" } - ] - }, - "turnOffScreen": { - "label": "Éteindre écran", - "options": [ - { "index": 0, "label": "Toujours allumé" }, - { "index": 1, "label": "30s inactivité" }, - { "index": 2, "label": "1 min inactivité" }, - { "index": 3, "label": "3 min inactivité" }, - { "index": 4, "label": "5 min inactivité" } - ] - }, - "knobBrightness": { - "label": "Luminosité bouton", - "options": [ - { "index": 0, "label": "Désactivée" }, - { "index": 1, "label": "Élevée" }, - { "index": 2, "label": "Moyenne" }, - { "index": 3, "label": "Faible" } - ] - }, - "sleepTime": { - "label": "Veille", - "options": [ - { "index": 0, "label": "Désactivé" }, - { "index": 1, "label": "Aucun signal après 1 min" }, - { "index": 2, "label": "Aucun signal après 5 min" }, - { "index": 3, "label": "Aucun signal après 10 min" } - ] - }, - "buttonLight": { - "label": "Lumière respiratoire du bouton (écran éteint)", - "options": [ - { "index": 0, "label": "Activé" }, - { "index": 1, "label": "Désactivé" } - ] - }, - "language": { - "label": "Langue", - "options": [ - { "index": 0, "label": "Anglais" }, - { "index": 1, "label": "Chinois (Traditionnel)" }, - { "index": 2, "label": "Chinois (Simplifié)" }, - { "index": 3, "label": "Allemand" }, - { "index": 4, "label": "Espagnol" }, - { "index": 5, "label": "Français" } - ] - }, - "Automatic": { - "label": "Retour auto. accueil", - "options": [ - { "index": 0, "label": "Désactivé" }, - { "index": 1, "label": "20s inactivité" }, - { "index": 2, "label": "40s inactivité" }, - { "index": 3, "label": "60s inactivité" } - ] - } - }, - "prompt": { - "prompt": "Avertissement", - "NotAdded": "Aucun casque ajouté. Ajouter un casque ?", - "confirm": "Confirmer", - "cancel": "Annuler", - "save": "Enregistrer", - "reset": "Réinitialiser", - "delete": "Supprimer", - "add": "Ajouter", - "edit": "Éditer", - "deleteConfirm": "Confirmer la suppression ?", - "saveConfirm": "Confirmer l'enregistrement ?", - "HP-EQ": "HP-EQ désactivé, activer ?", - "Effect": "Effet désactivé, activer ?", - "EnableEffectY": "Confirmer", - "EnableEffectN": "Annuler", - "noDelete": "Impossible de supprimer", - "SavePreset": "Nouveau préréglage", - "default": "Par défaut", - "invalidData": "Données invalides", - "range": "Plage", - "selectedYet": "Aucune courbe cible sélectionnée" - } -} diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json index f82657a..2775358 100644 --- a/client/src/locales/data-zh-HK.json +++ b/client/src/locales/data-zh-HK.json @@ -146,6 +146,7 @@ "Standard": "標準等化器預設", "edit": "批次編輯", "headset": "新增耳機型號", + "share": "分享 EQ", "preamp": "總增益", "filters": { "label": "濾波器", @@ -288,7 +289,27 @@ "paramInputInvalid": "請輸入有效數值", "paramInputOutOfRangeFreq": "頻率需在 {{min}} – {{max}} Hz 之間", "paramInputOutOfRangeGain": "增益需在 {{min}} – {{max}} dB 之間", - "paramInputOutOfRangeQ": "Q 值需在 {{min}} – {{max}} 之間" + "paramInputOutOfRangeQ": "Q 值需在 {{min}} – {{max}} 之間", + "shareTitle": "分享 EQ", + "shareSelectHint": "選擇要分享的 EQ 預設", + "shareConfirm": "產生分享碼", + "shareCodeLabel": "分享碼", + "shareCopied": "已複製分享碼", + "shareGenerating": "產生中…", + "importTab": "匯入 EQ", + "importCodeHint": "輸入或貼上 5 位分享碼", + "importQuery": "查詢", + "importQuerying": "查詢中…", + "importCodeNotFound": "分享碼不存在", + "importEqName": "EQ 名稱", + "importButton": "匯入", + "importSuccess": "EQ 匯入成功", + "mySharesTab": "我的分享", + "mySharesLoading": "載入中…", + "mySharesEmpty": "暫無分享記錄", + "mySharesCode": "分享碼", + "mySharesName": "EQ 名稱", + "mySharesLoadFail": "載入分享列表失敗" } }, "dac": { diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json index 9c47221..0bd08df 100644 --- a/client/src/locales/data-zh.json +++ b/client/src/locales/data-zh.json @@ -146,6 +146,7 @@ "Standard": "标准均衡器预设", "edit": "批量编辑", "headset": "添加耳机型号", + "share": "分享 EQ", "preamp": "总增益", "filters": { "label": "滤波器", @@ -288,7 +289,27 @@ "paramInputInvalid": "请输入有效数值", "paramInputOutOfRangeFreq": "频率需在 {{min}} – {{max}} Hz 之间", "paramInputOutOfRangeGain": "增益需在 {{min}} – {{max}} dB 之间", - "paramInputOutOfRangeQ": "Q 值需在 {{min}} – {{max}} 之间" + "paramInputOutOfRangeQ": "Q 值需在 {{min}} – {{max}} 之间", + "shareTitle": "分享 EQ", + "shareSelectHint": "选择要分享的 EQ 预设", + "shareConfirm": "生成分享码", + "shareCodeLabel": "分享码", + "shareCopied": "已复制分享码", + "shareGenerating": "生成中…", + "importTab": "导入 EQ", + "importCodeHint": "输入或粘贴 5 位分享码", + "importQuery": "查询", + "importQuerying": "查询中…", + "importCodeNotFound": "分享码不存在", + "importEqName": "EQ 名称", + "importButton": "导入", + "importSuccess": "EQ 导入成功", + "mySharesTab": "我的分享", + "mySharesLoading": "加载中…", + "mySharesEmpty": "暂无分享记录", + "mySharesCode": "分享码", + "mySharesName": "EQ 名称", + "mySharesLoadFail": "加载分享列表失败" } }, "dac": { diff --git a/client/src/pages/EQPage.tsx b/client/src/pages/EQPage.tsx index 175bf46..95e47b2 100644 --- a/client/src/pages/EQPage.tsx +++ b/client/src/pages/EQPage.tsx @@ -11,7 +11,7 @@ 7. Total gain card: 总增益 value + AUTO toggle + slider ============================================================ */ import { useDevice } from "@/contexts/DeviceContext"; -import { ChevronLeft, ChevronDown, Minus, Plus, Edit3, Headphones, Search, X } from "lucide-react"; +import { ChevronLeft, ChevronDown, Minus, Plus, Edit3, Headphones, Search, X, Share2, Copy, Loader2 } from "lucide-react"; import { useLocation } from "wouter"; import { useState, useMemo, useEffect, useRef, useCallback } from "react"; import { cn } from "@/lib/utils"; @@ -568,6 +568,7 @@ export default function EQPage() { name: string; brand?: string; model?: string; + form?: string; filters?: any[] | string; autoPre?: number; preamp?: number; @@ -597,6 +598,16 @@ export default function EQPage() { type BrandDrawerTab = "brands" | "models" | "target"; const [isBrandDrawerOpen, setIsBrandDrawerOpen] = useState(false); const [brandDrawerTab, setBrandDrawerTab] = useState("brands"); + const [isShareDialogOpen, setIsShareDialogOpen] = useState(false); + const [shareDialogTab, setShareDialogTab] = useState<"share" | "import" | "myShares">("share"); + const [shareSelectedIdx, setShareSelectedIdx] = useState(headphoneIdx); + const [shareCode, setShareCode] = useState(null); + const [shareGenerating, setShareGenerating] = useState(false); + const [importCodeInputs, setImportCodeInputs] = useState(["", "", "", "", ""]); + const [importQuerying, setImportQuerying] = useState(false); + const [importedEqData, setImportedEqData] = useState<{ name: string; brand?: string; model?: string; form?: string; filters?: any[] | string; preamp?: number } | null>(null); + const [mySharesList, setMySharesList] = useState>([]); + const [mySharesLoading, setMySharesLoading] = useState(false); const [brandSearchQuery, setBrandSearchQuery] = useState(""); const [catalogBrands, setCatalogBrands] = useState([]); const [catalogBrandsLoading, setCatalogBrandsLoading] = useState(false); @@ -1894,8 +1905,8 @@ export default function EQPage() { return updateSetting(bypass ? { peqEnable: 1, dsp_enable: 1 } : { peqEnable: 1 }); }} > - {/* ── Action cards: 批量编辑 / 添加耳机型号 ── */} -
+ {/* ── Action cards: 批量编辑 / 添加耳机型号 / 分享EQ ── */} +
{peqCardLabels.headset} +
{/* ── Headphone model selector ── */} @@ -2786,6 +2817,492 @@ export default function EQPage() { )} + {/* ── Share / Import EQ Dialog ── */} + {isShareDialogOpen && ( +
setIsShareDialogOpen(false)} + > +
e.stopPropagation()} + > + {/* header */} +
+ {/* tab bar */} +
+ + + +
+ +
+ + {/* ── Tab: Share EQ ── */} + {shareDialogTab === "share" && ( + <> + {/* hint */} +

{eqUi.shareSelectHint}

+ + {/* EQ preset list */} +
+ {peqItems.length === 0 && ( +
+ )} + {peqItems.map((item, idx) => { + const active = idx === shareSelectedIdx; + return ( + + ); + })} +
+ + {/* confirm button */} + + + {/* share code display */} + {shareCode && ( +
+

{eqUi.shareCodeLabel}

+
+
+ {shareCode.split("").map((ch, i) => ( +
+ {ch} +
+ ))} +
+ +
+
+ )} + + )} + + {/* ── Tab: Import EQ ── */} + {shareDialogTab === "import" && ( + <> + {/* hint */} +

{eqUi.importCodeHint}

+ + {/* 5 input boxes */} +
+ {importCodeInputs.map((ch, i) => ( + { + const val = e.target.value.replace(/[^a-zA-Z0-9]/g, "").slice(-1).toUpperCase(); + const next = [...importCodeInputs]; + next[i] = val; + setImportCodeInputs(next); + setImportedEqData(null); + // auto-focus next box + if (val && i < 4) { + const el = e.target.nextElementSibling as HTMLInputElement | null; + el?.focus(); + } + }} + onKeyDown={(e) => { + // backspace: clear current & focus previous + if (e.key === "Backspace" && !importCodeInputs[i] && i > 0) { + const next = [...importCodeInputs]; + next[i - 1] = ""; + setImportCodeInputs(next); + setImportedEqData(null); + const prev = (e.target as HTMLElement).previousElementSibling as HTMLInputElement | null; + prev?.focus(); + } + }} + onPaste={(e) => { + e.preventDefault(); + const text = (e.clipboardData.getData("text") || "").replace(/[^a-zA-Z0-9]/g, "").toUpperCase().slice(0, 5); + if (!text) return; + const next = [...importCodeInputs]; + for (let j = 0; j < 5; j++) { + next[j] = text[j] ?? ""; + } + setImportCodeInputs(next); + setImportedEqData(null); + // focus last filled or last box + const focusIdx = Math.min(text.length, 4); + const inputs = (e.target as HTMLElement).parentElement?.querySelectorAll("input"); + (inputs?.[focusIdx] as HTMLInputElement | undefined)?.focus(); + }} + ref={(el) => { + // stash ref for focus management if needed later + }} + /> + ))} +
+ + {/* query button */} + + + {/* imported EQ result */} + {importedEqData && ( +
+
+
+

{eqUi.importEqName}

+

{importedEqData.name}

+
+ +
+
+ )} + + )} + + {/* ── Tab: My Shares ── */} + {shareDialogTab === "myShares" && ( + <> + {mySharesLoading && mySharesList.length === 0 ? ( +
+ + {eqUi.mySharesLoading} +
+ ) : mySharesList.length === 0 ? ( +
{eqUi.mySharesEmpty}
+ ) : ( +
+ {mySharesList.map((item, idx) => ( +
+ {/* share code */} +
+ {item.code.split("").map((ch, ci) => ( + + {ch} + + ))} +
+ {/* EQ name */} +
{item.name}
+ {/* copy code button */} + +
+ ))} +
+ )} + + )} +
+
+ )} + );