Merge branch 'develop'

This commit is contained in:
misyaguziya
2025-04-22 17:35:46 +09:00
42 changed files with 1369 additions and 815 deletions

View File

@@ -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 reached or temporarily restricted."
deepl_auth_key_invalid_length: "DeepL auth key length is not correct."
deepl_auth_key_failed_authentication: "Auth key is incorrect or API 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"
foreground: "Set To Stay On Top"
language_settings: "Language Settings"
your_language: "Your Language"
translate_each_other_label: "Translate Each Other"
swap_button_label: "Swap Languages"
translate_each_other_label: "Translate Both Languages"
swap_button_label: "Switch 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: "You are selecting the same language for '{{your_language}}' and '{{target_language}}' so only '{{ctranslate2}}' is available."
message_log:
all: "All"
@@ -29,7 +48,7 @@ main_page:
system: "System"
show_resend_button: "Show Resend Button"
resend_button_on_hover_desc: "Press and hold to send"
resend_button_on_hover_desc: "Press And Hold To Send"
state_text_enabled: "Enabled"
state_text_disabled: "Disabled"
@@ -38,58 +57,50 @@ main_page:
title_your_language: "Select Your Language"
title_target_language: "Select Target Language"
update_available: "New version is here!"
update_available: "New version is ready!"
updating: "Now updating..."
update_modal:
cpu_desc: "Use CPU only as the compute device."
cuda_desc: "Selectable between CPU and NVIDIA GPUs as compute devices."
cuda_compare_cpu_desc: "With GPU selection, processing is faster compared to a CPU."
cuda_compare_cpu_desc: "GPUs offer faster processing than CPUs."
cuda_disk_space_desc: "Requires approximately {{size}} of disk space."
close_modal: "Close"
download_latest_and_restart: "The latest version will be downloaded,\nand the app will automatically restart."
is_latest_version_already: "Already using the latest version"
is_current_compute_device: "Currently using this version"
is_current_compute_device: "The version currently in use"
config_page:
version: "version {{version}}"
version: "Version {{version}}"
model_download_button_label: "Download"
side_menu_labels:
device: "Device"
appearance: "Appearance"
translation: "Translation"
transcription: "Transcription"
vr: "VR"
others: "Others"
others: "Other"
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}}."
label_for_automatic: "Mic Sensitivity Settings (Current Setting: Automatic)"
desc_for_automatic: "Automatically control mic input sensitivity."
label_for_manual: "Mic Sensitivity Settings (Current Setting: Manual)"
desc_for_manual: "Input sensitivity can be manually adjusted using the slider. Click the mic icon to test your voice input and adjust the level while monitoring the volume."
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."
label_for_automatic: "Speaker Input Sensitivity Adjustment (Current Setting: Automatic)"
desc_for_automatic: "Automatically control speaker input sensitivity."
label_for_manual: "Speaker Input Sensitivity Adjustment (Current Setting: Manual)"
desc_for_manual: "Input sensitivity can be manually adjusted using the slider. Click the headphone icon to listen to the audio and adjust the level while checking the volume."
appearance:
transparency:
@@ -99,12 +110,12 @@ config_page:
label: "UI Size"
textbox_ui_size:
label: "Message Logs Font Size"
desc: "You can adjust the font size used in the logs relative to the UI size."
desc: "You can adjust the log font size by changing the scaling factor relative to the UI size."
send_message_button_type:
label: "Send Message Button"
hide: "Hide (Use enter key to send)"
hide: "Hide (Use Enter key to send)"
show: "Show"
show_and_disable_enter_key: "Show and disable to send when pressed enter key"
show_and_disable_enter_key: "Show and disable sending using the Enter key."
font_family:
label: "Font Family"
ui_language:
@@ -112,20 +123,19 @@ config_page:
translation:
ctranslate2_weight_type:
label: "Internal Translation Model"
desc: "You can choose the translation model to use for the internal translation engine."
small: "Basic model ({{capacity}})"
large: "High accuracy model ({{capacity}})"
label: "{{ctranslate2}} Model"
desc: "You can choose the translation model when using the {{ctranslate2}} translation engine."
small: "Basic Model ({{capacity}})"
large: "High Accuracy Model ({{capacity}})"
ctranslate2_compute_device:
label: "Internal Translation Compute Device"
label: "Processing device for AI translation {{ctranslate2}}"
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."
desc: "When using it, please change {{translator}} on the main screen to DeepL_API. ※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"
@@ -133,48 +143,42 @@ config_page:
section_label_transcription_engines: "Transcription Engines"
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."
desc: "Detects silence and, when the specified number of seconds passes, the system considers the voice input to have ended. (Second(s))"
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."
desc: "If a registered word is detected, the message will not be sent. To add multiple words at once, separate them with ',' (comma).\n*Duplicate words will not be registered."
add_button_label: "Add"
count_desc: "Current registered word count: {{count}}"
count_desc: "Words Currently Registered: {{count}}"
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"
label: "Transcription Engine Used For Speech Recognition"
whisper_weight_type:
label: "Whisper Model"
desc: "Larger models tend to have higher accuracy, but they also consume more CPU or GPU resources.\nEspecially for models larger than medium, it may be difficult or even impossible to use them depending on the performance of your CPU/GPU."
desc: "Larger models have higher accuracy, but they also consume more CPU or GPU resources.\nEspecially for models larger than medium, it may be difficult or even impossible to use them depending on the performance of your CPU/GPU."
model_template: "{{model_name}} model ({{capacity}})"
recommended_model_template: "{{model_name}} model ({{capacity}}) (Recommended)"
whisper_compute_device:
label: "Whisper Compute Device"
label: "Processing Device Used For Whisper"
vr:
single_line: "Single line"
multi_lines: "Multi lines"
multi_lines: "Multiple Lines"
overlay_enable: "Enable"
restore_default_settings: "Restore Default Settings"
restore_default_settings: "Reset to Default Settings"
position: "Position"
rotation: "Rotation"
x_position: "X-axis (left-right)"
@@ -184,46 +188,46 @@ config_page:
y_rotation: "Y-axis rotation"
z_rotation: "Z-axis rotation"
sample_text_button:
start: "Send sample texts\nto Overlay"
start: "Send Sample Texts\nTo Overlay"
stop: "Stop Sending"
sample_text: "Sample text."
sample_text: "Sample Text."
opacity: "Opacity"
ui_scaling: "UI Scaling"
display_duration: "Display duration"
fadeout_duration: "Fadeout duration"
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"
left_hand: "Left Hand"
right_hand: "Right Hand"
overlay_show_only_translated_messages:
label: "Show Only Translated Messages"
others:
section_label_sounds: "Sounds"
auto_clear_the_message_box:
label: "Auto Clear The Message Box"
label: "Auto Clear Message box"
send_only_translated_messages:
label: "Send Only Translated Messages"
auto_export_message_logs:
label: "Auto Export Message Logs"
desc: "Automatically export the conversation messages as a text file."
label: "Auto Save Message Logs"
desc: "Automatically saves the conversation messages as text files."
vrc_mic_mute_sync:
label: "VRC Mic Mute Sync"
desc: "VRCT will not send the message to VRChat while VRChat's mic is muted.\n*There is a bit latency and Push-To-Talk is not supported."
desc: "Messages will not be sent to VRCT while VRChat's mic is muted.\n*There may be a slight delay. Push-To-Talk is not supported."
send_message_to_vrc:
label: "Send Message To VRChat"
desc: "There is a way to use it without sending messages to VRChat, but it is not supported. Enable this feature when you intend to send a message to VRChat."
desc: "This feature is not supported, but there is a way to use it without sending messages to VRChat. Make sure to enable this feature when you wish to send messages to VRChat."
notification_vrc_sfx:
label: "Enable Notification Sound When Sending Chat"
desc: "Disabling this feature will send chats quietly without playing a notification sound that others can hear."
desc: "When this feature is disabled, messages will be sent silently without playing the chatbox notification sound that others can hear."
send_received_message_to_vrc:
label: "Send Received Message To VRChat"
desc: "Send the message you received from the speaker's sound to VRChat's chatbox."
desc: "Send the message you received from the speaker's voice to VRChat's chatbox."
hotkeys:
toggle_vrct_visibility:
label: "Toggle VRCT Visibility"
label: "Toggle VRCT visibility"
toggle_translation:
label: "Toggle {{translation}}"
toggle_transcription_send:
@@ -239,4 +243,4 @@ config_page:
open_config_filepath:
label: "Open Config File"
switch_compute_device:
label: "Switch VRCT to CPU/GPU Version"
label: "Switch VRCT To CPU/GPU Version"

