diff --git a/locales/en.yml b/locales/en.yml index 9afc8b00..7c677718 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -6,21 +6,40 @@ common: go_back_button_label: "Go Back" +common_error: + no_device_mic: "No mic device detected." + no_device_speaker: "No Speaker device detected." + threshold_invalid_value: "You can set it with a value between {{min}} to {{max}}." + failed_download_weight_ctranslate2: "CTranslate2 weight download error." + failed_download_weight_whisper: "Whisper weight download error." + translation_limit: "Translation engine limit error." + deepl_auth_key_invalid_length: "DeepL auth key length is not correct." + deepl_auth_key_failed_authentication: "Auth Key is incorrect or Usage limit reached." + + invalid_value_mic_record_timeout: "It cannot be greater than '{{mic_phrase_timeout_label}}' with a value of 0 or more." + invalid_value_mic_phrase_timeout: "It cannot be set lower than '{{mic_record_timeout_label}}' with a value of 0 or more." + invalid_value_mic_max_phrase: "You can set a number equal to or greater than 0." + + invalid_value_speaker_record_timeout: "It cannot be greater than '{{speaker_phrase_timeout_label}}' with a value of 0 or more." + invalid_value_speaker_phrase_timeout: "It cannot be set lower than '{{speaker_record_timeout_label}}' with a value of 0 or more." + invalid_value_speaker_max_phrase: "You can set a number equal to or greater than 0." + main_page: translation: "Translation" transcription_send: "Voice2Chatbox" transcription_receive: "Speaker2Log" foreground: "Foreground" + language_settings: "Language Settings" your_language: "Your Language" translate_each_other_label: "Translate Each Other" swap_button_label: "Swap Languages" target_language: "Target Language" translator: "Translator" - translator_ctranslate2: "Internal (Default)" + translator_label_default: "Default" translator_selector: - is_selected_same_language: "Since the same language is selected for both '{{your_language}}' and '{{target_language}}', only '{{translator_ctranslate2}}' is available." + is_selected_same_language: "Since the same language is selected for both '{{your_language}}' and '{{target_language}}', only '{{ctranslate2}}' is available." message_log: all: "All" @@ -59,37 +78,29 @@ config_page: appearance: "Appearance" translation: "Translation" transcription: "Transcription" - vr: "VR" others: "Others" hotkeys: "Hotkeys" advanced_settings: "Advanced Settings" - supporters: "Supporters" - about_vrct: "About VRCT" device: check_volume: "Check Volume" + label_auto_select: "Auto Select" + label_host: "Host/Driver" + label_device: "Device" mic_host_device: label: "Mic Device" - label_auto_select: "Auto Select" - label_host: "Host/Driver" - label_device: "Device" mic_dynamic_energy_threshold: label_for_automatic: "Mic Energy Threshold (Current Setting: Automatic)" desc_for_automatic: "Automatically determine microphone input sensitivity." label_for_manual: "Mic Energy Threshold (Current Setting: Manual)" desc_for_manual: "Manually determine the microphone input sensitivity using the slider. Press the microphone icon to input your voice and adjust the sensitivity while monitoring the volume." - error_message: "You can set it with a value between 0 to {{max}}." speaker_device: label: "Speaker Device" - label_auto_select: "Auto Select" - label_device: "Device" speaker_dynamic_energy_threshold: label_for_automatic: "Speaker Energy Threshold (Current Setting: Automatic)" desc_for_automatic: "Automatically determine speaker input sensitivity." label_for_manual: "Speaker Energy Threshold (Current Setting: Manual)" desc_for_manual: "Manually determine the speaker input sensitivity using the slider. Press the headphones icon to listen to the audio and adjust the sensitivity while monitoring the volume." - error_message: "You can set it with a value between 0 to {{max}}." - no_device_error_message: "No speaker device detected." appearance: transparency: @@ -112,20 +123,19 @@ config_page: translation: ctranslate2_weight_type: - label: "Internal Translation Model" + label: "{{ctranslate2}} Model" desc: "You can choose the translation model to use for the internal translation engine." small: "Basic model ({{capacity}})" large: "High accuracy model ({{capacity}})" ctranslate2_compute_device: - label: "Internal Translation Compute Device" + label: "{{ctranslate2}} Compute Device" deepl_auth_key: label: "DeepL Auth Key" desc: "Please select {{translator}} on the main screen with DeepL_API when using. ※Some languages may not be supported." + open_auth_key_webpage: "Open DeepL Account Webpage" save: "Save" edit: "Edit" - open_auth_key_webpage: "Open DeepL Account Webpage" auth_key_success: "Auth key update completed." - auth_key_error: "Auth Key is incorrect or Usage limit reached." transcription: section_label_mic: "Mic" @@ -134,15 +144,12 @@ config_page: mic_record_timeout: label: "Mic Record Timeout" desc: "Detects silence and, when the specified number of seconds has passed, considers the mic input to have ended. (Second(s))" - error_message: "It cannot be greater than '{{mic_phrase_timeout_label}}' with a value of 0 or more." mic_phrase_timeout: label: "Mic Phrase Timeout" desc: "Transcription processing is performed at intervals of the specified number of seconds." - error_message: "It cannot be set lower than '{{mic_record_timeout_label}}' with a value of 0 or more." mic_max_phrase: label: "Mic Max Words" desc: "It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs and send to VRChat." - error_message: "You can set a number equal to or greater than 0." mic_word_filter: label: "Mic Word Filter" desc: "If a registered word is detected, the text will not be sent. To add multiple words at once, separate them with a ',' (comma).\n*Duplicate words will not be registered." @@ -151,15 +158,12 @@ config_page: speaker_record_timeout: label: "Speaker Record Timeout" desc: "Detects silence and, when the specified number of seconds has passed, considers the speaker input to have ended. (Second(s))" - error_message: "It cannot be greater than '{{speaker_phrase_timeout_label}}' with a value of 0 or more." speaker_phrase_timeout: label: "Speaker Phrase Timeout" desc: "Transcription processing is performed at intervals of the specified number of seconds." - error_message: "It cannot be set lower than '{{speaker_record_timeout_label}}' with a value of 0 or more." speaker_max_phrase: label: "Speaker Max Words" desc: "It is the lower limit for the number of transcribed words, and only when this number is exceeded will the transcription results be displayed logs." - error_message: "You can set a number equal to or greater than 0." select_transcription_engine: label: "Transcription Engine" whisper_weight_type: @@ -191,11 +195,11 @@ config_page: ui_scaling: "UI Scaling" display_duration: "Display duration" fadeout_duration: "Fadeout duration" + common_settings: "Common Settings" tracker: "Tracker" hmd: "HMD" left_hand: "Left hand" right_hand: "Right hand" - common_settings: "Common Settings" overlay_show_only_translated_messages: label: "Show Only Translated Messages" diff --git a/locales/ja.yml b/locales/ja.yml index 4fc04836..188c5325 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -6,21 +6,40 @@ common: go_back_button_label: "戻る" +common_error: + no_device_mic: "マイクデバイスが検出されませんでした。" + no_device_speaker: "スピーカーデバイスが検出されませんでした。" + threshold_invalid_value: "{{min}} から {{max}} までの数値で設定できます。" + failed_download_weight_ctranslate2: "CTranslate2 モデルのダウンロードに失敗しました。" + failed_download_weight_whisper: "Whisper モデルのダウンロードに失敗しました。" + translation_limit: "翻訳エンジンの使用制限に達したか、一時的に利用制限がかけられています。" + deepl_auth_key_invalid_length: "認証キーの文字数が間違っています。" + deepl_auth_key_failed_authentication: "認証キーが間違っているか、API使用制限が上限に達しています。" + + invalid_value_mic_record_timeout: "0 以上で 「{{mic_phrase_timeout_label}}」 より大きくすることはできません。" + invalid_value_mic_phrase_timeout: "0 以上で 「{{mic_record_timeout_label}}」 より小さくすることはできません。" + invalid_value_mic_max_phrase: "0 以上の数値を設定できます。" + + invalid_value_speaker_record_timeout: "0 以上で 「{{speaker_phrase_timeout_label}}」 より大きくすることはできません。" + invalid_value_speaker_phrase_timeout: "0 以上で 「{{speaker_record_timeout_label}}」 より小さくすることはできません。" + invalid_value_speaker_max_phrase: "0 以上の数値を設定できます。" + main_page: translation: "翻訳" - transcription_send: "音声認識(マイク)" - transcription_receive: "音声認識(スピーカー)" - foreground: "最前面表示" + transcription_send: "音声認識 マイク" + transcription_receive: "音声認識 スピーカー" + foreground: "最前面固定" + language_settings: "言語設定" your_language: "あなたの言語" translate_each_other_label: "双方向に翻訳" swap_button_label: "言語を入れ替え" target_language: "相手の言語" translator: "翻訳エンジン" - translator_ctranslate2: "オフライン翻訳 (Default)" + translator_label_default: "デフォルト" translator_selector: - is_selected_same_language: "{{your_language}}」と「{{target_language}}」に同じ言語が選択がされているため、「{{translator_ctranslate2}}」のみが使用できます。" + is_selected_same_language: "「{{your_language}}」と「{{target_language}}」に同じ言語が選択がされているため、「{{ctranslate2}}」のみが使用できます。" message_log: all: "全て" @@ -65,27 +84,23 @@ config_page: device: check_volume: "音量チェック" + label_auto_select: "自動選択" + label_host: "ホスト/ドライバー" + label_device: "デバイス" mic_host_device: label: "マイク (デバイス)" - label_auto_select: "自動選択" - label_host: "ホスト/ドライバー" - label_device: "デバイス" mic_dynamic_energy_threshold: - label_for_automatic: "マイク入力感度の調整 (現在の設定: 自動)" + label_for_automatic: "マイク入力感度の調整 (現在の設定: 自動)" desc_for_automatic: "マイクの入力感度を自動的に調節する。" - label_for_manual: "マイク入力感度の調整 (現在の設定: 手動)" + label_for_manual: "マイク入力感度の調整 (現在の設定: 手動)" desc_for_manual: "スライダーを調整して入力感度を手動で決められます。マイクのアイコンを押すと、実際に声を入力し、音量を確認しながら調節できます。" - error_message: "0 から {{max}} までの数値で設定できます。" speaker_device: label: "スピーカー (デバイス)" - label_auto_select: "自動選択" speaker_dynamic_energy_threshold: - label_for_automatic: "スピーカー入力感度の調整 (現在の設定: 自動)" + label_for_automatic: "スピーカー入力感度の調整 (現在の設定: 自動)" desc_for_automatic: "スピーカーの入力感度を自動的に調節する。" - label_for_manual: "スピーカー入力感度の調整 (現在の設定: 手動)" + label_for_manual: "スピーカー入力感度の調整 (現在の設定: 手動)" desc_for_manual: "スライダーを調整して入力感度を手動で決められます。ヘッドフォンのアイコンを押すと、実際に音声を聞き取り、音量を確認しながら調節できます。" - error_message: "0 から {{max}} までの数値で設定できます。" - no_device_error_message: "スピーカーデバイスが検出されませんでした。" appearance: transparency: @@ -98,7 +113,7 @@ config_page: desc: "ログに表示されるフォントのサイズを、UIサイズを基準にして倍率を変えられます。" send_message_button_type: label: "メッセージ送信ボタン" - hide: "非表示 (エンターキーを使って送信)" + hide: "非表示 (エンターキーを使って送信)" show: "表示" show_and_disable_enter_key: "表示し、エンターキーでの送信を無効" font_family: @@ -108,20 +123,19 @@ config_page: translation: ctranslate2_weight_type: - label: "オフライン翻訳のタイプ" - desc: "翻訳エンジン(オフライン翻訳)で翻訳する際に、使用する翻訳モデルを選択できます。" - small: "通常モデル ({{capacity}})" - large: "高精度モデル ({{capacity}})" + label: "AI翻訳 {{ctranslate2}} のモデルタイプ" + desc: "翻訳エンジン「{{ctranslate2}}」で翻訳する際に、使用する翻訳モデルを選択できます。" + small: "通常モデル ({{capacity}})" + large: "高精度モデル ({{capacity}})" ctranslate2_compute_device: - label: "オフライン翻訳の処理デバイス" + label: "AI翻訳 {{ctranslate2}} の処理デバイス" deepl_auth_key: - label: "DeepL 認証キー" + label: "DeepL API 認証キー" desc: "使用の際は、メイン画面にある {{translator}} をDeepL_APIに変更してください。\n※対応していない言語もあります。" open_auth_key_webpage: "DeepLアカウントページを開く" save: "保存" edit: "編集" auth_key_success: "認証キーの更新が完了しました。" - auth_key_error: "認証キーが間違っているか、API使用制限が上限に達しています。" transcription: section_label_mic: "マイク" @@ -130,15 +144,12 @@ config_page: mic_record_timeout: label: "入力が終了したとみなす無音時間" desc: "無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。" - error_message: "0 以上で 「{{mic_phrase_timeout_label}}」より大きくすることはできません。" mic_phrase_timeout: label: "一度に文字起こしする時間の長さ" desc: "設定された秒数ごとに文字起こし処理が行われます。" - error_message: "0 以上で 「{{mic_record_timeout_label}}」より小さくすることはできません。" mic_max_phrase: label: "送信するまでに保持する単語数" desc: "文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をVRChatへ送信し、ログに表示します。" - error_message: "0以上の数値を設定できます。" mic_word_filter: label: "ワードフィルター" desc: "登録された単語を検出すると、その文章は送信されません。\n「,」カンマで区切ると、まとめて複数の単語を追加できます。\n※重複した単語は登録されません。" @@ -147,22 +158,19 @@ config_page: speaker_record_timeout: label: "入力が終了したとみなす無音時間" desc: "無音を検出し、設定された秒数経過すると、音声入力が終了したとみなします。" - error_message: "0 以上で「{{speaker_phrase_timeout_label}}」より大きくすることはできません。" speaker_phrase_timeout: label: "一度に文字起こしする時間の長さ" desc: "設定された秒数ごとに文字起こし処理が行われます。" - error_message: "0 以上で「{{speaker_record_timeout_label}}」より小さくすることはできません。" speaker_max_phrase: label: "ログとして表示するまでに保持する単語数" desc: "文字起こしされた単語数の下限値で、この数値を超えた場合のみ結果をログに表示します。" - error_message: "0以上の数値を設定できます。" select_transcription_engine: label: "音声認識で使用するエンジン" whisper_weight_type: label: "Whisperモデルのタイプ" desc: "容量が大きいモデルほど精度は高いですが、その分CPUやGPUを占有します。\n※特にmediumより容量の大きいモデルは、CPU/GPUの性能によっては使用すらも困難です。" - model_template: "{{model_name}} モデル ({{capacity}})" - recommended_model_template: "{{model_name}} モデル ({{capacity}}) (推奨)" + model_template: "{{model_name}} モデル ({{capacity}})" + recommended_model_template: "{{model_name}} モデル ({{capacity}}) [推奨]" whisper_compute_device: label: "Whisperで使用する処理デバイス" @@ -188,17 +196,17 @@ config_page: display_duration: "表示時間" fadeout_duration: "フェードアウト時間" common_settings: "共通設定" + tracker: "表示するトラッカーの位置" hmd: "HMD" left_hand: "左手" right_hand: "右手" - tracker: "表示するトラッカーの位置" overlay_show_only_translated_messages: label: "翻訳後のメッセージのみ表示する" others: section_label_sounds: "サウンド" auto_clear_the_message_box: - label: "送信後はチャットボックスを空にする" + label: "送信後はメッセージ入力欄を空にする" send_only_translated_messages: label: "翻訳後のメッセージのみ送信する" auto_export_message_logs: @@ -221,11 +229,11 @@ config_page: toggle_vrct_visibility: label: "VRCTの最小化/アクティブ化の切り替え" toggle_translation: - label: "{{translation}}機能切り替え" + label: "「{{translation}}」 オン/オフの切り替え" toggle_transcription_send: - label: "{{transcription_send}}機能切り替え" + label: "「{{transcription_send}}」 オン/オフの切り替え" toggle_transcription_receive: - label: "{{transcription_receive}}機能切り替え" + label: "「{{transcription_receive}}」 オン/オフの切り替え" advanced_settings: osc_ip_address: diff --git a/locales/ko.yml b/locales/ko.yml index 6011d3ef..139f41a7 100644 --- a/locales/ko.yml +++ b/locales/ko.yml @@ -6,18 +6,40 @@ common: go_back_button_label: "돌아가기" +common_error: + no_device_mic: "마이크 디바이스를 찾지 못했습니다." + no_device_speaker: "스피커 디바이스를 찾지 못했습니다." + threshold_invalid_value: + failed_download_weight_ctranslate2: + failed_download_weight_whisper: + translation_limit: + deepl_auth_key_invalid_length: + deepl_auth_key_failed_authentication: "인증키가 잘못되었거나 API 사용 제한이 상한에 도달했습니다." + + invalid_value_mic_record_timeout: "0 이상에서 '{{mic_phrase_timeout_label}}'보다 클 수 없습니다." + invalid_value_mic_phrase_timeout: "0 이상에서 '{{mic_record_timeout_label}}'보다 작을 수 없습니다." + invalid_value_mic_max_phrase: "0 이상의 숫자만 설정할 수 있습니다." + + invalid_value_speaker_record_timeout: "0 이상에서 '{{speaker_phrase_timeout_label}}'보다 클 수 없습니다." + invalid_value_speaker_phrase_timeout: "0 이상에서 '{{speaker_record_timeout_label}}'보다 작을 수 없습니다." + invalid_value_speaker_max_phrase: "0 이상의 숫자만 설정할 수 있습니다." + main_page: translation: "번역" transcription_send: "음성인식 (마이크)" transcription_receive: "음성인식 (스피커)" foreground: "항상 위로" + language_settings: "언어 설정" your_language: "당신의 언어" translate_each_other_label: "양방향으로 번역" swap_button_label: "언어 교체" target_language: "상대방의 언어" translator: "번역 엔진" - translator_ctranslate2: "오프라인 번역 (기본값)" + translator_label_default: "기본값" + + translator_selector: + is_selected_same_language: message_log: all: "전체" @@ -25,6 +47,9 @@ main_page: received: "수신" system: "시스템" + show_resend_button: + resend_button_on_hover_desc: + state_text_enabled: "Enabled" state_text_disabled: "Disabled" @@ -36,31 +61,39 @@ main_page: updating: "업데이트 중..." update_modal: - update_software: "새 버전을 다운로드하고 재시작합니다.\n조금 시간이 걸립니다. 지금 시작할까요?" - deny_update_software: "나중에 하기" - accept_update_software: "업데이트 및 재시작" - + cpu_desc: + cuda_desc: + cuda_compare_cpu_desc: + cuda_disk_space_desc: + close_modal: + download_latest_and_restart: + is_latest_version_already: + is_current_compute_device: config_page: version: "버전 {{version}}" + model_download_button_label: side_menu_labels: + device: appearance: "모양" translation: "번역" transcription: "음성인식" others: "기타" + hotkeys: advanced_settings: "고급 설정" device: - mic_host: - label: "마이크 호스트/드라이버" - mic_device: + check_volume: + label_auto_select: + label_host: "호스트/드라이버" + label_device: + mic_host_device: label: "마이크 장치" mic_dynamic_energy_threshold: label_for_automatic: "음성 입력 최소 볼륨 (현재 설정: 자동)" desc_for_automatic: "마이크의 입력 감도를 자동으로 조절합니다." label_for_manual: "음성 입력 최소 볼륨 (현재 설정: 수동)" desc_for_manual: "슬라이더를 움직여 입력 감도를 수동으로 조절합니다. 마이크 아이콘을 누르면 실제 음성의 볼륨을 확인하며 감도를 조절할 수 있습니다." - error_message: "0에서 {{max}}까지의 숫자로만 설정할 수 있습니다." speaker_device: label: "스피커 장치" speaker_dynamic_energy_threshold: @@ -68,8 +101,6 @@ config_page: desc_for_automatic: "스피커의 입력 감도를 자동으로 조절합니다." label_for_manual: "음성 입력 최소 볼륨 (현재 설정: 수동)" desc_for_manual: "슬라이더를 움직여 입력 감도를 수동으로 조절합니다. 헤드폰 아이콘을 누르면 실제 음성의 볼륨을 확인하며 감도를 조절할 수 있습니다." - error_message: "0에서 {{max}}까지의 숫자로만 설정할 수 있습니다." - no_device_error_message: "스피커 디바이스를 찾지 못했습니다." appearance: transparency: @@ -96,28 +127,29 @@ config_page: desc: "오프라인 번역 시의 번역 모델을 변경합니다." small: "일반 모델 ({{capacity}})" large: "정밀 모델 ({{capacity}})" + ctranslate2_compute_device: + label: deepl_auth_key: label: "DeepL 인증키" desc: "사용시 메인화면에 있는 {{translator}}를 DeepL_API로 변경해 주세요.\n지원하지 않는 언어도 있습니다." open_auth_key_webpage: "DeepL 계정 페이지 열기" + save: + edit: auth_key_success: "인증키 갱신이 완료되었습니다." - auth_key_error: "인증키가 잘못되었거나 API 사용 제한이 상한에 도달했습니다." transcription: section_label_mic: "마이크" section_label_speaker: "스피커" + section_label_transcription_engines: mic_record_timeout: label: "최대 무음 시간" desc: "무음을 감지하고 설정된 시간(초)만큼의 시간이 지나면 음성 입력이 종료된 것으로 판단합니다." - error_message: "0 이상에서 '{{mic_phrase_timeout_label}}'보다 클 수 없습니다." mic_phrase_timeout: label: "최대 인식 시간" desc: "설정된 초 단위로 음성인식 처리가 이루어집니다." - error_message: "0 이상에서 '{{mic_record_timeout_label}}'보다 작을 수 없습니다." mic_max_phrase: label: "최대 입력 절(phrases) 수" desc: "인식된 단어 수의 하한값으로, 이 수치를 초과하는 경우에만 결과를 VRChat으로 전송하고 로그에 표시합니다." - error_message: "0 이상의 숫자만 설정할 수 있습니다." mic_word_filter: label: "단어 필터" desc: "등록된 단어가 감지되면 해당 문장은 전송되지 않습니다.\n',' 쉼표로 구분하면 여러 단어를 추가할 수 있습니다.\n* 중복된 단어는 등록되지 않습니다." @@ -126,38 +158,82 @@ config_page: speaker_record_timeout: label: "최대 무음 시간" desc: "무음을 감지하고 설정된 시간(초)만큼의 시간이 지나면 음성 입력이 종료된 것으로 판단합니다." - error_message: "0 이상에서 '{{speaker_phrase_timeout_label}}'보다 클 수 없습니다." speaker_phrase_timeout: label: "최대 인식 시간" desc: "설정된 초 단위로 음성인식 처리가 이루어집니다." - error_message: "0 이상에서 '{{speaker_record_timeout_label}}'보다 작을 수 없습니다." speaker_max_phrase: label: "최대 입력 절(phrases) 수" desc: "식된 단어 수의 하한값으로, 이 수치를 초과하는 경우에만 결과를 로그에 표시합니다." - error_message: "0 이상의 숫자만 설정할 수 있습니다." - use_whisper_feature: - label: "음성 인식에 Whisper 모델을 사용" - desc: "일부 언어에서는 음성 인식의 정확도가 향상될 수 있어요. 음성 인식 중 CPU 사용률이 올라가기 때문에 사용하시는 PC의 사양을 고려하여 이 기능을 사용해주세요." + select_transcription_engine: + label: whisper_weight_type: label: "Whisper 모델 타입" - # desc: "기본적으로 용량이 많은 모델일수록 정밀도는 높지만, 음성 인식의 시간이 늘어나며 CPU 사용률도 늘어나요.각 모델의 설명은 문서를 참조해주세요.\n※특히 medium보다 용량이 큰 모델은 CPU의 성능에 따라서는 사용조차 어려울 수 있어요. " + desc: model_template: "{{model_name}} 모델 ({{capacity}})" recommended_model_template: "{{model_name}} 모델 ({{capacity}}) (권장)" + whisper_compute_device: + label: + + vr: + single_line: + multi_lines: + overlay_enable: + restore_default_settings: + position: + rotation: + x_position: + y_position: + z_position: + x_rotation: + y_rotation: + z_rotation: + sample_text_button: + start: + stop: + sample_text: + opacity: + ui_scaling: + display_duration: + fadeout_duration: + common_settings: + tracker: + hmd: + left_hand: + right_hand: + overlay_show_only_translated_messages: + label: others: + section_label_sounds: auto_clear_the_message_box: label: "챗박스 자동 삭제" send_only_translated_messages: label: "번역된 메시지만 전송" - notice_xsoverlay: - label: "XSOverlay에서 알림 수신 기능 활성화" - desc: "수신된 메시지를 XSOverlay의 기능을 통해 알림으로 받아볼 수 있습니다." auto_export_message_logs: label: "대화 로그 자동 저장" desc: "logs 폴더에 텍스트 파일로 로그가 저장됩니다." + vrc_mic_mute_sync: + label: + desc: send_message_to_vrc: label: "VRChat에 메시지 전송" desc: "VRChat에 메시지를 보내지 않고 사용할 수 있는 방법이 있지만 지원되지 않습니다. VRChat에 메시지를 보내려면 이 기능을 활성화하세요." + notification_vrc_sfx: + label: + desc: + send_received_message_to_vrc: + label: + desc: + + hotkeys: + toggle_vrct_visibility: + label: + toggle_translation: + label: + toggle_transcription_send: + label: + toggle_transcription_receive: + label: advanced_settings: osc_ip_address: @@ -165,4 +241,6 @@ config_page: osc_port: label: "OSC 포트" open_config_filepath: - label: "설정 파일 열기" \ No newline at end of file + label: "설정 파일 열기" + switch_compute_device: + label: \ No newline at end of file diff --git a/locales/zh-Hans.yml b/locales/zh-Hans.yml index 0f317e5d..0a13c7f8 100644 --- a/locales/zh-Hans.yml +++ b/locales/zh-Hans.yml @@ -6,18 +6,40 @@ common: go_back_button_label: "返回" +common_error: + no_device_mic: # 未检测到他人语音 ? + no_device_speaker: # 未检测到他人语音 ? + threshold_invalid_value: # 数值应为 {{min}} 至 {{max}} 之间。 ? 设定的数值从 {{min}} 到 {{max}} ? + failed_download_weight_ctranslate2: + failed_download_weight_whisper: + translation_limit: + deepl_auth_key_invalid_length: + deepl_auth_key_failed_authentication: "授权密匙错误或已达API使用上限" + + invalid_value_mic_record_timeout: "数值应为 0 至 「{{mic_phrase_timeout_label}}」" + invalid_value_mic_phrase_timeout: "转录间隔时间大于0秒且不能小于「{{mic_record_timeout_label}}」" + invalid_value_mic_max_phrase: "数值应为 0 以上" + + invalid_value_speaker_record_timeout: "数值应为 0 至 「{{speaker_phrase_timeout_label}}」" + invalid_value_speaker_phrase_timeout: "转录间隔时间大于0秒且不能小于「{{speaker_record_timeout_label}}」" + invalid_value_speaker_max_phrase: "数值应为 0 以上" + main_page: translation: "翻译" transcription_send: "你的语音转文字" transcription_receive: "他人语音转文字" foreground: "顶层显示" + language_settings: "语言设定" your_language: "你的语言" translate_each_other_label: "双向翻译" swap_button_label: "互换" target_language: "目标语言" translator: "翻译器" - translator_ctranslate2: "离线翻译(默认)" + translator_label_default: "默认" + + translator_selector: + is_selected_same_language: message_log: all: "全部" @@ -25,6 +47,9 @@ main_page: received: "接受" system: "系统" + show_resend_button: + resend_button_on_hover_desc: + state_text_enabled: "启用" state_text_disabled: "停用" @@ -36,31 +61,39 @@ main_page: updating: "更新中..." update_modal: - update_software_desc: "下载新版本并自动启动\n会花少许时间,现在更新吗?" - deny_update_software: "稍后再说" - accept_update_software: "更新后自动启动" - + cpu_desc: + cuda_desc: + cuda_compare_cpu_desc: + cuda_disk_space_desc: + close_modal: + download_latest_and_restart: + is_latest_version_already: + is_current_compute_device: config_page: version: "版本 {{version}}" + model_download_button_label: side_menu_labels: + device: appearance: "外观" translation: "翻译" transcription: "转录" others: "其他" + hotkeys: advanced_settings: "高级设置" device: - mic_host: - label: "麦克风(host/driver)" - mic_device: + check_volume: "Check Volume" + label_auto_select: "Auto Select" + label_host: "Host/Driver" + label_device: "Device" + mic_host_device: label: "麦克风 (设备)" mic_dynamic_energy_threshold: label_for_automatic: "麦克风输入阈值(当前设置:自动)" desc_for_automatic: "自动调整麦克风输入阈值" label_for_manual: "麦克风输入阈值(当前设置:手动)" desc_for_manual: "使用滑杆手动确定麦克风输入灵敏度。按下麦克风图标输入语音,并在监控音量的同时调节灵敏度。" - error_message: "数值应为 0 至 {{max}} 之间。" speaker_device: label: "他人语音 (设备)" speaker_dynamic_energy_threshold: @@ -68,8 +101,6 @@ config_page: desc_for_automatic: "自动调节他人语音接收阈值" label_for_manual: "他人语音接收阈值(当前设置:手动)" desc_for_manual: "使用滑杆手动调整他人语音接收阈值.在按下耳机按钮时,请根据实际听到的声音调整该大小" - error_message: "设定的数值从 0 到 {{max}}" - no_device_error_message: "未检测到他人语音" appearance: transparency: @@ -96,28 +127,29 @@ config_page: desc: "可以选择用于离线翻译的翻译模型" small: "普通模型 ({{capacity}})" large: "高精度模型 ({{capacity}})" + ctranslate2_compute_device: + label: deepl_auth_key: label: "DeepL 授权密匙" desc: "在使用的时候,使用时请在主屏幕上通过 DeepL_API 选择 {{translator}}\n※某些语言可能不支持" open_auth_key_webpage: "打开DeepL账号页面" + save: + edit: auth_key_success: "授权密匙认证完成。" - auth_key_error: "授权密匙错误或已达API使用上限" transcription: section_label_mic: "你的麦克风" section_label_speaker: "他人声音" + section_label_transcription_engines: mic_record_timeout: label: "语音输入结束后的静音时间" desc: "当检测到静音并经过设定的秒数后,语音输入即被视为完成。" - error_message: "数值应为 0 至 [{{mic_phrase_timeout_label}}]" mic_phrase_timeout: label: "转录间隔" desc: "在经过设定的时间后执行转录" - error_message: "转录间隔时间大于0秒且不能小于「{{mic_record_timeout_label}}」" mic_max_phrase: label: "麦克风发送时的最小单词数" desc: "转录字数的下限,只有超过这个数字,才会记录翻译结果并发送到VRC" - error_message: "数值应为 0 以上" mic_word_filter: label: "单词过滤器" desc: "检测出被记录的单词时,不会发送这段话\n如要添加多个单词,可以用逗号来分割\n※不会记录重复的单词" @@ -126,40 +158,53 @@ config_page: speaker_record_timeout: label: "语音接收结束后的静音时间" desc: "当检测到静音并经过设定的秒数后,语音接收即被视为完成。" - error_message: "数值应为 0 至 「{{speaker_phrase_timeout_label}}」" speaker_phrase_timeout: label: "转录间隔" desc: "在经过设定的时间后执行转录" - error_message: "转录间隔时间大于0秒且不能小于「{{speaker_record_timeout_label}}」" speaker_max_phrase: label: "语音接收时的最小单词数" desc: "转录字数的下限,只有超过这个数字,才会记录转录结果" - error_message: "数值应为 0 以上" - use_whisper_feature: - label: "使用Whisper模型翻译" - desc: "在某些语言中,语音识别的准确性可能会提高.语音识别的过程中,CPU占有率可能会提高,请根据你的pc性能来决定是否使用它." + select_transcription_engine: + label: whisper_weight_type: label: "选择某个Whisper模型" - # desc: |- - # 通常来说,容量越大的模型精度也会越高,但也会增加文字显示所需要的时间和CPU的使用率。请浏览各个模型的文档 - # ※特别是大于medium容量的模型、因CPU性能原因甚至无法使用。 + desc: model_template: "{{model_name}} 模型 ({{capacity}})" recommended_model_template: "{{model_name}} 模型 ({{capacity}}) (推荐)" + whisper_compute_device: + label: vr: + single_line: + multi_lines: + overlay_enable: restore_default_settings: "恢复默认设置" - opacity: "透明度" - ui_scaling: "大小" + position: + rotation: x_position: "X轴(左右)" y_position: "Y轴(上下)" z_position: "Z轴(前后)" x_rotation: "X轴旋转" y_rotation: "Y轴旋转" z_rotation: "Z轴旋转" + sample_text_button: + start: + stop: + sample_text: + opacity: "透明度" + ui_scaling: "大小" display_duration: "显示持续时间" fadeout_duration: "渐隐持续时间" + common_settings: + tracker: + hmd: + left_hand: + right_hand: + overlay_show_only_translated_messages: + label: others: + section_label_sounds: auto_clear_the_message_box: label: "发言后自动清空chatbox" send_only_translated_messages: @@ -173,6 +218,22 @@ config_page: send_message_to_vrc: label: "发送信息至VRChat" desc: "不发送信息至VRChat的情况下也能使用它,但该功能现在并未完成.在想要发送信息时,请不要忘记打开这个功能." + notification_vrc_sfx: + label: + desc: + send_received_message_to_vrc: + label: + desc: + + hotkeys: + toggle_vrct_visibility: + label: + toggle_translation: + label: + toggle_transcription_send: + label: + toggle_transcription_receive: + label: advanced_settings: osc_ip_address: @@ -180,4 +241,6 @@ config_page: osc_port: label: "OSC 端口" open_config_filepath: - label: "打开设置文件" \ No newline at end of file + label: "打开设置文件" + switch_compute_device: + label: \ No newline at end of file diff --git a/locales/zh-Hant.yml b/locales/zh-Hant.yml index cc56ca58..4fd2fce4 100644 --- a/locales/zh-Hant.yml +++ b/locales/zh-Hant.yml @@ -6,18 +6,40 @@ common: go_back_button_label: "返回" +common_error: + no_device_mic: + no_device_speaker: "未偵測到喇叭裝置。" + threshold_invalid_value: "可以設置 {{min}} 到 {{max}} 之間的值。" + failed_download_weight_ctranslate2: + failed_download_weight_whisper: + translation_limit: + deepl_auth_key_invalid_length: + deepl_auth_key_failed_authentication: "授權金鑰錯誤或已達使用上限。" + + invalid_value_mic_record_timeout: "不能大於「{{mic_phrase_timeout_label}}」,應為 0 或更高。" + invalid_value_mic_phrase_timeout: "不能小於「{{mic_record_timeout_label}}」,應為 0 或更高。" + invalid_value_mic_max_phrase: "可以設置 0 或更高的數值。" + + invalid_value_speaker_record_timeout: "不能大於「{{speaker_phrase_timeout_label}}」,應為 0 或更高。" + invalid_value_speaker_phrase_timeout: "不能小於「{{speaker_record_timeout_label}}」,應為 0 或更高。" + invalid_value_speaker_max_phrase: "可以設置 0 或更高的數值。" + main_page: translation: "翻譯" transcription_send: "麥克風轉文字" transcription_receive: "喇叭轉文字" foreground: "最上層顯示" + language_settings: "語言設定" your_language: "你的語言" translate_each_other_label: "互相翻譯" swap_button_label: "交換語言" target_language: "目標語言" translator: "翻譯器" - translator_ctranslate2: "離線翻譯(預設)" + translator_label_default: "預設" + + translator_selector: + is_selected_same_language: message_log: all: "全部" @@ -25,6 +47,9 @@ main_page: received: "已接收" system: "系統" + show_resend_button: + resend_button_on_hover_desc: + state_text_enabled: "啟用" state_text_disabled: "停用" @@ -36,32 +61,39 @@ main_page: updating: "正在更新..." update_modal: - update_software_desc: "下載新版本並自動更新 VRCT。\n會花一些時間,現在更新嗎?" - deny_update_software: "稍後再說" - accept_update_software: "更新" - + cpu_desc: + cuda_desc: + cuda_compare_cpu_desc: + cuda_disk_space_desc: + close_modal: + download_latest_and_restart: + is_latest_version_already: + is_current_compute_device: config_page: version: "版本 {{version}}" + model_download_button_label: side_menu_labels: + device: appearance: "外觀" translation: "翻譯" transcription: "轉錄" - vr: "VR" others: "其他" + hotkeys: advanced_settings: "進階設定" device: - mic_host: - label: "麥克風 Host/Driver" - mic_device: + check_volume: "Check Volume" + label_auto_select: "Auto Select" + label_host: "Host/Driver" + label_device: "Device" + mic_host_device: label: "麥克風裝置" mic_dynamic_energy_threshold: label_for_automatic: "麥克風能量閾值(當前設置:自動)" desc_for_automatic: "自動判定麥克風輸入靈敏度。" label_for_manual: "麥克風能量閾值(當前設置:手動)" desc_for_manual: "使用滑桿調整麥克風輸入靈敏度,你可以按下麥克風圖示來測試。" - error_message: "可以設置 0 到 {{max}} 之間的值。" speaker_device: label: "喇叭裝置" speaker_dynamic_energy_threshold: @@ -69,8 +101,6 @@ config_page: desc_for_automatic: "自動確定喇叭輸入靈敏度。" label_for_manual: "喇叭能量閾值(當前設置:手動)" desc_for_manual: "使用滑桿調整喇叭輸入靈敏度,你可以按下喇叭圖示來測試。" - error_message: "可以設置 0 到 {{max}} 之間的值。" - no_device_error_message: "未偵測到喇叭裝置。" appearance: transparency: @@ -97,28 +127,29 @@ config_page: desc: "你可以選擇用於離線翻譯引擎的翻譯模型。" small: "基本模型({{capacity}})" large: "高準確率模型({{capacity}})" + ctranslate2_compute_device: + label: deepl_auth_key: label: "DeepL 授權金鑰" desc: "使用 DeepL API 時請在主螢幕選擇 {{translator}}。※可能不支援某些語言。" open_auth_key_webpage: "打開 DeepL 帳號頁面" + save: + edit: auth_key_success: "授權金鑰更新完成。" - auth_key_error: "授權金鑰錯誤或已達使用上限。" transcription: section_label_mic: "麥克風" section_label_speaker: "喇叭" + section_label_transcription_engines: mic_record_timeout: label: "麥克風音訊 - 判定結束時間" desc: "麥克風未收到音訊後,結束一段話的判定時間(秒)。" - error_message: "不能大於「{{mic_phrase_timeout_label}}」,應為 0 或更高。" mic_phrase_timeout: label: "麥克風音訊 - 紀錄間隔時間" desc: "每隔多久要紀錄一次音訊。" - error_message: "不能小於「{{mic_record_timeout_label}}」,應為 0 或更高。" mic_max_phrase: label: "麥克風音訊 - 最大單詞數量" desc: "只有在單詞超過此數量時,才會記錄結果並發送到 VRChat。" - error_message: "可以設置為 0 或更高的數值。" mic_word_filter: label: "麥克風單詞過濾器" desc: "如果偵測到清單內的單詞,則不會發送訊息。要一次新增多個詞語,請用「,」(半形逗號)分隔。\n*重複詞語會被忽略。" @@ -127,47 +158,57 @@ config_page: speaker_record_timeout: label: "喇叭音訊 - 判定結束時間" desc: "偵測到靜音並在指定秒數後認為喇叭輸入已結束。(秒)" - error_message: "不能大於「{{speaker_phrase_timeout_label}}」,應為 0 或更高。" speaker_phrase_timeout: label: "喇叭音訊 - 紀錄間隔時間" desc: "以指定秒數間隔進行轉錄處理。" - error_message: "不能小於「{{speaker_record_timeout_label}}」,應為 0 或更高。" speaker_max_phrase: label: "喇叭音訊 - 最大單詞數量" desc: "只有在單詞超過此數量時,才會記錄結果並發送到 VRChat。" - error_message: "可以設置 0 或更高的數值。" - use_whisper_feature: - label: "使用 Whisper 模型進行轉錄" - desc: "在某些語言中,語音識別的準確性可能會提高。使用語音識別時,CPU使用率會增加,請根據你的PC規格考慮是否使用此功能。" + select_transcription_engine: + label: whisper_weight_type: label: "選擇 Whisper 模型" - # desc: |- - # 一般來說,容量較大的模型往往具有更高的準確性,但這也導致轉錄時間較長和CPU使用率增加。請參考文檔了解各模型的說明。 - # ※特別是超過中等大小的模型,根據CPU性能可能難以運行。 + desc: model_template: "{{model_name}}模型({{capacity}})" recommended_model_template: "{{model_name}}模型({{capacity}})(推薦)" + whisper_compute_device: + label: vr: + single_line: + multi_lines: + overlay_enable: restore_default_settings: "恢復預設設定" - opacity: "透明度" - ui_scaling: "介面縮放" + position: + rotation: x_position: "X軸(左右)" y_position: "Y軸(上下)" z_position: "Z軸(前後)" x_rotation: "X軸旋轉" y_rotation: "Y軸旋轉" z_rotation: "Z軸旋轉" + sample_text_button: + start: + stop: + sample_text: + opacity: "透明度" + ui_scaling: "介面縮放" display_duration: "顯示持續時間" fadeout_duration: "淡出持續時間" + common_settings: + tracker: + hmd: + left_hand: + right_hand: + overlay_show_only_translated_messages: + label: others: + section_label_sounds: auto_clear_the_message_box: label: "自動清除 Chatbox" send_only_translated_messages: label: "僅發送翻譯訊息" - notice_xsoverlay: - label: "XSOverlay 通知" - desc: "從 XSOverlay 的通知功能接收訊息。" auto_export_message_logs: label: "自動匯出訊息記錄" desc: "自動將對話訊息匯出為文字文件。" @@ -177,6 +218,22 @@ config_page: send_message_to_vrc: label: "發送訊息到 VRChat" desc: "當你打算向 VRChat 發送訊息時啟用此功能。" + notification_vrc_sfx: + label: + desc: + send_received_message_to_vrc: + label: + desc: + + hotkeys: + toggle_vrct_visibility: + label: + toggle_translation: + label: + toggle_transcription_send: + label: + toggle_transcription_receive: + label: advanced_settings: osc_ip_address: @@ -184,4 +241,6 @@ config_page: osc_port: label: "OSC 端口" open_config_filepath: - label: "打開設定文件" \ No newline at end of file + label: "打開設定文件" + switch_compute_device: + label: \ No newline at end of file diff --git a/src-python/controller.py b/src-python/controller.py index bb94bf26..f0356287 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -150,7 +150,7 @@ class Controller: 400, self.run_mapping["error_device"], { - "message":"No mic device detected", + "message":"No Speaker device detected", "data": None }, ) @@ -777,7 +777,7 @@ class Controller: response = { "status":400, "result":{ - "message":"Speaker energy threshold value is out of range", + "message":"Mic energy threshold value is out of range", "data": config.MIC_THRESHOLD } } diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/word_filter/WordFilter.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/word_filter/WordFilter.jsx index d2a4a62d..bc338d4b 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/word_filter/WordFilter.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/word_filter/WordFilter.jsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import styles from "./WordFilter.module.scss"; import { _Entry } from "../_atoms/_entry/_Entry"; import { useState } from "react"; @@ -5,6 +6,8 @@ import { useStore_IsOpenedMicWordFilterList } from "@store"; import { useMicWordFilterList } from "@logics_configs"; export const WordFilter = () => { + const { t } = useTranslation(); + const [input_value, setInputValue] = useState(""); const { currentMicWordFilterList, updateMicWordFilterList, setMicWordFilterList } = useMicWordFilterList(); const { currentIsOpenedMicWordFilterList, updateIsOpenedMicWordFilterList } = useStore_IsOpenedMicWordFilterList(); @@ -82,7 +85,7 @@ export const WordFilter = () => { }
<_Entry width="30rem" onChange={onChangeEntry} ui_variable={input_value}/> - +
); @@ -121,8 +124,6 @@ const WordFilterItem = (props) => { ); }; -import { useTranslation } from "react-i18next"; - import ArrowLeftSvg from "@images/arrow_left.svg?react"; export const WordFilterListToggleComponent = (props) => { const { t } = useTranslation(); diff --git a/src-ui/app/config_page/setting_section/setting_box/device/Device.jsx b/src-ui/app/config_page/setting_section/setting_box/device/Device.jsx index 958cddf8..56b11398 100644 --- a/src-ui/app/config_page/setting_section/setting_box/device/Device.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/device/Device.jsx @@ -82,7 +82,7 @@ const Mic_Container = () => {
-

{t("config_page.device.mic_host_device.label_auto_select")}

+

{t("config_page.device.label_auto_select")}

{
-

{t("config_page.device.mic_host_device.label_host")}

+

{t("config_page.device.label_host")}

{
-

{t("config_page.device.mic_host_device.label_device")}

+

{t("config_page.device.label_device")}

{
-

{t("config_page.device.speaker_device.label_auto_select")}

+

{t("config_page.device.label_auto_select")}

{
-

{t("config_page.device.mic_host_device.label_device")}

+

{t("config_page.device.label_device")}

{ const MicRecordTimeout_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentMicRecordTimeout, setMicRecordTimeout } = useMicRecordTimeout(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setMicRecordTimeout(value); - } + + const selectFunction = (selected_data) => { + setMicRecordTimeout(selected_data.selected_id); }; - useEffect(()=> { - setUiVariable(currentMicRecordTimeout.data); - }, [currentMicRecordTimeout]); - return ( - ); }; const MicPhraseTimeout_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentMicPhraseTimeout, setMicPhraseTimeout } = useMicPhraseTimeout(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setMicPhraseTimeout(value); - } + + const selectFunction = (selected_data) => { + setMicPhraseTimeout(selected_data.selected_id); }; - useEffect(()=> { - setUiVariable(currentMicPhraseTimeout.data); - }, [currentMicPhraseTimeout]); - return ( - ); }; const MicMaxWords_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentMicMaxWords, setMicMaxWords } = useMicMaxWords(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setMicMaxWords(value); - } + + const selectFunction = (selected_data) => { + setMicMaxWords(selected_data.selected_id); }; - useEffect(()=> { - setUiVariable(currentMicMaxWords.data); - }, [currentMicMaxWords]); - return ( - ); }; @@ -167,82 +144,60 @@ const Speaker_Container = () => { const SpeakerRecordTimeout_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentSpeakerRecordTimeout, setSpeakerRecordTimeout } = useSpeakerRecordTimeout(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setSpeakerRecordTimeout(value); - } + + const selectFunction = (selected_data) => { + setSpeakerRecordTimeout(selected_data.selected_id); }; - useEffect(()=> { - setUiVariable(currentSpeakerRecordTimeout.data); - }, [currentSpeakerRecordTimeout]); - return ( - ); }; const SpeakerPhraseTimeout_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentSpeakerPhraseTimeout, setSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setSpeakerPhraseTimeout(value); - } + + const selectFunction = (selected_data) => { + setSpeakerPhraseTimeout(selected_data.selected_id); }; - - useEffect(()=> { - setUiVariable(currentSpeakerPhraseTimeout.data); - }, [currentSpeakerPhraseTimeout]); - return ( - ); }; const SpeakerMaxWords_Box = () => { const { t } = useTranslation(); - const [ui_variable, setUiVariable] = useState(""); const { currentSpeakerMaxWords, setSpeakerMaxWords } = useSpeakerMaxWords(); - const onChangeFunction = (e) => { - const value = e.currentTarget.value; - if (value === "") { - setUiVariable(""); - } else { - setUiVariable(value); - setSpeakerMaxWords(value); - } + + const selectFunction = (selected_data) => { + setSpeakerMaxWords(selected_data.selected_id); }; - useEffect(()=> { - setUiVariable(currentSpeakerMaxWords.data); - }, [currentSpeakerMaxWords]); - return ( - ); }; diff --git a/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx b/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx index b1bbc0fc..63f0630d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/translation/Translation.jsx @@ -56,7 +56,10 @@ const CTranslate2WeightType_Box = () => { return ( <> { const { currentComputeMode } = useComputeMode(); + const ctranslate2_compute_device_label = t("config_page.translation.ctranslate2_compute_device.label", { + ctranslate2: "Ctranslate2" + }); if (currentComputeMode.data === "cpu") { return ( { return ( { [styles["is_selected"]]: (currentSelectedConfigTabId.data === props.tab_id) ? true : false }); + const getLabel = () => { + if (props.tab_id === "vr") return "VR"; + if (props.tab_id === "supporters") return "Supporters"; + if (props.tab_id === "about_vrct") return "About VRCT"; + return t(`config_page.side_menu_labels.${props.tab_id}`); + }; + return (
-

{t(`config_page.side_menu_labels.${props.tab_id}`)}

+

{getLabel()}

); diff --git a/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss b/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss index 383ef8cb..2e27f0a8 100644 --- a/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss +++ b/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss @@ -9,6 +9,7 @@ flex-direction: column; justify-content: space-between; overflow-y: auto; + // overflow-x: hidden; height: 100%; max-height: 60rem; } @@ -59,7 +60,9 @@ } .tab_text { + overflow: hidden; font-size: 1.6rem; + text-overflow: ellipsis; } .separated_tabs_wrapper { diff --git a/src-ui/app/main_page/main_section/language_selector/language_selector_top_bar/LanguageSelectorTopBar.module.scss b/src-ui/app/main_page/main_section/language_selector/language_selector_top_bar/LanguageSelectorTopBar.module.scss index b2e7d219..9d0bc94a 100644 --- a/src-ui/app/main_page/main_section/language_selector/language_selector_top_bar/LanguageSelectorTopBar.module.scss +++ b/src-ui/app/main_page/main_section/language_selector/language_selector_top_bar/LanguageSelectorTopBar.module.scss @@ -16,7 +16,12 @@ position: absolute; left: 0; background-color: var(--dark_800_color); - padding: 1.2rem; + padding: 0 2rem 0 1.6rem; + height: 100%; + min-width: 8rem; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; &:hover{ background-color: var(--dark_750_color); diff --git a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.jsx b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.jsx index e6b68cd4..7a869e8a 100644 --- a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.jsx +++ b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.jsx @@ -16,11 +16,12 @@ export const TranslatorSelectorOpenButton = () => { currentSelectedTranslationEngines, } = useLanguageSettings(); - const new_labels = [ - {id: "CTranslate2", label: t("main_page.translator_ctranslate2")} - ]; + // const new_labels = [ + // {id: "CTranslate2", label: "AI\nCTranslate2"} + // ]; - const translation_engines = updateLabelsById(currentTranslationEngines.data, new_labels); + const translation_engines = currentTranslationEngines.data; + // const translation_engines = updateLabelsById(currentTranslationEngines.data, new_labels); const selected_engine_id = currentSelectedTranslationEngines.data[currentSelectedPresetTabNumber.data]; @@ -74,7 +75,7 @@ export const TranslatorSelectorOpenButton = () => { return (
-

{t("main_page.translator")}:

+

{t("main_page.translator")}:

{selected_label}

{is_selected_same_language ? diff --git a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.jsx b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.jsx index 45a45fad..cdbc159e 100644 --- a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.jsx +++ b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.jsx @@ -15,12 +15,13 @@ export const TranslatorSelector = ({selected_id, translation_engines, is_selecte
{columns.map((column, column_index) => (
- {column.map(({ id, label, is_available }) => ( + {column.map(({ id, label, is_available, is_default }) => ( ))} @@ -33,7 +34,7 @@ export const TranslatorSelector = ({selected_id, translation_engines, is_selecte {t("main_page.translator_selector.is_selected_same_language", { your_language: t("main_page.your_language"), target_language: t("main_page.target_language"), - translator_ctranslate2: t("main_page.translator_ctranslate2"), + ctranslate2: "CTranslate2", })}

@@ -45,6 +46,7 @@ export const TranslatorSelector = ({selected_id, translation_engines, is_selecte }; const TranslatorBox = (props) => { + const { t } = useTranslation(); const { setSelectedTranslationEngines} = useLanguageSettings(); const { updateIsOpenedTranslatorSelector} = useStore_IsOpenedTranslatorSelector(); @@ -53,6 +55,10 @@ const TranslatorBox = (props) => { { [styles.is_selected]: props.is_selected }, { [styles.is_available]: props.is_available } ); + const label_default_class_name = clsx( + styles.label_default, + { [styles.is_selected]: props.is_selected }, + ); const selectTranslator = () => { if (props.is_selected === false) { @@ -60,9 +66,11 @@ const TranslatorBox = (props) => { } updateIsOpenedTranslatorSelector(false); }; + return (

{props.label}

+ {props.is_default &&

{t("main_page.translator_label_default")}

}
); }; \ No newline at end of file diff --git a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss index efdd0854..b73fef4c 100644 --- a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss +++ b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss @@ -17,26 +17,26 @@ } .wrapper { - // padding: 1rem; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; - gap: 1rem; + gap: 1.4rem; } .column_wrapper { display: flex; justify-content: center; align-items: center; - gap: 1rem; + gap: 1.2rem; } -$box_size: 6.8rem; +$box_size: 6.2rem; .box { - width: $box_size; + position: relative; + width: 9.4rem; height: $box_size; background-color: var(--dark_875_color); display: flex; @@ -44,17 +44,17 @@ $box_size: 6.8rem; align-items: center; white-space: pre-wrap; text-align: center; - border-radius: 0.6rem; + border-radius: 0.2rem; cursor: pointer; &:hover { background-color: var(--dark_825_color); } &:active { background-color: var(--dark_900_color); - border: 0.1rem solid var(--primary_300_color); + outline: 0.1rem solid var(--primary_300_color); } &.is_selected { - border: 0.2rem solid var(--primary_300_color); + outline: 0.2rem solid var(--primary_300_color); } &:not(.is_available) { pointer-events: none; @@ -69,6 +69,22 @@ $box_size: 6.8rem; font-size: 1.4rem; } +.label_default { + background-color: var(--dark_875_color); + outline: 0.1rem solid var(--dark_1000_color); + padding: 0.2rem 0.4rem; + border-radius: 0.2rem; + font-size: 1.2rem; + position: absolute; + top: -0.8rem; + right: -0.8rem; + pointer-events: none; + &.is_selected { + outline: 0.1rem solid var(--primary_300_color); + + } +} + .is_selected_same_language_wrapper { position: absolute; bottom: 0; diff --git a/src-ui/app/main_page/sidebar_section/main_function_switch/MainFunctionSwitch.module.scss b/src-ui/app/main_page/sidebar_section/main_function_switch/MainFunctionSwitch.module.scss index 750505f7..0c1949bf 100644 --- a/src-ui/app/main_page/sidebar_section/main_function_switch/MainFunctionSwitch.module.scss +++ b/src-ui/app/main_page/sidebar_section/main_function_switch/MainFunctionSwitch.module.scss @@ -78,7 +78,9 @@ $pending_label_color: var(--dark_500_color); } .toggle_control { - @include toggle_control_styles; + // @include toggle_control_styles; + @include toggle_control_styles($toggle_width: 3.6rem, $toggle_height: 1.4rem); + display: flex; justify-content: end; align-items: center; diff --git a/src-ui/common_css/mixins.scss b/src-ui/common_css/mixins.scss index 4e563960..b0229e88 100644 --- a/src-ui/common_css/mixins.scss +++ b/src-ui/common_css/mixins.scss @@ -48,35 +48,41 @@ $toggle_width: 4rem; $toggle_height: 1.6rem; $toggle_gutter: 0.1rem; $toggle_radius: 50%; -$toggle_control_speed: .15s; +$toggle_control_speed: 0.15s; $toggle_control_ease: ease-out; -$toggle_radius: calc($toggle_height / 2); -$toggle_control_size: $toggle_height - calc($toggle_gutter * 2); +@mixin toggle_control_styles( + $toggle_width: $toggle_width, + $toggle_height: $toggle_height, + $toggle_gutter: $toggle_gutter, + $toggle_background_color_on: $toggle_background_color_on, + $toggle_background_color_off: $toggle_background_color_off, + $toggle_control_color: $toggle_control_color, + $toggle_control_speed: $toggle_control_speed, + $toggle_control_ease: $toggle_control_ease +) { + $toggle_radius: calc($toggle_height / 2); + $toggle_control_size: calc($toggle_height - ($toggle_gutter * 2)); -@mixin toggle_control_styles { - display: block; - position: relative; - height: 100%; - width: auto; .control { position: relative; - height: $toggle_height; width: $toggle_width; + height: $toggle_height; border-radius: $toggle_radius; background-color: $toggle_background_color_off; + transition: background-color $toggle_control_speed $toggle_control_ease; &:after { content: ""; position: absolute; - left: $toggle_gutter; top: $toggle_gutter; + left: $toggle_gutter; width: $toggle_control_size; height: $toggle_control_size; border-radius: $toggle_radius; background: $toggle_control_color; transition: left $toggle_control_speed $toggle_control_ease; } - &.is_pending:after{ + &.is_pending:after { background-color: var(--dark_600_color); } &.is_hovered { diff --git a/src-ui/logics/_useBackendErrorHandling.js b/src-ui/logics/_useBackendErrorHandling.js new file mode 100644 index 00000000..446330cc --- /dev/null +++ b/src-ui/logics/_useBackendErrorHandling.js @@ -0,0 +1,124 @@ +import { useTranslation } from "react-i18next"; + +import { + useNotificationStatus, +} from "@logics_common"; + +import { + useMicRecordTimeout, + useMicPhraseTimeout, + useMicMaxWords, + + useSpeakerRecordTimeout, + useSpeakerPhraseTimeout, + useSpeakerMaxWords, + + useDeepLAuthKey, +} from "@logics_configs"; +import { ui_configs } from "../ui_configs"; + +export const _useBackendErrorHandling = () => { + const { t } = useTranslation(); + const { showNotification_Error } = useNotificationStatus(); + + const { updateMicRecordTimeout } = useMicRecordTimeout(); + const { updateMicPhraseTimeout } = useMicPhraseTimeout(); + const { updateMicMaxWords } = useMicMaxWords(); + + const { updateSpeakerRecordTimeout } = useSpeakerRecordTimeout(); + const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); + const { updateSpeakerMaxWords } = useSpeakerMaxWords(); + + const { updateDeepLAuthKey } = useDeepLAuthKey(); + + const errorHandling_Backend = ({message, data, endpoint}) => { + switch (message) { + case "No mic device detected": + showNotification_Error(t("common_error.no_device_mic")); + break; + case "No Speaker device detected": + showNotification_Error(t("common_error.no_device_speaker")); + break; + + case "Mic energy threshold value is out of range": + showNotification_Error(t("common_error.threshold_invalid_value", + { min: ui_configs.mic_threshold_min, max: ui_configs.mic_threshold_max }, + )); + break; + case "Speaker energy threshold value is out of range": + showNotification_Error(t("common_error.threshold_invalid_value", + { min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max }, + )); + break; + + case "CTranslate2 weight download error": + showNotification_Error(t("common_error.failed_download_weight_ctranslate2")); + break; + case "Whisper weight download error": + showNotification_Error(t("common_error.failed_download_weight_whisper")); + break; + + case "Translation engine limit error": + showNotification_Error(t("common_error.translation_limit")); + break; + + case "DeepL auth key length is not correct": + updateDeepLAuthKey(data); + showNotification_Error(t("common_error.deepl_auth_key_invalid_length")); + break; + case "Authentication failure of deepL auth key": + updateDeepLAuthKey(data); + showNotification_Error(t("common_error.deepl_auth_key_failed_authentication")); + break; + + case "Mic record timeout value is out of range": + updateMicRecordTimeout(data); + showNotification_Error( + t("common_error.invalid_value_mic_record_timeout", + { mic_phrase_timeout_label: t("config_page.transcription.mic_phrase_timeout.label") } + )); + break; + case "Mic phrase timeout value is out of range": + updateMicPhraseTimeout(data); + showNotification_Error( + t("common_error.invalid_value_mic_phrase_timeout", + { mic_record_timeout_label: t("config_page.transcription.mic_record_timeout.label") } + )); + break; + case "Mic max phrases value is out of range": + updateMicMaxWords(data); + showNotification_Error(t("common_error.invalid_value_mic_max_phrase")); + break; + + + case "Speaker record timeout value is out of range": + updateSpeakerRecordTimeout(data); + showNotification_Error( + t("common_error.invalid_value_speaker_record_timeout", + { speaker_phrase_timeout_label: t("config_page.transcription.speaker_phrase_timeout.label") } + )); + break; + case "Speaker phrase timeout value is out of range": + updateSpeakerPhraseTimeout(data); + showNotification_Error( + t("common_error.invalid_value_speaker_phrase_timeout", + { speaker_record_timeout_label: t("config_page.transcription.speaker_record_timeout.label") } + )); + break; + case "Speaker max phrases value is out of range": + updateSpeakerMaxWords(data); + showNotification_Error(t("common_error.invalid_value_speaker_max_phrase")); + break; + + default: + if (endpoint === "/set/data/deepl_auth_key") updateDeepLAuthKey(data); + showNotification_Error(message); + break; + } + + } + + return { + errorHandling_Backend, + } +}; \ No newline at end of file diff --git a/src-ui/logics/common/useNotificationStatus.js b/src-ui/logics/common/useNotificationStatus.js index aa1ac703..a5b7bc3c 100644 --- a/src-ui/logics/common/useNotificationStatus.js +++ b/src-ui/logics/common/useNotificationStatus.js @@ -15,7 +15,7 @@ export const useNotificationStatus = () => { }); }; - const showNotification_Success = (message) => { + const showNotification_Success = (message, options = {}) => { updateNotificationStatus({ status: "success", is_open: true, diff --git a/src-ui/logics/configs/translation/useDeepLAuthKey.js b/src-ui/logics/configs/translation/useDeepLAuthKey.js index e379ce0f..3d66dfba 100644 --- a/src-ui/logics/configs/translation/useDeepLAuthKey.js +++ b/src-ui/logics/configs/translation/useDeepLAuthKey.js @@ -1,7 +1,11 @@ import { useStore_DeepLAuthKey } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; +import { useTranslation } from "react-i18next"; +import { useNotificationStatus } from "@logics_common"; export const useDeepLAuthKey = () => { + const { t } = useTranslation(); + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); const { asyncStdoutToPython } = useStdoutToPython(); const { currentDeepLAuthKey, updateDeepLAuthKey, pendingDeepLAuthKey } = useStore_DeepLAuthKey(); @@ -14,6 +18,10 @@ export const useDeepLAuthKey = () => { pendingDeepLAuthKey(); asyncStdoutToPython("/set/data/deepl_auth_key", selected_deepl_auth_key); }; + const saveSuccessDeepLAuthKey = (saved_deepl_auth_key) => { + updateDeepLAuthKey(saved_deepl_auth_key); + showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success")); + }; const deleteDeepLAuthKey = () => { pendingDeepLAuthKey(); @@ -25,6 +33,7 @@ export const useDeepLAuthKey = () => { getDeepLAuthKey, updateDeepLAuthKey, setDeepLAuthKey, + saveSuccessDeepLAuthKey, deleteDeepLAuthKey, }; }; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index b3a5c4cd..53a2feb3 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -1,6 +1,8 @@ import { translator_status } from "@ui_configs"; import { arrayToObject } from "@utils"; +import { _useBackendErrorHandling } from "./_useBackendErrorHandling"; + import { useIsVrctAvailable, useNotificationStatus, @@ -146,7 +148,7 @@ export const useReceiveRoutes = () => { const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); const { updateSpeakerMaxWords } = useSpeakerMaxWords(); - const { updateDeepLAuthKey } = useDeepLAuthKey(); + const { updateDeepLAuthKey, saveSuccessDeepLAuthKey } = useDeepLAuthKey(); const { updateSelectedCTranslate2WeightType } = useSelectedCTranslate2WeightType(); const { updateDownloadedCTranslate2WeightTypeStatus, @@ -184,6 +186,10 @@ export const useReceiveRoutes = () => { const { handleNetworkConnection } = useHandleNetworkConnection(); + const { + errorHandling_Backend, + } = _useBackendErrorHandling(); + const routes = { // Common "/run/feed_watchdog": () => {}, @@ -346,7 +352,7 @@ export const useReceiveRoutes = () => { // Translation "/get/data/deepl_auth_key": updateDeepLAuthKey, - "/set/data/deepl_auth_key": updateDeepLAuthKey, + "/set/data/deepl_auth_key": saveSuccessDeepLAuthKey, "/delete/data/deepl_auth_key": () => updateDeepLAuthKey(""), "/get/data/ctranslate2_weight_type": updateSelectedCTranslate2WeightType, @@ -498,16 +504,25 @@ export const useReceiveRoutes = () => { "/get/data/transcription_engines": ()=>{}, // Not implemented on UI yet. (if ai_models has not been detected, this will be blank array[]. if the ai_models are ok but just network has not connected, it'l be only ["Whisper"]) }; - const error_routes = { - "/set/data/mic_record_timeout": updateMicRecordTimeout, - "/set/data/mic_phrase_timeout": updateMicPhraseTimeout, - "/set/data/mic_max_phrases": updateMicMaxWords, + const error_status_routes = { + "/run/error_device": errorHandling_Backend, - "/set/data/speaker_record_timeout": updateSpeakerRecordTimeout, - "/set/data/speaker_phrase_timeout": updateSpeakerPhraseTimeout, - "/set/data/speaker_max_phrases": updateSpeakerMaxWords, + "/run/error_ctranslate2_weight": errorHandling_Backend, + "/run/error_whisper_weight": errorHandling_Backend, - "/set/data/deepl_auth_key": updateDeepLAuthKey, + "/set/data/deepl_auth_key": errorHandling_Backend, + + "/run/error_translation_engine": errorHandling_Backend, + + "/set/data/mic_threshold": errorHandling_Backend, + "/set/data/mic_record_timeout": errorHandling_Backend, + "/set/data/mic_phrase_timeout": errorHandling_Backend, + "/set/data/mic_max_phrases": errorHandling_Backend, + + "/set/data/speaker_threshold": errorHandling_Backend, + "/set/data/speaker_record_timeout": errorHandling_Backend, + "/set/data/speaker_phrase_timeout": errorHandling_Backend, + "/set/data/speaker_max_phrases": errorHandling_Backend, }; @@ -519,22 +534,37 @@ export const useReceiveRoutes = () => { } }; + const handleInvalidEndpoint = (parsed_data) => { + console.error(`Invalid endpoint: ${parsed_data.endpoint}\nresult: ${JSON.stringify(parsed_data.result)}`); + }; + + if (parsed_data.endpoint === "/run/initialization_complete") { + initDataSyncProcess(parsed_data.result); + updateIsBackendReady(true); + return; + }; + switch (parsed_data.status) { case 200: - if (parsed_data.endpoint === "/run/initialization_complete") { - initDataSyncProcess(parsed_data.result); - updateIsBackendReady(true); - break; - }; const route = routes[parsed_data.endpoint]; - (route) ? route(parsed_data.result) : console.error(`Invalid endpoint: ${parsed_data.endpoint}\nresult: ${JSON.stringify(parsed_data.result)}`); + if (route) { + route(parsed_data.result); + } else { + handleInvalidEndpoint(parsed_data); + } break; case 400: - const error_route = error_routes[parsed_data.endpoint]; - (error_route) ? error_route(parsed_data.result.data) : console.error(`Invalid endpoint: ${parsed_data.endpoint}\nresult: ${JSON.stringify(parsed_data.result)}`); - console.error(`status 400: ${JSON.stringify(parsed_data.result)}`); - showNotification_Error(parsed_data.result.message); + const error_route = error_status_routes[parsed_data.endpoint]; + if (error_route) { + error_route({ + message: parsed_data.result.message, + data: parsed_data.result.data, + endpoint: parsed_data.endpoint, + }); + } else { + handleInvalidEndpoint(parsed_data); + } break; case 348: diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index ef58fba8..4bee9957 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -54,11 +54,11 @@ export const ui_configs = { export const translator_status = [ { id: "DeepL", label: "DeepL", is_available: false }, - { id: "DeepL_API", label: `DeepL\nAPI`, is_available: false }, + { id: "DeepL_API", label: `DeepL API`, is_available: false }, { id: "Google", label: "Google", is_available: false }, { id: "Bing", label: "Bing", is_available: false }, { id: "Papago", label: "Papago", is_available: false }, - { id: "CTranslate2", label: `Internal\n(Default)`, is_available: false }, + { id: "CTranslate2", label: `AI\nCTranslate2`, is_available: false, is_default: true }, ]; export const ctranslate2_weight_type_status = [ diff --git a/src-ui/utils.js b/src-ui/utils.js index 145dc403..96a87c75 100644 --- a/src-ui/utils.js +++ b/src-ui/utils.js @@ -49,4 +49,12 @@ export const updateLabelsById = (data_array, updates) => { const update = updates.find(update_item => update_item.id === item.id); return update ? { ...item, label: update.label } : item; }); +}; + +export const genNumArray = (count, start_from = 0) => { + return [...Array(count).keys()].map(i => i + start_from); +}; + +export const genNumObjArray = (count, start_from = 0) => { + return arrayToObject(genNumArray(count, start_from)); }; \ No newline at end of file