diff --git a/locales/en.yml b/locales/en.yml index 05e5601d..1faf9639 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -25,7 +25,7 @@ common_error: invalid_value_speaker_max_phrase: "You can set a number equal to or greater than 0." common_warning: - unable_to_use_osc_query: "The functions below have been automatically disabled because receiving OSC data is not possible due to OSC IP Address settings." + unable_to_use_osc_query: "The following functions have been automatically disabled due to the OSC IP Address settings preventing OSC data from being received." main_page: translation: "Translation" @@ -211,6 +211,7 @@ config_page: others: section_label_sounds: "Sounds" + section_label_message_formats: "Message Formats" auto_clear_the_message_box: label: "Auto Clear Message box" send_only_translated_messages: @@ -230,6 +231,25 @@ config_page: send_received_message_to_vrc: label: "Send Received Message To VRChat" desc: "Send the message you received from the speaker's voice to VRChat's chatbox." + message_format_common: + example_view: + title: "Preview" + original_translated: "Original + Translation" + original_translated_multi: "Original + Translation (Multi-language)" + translated_only_multi: "Translation Only (Multi-language)" + translated_only: "Translation Only" + original_only: "Original Only" + settings: + title: "Settings" + original: "Original" + translated: "Translation" + for_multi_translation: "For Multi-Translation" + send_message_format: + label: "Message Format (Send)" + desc: "This allows you to change the format of the message that others see in VRChat." + received_message_format: + label: "Message Format (Speaker2Chatbox)" + desc: "Currently, it is used in Speaker2Chatbox." hotkeys: toggle_vrct_visibility: @@ -245,7 +265,7 @@ config_page: downloaded_version: "Downloaded version: {{downloaded_version}}" latest_version: "Latest version: {{latest_version}}" available_after_updating: "Available after updating to the latest version" - unavailable_downloaded: "Currently unavailable due to incompatibility with the VRCT version in use" + unavailable_downloaded: "Currently Unavailable - Incompatible with the VRCT version in use" no_latest_info: "Unable to retrieve the latest information" using_latest_version: "Using the latest version" available_latest_version: "Latest version available" @@ -273,16 +293,16 @@ config_page: save_success: "Settings have been saved." plugin_notifications: - downloading: Downloading the plugin. - downloaded_success: Downloaded successfully. - downloaded_error: Download failed. + downloading: "Downloading the plugin." + downloaded_success: "Downloaded successfully." + downloaded_error: "Download failed." - updating: Updating the plugin. - updated_success: Updated successfully. - updated_error: Update failed. + updating: "Updating the plugin." + updated_success: "Updated successfully." + updated_error: "Update failed." - disabled_out_of_support: The plugin has been disabled. It's not supported on this VRCT version. + disabled_out_of_support: "The plugin has been disabled. It's not supported on this VRCT version." disabled_due_to_an_error: "An error was detected while running the plugin. Please report this to the plugin developer." - is_enabled: The plugin has enabled. - is_disabled: The plugin has disabled. \ No newline at end of file + is_enabled: "The plugin enabled." + is_disabled: "The plugin disabled." \ No newline at end of file diff --git a/locales/ja.yml b/locales/ja.yml index d36bec89..d3da554e 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -211,6 +211,7 @@ config_page: others: section_label_sounds: "サウンド" + section_label_message_formats: "メッセージフォーマット" auto_clear_the_message_box: label: "送信後はメッセージ入力欄を空にする" send_only_translated_messages: @@ -230,6 +231,25 @@ config_page: send_received_message_to_vrc: label: "受信したメッセージをVRChatに送信する" desc: "スピーカーから聞き取り、文字起こしされたメッセージをVRChatに送信します。" + message_format_common: + example_view: + title: "プレビュー" + original_translated: "原文 + 翻訳" + original_translated_multi: "原文 + 翻訳(多言語)" + translated_only_multi: "翻訳のみ(多言語)" + translated_only: "翻訳のみ" + original_only: "原文のみ" + settings: + title: "設定" + original: "原文" + translated: "翻訳" + for_multi_translation: "多言語翻訳用" + send_message_format: + label: メッセージフォーマット(送信) + desc: VRChatで相手に実際に見えるフォーマットを変更できます。 + received_message_format: + label: メッセージフォーマット(Speaker2Chatbox) + desc: 今のところ、Speaker2Chatboxで送信した時の表示に使われます。 hotkeys: toggle_vrct_visibility: @@ -273,16 +293,16 @@ config_page: save_success: "設定を保存しました。" plugin_notifications: - downloading: プラグインをダウンロード中。 - downloaded_success: プラグインのダウンロードが完了しました。 - downloaded_error: プラグインのダウンロードに失敗しました。 + downloading: "プラグインをダウンロード中。" + downloaded_success: "プラグインのダウンロードが完了しました。" + downloaded_error: "プラグインのダウンロードに失敗しました。" - updating: プラグインをアップデート中。 - updated_success: プラグインのアップデートが完了しました。 - updated_error: プラグインのアップデートに失敗しました。 + updating: "プラグインをアップデート中。" + updated_success: "プラグインのアップデートが完了しました。" + updated_error: "プラグインのアップデートに失敗しました。" - disabled_out_of_support: 現在のバージョンとの互換性がありません。プラグインを無効にしました。 - disabled_due_to_an_error: プラグイン実行中にエラーを検知しました。プラグイン開発者に報告してください。 + disabled_out_of_support: "現在のバージョンとの互換性がありません。プラグインを無効にしました。" + disabled_due_to_an_error: "プラグイン実行中にエラーを検知しました。プラグイン開発者に報告してください。" - is_enabled: プラグインを有効にしました。 - is_disabled: プラグインを無効にしました。 \ No newline at end of file + is_enabled: "プラグインを有効にしました。" + is_disabled: "プラグインを無効にしました。" \ No newline at end of file diff --git a/locales/ko.yml b/locales/ko.yml index 39df6d4a..d2e171ac 100644 --- a/locales/ko.yml +++ b/locales/ko.yml @@ -9,11 +9,11 @@ common: 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: + threshold_invalid_value: "{{min}}부터 {{max}}까지의 숫자로 설정할 수 있습니다." + failed_download_weight_ctranslate2: "CTranslate2 모델 다운로드에 실패했습니다." + failed_download_weight_whisper: "Whisper 모델 다운로드에 실패했습니다." + translation_limit: "번역 엔진 사용 제한에 도달했거나 일시적으로 이용 제한이 걸렸습니다." + deepl_auth_key_invalid_length: "DeepL 인증 키의 문자 수가 잘못되었습니다." deepl_auth_key_failed_authentication: "인증키가 잘못되었거나 API 사용 제한이 상한에 도달했습니다." invalid_value_mic_record_timeout: "0 이상에서 '{{mic_phrase_timeout_label}}'보다 클 수 없습니다." @@ -24,6 +24,9 @@ common_error: invalid_value_speaker_phrase_timeout: "0 이상에서 '{{speaker_record_timeout_label}}'보다 작을 수 없습니다." invalid_value_speaker_max_phrase: "0 이상의 숫자만 설정할 수 있습니다." +common_warning: + unable_to_use_osc_query: "OSC IP 주소 설정으로 인해 OSC 데이터 수신이 불가능하므로, 아래 기능이 자동으로 비활성화되었습니다." + main_page: translation: "번역" transcription_send: "음성인식 (마이크)" @@ -39,7 +42,7 @@ main_page: translator_label_default: "기본값" translator_selector: - is_selected_same_language: + is_selected_same_language: "'{{your_language}}'와 '{{target_language}}'에 동일한 언어가 선택되어 있으므로, 「{{ctranslate2}}」만 사용할 수 있습니다." message_log: all: "전체" @@ -47,7 +50,7 @@ main_page: received: "수신" system: "시스템" - resend_button_on_hover_desc: + resend_button_on_hover_desc: "길게 눌러서 보내기" state_text_enabled: "Enabled" state_text_disabled: "Disabled" @@ -60,32 +63,33 @@ main_page: updating: "업데이트 중..." update_modal: - 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: + cpu_desc: "처리 장치로 CPU만을 사용합니다." + cuda_desc: "처리 장치로 CPU와 NVIDIA GPU를 선택할 수 있습니다." + cuda_compare_cpu_desc: "GPU 선택 시, CPU에 비해 처리가 빠릅니다." + cuda_disk_space_desc: "약 {{size}}의 디스크 용량이 필요합니다." + close_modal: "닫기" + download_latest_and_restart: "최신 버전이 다운로드되면 앱이 자동으로 재시작됩니다." + is_latest_version_already: "이미 최신 버전을 사용 중입니다." + is_current_compute_device: "현재 사용 중인 버전" config_page: version: "버전 {{version}}" - model_download_button_label: + model_download_button_label: "다운로드" side_menu_labels: - device: + device: "장치" appearance: "모양" translation: "번역" transcription: "음성인식" others: "기타" - hotkeys: + hotkeys: "단축키" + plugins: "플러그인" advanced_settings: "고급 설정" device: - check_volume: - label_auto_select: + check_volume: "음량 확인" + label_auto_select: "자동 선택" label_host: "호스트/드라이버" - label_device: + label_device: "장치" mic_host_device: label: "마이크 장치" mic_dynamic_energy_threshold: @@ -115,6 +119,9 @@ config_page: hide: "숨김 (Enter 키를 사용하여 전송)" show: "표시" show_and_disable_enter_key: "표시 (Enter 키 전송 비활성화)" + show_resend_button: + label: "재전송 버튼 표시" + desc: "보낸 메시지 로그에 마우스를 올리면 재전송 버튼이 표시됩니다. 클릭하면 편집 모드로 전환되며, 길게 누르면 재전송됩니다." font_family: label: "폰트" ui_language: @@ -127,19 +134,19 @@ config_page: small: "일반 모델 ({{capacity}})" large: "정밀 모델 ({{capacity}})" ctranslate2_compute_device: - label: + label: "AI 번역 {{ctranslate2}} 처리 장치" deepl_auth_key: label: "DeepL 인증키" desc: "사용시 메인화면에 있는 {{translator}}를 DeepL_API로 변경해 주세요.\n지원하지 않는 언어도 있습니다." open_auth_key_webpage: "DeepL 계정 페이지 열기" - save: - edit: + save: "저장" + edit: "편집" auth_key_success: "인증키 갱신이 완료되었습니다." transcription: section_label_mic: "마이크" section_label_speaker: "스피커" - section_label_transcription_engines: + section_label_transcription_engines: "음성 인식 엔진" mic_record_timeout: label: "최대 무음 시간" desc: "무음을 감지하고 설정된 시간(초)만큼의 시간이 지나면 음성 입력이 종료된 것으로 판단합니다." @@ -164,46 +171,47 @@ config_page: label: "최대 입력 절(phrases) 수" desc: "식된 단어 수의 하한값으로, 이 수치를 초과하는 경우에만 결과를 로그에 표시합니다." select_transcription_engine: - label: + label: "음성 인식에 사용할 엔진" whisper_weight_type: label: "Whisper 모델 타입" - desc: + desc: "용량이 큰 모델일수록 정확도는 높지만, 그만큼 CPU나 GPU를 많이 차지합니다. * 특히 medium보다 용량이 큰 모델은 CPU/GPU 성능에 따라 사용 자체가 어려울 수 있습니다." model_template: "{{model_name}} 모델 ({{capacity}})" recommended_model_template: "{{model_name}} 모델 ({{capacity}}) (권장)" whisper_compute_device: - label: + label: "Whisper에서 사용할 처리 장치" vr: - single_line: - multi_lines: - overlay_enable: - restore_default_settings: - position: - rotation: - x_position: - y_position: - z_position: - x_rotation: - y_rotation: - z_rotation: + single_line: "한 줄" + multi_lines: "여러 줄" + overlay_enable: "활성화" + restore_default_settings: "초기값으로 되돌리기" + 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: + start: "샘플 텍스트를\n오버레이에 전송" + stop: "전송 중지" + sample_text: "샘플 텍스트" + opacity: "투명도" + ui_scaling: "UI 크기 조정" + display_duration: "표시 시간" + fadeout_duration: "페이드 아웃 시간" + common_settings: "공통 설정" + tracker: "표시할 트래커의 위치" + hmd: "HMD" + left_hand: "왼손" + right_hand: "오른손" overlay_show_only_translated_messages: - label: + label: "번역된 메시지만 표시" others: - section_label_sounds: + section_label_sounds: "사운드" + section_label_message_formats: "메시지 형식" auto_clear_the_message_box: label: "챗박스 자동 삭제" send_only_translated_messages: @@ -212,27 +220,58 @@ config_page: label: "대화 로그 자동 저장" desc: "logs 폴더에 텍스트 파일로 로그가 저장됩니다." vrc_mic_mute_sync: - label: - desc: + label: "VRC 마이크 음소거 동기화" + desc: "VRChat의 마이크가 음소거 상태인 동안에는 메시지를 VRChat에 전송하지 않습니다. * 약간의 지연이 발생할 수 있습니다. 또한 눌러서 말하기(Push-To-Talk) 기능은 지원되지 않습니다." send_message_to_vrc: label: "VRChat에 메시지 전송" desc: "VRChat에 메시지를 보내지 않고 사용할 수 있는 방법이 있지만 지원되지 않습니다. VRChat에 메시지를 보내려면 이 기능을 활성화하세요." notification_vrc_sfx: - label: - desc: + label: "채팅 전송 시 채팅창 알림음 재생" + desc: "이 기능을 비활성화하면 다른 사람이 들을 수 있는 채팅창 알림음을 울리지 않고 조용히 전송합니다." send_received_message_to_vrc: - label: - desc: + label: "수신한 메시지를 VRChat에 전송" + desc: "스피커에서 인식된 내용을 텍스트로 변환한 메시지가 VRChat에 전송됩니다." + message_format_common: + example_view: + title: "미리 보기" + original_translated: "원문 + 번역문" + original_translated_multi: "원문 + 번역문 (다국어)" + translated_only_multi: "번역문만 (다국어)" + translated_only: "번역문만" + original_only: "원문만" + settings: + title: "설정" + original: "원문" + translated: "번역문" + for_multi_translation: "다국어 번역용" + send_message_format: + label: "메시지 형식 (송신)" + desc: "VRChat에서 상대방에게 실제로 보이는 형식을 변경할 수 있습니다." + received_message_format: + label: "메시지 형식 (Speaker2Chatbox)" + desc: "현재로서는 Speaker2Chatbox로 전송할 때의 표시용으로 사용됩니다." hotkeys: toggle_vrct_visibility: - label: + label: "VRCT 최소화/활성화 전환" toggle_translation: - label: + label: "'{{translation}}' 켜기/끄기 전환" toggle_transcription_send: - label: + label: "'{{transcription_send}}' 켜기/끄기 전환" toggle_transcription_receive: - label: + label: "'{{transcription_receive}}' 켜기/끄기 전환" + + plugins: + downloaded_version: "다운로드된 버전: {{downloaded_version}}" + latest_version: "최신 버전: {{latest_version}}" + available_after_updating: "최신 버전으로 업데이트 후 이용 가능" + unavailable_downloaded: "현재 사용 중인 VRCT 버전과의 호환성 문제로 인해 이용 불가" + no_latest_info: "최신 정보를 가져올 수 없습니다" + using_latest_version: "최신 버전을 사용 중" + available_latest_version: "최신 버전을 이용 가능" + unavailable_latest_version: "최신 버전은 현재 이용 불가" + available_in_latest_vrct_version: "VRCT 최신 버전에서 이용 가능" + unavailable_not_downloaded: "현재 이용 불가" advanced_settings: osc_ip_address: @@ -242,4 +281,28 @@ config_page: open_config_filepath: label: "설정 파일 열기" switch_compute_device: - label: \ No newline at end of file + label: "VRCT CPU/GPU 버전 전환" + enable_websocket: + label: "WebSocket 서버 활성화" + websocket_host: + label: "WebSocket 호스트" + websocket_port: + label: "WebSocket 포트" + + notifications: + save_success: "설정을 저장했습니다." + +plugin_notifications: + downloading: "플러그인을 다운로드 중입니다." + downloaded_success: "플러그인 다운로드가 완료되었습니다." + downloaded_error: "플러그인 다운로드에 실패했습니다." + + updating: "플러그인을 업데이트 중입니다." + updated_success: "플러그인 업데이트가 완료되었습니다." + updated_error: "플러그인 업데이트에 실패했습니다." + + disabled_out_of_support: "현재 버전과 호환되지 않습니다. 플러그인을 비활성화했습니다." + disabled_due_to_an_error: "플러그인 실행 중 오류를 감지했습니다. 플러그인 개발자에게 보고해 주세요." + + is_enabled: "플러그인을 활성화했습니다." + is_disabled: "플러그인을 비활성화했습니다." \ No newline at end of file diff --git a/locales/readme_first.txt b/locales/readme_first.txt index 02323f8f..6d87d7d0 100644 --- a/locales/readme_first.txt +++ b/locales/readme_first.txt @@ -1 +1,13 @@ -Thank you for considering translating VRCT's UI. However, please refrain from making any changes at this time. I am currently organizing the files, including reordering, adding, and removing elements, and some parts may change frequently until the UI becomes stable. (Note: This message was updated in February 2025.) \ No newline at end of file +Thank you for considering contributing to VRCT's UI translations. +If you want to edit a language: +- If it already exists: edit the corresponding [xx].yml file. +- If it doesn’t exist: please open a GitHub issue or contact us through another channel. (If you know how to add a new language yourself, feel free to do so! Sorry for the complicated structure...) + +Languages currently supported: +- en: English +- ja: 日本語 +- ko: 한국어 +- zh-Hant: 繁體中文 +- zh-Hans: 简体中文 + +Last updated: 2025/09 \ No newline at end of file diff --git a/locales/zh-Hans.yml b/locales/zh-Hans.yml index dd1dafc7..71a75390 100644 --- a/locales/zh-Hans.yml +++ b/locales/zh-Hans.yml @@ -1,245 +1,308 @@ -# ================================= -# IMPORTANT: -# Please read 'readme_first.txt' before making any changes. -# ================================= - -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_label_default: "默认" - - translator_selector: - is_selected_same_language: - - message_log: - all: "全部" - sent: "发送" - received: "接受" - system: "系统" - - resend_button_on_hover_desc: - - state_text_enabled: "启用" - state_text_disabled: "停用" - - language_selector: - title_your_language: "你的语言" - title_target_language: "目标语言" - - update_available: "有新版本可供使用!" - updating: "更新中..." - -update_modal: - 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: - 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: "使用滑杆手动确定麦克风输入灵敏度。按下麦克风图标输入语音,并在监控音量的同时调节灵敏度。" - speaker_device: - label: "他人语音 (设备)" - speaker_dynamic_energy_threshold: - label_for_automatic: "他人语音接收阈值(当前设置:自动)" - desc_for_automatic: "自动调节他人语音接收阈值" - label_for_manual: "他人语音接收阈值(当前设置:手动)" - desc_for_manual: "使用滑杆手动调整他人语音接收阈值.在按下耳机按钮时,请根据实际听到的声音调整该大小" - - appearance: - transparency: - label: "透明度" - desc: "更改主视窗透明度" - ui_size: - label: "界面大小" - textbox_ui_size: - label: "文本框字体大小" - desc: "你可以根据用户界面大小调整文本框中使用的字体大小。" - send_message_button_type: - label: "发送信息按钮" - hide: "隐藏 (可使用回车发送信息)" - show: "显示" - show_and_disable_enter_key: "显示,并且停用‘回车发送信息’" - font_family: - label: "字体" - ui_language: - label: "界面语言" - - translation: - ctranslate2_weight_type: - label: "选择离线翻译模型" - 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: "授权密匙认证完成。" - - transcription: - section_label_mic: "你的麦克风" - section_label_speaker: "他人声音" - section_label_transcription_engines: - mic_record_timeout: - label: "语音输入结束后的静音时间" - desc: "当检测到静音并经过设定的秒数后,语音输入即被视为完成。" - mic_phrase_timeout: - label: "转录间隔" - desc: "在经过设定的时间后执行转录" - mic_max_phrase: - label: "麦克风发送时的最小单词数" - desc: "转录字数的下限,只有超过这个数字,才会记录翻译结果并发送到VRC" - mic_word_filter: - label: "单词过滤器" - desc: "检测出被记录的单词时,不会发送这段话\n如要添加多个单词,可以用逗号来分割\n※不会记录重复的单词" - add_button_label: "添加" - count_desc: "现在被记录的单词数: {{count}}" - speaker_record_timeout: - label: "语音接收结束后的静音时间" - desc: "当检测到静音并经过设定的秒数后,语音接收即被视为完成。" - speaker_phrase_timeout: - label: "转录间隔" - desc: "在经过设定的时间后执行转录" - speaker_max_phrase: - label: "语音接收时的最小单词数" - desc: "转录字数的下限,只有超过这个数字,才会记录转录结果" - select_transcription_engine: - label: - whisper_weight_type: - label: "选择某个Whisper模型" - 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: "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: "只发送翻译后的信息" - auto_export_message_logs: - label: "自动导出聊天记录" - desc: "以文本文件的形式在logs文件夹中保存。" - vrc_mic_mute_sync: - label: "与VRC中的麦克风静音同步" - desc: "当VRChat的麦克风处于静音时,不在VRChat中发送信息\n※存在少许延迟且不支持按键发言." - 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: - label: "OSC IP 地址" - osc_port: - label: "OSC 端口" - open_config_filepath: - label: "打开设置文件" - switch_compute_device: - label: \ No newline at end of file +# ================================= +# IMPORTANT: +# Please read 'readme_first.txt' before making any changes. +# ================================= + +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 以上" + +common_warning: + unable_to_use_osc_query: + +main_page: + translation: "翻译" + transcription_send: "你的语音转文字" + transcription_receive: "他人语音转文字" + foreground: "顶层显示" + + language_settings: "语言设定" + your_language: "你的语言" + translate_each_other_label: "双向翻译" + swap_button_label: "互换" + target_language: "目标语言" + translator: "翻译器" + translator_label_default: "默认" + + translator_selector: + is_selected_same_language: + + message_log: + all: "全部" + sent: "发送" + received: "接受" + system: "系统" + + resend_button_on_hover_desc: + + state_text_enabled: "启用" + state_text_disabled: "停用" + + language_selector: + title_your_language: "你的语言" + title_target_language: "目标语言" + + update_available: "有新版本可供使用!" + updating: "更新中..." + +update_modal: + 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: + plugins: + advanced_settings: "高级设置" + + 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: "使用滑杆手动确定麦克风输入灵敏度。按下麦克风图标输入语音,并在监控音量的同时调节灵敏度。" + speaker_device: + label: "他人语音 (设备)" + speaker_dynamic_energy_threshold: + label_for_automatic: "他人语音接收阈值(当前设置:自动)" + desc_for_automatic: "自动调节他人语音接收阈值" + label_for_manual: "他人语音接收阈值(当前设置:手动)" + desc_for_manual: "使用滑杆手动调整他人语音接收阈值.在按下耳机按钮时,请根据实际听到的声音调整该大小" + + appearance: + transparency: + label: "透明度" + desc: "更改主视窗透明度" + ui_size: + label: "界面大小" + textbox_ui_size: + label: "文本框字体大小" + desc: "你可以根据用户界面大小调整文本框中使用的字体大小。" + send_message_button_type: + label: "发送信息按钮" + hide: "隐藏 (可使用回车发送信息)" + show: "显示" + show_and_disable_enter_key: "显示,并且停用‘回车发送信息’" + show_resend_button: + label: + desc: + font_family: + label: "字体" + ui_language: + label: "界面语言" + + translation: + ctranslate2_weight_type: + label: "选择离线翻译模型" + 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: "授权密匙认证完成。" + + transcription: + section_label_mic: "你的麦克风" + section_label_speaker: "他人声音" + section_label_transcription_engines: + mic_record_timeout: + label: "语音输入结束后的静音时间" + desc: "当检测到静音并经过设定的秒数后,语音输入即被视为完成。" + mic_phrase_timeout: + label: "转录间隔" + desc: "在经过设定的时间后执行转录" + mic_max_phrase: + label: "麦克风发送时的最小单词数" + desc: "转录字数的下限,只有超过这个数字,才会记录翻译结果并发送到VRC" + mic_word_filter: + label: "单词过滤器" + desc: "检测出被记录的单词时,不会发送这段话\n如要添加多个单词,可以用逗号来分割\n※不会记录重复的单词" + add_button_label: "添加" + count_desc: "现在被记录的单词数: {{count}}" + speaker_record_timeout: + label: "语音接收结束后的静音时间" + desc: "当检测到静音并经过设定的秒数后,语音接收即被视为完成。" + speaker_phrase_timeout: + label: "转录间隔" + desc: "在经过设定的时间后执行转录" + speaker_max_phrase: + label: "语音接收时的最小单词数" + desc: "转录字数的下限,只有超过这个数字,才会记录转录结果" + select_transcription_engine: + label: + whisper_weight_type: + label: "选择某个Whisper模型" + 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: "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: + section_label_message_formats: + auto_clear_the_message_box: + label: "发言后自动清空chatbox" + send_only_translated_messages: + label: "只发送翻译后的信息" + auto_export_message_logs: + label: "自动导出聊天记录" + desc: "以文本文件的形式在logs文件夹中保存。" + vrc_mic_mute_sync: + label: "与VRC中的麦克风静音同步" + desc: "当VRChat的麦克风处于静音时,不在VRChat中发送信息\n※存在少许延迟且不支持按键发言." + send_message_to_vrc: + label: "发送信息至VRChat" + desc: "不发送信息至VRChat的情况下也能使用它,但该功能现在并未完成.在想要发送信息时,请不要忘记打开这个功能." + notification_vrc_sfx: + label: + desc: + send_received_message_to_vrc: + label: + desc: + message_format_common: + example_view: + title: + original_translated: + original_translated_multi: + translated_only_multi: + translated_only: + original_only: + settings: + title: + original: + translated: + for_multi_translation: + send_message_format: + label: + desc: + received_message_format: + label: + desc: + + hotkeys: + toggle_vrct_visibility: + label: + toggle_translation: + label: + toggle_transcription_send: + label: + toggle_transcription_receive: + label: + + plugins: + downloaded_version: + latest_version: + available_after_updating: + unavailable_downloaded: + no_latest_info: + using_latest_version: + available_latest_version: + unavailable_latest_version: + available_in_latest_vrct_version: + unavailable_not_downloaded: + + advanced_settings: + osc_ip_address: + label: "OSC IP 地址" + osc_port: + label: "OSC 端口" + open_config_filepath: + label: "打开设置文件" + switch_compute_device: + label: + enable_websocket: + label: + websocket_host: + label: + websocket_port: + label: + + notifications: + save_success: + +plugin_notifications: + downloading: + downloaded_success: + downloaded_error: + + updating: + updated_success: + updated_error: + + disabled_out_of_support: + disabled_due_to_an_error: + + is_enabled: + is_disabled: \ No newline at end of file diff --git a/locales/zh-Hant.yml b/locales/zh-Hant.yml index 08210a7d..f091fd03 100644 --- a/locales/zh-Hant.yml +++ b/locales/zh-Hant.yml @@ -24,6 +24,9 @@ common_error: invalid_value_speaker_phrase_timeout: "不能小於「{{speaker_record_timeout_label}}」,應為 0 或更高。" invalid_value_speaker_max_phrase: "可以設置 0 或更高的數值。" +common_warning: + unable_to_use_osc_query: + main_page: translation: "翻譯" transcription_send: "麥克風轉文字" @@ -79,6 +82,7 @@ config_page: transcription: "轉錄" others: "其他" hotkeys: + plugins: advanced_settings: "進階設定" device: @@ -115,6 +119,9 @@ config_page: hide: "隱藏(使用 Enter 鍵發送)" show: "顯示" show_and_disable_enter_key: "顯示並停用 Enter 鍵發送" + show_resend_button: + label: + desc: font_family: label: "字型" ui_language: @@ -204,6 +211,7 @@ config_page: others: section_label_sounds: + section_label_message_formats: auto_clear_the_message_box: label: "自動清除 Chatbox" send_only_translated_messages: @@ -223,6 +231,25 @@ config_page: send_received_message_to_vrc: label: desc: + message_format_common: + example_view: + title: + original_translated: + original_translated_multi: + translated_only_multi: + translated_only: + original_only: + settings: + title: + original: + translated: + for_multi_translation: + send_message_format: + label: + desc: + received_message_format: + label: + desc: hotkeys: toggle_vrct_visibility: @@ -234,6 +261,18 @@ config_page: toggle_transcription_receive: label: + plugins: + downloaded_version: + latest_version: + available_after_updating: + unavailable_downloaded: + no_latest_info: + using_latest_version: + available_latest_version: + unavailable_latest_version: + available_in_latest_vrct_version: + unavailable_not_downloaded: + advanced_settings: osc_ip_address: label: "OSC IP 位址" @@ -242,4 +281,28 @@ config_page: open_config_filepath: label: "打開設定文件" switch_compute_device: - label: \ No newline at end of file + label: + enable_websocket: + label: + websocket_host: + label: + websocket_port: + label: + + notifications: + save_success: + +plugin_notifications: + downloading: + downloaded_success: + downloaded_error: + + updating: + updated_success: + updated_error: + + disabled_out_of_support: + disabled_due_to_an_error: + + is_enabled: + is_disabled: \ No newline at end of file diff --git a/src-python/config.py b/src-python/config.py index a9b2785a..c544727d 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -11,7 +11,7 @@ from models.translation.translation_languages import translation_lang from models.translation.translation_utils import ctranslate2_weights from models.transcription.transcription_languages import transcription_lang from models.transcription.transcription_whisper import _MODELS as whisper_models -from utils import errorLogging +from utils import errorLogging, validateDictStructure json_serializable_vars = {} def json_serializable(var_name): @@ -139,23 +139,44 @@ class Config: def SEND_MESSAGE_BUTTON_TYPE_LIST(self): return self._SEND_MESSAGE_BUTTON_TYPE_LIST - @property - def SEND_MESSAGE_FORMAT(self): - return self._SEND_MESSAGE_FORMAT - - @property - def SEND_MESSAGE_FORMAT_WITH_T(self): - return self._SEND_MESSAGE_FORMAT_WITH_T - - @property - def RECEIVED_MESSAGE_FORMAT(self): - return self._RECEIVED_MESSAGE_FORMAT - - @property - def RECEIVED_MESSAGE_FORMAT_WITH_T(self): - return self._RECEIVED_MESSAGE_FORMAT_WITH_T - # Read Write + @property + @json_serializable('SEND_MESSAGE_FORMAT_PARTS') + def SEND_MESSAGE_FORMAT_PARTS(self): + return self._SEND_MESSAGE_FORMAT_PARTS + + @SEND_MESSAGE_FORMAT_PARTS.setter + def SEND_MESSAGE_FORMAT_PARTS(self, value): + if isinstance(value, dict): + valid_parts = { + "message": {"prefix": str, "suffix": str}, + "separator": str, + "translation": {"prefix": str, "separator": str, "suffix": str}, + "translation_first": bool + } + + if validateDictStructure(value, valid_parts): + self._SEND_MESSAGE_FORMAT_PARTS = value + self.saveConfig(inspect.currentframe().f_code.co_name, value) + + @property + @json_serializable('RECEIVED_MESSAGE_FORMAT_PARTS') + def RECEIVED_MESSAGE_FORMAT_PARTS(self): + return self._RECEIVED_MESSAGE_FORMAT_PARTS + + @RECEIVED_MESSAGE_FORMAT_PARTS.setter + def RECEIVED_MESSAGE_FORMAT_PARTS(self, value): + if isinstance(value, dict): + valid_parts = { + "message": {"prefix": str, "suffix": str}, + "separator": str, + "translation": {"prefix": str, "separator": str, "suffix": str}, + "translation_first": bool + } + if validateDictStructure(value, valid_parts): + self._RECEIVED_MESSAGE_FORMAT_PARTS = value + self.saveConfig(inspect.currentframe().f_code.co_name, value) + @property def ENABLE_TRANSLATION(self): return self._ENABLE_TRANSLATION @@ -997,10 +1018,9 @@ class Config: self._WEBSOCKET_PORT = value self.saveConfig(inspect.currentframe().f_code.co_name, value) - def init_config(self): # Read Only - self._VERSION = "3.2.1" + self._VERSION = "3.2.2" if getattr(sys, 'frozen', False): self._PATH_LOCAL = os_path.dirname(sys.executable) else: @@ -1032,10 +1052,32 @@ class Config: self._SELECTABLE_COMPUTE_DEVICE_LIST.append({"device":"cuda", "device_index": i, "device_name": torch.cuda.get_device_name(i)}) self._SELECTABLE_COMPUTE_DEVICE_LIST.append({"device":"cpu", "device_index": 0, "device_name": "cpu"}) self._SEND_MESSAGE_BUTTON_TYPE_LIST = ["show", "hide", "show_and_disable_enter_key"] - self._SEND_MESSAGE_FORMAT = "[message]" - self._SEND_MESSAGE_FORMAT_WITH_T = "[message]\n[translation]" - self._RECEIVED_MESSAGE_FORMAT = "[message]" - self._RECEIVED_MESSAGE_FORMAT_WITH_T = "[message]\n[translation]" + self._SEND_MESSAGE_FORMAT_PARTS = { + "message": { + "prefix": "", + "suffix": "" + }, + "separator": "\n", + "translation": { + "prefix": "", + "separator": "\n", + "suffix": "" + }, + "translation_first": False, + } + self._RECEIVED_MESSAGE_FORMAT_PARTS = { + "message": { + "prefix": "", + "suffix": "" + }, + "separator": "\n", + "translation": { + "prefix": "", + "separator": "\n", + "suffix": "" + }, + "translation_first": False, + } # Read Write self._ENABLE_TRANSLATION = False diff --git a/src-python/controller.py b/src-python/controller.py index f0c3d55b..c34abaf8 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -306,11 +306,11 @@ class Controller: if config.SEND_MESSAGE_TO_VRC is True: if config.SEND_ONLY_TRANSLATED_MESSAGES is True: if config.ENABLE_TRANSLATION is False: - osc_message = self.messageFormatter("SEND", "", [message]) + osc_message = self.messageFormatter("SEND", [], message) else: - osc_message = self.messageFormatter("SEND", "", translation) + osc_message = self.messageFormatter("SEND", translation, "") else: - osc_message = self.messageFormatter("SEND", translation, [message]) + osc_message = self.messageFormatter("SEND", translation, message) model.oscSendMessage(osc_message) self.run( @@ -470,7 +470,13 @@ class Controller: model.updateOverlayLargeLog(overlay_image) if config.SEND_RECEIVED_MESSAGE_TO_VRC is True: - osc_message = self.messageFormatter("RECEIVED", translation, [message]) + if config.SEND_ONLY_TRANSLATED_MESSAGES is True: + if config.ENABLE_TRANSLATION is False: + osc_message = self.messageFormatter("RECEIVED", [], message) + else: + osc_message = self.messageFormatter("RECEIVED", translation, "") + else: + osc_message = self.messageFormatter("RECEIVED", translation, message) model.oscSendMessage(osc_message) # update textbox message log (Received) @@ -573,11 +579,11 @@ class Controller: if config.SEND_MESSAGE_TO_VRC is True: if config.SEND_ONLY_TRANSLATED_MESSAGES is True: if config.ENABLE_TRANSLATION is False: - osc_message = self.messageFormatter("SEND", "", [message]) + osc_message = self.messageFormatter("SEND", [], message) else: - osc_message = self.messageFormatter("SEND", "", translation) + osc_message = self.messageFormatter("SEND", translation, "") else: - osc_message = self.messageFormatter("SEND", translation, [message]) + osc_message = self.messageFormatter("SEND", translation, message) model.oscSendMessage(osc_message) if config.OVERLAY_LARGE_LOG is True: @@ -1450,6 +1456,24 @@ class Controller: config.WHISPER_WEIGHT_TYPE = str(data) return {"status":200, "result": config.WHISPER_WEIGHT_TYPE} + @staticmethod + def getSendMessageFormatParts(*args, **kwargs) -> dict: + return {"status":200, "result":config.SEND_MESSAGE_FORMAT_PARTS} + + @staticmethod + def setSendMessageFormatParts(data, *args, **kwargs) -> dict: + config.SEND_MESSAGE_FORMAT_PARTS = dict(data) + return {"status":200, "result":config.SEND_MESSAGE_FORMAT_PARTS} + + @staticmethod + def getReceivedMessageFormatParts(*args, **kwargs) -> dict: + return {"status":200, "result":config.RECEIVED_MESSAGE_FORMAT_PARTS} + + @staticmethod + def setReceivedMessageFormatParts(data, *args, **kwargs) -> dict: + config.RECEIVED_MESSAGE_FORMAT_PARTS = dict(data) + return {"status":200, "result":config.RECEIVED_MESSAGE_FORMAT_PARTS} + @staticmethod def getAutoClearMessageBox(*args, **kwargs) -> dict: return {"status":200, "result":config.AUTO_CLEAR_MESSAGE_BOX} @@ -1769,21 +1793,27 @@ class Controller: return {"status":200, "result":True} @staticmethod - def messageFormatter(format_type:str, translation:list, message:list) -> str: + def messageFormatter(format_type:str, translation:list, message:str) -> str: if format_type == "RECEIVED": - FORMAT_WITH_T = config.RECEIVED_MESSAGE_FORMAT_WITH_T - FORMAT = config.RECEIVED_MESSAGE_FORMAT + format_parts = config.RECEIVED_MESSAGE_FORMAT_PARTS elif format_type == "SEND": - FORMAT_WITH_T = config.SEND_MESSAGE_FORMAT_WITH_T - FORMAT = config.SEND_MESSAGE_FORMAT + format_parts = config.SEND_MESSAGE_FORMAT_PARTS else: raise ValueError("format_type is not found", format_type) - if len(translation) > 0: - osc_message = FORMAT_WITH_T.replace("[message]", "\n".join(message)) - osc_message = osc_message.replace("[translation]", "\n".join(translation)) + message_part = format_parts["message"]["prefix"] + message + format_parts["message"]["suffix"] + translation_part = format_parts["translation"]["prefix"] + format_parts["translation"]["separator"].join(translation) + format_parts["translation"]["suffix"] + + if len(translation) > 0 and message != "": + # 翻訳とメッセージの順序を決定 + if format_parts["translation_first"]: + osc_message = translation_part + format_parts["separator"] + message_part + else: + osc_message = message_part + format_parts["separator"] + translation_part + elif len(translation) > 0 and message == "": + osc_message = translation_part else: - osc_message = FORMAT.replace("[message]", "\n".join(message)) + osc_message = message_part return osc_message def changeToCTranslate2Process(self) -> None: diff --git a/src-python/mainloop.py b/src-python/mainloop.py index 61040535..0010b98a 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -285,6 +285,11 @@ mapping = { "/set/disable/overlay_show_only_translated_messages": {"status": True, "variable":controller.setDisableOverlayShowOnlyTranslatedMessages}, # Others + "/get/data/send_message_format_parts": {"status": True, "variable":controller.getSendMessageFormatParts}, + "/set/data/send_message_format_parts": {"status": True, "variable":controller.setSendMessageFormatParts}, + "/get/data/received_message_format_parts": {"status": True, "variable":controller.getReceivedMessageFormatParts}, + "/set/data/received_message_format_parts": {"status": True, "variable":controller.setReceivedMessageFormatParts}, + "/get/data/auto_clear_message_box": {"status": True, "variable":controller.getAutoClearMessageBox}, "/set/enable/auto_clear_message_box": {"status": True, "variable":controller.setEnableAutoClearMessageBox}, "/set/disable/auto_clear_message_box": {"status": True, "variable":controller.setDisableAutoClearMessageBox}, @@ -591,14 +596,34 @@ if __name__ == "__main__": "display_duration": 5, "fadeout_duration": 0.5, } - case "/set/data/send_message_format": - data = "[message]" - case "/set/data/send_message_format_with_t": - data = "[message]([translation])" - case "/set/data/received_message_format": - data = "[message]" - case "/set/data/received_message_format_with_t": - data = "[message]([translation])" + case "/set/data/send_message_format_parts": + data = { + "message": { + "prefix": "", + "suffix": "" + }, + "between_separator": "\n", + "translation": { + "prefix": "(", + "separator": "\\", + "suffix": ")" + }, + "translation_first": False, + } + case "/set/data/received_message_format_parts": + data = { + "message": { + "prefix": "", + "suffix": "" + }, + "between_separator": "\n", + "translation": { + "prefix": "(", + "separator": "\\", + "suffix": ")" + }, + "translation_first": True, + } case "/set/data/osc_ip_address": data = "127.0.0.1" case "/set/data/osc_port": diff --git a/src-python/utils.py b/src-python/utils.py index 2676d591..c3a857f2 100644 --- a/src-python/utils.py +++ b/src-python/utils.py @@ -10,6 +10,41 @@ import requests import ipaddress import socket +def validateDictStructure(data: dict, structure: dict) -> bool: + """ + 辞書とその期待される構造(型)が完全に一致するかを判別する関数 + Args: + data (dict): 検証対象の辞書 + structure (dict): 期待される構造を定義した辞書値には型(str, int, bool等)や入れ子の辞書を指定 + + Returns: + bool: 構造が完全に一致する場合True、そうでなければFalse + """ + + if not isinstance(data, dict) or not isinstance(structure, dict): + return False + + # キーの数と名前が完全に一致するかチェック + if set(data.keys()) != set(structure.keys()): + return False + + # 各キーの値の型または構造をチェック + for key, expected_type_or_structure in structure.items(): + if key not in data: + return False + + value = data[key] + # 期待される型が辞書の場合(入れ子構造) + if isinstance(expected_type_or_structure, dict): + # 再帰的に検証(多重入れ子に対応) + if not validateDictStructure(value, expected_type_or_structure): + return False + # 期待される型が型オブジェクトの場合 + else: + if not isinstance(value, expected_type_or_structure): + return False + return True + def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool: try: response = requests.get(url, timeout=timeout) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 898312d6..9e2402ef 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "VRCT", - "version": "3.2.1", + "version": "3.2.2", "identifier": "com.vrct.app", "build": { "beforeDevCommand": "", diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx index 641e7104..49ed1238 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_entry/_Entry.jsx @@ -21,11 +21,11 @@ const _Entry = forwardRef((props, ref) => { }); return ( -
-
+
+
{ + const { currentIsBreakPoint } = useStore_IsBreakPoint(); + const message_format_container_class = clsx(styles.container, { + [styles.is_break_point]: currentIsBreakPoint.data, + }); + + return ( +
+ +
+ +
+ ); +}; + +const ExampleComponent = ({ format, example_view_filter_variable, exampleViewFilterToggleFunction, format_id }) => { + const { currentUiLanguage } = useAppearance(); + const { t } = useTranslation(); + + + const locale_base_path = "config_page.others.message_format_common.example_view."; + + const label_title = t(locale_base_path + "title"); + + const label_original_translated = t(locale_base_path + "original_translated"); + const label_original_translated_multi = t(locale_base_path + "original_translated_multi"); + const label_translated_only_multi = t(locale_base_path + "translated_only_multi"); + const label_translated_only = t(locale_base_path + "translated_only"); + const label_original_only = t(locale_base_path + "original_only"); + + const createExampleMessage = (id) => { + // 言語順序を決定 + let example_text_order = []; + switch (currentUiLanguage.data) { + case "ja": + example_text_order = ["ja", "en", "ko", "fr"]; + break; + case "ko": + example_text_order = ["ko", "ja", "en", "fr"]; + break; + default: // en + example_text_order = ["en", "ja", "ko", "fr"]; + break; + } + + const original = EXAMPLE_TEXTS[example_text_order[0]]; + const translations = example_text_order.slice(1).map(lang => EXAMPLE_TEXTS[lang]); + + const originalPart = `${format.message.prefix}${original}${format.message.suffix}`; + const translationSingle = `${format.translation.prefix}${translations[0]}${format.translation.suffix}`; + const translationMulti = `${format.translation.prefix}${translations.join(format.translation.separator)}${format.translation.suffix}`; + + switch (id) { + case "original_translated": + return format.translation_first + ? `${translationSingle}${format.separator}${originalPart}` + : `${originalPart}${format.separator}${translationSingle}`; + + case "original_only": + return originalPart; + + case "translated_only": + return translationSingle; + + case "translated_only_multi": + return translationMulti; + + case "original_translated_multi": + return format.translation_first + ? `${translationMulti}${format.separator}${originalPart}` + : `${originalPart}${format.separator}${translationMulti}`; + + default: + throw new Error(`Unexpected id: ${id}`); + } + }; + + const ExampleBox = ({label, example_text_id}) => { + return ( +
+

{label}

+
+

{createExampleMessage(example_text_id)}

+
+
+ ); + + }; + + const svg_class_names = clsx(styles.arrow_left_svg, { + [styles.to_down]: example_view_filter_variable[format_id] === "Simplified", + [styles.to_up]: example_view_filter_variable[format_id] === "All" + }); + + + const FilteredExampleBox = ({format_id, id}) => { + if (format_id === "send" && id === "Simplified") { + return ( + <> + + + + ); + } else if ( format_id === "send" && id === "All") { + return ( + <> + + + + + + + ); + + } else if (format_id === "received") { + return ( + <> + + + + + ); + } + }; + + + return ( +
+

{label_title}

+
+ +
+ { format_id === "send" && +
exampleViewFilterToggleFunction(format_id)}> + +
+ } +
+ ); +}; + + +const InputComponent = ({id, variable, setFunction, format_id }) => { + const { t } = useTranslation(); + + const locale_base_path = "config_page.others.message_format_common.settings."; + const label_title = t(locale_base_path + "title"); + + const LABEL_ORIGINAL = t(locale_base_path + "original"); + const LABEL_TRANSLATED = t(locale_base_path + "translated"); + const LABEL_FOR_MULTI_TRANSLATION = t(locale_base_path + "for_multi_translation"); + + const replaceValue = (value) => { + if (value === "") return ""; + + const replaced = value.replace(/\\n/g, "\n"); + return replaced; + }; + + const handleChange = (parent_key, child_key) => (e) => { + const rawValue = e.target.value; + const parsedValue = replaceValue(rawValue); + + if (child_key !== undefined) { + setFunction({ + ...variable, + [parent_key]: { + ...variable[parent_key], + [child_key]: parsedValue + } + }); + } else { + setFunction({ + ...variable, + [parent_key]: parsedValue + }); + } + }; + + const toUiValue = (v) => { + if (typeof v === "string") { + return v.replace(/\n/g, "\\n"); + } + console.log("Empty"); + + return v ?? ""; + }; + + const resetFunction = () => { + if (format_id === "send") { + setFunction(ui_configs.send_message_format_parts); + } else if (format_id === "received") { + setFunction(ui_configs.received_message_format_parts); + } + }; + + const SwapButton = ({ variable, setFunction }) => { + const swapMessageAndTranslate = () => { + setFunction({ ...variable, translation_first: !variable.translation_first }); + }; + + return ( +
+

{variable.translation_first ? LABEL_TRANSLATED : LABEL_ORIGINAL}

+ Swap Icon +

{variable.translation_first ? LABEL_ORIGINAL : LABEL_TRANSLATED}

+
+ ); + }; + + return ( +
+

{label_title}

+
+
+ +
+ { !variable.translation_first ? +
+
+ <_Entry ui_variable={toUiValue(variable.message.prefix)} width={ENTRY_WIDTH} onChange={handleChange("message", "prefix")} /> +

{LABEL_ORIGINAL}

+ <_Entry ui_variable={toUiValue(variable.message.suffix)} width={ENTRY_WIDTH} onChange={handleChange("message", "suffix")} /> +
+
+ <_Entry ui_variable={toUiValue(variable.separator)} width={ENTRY_WIDTH} onChange={handleChange("separator")} /> +
+
+ <_Entry ui_variable={toUiValue(variable.translation.prefix)} width={ENTRY_WIDTH} onChange={handleChange("translation", "prefix")} /> +

{LABEL_TRANSLATED}

+ <_Entry ui_variable={toUiValue(variable.translation.suffix)} width={ENTRY_WIDTH} onChange={handleChange("translation", "suffix")} /> +
+
+ : +
+
+ <_Entry ui_variable={toUiValue(variable.translation.prefix)} width={ENTRY_WIDTH} onChange={handleChange("translation", "prefix")} /> +

{LABEL_TRANSLATED}

+ <_Entry ui_variable={toUiValue(variable.translation.suffix)} width={ENTRY_WIDTH} onChange={handleChange("translation", "suffix")} /> +
+
+ <_Entry ui_variable={toUiValue(variable.separator)} width={ENTRY_WIDTH} onChange={handleChange("separator")} /> +
+
+ <_Entry ui_variable={toUiValue(variable.message.prefix)} width={ENTRY_WIDTH} onChange={handleChange("message", "prefix")} /> +

{LABEL_ORIGINAL}

+ <_Entry ui_variable={toUiValue(variable.message.suffix)} width={ENTRY_WIDTH} onChange={handleChange("message", "suffix")} /> +
+
+ } + { format_id === "send" && +
+

{LABEL_FOR_MULTI_TRANSLATION}

+
+

{LABEL_TRANSLATED}

+ <_Entry ui_variable={toUiValue(variable.translation.separator)} width={ENTRY_WIDTH} onChange={handleChange("translation", "separator")} /> +

{LABEL_TRANSLATED}

+
+
+ } +
+ +
+
+
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/message_format/MessageFormat.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/message_format/MessageFormat.module.scss new file mode 100644 index 00000000..18244109 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/message_format/MessageFormat.module.scss @@ -0,0 +1,200 @@ +.container { + display: flex; + justify-content: center; + width: 100%; + gap: 2.6rem; + padding-bottom: 2rem; + margin: 1rem 0; + &.is_break_point { + flex-direction: column; + gap: 2.2rem; + align-items: center; + .border { + height: 0.1rem; + width: 60%; + margin: 0; + flex-shrink: 0; + } + .show_more_container { + margin-top: 2rem; + } + .example_container { + gap: 0; + } + .message_format_settings_container { + gap: 0; + } + } +} + +.border { + height: auto; + width: 0.1rem; + background-color: var(--dark_800_color); + margin: 3.2rem 0; + flex-shrink: 0; +} + +.section_title { + font-size: 1.4rem; + text-align: center; + width: 100%; +} + +.example_container { + display: flex; + flex-direction: column; + gap: 2rem; + min-width: 14rem; + max-width: 34rem; + width: 100%; +} + +.example_view_container { + display: flex; + flex-direction: column; + gap: 1.2rem; +} + +.example_wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.4rem; +} +.example_label { + font-size: 1.4rem; + text-align: start; + width: 100%; + color: var(--dark_basic_text_color); +} +.example_chatbox { + padding: 0.6rem; + background-color: #3A4554; + border-radius: 1rem; + width: 100%; + text-align: center; +} + +.example_text { + font-size: 1.2rem; +} + +.show_more_container { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding: 0.6rem 0; + cursor: pointer; + border-radius: 0.6rem; + &:hover { + background-color: var(--dark_850_color); + } + &:active { + background-color: var(--dark_875_color); + } +} + +.arrow_left_svg { + width: 2rem; + color: var(--dark_450_color); + &.to_down { + transform: rotate(-90deg); + } + &.to_up { + transform: rotate(90deg); + } +} + +.message_format_settings_container { + flex-direction: column; + display: flex; + align-items: center; + flex-shrink: 0; + gap: 2rem; +} + +.message_format_settings_wrapper { + display: flex; + flex-direction: column; + gap: 3.8rem; + width: 34rem; + flex-shrink: 0; +} + +.swap_button_container { + width: 100%; + display: flex; + justify-content: end; +} + +.swap_button_wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: 0.8rem; + padding: 0.6rem 1.2rem; + border-radius: 0.4rem; + background-color: var(--dark_850_color); + cursor: pointer; + &:hover { + background-color: var(--dark_800_color); + } + &:active { + background-color: var(--dark_900_color); + } +} + +.swap_text { + font-size: 1.4rem; +} + +.swap_img { + width: 2rem; +} + + +.input_wrapper { + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1.8rem; +} + +.input_contents { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + color: var(--dark_basic_text_color); +} + +.preset_text { + font-size: 1.6rem; + text-align: center; + flex-shrink: 0; + color: var(--dark_basic_text_color); +} + +.multi_translation_input_wrapper { + display: flex; + flex-direction: column; + gap: 1rem; + justify-content: center; + color: var(--dark_basic_text_color); +} + +.multi_translation_title { + font-size: 1.2rem; +} + +.reset_button_wrapper { + width: 100%; + display: flex; + justify-content: end; + align-items: center; +} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_templates/Templates.jsx b/src-ui/app/config_page/setting_section/setting_box/_templates/Templates.jsx index b25af8b9..b23f8043 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_templates/Templates.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_templates/Templates.jsx @@ -18,6 +18,7 @@ import { WordFilter, WordFilterListToggleComponent, DownloadModels, + MessageFormat, } from "../_components/"; import { Checkbox } from "@common_components"; @@ -129,4 +130,17 @@ export const WordFilterContainer = (props) => ( export const DownloadModelsContainer = (props) => ( -); \ No newline at end of file +); + +export const MessageFormatContainer = (props) => { + return ( +
+
+ +
+
+ +
+
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/about_vrct/poster_showcase_contents/poster_showcase_worlds_contents/PosterShowcaseWorldsContents.jsx b/src-ui/app/config_page/setting_section/setting_box/about_vrct/poster_showcase_contents/poster_showcase_worlds_contents/PosterShowcaseWorldsContents.jsx index e51c8ea5..4d2044e1 100644 --- a/src-ui/app/config_page/setting_section/setting_box/about_vrct/poster_showcase_contents/poster_showcase_worlds_contents/PosterShowcaseWorldsContents.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/about_vrct/poster_showcase_contents/poster_showcase_worlds_contents/PosterShowcaseWorldsContents.jsx @@ -37,7 +37,7 @@ export const PosterShowcaseWorldsContents = () => { ); if (poster.x_post_num !== null) { return ( - + {content} ); diff --git a/src-ui/app/config_page/setting_section/setting_box/appearance/Appearance.jsx b/src-ui/app/config_page/setting_section/setting_box/appearance/Appearance.jsx index 1f2f9eb4..cc7e730f 100644 --- a/src-ui/app/config_page/setting_section/setting_box/appearance/Appearance.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/appearance/Appearance.jsx @@ -69,6 +69,7 @@ const UiScalingContainer = () => { asyncUpdateBreakPoint(); }, [currentUiScaling.data]); + // [Duplicated] const createMarks = (min, max) => { const marks = []; for (let value = min; value <= max; value += 10) { @@ -111,6 +112,7 @@ export const MessageLogUiScalingContainer = () => { setUiMessageLogUiScaling(currentMessageLogUiScaling.data); }, [currentMessageLogUiScaling.data]); + // [Duplicated] const createMarks = (min, max) => { const marks = []; for (let value = min; value <= max; value += 10) { @@ -207,6 +209,7 @@ const TransparencyContainer = () => { setUiTransparency(currentTransparency.data); }, [currentTransparency.data]); + // [Duplicated] const createMarks = (min, max) => { const marks = []; for (let value = min; value <= max; value += 10) { diff --git a/src-ui/app/config_page/setting_section/setting_box/others/Others.jsx b/src-ui/app/config_page/setting_section/setting_box/others/Others.jsx index 1baf00a5..65027ef3 100644 --- a/src-ui/app/config_page/setting_section/setting_box/others/Others.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/others/Others.jsx @@ -8,6 +8,7 @@ import { import { CheckboxContainer, + MessageFormatContainer, } from "../_templates/Templates"; import { @@ -39,6 +40,11 @@ export const Others = () => {
+
+ + + +
); }; @@ -151,4 +157,48 @@ const SendReceivedMessageToVrcContainer = () => { toggleFunction={toggleEnableSendReceivedMessageToVrc} /> ); +}; + +const SendMessageFormatPartsContainer = () => { + const { t } = useI18n(); + const { + currentSendMessageFormatParts, + setSendMessageFormatParts, + currentMessageFormat_ExampleViewFilter, + toggleMessageFormat_ExampleViewFilter, + } = useOthers(); + + return ( + + ); +}; + +const ReceivedMessageFormatPartsContainer = () => { + const { t } = useI18n(); + const { + currentReceivedMessageFormatParts, + setReceivedMessageFormatParts, + currentMessageFormat_ExampleViewFilter, + toggleMessageFormat_ExampleViewFilter, + } = useOthers(); + + return ( + + ); }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 6c80beb2..4115b667 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,10 +1,10 @@ -import { useEffect, useRef, useState, useCallback } from "react"; +import { useEffect, useRef } from "react"; import { useI18n } from "@useI18n"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; import { PluginsControlComponent } from "./plugins_control_component/PluginsControlComponent"; import { useNotificationStatus } from "@logics_common"; -import ExternalLink from "@images/external_link.svg?react"; +import { HomepageLinkButton } from "@common_components"; export const Plugins = () => { const { @@ -125,55 +125,4 @@ const PluginDownloadContainer = () => { })}
); -}; - -const HomepageLinkButton = ({ homepage_link, speed = 40 /* px/s */ }) => { - const containerRef = useRef(null); - const textRef = useRef(null); - const [inlineStyle, setInlineStyle] = useState({}); - - const handleMouseEnter = useCallback(() => { - const container = containerRef.current; - const text = textRef.current; - if (!container || !text) return; - const overflow = text.scrollWidth - container.clientWidth; - if (overflow > 0) { - const duration = overflow / speed; - setInlineStyle({ - transform: `translateX(-${overflow}px)`, - transition: `transform ${duration}s linear`, - }); - } - }, [speed]); - - const handleMouseLeave = useCallback(() => { - setInlineStyle({ - transform: 'translateX(0)', - transition: 'transform 0.3s ease-out', - }); - }, []); - - return ( -
- -
-

- {homepage_link} -

-
- -
-
- ); }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss index 2ad924bc..05c49046 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss @@ -56,45 +56,4 @@ // overflow: hidden; // white-space: nowrap; // text-overflow: ellipsis; -// } - -.open_homepage_button_wrapper { - width: 100%; -} - -.open_homepage_button { - padding: 0.6rem 0; - display: flex; - align-items: center; - gap: 1rem; - cursor: pointer; - text-decoration: none; - &:hover .external_link_svg { - color: var(--dark_200_color); - } - &:hover .open_homepage_text { - border-bottom: 0.1rem solid var(--dark_500_color); - } -} - -.text_container { - position: relative; - overflow: hidden; - flex: 1; /* テキスト部分をアイコンまで伸ばす */ -} - -.open_homepage_text { - margin: 0; /* pタグのデフォルトマージン除去 */ - font-size: 1.4rem; - color: var(--sent_400_color); - white-space: nowrap; - display: inline-block; - transform: translateX(0); - border-bottom: 0.1rem solid var(--sent_400_color); -} - -.external_link_svg { - flex-shrink: 0; - width: 1.6rem; - color: var(--dark_500_color); -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/transcription/Transcription.jsx b/src-ui/app/config_page/setting_section/setting_box/transcription/Transcription.jsx index 279c1d9d..7416c970 100644 --- a/src-ui/app/config_page/setting_section/setting_box/transcription/Transcription.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/transcription/Transcription.jsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from "react"; import { useI18n } from "@useI18n"; import styles from "./Transcription.module.scss"; import { updateLabelsById, genNumObjArray } from "@utils"; @@ -12,6 +13,7 @@ import { RadioButtonContainer, DropdownMenuContainer, ComputeDeviceContainer, + SliderContainer, } from "../_templates/Templates"; import { @@ -24,6 +26,7 @@ export const Transcription = () => { +
); }; @@ -353,4 +356,196 @@ const findKeyByDeviceValue = (devices, target_value) => { } } return null; +}; + + + + + +const Advanced_Container = () => { + const { t } = useI18n(); + return ( +
+ + {/* */} + + + + +
+ ); + + +}; + +export const MicAvgLogprobContainer = () => { + const { t } = useI18n(); + const { currentMicAvgLogprob, setMicAvgLogprob } = useTranscription(); + const [ui_mic_avg_logprob, setUiMicAvgLogprob] = useState(currentMicAvgLogprob.data); + + const onchangeFunction = (value) => { + setUiMicAvgLogprob(value); + }; + const onchangeCommittedFunction = (value) => { + setMicAvgLogprob(value); + }; + useEffect(() => { + setUiMicAvgLogprob(currentMicAvgLogprob.data); + }, [currentMicAvgLogprob.data]); + + // [Duplicated] + const createMarks = (min, max) => { + const marks = []; + for (let value = min; value <= max; value += 0.2) { + value = parseFloat(value.toFixed(1)); + marks.push({ value, label: `${value}` }); + } + return marks; + }; + + const marks = createMarks(-2, 0); + + return ( + + ); +}; + +export const MicNoSpeechProbContainer = () => { + const { t } = useI18n(); + const { currentMicNoSpeechProb, setMicNoSpeechProb } = useTranscription(); + const [ui_mic_no_speech_prob, setUiMicNoSpeechProb] = useState(currentMicNoSpeechProb.data); + + const onchangeFunction = (value) => { + setUiMicNoSpeechProb(value); + }; + const onchangeCommittedFunction = (value) => { + setMicNoSpeechProb(value); + }; + useEffect(() => { + setUiMicNoSpeechProb(currentMicNoSpeechProb.data); + }, [currentMicNoSpeechProb.data]); + + // [Duplicated] + const createMarks = (min, max) => { + const marks = []; + for (let value = min; value <= max; value += 0.1) { + value = parseFloat(value.toFixed(1)); + marks.push({ value, label: `${value}` }); + } + return marks; + }; + + const marks = createMarks(0, 1); + + return ( + + ); +}; + +export const SpeakerAvgLogprobContainer = () => { + const { t } = useI18n(); + const { currentSpeakerAvgLogprob, setSpeakerAvgLogprob } = useTranscription(); + const [ui_speaker_avg_logprob, setUiSpeakerAvgLogprob] = useState(currentSpeakerAvgLogprob.data); + + const onchangeFunction = (value) => { + setUiSpeakerAvgLogprob(value); + }; + const onchangeCommittedFunction = (value) => { + setSpeakerAvgLogprob(value); + }; + useEffect(() => { + setUiSpeakerAvgLogprob(currentSpeakerAvgLogprob.data); + }, [currentSpeakerAvgLogprob.data]); + + // [Duplicated] + const createMarks = (min, max) => { + const marks = []; + for (let value = min; value <= max; value += 0.2) { + value = parseFloat(value.toFixed(1)); + marks.push({ value, label: `${value}` }); + } + return marks; + }; + + const marks = createMarks(-2, 0); + + return ( + + ); +}; + +export const SpeakerNoSpeechProbContainer = () => { + const { t } = useI18n(); + const { currentSpeakerNoSpeechProb, setSpeakerNoSpeechProb } = useTranscription(); + const [ui_speaker_no_speech_prob, setUiSpeakerNoSpeechProb] = useState(currentSpeakerNoSpeechProb.data); + + const onchangeFunction = (value) => { + setUiSpeakerNoSpeechProb(value); + }; + const onchangeCommittedFunction = (value) => { + setSpeakerNoSpeechProb(value); + }; + useEffect(() => { + setUiSpeakerNoSpeechProb(currentSpeakerNoSpeechProb.data); + }, [currentSpeakerNoSpeechProb.data]); + + // [Duplicated] + const createMarks = (min, max) => { + const marks = []; + for (let value = min; value <= max; value += 0.1) { + value = parseFloat(value.toFixed(1)); + marks.push({ value, label: `${value}` }); + } + return marks; + }; + + const marks = createMarks(0, 1); + + return ( + + ); }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx index 31c38816..4bd9b6f5 100644 --- a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx @@ -14,12 +14,12 @@ import { SectionLabelComponent, } from "../_components/"; +import { ResetButton } from "@common_components"; + import { useVr, } from "@logics_configs"; -import RedoSvg from "@images/redo.svg?react"; - import SquareSvg from "@images/square.svg?react"; import TriangleSvg from "@images/triangle.svg?react"; import { randomIntMinMax } from "@utils"; @@ -528,13 +528,6 @@ const CommonSettingsContainer = () => { ); }; -const ResetButton = ({onClickFunction}) => { - return ( - - ); -}; const SendSampleTextToggleButton = () => { const { t } = useI18n(); diff --git a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss index 9d0963e8..a2a76261 100644 --- a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss @@ -301,34 +301,6 @@ @include variable-button-wrapper(bottom, 50%, right, -60%, -45deg); } - - -.slider_reset_button { - background-color: var(--dark_875_color); - padding: 0.6rem; - display: flex; - justify-content: center; - align-items: center; - border-radius: 0.4rem; - flex-shrink: 0; - &:hover { - background-color: var(--dark_825_color); - & .slider_reset_svg { - color: var(--dark_200_color); - } - } - &:active { - background-color: var(--dark_925_color); - } -} - -.slider_reset_svg { - width: 1.4rem; - color: var(--dark_550_color); -} - - - .other_controls { margin-top: 6rem; display: flex; diff --git a/src-ui/app/others/snackbar_controller/SnackbarController.jsx b/src-ui/app/others/snackbar_controller/SnackbarController.jsx index 60edac1f..d2197e1b 100644 --- a/src-ui/app/others/snackbar_controller/SnackbarController.jsx +++ b/src-ui/app/others/snackbar_controller/SnackbarController.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, isValidElement } from "react"; import { ToastContainer, toast, cssTransition } from "react-toastify"; import clsx from "clsx"; diff --git a/src-ui/common_components/homepage_link_button/HomepageLinkButton.jsx b/src-ui/common_components/homepage_link_button/HomepageLinkButton.jsx new file mode 100644 index 00000000..b68f235f --- /dev/null +++ b/src-ui/common_components/homepage_link_button/HomepageLinkButton.jsx @@ -0,0 +1,54 @@ +import ExternalLink from "@images/external_link.svg?react"; +import styles from "./HomepageLinkButton.module.scss"; +import { useRef, useState, useCallback } from "react"; + +export const HomepageLinkButton = ({ homepage_link, speed = 40 /* px/s */ }) => { + const containerRef = useRef(null); + const textRef = useRef(null); + const [inlineStyle, setInlineStyle] = useState({}); + + const handleMouseEnter = useCallback(() => { + const container = containerRef.current; + const text = textRef.current; + if (!container || !text) return; + const overflow = text.scrollWidth - container.clientWidth; + if (overflow > 0) { + const duration = overflow / speed; + setInlineStyle({ + transform: `translateX(-${overflow}px)`, + transition: `transform ${duration}s linear`, + }); + } + }, [speed]); + + const handleMouseLeave = useCallback(() => { + setInlineStyle({ + transform: 'translateX(0)', + transition: 'transform 0.3s ease-out', + }); + }, []); + + return ( +
+ +
+

+ {homepage_link} +

+
+ +
+
+ ); +}; \ No newline at end of file diff --git a/src-ui/common_components/homepage_link_button/HomepageLinkButton.module.scss b/src-ui/common_components/homepage_link_button/HomepageLinkButton.module.scss new file mode 100644 index 00000000..888d8a6f --- /dev/null +++ b/src-ui/common_components/homepage_link_button/HomepageLinkButton.module.scss @@ -0,0 +1,39 @@ +.open_homepage_button_wrapper { + width: 100%; +} + +.open_homepage_button { + padding: 0.6rem 0; + display: flex; + align-items: center; + gap: 1rem; + cursor: pointer; + text-decoration: none; + &:hover .external_link_svg { + color: var(--dark_200_color); + } + &:hover .open_homepage_text { + border-bottom: 0.1rem solid var(--dark_500_color); + } +} + +.text_container { + position: relative; + overflow: hidden; +} + +.open_homepage_text { + margin: 0; /* pタグのデフォルトマージン除去 */ + font-size: 1.4rem; + color: var(--sent_400_color); + white-space: nowrap; + display: inline-block; + transform: translateX(0); + border-bottom: 0.1rem solid var(--sent_400_color); +} + +.external_link_svg { + flex-shrink: 0; + width: 1.6rem; + color: var(--dark_500_color); +} \ No newline at end of file diff --git a/src-ui/common_components/index.js b/src-ui/common_components/index.js index 244dff53..e047306c 100644 --- a/src-ui/common_components/index.js +++ b/src-ui/common_components/index.js @@ -1 +1,3 @@ -export { Checkbox } from "./checkbox/Checkbox"; \ No newline at end of file +export { Checkbox } from "./checkbox/Checkbox"; +export { HomepageLinkButton } from "./homepage_link_button/HomepageLinkButton"; +export { ResetButton } from "./reset_button/ResetButton"; \ No newline at end of file diff --git a/src-ui/common_components/reset_button/ResetButton.jsx b/src-ui/common_components/reset_button/ResetButton.jsx new file mode 100644 index 00000000..65974739 --- /dev/null +++ b/src-ui/common_components/reset_button/ResetButton.jsx @@ -0,0 +1,10 @@ +import styles from "./ResetButton.module.scss"; +import RedoSvg from "@images/redo.svg?react"; + +export const ResetButton = ({ onClickFunction }) => { + return ( + + ); +}; \ No newline at end of file diff --git a/src-ui/common_components/reset_button/ResetButton.module.scss b/src-ui/common_components/reset_button/ResetButton.module.scss new file mode 100644 index 00000000..9be83c07 --- /dev/null +++ b/src-ui/common_components/reset_button/ResetButton.module.scss @@ -0,0 +1,24 @@ +.reset_button { + width: fit-content; + background-color: var(--dark_875_color); + padding: 0.6rem; + display: flex; + justify-content: center; + align-items: center; + border-radius: 0.4rem; + flex-shrink: 0; + &:hover { + background-color: var(--dark_825_color); + & .reset_svg { + color: var(--dark_200_color); + } + } + &:active { + background-color: var(--dark_925_color); + } +} + +.reset_svg { + width: 1.4rem; + color: var(--dark_550_color); +} \ No newline at end of file diff --git a/src-ui/logics/common/useIsVrctAvailable.js b/src-ui/logics/common/useIsVrctAvailable.js deleted file mode 100644 index 569a0210..00000000 --- a/src-ui/logics/common/useIsVrctAvailable.js +++ /dev/null @@ -1,21 +0,0 @@ -import { useStore_IsVrctAvailable } from "@store"; -import { useNotificationStatus } from "@logics_common"; - -export const useIsVrctAvailable = () => { - const { currentIsVrctAvailable, updateIsVrctAvailable } = useStore_IsVrctAvailable(); - const { showNotification_Success, showNotification_Error } = useNotificationStatus(); - - const handleAiModelsAvailability = (is_ai_models_available) => { - if (is_ai_models_available === false) { - updateIsVrctAvailable(false); - showNotification_Error("AI models have not been detected. Check the network connection and restart VRCT (it will download automatically, normally).", { hide_duration: null }); - } - }; - - return { - currentIsVrctAvailable, - updateIsVrctAvailable, - - handleAiModelsAvailability, - }; -}; \ No newline at end of file diff --git a/src-ui/logics/common/useIsVrctAvailable.jsx b/src-ui/logics/common/useIsVrctAvailable.jsx new file mode 100644 index 00000000..613f0a61 --- /dev/null +++ b/src-ui/logics/common/useIsVrctAvailable.jsx @@ -0,0 +1,33 @@ +import { useStore_IsVrctAvailable } from "@store"; +import { useNotificationStatus } from "@logics_common"; +import { HomepageLinkButton } from "@common_components"; + +export const useIsVrctAvailable = () => { + const { currentIsVrctAvailable, updateIsVrctAvailable } = useStore_IsVrctAvailable(); + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); + + const handleAiModelsAvailability = (is_ai_models_available) => { + if (is_ai_models_available === false) { + updateIsVrctAvailable(false); + const ErrorComponent = () => { + return ( +
+

AI models have not been detected. Check the network connection and restart VRCT (it will download automatically, normally).

+

If this error occurs frequently, try the following:

+ +
+ ); + }; + showNotification_Error(ErrorComponent, { hide_duration: null }); + } + }; + + return { + currentIsVrctAvailable, + updateIsVrctAvailable, + + handleAiModelsAvailability, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/configs/others/useOthers.js b/src-ui/logics/configs/others/useOthers.js index 62a14364..c2b0179f 100644 --- a/src-ui/logics/configs/others/useOthers.js +++ b/src-ui/logics/configs/others/useOthers.js @@ -6,6 +6,9 @@ import { useStore_EnableSendMessageToVrc, useStore_EnableNotificationVrcSfx, useStore_EnableSendReceivedMessageToVrc, + useStore_MessageFormat_ExampleViewFilter, + useStore_SendMessageFormatParts, + useStore_ReceivedMessageFormatParts, } from "@store"; import { useStdoutToPython } from "@useStdoutToPython"; import { useNotificationStatus } from "@logics_common"; @@ -29,6 +32,12 @@ export const useOthers = () => { // Speaker2Chatbox // Send Received Message To VRC const { currentEnableSendReceivedMessageToVrc, updateEnableSendReceivedMessageToVrc, pendingEnableSendReceivedMessageToVrc } = useStore_EnableSendReceivedMessageToVrc(); + // Message Formats + const { currentMessageFormat_ExampleViewFilter, updateMessageFormat_ExampleViewFilter, pendingMessageFormat_ExampleViewFilter } = useStore_MessageFormat_ExampleViewFilter(); + // Send + const { currentSendMessageFormatParts, updateSendMessageFormatParts, pendingSendMessageFormatParts } = useStore_SendMessageFormatParts(); + // Received + const { currentReceivedMessageFormatParts, updateReceivedMessageFormatParts, pendingReceivedMessageFormatParts } = useStore_ReceivedMessageFormatParts(); const { showNotification_SaveSuccess } = useNotificationStatus(); @@ -107,6 +116,10 @@ export const useOthers = () => { } }; + const getSuccessEnableVrcMicMuteSync = (is_enabled) => { + updateEnableVrcMicMuteSync(old => ({ ...old.data, is_enabled: is_enabled })); + }; + const setSuccessEnableVrcMicMuteSync = (is_enabled) => { updateEnableVrcMicMuteSync(old => ({ ...old.data, is_enabled: is_enabled })); showNotification_SaveSuccess(); @@ -174,6 +187,53 @@ export const useOthers = () => { showNotification_SaveSuccess(); }; + // Message Formats + // Send + const getSendMessageFormatParts = () => { + pendingSendMessageFormatParts(); + asyncStdoutToPython("/get/data/send_message_format_parts"); + }; + + const setSendMessageFormatParts = (message_format_parts) => { + pendingSendMessageFormatParts(); + asyncStdoutToPython("/set/data/send_message_format_parts", message_format_parts); + }; + + const setSuccessSendMessageFormatParts = (message_format_parts) => { + updateSendMessageFormatParts(message_format_parts); + showNotification_SaveSuccess(); + }; + + // Received + const getReceivedMessageFormatParts = () => { + pendingReceivedMessageFormatParts(); + asyncStdoutToPython("/get/data/received_message_format_parts"); + }; + + const setReceivedMessageFormatParts = (message_format_parts) => { + pendingReceivedMessageFormatParts(); + asyncStdoutToPython("/set/data/received_message_format_parts", message_format_parts); + }; + + const setSuccessReceivedMessageFormatParts = (message_format_parts) => { + updateReceivedMessageFormatParts(message_format_parts); + showNotification_SaveSuccess(); + }; + + + const toggleMessageFormat_ExampleViewFilter = (id) => { + pendingMessageFormat_ExampleViewFilter(); + if (["send", "received"].includes(id) === false) return console.error(`id should be small case 'send' or 'received'. got id: ${id}`); + + updateMessageFormat_ExampleViewFilter({ + ...currentMessageFormat_ExampleViewFilter.data, + [id]: currentMessageFormat_ExampleViewFilter.data[id] === "Simplified" + ? "All" + : "Simplified" + }); + }; + + return { // Auto Clear Message Input Box currentEnableAutoClearMessageInputBox, @@ -199,6 +259,7 @@ export const useOthers = () => { // VRC Mic Mute Sync currentEnableVrcMicMuteSync, getEnableVrcMicMuteSync, + getSuccessEnableVrcMicMuteSync, toggleEnableVrcMicMuteSync, updateEnableVrcMicMuteSync, setSuccessEnableVrcMicMuteSync, @@ -225,5 +286,22 @@ export const useOthers = () => { toggleEnableSendReceivedMessageToVrc, updateEnableSendReceivedMessageToVrc, setSuccessEnableSendReceivedMessageToVrc, + + // Message Formats + currentMessageFormat_ExampleViewFilter, + toggleMessageFormat_ExampleViewFilter, + // Send + currentSendMessageFormatParts, + updateSendMessageFormatParts, + getSendMessageFormatParts, + setSendMessageFormatParts, + setSuccessSendMessageFormatParts, + + // Received + currentReceivedMessageFormatParts, + updateReceivedMessageFormatParts, + getReceivedMessageFormatParts, + setReceivedMessageFormatParts, + setSuccessReceivedMessageFormatParts, }; }; \ No newline at end of file diff --git a/src-ui/logics/configs/transcription/useTranscription.js b/src-ui/logics/configs/transcription/useTranscription.js index 0c4b8b2b..294b0473 100644 --- a/src-ui/logics/configs/transcription/useTranscription.js +++ b/src-ui/logics/configs/transcription/useTranscription.js @@ -14,6 +14,11 @@ import { useStore_SelectedWhisperWeightType, useStore_WhisperWeightTypeStatus, + + useStore_MicAvgLogprob, + useStore_MicNoSpeechProb, + useStore_SpeakerAvgLogprob, + useStore_SpeakerNoSpeechProb, } from "@store"; import { useStdoutToPython } from "@useStdoutToPython"; import { transformToIndexedArray } from "@utils"; @@ -41,6 +46,13 @@ export const useTranscription = () => { const { currentSelectableWhisperComputeDeviceList, updateSelectableWhisperComputeDeviceList, pendingSelectableWhisperComputeDeviceList } = useStore_SelectableWhisperComputeDeviceList(); const { currentSelectedWhisperComputeDevice, updateSelectedWhisperComputeDevice, pendingSelectedWhisperComputeDevice } = useStore_SelectedWhisperComputeDevice(); + // Advanced Settings + const { currentMicAvgLogprob, updateMicAvgLogprob, pendingMicAvgLogprob } = useStore_MicAvgLogprob(); + const { currentMicNoSpeechProb, updateMicNoSpeechProb, pendingMicNoSpeechProb } = useStore_MicNoSpeechProb(); + const { currentSpeakerAvgLogprob, updateSpeakerAvgLogprob, pendingSpeakerAvgLogprob } = useStore_SpeakerAvgLogprob(); + const { currentSpeakerNoSpeechProb, updateSpeakerNoSpeechProb, pendingSpeakerNoSpeechProb } = useStore_SpeakerNoSpeechProb(); + + // Mic const getMicRecordTimeout = () => { pendingMicRecordTimeout(); @@ -97,6 +109,21 @@ export const useTranscription = () => { asyncStdoutToPython("/set/data/mic_word_filter", selected_mic_word_filter); }; + const getSuccessMicWordFilterList = (payload) => { + updateMicWordFilterList((prev_list) => { + const updated_list = [...prev_list.data]; + for (const value of payload) { + const existing_item = updated_list.find(item => item.value === value); + if (existing_item) { + existing_item.is_redoable = false; + } else { + updated_list.push({ value, is_redoable: false }); + } + } + return updated_list; + }); + }; + const setSuccessMicWordFilterList = (payload) => { updateMicWordFilterList((prev_list) => { const updated_list = [...prev_list.data]; @@ -261,6 +288,67 @@ export const useTranscription = () => { showNotification_SaveSuccess(); }; + // Advanced (Mic Avg Logprob) + const getMicAvgLogprob = () => { + pendingMicAvgLogprob(); + asyncStdoutToPython("/get/data/mic_avg_logprob"); + }; + + const setMicAvgLogprob = (selected_mic_avg_logprob) => { + pendingMicAvgLogprob(); + asyncStdoutToPython("/set/data/mic_avg_logprob", selected_mic_avg_logprob); + }; + + const setSuccessMicAvgLogprob = (selected_mic_avg_logprob) => { + updateMicAvgLogprob(selected_mic_avg_logprob); + showNotification_SaveSuccess(); + }; + // Advanced (Mic No Speech Prob) + const getMicNoSpeechProb = () => { + pendingMicNoSpeechProb(); + asyncStdoutToPython("/get/data/mic_no_speech_prob"); + }; + + const setMicNoSpeechProb = (selected_mic_no_speech_prob) => { + pendingMicNoSpeechProb(); + asyncStdoutToPython("/set/data/mic_no_speech_prob", selected_mic_no_speech_prob); + }; + + const setSuccessMicNoSpeechProb = (selected_mic_no_speech_prob) => { + updateMicNoSpeechProb(selected_mic_no_speech_prob); + showNotification_SaveSuccess(); + }; + // Advanced (Speaker Avg Logprob) + const getSpeakerAvgLogprob = () => { + pendingSpeakerAvgLogprob(); + asyncStdoutToPython("/get/data/speaker_avg_logprob"); + }; + + const setSpeakerAvgLogprob = (selected_speaker_avg_logprob) => { + pendingSpeakerAvgLogprob(); + asyncStdoutToPython("/set/data/speaker_avg_logprob", selected_speaker_avg_logprob); + }; + + const setSuccessSpeakerAvgLogprob = (selected_speaker_avg_logprob) => { + updateSpeakerAvgLogprob(selected_speaker_avg_logprob); + showNotification_SaveSuccess(); + }; + // Advanced (Speaker No Speech Prob) + const getSpeakerNoSpeechProb = () => { + pendingSpeakerNoSpeechProb(); + asyncStdoutToPython("/get/data/speaker_no_speech_prob"); + }; + + const setSpeakerNoSpeechProb = (selected_speaker_no_speech_prob) => { + pendingSpeakerNoSpeechProb(); + asyncStdoutToPython("/set/data/speaker_no_speech_prob", selected_speaker_no_speech_prob); + }; + + const setSuccessSpeakerNoSpeechProb = (selected_speaker_no_speech_prob) => { + updateSpeakerNoSpeechProb(selected_speaker_no_speech_prob); + showNotification_SaveSuccess(); + }; + return { // Mic currentMicRecordTimeout, @@ -283,6 +371,7 @@ export const useTranscription = () => { currentMicWordFilterList, getMicWordFilterList, + getSuccessMicWordFilterList, updateMicWordFilterList, setMicWordFilterList, setSuccessMicWordFilterList, @@ -337,5 +426,31 @@ export const useTranscription = () => { updateSelectedWhisperComputeDevice, setSelectedWhisperComputeDevice, setSuccessSelectedWhisperComputeDevice, + + // Advanced + // Mic Avg Logprob + currentMicAvgLogprob, + getMicAvgLogprob, + updateMicAvgLogprob, + setMicAvgLogprob, + setSuccessMicAvgLogprob, + // Mic No Speech Prob + currentMicNoSpeechProb, + getMicNoSpeechProb, + updateMicNoSpeechProb, + setMicNoSpeechProb, + setSuccessMicNoSpeechProb, + // Speaker Avg Logprob + currentSpeakerAvgLogprob, + getSpeakerAvgLogprob, + updateSpeakerAvgLogprob, + setSpeakerAvgLogprob, + setSuccessSpeakerAvgLogprob, + // Speaker No Speech Prob + currentSpeakerNoSpeechProb, + getSpeakerNoSpeechProb, + updateSpeakerNoSpeechProb, + setSpeakerNoSpeechProb, + setSuccessSpeakerNoSpeechProb, }; }; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index d83054d3..c36b5fa7 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -194,7 +194,7 @@ export const ROUTE_META_LIST = [ { endpoint: "/get/data/mic_max_phrases", ns: configs, hook_name: "useTranscription", method_name: "updateMicMaxWords" }, { endpoint: "/set/data/mic_max_phrases", ns: configs, hook_name: "useTranscription", method_name: "setSuccessMicMaxWords" }, - { endpoint: "/get/data/mic_word_filter", ns: configs, hook_name: "useTranscription", method_name: "updateMicWordFilterList" }, + { endpoint: "/get/data/mic_word_filter", ns: configs, hook_name: "useTranscription", method_name: "getSuccessMicWordFilterList" }, { endpoint: "/set/data/mic_word_filter", ns: configs, hook_name: "useTranscription", method_name: "setSuccessMicWordFilterList" }, // Transcription (Speaker) @@ -224,6 +224,16 @@ export const ROUTE_META_LIST = [ { endpoint: "/get/data/selected_transcription_compute_device", ns: configs, hook_name: "useTranscription", method_name: "updateSelectedWhisperComputeDevice" }, { endpoint: "/set/data/selected_transcription_compute_device", ns: configs, hook_name: "useTranscription", method_name: "setSuccessSelectedWhisperComputeDevice" }, + // Transcription (Advanced) + { endpoint: "/get/data/mic_avg_logprob", ns: configs, hook_name: "useTranscription", method_name: "updateMicAvgLogprob" }, + { endpoint: "/set/data/mic_avg_logprob", ns: configs, hook_name: "useTranscription", method_name: "setSuccessMicAvgLogprob" }, + { endpoint: "/get/data/mic_no_speech_prob", ns: configs, hook_name: "useTranscription", method_name: "updateMicNoSpeechProb" }, + { endpoint: "/set/data/mic_no_speech_prob", ns: configs, hook_name: "useTranscription", method_name: "setSuccessMicNoSpeechProb" }, + { endpoint: "/get/data/speaker_avg_logprob", ns: configs, hook_name: "useTranscription", method_name: "updateSpeakerAvgLogprob" }, + { endpoint: "/set/data/speaker_avg_logprob", ns: configs, hook_name: "useTranscription", method_name: "setSuccessSpeakerAvgLogprob" }, + { endpoint: "/get/data/speaker_no_speech_prob", ns: configs, hook_name: "useTranscription", method_name: "updateSpeakerNoSpeechProb" }, + { endpoint: "/set/data/speaker_no_speech_prob", ns: configs, hook_name: "useTranscription", method_name: "setSuccessSpeakerNoSpeechProb" }, + // VR { endpoint: "/get/data/overlay_small_log", ns: configs, hook_name: "useVr", method_name: "updateIsEnabledOverlaySmallLog" }, { endpoint: "/set/enable/overlay_small_log", ns: configs, hook_name: "useVr", method_name: "setSuccessIsEnabledOverlaySmallLog" }, @@ -259,7 +269,7 @@ export const ROUTE_META_LIST = [ { endpoint: "/set/enable/logger_feature", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableAutoExportMessageLogs" }, { endpoint: "/set/disable/logger_feature", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableAutoExportMessageLogs" }, - { endpoint: "/get/data/vrc_mic_mute_sync", ns: configs, hook_name: "useOthers", method_name: "updateEnableVrcMicMuteSync_FromBackend" }, + { endpoint: "/get/data/vrc_mic_mute_sync", ns: configs, hook_name: "useOthers", method_name: "getSuccessEnableVrcMicMuteSync" }, { endpoint: "/set/enable/vrc_mic_mute_sync", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableVrcMicMuteSync" }, { endpoint: "/set/disable/vrc_mic_mute_sync", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableVrcMicMuteSync" }, @@ -275,6 +285,12 @@ export const ROUTE_META_LIST = [ { endpoint: "/get/data/notification_vrc_sfx", ns: configs, hook_name: "useOthers", method_name: "updateEnableNotificationVrcSfx" }, { endpoint: "/set/enable/notification_vrc_sfx", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableNotificationVrcSfx" }, { endpoint: "/set/disable/notification_vrc_sfx", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableNotificationVrcSfx" }, + { endpoint: "/set/disable/notification_vrc_sfx", ns: configs, hook_name: "useOthers", method_name: "setSuccessEnableNotificationVrcSfx" }, + + { endpoint: "/get/data/send_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "updateSendMessageFormatParts" }, + { endpoint: "/set/data/send_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "setSuccessSendMessageFormatParts" }, + { endpoint: "/get/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "updateReceivedMessageFormatParts" }, + { endpoint: "/set/data/received_message_format_parts", ns: configs, hook_name: "useOthers", method_name: "setSuccessReceivedMessageFormatParts" }, // Hotkeys { endpoint: "/get/data/hotkeys", ns: configs, hook_name: "useHotkeys", method_name: "updateHotkeys" }, @@ -334,6 +350,9 @@ export const useReceiveRoutes = () => { ROUTE_META_LIST.map(({ endpoint, hook_name, method_name }) => { const result_obj = hook_results[hook_name] || {}; const fn = result_obj[method_name]; + if (fn === undefined && method_name !== null) { + console.error("Method not found.", {endpoint, hook_name, method_name, result_obj, fn}); + } return [endpoint, typeof fn === "function" ? fn : noop]; }) ); diff --git a/src-ui/store.js b/src-ui/store.js index 7d2fac28..e36aad46 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -238,6 +238,11 @@ export const { atomInstance: Atom_SelectedTranscriptionEngine, useHook: useStore export const { atomInstance: Atom_SelectableWhisperComputeDeviceList, useHook: useStore_SelectableWhisperComputeDeviceList } = createAtomWithHook({}, "SelectableWhisperComputeDeviceList"); export const { atomInstance: Atom_SelectedWhisperComputeDevice, useHook: useStore_SelectedWhisperComputeDevice } = createAtomWithHook("", "SelectedWhisperComputeDevice"); +export const { atomInstance: Atom_MicAvgLogprob, useHook: useStore_MicAvgLogprob } = createAtomWithHook(-0.8, "MicAvgLogprob"); +export const { atomInstance: Atom_MicNoSpeechProb, useHook: useStore_MicNoSpeechProb } = createAtomWithHook(0.6, "MicNoSpeechProb"); +export const { atomInstance: Atom_SpeakerAvgLogprob, useHook: useStore_SpeakerAvgLogprob } = createAtomWithHook(-0.8, "SpeakerAvgLogprob"); +export const { atomInstance: Atom_SpeakerNoSpeechProb, useHook: useStore_SpeakerNoSpeechProb } = createAtomWithHook(0.6, "SpeakerNoSpeechProb"); + // VR export const { atomInstance: Atom_OverlaySmallLogSettings, useHook: useStore_OverlaySmallLogSettings } = createAtomWithHook({ @@ -274,6 +279,37 @@ export const { atomInstance: Atom_EnableVrcMicMuteSync, useHook: useStore_Enable export const { atomInstance: Atom_EnableSendMessageToVrc, useHook: useStore_EnableSendMessageToVrc } = createAtomWithHook(true, "EnableSendMessageToVrc"); export const { atomInstance: Atom_EnableSendReceivedMessageToVrc, useHook: useStore_EnableSendReceivedMessageToVrc } = createAtomWithHook(false, "EnableSendReceivedMessageToVrc"); export const { atomInstance: Atom_EnableNotificationVrcSfx, useHook: useStore_EnableNotificationVrcSfx } = createAtomWithHook(true, "EnableNotificationVrcSfx"); +export const { atomInstance: Atom_MessageFormat_ExampleViewFilter, useHook: useStore_MessageFormat_ExampleViewFilter } = createAtomWithHook({ + send: "Simplified", + received: "Simplified", +}, "MessageFormat_ExampleViewFilter"); +export const { atomInstance: Atom_SendMessageFormatParts, useHook: useStore_SendMessageFormatParts } = createAtomWithHook({ + message: { + prefix: "", + suffix: "" + }, + separator: "\n", + translation: { + prefix: "", + separator: "\n", + suffix: "" + }, + translation_first: false, +}, "SendMessageFormatParts"); +export const { atomInstance: Atom_ReceivedMessageFormatParts, useHook: useStore_ReceivedMessageFormatParts } = createAtomWithHook({ + message: { + prefix: "", + suffix: "" + }, + separator: "\n", + translation: { + prefix: "", + separator: "\n", + suffix: "" + }, + translation_first: false, +}, "ReceivedMessageFormatParts"); + // Hotkeys export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createAtomWithHook({ diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index 915d6a6b..e3bf85f2 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -49,6 +49,33 @@ export const ui_configs = { tracker: "LeftHand", }, + send_message_format_parts: { + message: { + prefix: "", + suffix: "" + }, + separator: "\n", + translation: { + prefix: "", + separator: "\n", + suffix: "" + }, + translation_first: false, + }, + received_message_format_parts: { + message: { + prefix: "", + suffix: "" + }, + separator: "\n", + translation: { + prefix: "", + separator: "\n", + suffix: "" + }, + translation_first: false, + }, + selectable_ui_languages: [ {id: "en", label: "English"}, {id: "ja", label: "日本語"},