View File

@@ -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:

View File

@@ -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:
@@ -166,3 +242,5 @@ config_page:
label: "OSC 포트"
open_config_filepath:
label: "설정 파일 열기"
switch_compute_device:
label:

View File

@@ -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:
@@ -181,3 +242,5 @@ config_page:
label: "OSC 端口"
open_config_filepath:
label: "打开设置文件"
switch_compute_device:
label:

View File

@@ -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:
@@ -185,3 +242,5 @@ config_page:
label: "OSC 端口"
open_config_filepath:
label: "打開設定文件"
switch_compute_device:
label:

View File

@@ -944,7 +944,7 @@ class Config:
def init_config(self):
# Read Only
self._VERSION = "3.0.4"
self._VERSION = "3.0.5"
if getattr(sys, 'frozen', False):
self._PATH_LOCAL = os_path.dirname(sys.executable)
else:

View File

@@ -6,7 +6,7 @@ import re
from device_manager import device_manager
from config import config
from model import model
from utils import removeLog, printLog, errorLogging, isConnectedNetwork
from utils import removeLog, printLog, errorLogging, isConnectedNetwork, isValidIpAddress
class Controller:
def __init__(self) -> None:
@@ -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
}
}
@@ -1085,9 +1085,29 @@ class Controller:
@staticmethod
def setOscIpAddress(data, *args, **kwargs) -> dict:
config.OSC_IP_ADDRESS = data
model.setOscIpAddress(config.OSC_IP_ADDRESS)
return {"status":200, "result":config.OSC_IP_ADDRESS}
if isValidIpAddress(data) is False:
response = {
"status":400,
"result":{
"message":"Invalid IP address",
"data": config.OSC_IP_ADDRESS
}
}
else:
try:
model.setOscIpAddress(data)
config.OSC_IP_ADDRESS = data
response = {"status":200, "result":config.OSC_IP_ADDRESS}
except Exception:
model.setOscIpAddress(config.OSC_IP_ADDRESS)
response = {
"status":400,
"result":{
"message":"Cannot set IP address",
"data": config.OSC_IP_ADDRESS
}
}
return response
@staticmethod
def getOscPort(*args, **kwargs) -> dict:

View File

