diff --git a/ui/localization/messages/en.json b/ui/localization/messages/en.json index 5f1f6e88..b38d1c64 100644 --- a/ui/localization/messages/en.json +++ b/ui/localization/messages/en.json @@ -185,7 +185,7 @@ "connection_stats_video_description": "The video stream from the JetKVM to the client.", "connection_stats_video": "Video", "continue": "Continue", - "creating_peer_connection": "Creating peer connection...", + "creating_peer_connection": "Creating peer connection…", "dc_power_control_current_unit": "A", "dc_power_control_current": "Current", "dc_power_control_get_state_error": "Failed to get DC power state: {error}", @@ -236,7 +236,7 @@ "extensions_dc_power_control_description": "Control your DC Power extension", "extensions_dc_power_control": "DC Power Control", "extensions_popover_extensions": "Extensions", - "gathering_ice_candidates": "Gathering ICE candidates...", + "gathering_ice_candidates": "Gathering ICE candidates…", "general_app_version": "App: {version}", "general_auto_update_description": "Automatically update the device to the latest version", "general_auto_update_error": "Failed to set auto-update: {error}", @@ -258,7 +258,7 @@ "general_update_background_button": "Update in Background", "general_update_check_again_button": "Check Again", "general_update_checking_description": "We're ensuring your device has the latest features and improvements.", - "general_update_checking_title": "Checking for updates...", + "general_update_checking_title": "Checking for updates…", "general_update_completed_description": "Your device has been successfully updated to the latest version. Enjoy the new features and improvements!", "general_update_completed_title": "Update Completed Successfully", "general_update_error_description": "An error occurred while updating your device. Please try again later.", @@ -266,12 +266,12 @@ "general_update_error_title": "Update Error", "general_update_later_button": "Do it later", "general_update_now_button": "Update Now", - "general_update_rebooting": "Rebooting to complete the update...", + "general_update_rebooting": "Rebooting to complete the update…", "general_update_status_awaiting_reboot": "Awaiting reboot", - "general_update_status_downloading": "Downloading {update_type} update...", - "general_update_status_fetching": "Fetching update information...", - "general_update_status_installing": "Installing {update_type} update...", - "general_update_status_verifying": "Verifying {update_type} update...", + "general_update_status_downloading": "Downloading {update_type} update…", + "general_update_status_fetching": "Fetching update information…", + "general_update_status_installing": "Installing {update_type} update…", + "general_update_status_verifying": "Verifying {update_type} update…", "general_update_system_type": "System", "general_update_system_update_title": "Linux System Update", "general_update_up_to_date_description": "Your system is running the latest version. No updates are currently available.", @@ -438,6 +438,54 @@ "macro_step_search_for_key": "Search for key…", "macro_steps_description": "Keys/modifiers executed in sequence with a delay between each step.", "macro_steps_label": "Steps", + "macros_add_description": "Create a new keyboard macro", + "macros_add_new": "Add New Macro", + "macros_create_first": "Create your first macro to get started", + "macros_created_success": "Macro \"{name}\" created successfully", + "macros_delete_confirm": "Are you sure you want to delete this macro? This action cannot be undone.", + "macros_delete_macro": "Delete Macro", + "macros_deleted_success": "Macro \"{name}\" deleted successfully", + "macros_deleting": "Deleting", + "macros_duplicate": "Duplicate", + "macros_duplicated_success": "Macro \"{name}\" duplicated successfully", + "macros_edit_description": "Modify your keyboard macro", + "macros_edit_title": "Edit Macro", + "macros_edit": "Edit", + "macros_failed_create": "Failed to create macro", + "macros_failed_create_error": "Failed to create macro: {error}", + "macros_failed_delete": "Failed to delete macro", + "macros_failed_delete_error": "Failed to delete macro: {error}", + "macros_failed_duplicate": "Failed to duplicate macro", + "macros_failed_duplicate_error": "Failed to duplicate macro: {error}", + "macros_failed_reorder": "Failed to reorder macros", + "macros_failed_reorder_error": "Failed to reorder macros: {error}", + "macros_failed_update": "Failed to update macro", + "macros_failed_update_error": "Failed to update macro: {error}", + "macros_invalid_data": "Invalid macro data", + "macros_maximum_macros_reached": "You have reached the maximum number of {maximum} macros allowed.", + "macros_move_down": "Move Down", + "macros_move_up": "Move Up", + "macros_no_macros_available": "No macros available", + "macros_no_macros_found": "No macros found", + "macros_order_updated": "Macro order updated successfully", + "macros_title": "Keyboard Macros", + "macros_updated_success": "Macro \"{name}\" updated successfully", + "macros_aria_delete": "Delete macro {name}", + "macros_aria_duplicate": "Duplicate macro {name}", + "macros_aria_edit": "Edit macro {name}", + "macros_aria_move_down": "Move {name} down", + "macros_aria_move_up": "Move {name} up", + "macros_confirm_delete_description": "Are you sure you want to delete \"{name}\"? This action cannot be undone.", + "macros_confirm_delete_title": "Delete Macro", + "macros_confirm_deleting": "Deleting…", + "macros_add_new_macro": "Add New Macro", + "macros_aria_add_new": "Add new macro", + "macros_create_first_headline": "Create Your First Macro", + "macros_create_first_description": "Combine keystrokes into a single action", + "macros_delay_only": "Delay only", + "macros_edit_button": "Edit", + "macros_loading": "Loading macros…", + "macros_max_reached": "Max Reached", "metric_not_supported": "Metric not supported", "metric_waiting_for_data": "Waiting for data…", "mount_add_file_to_get_started": "Add a file to get started", diff --git a/ui/src/routes/devices.$id.settings.macros.add.tsx b/ui/src/routes/devices.$id.settings.macros.add.tsx index 7a9f493e..123fee5f 100644 --- a/ui/src/routes/devices.$id.settings.macros.add.tsx +++ b/ui/src/routes/devices.$id.settings.macros.add.tsx @@ -1,24 +1,19 @@ -import { useNavigate } from "react-router"; import { useState } from "react"; +import { useNavigate } from "react-router"; -import { KeySequence, useMacrosStore, generateMacroId } from "@/hooks/stores"; -import { SettingsPageHeader } from "@/components/SettingsPageheader"; -import { MacroForm } from "@/components/MacroForm"; +import { KeySequence, useMacrosStore, generateMacroId } from "@hooks/stores"; +import { MacroForm } from "@components/MacroForm"; +import { SettingsPageHeader } from "@components/SettingsPageheader"; import { DEFAULT_DELAY } from "@/constants/macros"; import notifications from "@/notifications"; +import { normalizeSortOrders } from "@/utils"; +import { m } from "@localizations/messages.js"; export default function SettingsMacrosAddRoute() { const { macros, saveMacros } = useMacrosStore(); const [isSaving, setIsSaving] = useState(false); const navigate = useNavigate(); - const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => { - return macros.map((macro, index) => ({ - ...macro, - sortOrder: index + 1, - })); - }; - const handleAddMacro = async (macro: Partial) => { setIsSaving(true); try { @@ -30,13 +25,13 @@ export default function SettingsMacrosAddRoute() { }; await saveMacros(normalizeSortOrders([...macros, newMacro])); - notifications.success(`Macro "${newMacro.name}" created successfully`); + notifications.success(m.macros_created_success({name: newMacro.name})); navigate("../"); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to create macro: ${error.message}`); + notifications.error(m.macros_failed_create_error({error: error.message || m.unknown_error() })); } else { - notifications.error("Failed to create macro"); + notifications.error(m.macros_failed_create()); } } finally { setIsSaving(false); @@ -46,8 +41,8 @@ export default function SettingsMacrosAddRoute() { return (
); -} \ No newline at end of file +} \ No newline at end of file diff --git a/ui/src/routes/devices.$id.settings.macros.edit.tsx b/ui/src/routes/devices.$id.settings.macros.edit.tsx index 8b974d51..06783159 100644 --- a/ui/src/routes/devices.$id.settings.macros.edit.tsx +++ b/ui/src/routes/devices.$id.settings.macros.edit.tsx @@ -1,20 +1,15 @@ -import { useNavigate, useParams } from "react-router"; import { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router"; import { LuTrash2 } from "react-icons/lu"; -import { KeySequence, useMacrosStore } from "@/hooks/stores"; -import { SettingsPageHeader } from "@/components/SettingsPageheader"; -import { MacroForm } from "@/components/MacroForm"; +import { KeySequence, useMacrosStore } from "@hooks/stores"; +import { Button } from "@components/Button"; +import { ConfirmDialog } from "@components/ConfirmDialog"; +import { MacroForm } from "@components/MacroForm"; +import { SettingsPageHeader } from "@components/SettingsPageheader"; import notifications from "@/notifications"; -import { Button } from "@/components/Button"; -import { ConfirmDialog } from "@/components/ConfirmDialog"; - -const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => { - return macros.map((macro, index) => ({ - ...macro, - sortOrder: index + 1, - })); -}; +import { normalizeSortOrders } from "@/utils"; +import { m } from "@localizations/messages.js"; export default function SettingsMacrosEditRoute() { const { macros, saveMacros } = useMacrosStore(); @@ -56,13 +51,13 @@ export default function SettingsMacrosEditRoute() { ); await saveMacros(normalizeSortOrders(newMacros)); - notifications.success(`Macro "${updatedMacro.name}" updated successfully`); + notifications.success(m.macros_updated_success({ name: updatedMacro.name })); navigate("../"); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to update macro: ${error.message}`); + notifications.error(m.macros_failed_update({ error: error.message })); } else { - notifications.error("Failed to update macro"); + notifications.error(m.macros_failed_update()); } } finally { setIsUpdating(false); @@ -76,13 +71,13 @@ export default function SettingsMacrosEditRoute() { try { const updatedMacros = normalizeSortOrders(macros.filter(m => m.id !== macro.id)); await saveMacros(updatedMacros); - notifications.success(`Macro "${macro.name}" deleted successfully`); + notifications.success(m.macros_deleted_success({ name: macro.name })); navigate("../macros"); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to delete macro: ${error.message}`); + notifications.error(m.macros_failed_delete_error({ error: error.message })); } else { - notifications.error("Failed to delete macro"); + notifications.error(m.macros_failed_delete()); } } finally { setIsDeleting(false); @@ -95,13 +90,13 @@ export default function SettingsMacrosEditRoute() {
); -} \ No newline at end of file +} \ No newline at end of file diff --git a/ui/src/routes/devices.$id.settings.macros.tsx b/ui/src/routes/devices.$id.settings.macros.tsx index 94fded36..d5c0725b 100644 --- a/ui/src/routes/devices.$id.settings.macros.tsx +++ b/ui/src/routes/devices.$id.settings.macros.tsx @@ -11,23 +11,18 @@ import { LuCommand, } from "react-icons/lu"; -import { KeySequence, useMacrosStore, generateMacroId } from "@/hooks/stores"; -import { SettingsPageHeader } from "@/components/SettingsPageheader"; -import { Button } from "@/components/Button"; -import EmptyCard from "@/components/EmptyCard"; -import Card from "@/components/Card"; -import { MAX_TOTAL_MACROS, COPY_SUFFIX, DEFAULT_DELAY } from "@/constants/macros"; +import { KeySequence, useMacrosStore, generateMacroId } from "@hooks/stores"; +import useKeyboardLayout from "@hooks/useKeyboardLayout"; +import { SettingsPageHeader } from "@components/SettingsPageheader"; +import { Button } from "@components/Button"; +import Card from "@components/Card"; +import { ConfirmDialog } from "@components/ConfirmDialog"; +import EmptyCard from "@components/EmptyCard"; +import LoadingSpinner from "@components/LoadingSpinner"; import notifications from "@/notifications"; -import { ConfirmDialog } from "@/components/ConfirmDialog"; -import LoadingSpinner from "@/components/LoadingSpinner"; -import useKeyboardLayout from "@/hooks/useKeyboardLayout"; - -const normalizeSortOrders = (macros: KeySequence[]): KeySequence[] => { - return macros.map((macro, index) => ({ - ...macro, - sortOrder: index + 1, - })); -}; +import { normalizeSortOrders } from "@/utils"; +import { MAX_TOTAL_MACROS, COPY_SUFFIX, DEFAULT_DELAY } from "@/constants/macros"; +import { m } from "@localizations/messages.js"; export default function SettingsMacrosRoute() { const { macros, loading, initialized, loadMacros, saveMacros } = useMacrosStore(); @@ -35,7 +30,7 @@ export default function SettingsMacrosRoute() { const [actionLoadingId, setActionLoadingId] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [macroToDelete, setMacroToDelete] = useState(null); - const { selectedKeyboard } = useKeyboardLayout(); + const { selectedKeyboard } = useKeyboardLayout(); const isMaxMacrosReached = useMemo( () => macros.length >= MAX_TOTAL_MACROS, @@ -51,12 +46,12 @@ export default function SettingsMacrosRoute() { const handleDuplicateMacro = useCallback( async (macro: KeySequence) => { if (!macro?.id || !macro?.name) { - notifications.error("Invalid macro data"); + notifications.error(m.macros_invalid_data()); return; } if (isMaxMacrosReached) { - notifications.error(`Maximum of ${MAX_TOTAL_MACROS} macros allowed`); + notifications.error(m.macros_maximum_macros_reached({ maximum: MAX_TOTAL_MACROS })); return; } @@ -71,12 +66,12 @@ export default function SettingsMacrosRoute() { try { await saveMacros(normalizeSortOrders([...macros, newMacroCopy])); - notifications.success(`Macro "${newMacroCopy.name}" duplicated successfully`); + notifications.success(m.macros_duplicated_success({ name: newMacroCopy.name })); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to duplicate macro: ${error.message}`); + notifications.error(m.macros_failed_duplicate_error({ error: error.message || m.unknown_error() })); } else { - notifications.error("Failed to duplicate macro"); + notifications.error(m.macros_failed_duplicate()); } } finally { setActionLoadingId(null); @@ -88,7 +83,7 @@ export default function SettingsMacrosRoute() { const handleMoveMacro = useCallback( async (index: number, direction: "up" | "down", macroId: string) => { if (!Array.isArray(macros) || macros.length === 0) { - notifications.error("No macros available"); + notifications.error(m.macros_no_macros_available()); return; } @@ -103,12 +98,12 @@ export default function SettingsMacrosRoute() { const updatedMacros = normalizeSortOrders(newMacros); await saveMacros(updatedMacros); - notifications.success("Macro order updated successfully"); + notifications.success(m.macros_order_updated()); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to reorder macros: ${error.message}`); + notifications.error(m.macros_failed_reorder_error({ error: error.message || m.unknown_error() })); } else { - notifications.error("Failed to reorder macros"); + notifications.error(m.macros_failed_reorder()); } } finally { setActionLoadingId(null); @@ -126,14 +121,14 @@ export default function SettingsMacrosRoute() { macros.filter(m => m.id !== macroToDelete.id), ); await saveMacros(updatedMacros); - notifications.success(`Macro "${macroToDelete.name}" deleted successfully`); + notifications.success(m.macros_deleted_success({ name: macroToDelete.name })); setShowDeleteConfirm(false); setMacroToDelete(null); } catch (error: unknown) { if (error instanceof Error) { - notifications.error(`Failed to delete macro: ${error.message}`); + notifications.error(m.macros_failed_delete_error({ error: error.message || m.unknown_error() })); } else { - notifications.error("Failed to delete macro"); + notifications.error(m.macros_failed_delete()); } } finally { setActionLoadingId(null); @@ -153,7 +148,7 @@ export default function SettingsMacrosRoute() { onClick={() => handleMoveMacro(index, "up", macro.id)} disabled={index === 0 || actionLoadingId === macro.id} LeadingIcon={LuArrowUp} - aria-label={`Move ${macro.name} up`} + aria-label={m.macros_aria_move_up({ name: macro.name })} />
@@ -180,7 +175,7 @@ export default function SettingsMacrosRoute() { {(Array.isArray(step.modifiers) && step.modifiers.length > 0) || - (Array.isArray(step.keys) && step.keys.length > 0) ? ( + (Array.isArray(step.keys) && step.keys.length > 0) ? ( <> {Array.isArray(step.modifiers) && step.modifiers.map((modifier, idx) => ( @@ -189,10 +184,7 @@ export default function SettingsMacrosRoute() { {selectedKeyboard.modifierDisplayMap[modifier] || modifier} {idx < step.modifiers.length - 1 && ( - - {" "} - +{" "} - +  +  )} ))} @@ -201,10 +193,7 @@ export default function SettingsMacrosRoute() { step.modifiers.length > 0 && Array.isArray(step.keys) && step.keys.length > 0 && ( - - {" "} - +{" "} - +  +  )} {Array.isArray(step.keys) && @@ -214,17 +203,14 @@ export default function SettingsMacrosRoute() { {selectedKeyboard.keyDisplayMap[key] || key} {idx < step.keys.length - 1 && ( - - {" "} - +{" "} - +  +  )} ))} ) : ( - Delay only + {m.macros_delay_only()} )} {step.delay !== DEFAULT_DELAY && ( @@ -251,7 +237,7 @@ export default function SettingsMacrosRoute() { setShowDeleteConfirm(true); }} disabled={actionLoadingId === macro.id} - aria-label={`Delete macro ${macro.name}`} + aria-label={m.macros_aria_delete({ name: macro.name })} />