From ea43caae2750fad865aa946a57806ccbdca9ec1c Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Wed, 8 Oct 2025 16:29:56 -0500 Subject: [PATCH] Localized all components, hooks, providers, hooks --- ui/localization/messages/en.json | 391 +++++++++++++----- ui/src/components/ActionBar.tsx | 4 +- ui/src/components/Button.tsx | 9 +- ui/src/components/Checkbox.tsx | 2 +- ui/src/components/Combobox.tsx | 10 +- ui/src/components/ConfirmDialog.tsx | 9 +- ui/src/components/DhcpLeaseCard.tsx | 44 +- ui/src/components/EmptyCard.tsx | 5 +- ui/src/components/FeatureFlag.tsx | 2 +- ui/src/components/Fieldset.tsx | 2 +- ui/src/components/Header.tsx | 25 +- ui/src/components/InfoBar.tsx | 80 ++-- ui/src/components/Ipv6NetworkCard.tsx | 22 +- ui/src/components/JigglerSetting.tsx | 37 +- ui/src/components/KvmCard.tsx | 13 +- ui/src/components/MacroBar.tsx | 6 +- ui/src/components/MacroForm.tsx | 59 ++- ui/src/components/MacroStepCard.tsx | 28 +- ui/src/components/Metric.tsx | 12 +- ui/src/components/MetricsChart.tsx | 1 + ui/src/components/NotFoundPage.tsx | 7 +- .../components/PeerConnectionStatusCard.tsx | 17 +- ui/src/components/SelectMenuBasic.tsx | 6 +- ui/src/components/SidebarHeader.tsx | 5 +- ui/src/components/SimpleNavbar.tsx | 8 +- ui/src/components/StepCounter.tsx | 5 +- ui/src/components/Terminal.tsx | 10 +- ui/src/components/TextArea.tsx | 6 +- ui/src/components/USBStateStatus.tsx | 22 +- .../components/UpdateInProgressStatusCard.tsx | 16 +- ui/src/components/UsbDeviceSetting.tsx | 56 +-- ui/src/components/UsbInfoSetting.tsx | 62 +-- ui/src/components/VideoOverlay.tsx | 62 ++- ui/src/components/VirtualKeyboard.tsx | 26 +- ui/src/components/WebRTCVideo.tsx | 28 +- ui/src/components/popovers/PasteModal.tsx | 2 +- ui/src/components/sidebar/connectionStats.tsx | 2 +- ui/src/hooks/useVersion.tsx | 7 +- .../devices.$id.settings.macros.edit.tsx | 1 - ui/src/routes/login.tsx | 17 +- ui/src/routes/signup.tsx | 17 +- 41 files changed, 660 insertions(+), 483 deletions(-) diff --git a/ui/localization/messages/en.json b/ui/localization/messages/en.json index 7ac90c6c..ade66285 100644 --- a/ui/localization/messages/en.json +++ b/ui/localization/messages/en.json @@ -1,144 +1,323 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", "jetkvm": "JetKVM", - "oh_no": "Oh no!", - "something_went_wrong": "Something went wrong. Please try again later or contact support", "jetkvm_logo": "JetKVM Logo", - "load": "Load", - "unknown_error": "Unknown error", - - "close": "Close", + "attach": "Attach", "cancel": "Cancel", - - "action_bar_virtual_media": "Virtual Media", - "action_bar_paste_text": "Paste text", - "action_bar_web_terminal": "Web Terminal", - "action_bar_wake_on_lan": "Wake on LAN", - "action_bar_virtual_keyboard": "Virtual Keyboard", - "action_bar_extension": "Extension", + "close": "Close", + "confirm": "Confirm", + "connect_to_kvm": "Connect to KVM", + "default": "Default", + "delete": "Delete", + "deregister_from_cloud": "Deregister from cloud", + "detach": "Detach", + "dhcp_server": "DHCP Server", + "dns_servers": "DNS Servers", + "hide": "Hide", + "info_caps_lock": "Caps Lock", + "info_compose": "Compose", + "info_hdmi_state": "HDMI State:", + "info_hidrpc_state": "HidRPC State:", + "info_kana": "Kana", + "info_keys": "Keys:", + "info_last_move": "Last Move:", + "info_num_lock": "Num Lock", + "info_paste_enabled": "Enabled", + "info_paste_mode": "Paste Mode:", + "info_pointer": "Pointer:", + "info_relayed_by_cloudflare": "Relayed by Cloudflare", + "info_resolution": "Resolution:", + "info_scroll_lock": "Scroll Lock", + "info_shift": "Shift", + "info_usb_state": "USB State:", + "info_video_size": "Video Size:", + "input_disabled": "Input disabled", + "ip_address": "IP Address", + "last_online": "Last online {time}", + "load": "Load", + "log_out": "Log out", + "logged_in_as": "Logged in as", + "never_seen_online": "Never seen online", + "no_results_found": "No results found", + "not_available": "N/A", + "not_found": "Not found", + "ntp_servers": "NTP Servers", + "oh_no": "Oh no!", + "online": "Online", + "page_not_found_description": "The page you were looking for does not exist.", + "rename": "Rename", + "saving": "Saving...", + "search_placeholder": "Search...", + "something_went_wrong": "Something went wrong. Please try again later or contact support", + "subnet_mask": "Subnet Mask", + "troubleshoot_connection": "Troubleshoot Connection", + "unknown_error": "Unknown error", + "update_in_progress": "Update in Progress", + "updating_leave_device_on": "Please don't turn off your device...", + "usb": "USB", + "view_details": "View Details", "action_bar_connection_stats": "Connection Stats", - "action_bar_settings": "Settings", - "action_bar_fullscreen": "Fullscreen", "action_bar_exit_fullscreen": "Exit Fullscreen", - - "extensions_popover_extensions": "Extensions", + "action_bar_extension": "Extension", + "action_bar_fullscreen": "Fullscreen", + "action_bar_paste_text": "Paste text", + "action_bar_settings": "Settings", + "action_bar_virtual_keyboard": "Virtual Keyboard", + "action_bar_virtual_media": "Virtual Media", + "action_bar_wake_on_lan": "Wake on LAN", + "action_bar_web_terminal": "Web Terminal", + "extension_popover_load_and_manage_extensions": "Load and manage your extensions", "extension_popover_set_error_notification": "Failed to set active extension: {error}", "extension_popover_unload_extension": "Unload Extension", - "extension_popover_load_and_manage_extensions": "Load and manage your extensions", - "extensions_atx_power_control": "ATX Power Control", - "extensions_atx_power_control_description": "Control the power state of your machine via ATX power control.", - "extensions_dc_power_control": "DC Power Control", - "extensions_dc_power_control_description": "Control your DC Power extension", - "extension_serial_console": "Serial Console", "extension_serial_console_description": "Access your serial console extension", - + "extension_serial_console": "Serial Console", + "extensions_atx_power_control_description": "Control the power state of your machine via ATX power control.", + "extensions_atx_power_control": "ATX Power Control", + "extensions_dc_power_control_description": "Control your DC Power extension", + "extensions_dc_power_control": "DC Power Control", + "extensions_popover_extensions": "Extensions", "atx_power_control_get_state_error": "Failed to get ATX power state: {error}", - "atx_power_control_send_action_error": "Failed to send ATX power action {action}: {error}", - "atx_power_control_power_button": "Power", - "atx_power_control_short_power_button": "Short Press", - "atx_power_control_long_power_button": "Long Press", - "atx_power_control_reset_button": "Reset", - "atx_power_control_power_led": "Power LED", "atx_power_control_hdd_led": "HDD LED", - + "atx_power_control_long_power_button": "Long Press", + "atx_power_control_power_button": "Power", + "atx_power_control_power_led": "Power LED", + "atx_power_control_reset_button": "Reset", + "atx_power_control_send_action_error": "Failed to send ATX power action {action}: {error}", + "atx_power_control_short_power_button": "Short Press", + "dc_power_control_current_unit": "A", + "dc_power_control_current": "Current", "dc_power_control_get_state_error": "Failed to get DC power state: {error}", + "dc_power_control_power_off_button": "Power Off", + "dc_power_control_power_off_state": "Power OFF", + "dc_power_control_power_on_button": "Power On", + "dc_power_control_power_on_state": "Power ON", + "dc_power_control_power_unit": "W", + "dc_power_control_power": "Power", + "dc_power_control_restore_last_state": "Last State", + "dc_power_control_restore_power_state": "Restore Power Loss", "dc_power_control_set_power_state_error": "Failed to send DC power state to {enabled}: {error}", "dc_power_control_set_restore_state_error": "Failed to send DC power restore state to {state}: {error}", - "dc_power_control_power_on_button": "Power On", - "dc_power_control_power_off_button": "Power Off", - "dc_power_control_restore_power_state": "Restore Power Loss", - "dc_power_control_power_on_state": "Power ON", - "dc_power_control_power_off_state": "Power OFF", - "dc_power_control_restore_last_state": "Last State", - "dc_power_control_voltage": "Voltage", "dc_power_control_voltage_unit": "V", - "dc_power_control_current": "Current", - "dc_power_control_current_unit": "A", - "dc_power_control_power": "Power", - "dc_power_control_power_unit": "W", - - "serial_console_get_settings_error": "Failed to get serial console settings: {error}", - "serial_console_set_settings_error": "Failed to set serial console settings to {settings}: {error}", - "serial_console_configure_description": "Configure your serial console settings", - "serial_console_open_console": "Open Console", + "dc_power_control_voltage": "Voltage", "serial_console_baud_rate": "Baud Rate", + "serial_console_configure_description": "Configure your serial console settings", "serial_console_data_bits": "Data Bits", - "serial_console_stop_bits": "Stop Bits", - "serial_console_parity": "Parity", + "serial_console_get_settings_error": "Failed to get serial console settings: {error}", + "serial_console_open_console": "Open Console", "serial_console_parity_even": "Even Parity", - "serial_console_parity_odd": "Odd Parity", - "serial_console_parity_none": "No Parity", "serial_console_parity_mark": "Mark Parity", + "serial_console_parity_none": "No Parity", + "serial_console_parity_odd": "Odd Parity", "serial_console_parity_space": "Space Parity", - + "serial_console_parity": "Parity", + "serial_console_set_settings_error": "Failed to set serial console settings to {settings}: {error}", + "serial_console_stop_bits": "Stop Bits", + "wake_on_lan_add_device_back": "Back", "wake_on_lan_add_device_device_name": "Device Name", "wake_on_lan_add_device_example_device_name": "Plex Media Server", "wake_on_lan_add_device_mac_address": "MAC Address", - "wake_on_lan_add_device_back": "Back", "wake_on_lan_add_device_save_device": "Save Device", - - "paste_modal_paste_text": "Paste text", - "paste_modal_paste_text_description": "Paste text from your client to the remote host", - "paste_modal_paste_from_host": "Paste from host", - "paste_modal_invalid_chars_intro": "The following characters won't be pasted:", + "paste_modal_confirm_paste": "Confirm Paste", "paste_modal_delay_between_keys": "Delay between keys", "paste_modal_delay_out_of_range": "Delay must be between {min} and {max}", + "paste_modal_invalid_chars_intro": "The following characters won't be pasted:", + "paste_modal_paste_from_host": "Paste from host", + "paste_modal_paste_text_description": "Paste text from your client to the remote host", + "paste_modal_paste_text": "Paste text", "paste_modal_sending_using_layout": "Sending text using keyboard layout: {iso}-{name}", - "paste_modal_confirm_paste": "Confirm Paste", - - "mount_virtual_media": "Virtual Media", - "mount_virtual_media_description": "Mount an image to boot from or install an operating system.", - "mount_no_mounted_media": "No mounted media", + "paste_modal_failed_paste": "Failed to paste text: {error}", "mount_add_file_to_get_started": "Add a file to get started", - "mount_streaming_from_url": "Streaming from URL", - "mount_mounted_from_storage": "Mounted from JetKVM Storage", - "mount_unmount": "Unmount", "mount_add_new_media": "Add New Media", - + "mount_mounted_from_storage": "Mounted from JetKVM Storage", + "mount_no_mounted_media": "No mounted media", + "mount_streaming_from_url": "Streaming from URL", + "mount_unmount": "Unmount", + "mount_virtual_media_description": "Mount an image to boot from or install an operating system.", + "mount_virtual_media": "Virtual Media", "mount_get_state_error": "Failed to get virtual media state: {error}", - "mount_unmount_error": "Failed to unmount image: {error}", - "mount_mounted_as": "Mounted as", - "mount_mode_disk": "Disk", "mount_mode_cdrom": "CD-ROM", - - "wake_on_lan": "Wake On LAN", + "mount_mode_disk": "Disk", + "mount_mounted_as": "Mounted as", + "mount_unmount_error": "Failed to unmount image: {error}", "wake_on_lan_description": "Send a Magic Packet to wake up a remote device.", - - "wake_on_lan_invalid_mac": "Invalid MAC address", - "wake_on_lan_failed_send_magic": "Failed to send Magic Packet", - "wake_on_lan_magic_sent_success": "Magic Packet sent successfully", - - "wake_on_lan_failed_add_device": "Failed to add device", - - "wake_on_lan_empty_no_devices_added": "No devices added", + "wake_on_lan_device_list_add_new_device": "Add New Device", + "wake_on_lan_device_list_delete_device": "Delete device", + "wake_on_lan_device_list_wake": "Wake", "wake_on_lan_empty_add_device_to_start": "Add a device to start using Wake-on-LAN", "wake_on_lan_empty_add_new_device": "Add New Device", - - "wake_on_lan_device_list_wake": "Wake", - "wake_on_lan_device_list_delete_device": "Delete device", - "wake_on_lan_device_list_add_new_device": "Add New Device", - - "connection_stats_sidebar": "Connection Stats", - - "connection_stats_connection": "Connection", - "connection_stats_connection_description": "The connection between the client and the JetKVM.", - "connection_stats_round_trip_time": "Round-Trip Time", - "connection_stats_round_trip_time_description": "Round-trip time for the active ICE candidate pair between peers.", - - "connection_stats_video": "Video", - "connection_stats_video_description": "The video stream from the JetKVM to the client.", - - "connection_stats_network_stability": "Network Stability", - "connection_stats_network_stability_description": "How steady the flow of inbound video packets is across the network.", - "connection_stats_badge_jitter": "Jitter", - - "connection_stats_playback_delay": "Playback Delay", - "connection_stats_playback_delay_description": "Delay added by the jitter buffer to smooth playback when frames arrive unevenly.", + "wake_on_lan_empty_no_devices_added": "No devices added", + "wake_on_lan_failed_add_device": "Failed to add device", + "wake_on_lan_failed_send_magic": "Failed to send Magic Packet", + "wake_on_lan_invalid_mac": "Invalid MAC address", + "wake_on_lan_magic_sent_success": "Magic Packet sent successfully", + "wake_on_lan": "Wake On LAN", "connection_stats_badge_jitter_buffer_avg_delay": "Jitter Buffer Avg. Delay", - - "connection_stats_packets_lost": "Packets Lost", - "connection_stats_packets_lost_description": "Count of lost inbound video RTP packets.", - + "connection_stats_badge_jitter": "Jitter", + "connection_stats_connection_description": "The connection between the client and the JetKVM.", + "connection_stats_connection": "Connection", + "connection_stats_frames_per_second_description": "Number of inbound video frames displayed per second.", "connection_stats_frames_per_second": "Frames per second", - "connection_stats_frames_per_second_description": "Number of inbound video frames displayed per second." + "connection_stats_network_stability_description": "How steady the flow of inbound video packets is across the network.", + "connection_stats_network_stability": "Network Stability", + "connection_stats_packets_lost_description": "Count of lost inbound video RTP packets.", + "connection_stats_packets_lost": "Packets Lost", + "connection_stats_playback_delay_description": "Delay added by the jitter buffer to smooth playback when frames arrive unevenly.", + "connection_stats_playback_delay": "Playback Delay", + "connection_stats_round_trip_time_description": "Round-trip time for the active ICE candidate pair between peers.", + "connection_stats_round_trip_time": "Round-Trip Time", + "connection_stats_sidebar": "Connection Stats", + "connection_stats_video_description": "The video stream from the JetKVM to the client.", + "connection_stats_video": "Video", + "peer_connection_connected": "Connected", + "peer_connection_connecting": "Connecting", + "peer_connection_disconnected": "Disconnected", + "peer_connection_error": "Connection error", + "peer_connection_closing": "Closing", + "peer_connection_failed": "Connection failed", + "peer_connection_closed": "Closed", + "peer_connection_new": "Connecting", + "usb_device_keyboard_mouse_and_mass_storage": "Keyboard, Mouse and Mass Storage", + "usb_device_keyboard_only": "Keyboard Only", + "usb_device_custom": "Custom", + "usb_device_title": "USB Device", + "usb_device_description": "USB devices to emulate on the target computer", + "usb_device_classes_title": "Classes", + "usb_device_classes_description": "USB device classes in the composite device", + "usb_device_enable_keyboard_title": "Enable Keyboard", + "usb_device_enable_keyboard_description": "Enable Keyboard", + "usb_device_enable_absolute_mouse_title": "Enable Absolute Mouse (Pointer)", + "usb_device_enable_absolute_mouse_description": "Enable Absolute Mouse (Pointer)", + "usb_device_enable_relative_mouse_title": "Enable Relative Mouse", + "usb_device_enable_relative_mouse_description": "Enable Relative Mouse", + "usb_device_enable_mass_storage_title": "Enable USB Mass Storage", + "usb_device_enable_mass_storage_description": "Sometimes it might need to be disabled to prevent issues with certain devices", + "usb_device_update_classes": "Update USB Classes", + "usb_device_restore_default": "Restore to Default", + "usb_device_failed_load": "Failed to load USB devices: {error}", + "usb_device_failed_set": "Failed to set USB devices: {error}", + "usb_device_updated": "USB Devices updated", + "updates_failed_check": "Failed to check for updates: {error}", + "updates_failed_get_device_version": "Failed to get device version: {error}", + "auth_connect_to_cloud_action": "Log in & Connect device", + "auth_connect_to_cloud_description": "Unlock remote access and advanced features for your device", + "auth_connect_to_cloud": "Connect your JetKVM to the cloud", + "auth_signup_connect_to_cloud_action": "Signup & Connect device", + "auth_login_action": "Log in", + "auth_login_description": "Log in to access and manage your devices securely", + "auth_login": "Log in to your JetKVM account", + "auth_header_cta_already_have_account": "Already have an account?", + "auth_header_cta_dont_have_account": "Don't have an account?", + "auth_header_cta_new_to_jetkvm": "New to JetKVM?", + "auth_signup_create_account_action": "Create Account", + "auth_signup_create_account_description": "Create your account and start managing your devices with ease.", + "auth_signup_create_account": "Create your JetKVM account", + "video_overlay_autoplay_permissions_required": "Autoplay permissions required", + "video_overlay_conn_check_cables": "Check all cable connections for any loose or damaged wires", + "video_overlay_conn_ensure_network": "Ensure your network connection is stable and active", + "video_overlay_conn_restart": "Try restarting both the device and your computer", + "video_overlay_conn_verify_power": "Verify that the device is powered on and properly connected", + "video_overlay_connection_issue_title": "Connection Issue Detected", + "video_overlay_enable_autoplay_settings": "Please adjust browser settings to enable autoplay", + "video_overlay_hdmi_error_title": "HDMI signal error detected.", + "video_overlay_hdmi_incompatible_resolution": "Incompatible resolution or refresh rate settings", + "video_overlay_hdmi_loose_faulty": "A loose or faulty HDMI connection", + "video_overlay_hdmi_source_issue": "Issues with the source device's HDMI output", + "video_overlay_learn_more": "Learn more", + "video_overlay_loading_stream": "Loading video stream...", + "video_overlay_manually_start_stream": "Manually start stream", + "video_overlay_no_hdmi_adapter_compat": "If using an adapter, ensure it's compatible and functioning correctly", + "video_overlay_no_hdmi_ensure_cable": "Ensure the HDMI cable securely connected at both ends", + "video_overlay_no_hdmi_ensure_power": "Ensure source device is powered on and outputting a signal", + "video_overlay_no_hdmi_signal": "No HDMI signal detected.", + "video_overlay_pointerlock_click_to_enable": "Click on the video to enable mouse control", + "video_overlay_retrying_connection": "Retrying connection...", + "video_overlay_troubleshooting_guide": "Troubleshooting Guide", + "video_overlay_try_again": "Try again", + "video_pointer_lock_enabled": "Pointer lock enabled — press Escape to unlock", + "video_pointer_lock_disabled": "Pointer lock disabled", + "ipv6_information": "IPv6 Information", + "ipv6_link_local": "Link-local", + "ipv6_addresses": "IPv6 Addresses", + "ipv6_address_label": "Address", + "ipv6_valid_lifetime": "Valid Lifetime", + "ipv6_preferred_lifetime": "Preferred Lifetime", + "dhcp_lease_boot_file": "Boot File", + "dhcp_lease_boot_next_server": "Boot Next Server", + "dhcp_lease_boot_server_name": "Boot Server Name", + "dhcp_lease_broadcast": "Broadcast", + "dhcp_lease_domain": "Domain", + "dhcp_lease_gateway": "Gateway", + "dhcp_lease_header": "DHCP Lease Information", + "dhcp_lease_hostname": "Hostname", + "dhcp_lease_lease_expires": "Lease Expires", + "dhcp_lease_maximum_transfer_unit": "MTU", + "dhcp_lease_renew": "Renew DHCP Lease", + "dhcp_lease_time_to_live": "TTL", + "step_counter_step": "Step {step}", + "macro_add_step": "Add Step{maxed_out}", + "macro_at_least_one_step_keys_or_modifiers": "At least one step must have keys or modifiers", + "macro_at_least_one_step_required": "At least one step is required", + "macro_max_steps_error": "You can only add a maximum of {max} steps per macro.", + "macro_max_steps_reached": "({max} max)", + "macro_name_label": "Macro Name", + "macro_name_required": "Name is required", + "macro_name_too_long": "Name must be less than 50 characters", + "macro_please_fix_validation_errors": "Please fix the validation errors", + "macro_save": "Save Macro", + "macro_save_error": "An error occurred while saving.", + "macro_step_count": "{steps} / {max} steps", + "macro_steps_description": "Keys/modifiers executed in sequence with a delay between each step.", + "macro_steps_label": "Steps", + "macro_step_duration_description": "Time to wait before executing the next step.", + "macro_step_duration_label": "Step Duration", + "macro_step_keys_description": "Maximum {max} keys per step.", + "macro_step_keys_label": "Keys", + "macro_step_max_keys_reached": "Maximum keys reached", + "macro_step_modifiers_description": "What modifiers (Shift/Ctrl/Alt/Meta) are pressed during this step.", + "macro_step_modifiers_label": "Modifiers", + "macro_step_no_matching_keys_found": "No matching keys found", + "macro_step_search_for_key": "Search for key...", + "jiggler_examples_label": "Examples", + "jiggler_more_examples": "More examples", + "jiggler_cron_schedule_label": "Cron Schedule", + "jiggler_cron_schedule_description": "Cron expression for scheduling", + "jiggler_example_business_hours_late": "Business Hours 9-17", + "jiggler_example_business_hours_early": "Business Hours 8-17", + "jiggler_inactivity_limit_label": "Inactivity Limit Seconds", + "jiggler_inactivity_limit_description": "Inactivity time before jiggle", + "jiggler_random_delay_label": "Random delay", + "jiggler_random_delay_description": "To avoid recognizable patterns", + "jiggler_timezone_label": "Timezone", + "jiggler_timezone_description": "Timezone for cron schedule", + "jiggler_save_jiggler_config": "Save Jiggler Config", + "metric_waiting_for_data": "Waiting for data...", + "metric_not_supported": "Metric not supported", + "usb_config_custom": "Custom", + "usb_config_default": "JetKVM Default", + "usb_config_dell": "Dell Multimedia Pro Keyboard", + "usb_config_failed_load": "Failed to load USB Config: {error}", + "usb_config_failed_set": "Failed to set usb config: {error}", + "usb_config_identifiers_description": "USB device identifiers exposed to the target computer", + "usb_config_identifiers_title": "Identifiers", + "usb_config_logitech": "Logitech Universal Adapter", + "usb_config_manufacturer_label": "Manufacturer", + "usb_config_manufacturer_placeholder": "Enter Manufacturer", + "usb_config_microsoft": "Microsoft Wireless MultiMedia Keyboard", + "usb_config_product_id_label": "Product ID", + "usb_config_product_id_placeholder": "Enter Product ID", + "usb_config_product_name_label": "Product Name", + "usb_config_product_name_placeholder": "Enter Product Name", + "usb_config_restore_default": "Restore to Default", + "usb_config_serial_number_label": "Serial Number", + "usb_config_serial_number_placeholder": "Enter Serial Number", + "usb_config_set_success": "USB Config set to {manufacturer} {product}", + "usb_config_update_identifiers": "Update USB Identifiers", + "usb_config_vendor_id_label": "Vendor ID", + "usb_config_vendor_id_placeholder": "Enter Vendor ID", + "usb_state_connected": "Connected", + "usb_state_connecting": "Connecting", + "usb_state_disconnected": "Disconnected", + "usb_state_low_power_mode": "Low power mode", + "virtual_keyboard_header": "Virtual Keyboard", + "virtual_keyboard_description": "Use the virtual keyboard to send special keys or key combinations to the remote computer." } \ No newline at end of file diff --git a/ui/src/components/ActionBar.tsx b/ui/src/components/ActionBar.tsx index 04164696..1c34a6b8 100644 --- a/ui/src/components/ActionBar.tsx +++ b/ui/src/components/ActionBar.tsx @@ -4,6 +4,7 @@ import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-ic import { FaKeyboard } from "react-icons/fa6"; import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; import { CommandLineIcon } from "@heroicons/react/20/solid"; +import { cx } from "@/cva.config"; import { useHidStore, @@ -11,14 +12,13 @@ import { useSettingsStore, useUiStore, } from "@hooks/stores"; -import { cx } from "@/cva.config"; +import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; import { Button } from "@components/Button"; import Container from "@components/Container"; import PasteModal from "@components/popovers/PasteModal"; import WakeOnLanModal from "@components/popovers/WakeOnLan/Index"; import MountPopopover from "@components/popovers/MountPopover"; import ExtensionPopover from "@components/popovers/ExtensionPopover"; -import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; import { m } from "@localizations/messages.js"; export default function Actionbar({ diff --git a/ui/src/components/Button.tsx b/ui/src/components/Button.tsx index b1dc3ab9..eba9a964 100644 --- a/ui/src/components/Button.tsx +++ b/ui/src/components/Button.tsx @@ -1,11 +1,10 @@ import React, { JSX } from "react"; -import { Link, useNavigation } from "react-router"; -import type { FetcherWithComponents, LinkProps } from "react-router"; - -import ExtLink from "@/components/ExtLink"; -import LoadingSpinner from "@/components/LoadingSpinner"; +import { Link, type FetcherWithComponents, type LinkProps, useNavigation } from "react-router"; import { cva, cx } from "@/cva.config"; +import ExtLink from "@components/ExtLink"; +import LoadingSpinner from "@components/LoadingSpinner"; + const sizes = { XS: "h-[28px] px-2 text-xs", SM: "h-[36px] px-3 text-[13px]", diff --git a/ui/src/components/Checkbox.tsx b/ui/src/components/Checkbox.tsx index cf9855df..dd987b18 100644 --- a/ui/src/components/Checkbox.tsx +++ b/ui/src/components/Checkbox.tsx @@ -2,7 +2,7 @@ import type { Ref } from "react"; import React, { forwardRef, JSX } from "react"; import clsx from "clsx"; -import FieldLabel from "@/components/FieldLabel"; +import FieldLabel from "@components/FieldLabel"; import { cva, cx } from "@/cva.config"; const sizes = { diff --git a/ui/src/components/Combobox.tsx b/ui/src/components/Combobox.tsx index 3fce228f..34806e38 100644 --- a/ui/src/components/Combobox.tsx +++ b/ui/src/components/Combobox.tsx @@ -7,10 +7,10 @@ import { ComboboxOptions, } from "@headlessui/react"; +import { m } from "@localizations/messages.js"; +import Card from "@components/Card"; import { cva } from "@/cva.config"; -import Card from "./Card"; - export interface ComboboxOption { value: string; label: string; @@ -44,11 +44,11 @@ export function Combobox({ displayValue, options, disabled = false, - placeholder = "Search...", - emptyMessage = "No results found", + placeholder = m.search_placeholder(), + emptyMessage = m.no_results_found(), size = "MD", onChange, - disabledMessage = "Input disabled", + disabledMessage = m.input_disabled(), ...otherProps }: ComboboxProps) { const inputRef = useRef(null); diff --git a/ui/src/components/ConfirmDialog.tsx b/ui/src/components/ConfirmDialog.tsx index f6a39231..819666c2 100644 --- a/ui/src/components/ConfirmDialog.tsx +++ b/ui/src/components/ConfirmDialog.tsx @@ -4,8 +4,9 @@ import { InformationCircleIcon, } from "@heroicons/react/24/outline"; -import { Button } from "@/components/Button"; -import Modal from "@/components/Modal"; +import { m } from "@localizations/messages.js"; +import { Button } from "@components/Button"; +import Modal from "@components/Modal"; import { cx } from "@/cva.config"; type Variant = "danger" | "success" | "warning" | "info"; @@ -63,8 +64,8 @@ export function ConfirmDialog({ title, description, variant = "info", - confirmText = "Confirm", - cancelText = "Cancel", + confirmText = m.confirm(), + cancelText = m.cancel(), onConfirm, isConfirming = false, }: ConfirmDialogProps) { diff --git a/ui/src/components/DhcpLeaseCard.tsx b/ui/src/components/DhcpLeaseCard.tsx index 8a6e59c1..11c100bc 100644 --- a/ui/src/components/DhcpLeaseCard.tsx +++ b/ui/src/components/DhcpLeaseCard.tsx @@ -1,9 +1,10 @@ import { LuRefreshCcw } from "react-icons/lu"; -import { Button } from "@/components/Button"; -import { GridCard } from "@/components/Card"; -import { LifeTimeLabel } from "@/routes/devices.$id.settings.network"; -import { NetworkState } from "@/hooks/stores"; +import { Button } from "@components/Button"; +import { GridCard } from "@components/Card"; +import { LifeTimeLabel } from "@routes/devices.$id.settings.network"; +import { NetworkState } from "@hooks/stores"; +import { m } from "@localizations/messages.js"; export default function DhcpLeaseCard({ networkState, @@ -17,7 +18,7 @@ export default function DhcpLeaseCard({