@@ -396,83 +396,83 @@ class Model:
mic_device_list = device_manager.getMicDevices().get(mic_host_name, [{"name": "NoDevice"}])
selected_mic_device = [device for device in mic_device_list if device["name"] == mic_device_name]
if len(selected_mic_device) == 0:
return False
if len(selected_mic_device) == 0 or mic_device_name == "NoDevice":
fnc({"text": False, "language": None})
else:
self.mic_audio_queue = Queue()
# self.mic_energy_queue = Queue()
self.mic_audio_queue = Queue()
# self.mic_energy_queue = Queue()
mic_device = selected_mic_device[0]
record_timeout = config.MIC_RECORD_TIMEOUT
phrase_timeout = config.MIC_PHRASE_TIMEOUT
if record_timeout > phrase_timeout:
record_timeout = phrase_timeout
mic_device = selected_mic_device[0]
record_timeout = config.MIC_RECORD_TIMEOUT
phrase_timeout = config.MIC_PHRASE_TIMEOUT
if record_timeout > phrase_timeout:
record_timeout = phrase_timeout
self.mic_audio_recorder = SelectedMicEnergyAndAudioRecorder(
device=mic_device,
energy_threshold=config.MIC_THRESHOLD,
dynamic_energy_threshold=config.MIC_AUTOMATIC_THRESHOLD,
phrase_time_limit=record_timeout,
)
# self.mic_audio_recorder.recordIntoQueue(self.mic_audio_queue, mic_energy_queue)
self.mic_audio_recorder.recordIntoQueue(self.mic_audio_queue, None)
self.mic_transcriber = AudioTranscriber(
speaker=False,
source=self.mic_audio_recorder.source,
phrase_timeout=phrase_timeout,
max_phrases=config.MIC_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
device=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device"],
device_index=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device_index"],
)
def sendMicTranscript():
try:
selected_your_languages = config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO]
languages = [data["language"] for data in selected_your_languages.values() if data["enable"] is True]
countries = [data["country"] for data in selected_your_languages.values() if data["enable"] is True]
if isinstance(self.mic_transcriber, AudioTranscriber) is True:
res = self.mic_transcriber.transcribeAudioQueue(
self.mic_audio_queue,
languages,
countries,
config.MIC_AVG_LOGPROB,
config.MIC_NO_SPEECH_PROB
)
if res:
result = self.mic_transcriber.getTranscript()
fnc(result)
except Exception:
errorLogging()
self.mic_audio_recorder = SelectedMicEnergyAndAudioRecorder(
device=mic_device,
energy_threshold=config.MIC_THRESHOLD,
dynamic_energy_threshold=config.MIC_AUTOMATIC_THRESHOLD,
phrase_time_limit=record_timeout,
)
# self.mic_audio_recorder.recordIntoQueue(self.mic_audio_queue, mic_energy_queue)
self.mic_audio_recorder.recordIntoQueue(self.mic_audio_queue, None)
self.mic_transcriber = AudioTranscriber(
speaker=False,
source=self.mic_audio_recorder.source,
phrase_timeout=phrase_timeout,
max_phrases=config.MIC_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
device=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device"],
device_index=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device_index"],
)
def sendMicTranscript():
try:
selected_your_languages = config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO]
languages = [data["language"] for data in selected_your_languages.values() if data["enable"] is True]
countries = [data["country"] for data in selected_your_languages.values() if data["enable"] is True]
if isinstance(self.mic_transcriber, AudioTranscriber) is True:
res = self.mic_transcriber.transcribeAudioQueue(
self.mic_audio_queue,
languages,
countries,
config.MIC_AVG_LOGPROB,
config.MIC_NO_SPEECH_PROB
)
if res:
result = self.mic_transcriber.getTranscript()
fnc(result)
except Exception:
errorLogging()
def endMicTranscript():
while not self.mic_audio_queue.empty():
self.mic_audio_queue.get()
# while not self.mic_energy_queue.empty():
# self.mic_energy_queue.get()
self.mic_transcriber = None
gc.collect()
def endMicTranscript():
while not self.mic_audio_queue.empty():
self.mic_audio_queue.get()
# while not self.mic_energy_queue.empty():
# self.mic_energy_queue.get()
self.mic_transcriber = None
gc.collect()
# def sendMicEnergy():
# if mic_energy_queue.empty() is False:
# energy = mic_energy_queue.get()
# # print("mic energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
# def sendMicEnergy():
# if mic_energy_queue.empty() is False:
# energy = mic_energy_queue.get()
# # print("mic energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
self.mic_print_transcript = threadFnc(sendMicTranscript, end_fnc=endMicTranscript)
self.mic_print_transcript.daemon = True
self.mic_print_transcript.start()
self.mic_print_transcript = threadFnc(sendMicTranscript, end_fnc=endMicTranscript)
self.mic_print_transcript.daemon = True
self.mic_print_transcript.start()
# self.mic_get_energy = threadFnc(sendMicEnergy)
# self.mic_get_energy.daemon = True
# self.mic_get_energy.start()
# self.mic_get_energy = threadFnc(sendMicEnergy)
# self.mic_get_energy.daemon = True
# self.mic_get_energy.start()
self.changeMicTranscriptStatus()
self.changeMicTranscriptStatus()
def resumeMicTranscript(self):
# キューをクリア
@@ -531,25 +531,25 @@ class Model:
mic_device_list = device_manager.getMicDevices().get(mic_host_name, [{"name": "NoDevice"}])
selected_mic_device = [device for device in mic_device_list if device["name"] == mic_device_name]
if len(selected_mic_device) == 0:
return False
if len(selected_mic_device) == 0 or mic_device_name == "NoDevice":
self.check_mic_energy_fnc(False)
else:
def sendMicEnergy():
if mic_energy_queue.empty() is False:
energy = mic_energy_queue.get()
try:
self.check_mic_energy_fnc(energy)
except Exception:
errorLogging()
sleep(0.01)
def sendMicEnergy():
if mic_energy_queue.empty() is False:
energy = mic_energy_queue.get()
try:
self.check_mic_energy_fnc(energy)
except Exception:
errorLogging()
sleep(0.01)
mic_energy_queue = Queue()
mic_device = selected_mic_device[0]
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
self.mic_energy_recorder.recordIntoQueue(mic_energy_queue)
self.mic_energy_plot_progressbar = threadFnc(sendMicEnergy)
self.mic_energy_plot_progressbar.daemon = True
self.mic_energy_plot_progressbar.start()
mic_energy_queue = Queue()
mic_device = selected_mic_device[0]
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
self.mic_energy_recorder.recordIntoQueue(mic_energy_queue)
self.mic_energy_plot_progressbar = threadFnc(sendMicEnergy)
self.mic_energy_plot_progressbar.daemon = True
self.mic_energy_plot_progressbar.start()
def stopCheckMicEnergy(self):
if isinstance(self.mic_energy_plot_progressbar, threadFnc):
@@ -562,83 +562,85 @@ class Model:
self.mic_energy_recorder = None
def startSpeakerTranscript(self, fnc):
speaker_device_name = config.SELECTED_SPEAKER_DEVICE
speaker_device_list = device_manager.getSpeakerDevices()
selected_speaker_device = [device for device in speaker_device_list if device["name"] == config.SELECTED_SPEAKER_DEVICE]
selected_speaker_device = [device for device in speaker_device_list if device["name"] == speaker_device_name]
if len(selected_speaker_device) == 0:
return False
if len(selected_speaker_device) == 0 or speaker_device_name == "NoDevice":
fnc({"text": False, "language": None})
else:
speaker_audio_queue = Queue()
# speaker_energy_queue = Queue()
speaker_device = selected_speaker_device[0]
record_timeout = config.SPEAKER_RECORD_TIMEOUT
phrase_timeout = config.SPEAKER_PHRASE_TIMEOUT
if record_timeout > phrase_timeout:
record_timeout = phrase_timeout
speaker_audio_queue = Queue()
# speaker_energy_queue = Queue()
speaker_device = selected_speaker_device[0]
record_timeout = config.SPEAKER_RECORD_TIMEOUT
phrase_timeout = config.SPEAKER_PHRASE_TIMEOUT
if record_timeout > phrase_timeout:
record_timeout = phrase_timeout
self.speaker_audio_recorder = SelectedSpeakerEnergyAndAudioRecorder(
device=speaker_device,
energy_threshold=config.SPEAKER_THRESHOLD,
dynamic_energy_threshold=config.SPEAKER_AUTOMATIC_THRESHOLD,
phrase_time_limit=record_timeout,
)
# self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue, speaker_energy_queue)
self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue, None)
self.speaker_transcriber = AudioTranscriber(
speaker=True,
source=self.speaker_audio_recorder.source,
phrase_timeout=phrase_timeout,
max_phrases=config.SPEAKER_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
device=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device"],
device_index=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device_index"],
)
def sendSpeakerTranscript():
try:
selected_target_languages = config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO]
languages = [data["language"] for data in selected_target_languages.values() if data["enable"] is True]
countries = [data["country"] for data in selected_target_languages.values() if data["enable"] is True]
if isinstance(self.speaker_transcriber, AudioTranscriber) is True:
res = self.speaker_transcriber.transcribeAudioQueue(
speaker_audio_queue,
languages,
countries,
config.SPEAKER_AVG_LOGPROB,
config.SPEAKER_NO_SPEECH_PROB
)
if res:
result = self.speaker_transcriber.getTranscript()
fnc(result)
except Exception:
errorLogging()
self.speaker_audio_recorder = SelectedSpeakerEnergyAndAudioRecorder(
device=speaker_device,
energy_threshold=config.SPEAKER_THRESHOLD,
dynamic_energy_threshold=config.SPEAKER_AUTOMATIC_THRESHOLD,
phrase_time_limit=record_timeout,
)
# self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue, speaker_energy_queue)
self.speaker_audio_recorder.recordIntoQueue(speaker_audio_queue, None)
self.speaker_transcriber = AudioTranscriber(
speaker=True,
source=self.speaker_audio_recorder.source,
phrase_timeout=phrase_timeout,
max_phrases=config.SPEAKER_MAX_PHRASES,
transcription_engine=config.SELECTED_TRANSCRIPTION_ENGINE,
root=config.PATH_LOCAL,
whisper_weight_type=config.WHISPER_WEIGHT_TYPE,
device=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device"],
device_index=config.SELECTED_TRANSCRIPTION_COMPUTE_DEVICE["device_index"],
)
def sendSpeakerTranscript():
try:
selected_target_languages = config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO]
languages = [data["language"] for data in selected_target_languages.values() if data["enable"] is True]
countries = [data["country"] for data in selected_target_languages.values() if data["enable"] is True]
if isinstance(self.speaker_transcriber, AudioTranscriber) is True:
res = self.speaker_transcriber.transcribeAudioQueue(
speaker_audio_queue,
languages,
countries,
config.SPEAKER_AVG_LOGPROB,
config.SPEAKER_NO_SPEECH_PROB
)
if res:
result = self.speaker_transcriber.getTranscript()
fnc(result)
except Exception:
errorLogging()
def endSpeakerTranscript():
while not speaker_audio_queue.empty():
speaker_audio_queue.get()
# while not speaker_energy_queue.empty():
# speaker_energy_queue.get()
self.speaker_transcriber = None
gc.collect()
def endSpeakerTranscript():
while not speaker_audio_queue.empty():
speaker_audio_queue.get()
# while not speaker_energy_queue.empty():
# speaker_energy_queue.get()
self.speaker_transcriber = None
gc.collect()
# def sendSpeakerEnergy():
# if speaker_energy_queue.empty() is False:
# energy = speaker_energy_queue.get()
# # print("speaker energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
# def sendSpeakerEnergy():
# if speaker_energy_queue.empty() is False:
# energy = speaker_energy_queue.get()
# # print("speaker energy:", energy)
# try:
# fnc(energy)
# except Exception:
# pass
# sleep(0.01)
self.speaker_print_transcript = threadFnc(sendSpeakerTranscript, end_fnc=endSpeakerTranscript)
self.speaker_print_transcript.daemon = True
self.speaker_print_transcript.start()
self.speaker_print_transcript = threadFnc(sendSpeakerTranscript, end_fnc=endSpeakerTranscript)
self.speaker_print_transcript.daemon = True
self.speaker_print_transcript.start()
# self.speaker_get_energy = threadFnc(sendSpeakerEnergy)
# self.speaker_get_energy.daemon = True
# self.speaker_get_energy.start()
# self.speaker_get_energy = threadFnc(sendSpeakerEnergy)
# self.speaker_get_energy.daemon = True
# self.speaker_get_energy.start()
def stopSpeakerTranscript(self):
if isinstance(self.speaker_print_transcript, threadFnc):
@@ -656,28 +658,29 @@ class Model:
if isinstance(fnc, Callable):
self.check_speaker_energy_fnc = fnc
speaker_device_name = config.SELECTED_SPEAKER_DEVICE
speaker_device_list = device_manager.getSpeakerDevices()
selected_speaker_device = [device for device in speaker_device_list if device["name"] == config.SELECTED_SPEAKER_DEVICE]
selected_speaker_device = [device for device in speaker_device_list if device["name"] == speaker_device_name]
if len(selected_speaker_device) == 0:
return False
if len(selected_speaker_device) == 0 or speaker_device_name == "NoDevice":
self.check_speaker_energy_fnc(False)
else:
def sendSpeakerEnergy():
if speaker_energy_queue.empty() is False:
energy = speaker_energy_queue.get()
try:
self.check_speaker_energy_fnc(energy)
except Exception:
errorLogging()
sleep(0.01)
def sendSpeakerEnergy():
if speaker_energy_queue.empty() is False:
energy = speaker_energy_queue.get()
try:
self.check_speaker_energy_fnc(energy)
except Exception:
errorLogging()
sleep(0.01)
speaker_energy_queue = Queue()
speaker_device = selected_speaker_device[0]
self.speaker_energy_recorder = SelectedSpeakerEnergyRecorder(speaker_device)
self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue)
self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy)
self.speaker_energy_plot_progressbar.daemon = True
self.speaker_energy_plot_progressbar.start()
speaker_energy_queue = Queue()
speaker_device = selected_speaker_device[0]
self.speaker_energy_recorder = SelectedSpeakerEnergyRecorder(speaker_device)
self.speaker_energy_recorder.recordIntoQueue(speaker_energy_queue)
self.speaker_energy_plot_progressbar = threadFnc(sendSpeakerEnergy)
self.speaker_energy_plot_progressbar.daemon = True
self.speaker_energy_plot_progressbar.start()
def stopCheckSpeakerEnergy(self):
if isinstance(self.speaker_energy_plot_progressbar, threadFnc):

