diff --git a/client/public/changelog.md b/client/public/changelog.md index e432792..a887894 100644 --- a/client/public/changelog.md +++ b/client/public/changelog.md @@ -2,6 +2,13 @@ > This document records feature updates and bug fixes for the Luxsin X8 controller. Entries are listed in reverse chronological order — the newest release appears first. +## v2026.11.03 + +### New Features +- EQ frequency-response chart: the Y-axis (gain) range is now adjustable. Use the segmented control beside the legend to switch between ±6 / ±12 / ±15 / ±20 dB; your choice is remembered next time. +- EQ filter type: added "BYPASS" as the first option in the filter-type menu. Selecting it bypasses the band and locks its FREQ / GAIN / Q controls, while the underlying filter values stay unchanged. +- Added more interface languages, including Russian and Japanese. These languages apply to the app UI only and are not sent to the device. + ## v2026.09.22 ### New Features diff --git a/client/src/lib/peqAudio.ts b/client/src/lib/peqAudio.ts index 26c2c7e..e866b83 100644 --- a/client/src/lib/peqAudio.ts +++ b/client/src/lib/peqAudio.ts @@ -12,6 +12,11 @@ export const TYPE_HIGHPASS = 1; export const TYPE_BANDPASS = 2; export const TYPE_NOTCH = 3; export const TYPE_ALLPASS = 7; +/** + * 旁通标记:设备协议用 type + 10 表示该滤波器被 bypass(见 getSectionsMatrix)。 + * UI 固定下发 10,系数落入 default 分支→unity(0 dB 直通)。 + */ +export const TYPE_BYPASS = 10; // Filter type mapping (device uses numeric type; pass-through when already a number) export function getFilterType(typeName: string | number): number { @@ -340,6 +345,8 @@ export type PeqBandForResponse = { freq: number; q: number; type: string | number; + /** 旁通:该带不参与级联响应(曲线贡献 0 dB),type 仍保留原滤波器类型。 */ + bypassed?: boolean; }; /** @@ -351,7 +358,7 @@ export function computePeqMagnitudeDb(bands: PeqBandForResponse[], fs: number): const list: Coeff[] = []; bands.forEach((b) => { if (!b.enabled) return; - list.push(getSectionsMatrix(b.gain, b.freq, b.q, getFilterType(b.type), false, fs)); + list.push(getSectionsMatrix(b.gain, b.freq, b.q, getFilterType(b.type), !!b.bypassed, fs)); }); if (list.length === 0) { return f.map(() => 0); @@ -372,7 +379,7 @@ export function sampleCombinedPeqMagnitudeDb( const list: Coeff[] = []; bands.forEach((b) => { if (!b.enabled) return; - list.push(getSectionsMatrix(b.gain, b.freq, b.q, getFilterType(b.type), false, fs)); + list.push(getSectionsMatrix(b.gain, b.freq, b.q, getFilterType(b.type), !!b.bypassed, fs)); }); if (list.length === 0) return 0; const out = getFreqznList(list, fs, [fc]); diff --git a/client/src/locales/data-ja.json b/client/src/locales/data-ja.json new file mode 100644 index 0000000..122e22d --- /dev/null +++ b/client/src/locales/data-ja.json @@ -0,0 +1,1068 @@ +{ + "connect": { + "ipMissingTitle": "デバイスのIPが指定されていません", + "ipMissingDesc": "このページを開く前に、アドレスバーにデバイスのIPを追加してください。例:", + "connecting": "{ip} に接続中…", + "failedTitle": "接続に失敗しました", + "failedDesc": "デバイスに接続できません。このページとデバイスが同じネットワーク上にあり、IPが正しいことを確認してください。", + "failedHint": "URLでデバイスを指定してください(例:?ip={ip})" + }, + "home": { + "audioSet": "オーディオ設定", + "systemSet": "システム", + "power": "電源", + "input": "入力", + "output": "出力", + "vu": "VUメーター", + "powerOffConfirmTitle": "デバイスの電源を切りますか?", + "powerOffConfirmDesc": "デバイスがシャットダウンし、このアプリとの接続が切断されます。続行しますか?", + "powerOffConfirmOk": "電源を切る", + "powerOffConfirmCancel": "キャンセル", + "volumeMinConfirmTitle": "音量を最小にしますか?", + "volumeMinConfirmDesc": "音量が最小(ミュート)に設定されます。続行しますか?", + "volumeMinConfirmOk": "最小にする", + "volumeMaxConfirmTitle": "音量を最大にしますか?", + "volumeMaxConfirmDesc": "音量が最大に設定されます。続行しますか?", + "volumeMaxConfirmOk": "最大にする", + "volumePassthroughLockedToast": "音量パススルーモードのため、音量を調整できません。", + "volumePassthroughLockedHint": "音量パススルーモードのため、音量を調整できません。", + "hpMaxVolumeHint": "ヘッドホンの最大音量が設定されています。最大:{db}dB", + "powerOffToastDemo": "デモモード:シャットダウンコマンドを送信しました。", + "powerOffToastSent": "シャットダウンコマンドを送信しました。", + "doubleClickHint": "ダブルクリックで開く", + "changelog": "更新情報", + "changelogLoadFailed": "読み込みに失敗しました。しばらくしてから再試行してください" + }, + "vuPage": { + "title": "VUメーターギャラリー" + }, + "bottomNav": { + "community": "コミュニティ", + "hpEq": "HP-EQ", + "home": "ホーム", + "effects": "エフェクト", + "myAi": "マイAI" + }, + "communityPage": { + "title": "コミュニティ", + "intro": "フォーラムはシステムのブラウザで開くことをおすすめします。下のリンクをコピーし、スマートフォンのブラウザのアドレスバーに貼り付けて開いてください。", + "copyLink": "リンクをコピー", + "toastCopied": "リンクをコピーしました", + "toastCopyFailed": "コピーできませんでした。リンクを長押しして手動でコピーしてください。" + }, + "source": { + "label": "入力/出力", + "pageTitle": "入出力", + "inputSection": "入力", + "outputSection": "出力", + "inputOptions": [ + "USB-B", + "USB-C", + "同軸", + "光", + "Bluetooth", + "IIS" + ], + "outputOptions": [ + "XLR", + "RCA", + "ヘッドホン", + "XLR/RCA" + ], + "input": { + "coaxial": "同軸", + "optical": "光", + "bluetooth": "BLUETOOTH", + "rca": "RCA" + }, + "output": { + "headset": "ヘッドホン" + } + }, + "effect": { + "pageTitle": "エフェクト", + "selectStyleTitle": "サウンドスタイルを選択", + "selectCrossfeedTitle": "クロスフィードプリセットを選択", + "selectHearingTitle": "聴覚補正プロファイルを選択", + "lowBass": "低音", + "enterBass": "中音", + "highBass": "高音", + "tone": "トーン", + "loudness": "ラウドネス", + "threshold": "しきい値", + "delayed": "遅延", + "mainframeCase": "メインスピーカー", + "subwooferUnit": "サブウーファー", + "style": { + "label": "スタイル", + "options": [ + { + "index": 0, + "label": "クラシック" + }, + { + "index": 1, + "label": "ダンス" + }, + { + "index": 2, + "label": "ポップ" + }, + { + "index": 3, + "label": "レゲエ" + }, + { + "index": 4, + "label": "ライブ" + }, + { + "index": 5, + "label": "ロック" + }, + { + "index": 6, + "label": "ソフト" + }, + { + "index": 7, + "label": "エレクトロニック" + }, + { + "index": 8, + "label": "クラブ" + }, + { + "index": 9, + "label": "フルベース" + }, + { + "index": 10, + "label": "フルトレブル" + }, + { + "index": 11, + "label": "ヘッドホン" + }, + { + "index": 12, + "label": "ホール" + }, + { + "index": 13, + "label": "パーティー" + }, + { + "index": 14, + "label": "スカ" + }, + { + "index": 15, + "label": "スローロック" + } + ] + }, + "stereoWidth": { + "label": "ステレオ幅" + }, + "crossfeed": { + "label": "クロスフィード", + "options": [ + "BS2B デフォルト (700 Hz, 4.5 dB)", + "BS2B ポップ (700 Hz, 6 dB)", + "BS2B リラックス (650 Hz, 9.5 dB)", + "カスタム" + ], + "customTitle": "カスタム設定", + "freqLabel": "周波数", + "attenuationLabel": "減衰量" + }, + "hearing": { + "label": "聴覚補正", + "emptyProfile": "利用可能なプロファイルがありません", + "left": "左", + "right": "右" + }, + "subwoofer": { + "label": "サブウーファー", + "freq": "カットオフ周波数", + "attenuation": "減衰スロープ", + "gain": "ゲイン", + "output": { + "label": "出力方式", + "options": [ + { + "label": "モノラル (ミックス)", + "index": 0 + }, + { + "label": "ステレオ", + "index": 1 + } + ] + } + }, + "enableConfirmTitle": "エフェクトがオフです", + "enableConfirmDesc": "エフェクトは現在無効です。有効にしますか?", + "enableConfirmOk": "有効にする", + "enableConfirmCancel": "キャンセル" + }, + "peq": { + "Standard": "標準イコライザープリセット", + "edit": "一括編集", + "headset": "ヘッドホンを追加", + "preamp": "プリアンプ", + "filters": { + "label": "フィルター", + "options": [ + { + "index": 0, + "title": "LPF", + "desc1": "ローパスフィルター。特定のカットオフ周波数より", + "desc2": "低い信号のみを通過させ、", + "desc3": "高域の信号を減衰させます。", + "img": "LPF.png" + }, + { + "index": 1, + "title": "HPF", + "desc1": "ハイパスフィルター。特定のカットオフ周波数より", + "desc2": "高い信号のみを通過させ、", + "desc3": "低域の信号を減衰させます。", + "img": "HPF.png" + }, + { + "index": 2, + "title": "BPF", + "img": "BPF.png", + "desc1": "バンドパスフィルター。特定の", + "desc2": "周波数範囲の信号のみを通過させ、", + "desc3": "それ以外の周波数を減衰させます。" + }, + { + "index": 3, + "title": "NOTCH", + "img": "NOTCH.png", + "desc1": "ノッチフィルター。特定周波数の共振や", + "desc2": "干渉信号を除去します。" + }, + { + "index": 4, + "title": "PEAK", + "img": "PEAK.png", + "desc1": "ピークフィルター。特定の周波数付近の", + "desc2": "信号を増強または減衰させます。" + }, + { + "index": 5, + "title": "LSHELF", + "img": "LSHELF.png", + "desc1": "ローシェルフフィルター。", + "desc2": "低域の信号を増強または減衰させます。" + }, + { + "index": 5, + "title": "HSHELF", + "img": "HSHELF.png", + "desc1": "ハイシェルフフィルター。", + "desc2": "高域の信号を増強または減衰させます。" + }, + { + "index": 6, + "title": "APF", + "img": "APF.png", + "desc1": "オールパスフィルター。出力信号の", + "desc2": "振幅は変わらず、位相のみが変化します。" + } + ] + }, + "freq": "周波数", + "gain": "ゲイン", + "q": "Q値", + "loadDeviceBtn": "確定", + "peqNoneItem": "未設定", + "eqUi": { + "batchEditTitle": "一括編集", + "batchEditHint": "全11行:1行目がPreamp、2〜11行目がFilter 1〜10です。保存すると現在のヘッドホンプリセットが上書きされます。", + "addPresetTitle": "新規プリセット", + "addPresetNameEmpty": "名前を入力してください", + "addPresetNameExists": "その名前は既に存在します。別の名前を選んでください。", + "addPresetNameTooLong": "名前は80文字以内にしてください", + "toastAddPresetOk": "プリセットを追加しました", + "toastAddPresetFail": "プリセットの追加に失敗しました", + "toastPresetLimitReached": "プリセットの上限({{max}}件)に達しました。追加する前に一部を削除してください。", + "cancel": "キャンセル", + "save": "保存", + "tabBrands": "ブランド", + "tabModels": "モデル", + "tabTarget": "ターゲット", + "searchPlaceholder": "ブランドまたはモデルを検索", + "searching": "検索中…", + "noSearchResults": "該当するブランドまたはモデルがありません", + "loading": "読み込み中…", + "noBrandMatch": "該当するブランドがありません", + "selectBrandFirst": "先に「ブランド」タブでブランドを選択してください", + "brandLabel": "ブランド:", + "modelFromSearchLabel": "検索したモデル:", + "noModelsForBrand": "このブランドのモデルはありません", + "targetBrand": "ブランド:", + "targetModel": "モデル:", + "targetForm": "形状:", + "formAll": "すべて", + "confirmLoading": "処理中…", + "confirmButton": "確認", + "selectModelFirst": "先にモデルを選択してください", + "noTargets": "利用可能なターゲットカーブがありません", + "closeDrawer": "閉じる", + "toastBatchApplied": "一括編集を適用しました", + "toastBatchParseError": "一括編集の形式が正しくありません", + "undo": "元に戻す", + "toastUndo": "元に戻しました:{{detail}}", + "toastUndoEmpty": "元に戻しました", + "undoBandParam": "{{mode}} バンド{{band}} {{param}} {{from}} → {{to}}", + "undoSimpleParam": "{{param}} {{from}} → {{to}}", + "undoMultiple": "{{count}}件の変更({{summary}})", + "undoMoreSuffix": "(他{{count}}件)", + "undoDetailSep": ";", + "undoParamFreq": "周波数", + "undoParamGain": "ゲイン", + "undoParamQ": "Q", + "undoParamType": "フィルター", + "undoParamPreamp": "プリアンプ", + "undoParamAuto": "自動", + "undoParamAbMode": "A/B", + "undoAutoOn": "オン", + "undoAutoOff": "オフ", + "toastBrandsFail": "ブランドの読み込みに失敗しました", + "toastModelsFail": "モデルの読み込みに失敗しました", + "toastCurveFail": "カーブの読み込みに失敗しました", + "toastNewEqOk": "新しいヘッドホンEQを保存しました", + "toastTargetSelected": "ターゲットを選択しました:{{name}}", + "deleteConfirm": "ヘッドホン「{{name}}」を削除しますか?", + "batchDeleteConfirm": "選択した{{count}}件のプリセットを削除しますか?", + "deletePresetConfirm": "プリセット「{{name}}」を削除しますか?", + "managePresetTitle": "EQプリセットの管理", + "selectAll": "すべて選択", + "batchDelete": "選択項目を削除", + "noPresets": "プリセットがありません", + "renamePreset": "名前を変更", + "movePresetUp": "上へ移動", + "movePresetDown": "下へ移動", + "confirmOrder": "順序を確定", + "moveToFirst": "先頭へ移動", + "moveToLast": "末尾へ移動", + "currentPreset": "現在", + "toastOrderSaved": "順序を保存しました", + "toastOrderFail": "順序の保存に失敗しました", + "deletePreset": "削除", + "toastRenamed": "名前を変更しました", + "toastRenameFail": "名前の変更に失敗しました", + "renameEmptyName": "名前を入力してください", + "renameNameExists": "名前は既に存在します", + "toastDeleted": "ヘッドホンを削除しました", + "toastDeleteFail": "ヘッドホンの削除に失敗しました", + "bassBoost": "低音ブースト:fc {{fc}}、q {{q}}、gain {{gain}} dB", + "errMinLines": "11行以上が必要です(Preamp + フィルター10個)", + "errPreampFirstLine": "1行目は Preamp:-5.96dB の形式にしてください", + "errPreampValue": "Preampの値が無効です", + "errFilterLine": "{{line}}行目がFilterの形式と一致しません", + "errFilterNoRange": "Filter番号 {{no}} が範囲外です", + "errFilterType": "Filter {{no}} のタイプが無効です({{type}})", + "errFilterNumbers": "Filter {{no}} の数値が無効です", + "errFilterAll": "Filter 1〜10 をすべて指定してください", + "chartCopyTo": "{{mode}}にコピー", + "chartApplyB": "Bを適用", + "chartSaveB": "Bを別名保存", + "saveBTitle": "Bを別名保存", + "toastChartCopied": "{{mode}}にコピーしました", + "toastApplyBSuccess": "カーブBをAに適用しました", + "toastApplyBFail": "Bの適用に失敗しました", + "toastSaveBOk": "カーブBからプリセットを保存しました", + "toastSaveBFail": "カーブBの保存に失敗しました", + "enableConfirmTitle": "HP-EQがオフです", + "enableConfirmDesc": "HP-EQは現在無効です。有効にしますか?", + "enableConfirmOk": "有効にする", + "paramInputTitleFreq": "周波数を設定", + "paramInputTitleGain": "ゲインを設定", + "paramInputTitleQ": "Q値を設定", + "paramInputHintFreq": "範囲:20 – 20000 Hz。kHzは k で入力できます(例:9.5k)。", + "paramInputHintGain": "範囲:-15.0 – +15.0 dB", + "paramInputHintQ": "範囲:0.10 – 10.00", + "paramInputInvalid": "有効な数値を入力してください", + "paramInputOutOfRangeFreq": "周波数は {{min}}〜{{max}} Hz の範囲で入力してください", + "paramInputOutOfRangeGain": "ゲインは {{min}}〜{{max}} dB の範囲で入力してください", + "paramInputOutOfRangeQ": "Q値は {{min}}〜{{max}} の範囲で入力してください", + "overwritePresetTitle": "プリセット名は既に存在します", + "overwritePresetDesc": "プリセット「{{name}}」は既に存在します。上書きしますか?", + "overwritePresetConfirm": "上書き", + "overwritePresetCancelled": "キャンセルしました", + "shareTitle": "EQを共有", + "shareSelectHint": "共有するEQプリセットを選択", + "shareConfirm": "コードを生成", + "shareCodeLabel": "共有コード", + "shareCopied": "コードをコピーしました", + "shareGenerating": "生成中…", + "shareCreateFail": "共有コードの生成に失敗しました", + "shareActiveExists": "有効な共有コードが既にあります。しばらくしてから再試行してください。", + "shareInvalidParams": "リクエストが無効です。確認して再試行してください。", + "shareSystemError": "システムエラーです。しばらくしてから再試行してください。", + "shareExpireAt": "有効期限", + "importTab": "EQをインポート", + "importCodeHint": "5文字の共有コードを入力または貼り付け", + "importQuery": "照会", + "importQuerying": "照会中…", + "importCodeNotFound": "共有コードが見つからないか、期限切れです", + "importEqName": "EQ名", + "importEqNameHint": "名前は自由に変更できます", + "importSourceModel": "共有元デバイス", + "importButton": "インポート", + "importSaving": "インポート中…", + "importSuccess": "EQをインポートしました", + "importFail": "EQのインポートに失敗しました", + "mySharesTab": "共有履歴", + "mySharesLoading": "読み込み中…", + "mySharesEmpty": "共有したEQはまだありません", + "mySharesCode": "コード", + "mySharesName": "EQ名", + "mySharesLoadFail": "共有リストの読み込みに失敗しました", + "mySharesDeleteConfirmTitle": "共有コードを削除", + "mySharesDeleteConfirmDesc": "共有コード {{code}} を削除しますか?削除後はこのコードでインポートできなくなります。", + "mySharesDeleteConfirmOk": "削除", + "mySharesDeleteSuccess": "共有コードを削除しました", + "mySharesDeleteFail": "共有コードの削除に失敗しました" + }, + "share": "EQを共有" + }, + "dac": { + "sectionBalanceSensitivity": "バランスと感度", + "sectionOutput": "出力特性", + "sectionHpSettings": "ヘッドホン設定", + "sectionIisSettings": "IIS設定", + "sectionSystemInterface": "システムとインターフェース", + "balance": "左右バランス", + "sensitivity": "VUメーター感度", + "filters": { + "label": "フィルター", + "options": [ + { + "index": 0, + "label": "シャープロールオフ" + }, + { + "index": 1, + "label": "スローロールオフ" + }, + { + "index": 2, + "label": "ショートディレイ・シャープロールオフ" + }, + { + "index": 3, + "label": "ショートディレイ・スローロールオフ" + }, + { + "index": 4, + "label": "ディエンファシスフィルター" + }, + { + "index": 5, + "label": "ノンオーバーサンプリング(NOS)" + } + ] + }, + "dacGain": { + "label": "ヘッドホンゲイン", + "options": [ + { + "index": 0, + "label": "低" + }, + { + "index": 1, + "label": "中" + }, + { + "index": 2, + "label": "高" + } + ] + }, + "autoImpedance": { + "label": "インピーダンス自動検出", + "options": [ + { + "index": 0, + "label": "オフ" + }, + { + "index": 1, + "label": "オン" + } + ] + }, + "dreMode": { + "label": "ダイナミックレンジ拡張", + "options": [ + { + "index": 0, + "label": "オフ" + }, + { + "index": 1, + "label": "オン" + } + ] + }, + "dacVolumeDirect": { + "label": "プリアウト音量パススルー", + "options": [ + { + "index": 0, + "label": "オフ" + }, + { + "index": 1, + "label": "0dB" + }, + { + "index": 2, + "label": "-12dB" + } + ] + }, + "dacVolumeDirectConfirm": { + "title0db": "0 dB パススルーを有効にしますか?", + "desc0db": "パススルーモードでは音量が最大になり、音量調整が無効になります。", + "title12db": "-12 dB パススルーを有効にしますか?", + "desc12db": "パススルーモードでは音量が -12 dB になり、音量調整が無効になります。", + "confirm": "確認", + "cancel": "キャンセル" + }, + "volumeStep": "音量ステップ", + "hpMaxVolume": { + "label": "ヘッドホン最大音量", + "options": [ + { + "index": 0, + "label": "0dB" + }, + { + "index": 1, + "label": "-5dB" + }, + { + "index": 2, + "label": "-10dB" + }, + { + "index": 3, + "label": "-15dB" + }, + { + "index": 4, + "label": "-20dB" + }, + { + "index": 5, + "label": "-25dB" + }, + { + "index": 6, + "label": "-30dB" + }, + { + "index": 7, + "label": "-35dB" + }, + { + "index": 8, + "label": "-40dB" + }, + { + "index": 9, + "label": "-45dB" + }, + { + "index": 10, + "label": "-50dB" + } + ] + }, + "hpStartupVolume": { + "label": "ヘッドホン起動時音量", + "options": [ + { + "index": 0, + "label": "デフォルト" + }, + { + "index": 1, + "label": "-20dB" + }, + { + "index": 2, + "label": "-25dB" + }, + { + "index": 3, + "label": "-30dB" + }, + { + "index": 4, + "label": "-35dB" + }, + { + "index": 5, + "label": "-40dB" + }, + { + "index": 6, + "label": "-45dB" + }, + { + "index": 7, + "label": "-50dB" + } + ] + }, + "bootVolume": { + "label": "起動時音量", + "options": [ + { + "index": 0, + "label": "デフォルト" + }, + { + "index": 1, + "label": "-5dB" + }, + { + "index": 2, + "label": "-10dB" + }, + { + "index": 3, + "label": "-15dB" + }, + { + "index": 4, + "label": "-20dB" + }, + { + "index": 5, + "label": "-25dB" + }, + { + "index": 6, + "label": "-30dB" + }, + { + "index": 7, + "label": "-35dB" + }, + { + "index": 8, + "label": "-40dB" + }, + { + "index": 9, + "label": "-45dB" + }, + { + "index": 10, + "label": "-50dB" + } + ] + }, + "xlr": { + "label": "XLRポート極性", + "options": [ + { + "index": 0, + "label": "正相" + }, + { + "index": 1, + "label": "逆相" + } + ] + }, + "uacVer": { + "label": "UACバージョン", + "options": [ + { + "index": 0, + "label": "UAC 2.0" + }, + { + "index": 1, + "label": "UAC 1.0" + } + ] + }, + "arc": { + "label": "ARCモード", + "options": [ + { + "index": 0, + "label": "ARC" + }, + { + "index": 1, + "label": "EARC" + } + ] + }, + "mutePolar": { + "label": "IISミュート極性", + "options": [ + { + "index": 0, + "label": "Low(ミュート)" + }, + { + "index": 1, + "label": "High(ミュート)" + } + ] + }, + "IISMode": { + "label": "IISモード", + "options": [ + { + "index": 0, + "label": "モード1" + }, + { + "index": 1, + "label": "モード2" + }, + { + "index": 2, + "label": "モード3" + }, + { + "index": 3, + "label": "モード4" + }, + { + "index": 4, + "label": "モード5" + }, + { + "index": 5, + "label": "モード6" + }, + { + "index": 6, + "label": "モード7" + }, + { + "index": 7, + "label": "モード8" + } + ] + } + }, + "settings": { + "vu": "設定", + "vuMeter": "VUメーター", + "screenBrightness": { + "label": "画面の明るさ", + "options": [ + { + "index": 0, + "label": "明るい" + }, + { + "index": 1, + "label": "中" + }, + { + "index": 2, + "label": "暗い" + } + ] + }, + "sleep": { + "label": "スリープ", + "options": [ + { + "index": 0, + "label": "しない" + }, + { + "index": 1, + "label": "無信号1分" + }, + { + "index": 2, + "label": "無信号5分" + }, + { + "index": 3, + "label": "無信号10分" + }, + { + "index": 4, + "label": "無信号15分" + } + ] + }, + "turnOffScreen": { + "label": "画面オフ", + "options": [ + { + "index": 0, + "label": "しない" + }, + { + "index": 1, + "label": "30秒" + }, + { + "index": 2, + "label": "1分" + }, + { + "index": 3, + "label": "3分" + }, + { + "index": 4, + "label": "5分" + } + ] + }, + "knobBrightness": { + "label": "ノブの明るさ", + "options": [ + { + "index": 0, + "label": "オフ" + }, + { + "index": 1, + "label": "明るい" + }, + { + "index": 2, + "label": "中" + }, + { + "index": 3, + "label": "暗い" + } + ] + }, + "sleepTime": { + "label": "スリープ", + "options": [ + { + "index": 0, + "label": "オフ" + }, + { + "index": 1, + "label": "無信号1分後" + }, + { + "index": 2, + "label": "無信号5分後" + }, + { + "index": 3, + "label": "無信号10分後" + } + ] + }, + "buttonLight": { + "label": "画面オフ時のノブ呼吸ライト", + "options": [ + { + "index": 0, + "label": "オン" + }, + { + "index": 1, + "label": "オフ" + } + ] + }, + "language": { + "label": "言語", + "options": [ + { + "index": 0, + "label": "英語" + }, + { + "index": 1, + "label": "中国語(繁体字)" + }, + { + "index": 2, + "label": "中国語(簡体字)" + } + ] + }, + "Automatic": { + "label": "ホームへ自動復帰", + "options": [ + { + "index": 0, + "label": "しない" + }, + { + "index": 1, + "label": "無操作20秒" + }, + { + "index": 2, + "label": "無操作40秒" + }, + { + "index": 3, + "label": "無操作60秒" + } + ] + }, + "systemPage": { + "sectionDisplay": "表示", + "sectionGeneral": "一般", + "sectionAbout": "デバイス情報", + "screenBrightnessDesc": "画面全体の明るさを調整", + "turnOffScreenDesc": "操作がない場合に画面がオフになるまでの時間", + "knobBrightnessDesc": "ノブのインジケーターLEDの明るさ", + "buttonLightDesc": "画面ロック時にノブの呼吸ライトを表示", + "languageDesc": "システムの表示言語", + "automaticDesc": "一定時間操作がないとホーム画面に自動で戻ります", + "sleepDesc": "デバイスがスリープ(低消費電力モード)に入るタイミング", + "deviceName": "デバイス名", + "firmwareVersion": "ファームウェアバージョン", + "macAddress": "MACアドレス", + "deviceIp": "デバイスIP", + "sectionAmbientLight": "アンビエントライト", + "ambientLight": "マスタースイッチ", + "ledRed": "赤", + "ledGreen": "緑", + "ledBlue": "青" + } + }, + "prompt": { + "prompt": "お知らせ", + "NotAdded": "ヘッドホンが追加されていません。追加しますか?", + "confirm": "確定", + "cancel": "キャンセル", + "save": "保存", + "reset": "リセット", + "delete": "削除", + "add": "追加", + "edit": "編集", + "deleteConfirm": "削除しますか?", + "saveConfirm": "保存しますか?", + "HP-EQ": "HP-EQが無効です。有効にしますか?", + "Effect": "エフェクトが無効です。有効にしますか?", + "EnableEffectY": "はい", + "EnableEffectN": "いいえ", + "noDelete": "この項目は削除できません", + "SavePreset": "新規プリセット", + "default": "デフォルト", + "invalidData": "無効なデータ", + "range": "範囲", + "selectedYet": "ターゲットカーブがまだ選択されていません", + "leftChannel": "左チャンネル", + "rightChannel": "右チャンネル" + }, + "ai": { + "title": "Luxsin AI", + "welcomeTitle": "こんにちは、Luxsin AIです", + "welcomeDescription": "{device} のシステム設定、ヘッドホンの周波数特性分析、EQの最適化、高度なオーディオ設定をサポートします。", + "suggestionsLabel": "こんな質問をどうぞ", + "suggestions": [ + { + "title": "EQの最適化", + "prompt": "ボーカルがよりクリアに、低音がより深くなるよう今のEQを最適化して。" + }, + { + "title": "ポップス向けEQ", + "prompt": "ポップスに合うEQ設定をおすすめして。" + }, + { + "title": "デバイスの状態を確認", + "prompt": "現在の音量、入力ソース、出力ポートを教えて。" + }, + { + "title": "入力ソースを切り替え", + "prompt": "入力ソースをUSB-Cに切り替えて。" + } + ], + "inputPlaceholder": "デバイス、EQ、エフェクトについて質問…", + "thinking": "考え中…", + "stopped": "(停止しました)", + "requestFailed": "リクエストに失敗しました。しばらくしてから再試行してください。", + "deviceUnreachable": "デバイスに接続できません。このページとデバイスが同じネットワーク上にあることを確認して、再試行してください。", + "aiUnreachable": "Luxsin AIサービスに接続できません。インターネット接続を確認して再試行してください。", + "ariaSend": "送信", + "ariaStop": "停止", + "ariaNewChat": "新しいチャット", + "ariaHistory": "履歴", + "ariaClose": "閉じる", + "ariaFullPage": "全画面で開く", + "ariaHidePanel": "AIパネルを隠す", + "ariaRestoreSplit": "分割表示に戻す", + "clearCurrent": "このチャットをクリア", + "cleared": "現在のチャットをクリアしました", + "clearFailed": "クリアに失敗しました:{message}", + "deviceRequired": "先にデバイスを接続してください", + "inputTooLong": "メッセージが長すぎます。{max}文字以内にしてください", + "deviceDisconnected": "デバイスが切断されました", + "loadHistoryFailed": "履歴の読み込みに失敗しました:{message}", + "loadChatFailed": "チャットの読み込みに失敗しました:{message}", + "toolResultFailed": "ツール結果の送信に失敗しました:{message}", + "riskyConfirm": { + "title": "音量を大幅に上げますか?", + "descMax": "音量が {from} から最大({to} dB)に急上昇します。突然の最大音量は聴覚やヘッドホンを損傷するおそれがあります。続行しますか?", + "descJump": "音量が {from} から {to} dB へ {delta} dB 急上昇します。急な音量上昇は聴覚やヘッドホンを損傷するおそれがあります。続行しますか?", + "descDacDirect0db": "0 dB 音量パススルーを有効にします。音量が最大になり調整できなくなるため、ヘッドホンが非常に大きな音になるおそれがあります。", + "descDacDirect12db": "-12 dB 音量パススルーを有効にします。音量が固定され調整できなくなるため、ヘッドホンの音量が急に大きくなるおそれがあります。", + "fromUnknown": "現在の音量", + "confirm": "変更を確定", + "cancel": "現在の音量を維持", + "declined": "危険な音量変更をキャンセルしました。その他の設定は適用されます。" + }, + "history": { + "title": "チャット履歴", + "newChat": "新しいチャットを開始", + "empty": "チャット履歴はまだありません", + "current": "現在", + "delete": "削除", + "deleteConfirm": "このチャットを削除しますか?この操作は元に戻せません。", + "deleted": "チャットを削除しました", + "deleteFailed": "削除に失敗しました:{message}" + }, + "optimize": { + "badge": "最適化", + "applied": "適用済み", + "summary": "フィルター{count}個 · プリアンプ {preamp} dB", + "compare": "比較", + "apply": "適用", + "rollback": "ロールバック", + "applySuccess": "新しいEQを適用しました", + "rollbackSuccess": "元のEQに戻しました", + "applyFailed": "操作に失敗しました:{message}" + }, + "compareDialog": { + "title": "EQ比較", + "legendBefore": "最適化前", + "legendAfter": "最適化後", + "legendCurrent": "現在", + "legendAfterApply": "適用後", + "legendAfterRollback": "ロールバック後", + "close": "閉じる", + "apply": "この最適化を適用", + "rollback": "最適化前に戻す", + "paramSummary": "フィルター{count}個 · プリアンプ {preamp} dB" + } + } +} diff --git a/client/src/locales/data-ru.json b/client/src/locales/data-ru.json new file mode 100644 index 0000000..be198bb --- /dev/null +++ b/client/src/locales/data-ru.json @@ -0,0 +1,1068 @@ +{ + "connect": { + "ipMissingTitle": "IP-адрес устройства не указан", + "ipMissingDesc": "Перед открытием этой страницы добавьте IP-адрес устройства в адресную строку, например:", + "connecting": "Подключение к {ip}…", + "failedTitle": "Не удалось подключиться", + "failedDesc": "Не удаётся связаться с устройством. Убедитесь, что эта страница и устройство находятся в одной сети и IP-адрес указан верно.", + "failedHint": "Укажите устройство в URL, например ?ip={ip}" + }, + "home": { + "audioSet": "Настройки звука", + "systemSet": "Система", + "power": "Питание", + "input": "Вход", + "output": "Выход", + "vu": "VU-метр", + "powerOffConfirmTitle": "Выключить устройство?", + "powerOffConfirmDesc": "Устройство выключится и отключится от приложения. Продолжить?", + "powerOffConfirmOk": "Выключить", + "powerOffConfirmCancel": "Отмена", + "volumeMinConfirmTitle": "Установить минимальную громкость?", + "volumeMinConfirmDesc": "Громкость будет установлена на минимальный уровень (без звука). Продолжить?", + "volumeMinConfirmOk": "Минимум", + "volumeMaxConfirmTitle": "Установить максимальную громкость?", + "volumeMaxConfirmDesc": "Громкость будет установлена на максимальный уровень. Продолжить?", + "volumeMaxConfirmOk": "Максимум", + "volumePassthroughLockedToast": "Включён сквозной режим громкости, регулировка недоступна.", + "volumePassthroughLockedHint": "Включён сквозной режим громкости, регулировка недоступна.", + "hpMaxVolumeHint": "Задана максимальная громкость наушников: {db} дБ", + "powerOffToastDemo": "Демо-режим: команда выключения отправлена.", + "powerOffToastSent": "Команда выключения отправлена.", + "doubleClickHint": "Дважды нажмите, чтобы открыть", + "changelog": "Что нового", + "changelogLoadFailed": "Не удалось загрузить, повторите попытку позже" + }, + "vuPage": { + "title": "Галерея VU-метров" + }, + "bottomNav": { + "community": "Сообщество", + "hpEq": "HP-EQ", + "home": "Главная", + "effects": "Эффекты", + "myAi": "Мой ИИ" + }, + "communityPage": { + "title": "Сообщество", + "intro": "Форум лучше открывать в системном браузере. Скопируйте ссылку ниже, вставьте её в адресную строку мобильного браузера и откройте.", + "copyLink": "Копировать ссылку", + "toastCopied": "Ссылка скопирована", + "toastCopyFailed": "Не удалось скопировать. Нажмите и удерживайте ссылку, чтобы скопировать вручную." + }, + "source": { + "label": "Вход/выход", + "pageTitle": "Вход/выход", + "inputSection": "Вход", + "outputSection": "Выход", + "inputOptions": [ + "USB-B", + "USB-C", + "Коаксиальный", + "Оптический", + "Bluetooth", + "IIS" + ], + "outputOptions": [ + "XLR", + "RCA", + "Наушники", + "XLR/RCA" + ], + "input": { + "coaxial": "КОАКСИАЛ", + "optical": "ОПТИКА", + "bluetooth": "BLUETOOTH", + "rca": "RCA" + }, + "output": { + "headset": "Наушники" + } + }, + "effect": { + "pageTitle": "Эффекты", + "selectStyleTitle": "Выберите стиль звучания", + "selectCrossfeedTitle": "Выберите пресет кроссфида", + "selectHearingTitle": "Выберите профиль компенсации слуха", + "lowBass": "Низкие", + "enterBass": "Средние", + "highBass": "Высокие", + "tone": "Тембр", + "loudness": "Тонкомпенсация", + "threshold": "Порог", + "delayed": "Задержка", + "mainframeCase": "Основная АС", + "subwooferUnit": "Сабвуфер", + "style": { + "label": "Стиль", + "options": [ + { + "index": 0, + "label": "Классика" + }, + { + "index": 1, + "label": "Танцевальная" + }, + { + "index": 2, + "label": "Поп" + }, + { + "index": 3, + "label": "Регги" + }, + { + "index": 4, + "label": "Концерт" + }, + { + "index": 5, + "label": "Рок" + }, + { + "index": 6, + "label": "Мягкий" + }, + { + "index": 7, + "label": "Электроника" + }, + { + "index": 8, + "label": "Клуб" + }, + { + "index": 9, + "label": "Максимум баса" + }, + { + "index": 10, + "label": "Максимум высоких" + }, + { + "index": 11, + "label": "Наушники" + }, + { + "index": 12, + "label": "Зал" + }, + { + "index": 13, + "label": "Вечеринка" + }, + { + "index": 14, + "label": "Ска" + }, + { + "index": 15, + "label": "Медленный рок" + } + ] + }, + "stereoWidth": { + "label": "Ширина стерео" + }, + "crossfeed": { + "label": "Кроссфид", + "options": [ + "BS2B стандарт (700 Гц, 4,5 дБ)", + "BS2B поп (700 Гц, 6 дБ)", + "BS2B релакс (650 Гц, 9,5 дБ)", + "Пользовательский" + ], + "customTitle": "Пользовательские настройки", + "freqLabel": "Частота", + "attenuationLabel": "Ослабление" + }, + "hearing": { + "label": "Компенсация слуха", + "emptyProfile": "Нет доступных профилей", + "left": "Левый", + "right": "Правый" + }, + "subwoofer": { + "label": "Сабвуфер", + "freq": "Частота среза", + "attenuation": "Крутизна спада", + "gain": "Усиление", + "output": { + "label": "Тип выхода", + "options": [ + { + "label": "Моно (микс)", + "index": 0 + }, + { + "label": "Стерео", + "index": 1 + } + ] + } + }, + "enableConfirmTitle": "Эффекты выключены", + "enableConfirmDesc": "Эффекты сейчас отключены. Включить?", + "enableConfirmOk": "Включить", + "enableConfirmCancel": "Отмена" + }, + "peq": { + "Standard": "Стандартный пресет эквалайзера", + "edit": "Пакетное редактирование", + "headset": "Добавить наушники", + "preamp": "Предусиление", + "filters": { + "label": "Фильтры", + "options": [ + { + "index": 0, + "title": "LPF", + "desc1": "Фильтр нижних частот пропускает только сигналы ", + "desc2": "ниже определённой частоты среза, ", + "desc3": "а высокочастотные сигналы ослабляются.", + "img": "LPF.png" + }, + { + "index": 1, + "title": "HPF", + "desc1": "Фильтр верхних частот пропускает только сигналы ", + "desc2": "выше определённой частоты среза,", + "desc3": "а низкочастотные сигналы ослабляются.", + "img": "HPF.png" + }, + { + "index": 2, + "title": "BPF", + "img": "BPF.png", + "desc1": "Полосовой фильтр пропускает только", + "desc2": "сигналы в определённом диапазоне частот, ", + "desc3": "а остальные частоты ослабляются." + }, + { + "index": 3, + "title": "NOTCH", + "img": "NOTCH.png", + "desc1": "Режекторный фильтр устраняет резонанс", + "desc2": "или помехи на определённых частотах." + }, + { + "index": 4, + "title": "PEAK", + "img": "PEAK.png", + "desc1": "Пиковый фильтр усиливает или ослабляет ", + "desc2": "сигнал вблизи определённой частоты." + }, + { + "index": 5, + "title": "LSHELF", + "img": "LSHELF.png", + "desc1": "Низкочастотный полочный фильтр ", + "desc2": "усиливает или ослабляет низкие частоты." + }, + { + "index": 5, + "title": "HSHELF", + "img": "HSHELF.png", + "desc1": "Высокочастотный полочный фильтр ", + "desc2": "усиливает или ослабляет высокие частоты." + }, + { + "index": 6, + "title": "APF", + "img": "APF.png", + "desc1": "Фазовый фильтр: амплитуда выходного", + "desc2": "сигнала не меняется, но меняется фаза." + } + ] + }, + "freq": "FREQ", + "gain": "GAIN", + "q": "Добротность Q", + "loadDeviceBtn": "ОК", + "peqNoneItem": "Не настроено", + "eqUi": { + "batchEditTitle": "Пакетное редактирование", + "batchEditHint": "Всего 11 строк: строка 1 — Preamp, строки 2–11 — Filter 1–10. Сохранение перезапишет текущий пресет наушников.", + "addPresetTitle": "Новый пресет", + "addPresetNameEmpty": "Имя не может быть пустым", + "addPresetNameExists": "Такое имя уже существует. Выберите другое.", + "addPresetNameTooLong": "Имя не может превышать 80 символов", + "toastAddPresetOk": "Пресет добавлен", + "toastAddPresetFail": "Не удалось добавить пресет", + "toastPresetLimitReached": "Достигнут лимит пресетов ({{max}}). Удалите лишние, чтобы добавить новые.", + "cancel": "Отмена", + "save": "Сохранить", + "tabBrands": "Бренды", + "tabModels": "Модели", + "tabTarget": "Цель", + "searchPlaceholder": "Поиск бренда или модели", + "searching": "Поиск…", + "noSearchResults": "Бренд или модель не найдены", + "loading": "Загрузка…", + "noBrandMatch": "Бренды не найдены", + "selectBrandFirst": "Сначала выберите бренд на вкладке «Бренды»", + "brandLabel": "Бренд:", + "modelFromSearchLabel": "Модель из поиска:", + "noModelsForBrand": "Для этого бренда нет моделей", + "targetBrand": "Бренд:", + "targetModel": "Модель:", + "targetForm": "Тип:", + "formAll": "Все", + "confirmLoading": "Обработка…", + "confirmButton": "Подтвердить", + "selectModelFirst": "Сначала выберите модель", + "noTargets": "Нет доступных целевых кривых", + "closeDrawer": "Закрыть", + "toastBatchApplied": "Пакетное редактирование применено", + "toastBatchParseError": "Неверный формат пакетного редактирования", + "undo": "Отменить", + "toastUndo": "Отменено: {{detail}}", + "toastUndoEmpty": "Отменено", + "undoBandParam": "{{mode}} полоса {{band}} {{param}} {{from}} → {{to}}", + "undoSimpleParam": "{{param}} {{from}} → {{to}}", + "undoMultiple": "Изменений: {{count}} ({{summary}})", + "undoMoreSuffix": " (ещё {{count}})", + "undoDetailSep": "; ", + "undoParamFreq": "FREQ", + "undoParamGain": "GAIN", + "undoParamQ": "Q", + "undoParamType": "Фильтр", + "undoParamPreamp": "Предусиление", + "undoParamAuto": "АВТО", + "undoParamAbMode": "A/B", + "undoAutoOn": "ВКЛ", + "undoAutoOff": "ВЫКЛ", + "toastBrandsFail": "Не удалось загрузить бренды", + "toastModelsFail": "Не удалось загрузить модели", + "toastCurveFail": "Не удалось загрузить кривую", + "toastNewEqOk": "EQ новых наушников сохранён", + "toastTargetSelected": "Выбрана цель: {{name}}", + "deleteConfirm": "Удалить наушники «{{name}}»?", + "batchDeleteConfirm": "Удалить выбранные пресеты ({{count}})?", + "deletePresetConfirm": "Удалить пресет «{{name}}»?", + "managePresetTitle": "Управление пресетами EQ", + "selectAll": "Выбрать все", + "batchDelete": "Удалить выбранные", + "noPresets": "Нет пресетов", + "renamePreset": "Переименовать", + "movePresetUp": "Вверх", + "movePresetDown": "Вниз", + "confirmOrder": "Применить порядок", + "moveToFirst": "В начало", + "moveToLast": "В конец", + "currentPreset": "Текущий", + "toastOrderSaved": "Порядок сохранён", + "toastOrderFail": "Не удалось сохранить порядок", + "deletePreset": "Удалить", + "toastRenamed": "Переименовано", + "toastRenameFail": "Не удалось переименовать", + "renameEmptyName": "Имя не может быть пустым", + "renameNameExists": "Такое имя уже существует", + "toastDeleted": "Наушники удалены", + "toastDeleteFail": "Не удалось удалить наушники", + "bassBoost": "Усиление баса: fc {{fc}}, q {{q}}, gain {{gain}} дБ", + "errMinLines": "Требуется не менее 11 строк (Preamp + 10 фильтров)", + "errPreampFirstLine": "Строка 1 должна иметь вид Preamp:-5.96dB", + "errPreampValue": "Недопустимое значение Preamp", + "errFilterLine": "Строка {{line}} не соответствует формату Filter", + "errFilterNoRange": "Номер фильтра {{no}} вне диапазона", + "errFilterType": "Недопустимый тип фильтра {{no}} ({{type}})", + "errFilterNumbers": "Недопустимые значения фильтра {{no}}", + "errFilterAll": "Должны быть указаны все фильтры 1–10", + "chartCopyTo": "Копировать в {{mode}}", + "chartApplyB": "Применить B", + "chartSaveB": "Сохранить B как", + "saveBTitle": "Сохранить B как", + "toastChartCopied": "Скопировано в {{mode}}", + "toastApplyBSuccess": "Кривая B применена к A", + "toastApplyBFail": "Не удалось применить B", + "toastSaveBOk": "Пресет сохранён из кривой B", + "toastSaveBFail": "Не удалось сохранить кривую B", + "enableConfirmTitle": "HP-EQ выключен", + "enableConfirmDesc": "HP-EQ сейчас отключён. Включить?", + "enableConfirmOk": "Включить", + "paramInputTitleFreq": "Задать частоту", + "paramInputTitleGain": "Задать усиление", + "paramInputTitleQ": "Задать добротность Q", + "paramInputHintFreq": "Диапазон: 20 – 20000 Гц. Используйте k для кГц (например, 9.5k).", + "paramInputHintGain": "Диапазон: -15.0 – +15.0 дБ", + "paramInputHintQ": "Диапазон: 0.10 – 10.00", + "paramInputInvalid": "Введите допустимое число", + "paramInputOutOfRangeFreq": "Частота должна быть от {{min}} до {{max}} Гц", + "paramInputOutOfRangeGain": "Усиление должно быть от {{min}} до {{max}} дБ", + "paramInputOutOfRangeQ": "Q должна быть от {{min}} до {{max}}", + "overwritePresetTitle": "Пресет с таким именем уже существует", + "overwritePresetDesc": "Пресет «{{name}}» уже существует. Перезаписать?", + "overwritePresetConfirm": "Перезаписать", + "overwritePresetCancelled": "Отменено", + "shareTitle": "Поделиться EQ", + "shareSelectHint": "Выберите пресет EQ, которым хотите поделиться", + "shareConfirm": "Создать код", + "shareCodeLabel": "Код для обмена", + "shareCopied": "Код скопирован", + "shareGenerating": "Создание…", + "shareCreateFail": "Не удалось создать код", + "shareActiveExists": "У вас уже есть действующий код. Повторите попытку позже.", + "shareInvalidParams": "Неверный запрос. Проверьте данные и повторите попытку.", + "shareSystemError": "Системная ошибка. Повторите попытку позже.", + "shareExpireAt": "Действует до", + "importTab": "Импорт EQ", + "importCodeHint": "Введите или вставьте 5-значный код", + "importQuery": "Найти", + "importQuerying": "Поиск…", + "importCodeNotFound": "Код не найден или истёк", + "importEqName": "Название EQ", + "importEqNameHint": "Название можно изменить", + "importSourceModel": "Исходное устройство", + "importButton": "Импортировать", + "importSaving": "Импорт…", + "importSuccess": "EQ успешно импортирован", + "importFail": "Не удалось импортировать EQ", + "mySharesTab": "Мои коды", + "mySharesLoading": "Загрузка…", + "mySharesEmpty": "Вы ещё не делились EQ", + "mySharesCode": "Код", + "mySharesName": "Название EQ", + "mySharesLoadFail": "Не удалось загрузить список", + "mySharesDeleteConfirmTitle": "Удалить код", + "mySharesDeleteConfirmDesc": "Удалить код {{code}}? По нему больше нельзя будет импортировать EQ.", + "mySharesDeleteConfirmOk": "Удалить", + "mySharesDeleteSuccess": "Код удалён", + "mySharesDeleteFail": "Не удалось удалить код" + }, + "share": "Поделиться EQ" + }, + "dac": { + "sectionBalanceSensitivity": "Баланс и чувствительность", + "sectionOutput": "Характеристики выхода", + "sectionHpSettings": "Настройки наушников", + "sectionIisSettings": "Настройки IIS", + "sectionSystemInterface": "Система и интерфейс", + "balance": "Баланс Л/П", + "sensitivity": "Чувствительность VU-метра", + "filters": { + "label": "Фильтры", + "options": [ + { + "index": 0, + "label": "Резкий спад" + }, + { + "index": 1, + "label": "Плавный спад" + }, + { + "index": 2, + "label": "Короткая задержка, резкий спад" + }, + { + "index": 3, + "label": "Короткая задержка, плавный спад" + }, + { + "index": 4, + "label": "Фильтр деэмфазиса" + }, + { + "index": 5, + "label": "Без передискретизации (NOS)" + } + ] + }, + "dacGain": { + "label": "Усиление наушников", + "options": [ + { + "index": 0, + "label": "Низкое" + }, + { + "index": 1, + "label": "Среднее" + }, + { + "index": 2, + "label": "Высокое" + } + ] + }, + "autoImpedance": { + "label": "Автоопределение импеданса", + "options": [ + { + "index": 0, + "label": "Выкл." + }, + { + "index": 1, + "label": "Вкл." + } + ] + }, + "dreMode": { + "label": "Расширение динамического диапазона", + "options": [ + { + "index": 0, + "label": "Выкл." + }, + { + "index": 1, + "label": "Вкл." + } + ] + }, + "dacVolumeDirect": { + "label": "Сквозная громкость предвыхода", + "options": [ + { + "index": 0, + "label": "Выкл." + }, + { + "index": 1, + "label": "0dB" + }, + { + "index": 2, + "label": "-12dB" + } + ] + }, + "dacVolumeDirectConfirm": { + "title0db": "Включить сквозной режим 0 дБ?", + "desc0db": "Сквозной режим устанавливает максимальную громкость и отключает её регулировку.", + "title12db": "Включить сквозной режим -12 дБ?", + "desc12db": "Сквозной режим устанавливает громкость -12 дБ и отключает её регулировку.", + "confirm": "Подтвердить", + "cancel": "Отмена" + }, + "volumeStep": "Шаг громкости", + "hpMaxVolume": { + "label": "Макс. громкость наушников", + "options": [ + { + "index": 0, + "label": "0dB" + }, + { + "index": 1, + "label": "-5dB" + }, + { + "index": 2, + "label": "-10dB" + }, + { + "index": 3, + "label": "-15dB" + }, + { + "index": 4, + "label": "-20dB" + }, + { + "index": 5, + "label": "-25dB" + }, + { + "index": 6, + "label": "-30dB" + }, + { + "index": 7, + "label": "-35dB" + }, + { + "index": 8, + "label": "-40dB" + }, + { + "index": 9, + "label": "-45dB" + }, + { + "index": 10, + "label": "-50dB" + } + ] + }, + "hpStartupVolume": { + "label": "Громкость наушников при запуске", + "options": [ + { + "index": 0, + "label": "По умолчанию" + }, + { + "index": 1, + "label": "-20dB" + }, + { + "index": 2, + "label": "-25dB" + }, + { + "index": 3, + "label": "-30dB" + }, + { + "index": 4, + "label": "-35dB" + }, + { + "index": 5, + "label": "-40dB" + }, + { + "index": 6, + "label": "-45dB" + }, + { + "index": 7, + "label": "-50dB" + } + ] + }, + "bootVolume": { + "label": "Громкость при включении", + "options": [ + { + "index": 0, + "label": "По умолчанию" + }, + { + "index": 1, + "label": "-5dB" + }, + { + "index": 2, + "label": "-10dB" + }, + { + "index": 3, + "label": "-15dB" + }, + { + "index": 4, + "label": "-20dB" + }, + { + "index": 5, + "label": "-25dB" + }, + { + "index": 6, + "label": "-30dB" + }, + { + "index": 7, + "label": "-35dB" + }, + { + "index": 8, + "label": "-40dB" + }, + { + "index": 9, + "label": "-45dB" + }, + { + "index": 10, + "label": "-50dB" + } + ] + }, + "xlr": { + "label": "Полярность порта XLR", + "options": [ + { + "index": 0, + "label": "Нормальная" + }, + { + "index": 1, + "label": "Обратная" + } + ] + }, + "uacVer": { + "label": "Версия UAC", + "options": [ + { + "index": 0, + "label": "UAC 2.0" + }, + { + "index": 1, + "label": "UAC 1.0" + } + ] + }, + "arc": { + "label": "Режим ARC", + "options": [ + { + "index": 0, + "label": "ARC" + }, + { + "index": 1, + "label": "EARC" + } + ] + }, + "mutePolar": { + "label": "Уровень Mute IIS", + "options": [ + { + "index": 0, + "label": "Низкий (mute)" + }, + { + "index": 1, + "label": "Высокий (mute)" + } + ] + }, + "IISMode": { + "label": "Режим IIS", + "options": [ + { + "index": 0, + "label": "Режим 1" + }, + { + "index": 1, + "label": "Режим 2" + }, + { + "index": 2, + "label": "Режим 3" + }, + { + "index": 3, + "label": "Режим 4" + }, + { + "index": 4, + "label": "Режим 5" + }, + { + "index": 5, + "label": "Режим 6" + }, + { + "index": 6, + "label": "Режим 7" + }, + { + "index": 7, + "label": "Режим 8" + } + ] + } + }, + "settings": { + "vu": "Настройки", + "vuMeter": "VU-метр", + "screenBrightness": { + "label": "Яркость экрана", + "options": [ + { + "index": 0, + "label": "Ярче" + }, + { + "index": 1, + "label": "Средняя" + }, + { + "index": 2, + "label": "Темнее" + } + ] + }, + "sleep": { + "label": "Сон", + "options": [ + { + "index": 0, + "label": "Никогда" + }, + { + "index": 1, + "label": "Нет сигнала 1 минуту" + }, + { + "index": 2, + "label": "Нет сигнала 5 минут" + }, + { + "index": 3, + "label": "Нет сигнала 10 минут" + }, + { + "index": 4, + "label": "Нет сигнала 15 минут" + } + ] + }, + "turnOffScreen": { + "label": "Отключение экрана", + "options": [ + { + "index": 0, + "label": "Никогда" + }, + { + "index": 1, + "label": "30 секунд" + }, + { + "index": 2, + "label": "1 минута" + }, + { + "index": 3, + "label": "3 минуты" + }, + { + "index": 4, + "label": "5 минут" + } + ] + }, + "knobBrightness": { + "label": "Яркость ручки", + "options": [ + { + "index": 0, + "label": "Выкл." + }, + { + "index": 1, + "label": "Ярче" + }, + { + "index": 2, + "label": "Средняя" + }, + { + "index": 3, + "label": "Темнее" + } + ] + }, + "sleepTime": { + "label": "Сон", + "options": [ + { + "index": 0, + "label": "Выкл." + }, + { + "index": 1, + "label": "Нет сигнала более 1 мин" + }, + { + "index": 2, + "label": "Нет сигнала более 5 мин" + }, + { + "index": 3, + "label": "Нет сигнала более 10 мин" + } + ] + }, + "buttonLight": { + "label": "Дыхание подсветки ручки при выкл. экране", + "options": [ + { + "index": 0, + "label": "Вкл." + }, + { + "index": 1, + "label": "Выкл." + } + ] + }, + "language": { + "label": "Язык", + "options": [ + { + "index": 0, + "label": "Английский" + }, + { + "index": 1, + "label": "Китайский (традиционный)" + }, + { + "index": 2, + "label": "Китайский (упрощённый)" + } + ] + }, + "Automatic": { + "label": "Автовозврат на главную", + "options": [ + { + "index": 0, + "label": "Никогда" + }, + { + "index": 1, + "label": "20 секунд бездействия" + }, + { + "index": 2, + "label": "40 секунд бездействия" + }, + { + "index": 3, + "label": "60 секунд бездействия" + } + ] + }, + "systemPage": { + "sectionDisplay": "Экран", + "sectionGeneral": "Общие", + "sectionAbout": "Об устройстве", + "screenBrightnessDesc": "Общая яркость экрана", + "turnOffScreenDesc": "Время бездействия до отключения экрана", + "knobBrightnessDesc": "Яркость светодиодов индикатора ручки", + "buttonLightDesc": "Эффект «дыхания» подсветки ручки при заблокированном экране", + "languageDesc": "Язык интерфейса системы", + "automaticDesc": "Автоматический возврат на главный экран после простоя", + "sleepDesc": "Когда устройство переходит в сон или режим энергосбережения", + "deviceName": "Имя устройства", + "firmwareVersion": "Версия прошивки", + "macAddress": "MAC-адрес", + "deviceIp": "IP устройства", + "sectionAmbientLight": "Фоновая подсветка", + "ambientLight": "Главный выключатель", + "ledRed": "Красный", + "ledGreen": "Зелёный", + "ledBlue": "Синий" + } + }, + "prompt": { + "prompt": "Подсказка", + "NotAdded": "Наушники не добавлены. Добавить?", + "confirm": "ОК", + "cancel": "Отмена", + "save": "Сохранить", + "reset": "Сброс", + "delete": "Удалить", + "add": "Добавить", + "edit": "Изменить", + "deleteConfirm": "Удалить?", + "saveConfirm": "Сохранить?", + "HP-EQ": "HP-EQ отключён. Включить?", + "Effect": "Эффекты отключены. Включить?", + "EnableEffectY": "Да", + "EnableEffectN": "Нет", + "noDelete": "Этот элемент нельзя удалить", + "SavePreset": "Новый пресет", + "default": "По умолчанию", + "invalidData": "Недопустимые данные", + "range": "Диапазон", + "selectedYet": "Целевая кривая ещё не выбрана", + "leftChannel": "Левый канал", + "rightChannel": "Правый канал" + }, + "ai": { + "title": "Luxsin AI", + "welcomeTitle": "Привет, я Luxsin AI", + "welcomeDescription": "Я помогу с настройкой системы {device}, анализом АЧХ наушников, оптимизацией EQ и расширенной настройкой звука.", + "suggestionsLabel": "Попробуйте спросить", + "suggestions": [ + { + "title": "Оптимизация EQ", + "prompt": "Оптимизируй мой текущий EQ: более чёткий вокал и глубокий бас." + }, + { + "title": "EQ для поп-музыки", + "prompt": "Порекомендуй настройки EQ для поп-музыки." + }, + { + "title": "Состояние устройства", + "prompt": "Покажи текущую громкость, источник входа и выходной порт." + }, + { + "title": "Сменить источник входа", + "prompt": "Переключи источник входа на USB-C." + } + ], + "inputPlaceholder": "Спросите об устройстве, EQ, эффектах…", + "thinking": "Думаю…", + "stopped": "(Остановлено)", + "requestFailed": "Ошибка запроса. Повторите попытку позже.", + "deviceUnreachable": "Не удаётся связаться с устройством. Убедитесь, что эта страница и устройство находятся в одной сети, и повторите попытку.", + "aiUnreachable": "Не удаётся подключиться к сервису Luxsin AI. Проверьте подключение к интернету и повторите попытку.", + "ariaSend": "Отправить", + "ariaStop": "Остановить", + "ariaNewChat": "Новый чат", + "ariaHistory": "История", + "ariaClose": "Закрыть", + "ariaFullPage": "Открыть на весь экран", + "ariaHidePanel": "Скрыть панель ИИ", + "ariaRestoreSplit": "Вернуть разделённый вид", + "clearCurrent": "Очистить этот чат", + "cleared": "Текущий чат очищен", + "clearFailed": "Не удалось очистить: {message}", + "deviceRequired": "Сначала подключите устройство", + "inputTooLong": "Сообщение слишком длинное, не более {max} символов", + "deviceDisconnected": "Устройство отключено", + "loadHistoryFailed": "Не удалось загрузить историю: {message}", + "loadChatFailed": "Не удалось загрузить чат: {message}", + "toolResultFailed": "Не удалось отправить результат инструмента: {message}", + "riskyConfirm": { + "title": "Подтвердить сильное повышение громкости?", + "descMax": "Громкость резко изменится с {from} до максимума ({to} дБ). Внезапный максимальный уровень может повредить слух или наушники. Продолжить?", + "descJump": "Громкость резко вырастет на {delta} дБ: с {from} до {to} дБ. Резкое повышение может повредить слух или наушники. Продолжить?", + "descDacDirect0db": "Будет включён сквозной режим 0 дБ: громкость станет максимальной без возможности регулировки. Наушники могут звучать очень громко.", + "descDacDirect12db": "Будет включён сквозной режим -12 дБ: громкость будет зафиксирована без возможности регулировки. Наушники могут внезапно зазвучать намного громче.", + "fromUnknown": "текущего уровня", + "confirm": "Подтвердить", + "cancel": "Оставить текущую громкость", + "declined": "Опасное изменение громкости отменено. Остальные настройки будут применены." + }, + "history": { + "title": "История чатов", + "newChat": "Начать новый чат", + "empty": "История чатов пуста", + "current": "Текущий", + "delete": "Удалить", + "deleteConfirm": "Удалить этот чат? Это действие нельзя отменить.", + "deleted": "Чат удалён", + "deleteFailed": "Не удалось удалить: {message}" + }, + "optimize": { + "badge": "Оптимизация", + "applied": "Применено", + "summary": "Фильтров: {count} · предусиление {preamp} дБ", + "compare": "Сравнить", + "apply": "Применить", + "rollback": "Откатить", + "applySuccess": "Новый EQ применён", + "rollbackSuccess": "Восстановлен исходный EQ", + "applyFailed": "Ошибка операции: {message}" + }, + "compareDialog": { + "title": "Сравнение EQ", + "legendBefore": "До", + "legendAfter": "После", + "legendCurrent": "Текущий", + "legendAfterApply": "После применения", + "legendAfterRollback": "После отката", + "close": "Закрыть", + "apply": "Применить эту оптимизацию", + "rollback": "Вернуть исходный", + "paramSummary": "Фильтров: {count} · предусиление {preamp} дБ" + } + } +} diff --git a/client/src/locales/resolveLocale.ts b/client/src/locales/resolveLocale.ts index 2481031..7e3c8db 100644 --- a/client/src/locales/resolveLocale.ts +++ b/client/src/locales/resolveLocale.ts @@ -8,11 +8,14 @@ import localeKo from "./data-ko.json"; import localeVi from "./data-vi.json"; import localePt from "./data-pt.json"; import localePl from "./data-pl.json"; +import localeRu from "./data-ru.json"; +import localeJa from "./data-ja.json"; /** * 语言索引映射: * 0=en, 1=zh-HK, 2=zh-CN → 设备固件支持,选择后会下发 dev/info.cgi?action=setting&language= - * 3=de, 4=es, 5=fr, 6=ko, 7=vi, 8=pt, 9=pl → 设备不支持,仅在 controller 前端做界面语言切换,绝不下发设备 + * 3=de, 4=es, 5=fr, 6=ko, 7=vi, 8=pt, 9=pl, + * 10=ru, 11=ja → 设备不支持,仅在 controller 前端做界面语言切换,绝不下发设备 */ export const DEFAULT_DEVICE_LANGUAGE = 0; @@ -32,6 +35,8 @@ const LOCALE_PACKS = [ localeVi, localePt, localePl, + localeRu, + localeJa, ] as LocalePack[]; /** 语言选择器可展示的全部语言数量(含仅前端语言)。 */ @@ -39,7 +44,7 @@ export const LANGUAGE_COUNT = LOCALE_PACKS.length; /** * 语言选择器固定显示的「母语名称」,与当前界面语言无关。 - * 数组下标即语言索引(0=en … 9=pl),选择时直接作为语言值使用。 + * 数组下标即语言索引(0=en … 11=ja),选择时直接作为语言值使用。 */ export const LANGUAGE_NATIVE_LABELS = [ "English", @@ -52,6 +57,8 @@ export const LANGUAGE_NATIVE_LABELS = [ "Tiếng Việt", "Português (Portugal)", "Polski", + "Русский", + "日本語", ]; /** 是否为设备支持的语言(0/1/2),只有这些才会下发设备。 */ @@ -69,7 +76,7 @@ export function resolveDeviceLanguage(language: number | undefined): number { return language ?? DEFAULT_DEVICE_LANGUAGE; } -/* ── 前端语言覆盖(仅非设备语言 3..9,持久化到 localStorage,绝不下发设备) ── */ +/* ── 前端语言覆盖(仅非设备语言 3..11,持久化到 localStorage,绝不下发设备) ── */ const UI_LANGUAGE_OVERRIDE_KEY = "luxsin.uiLanguageOverride"; function readStoredOverride(): number | null { @@ -91,7 +98,7 @@ export function getUiLanguageOverride(): number | null { } /** - * 设置前端语言覆盖。传入仅前端语言(3..9)时生效并持久化; + * 设置前端语言覆盖。传入仅前端语言(3..11)时生效并持久化; * 传入设备语言(0/1/2)或 null 时清除覆盖,恢复跟随设备语言。 */ export function setUiLanguageOverride(index: number | null): number | null { diff --git a/client/src/pages/EQPage.tsx b/client/src/pages/EQPage.tsx index 279577f..c5b5846 100644 --- a/client/src/pages/EQPage.tsx +++ b/client/src/pages/EQPage.tsx @@ -83,6 +83,7 @@ import { fetchEqSyncPeq, getUniquePresetName, importedFiltersToPeqFilters, + isBypassFilterType, } from "./eq/peqMappers"; import type { BandParamKind, PeqCatalogItem, PeqEqUi, PeqPresetLocalCache } from "./eq/types"; @@ -355,12 +356,15 @@ export default function EQPage() { return filters.map((f: any) => { const rawFreq = f.fc ?? f.freq ?? f.frequency ?? 1000; const freq = normalizeBandFreqHz(rawFreq) ?? 1000; + // 设备用 type=10 表示旁通,原类型已丢失;回退 PEAK 供重新打开时使用 + const bypassed = isBypassFilterType(f.type); return { freq: Math.round(freq), gain: Number(f.gain || 0), q: Number(f.q || 1), - type: normalizeFilterType(f.type), + type: bypassed ? "PEAK" : normalizeFilterType(f.type), enabled: f.enabled !== undefined ? f.enabled : true, + bypassed, }; }); }; @@ -415,6 +419,7 @@ export default function EQPage() { freq: number; gain: number; q: number; + bypassed: boolean; }>(10); for (let i = 1; i <= 10; i++) { @@ -449,6 +454,7 @@ export default function EQPage() { freq: Number(freq.toFixed(2)), gain: Number(gain.toFixed(2)), q: Number(q.toFixed(2)), + bypassed: false, }; } @@ -1243,6 +1249,7 @@ export default function EQPage() { }, []); const band = bands[selectedBand] ?? DEFAULT_BANDS[selectedBand] ?? DEFAULT_BANDS[0]; + const bandBypassed = !!band.bypassed; useEffect(() => { setBandSliderPreview(null); @@ -1682,29 +1689,44 @@ export default function EQPage() { {/* ── Band detail card ── */} -