- DHCP Lease Information + {m.dhcp_lease_header()}

@@ -25,7 +26,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.ip && (
- IP Address + {m.ip_address()} {networkState?.dhcp_lease?.ip} @@ -36,7 +37,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.netmask && (
- Subnet Mask + {m.subnet_mask()} {networkState?.dhcp_lease?.netmask} @@ -47,7 +48,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.dns && (
- DNS Servers + {m.dns_servers()} {networkState?.dhcp_lease?.dns.map(dns =>
{dns}
)} @@ -58,7 +59,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.broadcast && (
- Broadcast + {m.dhcp_lease_broadcast()} {networkState?.dhcp_lease?.broadcast} @@ -69,7 +70,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.domain && (
- Domain + {m.dhcp_lease_domain()} {networkState?.dhcp_lease?.domain} @@ -81,7 +82,7 @@ export default function DhcpLeaseCard({ networkState?.dhcp_lease?.ntp_servers.length > 0 && (
- NTP Servers + {m.ntp_servers()}
{networkState?.dhcp_lease?.ntp_servers.map(server => ( @@ -94,7 +95,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.hostname && (
- Hostname + {m.dhcp_lease_hostname()} {networkState?.dhcp_lease?.hostname} @@ -108,7 +109,7 @@ export default function DhcpLeaseCard({ networkState?.dhcp_lease?.routers.length > 0 && (
- Gateway + {m.dhcp_lease_gateway()} {networkState?.dhcp_lease?.routers.map(router => ( @@ -121,7 +122,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.server_id && (
- DHCP Server + {m.dhcp_server()} {networkState?.dhcp_lease?.server_id} @@ -132,7 +133,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.lease_expiry && (
- Lease Expires + {m.dhcp_lease_lease_expires()} MTU - {networkState?.dhcp_lease?.mtu} + {m.dhcp_lease_maximum_transfer_unit()}
)} @@ -155,7 +156,7 @@ export default function DhcpLeaseCard({
TTL - {networkState?.dhcp_lease?.ttl} + {m.dhcp_lease_time_to_live()}
)} @@ -163,7 +164,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.bootp_next_server && (
- Boot Next Server + {m.dhcp_lease_boot_next_server()} {networkState?.dhcp_lease?.bootp_next_server} @@ -174,7 +175,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.bootp_server_name && (
- Boot Server Name + {m.dhcp_lease_boot_server_name()} {networkState?.dhcp_lease?.bootp_server_name} @@ -185,7 +186,7 @@ export default function DhcpLeaseCard({ {networkState?.dhcp_lease?.bootp_file && (
- Boot File + {m.dhcp_lease_boot_file()} {networkState?.dhcp_lease?.bootp_file} @@ -194,13 +195,12 @@ export default function DhcpLeaseCard({ )}
-
diff --git a/ui/src/components/InfoBar.tsx b/ui/src/components/InfoBar.tsx index ce444d85..6d5db383 100644 --- a/ui/src/components/InfoBar.tsx +++ b/ui/src/components/InfoBar.tsx @@ -1,6 +1,5 @@ import { useEffect, useMemo } from "react"; -import { cx } from "@/cva.config"; import { useHidStore, useMouseStore, @@ -8,9 +7,11 @@ import { useSettingsStore, useVideoStore, VideoState -} from "@/hooks/stores"; +} from "@hooks/stores"; +import { useHidRpc } from "@hooks/useHidRpc"; import { keys, modifiers } from "@/keyboardMappings"; -import { useHidRpc } from "@/hooks/useHidRpc"; +import { cx } from "@/cva.config"; +import { m } from "@localizations/messages.js"; export default function InfoBar() { const { keysDownState } = useHidStore(); @@ -41,13 +42,16 @@ export default function InfoBar() { const { hdmiState } = useVideoStore(); const displayKeys = useMemo(() => { - if (!showPressedKeys) - return ""; + if (!showPressedKeys) return ""; const activeModifierMask = keysDownState.modifier || 0; const keysDown = keysDownState.keys || []; - const modifierNames = Object.entries(modifiers).filter(([_, mask]) => (activeModifierMask & mask) !== 0).map(([name, _]) => name); - const keyNames = Object.entries(keys).filter(([_, value]) => keysDown.includes(value)).map(([name, _]) => name); + const modifierNames = Object.entries(modifiers) + .filter(([_, mask]) => (activeModifierMask & mask) !== 0) + .map(([name]) => name); + const keyNames = Object.entries(keys) + .filter(([_, value]) => keysDown.includes(value)) + .map(([name]) => name); return [...modifierNames, ...keyNames].join(", "); }, [keysDownState, showPressedKeys]); @@ -59,76 +63,75 @@ export default function InfoBar() {
{debugMode ? (
- Resolution:{" "} + {m.info_resolution()}{" "} {videoSize}
) : null} {debugMode ? (
- Video Size: + {m.info_video_size()} {videoClientSize}
) : null} {(debugMode && mouseMode == "absolute") ? (
- Pointer: - - {mouseX},{mouseY} - + {m.info_pointer()} + {mouseX},{mouseY}
) : null} {(debugMode && mouseMode == "relative") ? (
- Last Move: + {m.info_last_move()} - {mouseMove ? - `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : - "N/A"} + {mouseMove ? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : "N/A"}
) : null} {debugMode && (
- USB State: + {m.info_usb_state()} {usbState}
)} + {debugMode && (
- HDMI State: + {m.info_hdmi_state()} {hdmiState}
)} + {debugMode && (
- HidRPC State: + {m.info_hidrpc_state()} {rpcHidStatus}
)} + {isPasteInProgress && (
- Paste Mode: - Enabled + {m.info_paste_mode()} + {m.info_paste_enabled()}
)} + {showPressedKeys && (
- Keys: -

- {displayKeys} -

+ {m.info_keys()} +

{displayKeys}

)}
+
{isTurnServerInUse && (
- Relayed by Cloudflare + {m.info_relayed_by_cloudflare()}
)} @@ -140,8 +143,9 @@ export default function InfoBar() { : "text-slate-800/20 dark:text-slate-300/20", )} > - Caps Lock + {m.info_caps_lock()}
+
- Num Lock + {m.info_num_lock()}
+
- Scroll Lock + {m.info_scroll_lock()}
+ {keyboardLedState.compose ? ( -
- Compose -
+
{m.info_compose()}
) : null} + {keyboardLedState.kana ? ( -
- Kana -
+
{m.info_kana()}
) : null} + {keyboardLedState.shift ? ( -
- Shift -
+
{m.info_shift()}
) : null}
diff --git a/ui/src/components/Ipv6NetworkCard.tsx b/ui/src/components/Ipv6NetworkCard.tsx index 0cfacc6d..6d561510 100644 --- a/ui/src/components/Ipv6NetworkCard.tsx +++ b/ui/src/components/Ipv6NetworkCard.tsx @@ -1,7 +1,7 @@ -import { NetworkState } from "../hooks/stores"; -import { LifeTimeLabel } from "../routes/devices.$id.settings.network"; - -import { GridCard } from "./Card"; +import { NetworkState } from "@hooks/stores"; +import { GridCard } from "@components/Card"; +import { LifeTimeLabel } from "@routes/devices.$id.settings.network"; +import { m } from "@localizations/messages.js"; export default function Ipv6NetworkCard({ networkState, @@ -13,14 +13,14 @@ export default function Ipv6NetworkCard({

- IPv6 Information + {m.ipv6_information()}

{networkState?.ipv6_link_local && (
- Link-local + {m.ipv6_link_local()} {networkState?.ipv6_link_local} @@ -42,7 +42,7 @@ export default function Ipv6NetworkCard({
- Address + {m.ipv6_address_label()} {addr.address}
@@ -50,12 +50,12 @@ export default function Ipv6NetworkCard({ {addr.valid_lifetime && (
- Valid Lifetime + {m.ipv6_valid_lifetime()} {addr.valid_lifetime === "" ? ( - N/A + {m.not_available()} ) : ( @@ -66,12 +66,12 @@ export default function Ipv6NetworkCard({ {addr.preferred_lifetime && (
- Preferred Lifetime + {m.ipv6_preferred_lifetime()} {addr.preferred_lifetime === "" ? ( - N/A + {m.not_available()} ) : ( diff --git a/ui/src/components/JigglerSetting.tsx b/ui/src/components/JigglerSetting.tsx index fc0f50dd..6578906e 100644 --- a/ui/src/components/JigglerSetting.tsx +++ b/ui/src/components/JigglerSetting.tsx @@ -1,11 +1,11 @@ import { useEffect, useMemo, useState } from "react"; import { LuExternalLink } from "react-icons/lu"; +import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { Button, LinkButton } from "@components/Button"; -import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc"; - -import { InputFieldWithLabel } from "./InputField"; -import { SelectMenuBasic } from "./SelectMenuBasic"; +import { InputFieldWithLabel } from "@components/InputField"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; +import { m } from "@localizations/messages.js"; export interface JigglerConfig { inactivity_limit_seconds: number; @@ -51,7 +51,7 @@ export function JigglerSetting({ const exampleConfigs = [ { - name: "Business Hours 9-17", + name: m.jiggler_example_business_hours_late(), config: { inactivity_limit_seconds: 60, jitter_percentage: 25, @@ -60,7 +60,7 @@ export function JigglerSetting({ }, }, { - name: "Business Hours 8-17", + name: m.jiggler_example_business_hours_early(), config: { inactivity_limit_seconds: 60, jitter_percentage: 25, @@ -69,13 +69,10 @@ export function JigglerSetting({ }, }, ]; - return (
-

- Examples -

+

{m.jiggler_examples_label()}

{exampleConfigs.map((example, index) => (
@@ -100,8 +97,8 @@ export function JigglerSetting({ @@ -114,8 +111,8 @@ export function JigglerSetting({ %} value={jigglerConfigState.jitter_percentage} @@ -149,8 +146,8 @@ export function JigglerSetting({ @@ -167,7 +164,7 @@ export function JigglerSetting({
diff --git a/ui/src/components/KvmCard.tsx b/ui/src/components/KvmCard.tsx index ab34976b..f4e3bf4d 100644 --- a/ui/src/components/KvmCard.tsx +++ b/ui/src/components/KvmCard.tsx @@ -1,10 +1,11 @@ +import { Link } from "react-router"; import { MdConnectWithoutContact } from "react-icons/md"; import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; -import { Link } from "react-router"; import { LuEllipsisVertical } from "react-icons/lu"; import Card from "@components/Card"; import { Button, LinkButton } from "@components/Button"; +import { m } from "@localizations/messages.js"; function getRelativeTimeString(date: Date | number, lang = navigator.language): string { // Allow dates or times to be passed @@ -62,16 +63,16 @@ export default function KvmCard({ {online ? (
-
Online
+
{m.online()}
) : (
{lastSeen ? ( - <>Last online {getRelativeTimeString(lastSeen)} + <>{m.last_online({ time: getRelativeTimeString(lastSeen) })} ) : ( - <>Never seen online + <>{m.never_seen_online()} )}
@@ -85,7 +86,7 @@ export default function KvmCard({ )} diff --git a/ui/src/components/MacroBar.tsx b/ui/src/components/MacroBar.tsx index 0ba8cf4f..7536ad2a 100644 --- a/ui/src/components/MacroBar.tsx +++ b/ui/src/components/MacroBar.tsx @@ -1,11 +1,11 @@ import { useEffect } from "react"; import { LuCommand } from "react-icons/lu"; +import { useMacrosStore } from "@hooks/stores"; +import useKeyboard from "@hooks/useKeyboard"; +import { useJsonRpc } from "@hooks/useJsonRpc"; import { Button } from "@components/Button"; import Container from "@components/Container"; -import { useMacrosStore } from "@/hooks/stores"; -import useKeyboard from "@/hooks/useKeyboard"; -import { useJsonRpc } from "@/hooks/useJsonRpc"; export default function MacroBar() { const { macros, initialized, loadMacros, setSendFn } = useMacrosStore(); diff --git a/ui/src/components/MacroForm.tsx b/ui/src/components/MacroForm.tsx index 1aafe9c9..f9517164 100644 --- a/ui/src/components/MacroForm.tsx +++ b/ui/src/components/MacroForm.tsx @@ -1,18 +1,19 @@ import { useState } from "react"; import { LuPlus } from "react-icons/lu"; -import { Button } from "@/components/Button"; -import FieldLabel from "@/components/FieldLabel"; -import Fieldset from "@/components/Fieldset"; -import { InputFieldWithLabel, FieldError } from "@/components/InputField"; -import { MacroStepCard } from "@/components/MacroStepCard"; +import { KeySequence } from "@hooks/stores"; +import useKeyboardLayout from "@hooks/useKeyboardLayout"; +import { Button } from "@components/Button"; +import FieldLabel from "@components/FieldLabel"; +import Fieldset from "@components/Fieldset"; +import { InputFieldWithLabel, FieldError } from "@components/InputField"; +import { MacroStepCard } from "@components/MacroStepCard"; import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP, } from "@/constants/macros"; -import { KeySequence } from "@/hooks/stores"; -import useKeyboardLayout from "@/hooks/useKeyboardLayout"; +import { m } from "@localizations/messages.js"; interface ValidationErrors { name?: string; @@ -31,7 +32,6 @@ interface MacroFormProps { onSubmit: (macro: Partial) => Promise; onCancel: () => void; isSubmitting?: boolean; - submitText?: string; } export function MacroForm({ @@ -39,7 +39,6 @@ export function MacroForm({ onSubmit, onCancel, isSubmitting = false, - submitText = "Save Macro", }: MacroFormProps) { const [macro, setMacro] = useState>(initialData); const [keyQueries, setKeyQueries] = useState>({}); @@ -57,13 +56,13 @@ export function MacroForm({ // Name validation if (!macro.name?.trim()) { - newErrors.name = "Name is required"; + newErrors.name = m.macro_name_required(); } else if (macro.name.trim().length > 50) { - newErrors.name = "Name must be less than 50 characters"; + newErrors.name = m.macro_name_too_long(); } if (!macro.steps?.length) { - newErrors.steps = { 0: { keys: "At least one step is required" } }; + newErrors.steps = { 0: { keys: m.macro_at_least_one_step_required() } }; } else { const hasKeyOrModifier = macro.steps.some( step => (step.keys?.length || 0) > 0 || (step.modifiers?.length || 0) > 0, @@ -71,7 +70,7 @@ export function MacroForm({ if (!hasKeyOrModifier) { newErrors.steps = { - 0: { keys: "At least one step must have keys or modifiers" }, + 0: { keys: m.macro_at_least_one_step_keys_or_modifiers() }, }; } } @@ -82,7 +81,7 @@ export function MacroForm({ const handleSubmit = async () => { if (!validateForm()) { - showTemporaryError("Please fix the validation errors"); + showTemporaryError(m.macro_please_fix_validation_errors()); return; } @@ -92,7 +91,7 @@ export function MacroForm({ if (error instanceof Error) { showTemporaryError(error.message); } else { - showTemporaryError("An error occurred while saving"); + showTemporaryError(m.macro_save_error()); } } }; @@ -114,7 +113,7 @@ export function MacroForm({ ? newSteps[stepIndex].keys : []; if (keysArray.length >= MAX_KEYS_PER_STEP) { - showTemporaryError(`Maximum of ${MAX_KEYS_PER_STEP} keys per step allowed`); + showTemporaryError(m.macro_max_steps_error({max: MAX_KEYS_PER_STEP})); return; } newSteps[stepIndex].keys = [...keysArray, option.value]; @@ -178,8 +177,8 @@ export function MacroForm({
{ @@ -197,12 +196,12 @@ export function MacroForm({
- {macro.steps?.length || 0}/{MAX_STEPS_PER_MACRO} steps + {m.macro_step_count({steps: macro.steps?.length || 0, max: MAX_STEPS_PER_MACRO})}
{errors.steps && errors.steps[0]?.keys && ( @@ -220,10 +219,10 @@ export function MacroForm({ onDelete={ macro.steps && macro.steps.length > 1 ? () => { - const newSteps = [...(macro.steps || [])]; - newSteps.splice(stepIndex, 1); - setMacro(prev => ({ ...prev, steps: newSteps })); - } + const newSteps = [...(macro.steps || [])]; + newSteps.splice(stepIndex, 1); + setMacro(prev => ({ ...prev, steps: newSteps })); + } : undefined } onMoveUp={() => handleStepMove(stepIndex, "up")} @@ -248,12 +247,10 @@ export function MacroForm({ theme="light" fullWidth LeadingIcon={LuPlus} - text={`Add Step ${isMaxStepsReached ? `(${MAX_STEPS_PER_MACRO} max)` : ""}`} + text={m.macro_add_step({ maxed_out: isMaxStepsReached ? m.macro_max_steps_reached({ max: MAX_STEPS_PER_MACRO} ) : ""})} onClick={() => { if (isMaxStepsReached) { - showTemporaryError( - `You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`, - ); + showTemporaryError(m.macro_max_steps_error({max: MAX_STEPS_PER_MACRO})); return; } @@ -280,11 +277,11 @@ export function MacroForm({
diff --git a/ui/src/components/MacroStepCard.tsx b/ui/src/components/MacroStepCard.tsx index c795520d..337e74a3 100644 --- a/ui/src/components/MacroStepCard.tsx +++ b/ui/src/components/MacroStepCard.tsx @@ -1,14 +1,15 @@ import { useMemo } from "react"; import { LuArrowUp, LuArrowDown, LuX, LuTrash2 } from "react-icons/lu"; -import { Button } from "@/components/Button"; -import { Combobox } from "@/components/Combobox"; -import { SelectMenuBasic } from "@/components/SelectMenuBasic"; -import Card from "@/components/Card"; -import FieldLabel from "@/components/FieldLabel"; +import { Button } from "@components/Button"; +import { Combobox } from "@components/Combobox"; +import Card from "@components/Card"; +import FieldLabel from "@components/FieldLabel"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros"; import { KeyboardLayout } from "@/keyboardLayouts"; import { keys, modifiers } from "@/keyboardMappings"; +import { m } from "@localizations/messages.js"; // Filter out modifier keys since they're handled in the modifiers section const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta']; @@ -25,6 +26,7 @@ const groupedModifiers: Record = { Meta: modifierOptions.filter(mod => mod.value.startsWith('Meta')), }; +// not going to localize these since they're short time intervals const basePresetDelays = [ { value: "50", label: "50ms" }, { value: "100", label: "100ms" }, @@ -132,12 +134,12 @@ export function MacroStepCard({ LeadingIcon={LuArrowDown} />
- {onDelete && ( + {onDelete && (
); } diff --git a/ui/src/components/Terminal.tsx b/ui/src/components/Terminal.tsx index ba3e667c..3ba09bfa 100644 --- a/ui/src/components/Terminal.tsx +++ b/ui/src/components/Terminal.tsx @@ -1,17 +1,17 @@ +import { useEffect, useMemo } from "react"; import "react-simple-keyboard/build/css/index.css"; import { ChevronDownIcon } from "@heroicons/react/16/solid"; -import { useEffect, useMemo } from "react"; import { useXTerm } from "react-xtermjs"; import { FitAddon } from "@xterm/addon-fit"; import { WebLinksAddon } from "@xterm/addon-web-links"; import { WebglAddon } from "@xterm/addon-webgl"; import { Unicode11Addon } from "@xterm/addon-unicode11"; import { ClipboardAddon } from "@xterm/addon-clipboard"; - import { cx } from "@/cva.config"; -import { AvailableTerminalTypes, useUiStore } from "@/hooks/stores"; -import { Button } from "./Button"; +import { AvailableTerminalTypes, useUiStore } from "@hooks/stores"; +import { Button } from "@components/Button"; +import { m } from "@localizations/messages.js"; const isWebGl2Supported = !!document.createElement("canvas").getContext("webgl2"); @@ -191,7 +191,7 @@ function Terminal({
diff --git a/ui/src/components/UsbInfoSetting.tsx b/ui/src/components/UsbInfoSetting.tsx index 1b4f3114..1a564e03 100644 --- a/ui/src/components/UsbInfoSetting.tsx +++ b/ui/src/components/UsbInfoSetting.tsx @@ -1,15 +1,14 @@ import { useMemo , useCallback , useEffect, useState } from "react"; +import { UsbConfigState } from "@hooks/stores"; +import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { Button } from "@components/Button"; +import Fieldset from "@components/Fieldset"; +import { InputFieldWithLabel } from "@components/InputField"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { SettingsItem } from "@components/SettingsItem"; - -import { UsbConfigState } from "../hooks/stores"; -import { JsonRpcResponse, useJsonRpc } from "../hooks/useJsonRpc"; -import notifications from "../notifications"; - -import { InputFieldWithLabel } from "./InputField"; -import { SelectMenuBasic } from "./SelectMenuBasic"; -import Fieldset from "./Fieldset"; +import notifications from "@/notifications"; +import { m } from "@localizations/messages.js"; const generatedSerialNumber = [generateNumber(1, 9), generateHex(7, 7), 0, 1].join("&"); @@ -31,21 +30,22 @@ export interface USBConfig { product: string; } + const usbConfigs = [ { - label: "JetKVM Default", + label: m.usb_config_default(), value: "USB Emulation Device", }, { - label: "Logitech Universal Adapter", + label: m.usb_config_logitech(), value: "Logitech USB Input Device", }, { - label: "Microsoft Wireless MultiMedia Keyboard", + label: m.usb_config_microsoft(), value: "Wireless MultiMedia Keyboard", }, { - label: "Dell Multimedia Pro Keyboard", + label: m.usb_config_dell(), value: "Multimedia Pro Keyboard", }, ]; @@ -94,10 +94,10 @@ export function UsbInfoSetting() { const syncUsbConfigProduct = useCallback(() => { send("getUsbConfig", {}, (resp: JsonRpcResponse) => { - if ("error" in resp) { + if ("error" in resp) { console.error("Failed to load USB Config:", resp.error); notifications.error( - `Failed to load USB Config: ${resp.error.data || "Unknown error"}`, + m.usb_config_failed_load({ error: String(resp.error.data || "Unknown error") }), ); } else { const usbConfigState = resp.result as UsbConfigState; @@ -116,7 +116,7 @@ export function UsbInfoSetting() { send("setUsbConfig", { usbConfig }, async (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( - `Failed to set usb config: ${resp.error.data || "Unknown error"}`, + m.usb_config_failed_set({ error: String(resp.error.data || "Unknown error") }), ); setLoading(false); return; @@ -126,7 +126,7 @@ export function UsbInfoSetting() { await new Promise(resolve => setTimeout(resolve, 2000)); setLoading(false); notifications.success( - `USB Config set to ${usbConfig.manufacturer} ${usbConfig.product}`, + m.usb_config_set_success({ manufacturer: usbConfig.manufacturer, product: usbConfig.product }), ); syncUsbConfigProduct(); @@ -152,8 +152,8 @@ export function UsbInfoSetting() {
{usbConfigProduct === "custom" && ( @@ -246,38 +246,38 @@ function USBConfigDialog({
handleUsbVendorIdChange(e.target.value)} /> handleUsbProductIdChange(e.target.value)} /> handleUsbSerialChange(e.target.value)} /> handleUsbManufacturer(e.target.value)} /> handleUsbProduct(e.target.value)} /> @@ -287,13 +287,13 @@ function USBConfigDialog({ loading={loading} size="SM" theme="primary" - text="Update USB Identifiers" + text={m.usb_config_update_identifiers()} onClick={() => onSetUsbConfig(usbConfigState)} />
diff --git a/ui/src/components/VideoOverlay.tsx b/ui/src/components/VideoOverlay.tsx index 1c59e788..1194e1e9 100644 --- a/ui/src/components/VideoOverlay.tsx +++ b/ui/src/components/VideoOverlay.tsx @@ -5,6 +5,7 @@ import { motion, AnimatePresence } from "framer-motion"; import { LuPlay } from "react-icons/lu"; import { BsMouseFill } from "react-icons/bs"; +import { m } from "@localizations/messages.js"; import { Button, LinkButton } from "@components/Button"; import LoadingSpinner from "@components/LoadingSpinner"; import Card, { GridCard } from "@components/Card"; @@ -46,7 +47,7 @@ export function LoadingVideoOverlay({ show }: LoadingOverlayProps) {

- Loading video stream... + {m.video_overlay_loading_stream()}

@@ -118,26 +119,26 @@ export function ConnectionFailedOverlay({
-

Connection Issue Detected

+

{m.video_overlay_connection_issue_title()}

    -
  • Verify that the device is powered on and properly connected
  • -
  • Check all cable connections for any loose or damaged wires
  • -
  • Ensure your network connection is stable and active
  • -
  • Try restarting both the device and your computer
  • +
  • {m.video_overlay_conn_verify_power()}
  • +
  • {m.video_overlay_conn_check_cables()}
  • +
  • {m.video_overlay_conn_ensure_network()}
  • +
  • {m.video_overlay_conn_restart()}

- Virtual Keyboard + m.virtual_keyboard_header()

@@ -274,7 +272,7 @@ function KeyboardWrapper() {