View File

@@ -17,6 +17,8 @@ _MODELS = {
"large-v1": "Systran/faster-whisper-large-v1",
"large-v2": "Systran/faster-whisper-large-v2",
"large-v3": "Systran/faster-whisper-large-v3",
"large-v3-turbo-int8": "Zoont/faster-whisper-large-v3-turbo-int8-ct2", #794MB
"large-v3-turbo": "deepdml/faster-whisper-large-v3-turbo-ct2", #1.58GB
}
_FILENAMES = [

View File

@@ -7,14 +7,22 @@ from logging.handlers import RotatingFileHandler
from ctranslate2 import get_supported_compute_types
import requests
import ipaddress
def isConnectedNetwork(url="http://www.google.com", timeout=3):
def isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool:
try:
response = requests.get(url, timeout=timeout)
return response.status_code == 200
except requests.RequestException:
return False
def isValidIpAddress(ip_address: str) -> bool:
try:
ipaddress.ip_address(ip_address)
return True
except ValueError:
return False
def getBestComputeType(device, device_index) -> str:
compute_types = get_supported_compute_types(device, device_index)
compute_types = set(compute_types)

View File

@@ -1,89 +1,83 @@
{
"build": {
"beforeDevCommand": "",
"beforeBuildCommand": "",
"devPath": "http://localhost:1420",
"distDir": "../dist"
},
"package": {
"productName": "VRCT",
"version": "3.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"window": {
"all": false,
"setAlwaysOnTop": true,
"setFocus": true,
"setDecorations": true,
"close": true,
"hide": true,
"setPosition": true,
"setSize": true,
"maximize": true,
"minimize": true,
"unmaximize": true,
"unminimize": true,
"startDragging": true
},
"globalShortcut": {
"all": true
},
"shell": {
"all": false,
"open": true,
"sidecar": true,
"scope": [
{
"name": "bin/VRCT-sidecar", "sidecar": true,"args": true
}
]
}
},
"windows": [
{
"title": "VRCT",
"center": true,
"width": 450,
"height": 220,
"minWidth": 400,
"minHeight": 200,
"transparent": true,
"decorations": false
}
],
"security": {
"csp": null
"build": {
"beforeDevCommand": "",
"beforeBuildCommand": "",
"devPath": "http://localhost:1420",
"distDir": "../dist"
},
"bundle": {
"active": true,
"targets": "nsis",
"identifier": "com.vrct.dev",
"publisher": "m's software",
"copyright": "Copyright m's software",
"shortDescription": "VRCT",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"externalBin": [
"bin/VRCT-sidecar"
],
"resources":{
"bin/_internal": "_internal"
},
"windows": {
"nsis": {
"template": "nsis/template.nsi",
"license": "../LICENSE",
"installMode": "currentUser",
"displayLanguageSelector": true
"package": {
"productName": "VRCT",
"version": "3.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"window": {
"all": false,
"setAlwaysOnTop": true,
"setFocus": true,
"setDecorations": true,
"close": true,
"hide": true,
"setPosition": true,
"setSize": true,
"maximize": true,
"minimize": true,
"unmaximize": true,
"unminimize": true,
"startDragging": true
},
"globalShortcut": {
"all": true
},
"shell": {
"all": false,
"open": true,
"sidecar": true,
"scope": [
{ "name": "bin/VRCT-sidecar", "sidecar": true, "args": true }
]
}
},
"windows": [{
"title": "VRCT",
"center": true,
"width": 450,
"height": 220,
"minWidth": 400,
"minHeight": 200,
"transparent": true,
"decorations": false
}],
"security": { "csp": null },
"bundle": {
"active": true,
"targets": "nsis",
"identifier": "com.vrct.dev",
"publisher": "m's software",
"copyright": "Copyright m's software",
"shortDescription": "VRCT",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"externalBin": [
"bin/VRCT-sidecar"
],
"resources": {
"bin/_internal": "_internal"
},
"windows": {
"nsis": {
"template": "nsis/template.nsi",
"license": "../LICENSE",
"installMode": "currentUser",
"displayLanguageSelector": true
}
}
}
}
}
}
}

View File

@@ -24,7 +24,7 @@ const _Entry = forwardRef((props, ref) => {
<div className={styles.entry_container}>
<div
className={input_wrapper_class_names}
style={{width: props.width }}
style={{width: props.width || "20rem" }}
>
<input
ref={inputRef}

View File

@@ -0,0 +1,32 @@
import styles from "./EntryWithSaveButton.module.scss";
import { _Entry } from "../_atoms/_entry/_Entry";
import CircularProgress from "@mui/material/CircularProgress";
import { useTranslation } from "react-i18next";
import { clsx } from "clsx";
export const EntryWithSaveButton = (props) => {
const { t } = useTranslation();
const onChangeFunction = (e) => {
props.onChangeFunction?.(e.target.value);
};
const saveFunction = () => {
props.saveFunction();
};
const is_disabled = props.state === "pending";
const save_button_class_names = clsx(styles.save_button, {
[styles.is_disabled]: is_disabled
});
return (
<div className={styles.container}>
<_Entry width={props.width} onChange={onChangeFunction} ui_variable={props.variable} is_disabled={is_disabled}/>
<button className={save_button_class_names} onClick={saveFunction}>
{is_disabled
? <CircularProgress size="1.4rem" sx={{ color: "var(--dark_basic_text_color)" }}/>
: <p className={styles.save_button_label}>{t("config_page.translation.deepl_auth_key.save")}</p>
}
</button>
</div>
);
};

View File

@@ -0,0 +1,30 @@
.container {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
flex-shrink: 0;
}
.save_button {
padding: 0.8rem 1.2rem;
background-color: var(--primary_600_color);
border-radius: 0.4rem;
text-align: center;
flex-shrink: 0;
min-width: 5.4rem;
&:hover {
background-color: var(--primary_500_color);
}
&:active {
background-color: var(--primary_700_color);
}
&.is_disabled {
pointer-events: none;
background-color: var(--primary_800_color);
}
}
.save_button_label {
font-size: 1.4rem;
}

View File

@@ -3,6 +3,7 @@ export { ComputeDevice } from "./compute_device/ComputeDevice";
export { DeeplAuthKey, OpenWebpage_DeeplAuthKey } from "./deepl_auth_key/DeeplAuthKey";
export { DropdownMenu } from "./dropdown_menu/DropdownMenu";
export { Entry } from "./entry/Entry";
export { EntryWithSaveButton } from "./entry_with_save_button/EntryWithSaveButton";
export { HotkeysEntry } from "./hotkeys_entry/HotkeysEntry";
export { LabelComponent } from "./label_component/LabelComponent";
export { RadioButton } from "./radio_button/RadioButton";

View File

@@ -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 = () => {
}
<div className={styles.entry_section_wrapper}>
<_Entry width="30rem" onChange={onChangeEntry} ui_variable={input_value}/>
<button className={styles.add_button} onClick={addWords}>Add</button>
<button className={styles.add_button} onClick={addWords}>{t("config_page.transcription.mic_word_filter.add_button_label")}</button>
</div>
</div>
);
@@ -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();

View File

@@ -8,6 +8,7 @@ import {
Slider,
SwitchBox,
Entry,
EntryWithSaveButton,
HotkeysEntry,
RadioButton,
OpenWebpage_DeeplAuthKey,
@@ -75,6 +76,9 @@ export const SwitchBoxContainer = (props) => (
export const EntryContainer = (props) => (
<CommonContainer Component={Entry} {...props} add_break_point={false} />
);
export const EntryWithSaveButtonContainer = (props) => (
<CommonContainer Component={EntryWithSaveButton} {...props} add_break_point={false} />
);
export const HotkeysEntryContainer = (props) => (
<CommonContainer Component={HotkeysEntry} {...props} />

View File

@@ -10,6 +10,7 @@ import contributor_iya from "@images/about_vrct/contributor_iya.png";
import contributor_rera from "@images/about_vrct/contributor_rera.png";
import contributor_poposuke from "@images/about_vrct/contributor_poposuke.png";
import contributor_kumaguma from "@images/about_vrct/contributor_kumaguma.png";
import contributor_riku from "@images/about_vrct/contributor_riku.png";
import localization_section_title from "@images/about_vrct/localization_section_title.png";
import localization_1 from "@images/about_vrct/localization_1.png";
@@ -86,6 +87,10 @@ export const AboutVrct = () => {
<img src={contributor_kumaguma} className={clsx(styles.contributors_img, styles.contributors)} />
<OpenLinkContainer className={styles.contributors_kumaguma_x} href_id="contributors_kumaguma_x" />
</div>
<div className={styles.contributor_card_wrapper}>
<img src={contributor_riku} className={clsx(styles.contributors_img, styles.contributors)} />
<OpenLinkContainer className={styles.contributors_riku_x} href_id="contributors_riku_x" />
</div>
</div>
</div>
@@ -158,6 +163,7 @@ const about_vrct_links = {
contributors_rera_github: { img: contributors_github_icon, href: "https://github.com/soumt-r" },
contributors_poposuke_x: { img: contributors_x_icon, href: "https://twitter.com/sig_popo" },
contributors_kumaguma_x: { img: contributors_x_icon, href: "https://twitter.com/K_kumaguma_A" },
contributors_riku_x: { img: contributors_x_icon, href: "https://twitter.com/Riku7302" },
};
const OpenLinkContainer = ({className, href_id}) => {

View File

@@ -160,6 +160,9 @@ $sns_left_pos: 0.8rem;
.contributors_kumaguma_x {
@include contributors_sns_styles($bottom_pos, $sns_left_pos);
}
.contributors_riku_x {
@include contributors_sns_styles($bottom_pos, $sns_left_pos);
}
.localization_section {
display: flex;

View File

@@ -11,6 +11,7 @@ import {
import {
ActionButtonContainer,
EntryContainer,
EntryWithSaveButtonContainer,
} from "../_templates/Templates";
@@ -30,54 +31,59 @@ export const AdvancedSettings = () => {
const OscIpAddressContainer = () => {
const { t } = useTranslation();
const [ui_variable, setUiVariable] = useState("");
const { currentOscIpAddress, setOscIpAddress } = useOscIpAddress();
const onChangeFunction = (e) => {
const value = e.currentTarget.value;
if (value === "") {
setUiVariable("");
} else {
setUiVariable(value);
setOscIpAddress(value);
}
const [input_value, setInputValue] = useState(currentOscIpAddress.data);
const onChangeFunction = (value) => {
setInputValue(value);
};
const saveFunction = () => {
setOscIpAddress(input_value);
};
useEffect(()=> {
setUiVariable(currentOscIpAddress.data);
setInputValue(currentOscIpAddress.data);
}, [currentOscIpAddress]);
return (
<EntryContainer
<EntryWithSaveButtonContainer
label={t("config_page.advanced_settings.osc_ip_address.label")}
ui_variable={ui_variable}
onChange={onChangeFunction}
variable={input_value}
saveFunction={saveFunction}
onChangeFunction={onChangeFunction}
state={currentOscIpAddress.state}
width="14rem"
/>
);
};
const OscPortContainer = () => {
const { t } = useTranslation();
const [ui_variable, setUiVariable] = useState("");
const { currentOscPort, setOscPort } = useOscPort();
const onChangeFunction = (e) => {
const value = e.currentTarget.value;
if (value === "") {
setUiVariable("");
} else {
setUiVariable(value);
setOscPort(value);
}
const [input_value, setInputValue] = useState(currentOscPort.data);
const onChangeFunction = (value) => {
value = value.replace(/[^0-9]/g, "");
setInputValue(value);
};
const saveFunction = () => {
setOscPort(input_value);
};
useEffect(()=> {
setUiVariable(currentOscPort.data);
setInputValue(currentOscPort.data);
}, [currentOscPort]);
return (
<EntryContainer
<EntryWithSaveButtonContainer
label={t("config_page.advanced_settings.osc_port.label")}
ui_variable={ui_variable}
onChange={onChangeFunction}
variable={input_value}
saveFunction={saveFunction}
onChangeFunction={onChangeFunction}
state={currentOscPort.state}
width="10rem"
/>
);
};

View File

@@ -82,7 +82,7 @@ const Mic_Container = () => {
<div className={styles.device_contents}>
<div className={styles.device_auto_select_wrapper}>
<p className={styles.device_secondary_label}>{t("config_page.device.mic_host_device.label_auto_select")}</p>
<p className={styles.device_secondary_label}>{t("config_page.device.label_auto_select")}</p>
<SwitchBox
variable={currentEnableAutoMicSelect}
toggleFunction={toggleEnableAutoMicSelect}
@@ -91,7 +91,7 @@ const Mic_Container = () => {
<div className={styles.device_dropdown_wrapper}>
<div className={styles.device_dropdown}>
<p className={styles.device_secondary_label}>{t("config_page.device.mic_host_device.label_host")}</p>
<p className={styles.device_secondary_label}>{t("config_page.device.label_host")}</p>
<DropdownMenu
dropdown_id="mic_host"
selected_id={currentSelectedMicHost.data}
@@ -104,7 +104,7 @@ const Mic_Container = () => {
</div>
<div className={styles.device_dropdown}>
<p className={styles.device_secondary_label}>{t("config_page.device.mic_host_device.label_device")}</p>
<p className={styles.device_secondary_label}>{t("config_page.device.label_device")}</p>
<DropdownMenu
dropdown_id="mic_device"
selected_id={currentSelectedMicDevice.data}
@@ -178,7 +178,7 @@ const Speaker_Container = () => {
<div className={styles.device_contents}>
<div className={styles.device_auto_select_wrapper}>
<p className={styles.device_secondary_label}>{t("config_page.device.speaker_device.label_auto_select")}</p>
<p className={styles.device_secondary_label}>{t("config_page.device.label_auto_select")}</p>
<SwitchBox
variable={currentEnableAutoSpeakerSelect}
toggleFunction={toggleEnableAutoSpeakerSelect}
@@ -186,7 +186,7 @@ const Speaker_Container = () => {
</div>
<div className={styles.device_dropdown}>
<p className={styles.device_secondary_label}>{t("config_page.device.mic_host_device.label_device")}</p>
<p className={styles.device_secondary_label}>{t("config_page.device.label_device")}</p>
<DropdownMenu
dropdown_id="speaker_device"
label={t("config_page.device.speaker_device.label")}

View File

@@ -1,7 +1,6 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import styles from "./Transcription.module.scss";
import { updateLabelsById } from "@utils";
import { updateLabelsById, genNumObjArray } from "@utils";
import {
useMicRecordTimeout,
@@ -21,7 +20,6 @@ import {
} from "@logics_configs";
import {
EntryContainer,
WordFilterContainer,
DownloadModelsContainer,
RadioButtonContainer,
@@ -59,82 +57,61 @@ const Mic_Container = () => {
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 (
<EntryContainer
<DropdownMenuContainer
dropdown_id="mic_record_timeout"
label={t("config_page.transcription.mic_record_timeout.label")}
desc={t("config_page.transcription.mic_record_timeout.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
selected_id={currentMicRecordTimeout.data}
list={genNumObjArray(31)}
selectFunction={selectFunction}
state={currentMicRecordTimeout.state}
/>
);
};
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 (
<EntryContainer
<DropdownMenuContainer
dropdown_id="mic_phrase_timeout"
label={t("config_page.transcription.mic_phrase_timeout.label")}
desc={t("config_page.transcription.mic_phrase_timeout.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
selected_id={currentMicPhraseTimeout.data}
list={genNumObjArray(31)}
selectFunction={selectFunction}
state={currentMicPhraseTimeout.state}
/>
);
};
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 (
<EntryContainer
<DropdownMenuContainer
dropdown_id="mic_max_phrase"
label={t("config_page.transcription.mic_max_phrase.label")}
desc={t("config_page.transcription.mic_max_phrase.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
selected_id={currentMicMaxWords.data}
list={genNumObjArray(31)}
selectFunction={selectFunction}
state={currentMicMaxWords.state}
/>
);
};
@@ -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 (
<EntryContainer
label={t("config_page.transcription.speaker_record_timeout.label")}
<DropdownMenuContainer
dropdown_id="speaker_record_timeout"
desc={t("config_page.transcription.speaker_record_timeout.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
label={t("config_page.transcription.speaker_record_timeout.label")}
selected_id={currentSpeakerRecordTimeout.data}
list={genNumObjArray(31)}
selectFunction={selectFunction}
state={currentSpeakerRecordTimeout.state}
/>
);
};
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 (
<EntryContainer
<DropdownMenuContainer
dropdown_id="speaker_phrase_timeout"
label={t("config_page.transcription.speaker_phrase_timeout.label")}
desc={t("config_page.transcription.speaker_phrase_timeout.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
selected_id={currentSpeakerPhraseTimeout.data}
list={genNumObjArray(31)}
selectFunction={selectFunction}
state={currentSpeakerPhraseTimeout.state}
/>
);
};
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 (
<EntryContainer
<DropdownMenuContainer
dropdown_id="speaker_max_phrase"
label={t("config_page.transcription.speaker_max_phrase.label")}
desc={t("config_page.transcription.speaker_max_phrase.desc")}
ui_variable={ui_variable}
onChange={onChangeFunction}
selected_id={currentSpeakerMaxWords.data}
list={genNumObjArray(61)}
selectFunction={selectFunction}
state={currentSpeakerMaxWords.state}
/>
);
};
@@ -305,6 +260,8 @@ const WhisperWeightType_Box = () => {
{ id: "large-v1", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v1", capacity: "2.87GB"}) },
{ id: "large-v2", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v2", capacity: "2.87GB"}) },
{ id: "large-v3", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3", capacity: "2.87GB"}) },
{ id: "large-v3-turbo-int8", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3-turbo-int8", capacity: "794MB"}) },
{ id: "large-v3-turbo", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3-turbo", capacity: "1.58GB"}) },
];
const whisper_weight_types = updateLabelsById(currentWhisperWeightTypeStatus.data, new_labels);

View File

@@ -56,10 +56,13 @@ const CTranslate2WeightType_Box = () => {
return (
<>
<DownloadModelsContainer
label={t("config_page.translation.ctranslate2_weight_type.label")}
label={t(
"config_page.translation.ctranslate2_weight_type.label",
{ctranslate2: "CTranslate2"}
)}
desc={t(
"config_page.translation.ctranslate2_weight_type.desc",
{translator: t("main_page.translator")}
{ctranslate2: "CTranslate2"}
)}
name="ctransalte2_weight_type"
options={c_translate2_weight_types}
@@ -89,10 +92,13 @@ const CTranslation2ComputeDevice_Box = () => {
const { currentComputeMode } = useComputeMode();
const ctranslate2_compute_device_label = t("config_page.translation.ctranslate2_compute_device.label", {
ctranslate2: "Ctranslate2"
});
if (currentComputeMode.data === "cpu") {
return (
<ComputeDeviceContainer
label={t("config_page.translation.ctranslate2_compute_device.label")}
label={ctranslate2_compute_device_label}
selected_id={target_index}
list={list_for_ui}
selectFunction={selectFunction}
@@ -104,8 +110,7 @@ const CTranslation2ComputeDevice_Box = () => {
return (
<DropdownMenuContainer
dropdown_id="ctranslate2_compute_device"
label={t("config_page.translation.ctranslate2_compute_device.label")}
// desc={t("config_page.translation.ctranslate2_compute_device.label")}
label={ctranslate2_compute_device_label}
selected_id={target_index}
list={list_for_ui}
selectFunction={selectFunction}

View File

@@ -42,9 +42,16 @@ const Tab = (props) => {
[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 (
<div className={tab_container_class_names} onClick={onclickFunction}>
<p className={styles.tab_text}>{t(`config_page.side_menu_labels.${props.tab_id}`)}</p>
<p className={styles.tab_text}>{getLabel()}</p>
<div className={switch_indicator_class_names}></div>
</div>
);

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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 (
<div className={styles.container}>
<div className={styles.translator_selector_button} onClick={openTranslatorSelector}>
<p className={styles.label}>{t("main_page.translator")}: </p>
<p className={styles.label}>{t("main_page.translator")}:</p>
<p className={styles.label}>{selected_label}</p>
{is_selected_same_language
? <WarningSvg className={styles.warning_svg}/>

View File

@@ -15,12 +15,13 @@ export const TranslatorSelector = ({selected_id, translation_engines, is_selecte
<div className={styles.wrapper}>
{columns.map((column, column_index) => (
<div className={styles.column_wrapper} key={`column_${column_index}`}>
{column.map(({ id, label, is_available }) => (
{column.map(({ id, label, is_available, is_default }) => (
<TranslatorBox
key={id}
id={id}
label={label}
is_available={is_available}
is_default={is_default}
is_selected={(id === selected_id)}
/>
))}
@@ -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",
})}
</p>
</div>
@@ -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 (
<div className={box_class_name} onClick={selectTranslator}>
<p className={styles.translator_name}>{props.label}</p>
{props.is_default && <p className={label_default_class_name}>{t("main_page.translator_label_default")}</p>}
</div>
);
};

View File

@@ -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;

View File

@@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -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 {

View File

@@ -0,0 +1,141 @@
import { useTranslation } from "react-i18next";
import {
useNotificationStatus,
} from "@logics_common";
import {
useMicRecordTimeout,
useMicPhraseTimeout,
useMicMaxWords,
useSpeakerRecordTimeout,
useSpeakerPhraseTimeout,
useSpeakerMaxWords,
useDeepLAuthKey,
useOscIpAddress,
} 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, saveErrorDeepLAuthKey } = useDeepLAuthKey();
const { updateOscIpAddress } = useOscIpAddress();
const errorHandling_Backend = ({message, data, endpoint, _result}) => {
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;
// Advanced Settings, error messages are set by Backend (EN only)
case "Invalid IP address":
updateOscIpAddress(data);
showNotification_Error(message);
break;
case "Cannot set IP address":
updateOscIpAddress(data);
showNotification_Error(message);
break;
default:
// determine by endpoint, not message.
if (endpoint === "/set/data/deepl_auth_key") saveErrorDeepLAuthKey({message, data, endpoint, _result});
break;
}
}
return {
errorHandling_Backend,
}
};

View File

@@ -15,7 +15,7 @@ export const useNotificationStatus = () => {
});
};
const showNotification_Success = (message) => {
const showNotification_Success = (message, options = {}) => {
updateNotificationStatus({
status: "success",
is_open: true,

View File

@@ -1,7 +1,9 @@
import { useStore_OscPort } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
import { useNotificationStatus } from "@logics_common";
export const useOscPort = () => {
const { showNotification_Error } = useNotificationStatus();
const { asyncStdoutToPython } = useStdoutToPython();
const { currentOscPort, updateOscPort, pendingOscPort } = useStore_OscPort();
@@ -15,10 +17,17 @@ export const useOscPort = () => {
asyncStdoutToPython("/set/data/osc_port", osc_port);
};
const saveErrorOscPort = ({data, message, _result}) => {
updateOscPort(d => d.data);
showNotification_Error(_result);
};
return {
currentOscPort,
getOscPort,
updateOscPort,
setOscPort,
saveErrorOscPort,
};
};

View File

@@ -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();
@@ -20,11 +24,24 @@ export const useDeepLAuthKey = () => {
asyncStdoutToPython("/delete/data/deepl_auth_key");
};
const savedDeepLAuthKey = (data) => {
updateDeepLAuthKey(data);
showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success"));
};
const saveErrorDeepLAuthKey = ({data, message}) => {
updateDeepLAuthKey(data);
showNotification_Error(message);
};
return {
currentDeepLAuthKey,
getDeepLAuthKey,
updateDeepLAuthKey,
setDeepLAuthKey,
deleteDeepLAuthKey,
saveErrorDeepLAuthKey,
savedDeepLAuthKey,
};
};

View File

@@ -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, savedDeepLAuthKey } = 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": savedDeepLAuthKey,
"/delete/data/deepl_auth_key": () => updateDeepLAuthKey(""),
"/get/data/ctranslate2_weight_type": updateSelectedCTranslate2WeightType,
@@ -498,16 +504,27 @@ 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,
"/set/data/osc_ip_address": errorHandling_Backend,
};
@@ -519,22 +536,42 @@ 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,
_result: parsed_data.result,
});
} else {
handleInvalidEndpoint(parsed_data);
}
break;
case 500:
showNotification_Error(
`An error occurred. Please restart VRCT or contact the developers. ${JSON.stringify(parsed_data.result)}`, { hide_duration: null });
break;
case 348:

View File

@@ -1,8 +1,13 @@
import { Command } from "@tauri-apps/api/shell";
import { store } from "@store";
import { useReceiveRoutes } from "./useReceiveRoutes";
import {
useNotificationStatus,
} from "@logics_common";
export const useStartPython = () => {
const { receiveRoutes } = useReceiveRoutes();
const { showNotification_Success, showNotification_Error } = useNotificationStatus();
const asyncStartPython = async () => {
const command = Command.sidecar("bin/VRCT-sidecar");
@@ -16,7 +21,11 @@ export const useStartPython = () => {
console.log(error, line);
}
});
command.stderr.on("data", line => console.error("stderr:", line));
command.stderr.on("data", line => {
showNotification_Error(
`An error occurred. Please restart VRCT or contact the developers. The last line:${JSON.stringify(line)}`, { hide_duration: null });
console.error("stderr", line)
});
const backend_subprocess = await command.spawn();
store.backend_subprocess = backend_subprocess;
};

View File

@@ -126,37 +126,38 @@ export const { atomInstance: Atom_NotificationStatus, useHook: useStore_Notifica
}, "NotificationStatus");
// Main Page
// Functions
// Common
export const { atomInstance: Atom_IsMainPageCompactMode, useHook: useStore_IsMainPageCompactMode } = createAtomWithHook(false, "IsMainPageCompactMode");
// Sidebar Section
export const { atomInstance: Atom_TranslationStatus, useHook: useStore_TranslationStatus } = createAtomWithHook(false, "TranslationStatus", {is_state_ok: true});
export const { atomInstance: Atom_TranscriptionSendStatus, useHook: useStore_TranscriptionSendStatus } = createAtomWithHook(false, "TranscriptionSendStatus", {is_state_ok: true});
export const { atomInstance: Atom_TranscriptionReceiveStatus, useHook: useStore_TranscriptionReceiveStatus } = createAtomWithHook(false, "TranscriptionReceiveStatus", {is_state_ok: true});
export const { atomInstance: Atom_ForegroundStatus, useHook: useStore_ForegroundStatus } = createAtomWithHook(false, "ForegroundStatus", {is_state_ok: true});
export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook([], "MessageLogs");
// export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook(generateTestData(20), "MessageLogs"); // For testing
export const { atomInstance: Atom_MessageInputValue, useHook: useStore_MessageInputValue } = createAtomWithHook("", "MessageInputValue");
export const { atomInstance: Atom_IsVisibleResendButton, useHook: useStore_IsVisibleResendButton } = createAtomWithHook(false, "IsVisibleResendButton", {is_state_ok: true});
export const { atomInstance: Atom_IsAppliedInitMessageBoxHeight, useHook: useStore_IsAppliedInitMessageBoxHeight } = createAtomWithHook(false, "IsAppliedInitMessageBoxHeight");
export const { atomInstance: Atom_SelectableLanguageList, useHook: useStore_SelectableLanguageList } = createAtomWithHook([], "SelectableLanguageList");
export const { atomInstance: Atom_SelectedPresetTabNumber, useHook: useStore_SelectedPresetTabNumber } = createAtomWithHook("1", "SelectedPresetTabNumber");
export const { atomInstance: Atom_EnableMultiTranslation, useHook: useStore_EnableMultiTranslation } = createAtomWithHook(false, "EnableMultiTranslation");
export const { atomInstance: Atom_SelectedYourLanguages, useHook: useStore_SelectedYourLanguages } = createAtomWithHook({}, "SelectedYourLanguages");
export const { atomInstance: Atom_SelectedTargetLanguages, useHook: useStore_SelectedTargetLanguages } = createAtomWithHook({}, "SelectedTargetLanguages");
export const { atomInstance: Atom_TranslationEngines, useHook: useStore_TranslationEngines } = createAtomWithHook(translator_status, "TranslationEngines");
export const { atomInstance: Atom_SelectedTranslationEngines, useHook: useStore_SelectedTranslationEngines } = createAtomWithHook({1:"", 2:"", 3:""}, "SelectedTranslationEngines");
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
// Designs
export const { atomInstance: Atom_IsMainPageCompactMode, useHook: useStore_IsMainPageCompactMode } = createAtomWithHook(false, "IsMainPageCompactMode");
export const { atomInstance: Atom_MessageInputBoxRatio, useHook: useStore_MessageInputBoxRatio } = createAtomWithHook(20, "MessageInputBoxRatio");
// Language Selector
export const { atomInstance: Atom_IsOpenedLanguageSelector, useHook: useStore_IsOpenedLanguageSelector } = createAtomWithHook(
{ your_language: false, target_language: false, target_key: "1" },
"IsOpenedLanguageSelector"
);
export const { atomInstance: Atom_SelectableLanguageList, useHook: useStore_SelectableLanguageList } = createAtomWithHook([], "SelectableLanguageList");
// Message Container
export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook([], "MessageLogs");
// export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook(generateTestData(20), "MessageLogs"); // For testing
export const { atomInstance: Atom_MessageInputBoxRatio, useHook: useStore_MessageInputBoxRatio } = createAtomWithHook(20, "MessageInputBoxRatio");
export const { atomInstance: Atom_MessageInputValue, useHook: useStore_MessageInputValue } = createAtomWithHook("", "MessageInputValue");
export const { atomInstance: Atom_IsVisibleResendButton, useHook: useStore_IsVisibleResendButton } = createAtomWithHook(false, "IsVisibleResendButton", {is_state_ok: true});
// Config Page
@@ -164,8 +165,6 @@ export const { atomInstance: Atom_IsOpenedLanguageSelector, useHook: useStore_Is
export const { atomInstance: Atom_SoftwareVersion, useHook: useStore_SoftwareVersion } = createAtomWithHook("-", "SoftwareVersion");
export const { atomInstance: Atom_SelectedConfigTabId, useHook: useStore_SelectedConfigTabId } = createAtomWithHook("device", "SelectedConfigTabId");
export const { atomInstance: Atom_SettingBoxScrollPosition, useHook: useStore_SettingBoxScrollPosition } = createAtomWithHook(0, "SettingBoxScrollPosition");
// Designs
export const { atomInstance: Atom_IsOpenedDropdownMenu, useHook: useStore_IsOpenedDropdownMenu } = createAtomWithHook("", "IsOpenedDropdownMenu");
// Device
@@ -280,9 +279,9 @@ export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createA
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
// Supporters
export const { atomInstance: Atom_SupportersData, useHook: useStore_SupportersData } = createAtomWithHook(null, "SupportersData", {is_state_ok: true});
// About VRCT
export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex");
export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex");

View File

@@ -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 = [
@@ -74,6 +74,8 @@ export const whisper_weight_type_status = [
{ id: "large-v1", label: "large-v1", is_downloaded: false, progress: null },
{ id: "large-v2", label: "large-v2", is_downloaded: false, progress: null },
{ id: "large-v3", label: "large-v3", is_downloaded: false, progress: null },
{ id: "large-v3-turbo-int8", label: "large-v3-turbo-int8", is_downloaded: false, progress: null },
{ id: "large-v3-turbo", label: "large-v3-turbo", is_downloaded: false, progress: null },
];
export const supporters_data_url = "https://shiinasakamoto.github.io/vrct_supporters/assets/supporters/data.json";

View File

@@ -50,3 +50,11 @@ export const updateLabelsById = (data_array, updates) => {
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));
};