Compare commits

..

2 Commits

Author SHA1 Message Date
Marc Brooks ad188365b2
Merge e6fdd4a73b into 74e64f69a7 2025-10-16 21:26:45 -05:00
Marc Brooks e6fdd4a73b
Add inlang/paraglide-js localization
Remove the temporary directory after extracting buildkit
Localize the extension popovers.
Update package and fix tsconfig.json
Expand development directory guide
Move messages under localization
Popovers and sidebar
Update Chinese translations
Accidentally lost the changes that @ym provided, brought them back
File formatting pass
Localized all components, hooks, providers, hooks
Localize all pages except Settings
Bump packages
Settings Access page
Settings local auth page
Fix ref lint warning
Settings Advanced page
Fix UI lint warnings there were a bunch of ref and useEffect violations.
Settings appearance page
Settings general pages
Settings hardware page
Settings keyboard page
Settings macros pages
Settings mouse page
Settings page
Settings video page
Settings network page
Fix compilation issues
Ran machine translate
Use getLocale for date, relative time, and money formatting
Fix eslint
Delete unused messages
Added setting to choose locale
Merged in dev hotfix
Fix update status rendering
2025-10-16 21:26:31 -05:00
30 changed files with 933 additions and 541 deletions

View File

@ -15,5 +15,36 @@
"containerUser": "vscode", "containerUser": "vscode",
"containerEnv": { "containerEnv": {
"HOME": "/home/vscode" "HOME": "/home/vscode"
},
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached"
],
"onCreateCommand": ".devcontainer/install-deps.sh",
"customizations": {
"vscode": {
"extensions": [
// coding styles
"chrislajoie.vscode-modelines",
"editorconfig.editorconfig",
// GitHub
"GitHub.vscode-pull-request-github",
"github.vscode-github-actions",
// Golang
"golang.go",
// C / C++
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
// CMake / Makefile
"ms-vscode.makefile-tools",
"ms-vscode.cmake-tools",
// Frontend
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"bradlc.vscode-tailwindcss",
"codeandstuff.package-json-upgrade",
// Localization
"inlang.vs-code-extension"
]
}
} }
} }

2
.gitignore vendored
View File

@ -13,3 +13,5 @@ node_modules
# generated during the build process # generated during the build process
#internal/native/include #internal/native/include
#internal/native/lib #internal/native/lib
ui/reports

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
import json
from pathlib import Path
messages_dir = Path(__file__).resolve().parent.parent / 'ui' / 'localization' / 'messages'
files = list(messages_dir.glob('*.json'))
for f in files:
data = json.loads(f.read_text(encoding='utf-8'))
# Keep $schema first if present
schema = None
if '$schema' in data:
schema = data.pop('$schema')
sorted_items = dict(sorted(data.items()))
if schema is not None:
out = {'$schema': schema}
out.update(sorted_items)
else:
out = sorted_items
f.write_text(json.dumps(out, ensure_ascii=False, indent=4) + '\n', encoding='utf-8')
print(f'Processed {len(files)} files in {messages_dir}')

View File

@ -31,7 +31,7 @@
"access_no_device_id": "Intet enheds-ID tilgængeligt", "access_no_device_id": "Intet enheds-ID tilgængeligt",
"access_private_key_description": "Af sikkerhedsmæssige årsager vil den ikke blive vist efter lagring.", "access_private_key_description": "Af sikkerhedsmæssige årsager vil den ikke blive vist efter lagring.",
"access_private_key_label": "Privat nøgle", "access_private_key_label": "Privat nøgle",
"access_provider_custom": "Skik", "access_provider_custom": "Tilpasset",
"access_provider_jetkvm": "JetKVM Cloud", "access_provider_jetkvm": "JetKVM Cloud",
"access_remote_description": "Administrer tilstanden for fjernadgang til enheden", "access_remote_description": "Administrer tilstanden for fjernadgang til enheden",
"access_security_encryption": "End-to-end-kryptering ved hjælp af WebRTC (DTLS og SRTP)", "access_security_encryption": "End-to-end-kryptering ved hjælp af WebRTC (DTLS og SRTP)",
@ -42,15 +42,14 @@
"access_title": "Adgang", "access_title": "Adgang",
"access_tls_certificate_description": "Indsæt dit TLS-certifikat nedenfor. For certifikatkæder skal du inkludere hele kæden (blad-, mellem- og rodcertifikater).", "access_tls_certificate_description": "Indsæt dit TLS-certifikat nedenfor. For certifikatkæder skal du inkludere hele kæden (blad-, mellem- og rodcertifikater).",
"access_tls_certificate_title": "TLS-certifikat", "access_tls_certificate_title": "TLS-certifikat",
"access_tls_custom": "Skik", "access_tls_custom": "Tilpasset",
"access_tls_disabled": "Handicappet", "access_tls_disabled": "Deaktiveret",
"access_tls_self_signed": "Selvsigneret", "access_tls_self_signed": "Selvsigneret",
"access_tls_updated": "TLS-indstillingerne er blevet opdateret", "access_tls_updated": "TLS-indstillingerne er blevet opdateret",
"access_update_tls_settings": "Opdater TLS-indstillinger", "access_update_tls_settings": "Opdater TLS-indstillinger",
"action_bar_connection_stats": "Forbindelsesstatistik", "action_bar_connection_stats": "Forbindelsesstatistik",
"action_bar_extension": "Udvidelse", "action_bar_extension": "Udvidelse",
"action_bar_fullscreen": "Fuldskærm", "action_bar_fullscreen": "Fuldskærm",
"action_bar_paste_text": "Indsæt tekst",
"action_bar_settings": "Indstillinger", "action_bar_settings": "Indstillinger",
"action_bar_virtual_keyboard": "Virtuelt tastatur", "action_bar_virtual_keyboard": "Virtuelt tastatur",
"action_bar_virtual_media": "Virtuelle medier", "action_bar_virtual_media": "Virtuelle medier",
@ -116,7 +115,7 @@
"atx_power_control_get_state_error": "Kunne ikke hente ATX-strømtilstand: {error}", "atx_power_control_get_state_error": "Kunne ikke hente ATX-strømtilstand: {error}",
"atx_power_control_hdd_led": "HDD-LED", "atx_power_control_hdd_led": "HDD-LED",
"atx_power_control_long_power_button": "Langt tryk", "atx_power_control_long_power_button": "Langt tryk",
"atx_power_control_power_button": "Magt", "atx_power_control_power_button": "Strøm",
"atx_power_control_power_led": "Strøm-LED", "atx_power_control_power_led": "Strøm-LED",
"atx_power_control_reset_button": "Nulstil", "atx_power_control_reset_button": "Nulstil",
"atx_power_control_send_action_error": "Kunne ikke sende ATX-strømfunktion {action} : {error}", "atx_power_control_send_action_error": "Kunne ikke sende ATX-strømfunktion {action} : {error}",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Bekræft din adgangskode", "auth_mode_local_password_confirm_description": "Bekræft din adgangskode",
"auth_mode_local_password_confirm_label": "Bekræft adgangskode", "auth_mode_local_password_confirm_label": "Bekræft adgangskode",
"auth_mode_local_password_description": "Sikr din enhed med en adgangskode for ekstra beskyttelse.", "auth_mode_local_password_description": "Sikr din enhed med en adgangskode for ekstra beskyttelse.",
"auth_mode_local_password_do_not_match": "Adgangskoderne stemmer ikke overens",
"auth_mode_local_password_failed_set": "Kunne ikke angive adgangskode: {error}", "auth_mode_local_password_failed_set": "Kunne ikke angive adgangskode: {error}",
"auth_mode_local_password_note": "Denne adgangskode vil blive brugt til at sikre dine enhedsdata og beskytte mod uautoriseret adgang.", "auth_mode_local_password_note": "Denne adgangskode vil blive brugt til at sikre dine enhedsdata og beskytte mod uautoriseret adgang.",
"auth_mode_local_password_note_local": "Alle data forbliver på din lokale enhed.", "auth_mode_local_password_note_local": "Alle data forbliver på din lokale enhed.",
@ -156,8 +154,8 @@
"auth_signup_create_account_description": "Opret din konto, og begynd nemt at administrere dine enheder.", "auth_signup_create_account_description": "Opret din konto, og begynd nemt at administrere dine enheder.",
"back": "Tilbage", "back": "Tilbage",
"back_to_devices": "Tilbage til Enheder", "back_to_devices": "Tilbage til Enheder",
"cancel": "Ophæve", "cancel": "Annuller",
"close": "Tæt", "close": "Luk",
"cloud_kvms": "Cloud KVM'er", "cloud_kvms": "Cloud KVM'er",
"cloud_kvms_description": "Administrer dine cloud-KVM'er, og opret forbindelse til dem sikkert.", "cloud_kvms_description": "Administrer dine cloud-KVM'er, og opret forbindelse til dem sikkert.",
"cloud_kvms_no_devices": "Ingen enheder fundet", "cloud_kvms_no_devices": "Ingen enheder fundet",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Rundturstid", "connection_stats_round_trip_time": "Rundturstid",
"connection_stats_round_trip_time_description": "Rundrejsetid for det aktive ICE-kandidatpar mellem peers.", "connection_stats_round_trip_time_description": "Rundrejsetid for det aktive ICE-kandidatpar mellem peers.",
"connection_stats_sidebar": "Forbindelsesstatistik", "connection_stats_sidebar": "Forbindelsesstatistik",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " pakker",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "Videostreamen fra JetKVM'en til klienten.", "connection_stats_video_description": "Videostreamen fra JetKVM'en til klienten.",
"continue": "Fortsætte", "continue": "Fortsætte",
@ -188,7 +189,7 @@
"dc_power_control_current": "Strøm", "dc_power_control_current": "Strøm",
"dc_power_control_current_unit": "EN", "dc_power_control_current_unit": "EN",
"dc_power_control_get_state_error": "Kunne ikke hente DC-strømtilstand: {error}", "dc_power_control_get_state_error": "Kunne ikke hente DC-strømtilstand: {error}",
"dc_power_control_power": "Magt", "dc_power_control_power": "Strøm",
"dc_power_control_power_off_button": "Sluk", "dc_power_control_power_off_button": "Sluk",
"dc_power_control_power_off_state": "Sluk", "dc_power_control_power_off_state": "Sluk",
"dc_power_control_power_on_button": "Tænd", "dc_power_control_power_on_button": "Tænd",
@ -207,6 +208,8 @@
"deregister_error": "Der opstod en fejl {status} under afregistreringen af din enhed. Prøv igen.", "deregister_error": "Der opstod en fejl {status} under afregistreringen af din enhed. Prøv igen.",
"deregister_headline": "Afregistrér {device} fra din cloud-konto", "deregister_headline": "Afregistrér {device} fra din cloud-konto",
"detach": "Løsrive", "detach": "Løsrive",
"dhcp_empty_lease_description": "Vi har endnu ikke modtaget nogen DHCP-leaseoplysninger fra enheden.",
"dhcp_empty_lease_headline": "Ingen DHCP-leaseoplysninger",
"dhcp_lease_boot_file": "Boot-fil", "dhcp_lease_boot_file": "Boot-fil",
"dhcp_lease_boot_next_server": "Start næste server", "dhcp_lease_boot_next_server": "Start næste server",
"dhcp_lease_boot_server_name": "Navn på bootserver", "dhcp_lease_boot_server_name": "Navn på bootserver",
@ -371,7 +374,7 @@
"local_auth_create_new_password_label": "Ny adgangskode", "local_auth_create_new_password_label": "Ny adgangskode",
"local_auth_create_new_password_placeholder": "Indtast en stærk adgangskode", "local_auth_create_new_password_placeholder": "Indtast en stærk adgangskode",
"local_auth_create_not_now_button": "Ikke nu", "local_auth_create_not_now_button": "Ikke nu",
"local_auth_create_secure_button": "Sikker enhed", "local_auth_create_secure_button": "Sikr enheden",
"local_auth_create_title": "Beskyttelse af lokal enhed", "local_auth_create_title": "Beskyttelse af lokal enhed",
"local_auth_current_password_label": "Nuværende adgangskode", "local_auth_current_password_label": "Nuværende adgangskode",
"local_auth_disable_local_device_protection_description": "Indtast din nuværende adgangskode for at deaktivere lokal enhedsbeskyttelse.", "local_auth_disable_local_device_protection_description": "Indtast din nuværende adgangskode for at deaktivere lokal enhedsbeskyttelse.",
@ -396,7 +399,7 @@
"local_auth_success_password_updated_description": "Du har ændret din adgangskode til beskyttelse af din lokale enhed. Husk din nye adgangskode til senere brug.", "local_auth_success_password_updated_description": "Du har ændret din adgangskode til beskyttelse af din lokale enhed. Husk din nye adgangskode til senere brug.",
"local_auth_success_password_updated_title": "Adgangskode opdateret", "local_auth_success_password_updated_title": "Adgangskode opdateret",
"local_auth_update_password_button": "Opdater adgangskode", "local_auth_update_password_button": "Opdater adgangskode",
"locale_auto": "Bil", "locale_auto": "Auto",
"locale_change_success": "Sproget er ændret til {locale}", "locale_change_success": "Sproget er ændret til {locale}",
"locale_da": "Dansk", "locale_da": "Dansk",
"locale_de": "Tysk", "locale_de": "Tysk",
@ -426,7 +429,8 @@
"macro_name_too_long": "Navnet skal være mindre end 50 tegn", "macro_name_too_long": "Navnet skal være mindre end 50 tegn",
"macro_please_fix_validation_errors": "Ret venligst valideringsfejlene", "macro_please_fix_validation_errors": "Ret venligst valideringsfejlene",
"macro_save": "Gem makro", "macro_save": "Gem makro",
"macro_save_error": "Der opstod en fejl under lagring.", "macro_save_failed": "Der opstod en fejl under lagring.",
"macro_save_failed_error": "Der opstod en fejl under lagring: {error}.",
"macro_step_count": "{steps} / {max} trin", "macro_step_count": "{steps} / {max} trin",
"macro_step_duration_description": "Tid til at vente, før man udfører det næste trin.", "macro_step_duration_description": "Tid til at vente, før man udfører det næste trin.",
"macro_step_duration_label": "Trinvarighed", "macro_step_duration_label": "Trinvarighed",
@ -549,9 +553,9 @@
"mouse_hide_cursor_description": "Skjul markøren, når du sender musebevægelser", "mouse_hide_cursor_description": "Skjul markøren, når du sender musebevægelser",
"mouse_hide_cursor_title": "Skjul markør", "mouse_hide_cursor_title": "Skjul markør",
"mouse_jiggler_config_updated": "Jiggler-konfigurationen er blevet opdateret", "mouse_jiggler_config_updated": "Jiggler-konfigurationen er blevet opdateret",
"mouse_jiggler_custom": "Skik", "mouse_jiggler_custom": "Tilpasset",
"mouse_jiggler_description": "Simuler bevægelsen af en computermus", "mouse_jiggler_description": "Simuler bevægelsen af en computermus",
"mouse_jiggler_disabled": "Handicappet", "mouse_jiggler_disabled": "Deaktiveret",
"mouse_jiggler_error_config": "Der opstod en fejl under indstilling af jiggler-konfigurationen", "mouse_jiggler_error_config": "Der opstod en fejl under indstilling af jiggler-konfigurationen",
"mouse_jiggler_failed_state": "Kunne ikke indstille jiggler-tilstand: {error}", "mouse_jiggler_failed_state": "Kunne ikke indstille jiggler-tilstand: {error}",
"mouse_jiggler_frequent": "Hyppig - 30'erne", "mouse_jiggler_frequent": "Hyppig - 30'erne",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Konfigurer hvilken DHCP-klient der skal bruges", "network_dhcp_client_description": "Konfigurer hvilken DHCP-klient der skal bruges",
"network_dhcp_client_jetkvm": "JetKVM Intern", "network_dhcp_client_jetkvm": "JetKVM Intern",
"network_dhcp_client_title": "DHCP-klient", "network_dhcp_client_title": "DHCP-klient",
"network_dhcp_information": "DHCP-oplysninger",
"network_dhcp_lease_renew": "Forny DHCP-lease", "network_dhcp_lease_renew": "Forny DHCP-lease",
"network_dhcp_lease_renew_confirm": "Forny lejekontrakt", "network_dhcp_lease_renew_confirm": "Forny lejekontrakt",
"network_dhcp_lease_renew_confirm_description": "Dette vil anmode om en ny IP-adresse fra din DHCP-server. Din enhed kan midlertidigt miste netværksforbindelsen under denne proces.", "network_dhcp_lease_renew_confirm_description": "Dette vil anmode om en ny IP-adresse fra din DHCP-server. Din enhed kan midlertidigt miste netværksforbindelsen under denne proces.",
@ -586,7 +589,7 @@
"network_dhcp_lease_renew_confirm_new_b": "du skal muligvis genoprette forbindelsen ved hjælp af den nye adresse", "network_dhcp_lease_renew_confirm_new_b": "du skal muligvis genoprette forbindelsen ved hjælp af den nye adresse",
"network_dhcp_lease_renew_failed": "Kunne ikke forny leasing: {error}", "network_dhcp_lease_renew_failed": "Kunne ikke forny leasing: {error}",
"network_dhcp_lease_renew_success": "DHCP-lease fornyet", "network_dhcp_lease_renew_success": "DHCP-lease fornyet",
"network_domain_custom": "Skik", "network_domain_custom": "Tilpasset",
"network_domain_description": "Netværksdomænesuffiks for enheden", "network_domain_description": "Netværksdomænesuffiks for enheden",
"network_domain_dhcp_provided": "DHCP leveret", "network_domain_dhcp_provided": "DHCP leveret",
"network_domain_local": ".lokal", "network_domain_local": ".lokal",
@ -599,39 +602,46 @@
"network_ipv4_address": "IPv4-adresse", "network_ipv4_address": "IPv4-adresse",
"network_ipv4_dns": "IPv4 DNS", "network_ipv4_dns": "IPv4 DNS",
"network_ipv4_gateway": "IPv4-gateway", "network_ipv4_gateway": "IPv4-gateway",
"network_ipv4_invalid": "Ugyldig IPv4-adresse",
"network_ipv4_invalid_cidr": "Ugyldig CIDR-notation for IPv4-adresse",
"network_ipv4_mode_description": "Konfigurer IPv4-tilstanden", "network_ipv4_mode_description": "Konfigurer IPv4-tilstanden",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statisk", "network_ipv4_mode_static": "Statisk",
"network_ipv4_mode_title": "IPv4-tilstand", "network_ipv4_mode_title": "IPv4-tilstand",
"network_ipv4_netmask": "IPv4-netmaske", "network_ipv4_netmask": "IPv4-netmaske",
"network_ipv6_address": "IPv6-adresse", "network_ipv6_addresses_header": "IPv6-adresser",
"network_ipv6_cidr_suggestion": "Brug venligst CIDR-notation (f.eks. 2001:db8::1/64)",
"network_ipv6_dns": "IPv6 DNS",
"network_ipv6_flag_dad_failed": "DAD mislykkedes",
"network_ipv6_flag_deprecated": "Udfaset",
"network_ipv6_gateway": "IPv6-gateway",
"network_ipv6_information": "IPv6-oplysninger", "network_ipv6_information": "IPv6-oplysninger",
"network_ipv6_invalid": "Ugyldig IPv6-adresse",
"network_ipv6_mode_description": "Konfigurer IPv6-tilstanden", "network_ipv6_mode_description": "Konfigurer IPv6-tilstanden",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Handicappet", "network_ipv6_mode_disabled": "Deaktiveret",
"network_ipv6_mode_link_local": "Kun link-lokal", "network_ipv6_mode_link_local": "Kun link-lokal",
"network_ipv6_mode_slaac": "SLAAC", "network_ipv6_mode_slaac": "SLAAC",
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statisk", "network_ipv6_mode_static": "Statisk",
"network_ipv6_mode_title": "IPv6-tilstand", "network_ipv6_mode_title": "IPv6-tilstand",
"network_ipv6_netmask": "IPv6-netmaske", "network_ipv6_prefix": "IP-præfiks",
"network_ipv6_no_addresses": "Ingen IPv6-adresser konfigureret", "network_ipv6_prefix_invalid": "Præfikset skal være mellem 0 og 128",
"network_ll_dp_all": "Alle", "network_ll_dp_all": "Alle",
"network_ll_dp_basic": "Grundlæggende", "network_ll_dp_basic": "Grundlæggende",
"network_ll_dp_description": "Styr hvilke TLV'er der sendes via Link Layer Discovery Protocol", "network_ll_dp_description": "Styr hvilke TLV'er der sendes via Link Layer Discovery Protocol",
"network_ll_dp_disabled": "Handicappet", "network_ll_dp_disabled": "Deaktiveret",
"network_ll_dp_title": "LLDP", "network_ll_dp_title": "LLDP",
"network_mac_address_copy_error": "Kunne ikke kopiere MAC-adressen", "network_mac_address_copy_error": "Kunne ikke kopiere MAC-adressen",
"network_mac_address_copy_success": "MAC-adresse { mac } kopieret til udklipsholder", "network_mac_address_copy_success": "MAC-adresse { mac } kopieret til udklipsholder",
"network_mac_address_description": "Hardware-identifikator for netværksgrænsefladen", "network_mac_address_description": "Hardware-identifikator for netværksgrænsefladen",
"network_mac_address_title": "MAC-adresse", "network_mac_address_title": "MAC-adresse",
"network_mdns_auto": "Bil", "network_mdns_auto": "Auto",
"network_mdns_description": "Styr mDNS (multicast DNS) driftstilstand", "network_mdns_description": "Styr mDNS (multicast DNS) driftstilstand",
"network_mdns_disabled": "Handicappet", "network_mdns_disabled": "Deaktiveret",
"network_mdns_ipv4_only": "Kun IPv4", "network_mdns_ipv4_only": "Kun IPv4",
"network_mdns_ipv6_only": "Kun IPv6", "network_mdns_ipv6_only": "Kun IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Ingen DHCP-leaseoplysninger tilgængelige",
"network_no_information_description": "Ingen netværkskonfiguration tilgængelig", "network_no_information_description": "Ingen netværkskonfiguration tilgængelig",
"network_no_information_headline": "Netværksoplysninger", "network_no_information_headline": "Netværksoplysninger",
"network_pending_dhcp_mode_change_description": "Gem indstillinger for at aktivere DHCP-tilstand og se leasingoplysninger", "network_pending_dhcp_mode_change_description": "Gem indstillinger for at aktivere DHCP-tilstand og se leasingoplysninger",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Konfigurationsændringer", "network_save_settings_confirm_heading": "Konfigurationsændringer",
"network_save_settings_failed": "Kunne ikke gemme netværksindstillinger: {error}", "network_save_settings_failed": "Kunne ikke gemme netværksindstillinger: {error}",
"network_save_settings_success": "Netværksindstillinger gemt", "network_save_settings_success": "Netværksindstillinger gemt",
"network_settings_invalid_ipv4_cidr": "Ugyldig CIDR-notation for IPv4-adresse", "network_settings_add_dns": "Tilføj DNS-server",
"network_settings_load_error": "Kunne ikke indlæse netværksindstillinger: {error}", "network_settings_load_error": "Kunne ikke indlæse netværksindstillinger: {error}",
"network_static_ipv4_header": "Statisk IPv4-konfiguration",
"network_static_ipv6_header": "Statisk IPv6-konfiguration",
"network_time_sync_description": "Konfigurer indstillinger for tidssynkronisering", "network_time_sync_description": "Konfigurer indstillinger for tidssynkronisering",
"network_time_sync_http_only": "Kun HTTP", "network_time_sync_http_only": "Kun HTTP",
"network_time_sync_ntp_and_http": "NTP og HTTP", "network_time_sync_ntp_and_http": "NTP og HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Kunne ikke indsætte tekst: {error}", "paste_modal_failed_paste": "Kunne ikke indsætte tekst: {error}",
"paste_modal_invalid_chars_intro": "Følgende tegn vil ikke blive indsat:", "paste_modal_invalid_chars_intro": "Følgende tegn vil ikke blive indsat:",
"paste_modal_paste_from_host": "Indsæt fra vært", "paste_modal_paste_from_host": "Indsæt fra vært",
"paste_modal_paste_text": "Indsæt tekst",
"paste_modal_paste_text_description": "Indsæt tekst fra din klient til den eksterne vært",
"paste_modal_sending_using_layout": "Sender tekst ved hjælp af tastaturlayout: {iso} - {name}", "paste_modal_sending_using_layout": "Sender tekst ved hjælp af tastaturlayout: {iso} - {name}",
"paste_text": "Indsæt tekst",
"paste_text_description": "Indsæt tekst fra din klient til den eksterne vært",
"peer_connection_closed": "Lukket", "peer_connection_closed": "Lukket",
"peer_connection_closing": "Lukker", "peer_connection_closing": "Lukker",
"peer_connection_connected": "Forbundet", "peer_connection_connected": "Forbundet",
@ -743,7 +755,7 @@
"updates_failed_get_device_version": "Kunne ikke hente enhedsversion: {error}", "updates_failed_get_device_version": "Kunne ikke hente enhedsversion: {error}",
"updating_leave_device_on": "Sluk venligst ikke din enhed…", "updating_leave_device_on": "Sluk venligst ikke din enhed…",
"usb": "USB", "usb": "USB",
"usb_config_custom": "Skik", "usb_config_custom": "Tilpasset",
"usb_config_default": "JetKVM-standard", "usb_config_default": "JetKVM-standard",
"usb_config_dell": "Dell Multimedia Pro-tastatur", "usb_config_dell": "Dell Multimedia Pro-tastatur",
"usb_config_failed_load": "Kunne ikke indlæse USB-konfiguration: {error}", "usb_config_failed_load": "Kunne ikke indlæse USB-konfiguration: {error}",
@ -767,7 +779,7 @@
"usb_config_vendor_id_placeholder": "Indtast leverandør-ID", "usb_config_vendor_id_placeholder": "Indtast leverandør-ID",
"usb_device_classes_description": "USB-enhedsklasser i den sammensatte enhed", "usb_device_classes_description": "USB-enhedsklasser i den sammensatte enhed",
"usb_device_classes_title": "Klasser", "usb_device_classes_title": "Klasser",
"usb_device_custom": "Skik", "usb_device_custom": "Tilpasset",
"usb_device_description": "USB-enheder, der skal emuleres på målcomputeren", "usb_device_description": "USB-enheder, der skal emuleres på målcomputeren",
"usb_device_enable_absolute_mouse_description": "Aktivér absolut mus (markør)", "usb_device_enable_absolute_mouse_description": "Aktivér absolut mus (markør)",
"usb_device_enable_absolute_mouse_title": "Aktivér absolut mus (markør)", "usb_device_enable_absolute_mouse_title": "Aktivér absolut mus (markør)",
@ -802,7 +814,7 @@
"video_description": "Konfigurer skærmindstillinger og EDID for optimal kompatibilitet", "video_description": "Konfigurer skærmindstillinger og EDID for optimal kompatibilitet",
"video_edid_acer_b246wl": "Acer B246WL, 1920x1200", "video_edid_acer_b246wl": "Acer B246WL, 1920x1200",
"video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200", "video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200",
"video_edid_custom": "Skik", "video_edid_custom": "Tilpasset",
"video_edid_dell_d2721h": "DELL D2721H, 1920x1080", "video_edid_dell_d2721h": "DELL D2721H, 1920x1080",
"video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024", "video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024",
"video_edid_description": "Juster EDID-indstillingerne for skærmen", "video_edid_description": "Juster EDID-indstillingerne for skærmen",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "Verbindungsstatistiken", "action_bar_connection_stats": "Verbindungsstatistiken",
"action_bar_extension": "Verlängerung", "action_bar_extension": "Verlängerung",
"action_bar_fullscreen": "Vollbild", "action_bar_fullscreen": "Vollbild",
"action_bar_paste_text": "Text einfügen",
"action_bar_settings": "Einstellungen", "action_bar_settings": "Einstellungen",
"action_bar_virtual_keyboard": "Virtuelle Tastatur", "action_bar_virtual_keyboard": "Virtuelle Tastatur",
"action_bar_virtual_media": "Virtuelle Medien", "action_bar_virtual_media": "Virtuelle Medien",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Bestätigen Sie Ihr Passwort", "auth_mode_local_password_confirm_description": "Bestätigen Sie Ihr Passwort",
"auth_mode_local_password_confirm_label": "Passwort bestätigen", "auth_mode_local_password_confirm_label": "Passwort bestätigen",
"auth_mode_local_password_description": "Sichern Sie Ihr Gerät für zusätzlichen Schutz mit einem Passwort.", "auth_mode_local_password_description": "Sichern Sie Ihr Gerät für zusätzlichen Schutz mit einem Passwort.",
"auth_mode_local_password_do_not_match": "Passwörter stimmen nicht überein",
"auth_mode_local_password_failed_set": "Kennwort konnte nicht festgelegt werden: {error}", "auth_mode_local_password_failed_set": "Kennwort konnte nicht festgelegt werden: {error}",
"auth_mode_local_password_note": "Dieses Passwort wird verwendet, um Ihre Gerätedaten zu sichern und vor unbefugtem Zugriff zu schützen.", "auth_mode_local_password_note": "Dieses Passwort wird verwendet, um Ihre Gerätedaten zu sichern und vor unbefugtem Zugriff zu schützen.",
"auth_mode_local_password_note_local": "Alle Daten verbleiben auf Ihrem lokalen Gerät.", "auth_mode_local_password_note_local": "Alle Daten verbleiben auf Ihrem lokalen Gerät.",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Round-Trip-Zeit", "connection_stats_round_trip_time": "Round-Trip-Zeit",
"connection_stats_round_trip_time_description": "Roundtrip-Zeit für das aktive ICE-Kandidatenpaar zwischen Peers.", "connection_stats_round_trip_time_description": "Roundtrip-Zeit für das aktive ICE-Kandidatenpaar zwischen Peers.",
"connection_stats_sidebar": "Verbindungsstatistiken", "connection_stats_sidebar": "Verbindungsstatistiken",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " Pakete",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "Der Videostream vom JetKVM zum Client.", "connection_stats_video_description": "Der Videostream vom JetKVM zum Client.",
"continue": "Weitermachen", "continue": "Weitermachen",
@ -207,6 +208,8 @@
"deregister_error": "Beim Abmelden Ihres Geräts ist ein Fehler aufgetreten {status} . Bitte versuchen Sie es erneut.", "deregister_error": "Beim Abmelden Ihres Geräts ist ein Fehler aufgetreten {status} . Bitte versuchen Sie es erneut.",
"deregister_headline": "Melden Sie {device}", "deregister_headline": "Melden Sie {device}",
"detach": "Abtrennen", "detach": "Abtrennen",
"dhcp_empty_lease_description": "Wir haben noch keine DHCP-Lease-Informationen vom Gerät erhalten.",
"dhcp_empty_lease_headline": "Keine DHCP-Lease-Informationen",
"dhcp_lease_boot_file": "Boot-Datei", "dhcp_lease_boot_file": "Boot-Datei",
"dhcp_lease_boot_next_server": "Nächsten Server starten", "dhcp_lease_boot_next_server": "Nächsten Server starten",
"dhcp_lease_boot_server_name": "Name des Boot-Servers", "dhcp_lease_boot_server_name": "Name des Boot-Servers",
@ -426,7 +429,8 @@
"macro_name_too_long": "Der Name muss weniger als 50 Zeichen lang sein", "macro_name_too_long": "Der Name muss weniger als 50 Zeichen lang sein",
"macro_please_fix_validation_errors": "Bitte beheben Sie die Validierungsfehler", "macro_please_fix_validation_errors": "Bitte beheben Sie die Validierungsfehler",
"macro_save": "Makro speichern", "macro_save": "Makro speichern",
"macro_save_error": "Beim Speichern ist ein Fehler aufgetreten.", "macro_save_failed": "Beim Speichern ist ein Fehler aufgetreten.",
"macro_save_failed_error": "Beim Speichern ist ein Fehler aufgetreten: {error}.",
"macro_step_count": "{steps} / {max} Schritte", "macro_step_count": "{steps} / {max} Schritte",
"macro_step_duration_description": "Wartezeit vor der Ausführung des nächsten Schritts.", "macro_step_duration_description": "Wartezeit vor der Ausführung des nächsten Schritts.",
"macro_step_duration_label": "Schrittdauer", "macro_step_duration_label": "Schrittdauer",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Konfigurieren Sie, welcher DHCP-Client verwendet werden soll", "network_dhcp_client_description": "Konfigurieren Sie, welcher DHCP-Client verwendet werden soll",
"network_dhcp_client_jetkvm": "JetKVM intern", "network_dhcp_client_jetkvm": "JetKVM intern",
"network_dhcp_client_title": "DHCP-Client", "network_dhcp_client_title": "DHCP-Client",
"network_dhcp_information": "DHCP-Informationen",
"network_dhcp_lease_renew": "DHCP-Lease erneuern", "network_dhcp_lease_renew": "DHCP-Lease erneuern",
"network_dhcp_lease_renew_confirm": "Mietvertrag verlängern", "network_dhcp_lease_renew_confirm": "Mietvertrag verlängern",
"network_dhcp_lease_renew_confirm_description": "Dadurch wird eine neue IP-Adresse von Ihrem DHCP-Server angefordert. Während dieses Vorgangs kann die Netzwerkverbindung Ihres Geräts vorübergehend unterbrochen werden.", "network_dhcp_lease_renew_confirm_description": "Dadurch wird eine neue IP-Adresse von Ihrem DHCP-Server angefordert. Während dieses Vorgangs kann die Netzwerkverbindung Ihres Geräts vorübergehend unterbrochen werden.",
@ -599,13 +602,21 @@
"network_ipv4_address": "IPv4-Adresse", "network_ipv4_address": "IPv4-Adresse",
"network_ipv4_dns": "IPv4 DNS", "network_ipv4_dns": "IPv4 DNS",
"network_ipv4_gateway": "IPv4-Gateway", "network_ipv4_gateway": "IPv4-Gateway",
"network_ipv4_invalid": "Ungültige IPv4-Adresse",
"network_ipv4_invalid_cidr": "Ungültige CIDR-Notation für IPv4-Adresse",
"network_ipv4_mode_description": "Konfigurieren des IPv4-Modus", "network_ipv4_mode_description": "Konfigurieren des IPv4-Modus",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statisch", "network_ipv4_mode_static": "Statisch",
"network_ipv4_mode_title": "IPv4-Modus", "network_ipv4_mode_title": "IPv4-Modus",
"network_ipv4_netmask": "IPv4-Netzmaske", "network_ipv4_netmask": "IPv4-Netzmaske",
"network_ipv6_address": "IPv6-Adresse", "network_ipv6_addresses_header": "IPv6-Adressen",
"network_ipv6_cidr_suggestion": "Bitte verwenden Sie die CIDR-Notation (z. B. 2001:db8::1/64).",
"network_ipv6_dns": "IPv6 DNS",
"network_ipv6_flag_dad_failed": "DAD ist fehlgeschlagen",
"network_ipv6_flag_deprecated": "Veraltet",
"network_ipv6_gateway": "IPv6-Gateway",
"network_ipv6_information": "IPv6-Informationen", "network_ipv6_information": "IPv6-Informationen",
"network_ipv6_invalid": "Ungültige IPv6-Adresse",
"network_ipv6_mode_description": "Konfigurieren des IPv6-Modus", "network_ipv6_mode_description": "Konfigurieren des IPv6-Modus",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Deaktiviert", "network_ipv6_mode_disabled": "Deaktiviert",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statisch", "network_ipv6_mode_static": "Statisch",
"network_ipv6_mode_title": "IPv6-Modus", "network_ipv6_mode_title": "IPv6-Modus",
"network_ipv6_netmask": "IPv6-Netzmaske", "network_ipv6_prefix": "IP-Präfix",
"network_ipv6_no_addresses": "Keine IPv6-Adressen konfiguriert", "network_ipv6_prefix_invalid": "Das Präfix muss zwischen 0 und 128 liegen",
"network_ll_dp_all": "Alle", "network_ll_dp_all": "Alle",
"network_ll_dp_basic": "Basic", "network_ll_dp_basic": "Basic",
"network_ll_dp_description": "Steuern Sie, welche TLVs über das Link Layer Discovery Protocol gesendet werden", "network_ll_dp_description": "Steuern Sie, welche TLVs über das Link Layer Discovery Protocol gesendet werden",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "Nur IPv4", "network_mdns_ipv4_only": "Nur IPv4",
"network_mdns_ipv6_only": "Nur IPv6", "network_mdns_ipv6_only": "Nur IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Keine DHCP-Lease-Informationen verfügbar",
"network_no_information_description": "Keine Netzwerkkonfiguration verfügbar", "network_no_information_description": "Keine Netzwerkkonfiguration verfügbar",
"network_no_information_headline": "Netzwerkinformationen", "network_no_information_headline": "Netzwerkinformationen",
"network_pending_dhcp_mode_change_description": "Speichern Sie die Einstellungen, um den DHCP-Modus zu aktivieren und Leasinginformationen anzuzeigen", "network_pending_dhcp_mode_change_description": "Speichern Sie die Einstellungen, um den DHCP-Modus zu aktivieren und Leasinginformationen anzuzeigen",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Konfigurationsänderungen", "network_save_settings_confirm_heading": "Konfigurationsänderungen",
"network_save_settings_failed": "Netzwerkeinstellungen konnten nicht gespeichert werden: {error}", "network_save_settings_failed": "Netzwerkeinstellungen konnten nicht gespeichert werden: {error}",
"network_save_settings_success": "Netzwerkeinstellungen gespeichert", "network_save_settings_success": "Netzwerkeinstellungen gespeichert",
"network_settings_invalid_ipv4_cidr": "Ungültige CIDR-Notation für IPv4-Adresse", "network_settings_add_dns": "DNS-Server hinzufügen",
"network_settings_load_error": "Netzwerkeinstellungen konnten nicht geladen werden: {error}", "network_settings_load_error": "Netzwerkeinstellungen konnten nicht geladen werden: {error}",
"network_static_ipv4_header": "Statische IPv4-Konfiguration",
"network_static_ipv6_header": "Statische IPv6-Konfiguration",
"network_time_sync_description": "Konfigurieren der Zeitsynchronisierungseinstellungen", "network_time_sync_description": "Konfigurieren der Zeitsynchronisierungseinstellungen",
"network_time_sync_http_only": "Nur HTTP", "network_time_sync_http_only": "Nur HTTP",
"network_time_sync_ntp_and_http": "NTP und HTTP", "network_time_sync_ntp_and_http": "NTP und HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Fehler beim Einfügen des Textes: {error}", "paste_modal_failed_paste": "Fehler beim Einfügen des Textes: {error}",
"paste_modal_invalid_chars_intro": "Die folgenden Zeichen werden nicht eingefügt:", "paste_modal_invalid_chars_intro": "Die folgenden Zeichen werden nicht eingefügt:",
"paste_modal_paste_from_host": "Vom Host einfügen", "paste_modal_paste_from_host": "Vom Host einfügen",
"paste_modal_paste_text": "Text einfügen",
"paste_modal_paste_text_description": "Fügen Sie Text von Ihrem Client in den Remote-Host ein",
"paste_modal_sending_using_layout": "Senden von Text mithilfe des Tastaturlayouts: {iso} - {name}", "paste_modal_sending_using_layout": "Senden von Text mithilfe des Tastaturlayouts: {iso} - {name}",
"paste_text": "Text einfügen",
"paste_text_description": "Fügen Sie Text von Ihrem Client in den Remote-Host ein",
"peer_connection_closed": "Geschlossen", "peer_connection_closed": "Geschlossen",
"peer_connection_closing": "Schließen", "peer_connection_closing": "Schließen",
"peer_connection_connected": "Verbunden", "peer_connection_connected": "Verbunden",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "Connection Stats", "action_bar_connection_stats": "Connection Stats",
"action_bar_extension": "Extension", "action_bar_extension": "Extension",
"action_bar_fullscreen": "Fullscreen", "action_bar_fullscreen": "Fullscreen",
"action_bar_paste_text": "Paste text",
"action_bar_settings": "Settings", "action_bar_settings": "Settings",
"action_bar_virtual_keyboard": "Virtual Keyboard", "action_bar_virtual_keyboard": "Virtual Keyboard",
"action_bar_virtual_media": "Virtual Media", "action_bar_virtual_media": "Virtual Media",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Confirm your password", "auth_mode_local_password_confirm_description": "Confirm your password",
"auth_mode_local_password_confirm_label": "Confirm Password", "auth_mode_local_password_confirm_label": "Confirm Password",
"auth_mode_local_password_description": "Secure your device with a password for added protection.", "auth_mode_local_password_description": "Secure your device with a password for added protection.",
"auth_mode_local_password_do_not_match": "Passwords do not match",
"auth_mode_local_password_failed_set": "Failed to set password: {error}", "auth_mode_local_password_failed_set": "Failed to set password: {error}",
"auth_mode_local_password_note": "This password will be used to secure your device data and protect against unauthorized access.", "auth_mode_local_password_note": "This password will be used to secure your device data and protect against unauthorized access.",
"auth_mode_local_password_note_local": "All data remains on your local device.", "auth_mode_local_password_note_local": "All data remains on your local device.",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Round-Trip Time", "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_round_trip_time_description": "Round-trip time for the active ICE candidate pair between peers.",
"connection_stats_sidebar": "Connection Stats", "connection_stats_sidebar": "Connection Stats",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " packets",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "The video stream from the JetKVM to the client.", "connection_stats_video_description": "The video stream from the JetKVM to the client.",
"continue": "Continue", "continue": "Continue",
@ -207,6 +208,8 @@
"deregister_error": "There was an error {status} deregistering your device. Please try again.", "deregister_error": "There was an error {status} deregistering your device. Please try again.",
"deregister_headline": "Deregister {device} from your cloud account", "deregister_headline": "Deregister {device} from your cloud account",
"detach": "Detach", "detach": "Detach",
"dhcp_empty_lease_description": "We haven't received any DHCP lease information from the device yet.",
"dhcp_empty_lease_headline": "No DHCP Lease information",
"dhcp_lease_boot_file": "Boot File", "dhcp_lease_boot_file": "Boot File",
"dhcp_lease_boot_next_server": "Boot Next Server", "dhcp_lease_boot_next_server": "Boot Next Server",
"dhcp_lease_boot_server_name": "Boot Server Name", "dhcp_lease_boot_server_name": "Boot Server Name",
@ -426,7 +429,8 @@
"macro_name_too_long": "Name must be less than 50 characters", "macro_name_too_long": "Name must be less than 50 characters",
"macro_please_fix_validation_errors": "Please fix the validation errors", "macro_please_fix_validation_errors": "Please fix the validation errors",
"macro_save": "Save Macro", "macro_save": "Save Macro",
"macro_save_error": "An error occurred while saving.", "macro_save_failed": "An error occurred while saving.",
"macro_save_failed_error": "An error occurred while saving: {error}.",
"macro_step_count": "{steps} / {max} steps", "macro_step_count": "{steps} / {max} steps",
"macro_step_duration_description": "Time to wait before executing the next step.", "macro_step_duration_description": "Time to wait before executing the next step.",
"macro_step_duration_label": "Step Duration", "macro_step_duration_label": "Step Duration",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Configure which DHCP client to use", "network_dhcp_client_description": "Configure which DHCP client to use",
"network_dhcp_client_jetkvm": "JetKVM Internal", "network_dhcp_client_jetkvm": "JetKVM Internal",
"network_dhcp_client_title": "DHCP Client", "network_dhcp_client_title": "DHCP Client",
"network_dhcp_information": "DHCP Information",
"network_dhcp_lease_renew": "Renew DHCP Lease", "network_dhcp_lease_renew": "Renew DHCP Lease",
"network_dhcp_lease_renew_confirm": "Renew Lease", "network_dhcp_lease_renew_confirm": "Renew Lease",
"network_dhcp_lease_renew_confirm_description": "This will request a new IP address from your DHCP server. Your device may temporarily lose network connectivity during this process.", "network_dhcp_lease_renew_confirm_description": "This will request a new IP address from your DHCP server. Your device may temporarily lose network connectivity during this process.",
@ -599,13 +602,21 @@
"network_ipv4_address": "IPv4 Address", "network_ipv4_address": "IPv4 Address",
"network_ipv4_dns": "IPv4 DNS", "network_ipv4_dns": "IPv4 DNS",
"network_ipv4_gateway": "IPv4 Gateway", "network_ipv4_gateway": "IPv4 Gateway",
"network_ipv4_invalid": "Invalid IPv4 address",
"network_ipv4_invalid_cidr": "Invalid CIDR notation for IPv4 address",
"network_ipv4_mode_description": "Configure the IPv4 mode", "network_ipv4_mode_description": "Configure the IPv4 mode",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Static", "network_ipv4_mode_static": "Static",
"network_ipv4_mode_title": "IPv4 Mode", "network_ipv4_mode_title": "IPv4 Mode",
"network_ipv4_netmask": "IPv4 Netmask", "network_ipv4_netmask": "IPv4 Netmask",
"network_ipv6_address": "IPv6 Address", "network_ipv6_addresses_header": "IPv6 Addresses",
"network_ipv6_cidr_suggestion": "Please use CIDR notation (e.g., 2001:db8::1/64)",
"network_ipv6_dns": "IPv6 DNS",
"network_ipv6_flag_dad_failed": "DAD Failed",
"network_ipv6_flag_deprecated": "Deprecated",
"network_ipv6_gateway": "IPv6 Gateway",
"network_ipv6_information": "IPv6 Information", "network_ipv6_information": "IPv6 Information",
"network_ipv6_invalid": "Invalid IPv6 address",
"network_ipv6_mode_description": "Configure the IPv6 mode", "network_ipv6_mode_description": "Configure the IPv6 mode",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Disabled", "network_ipv6_mode_disabled": "Disabled",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Static", "network_ipv6_mode_static": "Static",
"network_ipv6_mode_title": "IPv6 Mode", "network_ipv6_mode_title": "IPv6 Mode",
"network_ipv6_netmask": "IPv6 Netmask", "network_ipv6_prefix": "IP Prefix",
"network_ipv6_no_addresses": "No IPv6 addresses configured", "network_ipv6_prefix_invalid": "Prefix must be between 0 and 128",
"network_ll_dp_all": "All", "network_ll_dp_all": "All",
"network_ll_dp_basic": "Basic", "network_ll_dp_basic": "Basic",
"network_ll_dp_description": "Control which TLVs will be sent over Link Layer Discovery Protocol", "network_ll_dp_description": "Control which TLVs will be sent over Link Layer Discovery Protocol",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "IPv4 only", "network_mdns_ipv4_only": "IPv4 only",
"network_mdns_ipv6_only": "IPv6 only", "network_mdns_ipv6_only": "IPv6 only",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "No DHCP lease information available",
"network_no_information_description": "No network configuration available", "network_no_information_description": "No network configuration available",
"network_no_information_headline": "Network Information", "network_no_information_headline": "Network Information",
"network_pending_dhcp_mode_change_description": "Save settings to enable DHCP mode and view lease information", "network_pending_dhcp_mode_change_description": "Save settings to enable DHCP mode and view lease information",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Configuration changes", "network_save_settings_confirm_heading": "Configuration changes",
"network_save_settings_failed": "Failed to save network settings: {error}", "network_save_settings_failed": "Failed to save network settings: {error}",
"network_save_settings_success": "Network settings saved", "network_save_settings_success": "Network settings saved",
"network_settings_invalid_ipv4_cidr": "Invalid CIDR notation for IPv4 address", "network_settings_add_dns": "Add DNS Server",
"network_settings_load_error": "Failed to load network settings: {error}", "network_settings_load_error": "Failed to load network settings: {error}",
"network_static_ipv4_header": "Static IPv4 Configuration",
"network_static_ipv6_header": "Static IPv6 Configuration",
"network_time_sync_description": "Configure time synchronization settings", "network_time_sync_description": "Configure time synchronization settings",
"network_time_sync_http_only": "HTTP only", "network_time_sync_http_only": "HTTP only",
"network_time_sync_ntp_and_http": "NTP and HTTP", "network_time_sync_ntp_and_http": "NTP and HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Failed to paste text: {error}", "paste_modal_failed_paste": "Failed to paste text: {error}",
"paste_modal_invalid_chars_intro": "The following characters won't be pasted:", "paste_modal_invalid_chars_intro": "The following characters won't be pasted:",
"paste_modal_paste_from_host": "Paste from host", "paste_modal_paste_from_host": "Paste from host",
"paste_modal_paste_text": "Paste text",
"paste_modal_paste_text_description": "Paste text from your client to the remote host",
"paste_modal_sending_using_layout": "Sending text using keyboard layout: {iso}-{name}", "paste_modal_sending_using_layout": "Sending text using keyboard layout: {iso}-{name}",
"paste_text": "Paste text",
"paste_text_description": "Paste text from your client to the remote host",
"peer_connection_closed": "Closed", "peer_connection_closed": "Closed",
"peer_connection_closing": "Closing", "peer_connection_closing": "Closing",
"peer_connection_connected": "Connected", "peer_connection_connected": "Connected",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "Estadísticas de conexión", "action_bar_connection_stats": "Estadísticas de conexión",
"action_bar_extension": "Extensión", "action_bar_extension": "Extensión",
"action_bar_fullscreen": "Pantalla completa", "action_bar_fullscreen": "Pantalla completa",
"action_bar_paste_text": "Pegar texto",
"action_bar_settings": "Ajustes", "action_bar_settings": "Ajustes",
"action_bar_virtual_keyboard": "Teclado virtual", "action_bar_virtual_keyboard": "Teclado virtual",
"action_bar_virtual_media": "Medios virtuales", "action_bar_virtual_media": "Medios virtuales",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Confirma tu contraseña", "auth_mode_local_password_confirm_description": "Confirma tu contraseña",
"auth_mode_local_password_confirm_label": "confirmar Contraseña", "auth_mode_local_password_confirm_label": "confirmar Contraseña",
"auth_mode_local_password_description": "Proteja su dispositivo con una contraseña para mayor protección.", "auth_mode_local_password_description": "Proteja su dispositivo con una contraseña para mayor protección.",
"auth_mode_local_password_do_not_match": "Las contraseñas no coinciden",
"auth_mode_local_password_failed_set": "No se pudo establecer la contraseña: {error}", "auth_mode_local_password_failed_set": "No se pudo establecer la contraseña: {error}",
"auth_mode_local_password_note": "Esta contraseña se utilizará para proteger los datos de su dispositivo y contra accesos no autorizados.", "auth_mode_local_password_note": "Esta contraseña se utilizará para proteger los datos de su dispositivo y contra accesos no autorizados.",
"auth_mode_local_password_note_local": "Todos los datos permanecen en su dispositivo local.", "auth_mode_local_password_note_local": "Todos los datos permanecen en su dispositivo local.",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Tiempo de ida y vuelta", "connection_stats_round_trip_time": "Tiempo de ida y vuelta",
"connection_stats_round_trip_time_description": "Tiempo de ida y vuelta para el par de candidatos ICE activos entre pares.", "connection_stats_round_trip_time_description": "Tiempo de ida y vuelta para el par de candidatos ICE activos entre pares.",
"connection_stats_sidebar": "Estadísticas de conexión", "connection_stats_sidebar": "Estadísticas de conexión",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " paquetes",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "La transmisión de vídeo desde JetKVM al cliente.", "connection_stats_video_description": "La transmisión de vídeo desde JetKVM al cliente.",
"continue": "Continuar", "continue": "Continuar",
@ -207,6 +208,8 @@
"deregister_error": "Se produjo un error {status} al cancelar el registro de su dispositivo. Inténtelo de nuevo.", "deregister_error": "Se produjo un error {status} al cancelar el registro de su dispositivo. Inténtelo de nuevo.",
"deregister_headline": "Anular el registro de {device} en su cuenta en la nube", "deregister_headline": "Anular el registro de {device} en su cuenta en la nube",
"detach": "Despegar", "detach": "Despegar",
"dhcp_empty_lease_description": "Aún no hemos recibido ninguna información de concesión de DHCP del dispositivo.",
"dhcp_empty_lease_headline": "No hay información de arrendamiento de DHCP",
"dhcp_lease_boot_file": "Archivo de arranque", "dhcp_lease_boot_file": "Archivo de arranque",
"dhcp_lease_boot_next_server": "Arrancar el siguiente servidor", "dhcp_lease_boot_next_server": "Arrancar el siguiente servidor",
"dhcp_lease_boot_server_name": "Nombre del servidor de arranque", "dhcp_lease_boot_server_name": "Nombre del servidor de arranque",
@ -426,7 +429,8 @@
"macro_name_too_long": "El nombre debe tener menos de 50 caracteres.", "macro_name_too_long": "El nombre debe tener menos de 50 caracteres.",
"macro_please_fix_validation_errors": "Por favor corrija los errores de validación", "macro_please_fix_validation_errors": "Por favor corrija los errores de validación",
"macro_save": "Guardar macro", "macro_save": "Guardar macro",
"macro_save_error": "Se produjo un error al guardar.", "macro_save_failed": "Se produjo un error al guardar.",
"macro_save_failed_error": "Se produjo un error al guardar: {error}.",
"macro_step_count": "{steps} / {max} pasos", "macro_step_count": "{steps} / {max} pasos",
"macro_step_duration_description": "Tiempo de espera antes de ejecutar el siguiente paso.", "macro_step_duration_description": "Tiempo de espera antes de ejecutar el siguiente paso.",
"macro_step_duration_label": "Duración del paso", "macro_step_duration_label": "Duración del paso",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Configurar qué cliente DHCP utilizar", "network_dhcp_client_description": "Configurar qué cliente DHCP utilizar",
"network_dhcp_client_jetkvm": "JetKVM interno", "network_dhcp_client_jetkvm": "JetKVM interno",
"network_dhcp_client_title": "Cliente DHCP", "network_dhcp_client_title": "Cliente DHCP",
"network_dhcp_information": "Información de DHCP",
"network_dhcp_lease_renew": "Renovar la concesión de DHCP", "network_dhcp_lease_renew": "Renovar la concesión de DHCP",
"network_dhcp_lease_renew_confirm": "Renovar el contrato de arrendamiento", "network_dhcp_lease_renew_confirm": "Renovar el contrato de arrendamiento",
"network_dhcp_lease_renew_confirm_description": "Esto solicitará una nueva dirección IP a su servidor DHCP. Es posible que su dispositivo pierda temporalmente la conexión a la red durante este proceso.", "network_dhcp_lease_renew_confirm_description": "Esto solicitará una nueva dirección IP a su servidor DHCP. Es posible que su dispositivo pierda temporalmente la conexión a la red durante este proceso.",
@ -599,13 +602,21 @@
"network_ipv4_address": "Dirección IPv4", "network_ipv4_address": "Dirección IPv4",
"network_ipv4_dns": "DNS IPv4", "network_ipv4_dns": "DNS IPv4",
"network_ipv4_gateway": "Puerta de enlace IPv4", "network_ipv4_gateway": "Puerta de enlace IPv4",
"network_ipv4_invalid": "Dirección IPv4 no válida",
"network_ipv4_invalid_cidr": "Notación CIDR no válida para la dirección IPv4",
"network_ipv4_mode_description": "Configurar el modo IPv4", "network_ipv4_mode_description": "Configurar el modo IPv4",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Estático", "network_ipv4_mode_static": "Estático",
"network_ipv4_mode_title": "Modo IPv4", "network_ipv4_mode_title": "Modo IPv4",
"network_ipv4_netmask": "Máscara de red IPv4", "network_ipv4_netmask": "Máscara de red IPv4",
"network_ipv6_address": "Dirección IPv6", "network_ipv6_addresses_header": "Direcciones IPv6",
"network_ipv6_cidr_suggestion": "Utilice la notación CIDR (por ejemplo, 2001:db8::1/64)",
"network_ipv6_dns": "DNS IPv6",
"network_ipv6_flag_dad_failed": "Papá falló",
"network_ipv6_flag_deprecated": "Obsoleto",
"network_ipv6_gateway": "Puerta de enlace IPv6",
"network_ipv6_information": "Información de IPv6", "network_ipv6_information": "Información de IPv6",
"network_ipv6_invalid": "Dirección IPv6 no válida",
"network_ipv6_mode_description": "Configurar el modo IPv6", "network_ipv6_mode_description": "Configurar el modo IPv6",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Desactivado", "network_ipv6_mode_disabled": "Desactivado",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Estático", "network_ipv6_mode_static": "Estático",
"network_ipv6_mode_title": "Modo IPv6", "network_ipv6_mode_title": "Modo IPv6",
"network_ipv6_netmask": "Máscara de red IPv6", "network_ipv6_prefix": "Prefijo IP",
"network_ipv6_no_addresses": "No hay direcciones IPv6 configuradas", "network_ipv6_prefix_invalid": "El prefijo debe estar entre 0 y 128",
"network_ll_dp_all": "Todo", "network_ll_dp_all": "Todo",
"network_ll_dp_basic": "Básico", "network_ll_dp_basic": "Básico",
"network_ll_dp_description": "Controlar qué TLV se enviarán a través del Protocolo de descubrimiento de capa de enlace", "network_ll_dp_description": "Controlar qué TLV se enviarán a través del Protocolo de descubrimiento de capa de enlace",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "Sólo IPv4", "network_mdns_ipv4_only": "Sólo IPv4",
"network_mdns_ipv6_only": "Sólo IPv6", "network_mdns_ipv6_only": "Sólo IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "No hay información de arrendamiento de DHCP disponible",
"network_no_information_description": "No hay configuración de red disponible", "network_no_information_description": "No hay configuración de red disponible",
"network_no_information_headline": "Información de la red", "network_no_information_headline": "Información de la red",
"network_pending_dhcp_mode_change_description": "Guardar la configuración para habilitar el modo DHCP y ver la información de arrendamiento", "network_pending_dhcp_mode_change_description": "Guardar la configuración para habilitar el modo DHCP y ver la información de arrendamiento",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Cambios de configuración", "network_save_settings_confirm_heading": "Cambios de configuración",
"network_save_settings_failed": "No se pudo guardar la configuración de red: {error}", "network_save_settings_failed": "No se pudo guardar la configuración de red: {error}",
"network_save_settings_success": "Configuración de red guardada", "network_save_settings_success": "Configuración de red guardada",
"network_settings_invalid_ipv4_cidr": "Notación CIDR no válida para la dirección IPv4", "network_settings_add_dns": "Agregar servidor DNS",
"network_settings_load_error": "No se pudo cargar la configuración de red: {error}", "network_settings_load_error": "No se pudo cargar la configuración de red: {error}",
"network_static_ipv4_header": "Configuración estática de IPv4",
"network_static_ipv6_header": "Configuración estática de IPv6",
"network_time_sync_description": "Configurar los ajustes de sincronización horaria", "network_time_sync_description": "Configurar los ajustes de sincronización horaria",
"network_time_sync_http_only": "Sólo HTTP", "network_time_sync_http_only": "Sólo HTTP",
"network_time_sync_ntp_and_http": "NTP y HTTP", "network_time_sync_ntp_and_http": "NTP y HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "No se pudo pegar el texto: {error}", "paste_modal_failed_paste": "No se pudo pegar el texto: {error}",
"paste_modal_invalid_chars_intro": "Los siguientes caracteres no se pegarán:", "paste_modal_invalid_chars_intro": "Los siguientes caracteres no se pegarán:",
"paste_modal_paste_from_host": "Pegar desde el host", "paste_modal_paste_from_host": "Pegar desde el host",
"paste_modal_paste_text": "Pegar texto",
"paste_modal_paste_text_description": "Pegue el texto de su cliente al host remoto",
"paste_modal_sending_using_layout": "Envío de texto mediante la distribución del teclado: {iso} - {name}", "paste_modal_sending_using_layout": "Envío de texto mediante la distribución del teclado: {iso} - {name}",
"paste_text": "Pegar texto",
"paste_text_description": "Pegue el texto de su cliente al host remoto",
"peer_connection_closed": "Cerrado", "peer_connection_closed": "Cerrado",
"peer_connection_closing": "Cierre", "peer_connection_closing": "Cierre",
"peer_connection_connected": "Conectado", "peer_connection_connected": "Conectado",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "Statistiques de connexion", "action_bar_connection_stats": "Statistiques de connexion",
"action_bar_extension": "Extension", "action_bar_extension": "Extension",
"action_bar_fullscreen": "Plein écran", "action_bar_fullscreen": "Plein écran",
"action_bar_paste_text": "Coller du texte",
"action_bar_settings": "Paramètres", "action_bar_settings": "Paramètres",
"action_bar_virtual_keyboard": "Clavier virtuel", "action_bar_virtual_keyboard": "Clavier virtuel",
"action_bar_virtual_media": "Médias virtuels", "action_bar_virtual_media": "Médias virtuels",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Confirmez votre mot de passe", "auth_mode_local_password_confirm_description": "Confirmez votre mot de passe",
"auth_mode_local_password_confirm_label": "Confirmez le mot de passe", "auth_mode_local_password_confirm_label": "Confirmez le mot de passe",
"auth_mode_local_password_description": "Sécurisez votre appareil avec un mot de passe pour une protection supplémentaire.", "auth_mode_local_password_description": "Sécurisez votre appareil avec un mot de passe pour une protection supplémentaire.",
"auth_mode_local_password_do_not_match": "Les mots de passe ne correspondent pas",
"auth_mode_local_password_failed_set": "Échec de la définition du mot de passe : {error}", "auth_mode_local_password_failed_set": "Échec de la définition du mot de passe : {error}",
"auth_mode_local_password_note": "Ce mot de passe sera utilisé pour sécuriser les données de votre appareil et les protéger contre tout accès non autorisé.", "auth_mode_local_password_note": "Ce mot de passe sera utilisé pour sécuriser les données de votre appareil et les protéger contre tout accès non autorisé.",
"auth_mode_local_password_note_local": "Toutes les données restent sur votre appareil local.", "auth_mode_local_password_note_local": "Toutes les données restent sur votre appareil local.",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Temps de trajet aller-retour", "connection_stats_round_trip_time": "Temps de trajet aller-retour",
"connection_stats_round_trip_time_description": "Temps de trajet aller-retour pour la paire de candidats ICE actifs entre pairs.", "connection_stats_round_trip_time_description": "Temps de trajet aller-retour pour la paire de candidats ICE actifs entre pairs.",
"connection_stats_sidebar": "Statistiques de connexion", "connection_stats_sidebar": "Statistiques de connexion",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " paquets",
"connection_stats_video": "Vidéo", "connection_stats_video": "Vidéo",
"connection_stats_video_description": "Le flux vidéo du JetKVM vers le client.", "connection_stats_video_description": "Le flux vidéo du JetKVM vers le client.",
"continue": "Continuer", "continue": "Continuer",
@ -207,6 +208,8 @@
"deregister_error": "Une erreur {status} s'est produite lors de l'annulation de l'enregistrement de votre appareil. Veuillez réessayer.", "deregister_error": "Une erreur {status} s'est produite lors de l'annulation de l'enregistrement de votre appareil. Veuillez réessayer.",
"deregister_headline": "Désinscrivez {device} de votre compte cloud", "deregister_headline": "Désinscrivez {device} de votre compte cloud",
"detach": "Détacher", "detach": "Détacher",
"dhcp_empty_lease_description": "Nous n'avons pas encore reçu d'informations sur le bail DHCP de l'appareil.",
"dhcp_empty_lease_headline": "Aucune information sur le bail DHCP",
"dhcp_lease_boot_file": "Fichier de démarrage", "dhcp_lease_boot_file": "Fichier de démarrage",
"dhcp_lease_boot_next_server": "Démarrer le serveur suivant", "dhcp_lease_boot_next_server": "Démarrer le serveur suivant",
"dhcp_lease_boot_server_name": "Nom du serveur de démarrage", "dhcp_lease_boot_server_name": "Nom du serveur de démarrage",
@ -426,7 +429,8 @@
"macro_name_too_long": "Le nom doit comporter moins de 50 caractères", "macro_name_too_long": "Le nom doit comporter moins de 50 caractères",
"macro_please_fix_validation_errors": "Veuillez corriger les erreurs de validation", "macro_please_fix_validation_errors": "Veuillez corriger les erreurs de validation",
"macro_save": "Enregistrer la macro", "macro_save": "Enregistrer la macro",
"macro_save_error": "Une erreur s'est produite lors de l'enregistrement.", "macro_save_failed": "Une erreur s'est produite lors de l'enregistrement.",
"macro_save_failed_error": "Une erreur s'est produite lors de l'enregistrement: {error}.",
"macro_step_count": "{steps} / {max} étapes", "macro_step_count": "{steps} / {max} étapes",
"macro_step_duration_description": "Il est temps dattendre avant dexécuter létape suivante.", "macro_step_duration_description": "Il est temps dattendre avant dexécuter létape suivante.",
"macro_step_duration_label": "Durée de l'étape", "macro_step_duration_label": "Durée de l'étape",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Configurer le client DHCP à utiliser", "network_dhcp_client_description": "Configurer le client DHCP à utiliser",
"network_dhcp_client_jetkvm": "JetKVM interne", "network_dhcp_client_jetkvm": "JetKVM interne",
"network_dhcp_client_title": "Client DHCP", "network_dhcp_client_title": "Client DHCP",
"network_dhcp_information": "Informations DHCP",
"network_dhcp_lease_renew": "Renouveler le bail DHCP", "network_dhcp_lease_renew": "Renouveler le bail DHCP",
"network_dhcp_lease_renew_confirm": "Renouveler le bail", "network_dhcp_lease_renew_confirm": "Renouveler le bail",
"network_dhcp_lease_renew_confirm_description": "Cette opération demandera une nouvelle adresse IP à votre serveur DHCP. Votre appareil pourrait perdre temporairement sa connectivité réseau pendant cette opération.", "network_dhcp_lease_renew_confirm_description": "Cette opération demandera une nouvelle adresse IP à votre serveur DHCP. Votre appareil pourrait perdre temporairement sa connectivité réseau pendant cette opération.",
@ -599,13 +602,21 @@
"network_ipv4_address": "Adresse IPv4", "network_ipv4_address": "Adresse IPv4",
"network_ipv4_dns": "DNS IPv4", "network_ipv4_dns": "DNS IPv4",
"network_ipv4_gateway": "Passerelle IPv4", "network_ipv4_gateway": "Passerelle IPv4",
"network_ipv4_invalid": "Adresse IPv4 non valide",
"network_ipv4_invalid_cidr": "Notation CIDR non valide pour l'adresse IPv4",
"network_ipv4_mode_description": "Configurer le mode IPv4", "network_ipv4_mode_description": "Configurer le mode IPv4",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statique", "network_ipv4_mode_static": "Statique",
"network_ipv4_mode_title": "Mode IPv4", "network_ipv4_mode_title": "Mode IPv4",
"network_ipv4_netmask": "Masque de réseau IPv4", "network_ipv4_netmask": "Masque de réseau IPv4",
"network_ipv6_address": "Adresse IPv6", "network_ipv6_addresses_header": "Adresses IPv6",
"network_ipv6_cidr_suggestion": "Veuillez utiliser la notation CIDR (par exemple, 2001:db8::1/64)",
"network_ipv6_dns": "DNS IPv6",
"network_ipv6_flag_dad_failed": "Papa a échoué",
"network_ipv6_flag_deprecated": "Obsolète",
"network_ipv6_gateway": "Passerelle IPv6",
"network_ipv6_information": "Informations IPv6", "network_ipv6_information": "Informations IPv6",
"network_ipv6_invalid": "Adresse IPv6 non valide",
"network_ipv6_mode_description": "Configurer le mode IPv6", "network_ipv6_mode_description": "Configurer le mode IPv6",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Désactivé", "network_ipv6_mode_disabled": "Désactivé",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statique", "network_ipv6_mode_static": "Statique",
"network_ipv6_mode_title": "Mode IPv6", "network_ipv6_mode_title": "Mode IPv6",
"network_ipv6_netmask": "Masque de réseau IPv6", "network_ipv6_prefix": "Préfixe IP",
"network_ipv6_no_addresses": "Aucune adresse IPv6 configurée", "network_ipv6_prefix_invalid": "Le préfixe doit être compris entre 0 et 128",
"network_ll_dp_all": "Tous", "network_ll_dp_all": "Tous",
"network_ll_dp_basic": "Basique", "network_ll_dp_basic": "Basique",
"network_ll_dp_description": "Contrôler les TLV qui seront envoyés via le protocole Link Layer Discovery", "network_ll_dp_description": "Contrôler les TLV qui seront envoyés via le protocole Link Layer Discovery",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "IPv4 uniquement", "network_mdns_ipv4_only": "IPv4 uniquement",
"network_mdns_ipv6_only": "IPv6 uniquement", "network_mdns_ipv6_only": "IPv6 uniquement",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Aucune information de bail DHCP disponible",
"network_no_information_description": "Aucune configuration réseau disponible", "network_no_information_description": "Aucune configuration réseau disponible",
"network_no_information_headline": "Informations sur le réseau", "network_no_information_headline": "Informations sur le réseau",
"network_pending_dhcp_mode_change_description": "Enregistrer les paramètres pour activer le mode DHCP et afficher les informations de bail", "network_pending_dhcp_mode_change_description": "Enregistrer les paramètres pour activer le mode DHCP et afficher les informations de bail",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Modifications de configuration", "network_save_settings_confirm_heading": "Modifications de configuration",
"network_save_settings_failed": "Échec de l'enregistrement des paramètres réseau : {error}", "network_save_settings_failed": "Échec de l'enregistrement des paramètres réseau : {error}",
"network_save_settings_success": "Paramètres réseau enregistrés", "network_save_settings_success": "Paramètres réseau enregistrés",
"network_settings_invalid_ipv4_cidr": "Notation CIDR non valide pour l'adresse IPv4", "network_settings_add_dns": "Ajouter un serveur DNS",
"network_settings_load_error": "Échec du chargement des paramètres réseau : {error}", "network_settings_load_error": "Échec du chargement des paramètres réseau : {error}",
"network_static_ipv4_header": "Configuration IPv4 statique",
"network_static_ipv6_header": "Configuration IPv6 statique",
"network_time_sync_description": "Configurer les paramètres de synchronisation de l'heure", "network_time_sync_description": "Configurer les paramètres de synchronisation de l'heure",
"network_time_sync_http_only": "HTTP uniquement", "network_time_sync_http_only": "HTTP uniquement",
"network_time_sync_ntp_and_http": "NTP et HTTP", "network_time_sync_ntp_and_http": "NTP et HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Échec du collage du texte : {error}", "paste_modal_failed_paste": "Échec du collage du texte : {error}",
"paste_modal_invalid_chars_intro": "Les caractères suivants ne seront pas collés :", "paste_modal_invalid_chars_intro": "Les caractères suivants ne seront pas collés :",
"paste_modal_paste_from_host": "Coller depuis l'hôte", "paste_modal_paste_from_host": "Coller depuis l'hôte",
"paste_modal_paste_text": "Coller du texte",
"paste_modal_paste_text_description": "Collez le texte de votre client sur l'hôte distant",
"paste_modal_sending_using_layout": "Envoi de texte à l'aide de la disposition du clavier : {iso} - {name}", "paste_modal_sending_using_layout": "Envoi de texte à l'aide de la disposition du clavier : {iso} - {name}",
"paste_text": "Coller du texte",
"paste_text_description": "Collez le texte de votre client sur l'hôte distant",
"peer_connection_closed": "Fermé", "peer_connection_closed": "Fermé",
"peer_connection_closing": "Clôture", "peer_connection_closing": "Clôture",
"peer_connection_connected": "Connecté", "peer_connection_connected": "Connecté",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "Statistiche di connessione", "action_bar_connection_stats": "Statistiche di connessione",
"action_bar_extension": "Estensione", "action_bar_extension": "Estensione",
"action_bar_fullscreen": "A schermo intero", "action_bar_fullscreen": "A schermo intero",
"action_bar_paste_text": "Incolla il testo",
"action_bar_settings": "Impostazioni", "action_bar_settings": "Impostazioni",
"action_bar_virtual_keyboard": "Tastiera virtuale", "action_bar_virtual_keyboard": "Tastiera virtuale",
"action_bar_virtual_media": "Media virtuali", "action_bar_virtual_media": "Media virtuali",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Conferma la tua password", "auth_mode_local_password_confirm_description": "Conferma la tua password",
"auth_mode_local_password_confirm_label": "Conferma password", "auth_mode_local_password_confirm_label": "Conferma password",
"auth_mode_local_password_description": "Per una maggiore protezione, proteggi il tuo dispositivo con una password.", "auth_mode_local_password_description": "Per una maggiore protezione, proteggi il tuo dispositivo con una password.",
"auth_mode_local_password_do_not_match": "Le password non corrispondono",
"auth_mode_local_password_failed_set": "Impossibile impostare la password: {error}", "auth_mode_local_password_failed_set": "Impossibile impostare la password: {error}",
"auth_mode_local_password_note": "Questa password verrà utilizzata per proteggere i dati del tuo dispositivo e proteggerli da accessi non autorizzati.", "auth_mode_local_password_note": "Questa password verrà utilizzata per proteggere i dati del tuo dispositivo e proteggerli da accessi non autorizzati.",
"auth_mode_local_password_note_local": "Tutti i dati rimangono sul tuo dispositivo locale.", "auth_mode_local_password_note_local": "Tutti i dati rimangono sul tuo dispositivo locale.",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Tempo di andata e ritorno", "connection_stats_round_trip_time": "Tempo di andata e ritorno",
"connection_stats_round_trip_time_description": "Tempo di andata e ritorno per la coppia di candidati ICE attivi tra pari.", "connection_stats_round_trip_time_description": "Tempo di andata e ritorno per la coppia di candidati ICE attivi tra pari.",
"connection_stats_sidebar": "Statistiche di connessione", "connection_stats_sidebar": "Statistiche di connessione",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " pacchetti",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "Il flusso video dal JetKVM al client.", "connection_stats_video_description": "Il flusso video dal JetKVM al client.",
"continue": "Continuare", "continue": "Continuare",
@ -207,6 +208,8 @@
"deregister_error": "Si è verificato un errore {status} durante l'annullamento della registrazione del dispositivo. Riprova.", "deregister_error": "Si è verificato un errore {status} durante l'annullamento della registrazione del dispositivo. Riprova.",
"deregister_headline": "Annulla la registrazione di {device} dal tuo account cloud", "deregister_headline": "Annulla la registrazione di {device} dal tuo account cloud",
"detach": "Staccare", "detach": "Staccare",
"dhcp_empty_lease_description": "Non abbiamo ancora ricevuto alcuna informazione di lease DHCP dal dispositivo.",
"dhcp_empty_lease_headline": "Nessuna informazione sul lease DHCP",
"dhcp_lease_boot_file": "File di avvio", "dhcp_lease_boot_file": "File di avvio",
"dhcp_lease_boot_next_server": "Avvia il server successivo", "dhcp_lease_boot_next_server": "Avvia il server successivo",
"dhcp_lease_boot_server_name": "Nome del server di avvio", "dhcp_lease_boot_server_name": "Nome del server di avvio",
@ -426,7 +429,8 @@
"macro_name_too_long": "Il nome deve contenere meno di 50 caratteri", "macro_name_too_long": "Il nome deve contenere meno di 50 caratteri",
"macro_please_fix_validation_errors": "Si prega di correggere gli errori di convalida", "macro_please_fix_validation_errors": "Si prega di correggere gli errori di convalida",
"macro_save": "Salva macro", "macro_save": "Salva macro",
"macro_save_error": "Si è verificato un errore durante il salvataggio.", "macro_save_failed": "Si è verificato un errore durante il salvataggio.",
"macro_save_failed_error": "Si è verificato un errore durante il salvataggio: {error}.",
"macro_step_count": "{steps} / {max} steps", "macro_step_count": "{steps} / {max} steps",
"macro_step_duration_description": "Tempo di attesa prima di eseguire il passaggio successivo.", "macro_step_duration_description": "Tempo di attesa prima di eseguire il passaggio successivo.",
"macro_step_duration_label": "Durata del passo", "macro_step_duration_label": "Durata del passo",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Configurare quale client DHCP utilizzare", "network_dhcp_client_description": "Configurare quale client DHCP utilizzare",
"network_dhcp_client_jetkvm": "JetKVM interno", "network_dhcp_client_jetkvm": "JetKVM interno",
"network_dhcp_client_title": "Cliente DHCP", "network_dhcp_client_title": "Cliente DHCP",
"network_dhcp_information": "Informazioni DHCP",
"network_dhcp_lease_renew": "Rinnova il contratto di locazione DHCP", "network_dhcp_lease_renew": "Rinnova il contratto di locazione DHCP",
"network_dhcp_lease_renew_confirm": "Rinnovare il contratto di locazione", "network_dhcp_lease_renew_confirm": "Rinnovare il contratto di locazione",
"network_dhcp_lease_renew_confirm_description": "Verrà richiesto un nuovo indirizzo IP al server DHCP. Durante questo processo, il dispositivo potrebbe perdere temporaneamente la connettività di rete.", "network_dhcp_lease_renew_confirm_description": "Verrà richiesto un nuovo indirizzo IP al server DHCP. Durante questo processo, il dispositivo potrebbe perdere temporaneamente la connettività di rete.",
@ -599,13 +602,21 @@
"network_ipv4_address": "Indirizzo IPv4", "network_ipv4_address": "Indirizzo IPv4",
"network_ipv4_dns": "DNS IPv4", "network_ipv4_dns": "DNS IPv4",
"network_ipv4_gateway": "Gateway IPv4", "network_ipv4_gateway": "Gateway IPv4",
"network_ipv4_invalid": "Indirizzo IPv4 non valido",
"network_ipv4_invalid_cidr": "Notazione CIDR non valida per l'indirizzo IPv4",
"network_ipv4_mode_description": "Configurare la modalità IPv4", "network_ipv4_mode_description": "Configurare la modalità IPv4",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statico", "network_ipv4_mode_static": "Statico",
"network_ipv4_mode_title": "Modalità IPv4", "network_ipv4_mode_title": "Modalità IPv4",
"network_ipv4_netmask": "Maschera di rete IPv4", "network_ipv4_netmask": "Maschera di rete IPv4",
"network_ipv6_address": "Indirizzo IPv6", "network_ipv6_addresses_header": "Indirizzi IPv6",
"network_ipv6_cidr_suggestion": "Si prega di utilizzare la notazione CIDR (ad esempio, 2001:db8::1/64)",
"network_ipv6_dns": "DNS IPv6",
"network_ipv6_flag_dad_failed": "DAD fallito",
"network_ipv6_flag_deprecated": "Obsoleto",
"network_ipv6_gateway": "Gateway IPv6",
"network_ipv6_information": "Informazioni IPv6", "network_ipv6_information": "Informazioni IPv6",
"network_ipv6_invalid": "Indirizzo IPv6 non valido",
"network_ipv6_mode_description": "Configurare la modalità IPv6", "network_ipv6_mode_description": "Configurare la modalità IPv6",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Disabili", "network_ipv6_mode_disabled": "Disabili",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statico", "network_ipv6_mode_static": "Statico",
"network_ipv6_mode_title": "Modalità IPv6", "network_ipv6_mode_title": "Modalità IPv6",
"network_ipv6_netmask": "Maschera di rete IPv6", "network_ipv6_prefix": "Prefisso IP",
"network_ipv6_no_addresses": "Nessun indirizzo IPv6 configurato", "network_ipv6_prefix_invalid": "Il prefisso deve essere compreso tra 0 e 128",
"network_ll_dp_all": "Tutto", "network_ll_dp_all": "Tutto",
"network_ll_dp_basic": "Di base", "network_ll_dp_basic": "Di base",
"network_ll_dp_description": "Controlla quali TLV verranno inviati tramite Link Layer Discovery Protocol", "network_ll_dp_description": "Controlla quali TLV verranno inviati tramite Link Layer Discovery Protocol",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "Solo IPv4", "network_mdns_ipv4_only": "Solo IPv4",
"network_mdns_ipv6_only": "Solo IPv6", "network_mdns_ipv6_only": "Solo IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Nessuna informazione disponibile sul lease DHCP",
"network_no_information_description": "Nessuna configurazione di rete disponibile", "network_no_information_description": "Nessuna configurazione di rete disponibile",
"network_no_information_headline": "Informazioni di rete", "network_no_information_headline": "Informazioni di rete",
"network_pending_dhcp_mode_change_description": "Salva le impostazioni per abilitare la modalità DHCP e visualizzare le informazioni di locazione", "network_pending_dhcp_mode_change_description": "Salva le impostazioni per abilitare la modalità DHCP e visualizzare le informazioni di locazione",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Modifiche alla configurazione", "network_save_settings_confirm_heading": "Modifiche alla configurazione",
"network_save_settings_failed": "Impossibile salvare le impostazioni di rete: {error}", "network_save_settings_failed": "Impossibile salvare le impostazioni di rete: {error}",
"network_save_settings_success": "Impostazioni di rete salvate", "network_save_settings_success": "Impostazioni di rete salvate",
"network_settings_invalid_ipv4_cidr": "Notazione CIDR non valida per l'indirizzo IPv4", "network_settings_add_dns": "Aggiungi server DNS",
"network_settings_load_error": "Impossibile caricare le impostazioni di rete: {error}", "network_settings_load_error": "Impossibile caricare le impostazioni di rete: {error}",
"network_static_ipv4_header": "Configurazione IPv4 statica",
"network_static_ipv6_header": "Configurazione IPv6 statica",
"network_time_sync_description": "Configurare le impostazioni di sincronizzazione dell'ora", "network_time_sync_description": "Configurare le impostazioni di sincronizzazione dell'ora",
"network_time_sync_http_only": "Solo HTTP", "network_time_sync_http_only": "Solo HTTP",
"network_time_sync_ntp_and_http": "NTP e HTTP", "network_time_sync_ntp_and_http": "NTP e HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Impossibile incollare il testo: {error}", "paste_modal_failed_paste": "Impossibile incollare il testo: {error}",
"paste_modal_invalid_chars_intro": "I seguenti caratteri non verranno incollati:", "paste_modal_invalid_chars_intro": "I seguenti caratteri non verranno incollati:",
"paste_modal_paste_from_host": "Incolla dall'host", "paste_modal_paste_from_host": "Incolla dall'host",
"paste_modal_paste_text": "Incolla il testo",
"paste_modal_paste_text_description": "Incolla il testo dal tuo client all'host remoto",
"paste_modal_sending_using_layout": "Invio di testo tramite layout di tastiera: {iso} - {name}", "paste_modal_sending_using_layout": "Invio di testo tramite layout di tastiera: {iso} - {name}",
"paste_text": "Incolla il testo",
"paste_text_description": "Incolla il testo dal tuo client all'host remoto",
"peer_connection_closed": "Chiuso", "peer_connection_closed": "Chiuso",
"peer_connection_closing": "Chiusura", "peer_connection_closing": "Chiusura",
"peer_connection_connected": "Collegato", "peer_connection_connected": "Collegato",

View File

@ -31,7 +31,7 @@
"access_no_device_id": "Ingen enhets-ID tilgjengelig", "access_no_device_id": "Ingen enhets-ID tilgjengelig",
"access_private_key_description": "Av sikkerhetshensyn vil den ikke vises etter lagring.", "access_private_key_description": "Av sikkerhetshensyn vil den ikke vises etter lagring.",
"access_private_key_label": "Privat nøkkel", "access_private_key_label": "Privat nøkkel",
"access_provider_custom": "Skikk", "access_provider_custom": "Tilpasset",
"access_provider_jetkvm": "JetKVM Cloud", "access_provider_jetkvm": "JetKVM Cloud",
"access_remote_description": "Administrer modusen for ekstern tilgang til enheten", "access_remote_description": "Administrer modusen for ekstern tilgang til enheten",
"access_security_encryption": "Ende-til-ende-kryptering ved bruk av WebRTC (DTLS og SRTP)", "access_security_encryption": "Ende-til-ende-kryptering ved bruk av WebRTC (DTLS og SRTP)",
@ -42,15 +42,14 @@
"access_title": "Adgang", "access_title": "Adgang",
"access_tls_certificate_description": "Lim inn TLS-sertifikatet ditt nedenfor. For sertifikatkjeder, inkluder hele kjeden (blad-, mellom- og rotsertifikater).", "access_tls_certificate_description": "Lim inn TLS-sertifikatet ditt nedenfor. For sertifikatkjeder, inkluder hele kjeden (blad-, mellom- og rotsertifikater).",
"access_tls_certificate_title": "TLS-sertifikat", "access_tls_certificate_title": "TLS-sertifikat",
"access_tls_custom": "Skikk", "access_tls_custom": "Tilpasset",
"access_tls_disabled": "Funksjonshemmet", "access_tls_disabled": "Deaktivert",
"access_tls_self_signed": "Selvsignert", "access_tls_self_signed": "Selvsignert",
"access_tls_updated": "TLS-innstillingene er oppdatert", "access_tls_updated": "TLS-innstillingene er oppdatert",
"access_update_tls_settings": "Oppdater TLS-innstillinger", "access_update_tls_settings": "Oppdater TLS-innstillinger",
"action_bar_connection_stats": "Tilkoblingsstatistikk", "action_bar_connection_stats": "Tilkoblingsstatistikk",
"action_bar_extension": "Forlengelse", "action_bar_extension": "Forlengelse",
"action_bar_fullscreen": "Fullskjerm", "action_bar_fullscreen": "Fullskjerm",
"action_bar_paste_text": "Lim inn tekst",
"action_bar_settings": "Innstillinger", "action_bar_settings": "Innstillinger",
"action_bar_virtual_keyboard": "Virtuelt tastatur", "action_bar_virtual_keyboard": "Virtuelt tastatur",
"action_bar_virtual_media": "Virtuelle medier", "action_bar_virtual_media": "Virtuelle medier",
@ -116,7 +115,7 @@
"atx_power_control_get_state_error": "Klarte ikke å hente ATX-strømstatus: {error}", "atx_power_control_get_state_error": "Klarte ikke å hente ATX-strømstatus: {error}",
"atx_power_control_hdd_led": "HDD-LED", "atx_power_control_hdd_led": "HDD-LED",
"atx_power_control_long_power_button": "Langt trykk", "atx_power_control_long_power_button": "Langt trykk",
"atx_power_control_power_button": "Makt", "atx_power_control_power_button": "Strøm",
"atx_power_control_power_led": "Strøm-LED", "atx_power_control_power_led": "Strøm-LED",
"atx_power_control_reset_button": "Tilbakestill", "atx_power_control_reset_button": "Tilbakestill",
"atx_power_control_send_action_error": "Kunne ikke sende ATX-strømhandling {action} : {error}", "atx_power_control_send_action_error": "Kunne ikke sende ATX-strømhandling {action} : {error}",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Bekreft passordet ditt", "auth_mode_local_password_confirm_description": "Bekreft passordet ditt",
"auth_mode_local_password_confirm_label": "Bekreft passord", "auth_mode_local_password_confirm_label": "Bekreft passord",
"auth_mode_local_password_description": "Sikre enheten din med et passord for ekstra beskyttelse.", "auth_mode_local_password_description": "Sikre enheten din med et passord for ekstra beskyttelse.",
"auth_mode_local_password_do_not_match": "Passordene stemmer ikke overens",
"auth_mode_local_password_failed_set": "Klarte ikke å angi passord: {error}", "auth_mode_local_password_failed_set": "Klarte ikke å angi passord: {error}",
"auth_mode_local_password_note": "Dette passordet vil bli brukt til å sikre enhetsdataene dine og beskytte mot uautorisert tilgang.", "auth_mode_local_password_note": "Dette passordet vil bli brukt til å sikre enhetsdataene dine og beskytte mot uautorisert tilgang.",
"auth_mode_local_password_note_local": "Alle dataene forblir på din lokale enhet.", "auth_mode_local_password_note_local": "Alle dataene forblir på din lokale enhet.",
@ -156,8 +154,8 @@
"auth_signup_create_account_description": "Opprett kontoen din og begynn å administrere enhetene dine med letthet.", "auth_signup_create_account_description": "Opprett kontoen din og begynn å administrere enhetene dine med letthet.",
"back": "Tilbake", "back": "Tilbake",
"back_to_devices": "Tilbake til Enheter", "back_to_devices": "Tilbake til Enheter",
"cancel": "Kansellere", "cancel": "Avbryt",
"close": "Lukke", "close": "Lukk",
"cloud_kvms": "Cloud KVM-er", "cloud_kvms": "Cloud KVM-er",
"cloud_kvms_description": "Administrer skybaserte KVM-er og koble til dem sikkert.", "cloud_kvms_description": "Administrer skybaserte KVM-er og koble til dem sikkert.",
"cloud_kvms_no_devices": "Ingen enheter funnet", "cloud_kvms_no_devices": "Ingen enheter funnet",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Tur-retur-tid", "connection_stats_round_trip_time": "Tur-retur-tid",
"connection_stats_round_trip_time_description": "Rundturstid for det aktive ICE-kandidatparet mellom jevnaldrende.", "connection_stats_round_trip_time_description": "Rundturstid for det aktive ICE-kandidatparet mellom jevnaldrende.",
"connection_stats_sidebar": "Tilkoblingsstatistikk", "connection_stats_sidebar": "Tilkoblingsstatistikk",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " pakker",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "Videostrømmen fra JetKVM til klienten.", "connection_stats_video_description": "Videostrømmen fra JetKVM til klienten.",
"continue": "Fortsette", "continue": "Fortsette",
@ -188,7 +189,7 @@
"dc_power_control_current": "Nåværende", "dc_power_control_current": "Nåværende",
"dc_power_control_current_unit": "EN", "dc_power_control_current_unit": "EN",
"dc_power_control_get_state_error": "Klarte ikke å hente likestrømsstatus: {error}", "dc_power_control_get_state_error": "Klarte ikke å hente likestrømsstatus: {error}",
"dc_power_control_power": "Makt", "dc_power_control_power": "Strøm",
"dc_power_control_power_off_button": "Slå av", "dc_power_control_power_off_button": "Slå av",
"dc_power_control_power_off_state": "Slå av", "dc_power_control_power_off_state": "Slå av",
"dc_power_control_power_on_button": "Slå på", "dc_power_control_power_on_button": "Slå på",
@ -207,6 +208,8 @@
"deregister_error": "Det oppsto en feil {status} enheten din skulle avregistreres. Prøv på nytt.", "deregister_error": "Det oppsto en feil {status} enheten din skulle avregistreres. Prøv på nytt.",
"deregister_headline": "Avregistrer {device} fra skykontoen din", "deregister_headline": "Avregistrer {device} fra skykontoen din",
"detach": "Løsne", "detach": "Løsne",
"dhcp_empty_lease_description": "Vi har ikke mottatt noen DHCP-leaseinformasjon fra enheten ennå.",
"dhcp_empty_lease_headline": "Ingen DHCP-leaseinformasjon",
"dhcp_lease_boot_file": "Oppstartsfil", "dhcp_lease_boot_file": "Oppstartsfil",
"dhcp_lease_boot_next_server": "Start opp neste server", "dhcp_lease_boot_next_server": "Start opp neste server",
"dhcp_lease_boot_server_name": "Navn på oppstartsserver", "dhcp_lease_boot_server_name": "Navn på oppstartsserver",
@ -371,7 +374,7 @@
"local_auth_create_new_password_label": "Nytt passord", "local_auth_create_new_password_label": "Nytt passord",
"local_auth_create_new_password_placeholder": "Skriv inn et sterkt passord", "local_auth_create_new_password_placeholder": "Skriv inn et sterkt passord",
"local_auth_create_not_now_button": "Ikke nå", "local_auth_create_not_now_button": "Ikke nå",
"local_auth_create_secure_button": "Sikker enhet", "local_auth_create_secure_button": "Sikre enheten",
"local_auth_create_title": "Lokal enhetsbeskyttelse", "local_auth_create_title": "Lokal enhetsbeskyttelse",
"local_auth_current_password_label": "Nåværende passord", "local_auth_current_password_label": "Nåværende passord",
"local_auth_disable_local_device_protection_description": "Skriv inn ditt nåværende passord for å deaktivere lokal enhetsbeskyttelse.", "local_auth_disable_local_device_protection_description": "Skriv inn ditt nåværende passord for å deaktivere lokal enhetsbeskyttelse.",
@ -396,7 +399,7 @@
"local_auth_success_password_updated_description": "Du har endret passordet for beskyttelse av den lokale enheten. Husk det nye passordet for fremtidig tilgang.", "local_auth_success_password_updated_description": "Du har endret passordet for beskyttelse av den lokale enheten. Husk det nye passordet for fremtidig tilgang.",
"local_auth_success_password_updated_title": "Passord oppdatert", "local_auth_success_password_updated_title": "Passord oppdatert",
"local_auth_update_password_button": "Oppdater passord", "local_auth_update_password_button": "Oppdater passord",
"locale_auto": "Bil", "locale_auto": "Auto",
"locale_change_success": "Språket er endret til {locale}", "locale_change_success": "Språket er endret til {locale}",
"locale_da": "Dansk", "locale_da": "Dansk",
"locale_de": "Tysk", "locale_de": "Tysk",
@ -426,7 +429,8 @@
"macro_name_too_long": "Navnet må være mindre enn 50 tegn", "macro_name_too_long": "Navnet må være mindre enn 50 tegn",
"macro_please_fix_validation_errors": "Vennligst rett opp valideringsfeilene", "macro_please_fix_validation_errors": "Vennligst rett opp valideringsfeilene",
"macro_save": "Lagre makro", "macro_save": "Lagre makro",
"macro_save_error": "Det oppsto en feil under lagring.", "macro_save_failed": "Det oppsto en feil under lagring.",
"macro_save_failed_error": "Det oppsto en feil under lagring: {error}.",
"macro_step_count": "{steps} / {max} trinn", "macro_step_count": "{steps} / {max} trinn",
"macro_step_duration_description": "Tid for å vente før man tar neste steg.", "macro_step_duration_description": "Tid for å vente før man tar neste steg.",
"macro_step_duration_label": "Stegvarighet", "macro_step_duration_label": "Stegvarighet",
@ -549,9 +553,9 @@
"mouse_hide_cursor_description": "Skjul markøren når du sender musebevegelser", "mouse_hide_cursor_description": "Skjul markøren når du sender musebevegelser",
"mouse_hide_cursor_title": "Skjul markør", "mouse_hide_cursor_title": "Skjul markør",
"mouse_jiggler_config_updated": "Jiggler-konfigurasjonen er oppdatert", "mouse_jiggler_config_updated": "Jiggler-konfigurasjonen er oppdatert",
"mouse_jiggler_custom": "Skikk", "mouse_jiggler_custom": "Tilpasset",
"mouse_jiggler_description": "Simuler bevegelsen til en datamus", "mouse_jiggler_description": "Simuler bevegelsen til en datamus",
"mouse_jiggler_disabled": "Funksjonshemmet", "mouse_jiggler_disabled": "Deaktivert",
"mouse_jiggler_error_config": "Det oppsto en feil under innstilling av jiggler-konfigurasjonen", "mouse_jiggler_error_config": "Det oppsto en feil under innstilling av jiggler-konfigurasjonen",
"mouse_jiggler_failed_state": "Klarte ikke å angi jiggler-tilstand: {error}", "mouse_jiggler_failed_state": "Klarte ikke å angi jiggler-tilstand: {error}",
"mouse_jiggler_frequent": "Hyppig - 30-tallet", "mouse_jiggler_frequent": "Hyppig - 30-tallet",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Konfigurer hvilken DHCP-klient som skal brukes", "network_dhcp_client_description": "Konfigurer hvilken DHCP-klient som skal brukes",
"network_dhcp_client_jetkvm": "JetKVM intern", "network_dhcp_client_jetkvm": "JetKVM intern",
"network_dhcp_client_title": "DHCP-klient", "network_dhcp_client_title": "DHCP-klient",
"network_dhcp_information": "DHCP-informasjon",
"network_dhcp_lease_renew": "Forny DHCP-leieavtale", "network_dhcp_lease_renew": "Forny DHCP-leieavtale",
"network_dhcp_lease_renew_confirm": "Forny leieavtalen", "network_dhcp_lease_renew_confirm": "Forny leieavtalen",
"network_dhcp_lease_renew_confirm_description": "Dette vil be om en ny IP-adresse fra DHCP-serveren din. Enheten din kan midlertidig miste nettverkstilkoblingen under denne prosessen.", "network_dhcp_lease_renew_confirm_description": "Dette vil be om en ny IP-adresse fra DHCP-serveren din. Enheten din kan midlertidig miste nettverkstilkoblingen under denne prosessen.",
@ -586,7 +589,7 @@
"network_dhcp_lease_renew_confirm_new_b": "du må kanskje koble til på nytt med den nye adressen", "network_dhcp_lease_renew_confirm_new_b": "du må kanskje koble til på nytt med den nye adressen",
"network_dhcp_lease_renew_failed": "Kunne ikke fornye leieavtalen: {error}", "network_dhcp_lease_renew_failed": "Kunne ikke fornye leieavtalen: {error}",
"network_dhcp_lease_renew_success": "DHCP-leieavtale fornyet", "network_dhcp_lease_renew_success": "DHCP-leieavtale fornyet",
"network_domain_custom": "Skikk", "network_domain_custom": "Tilpasset",
"network_domain_description": "Nettverksdomenesuffiks for enheten", "network_domain_description": "Nettverksdomenesuffiks for enheten",
"network_domain_dhcp_provided": "DHCP levert", "network_domain_dhcp_provided": "DHCP levert",
"network_domain_local": ".lokal", "network_domain_local": ".lokal",
@ -599,39 +602,46 @@
"network_ipv4_address": "IPv4-adresse", "network_ipv4_address": "IPv4-adresse",
"network_ipv4_dns": "IPv4 DNS", "network_ipv4_dns": "IPv4 DNS",
"network_ipv4_gateway": "IPv4-gateway", "network_ipv4_gateway": "IPv4-gateway",
"network_ipv4_invalid": "Ugyldig IPv4-adresse",
"network_ipv4_invalid_cidr": "Ugyldig CIDR-notasjon for IPv4-adresse",
"network_ipv4_mode_description": "Konfigurer IPv4-modusen", "network_ipv4_mode_description": "Konfigurer IPv4-modusen",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statisk", "network_ipv4_mode_static": "Statisk",
"network_ipv4_mode_title": "IPv4-modus", "network_ipv4_mode_title": "IPv4-modus",
"network_ipv4_netmask": "IPv4-nettmaske", "network_ipv4_netmask": "IPv4-nettmaske",
"network_ipv6_address": "IPv6-adresse", "network_ipv6_addresses_header": "IPv6-adresser",
"network_ipv6_cidr_suggestion": "Vennligst bruk CIDR-notasjon (f.eks. 2001:db8::1/64)",
"network_ipv6_dns": "IPv6 DNS",
"network_ipv6_flag_dad_failed": "DAD mislyktes",
"network_ipv6_flag_deprecated": "Utdatert",
"network_ipv6_gateway": "IPv6-gateway",
"network_ipv6_information": "IPv6-informasjon", "network_ipv6_information": "IPv6-informasjon",
"network_ipv6_invalid": "Ugyldig IPv6-adresse",
"network_ipv6_mode_description": "Konfigurer IPv6-modusen", "network_ipv6_mode_description": "Konfigurer IPv6-modusen",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Funksjonshemmet", "network_ipv6_mode_disabled": "Deaktivert",
"network_ipv6_mode_link_local": "Kun lenkelokal", "network_ipv6_mode_link_local": "Kun lenkelokal",
"network_ipv6_mode_slaac": "SLAAC", "network_ipv6_mode_slaac": "SLAAC",
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statisk", "network_ipv6_mode_static": "Statisk",
"network_ipv6_mode_title": "IPv6-modus", "network_ipv6_mode_title": "IPv6-modus",
"network_ipv6_netmask": "IPv6-nettmaske", "network_ipv6_prefix": "IP-prefiks",
"network_ipv6_no_addresses": "Ingen IPv6-adresser konfigurert", "network_ipv6_prefix_invalid": "Prefikset må være mellom 0 og 128",
"network_ll_dp_all": "Alle", "network_ll_dp_all": "Alle",
"network_ll_dp_basic": "Grunnleggende", "network_ll_dp_basic": "Grunnleggende",
"network_ll_dp_description": "Kontroller hvilke TLV-er som skal sendes over Link Layer Discovery Protocol", "network_ll_dp_description": "Kontroller hvilke TLV-er som skal sendes over Link Layer Discovery Protocol",
"network_ll_dp_disabled": "Funksjonshemmet", "network_ll_dp_disabled": "Deaktivert",
"network_ll_dp_title": "LLDP", "network_ll_dp_title": "LLDP",
"network_mac_address_copy_error": "Kunne ikke kopiere MAC-adressen", "network_mac_address_copy_error": "Kunne ikke kopiere MAC-adressen",
"network_mac_address_copy_success": "MAC-adresse { mac } kopiert til utklippstavlen", "network_mac_address_copy_success": "MAC-adresse { mac } kopiert til utklippstavlen",
"network_mac_address_description": "Maskinvareidentifikator for nettverksgrensesnittet", "network_mac_address_description": "Maskinvareidentifikator for nettverksgrensesnittet",
"network_mac_address_title": "MAC-adresse", "network_mac_address_title": "MAC-adresse",
"network_mdns_auto": "Bil", "network_mdns_auto": "Auto",
"network_mdns_description": "Kontrollmodus for mDNS (multicast DNS)", "network_mdns_description": "Kontrollmodus for mDNS (multicast DNS)",
"network_mdns_disabled": "Funksjonshemmet", "network_mdns_disabled": "Deaktivert",
"network_mdns_ipv4_only": "Kun IPv4", "network_mdns_ipv4_only": "Kun IPv4",
"network_mdns_ipv6_only": "Kun IPv6", "network_mdns_ipv6_only": "Kun IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Ingen DHCP-leaseinformasjon tilgjengelig",
"network_no_information_description": "Ingen nettverkskonfigurasjon tilgjengelig", "network_no_information_description": "Ingen nettverkskonfigurasjon tilgjengelig",
"network_no_information_headline": "Nettverksinformasjon", "network_no_information_headline": "Nettverksinformasjon",
"network_pending_dhcp_mode_change_description": "Lagre innstillinger for å aktivere DHCP-modus og vise leieavtaleinformasjon", "network_pending_dhcp_mode_change_description": "Lagre innstillinger for å aktivere DHCP-modus og vise leieavtaleinformasjon",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Konfigurasjonsendringer", "network_save_settings_confirm_heading": "Konfigurasjonsendringer",
"network_save_settings_failed": "Kunne ikke lagre nettverksinnstillinger: {error}", "network_save_settings_failed": "Kunne ikke lagre nettverksinnstillinger: {error}",
"network_save_settings_success": "Nettverksinnstillinger lagret", "network_save_settings_success": "Nettverksinnstillinger lagret",
"network_settings_invalid_ipv4_cidr": "Ugyldig CIDR-notasjon for IPv4-adresse", "network_settings_add_dns": "Legg til DNS-server",
"network_settings_load_error": "Kunne ikke laste inn nettverksinnstillinger: {error}", "network_settings_load_error": "Kunne ikke laste inn nettverksinnstillinger: {error}",
"network_static_ipv4_header": "Statisk IPv4-konfigurasjon",
"network_static_ipv6_header": "Statisk IPv6-konfigurasjon",
"network_time_sync_description": "Konfigurer innstillinger for tidssynkronisering", "network_time_sync_description": "Konfigurer innstillinger for tidssynkronisering",
"network_time_sync_http_only": "Kun HTTP", "network_time_sync_http_only": "Kun HTTP",
"network_time_sync_ntp_and_http": "NTP og HTTP", "network_time_sync_ntp_and_http": "NTP og HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Klarte ikke å lime inn tekst: {error}", "paste_modal_failed_paste": "Klarte ikke å lime inn tekst: {error}",
"paste_modal_invalid_chars_intro": "Følgende tegn vil ikke bli limt inn:", "paste_modal_invalid_chars_intro": "Følgende tegn vil ikke bli limt inn:",
"paste_modal_paste_from_host": "Lim inn fra verten", "paste_modal_paste_from_host": "Lim inn fra verten",
"paste_modal_paste_text": "Lim inn tekst",
"paste_modal_paste_text_description": "Lim inn tekst fra klienten din til den eksterne verten",
"paste_modal_sending_using_layout": "Sende tekst ved hjelp av tastaturoppsett: {iso} - {name}", "paste_modal_sending_using_layout": "Sende tekst ved hjelp av tastaturoppsett: {iso} - {name}",
"paste_text": "Lim inn tekst",
"paste_text_description": "Lim inn tekst fra klienten din til den eksterne verten",
"peer_connection_closed": "Lukket", "peer_connection_closed": "Lukket",
"peer_connection_closing": "Lukking", "peer_connection_closing": "Lukking",
"peer_connection_connected": "Tilkoblet", "peer_connection_connected": "Tilkoblet",
@ -743,7 +755,7 @@
"updates_failed_get_device_version": "Klarte ikke å hente enhetsversjon: {error}", "updates_failed_get_device_version": "Klarte ikke å hente enhetsversjon: {error}",
"updating_leave_device_on": "Vennligst ikke slå av enheten din ...", "updating_leave_device_on": "Vennligst ikke slå av enheten din ...",
"usb": "USB", "usb": "USB",
"usb_config_custom": "Skikk", "usb_config_custom": "Tilpasset",
"usb_config_default": "JetKVM-standard", "usb_config_default": "JetKVM-standard",
"usb_config_dell": "Dell Multimedia Pro-tastatur", "usb_config_dell": "Dell Multimedia Pro-tastatur",
"usb_config_failed_load": "Klarte ikke å laste inn USB-konfigurasjon: {error}", "usb_config_failed_load": "Klarte ikke å laste inn USB-konfigurasjon: {error}",
@ -767,7 +779,7 @@
"usb_config_vendor_id_placeholder": "Skriv inn leverandør-ID", "usb_config_vendor_id_placeholder": "Skriv inn leverandør-ID",
"usb_device_classes_description": "USB-enhetsklasser i den sammensatte enheten", "usb_device_classes_description": "USB-enhetsklasser i den sammensatte enheten",
"usb_device_classes_title": "Klasser", "usb_device_classes_title": "Klasser",
"usb_device_custom": "Skikk", "usb_device_custom": "Tilpasset",
"usb_device_description": "USB-enheter som skal emuleres på måldatamaskinen", "usb_device_description": "USB-enheter som skal emuleres på måldatamaskinen",
"usb_device_enable_absolute_mouse_description": "Aktiver absolutt mus (peker)", "usb_device_enable_absolute_mouse_description": "Aktiver absolutt mus (peker)",
"usb_device_enable_absolute_mouse_title": "Aktiver absolutt mus (peker)", "usb_device_enable_absolute_mouse_title": "Aktiver absolutt mus (peker)",
@ -802,7 +814,7 @@
"video_description": "Konfigurer skjerminnstillinger og EDID for optimal kompatibilitet", "video_description": "Konfigurer skjerminnstillinger og EDID for optimal kompatibilitet",
"video_edid_acer_b246wl": "Acer B246WL, 1920x1200", "video_edid_acer_b246wl": "Acer B246WL, 1920x1200",
"video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200", "video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200",
"video_edid_custom": "Skikk", "video_edid_custom": "Tilpasset",
"video_edid_dell_d2721h": "DELL D2721H, 1920x1080", "video_edid_dell_d2721h": "DELL D2721H, 1920x1080",
"video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024", "video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024",
"video_edid_description": "Juster EDID-innstillingene for skjermen", "video_edid_description": "Juster EDID-innstillingene for skjermen",

View File

@ -31,7 +31,7 @@
"access_no_device_id": "Inget enhets-ID tillgängligt", "access_no_device_id": "Inget enhets-ID tillgängligt",
"access_private_key_description": "Av säkerhetsskäl kommer den inte att visas efter att den har sparats.", "access_private_key_description": "Av säkerhetsskäl kommer den inte att visas efter att den har sparats.",
"access_private_key_label": "Privat nyckel", "access_private_key_label": "Privat nyckel",
"access_provider_custom": "Beställnings", "access_provider_custom": "Anpassad",
"access_provider_jetkvm": "JetKVM-molnet", "access_provider_jetkvm": "JetKVM-molnet",
"access_remote_description": "Hantera läget för fjärråtkomst till enheten", "access_remote_description": "Hantera läget för fjärråtkomst till enheten",
"access_security_encryption": "End-to-end-kryptering med WebRTC (DTLS och SRTP)", "access_security_encryption": "End-to-end-kryptering med WebRTC (DTLS och SRTP)",
@ -42,15 +42,14 @@
"access_title": "Tillträde", "access_title": "Tillträde",
"access_tls_certificate_description": "Klistra in ditt TLS-certifikat nedan. För certifikatkedjor, inkludera hela kedjan (löv-, mellan- och rotcertifikat).", "access_tls_certificate_description": "Klistra in ditt TLS-certifikat nedan. För certifikatkedjor, inkludera hela kedjan (löv-, mellan- och rotcertifikat).",
"access_tls_certificate_title": "TLS-certifikat", "access_tls_certificate_title": "TLS-certifikat",
"access_tls_custom": "Beställnings", "access_tls_custom": "Anpassad",
"access_tls_disabled": "Funktionshindrad", "access_tls_disabled": "Inaktiverad",
"access_tls_self_signed": "Självsignerad", "access_tls_self_signed": "Självsignerad",
"access_tls_updated": "TLS-inställningarna har uppdaterats", "access_tls_updated": "TLS-inställningarna har uppdaterats",
"access_update_tls_settings": "Uppdatera TLS-inställningar", "access_update_tls_settings": "Uppdatera TLS-inställningar",
"action_bar_connection_stats": "Anslutningsstatistik", "action_bar_connection_stats": "Anslutningsstatistik",
"action_bar_extension": "Förlängning", "action_bar_extension": "Förlängning",
"action_bar_fullscreen": "Helskärm", "action_bar_fullscreen": "Helskärm",
"action_bar_paste_text": "Klistra in text",
"action_bar_settings": "Inställningar", "action_bar_settings": "Inställningar",
"action_bar_virtual_keyboard": "Virtuellt tangentbord", "action_bar_virtual_keyboard": "Virtuellt tangentbord",
"action_bar_virtual_media": "Virtuella medier", "action_bar_virtual_media": "Virtuella medier",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "Bekräfta ditt lösenord", "auth_mode_local_password_confirm_description": "Bekräfta ditt lösenord",
"auth_mode_local_password_confirm_label": "Bekräfta lösenord", "auth_mode_local_password_confirm_label": "Bekräfta lösenord",
"auth_mode_local_password_description": "Säkra din enhet med ett lösenord för extra skydd.", "auth_mode_local_password_description": "Säkra din enhet med ett lösenord för extra skydd.",
"auth_mode_local_password_do_not_match": "Lösenorden matchar inte",
"auth_mode_local_password_failed_set": "Misslyckades med att ange lösenord: {error}", "auth_mode_local_password_failed_set": "Misslyckades med att ange lösenord: {error}",
"auth_mode_local_password_note": "Detta lösenord kommer att användas för att säkra dina enhetsdata och skydda mot obehörig åtkomst.", "auth_mode_local_password_note": "Detta lösenord kommer att användas för att säkra dina enhetsdata och skydda mot obehörig åtkomst.",
"auth_mode_local_password_note_local": "All data finns kvar på din lokala enhet.", "auth_mode_local_password_note_local": "All data finns kvar på din lokala enhet.",
@ -156,8 +154,8 @@
"auth_signup_create_account_description": "Skapa ditt konto och börja enkelt hantera dina enheter.", "auth_signup_create_account_description": "Skapa ditt konto och börja enkelt hantera dina enheter.",
"back": "Tillbaka", "back": "Tillbaka",
"back_to_devices": "Tillbaka till Enheter", "back_to_devices": "Tillbaka till Enheter",
"cancel": "Avboka", "cancel": "Avbryt",
"close": "Nära", "close": "Stäng",
"cloud_kvms": "Moln-KVM:er", "cloud_kvms": "Moln-KVM:er",
"cloud_kvms_description": "Hantera dina moln-KVM:er och anslut till dem säkert.", "cloud_kvms_description": "Hantera dina moln-KVM:er och anslut till dem säkert.",
"cloud_kvms_no_devices": "Inga enheter hittades", "cloud_kvms_no_devices": "Inga enheter hittades",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "Tur- och returtid", "connection_stats_round_trip_time": "Tur- och returtid",
"connection_stats_round_trip_time_description": "Tur- och returtid för det aktiva ICE-kandidatparet mellan peers.", "connection_stats_round_trip_time_description": "Tur- och returtid för det aktiva ICE-kandidatparet mellan peers.",
"connection_stats_sidebar": "Anslutningsstatistik", "connection_stats_sidebar": "Anslutningsstatistik",
"connection_stats_unit_frames_per_second": " fps",
"connection_stats_unit_milliseconds": " ms",
"connection_stats_unit_packets": " paket",
"connection_stats_video": "Video", "connection_stats_video": "Video",
"connection_stats_video_description": "Videoströmmen från JetKVM till klienten.", "connection_stats_video_description": "Videoströmmen från JetKVM till klienten.",
"continue": "Fortsätta", "continue": "Fortsätta",
@ -207,6 +208,8 @@
"deregister_error": "Det uppstod ett fel {status} enheten avregistrerades. Försök igen.", "deregister_error": "Det uppstod ett fel {status} enheten avregistrerades. Försök igen.",
"deregister_headline": "Avregistrera {device} från ditt molnkonto", "deregister_headline": "Avregistrera {device} från ditt molnkonto",
"detach": "Lösgöra", "detach": "Lösgöra",
"dhcp_empty_lease_description": "Vi har inte mottagit någon DHCP-leaseinformation från enheten ännu.",
"dhcp_empty_lease_headline": "Ingen DHCP-leaseinformation",
"dhcp_lease_boot_file": "Startfil", "dhcp_lease_boot_file": "Startfil",
"dhcp_lease_boot_next_server": "Starta nästa server", "dhcp_lease_boot_next_server": "Starta nästa server",
"dhcp_lease_boot_server_name": "Namn på startserver", "dhcp_lease_boot_server_name": "Namn på startserver",
@ -371,7 +374,7 @@
"local_auth_create_new_password_label": "Nytt lösenord", "local_auth_create_new_password_label": "Nytt lösenord",
"local_auth_create_new_password_placeholder": "Ange ett starkt lösenord", "local_auth_create_new_password_placeholder": "Ange ett starkt lösenord",
"local_auth_create_not_now_button": "Inte nu", "local_auth_create_not_now_button": "Inte nu",
"local_auth_create_secure_button": "Säker enhet", "local_auth_create_secure_button": "Säkra enheten",
"local_auth_create_title": "Lokalt enhetsskydd", "local_auth_create_title": "Lokalt enhetsskydd",
"local_auth_current_password_label": "Nuvarande lösenord", "local_auth_current_password_label": "Nuvarande lösenord",
"local_auth_disable_local_device_protection_description": "Ange ditt nuvarande lösenord för att inaktivera lokalt enhetsskydd.", "local_auth_disable_local_device_protection_description": "Ange ditt nuvarande lösenord för att inaktivera lokalt enhetsskydd.",
@ -396,14 +399,14 @@
"local_auth_success_password_updated_description": "Du har ändrat ditt lösenord för lokal enhetsskydd. Se till att komma ihåg ditt nya lösenord för framtida åtkomst.", "local_auth_success_password_updated_description": "Du har ändrat ditt lösenord för lokal enhetsskydd. Se till att komma ihåg ditt nya lösenord för framtida åtkomst.",
"local_auth_success_password_updated_title": "Lösenordet har uppdaterats", "local_auth_success_password_updated_title": "Lösenordet har uppdaterats",
"local_auth_update_password_button": "Uppdatera lösenord", "local_auth_update_password_button": "Uppdatera lösenord",
"locale_auto": "Bil", "locale_auto": "Auto",
"locale_change_success": "Språket har ändrats till {locale}", "locale_change_success": "Språket har ändrats till {locale}",
"locale_da": "Danska", "locale_da": "Danska",
"locale_de": "Deutsch", "locale_de": "Tyska",
"locale_en": "Engelska", "locale_en": "Engelska",
"locale_es": "Spanska", "locale_es": "Spanska",
"locale_fr": "Franska", "locale_fr": "Franska",
"locale_it": "italiensk", "locale_it": "Italienska",
"locale_nb": "Norska (bokmål)", "locale_nb": "Norska (bokmål)",
"locale_sv": "Svenska", "locale_sv": "Svenska",
"locale_zh": "中文 (简体)", "locale_zh": "中文 (简体)",
@ -426,7 +429,8 @@
"macro_name_too_long": "Namnet måste vara kortare än 50 tecken", "macro_name_too_long": "Namnet måste vara kortare än 50 tecken",
"macro_please_fix_validation_errors": "Vänligen åtgärda valideringsfelen", "macro_please_fix_validation_errors": "Vänligen åtgärda valideringsfelen",
"macro_save": "Spara makro", "macro_save": "Spara makro",
"macro_save_error": "Ett fel uppstod när dokumentet skulle sparas.", "macro_save_failed": "Ett fel uppstod när dokumentet skulle sparas.",
"macro_save_failed_error": "Ett fel uppstod när dokumentet skulle sparas: {error}.",
"macro_step_count": "{steps} / {max} steg", "macro_step_count": "{steps} / {max} steg",
"macro_step_duration_description": "Dags att vänta innan nästa steg genomförs.", "macro_step_duration_description": "Dags att vänta innan nästa steg genomförs.",
"macro_step_duration_label": "Steglängd", "macro_step_duration_label": "Steglängd",
@ -549,9 +553,9 @@
"mouse_hide_cursor_description": "Dölj markören när du skickar musrörelser", "mouse_hide_cursor_description": "Dölj markören när du skickar musrörelser",
"mouse_hide_cursor_title": "Dölj markören", "mouse_hide_cursor_title": "Dölj markören",
"mouse_jiggler_config_updated": "Jiggler-konfigurationen har uppdaterats", "mouse_jiggler_config_updated": "Jiggler-konfigurationen har uppdaterats",
"mouse_jiggler_custom": "Beställnings", "mouse_jiggler_custom": "Anpassad",
"mouse_jiggler_description": "Simulera rörelsen hos en datormus", "mouse_jiggler_description": "Simulera rörelsen hos en datormus",
"mouse_jiggler_disabled": "Funktionshindrad", "mouse_jiggler_disabled": "Inaktiverad",
"mouse_jiggler_error_config": "Det uppstod ett fel vid konfiguration av jiggler", "mouse_jiggler_error_config": "Det uppstod ett fel vid konfiguration av jiggler",
"mouse_jiggler_failed_state": "Misslyckades med att ställa in jiggler-tillstånd: {error}", "mouse_jiggler_failed_state": "Misslyckades med att ställa in jiggler-tillstånd: {error}",
"mouse_jiggler_frequent": "Frekvent - 30-talet", "mouse_jiggler_frequent": "Frekvent - 30-talet",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "Konfigurera vilken DHCP-klient som ska användas", "network_dhcp_client_description": "Konfigurera vilken DHCP-klient som ska användas",
"network_dhcp_client_jetkvm": "JetKVM Intern", "network_dhcp_client_jetkvm": "JetKVM Intern",
"network_dhcp_client_title": "DHCP-klient", "network_dhcp_client_title": "DHCP-klient",
"network_dhcp_information": "DHCP-information",
"network_dhcp_lease_renew": "Förnya DHCP-lease", "network_dhcp_lease_renew": "Förnya DHCP-lease",
"network_dhcp_lease_renew_confirm": "Förnya hyresavtalet", "network_dhcp_lease_renew_confirm": "Förnya hyresavtalet",
"network_dhcp_lease_renew_confirm_description": "Detta kommer att begära en ny IP-adress från din DHCP-server. Din enhet kan tillfälligt förlora nätverksanslutningen under denna process.", "network_dhcp_lease_renew_confirm_description": "Detta kommer att begära en ny IP-adress från din DHCP-server. Din enhet kan tillfälligt förlora nätverksanslutningen under denna process.",
@ -586,7 +589,7 @@
"network_dhcp_lease_renew_confirm_new_b": "du kan behöva återansluta med den nya adressen", "network_dhcp_lease_renew_confirm_new_b": "du kan behöva återansluta med den nya adressen",
"network_dhcp_lease_renew_failed": "Misslyckades med att förnya leasingavtalet: {error}", "network_dhcp_lease_renew_failed": "Misslyckades med att förnya leasingavtalet: {error}",
"network_dhcp_lease_renew_success": "DHCP-lease förnyad", "network_dhcp_lease_renew_success": "DHCP-lease förnyad",
"network_domain_custom": "Beställnings", "network_domain_custom": "Anpassad",
"network_domain_description": "Nätverksdomänsuffix för enheten", "network_domain_description": "Nätverksdomänsuffix för enheten",
"network_domain_dhcp_provided": "DHCP tillhandahålls", "network_domain_dhcp_provided": "DHCP tillhandahålls",
"network_domain_local": ".lokal", "network_domain_local": ".lokal",
@ -599,39 +602,46 @@
"network_ipv4_address": "IPv4-adress", "network_ipv4_address": "IPv4-adress",
"network_ipv4_dns": "IPv4 DNS", "network_ipv4_dns": "IPv4 DNS",
"network_ipv4_gateway": "IPv4-gateway", "network_ipv4_gateway": "IPv4-gateway",
"network_ipv4_invalid": "Ogiltig IPv4-adress",
"network_ipv4_invalid_cidr": "Ogiltig CIDR-notation för IPv4-adress",
"network_ipv4_mode_description": "Konfigurera IPv4-läget", "network_ipv4_mode_description": "Konfigurera IPv4-läget",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "Statisk", "network_ipv4_mode_static": "Statisk",
"network_ipv4_mode_title": "IPv4-läge", "network_ipv4_mode_title": "IPv4-läge",
"network_ipv4_netmask": "IPv4-nätmask", "network_ipv4_netmask": "IPv4-nätmask",
"network_ipv6_address": "IPv6-adress", "network_ipv6_addresses_header": "IPv6-adresser",
"network_ipv6_cidr_suggestion": "Använd CIDR-notation (t.ex. 2001:db8::1/64)",
"network_ipv6_dns": "IPv6 DNS",
"network_ipv6_flag_dad_failed": "DAD misslyckades",
"network_ipv6_flag_deprecated": "Föråldrad",
"network_ipv6_gateway": "IPv6-gateway",
"network_ipv6_information": "IPv6-information", "network_ipv6_information": "IPv6-information",
"network_ipv6_invalid": "Ogiltig IPv6-adress",
"network_ipv6_mode_description": "Konfigurera IPv6-läget", "network_ipv6_mode_description": "Konfigurera IPv6-läget",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "Funktionshindrad", "network_ipv6_mode_disabled": "Inaktiverad",
"network_ipv6_mode_link_local": "Endast länklokal", "network_ipv6_mode_link_local": "Endast länklokal",
"network_ipv6_mode_slaac": "SLAAC", "network_ipv6_mode_slaac": "SLAAC",
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "Statisk", "network_ipv6_mode_static": "Statisk",
"network_ipv6_mode_title": "IPv6-läge", "network_ipv6_mode_title": "IPv6-läge",
"network_ipv6_netmask": "IPv6-nätmask", "network_ipv6_prefix": "IP-prefix",
"network_ipv6_no_addresses": "Inga IPv6-adresser konfigurerade", "network_ipv6_prefix_invalid": "Prefixet måste vara mellan 0 och 128",
"network_ll_dp_all": "Alla", "network_ll_dp_all": "Alla",
"network_ll_dp_basic": "Grundläggande", "network_ll_dp_basic": "Grundläggande",
"network_ll_dp_description": "Kontrollera vilka TLV:er som ska skickas via Link Layer Discovery Protocol", "network_ll_dp_description": "Kontrollera vilka TLV:er som ska skickas via Link Layer Discovery Protocol",
"network_ll_dp_disabled": "Funktionshindrad", "network_ll_dp_disabled": "Inaktiverad",
"network_ll_dp_title": "LLDP", "network_ll_dp_title": "LLDP",
"network_mac_address_copy_error": "Misslyckades med att kopiera MAC-adressen", "network_mac_address_copy_error": "Misslyckades med att kopiera MAC-adressen",
"network_mac_address_copy_success": "MAC-adress { mac } kopierad till urklipp", "network_mac_address_copy_success": "MAC-adress { mac } kopierad till urklipp",
"network_mac_address_description": "Maskinvaruidentifierare för nätverksgränssnittet", "network_mac_address_description": "Maskinvaruidentifierare för nätverksgränssnittet",
"network_mac_address_title": "MAC-adress", "network_mac_address_title": "MAC-adress",
"network_mdns_auto": "Bil", "network_mdns_auto": "Auto",
"network_mdns_description": "Kontroll av mDNS (multicast DNS) driftläge", "network_mdns_description": "Kontroll av mDNS (multicast DNS) driftläge",
"network_mdns_disabled": "Funktionshindrad", "network_mdns_disabled": "Inaktiverad",
"network_mdns_ipv4_only": "Endast IPv4", "network_mdns_ipv4_only": "Endast IPv4",
"network_mdns_ipv6_only": "Endast IPv6", "network_mdns_ipv6_only": "Endast IPv6",
"network_mdns_title": "mDNS", "network_mdns_title": "mDNS",
"network_no_dhcp_lease": "Ingen DHCP-leaseinformation tillgänglig",
"network_no_information_description": "Ingen nätverkskonfiguration tillgänglig", "network_no_information_description": "Ingen nätverkskonfiguration tillgänglig",
"network_no_information_headline": "Nätverksinformation", "network_no_information_headline": "Nätverksinformation",
"network_pending_dhcp_mode_change_description": "Spara inställningar för att aktivera DHCP-läge och visa leasinginformation", "network_pending_dhcp_mode_change_description": "Spara inställningar för att aktivera DHCP-läge och visa leasinginformation",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "Konfigurationsändringar", "network_save_settings_confirm_heading": "Konfigurationsändringar",
"network_save_settings_failed": "Misslyckades med att spara nätverksinställningar: {error}", "network_save_settings_failed": "Misslyckades med att spara nätverksinställningar: {error}",
"network_save_settings_success": "Nätverksinställningar sparade", "network_save_settings_success": "Nätverksinställningar sparade",
"network_settings_invalid_ipv4_cidr": "Ogiltig CIDR-notation för IPv4-adress", "network_settings_add_dns": "Lägg till DNS-server",
"network_settings_load_error": "Misslyckades med att läsa in nätverksinställningar: {error}", "network_settings_load_error": "Misslyckades med att läsa in nätverksinställningar: {error}",
"network_static_ipv4_header": "Statisk IPv4-konfiguration",
"network_static_ipv6_header": "Statisk IPv6-konfiguration",
"network_time_sync_description": "Konfigurera inställningar för tidssynkronisering", "network_time_sync_description": "Konfigurera inställningar för tidssynkronisering",
"network_time_sync_http_only": "Endast HTTP", "network_time_sync_http_only": "Endast HTTP",
"network_time_sync_ntp_and_http": "NTP och HTTP", "network_time_sync_ntp_and_http": "NTP och HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "Misslyckades med att klistra in text: {error}", "paste_modal_failed_paste": "Misslyckades med att klistra in text: {error}",
"paste_modal_invalid_chars_intro": "Följande tecken klistras inte in:", "paste_modal_invalid_chars_intro": "Följande tecken klistras inte in:",
"paste_modal_paste_from_host": "Klistra in från värd", "paste_modal_paste_from_host": "Klistra in från värd",
"paste_modal_paste_text": "Klistra in text",
"paste_modal_paste_text_description": "Klistra in text från din klient till fjärrdatorn",
"paste_modal_sending_using_layout": "Skicka text med tangentbordslayout: {iso} - {name}", "paste_modal_sending_using_layout": "Skicka text med tangentbordslayout: {iso} - {name}",
"paste_text": "Klistra in text",
"paste_text_description": "Klistra in text från din klient till fjärrdatorn",
"peer_connection_closed": "Stängd", "peer_connection_closed": "Stängd",
"peer_connection_closing": "Stängning", "peer_connection_closing": "Stängning",
"peer_connection_connected": "Ansluten", "peer_connection_connected": "Ansluten",
@ -743,7 +755,7 @@
"updates_failed_get_device_version": "Misslyckades med att hämta enhetsversionen: {error}", "updates_failed_get_device_version": "Misslyckades med att hämta enhetsversionen: {error}",
"updating_leave_device_on": "Stäng inte av din enhet…", "updating_leave_device_on": "Stäng inte av din enhet…",
"usb": "USB", "usb": "USB",
"usb_config_custom": "Beställnings", "usb_config_custom": "Anpassad",
"usb_config_default": "JetKVM-standard", "usb_config_default": "JetKVM-standard",
"usb_config_dell": "Dell Multimedia Pro-tangentbord", "usb_config_dell": "Dell Multimedia Pro-tangentbord",
"usb_config_failed_load": "Misslyckades med att ladda USB-konfigurationen: {error}", "usb_config_failed_load": "Misslyckades med att ladda USB-konfigurationen: {error}",
@ -767,7 +779,7 @@
"usb_config_vendor_id_placeholder": "Ange leverantörs-ID", "usb_config_vendor_id_placeholder": "Ange leverantörs-ID",
"usb_device_classes_description": "USB-enhetsklasser i den sammansatta enheten", "usb_device_classes_description": "USB-enhetsklasser i den sammansatta enheten",
"usb_device_classes_title": "Klasser", "usb_device_classes_title": "Klasser",
"usb_device_custom": "Beställnings", "usb_device_custom": "Anpassad",
"usb_device_description": "USB-enheter att emulera på måldatorn", "usb_device_description": "USB-enheter att emulera på måldatorn",
"usb_device_enable_absolute_mouse_description": "Aktivera absolut mus (pekare)", "usb_device_enable_absolute_mouse_description": "Aktivera absolut mus (pekare)",
"usb_device_enable_absolute_mouse_title": "Aktivera absolut mus (pekare)", "usb_device_enable_absolute_mouse_title": "Aktivera absolut mus (pekare)",
@ -802,7 +814,7 @@
"video_description": "Konfigurera skärminställningar och EDID för optimal kompatibilitet", "video_description": "Konfigurera skärminställningar och EDID för optimal kompatibilitet",
"video_edid_acer_b246wl": "Acer B246WL, 1920x1200", "video_edid_acer_b246wl": "Acer B246WL, 1920x1200",
"video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200", "video_edid_asus_pa248qv": "ASUS PA248QV, 1920x1200",
"video_edid_custom": "Beställnings", "video_edid_custom": "Anpassad",
"video_edid_dell_d2721h": "DELL D2721H, 1920x1080", "video_edid_dell_d2721h": "DELL D2721H, 1920x1080",
"video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024", "video_edid_dell_idrac": "DELL IDRAC EDID, 1280x1024",
"video_edid_description": "Justera EDID-inställningarna för skärmen", "video_edid_description": "Justera EDID-inställningarna för skärmen",

View File

@ -50,7 +50,6 @@
"action_bar_connection_stats": "连接统计", "action_bar_connection_stats": "连接统计",
"action_bar_extension": "扩展", "action_bar_extension": "扩展",
"action_bar_fullscreen": "全屏", "action_bar_fullscreen": "全屏",
"action_bar_paste_text": "粘贴文本",
"action_bar_settings": "设置", "action_bar_settings": "设置",
"action_bar_virtual_keyboard": "虚拟键盘", "action_bar_virtual_keyboard": "虚拟键盘",
"action_bar_virtual_media": "虚拟媒体", "action_bar_virtual_media": "虚拟媒体",
@ -142,7 +141,6 @@
"auth_mode_local_password_confirm_description": "确认您的密码", "auth_mode_local_password_confirm_description": "确认您的密码",
"auth_mode_local_password_confirm_label": "确认密码", "auth_mode_local_password_confirm_label": "确认密码",
"auth_mode_local_password_description": "使用密码保护您的设备以增强保护。", "auth_mode_local_password_description": "使用密码保护您的设备以增强保护。",
"auth_mode_local_password_do_not_match": "密码不匹配",
"auth_mode_local_password_failed_set": "无法设置密码: {error}", "auth_mode_local_password_failed_set": "无法设置密码: {error}",
"auth_mode_local_password_note": "此密码将用于保护您的设备数据并防止未经授权的访问。", "auth_mode_local_password_note": "此密码将用于保护您的设备数据并防止未经授权的访问。",
"auth_mode_local_password_note_local": "所有数据都保留在您的本地设备上。", "auth_mode_local_password_note_local": "所有数据都保留在您的本地设备上。",
@ -181,6 +179,9 @@
"connection_stats_round_trip_time": "往返时间", "connection_stats_round_trip_time": "往返时间",
"connection_stats_round_trip_time_description": "对等体之间活跃 ICE 候选对的往返时间。", "connection_stats_round_trip_time_description": "对等体之间活跃 ICE 候选对的往返时间。",
"connection_stats_sidebar": "连接统计", "connection_stats_sidebar": "连接统计",
"connection_stats_unit_frames_per_second": " 帧每秒",
"connection_stats_unit_milliseconds": " 毫秒",
"connection_stats_unit_packets": " 数据包",
"connection_stats_video": "视频", "connection_stats_video": "视频",
"connection_stats_video_description": "从 JetKVM 到客户端的视频流。", "connection_stats_video_description": "从 JetKVM 到客户端的视频流。",
"continue": "继续", "continue": "继续",
@ -207,6 +208,8 @@
"deregister_error": "注销您的设备时出现错误{status} 。请重试。", "deregister_error": "注销您的设备时出现错误{status} 。请重试。",
"deregister_headline": "从您的云帐户中取消注册{device}", "deregister_headline": "从您的云帐户中取消注册{device}",
"detach": "分离", "detach": "分离",
"dhcp_empty_lease_description": "我们尚未收到来自该设备的任何 DHCP 租约信息。",
"dhcp_empty_lease_headline": "无 DHCP 租约信息",
"dhcp_lease_boot_file": "引导文件", "dhcp_lease_boot_file": "引导文件",
"dhcp_lease_boot_next_server": "启动下一个服务器", "dhcp_lease_boot_next_server": "启动下一个服务器",
"dhcp_lease_boot_server_name": "启动服务器名称", "dhcp_lease_boot_server_name": "启动服务器名称",
@ -426,7 +429,8 @@
"macro_name_too_long": "名称必须少于 50 个字符", "macro_name_too_long": "名称必须少于 50 个字符",
"macro_please_fix_validation_errors": "请修复验证错误", "macro_please_fix_validation_errors": "请修复验证错误",
"macro_save": "保存宏", "macro_save": "保存宏",
"macro_save_error": "保存时发生错误。", "macro_save_failed": "保存时发生错误。",
"macro_save_failed_error": "保存时发生错误:{error}。",
"macro_step_count": "{steps} / {max}步骤", "macro_step_count": "{steps} / {max}步骤",
"macro_step_duration_description": "执行下一步之前需要等待的时间。", "macro_step_duration_description": "执行下一步之前需要等待的时间。",
"macro_step_duration_label": "步骤持续时间", "macro_step_duration_label": "步骤持续时间",
@ -578,7 +582,6 @@
"network_dhcp_client_description": "配置要使用的 DHCP 客户端", "network_dhcp_client_description": "配置要使用的 DHCP 客户端",
"network_dhcp_client_jetkvm": "JetKVM 内部", "network_dhcp_client_jetkvm": "JetKVM 内部",
"network_dhcp_client_title": "DHCP客户端", "network_dhcp_client_title": "DHCP客户端",
"network_dhcp_information": "DHCP 信息",
"network_dhcp_lease_renew": "续订 DHCP 租约", "network_dhcp_lease_renew": "续订 DHCP 租约",
"network_dhcp_lease_renew_confirm": "续租", "network_dhcp_lease_renew_confirm": "续租",
"network_dhcp_lease_renew_confirm_description": "这将从您的 DHCP 服务器请求新的 IP 地址。在此过程中,您的设备可能会暂时失去网络连接。", "network_dhcp_lease_renew_confirm_description": "这将从您的 DHCP 服务器请求新的 IP 地址。在此过程中,您的设备可能会暂时失去网络连接。",
@ -599,13 +602,21 @@
"network_ipv4_address": "IPv4 地址", "network_ipv4_address": "IPv4 地址",
"network_ipv4_dns": "IPv4 域名服务器", "network_ipv4_dns": "IPv4 域名服务器",
"network_ipv4_gateway": "IPv4 网关", "network_ipv4_gateway": "IPv4 网关",
"network_ipv4_invalid": "IPv4 地址无效",
"network_ipv4_invalid_cidr": "IPv4 地址的 CIDR 表示法无效",
"network_ipv4_mode_description": "配置 IPv4 模式", "network_ipv4_mode_description": "配置 IPv4 模式",
"network_ipv4_mode_dhcp": "DHCP", "network_ipv4_mode_dhcp": "DHCP",
"network_ipv4_mode_static": "静止的", "network_ipv4_mode_static": "静止的",
"network_ipv4_mode_title": "IPv4 模式", "network_ipv4_mode_title": "IPv4 模式",
"network_ipv4_netmask": "IPv4 网络掩码", "network_ipv4_netmask": "IPv4 网络掩码",
"network_ipv6_address": "IPv6 地址", "network_ipv6_addresses_header": "IPv6 地址",
"network_ipv6_cidr_suggestion": "请使用 CIDR 表示法例如2001:db8::1/64",
"network_ipv6_dns": "IPv6 域名服务器",
"network_ipv6_flag_dad_failed": "DAD 失败",
"network_ipv6_flag_deprecated": "已弃用",
"network_ipv6_gateway": "IPv6网关",
"network_ipv6_information": "IPv6 信息", "network_ipv6_information": "IPv6 信息",
"network_ipv6_invalid": "IPv6 地址无效",
"network_ipv6_mode_description": "配置 IPv6 模式", "network_ipv6_mode_description": "配置 IPv6 模式",
"network_ipv6_mode_dhcpv6": "DHCPv6", "network_ipv6_mode_dhcpv6": "DHCPv6",
"network_ipv6_mode_disabled": "已禁用", "network_ipv6_mode_disabled": "已禁用",
@ -614,8 +625,8 @@
"network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6", "network_ipv6_mode_slaac_dhcpv6": "SLAAC + DHCPv6",
"network_ipv6_mode_static": "静止的", "network_ipv6_mode_static": "静止的",
"network_ipv6_mode_title": "IPv6模式", "network_ipv6_mode_title": "IPv6模式",
"network_ipv6_netmask": "IPv6 网络掩码", "network_ipv6_prefix": "IP 前缀",
"network_ipv6_no_addresses": "未配置 IPv6 地址", "network_ipv6_prefix_invalid": "前缀必须介于 0 到 128 之间",
"network_ll_dp_all": "全部", "network_ll_dp_all": "全部",
"network_ll_dp_basic": "基本的", "network_ll_dp_basic": "基本的",
"network_ll_dp_description": "控制哪些 TLV 将通过链路层发现协议发送", "network_ll_dp_description": "控制哪些 TLV 将通过链路层发现协议发送",
@ -631,7 +642,6 @@
"network_mdns_ipv4_only": "仅限 IPv4", "network_mdns_ipv4_only": "仅限 IPv4",
"network_mdns_ipv6_only": "仅限 IPv6", "network_mdns_ipv6_only": "仅限 IPv6",
"network_mdns_title": "移动DNS", "network_mdns_title": "移动DNS",
"network_no_dhcp_lease": "没有可用的 DHCP 租约信息",
"network_no_information_description": "没有可用的网络配置", "network_no_information_description": "没有可用的网络配置",
"network_no_information_headline": "网络信息", "network_no_information_headline": "网络信息",
"network_pending_dhcp_mode_change_description": "保存设置以启用 DHCP 模式并查看租约信息", "network_pending_dhcp_mode_change_description": "保存设置以启用 DHCP 模式并查看租约信息",
@ -643,8 +653,10 @@
"network_save_settings_confirm_heading": "配置更改", "network_save_settings_confirm_heading": "配置更改",
"network_save_settings_failed": "无法保存网络设置: {error}", "network_save_settings_failed": "无法保存网络设置: {error}",
"network_save_settings_success": "网络设置已保存", "network_save_settings_success": "网络设置已保存",
"network_settings_invalid_ipv4_cidr": "IPv4 地址的 CIDR 表示法无效", "network_settings_add_dns": "添加 DNS 服务器",
"network_settings_load_error": "无法加载网络设置: {error}", "network_settings_load_error": "无法加载网络设置: {error}",
"network_static_ipv4_header": "静态 IPv4 配置",
"network_static_ipv6_header": "静态 IPv6 配置",
"network_time_sync_description": "配置时间同步设置", "network_time_sync_description": "配置时间同步设置",
"network_time_sync_http_only": "仅 HTTP", "network_time_sync_http_only": "仅 HTTP",
"network_time_sync_ntp_and_http": "NTP 和 HTTP", "network_time_sync_ntp_and_http": "NTP 和 HTTP",
@ -670,9 +682,9 @@
"paste_modal_failed_paste": "粘贴文本失败: {error}", "paste_modal_failed_paste": "粘贴文本失败: {error}",
"paste_modal_invalid_chars_intro": "以下字符将不会被粘贴:", "paste_modal_invalid_chars_intro": "以下字符将不会被粘贴:",
"paste_modal_paste_from_host": "从主机粘贴", "paste_modal_paste_from_host": "从主机粘贴",
"paste_modal_paste_text": "粘贴文本",
"paste_modal_paste_text_description": "将文本从客户端粘贴到远程主机",
"paste_modal_sending_using_layout": "使用键盘布局发送文本: {iso} - {name}", "paste_modal_sending_using_layout": "使用键盘布局发送文本: {iso} - {name}",
"paste_text": "粘贴文本",
"paste_text_description": "将文本从客户端粘贴到远程主机",
"peer_connection_closed": "关闭", "peer_connection_closed": "关闭",
"peer_connection_closing": "结束语", "peer_connection_closing": "结束语",
"peer_connection_connected": "已连接", "peer_connection_connected": "已连接",

45
ui/package-lock.json generated
View File

@ -1,16 +1,17 @@
{ {
"name": "kvm-ui", "name": "kvm-ui",
"version": "2025.10.14.2130", "version": "2025.10.15.1700",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "kvm-ui", "name": "kvm-ui",
"version": "2025.10.14.2130", "version": "2025.10.15.1700",
"dependencies": { "dependencies": {
"@headlessui/react": "^2.2.9", "@headlessui/react": "^2.2.9",
"@headlessui/tailwindcss": "^0.2.2", "@headlessui/tailwindcss": "^0.2.2",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-basic-ssl": "^2.1.0", "@vitejs/plugin-basic-ssl": "^2.1.0",
"@xterm/addon-clipboard": "^0.1.0", "@xterm/addon-clipboard": "^0.1.0",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
@ -68,7 +69,7 @@
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.0", "eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.23", "eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.4.0", "globals": "^16.4.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",
@ -126,6 +127,7 @@
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3", "@babel/generator": "^7.28.3",
@ -2405,6 +2407,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
@ -2414,6 +2417,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@ -2431,6 +2435,12 @@
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
"license": "MIT"
},
"node_modules/@types/validator": { "node_modules/@types/validator": {
"version": "13.15.3", "version": "13.15.3",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz",
@ -2484,6 +2494,7 @@
"integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/scope-manager": "8.46.1",
"@typescript-eslint/types": "8.46.1", "@typescript-eslint/types": "8.46.1",
@ -2790,13 +2801,15 @@
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.15.0", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -3132,6 +3145,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.8.9", "baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746", "caniuse-lite": "^1.0.30001746",
@ -3371,7 +3385,8 @@
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/cva": { "node_modules/cva": {
"version": "1.0.0-beta.4", "version": "1.0.0-beta.4",
@ -3967,6 +3982,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@ -4028,6 +4044,7 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"eslint-config-prettier": "bin/cli.js" "eslint-config-prettier": "bin/cli.js"
}, },
@ -4101,6 +4118,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@rtsao/scc": "^1.1.0", "@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9", "array-includes": "^3.1.9",
@ -5523,6 +5541,7 @@
"integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==", "integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
} }
@ -6247,6 +6266,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@ -6292,6 +6312,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
}, },
@ -6448,6 +6469,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -6470,6 +6492,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@ -6531,6 +6554,7 @@
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/use-sync-external-store": "^0.0.6", "@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0" "use-sync-external-store": "^1.4.0"
@ -6627,7 +6651,8 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/redux-thunk": { "node_modules/redux-thunk": {
"version": "3.1.0", "version": "3.1.0",
@ -7217,7 +7242,8 @@
"version": "4.1.14", "version": "4.1.14",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz",
"integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.3.0", "version": "2.3.0",
@ -7304,6 +7330,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -7480,6 +7507,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -7663,6 +7691,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz",
"integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@ -7774,6 +7803,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -7922,6 +7952,7 @@
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "kvm-ui", "name": "kvm-ui",
"private": true, "private": true,
"version": "2025.10.14.2130", "version": "2025.10.15.1700",
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^22.20.0" "node": "^22.20.0"
@ -16,14 +16,20 @@
"build:prod": "npm run paraglide && tsc && vite build --mode=cloud-production", "build:prod": "npm run paraglide && tsc && vite build --mode=cloud-production",
"lint": "npm run paraglide && eslint './src/**/*.{ts,tsx}'", "lint": "npm run paraglide && eslint './src/**/*.{ts,tsx}'",
"lint:fix": "npm run paraglide && eslint './src/**/*.{ts,tsx}' --fix", "lint:fix": "npm run paraglide && eslint './src/**/*.{ts,tsx}' --fix",
"paraglide": "paraglide-js compile --project ./localization/jetKVM.UI.inlang --outdir ./localization/paraglide", "i18n": "npm run i18n:resort && npm run i18n:validate && npm run i18n:compile",
"validate": "inlang validate --project ./localization/jetKVM.UI.inlang", "i18n:resort": "python3 tools/resort_messages.py",
"machine-translate": "inlang machine translate --project ./localization/jetKVM.UI.inlang" "i18n:validate": "inlang validate --project ./localization/jetKVM.UI.inlang",
"i18n:compile": "paraglide-js compile --project ./localization/jetKVM.UI.inlang --outdir ./localization/paraglide",
"i18n:machine-translate": "inlang machine translate --project ./localization/jetKVM.UI.inlang",
"i18n:find-excess": "python3 ./tools/find_excess_messages.py",
"i18n:find-unused": "python3 ./tools/find_unused_messages.py",
"i18n:find-dupes": "python3 ./tools/find_duplicate_translations.py"
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^2.2.9", "@headlessui/react": "^2.2.9",
"@headlessui/tailwindcss": "^0.2.2", "@headlessui/tailwindcss": "^0.2.2",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-basic-ssl": "^2.1.0", "@vitejs/plugin-basic-ssl": "^2.1.0",
"@xterm/addon-clipboard": "^0.1.0", "@xterm/addon-clipboard": "^0.1.0",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
@ -41,8 +47,8 @@
"react": "^19.2.0", "react": "^19.2.0",
"react-animate-height": "^3.2.3", "react-animate-height": "^3.2.3",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-hot-toast": "^2.6.0",
"react-hook-form": "^7.65.0", "react-hook-form": "^7.65.0",
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router": "^7.9.4", "react-router": "^7.9.4",
"react-simple-keyboard": "^3.8.130", "react-simple-keyboard": "^3.8.130",
@ -81,7 +87,7 @@
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.0", "eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.23", "eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.4.0", "globals": "^16.4.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",

View File

@ -72,7 +72,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text={m.action_bar_paste_text()} text={m.paste_text()}
LeadingIcon={MdOutlineContentPasteGo} LeadingIcon={MdOutlineContentPasteGo}
onClick={() => { onClick={() => {
setDisableVideoFocusTrap(true); setDisableVideoFocusTrap(true);

View File

@ -1,12 +1,12 @@
import { LuRefreshCcw } from "react-icons/lu"; import { LuRefreshCcw } from "react-icons/lu";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import EmptyCard from "@components/EmptyCard";
import { GridCard } from "@components/Card"; import { GridCard } from "@components/Card";
import { LifeTimeLabel } from "@routes/devices.$id.settings.network"; import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
import { NetworkState } from "@hooks/stores"; import { NetworkState } from "@hooks/stores";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
import EmptyCard from "./EmptyCard";
export default function DhcpLeaseCard({ export default function DhcpLeaseCard({
networkState, networkState,
@ -20,8 +20,8 @@ export default function DhcpLeaseCard({
if (isDhcpLeaseEmpty) { if (isDhcpLeaseEmpty) {
return ( return (
<EmptyCard <EmptyCard
headline="No DHCP Lease information" headline={m.dhcp_empty_lease_headline()}
description="We haven't received any DHCP lease information from the device yet." description={m.dhcp_empty_lease_description()}
/> />
); );
} }
@ -41,7 +41,7 @@ export default function DhcpLeaseCard({
theme="light" theme="light"
type="button" type="button"
className="text-red-500" className="text-red-500"
text="Renew DHCP Lease" text={m.dhcp_lease_renew()}
LeadingIcon={LuRefreshCcw} LeadingIcon={LuRefreshCcw}
onClick={() => setShowRenewLeaseConfirm(true)} onClick={() => setShowRenewLeaseConfirm(true)}
/> />
@ -54,7 +54,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ip_address()} {m.ip_address()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.ip} {networkState?.dhcp_lease?.ip}
</span> </span>
@ -65,7 +65,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.subnet_mask()} {m.subnet_mask()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.netmask} {networkState?.dhcp_lease?.netmask}
</span> </span>
@ -76,7 +76,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dns_servers()} {m.dns_servers()}
</span> </span>&nbsp;
<span className="text-right text-sm font-medium"> <span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.dns_servers.map(dns => ( {networkState?.dhcp_lease?.dns_servers.map(dns => (
<div key={dns}>{dns}</div> <div key={dns}>{dns}</div>
@ -89,7 +89,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_broadcast()} {m.dhcp_lease_broadcast()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.broadcast} {networkState?.dhcp_lease?.broadcast}
</span> </span>
@ -100,7 +100,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_domain()} {m.dhcp_lease_domain()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.domain} {networkState?.dhcp_lease?.domain}
</span> </span>
@ -112,7 +112,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400"> <div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
{m.ntp_servers()} {m.ntp_servers()}
</div> </div>&nbsp;
<div className="shrink text-right text-sm font-medium"> <div className="shrink text-right text-sm font-medium">
{networkState?.dhcp_lease?.ntp_servers.map(server => ( {networkState?.dhcp_lease?.ntp_servers.map(server => (
<div key={server}>{server}</div> <div key={server}>{server}</div>
@ -125,7 +125,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_hostname()} {m.dhcp_lease_hostname()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.hostname} {networkState?.dhcp_lease?.hostname}
</span> </span>
@ -139,7 +139,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between pt-2"> <div className="flex justify-between pt-2">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_gateway()} {m.dhcp_lease_gateway()}
</span> </span>&nbsp;
<span className="text-right text-sm font-medium"> <span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.routers.map(router => ( {networkState?.dhcp_lease?.routers.map(router => (
<div key={router}>{router}</div> <div key={router}>{router}</div>
@ -152,7 +152,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_server()} {m.dhcp_server()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.server_id} {networkState?.dhcp_lease?.server_id}
</span> </span>
@ -163,7 +163,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_lease_expires()} {m.dhcp_lease_lease_expires()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
<LifeTimeLabel <LifeTimeLabel
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`} lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
@ -175,8 +175,8 @@ export default function DhcpLeaseCard({
{networkState?.dhcp_lease?.broadcast && ( {networkState?.dhcp_lease?.broadcast && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
Broadcast {m.dhcp_lease_broadcast()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.broadcast} {networkState?.dhcp_lease?.broadcast}
</span> </span>
@ -185,18 +185,22 @@ export default function DhcpLeaseCard({
{networkState?.dhcp_lease?.mtu && ( {networkState?.dhcp_lease?.mtu && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">MTU</span> <span className="text-sm text-slate-600 dark:text-slate-400">
<span className="text-sm font-medium">
{m.dhcp_lease_maximum_transfer_unit()} {m.dhcp_lease_maximum_transfer_unit()}
</span>&nbsp;
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.mtu}
</span> </span>
</div> </div>
)} )}
{networkState?.dhcp_lease?.ttl && ( {networkState?.dhcp_lease?.ttl && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">TTL</span> <span className="text-sm text-slate-600 dark:text-slate-400">
<span className="text-sm font-medium">
{m.dhcp_lease_time_to_live()} {m.dhcp_lease_time_to_live()}
</span>&nbsp;
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.ttl}
</span> </span>
</div> </div>
)} )}
@ -205,7 +209,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_boot_next_server()} {m.dhcp_lease_boot_next_server()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_next_server} {networkState?.dhcp_lease?.bootp_next_server}
</span> </span>
@ -216,7 +220,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_boot_server_name()} {m.dhcp_lease_boot_server_name()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_server_name} {networkState?.dhcp_lease?.bootp_server_name}
</span> </span>
@ -227,7 +231,7 @@ export default function DhcpLeaseCard({
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.dhcp_lease_boot_file()} {m.dhcp_lease_boot_file()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_file} {networkState?.dhcp_lease?.bootp_file}
</span> </span>
@ -236,8 +240,12 @@ export default function DhcpLeaseCard({
{networkState?.dhcp_lease?.dhcp_client && ( {networkState?.dhcp_lease?.dhcp_client && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20"> <div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">DHCP Client</span> <span className="text-sm text-slate-600 dark:text-slate-400">
<span className="text-sm font-medium">{networkState?.dhcp_lease?.dhcp_client}</span> {m.network_dhcp_client_title()}
</span>&nbsp;
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.dhcp_client}
</span>
</div> </div>
)} )}
</div> </div>

View File

@ -33,7 +33,7 @@ export default function Ipv6NetworkCard({
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ipv6_link_local()} {m.ipv6_link_local()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.ipv6_link_local} {networkState?.ipv6_link_local}
</span> </span>
@ -42,7 +42,7 @@ export default function Ipv6NetworkCard({
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ipv6_gateway()} {m.ipv6_gateway()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{networkState?.ipv6_gateway} {networkState?.ipv6_gateway}
</span> </span>
@ -52,7 +52,9 @@ export default function Ipv6NetworkCard({
<div className="space-y-3 pt-2"> <div className="space-y-3 pt-2">
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && ( {networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
<div className="space-y-3"> <div className="space-y-3">
<h4 className="text-sm font-semibold">IPv6 Addresses</h4> <h4 className="text-sm font-semibold">
{m.network_ipv6_addresses_header()}
</h4>
{networkState.ipv6_addresses.map(addr => ( {networkState.ipv6_addresses.map(addr => (
<div <div
key={addr.address} key={addr.address}
@ -62,12 +64,12 @@ export default function Ipv6NetworkCard({
<div className="col-span-2 flex flex-col justify-between"> <div className="col-span-2 flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ipv6_address_label()} {m.ipv6_address_label()}
</span> </span>&nbsp;
<span className="text-sm font-medium flex"> <span className="text-sm font-medium flex">
<span className="flex-1">{addr.address}</span> <span className="flex-1">{addr.address}</span>&nbsp;
<span className="text-sm font-medium flex gap-x-1"> <span className="text-sm font-medium flex gap-x-1">
{addr.flag_deprecated ? <FlagLabel flag="Deprecated" /> : null} {addr.flag_deprecated ? <FlagLabel flag={m.network_ipv6_flag_deprecated()} /> : null}
{addr.flag_dad_failed ? <FlagLabel flag="DAD Failed" /> : null} {addr.flag_dad_failed ? <FlagLabel flag={m.network_ipv6_flag_dad_failed()} /> : null}
</span> </span>
</span> </span>
</div> </div>
@ -76,7 +78,7 @@ export default function Ipv6NetworkCard({
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ipv6_valid_lifetime()} {m.ipv6_valid_lifetime()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{addr.valid_lifetime === "" ? ( {addr.valid_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600"> <span className="text-slate-400 dark:text-slate-600">
@ -93,7 +95,7 @@ export default function Ipv6NetworkCard({
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
{m.ipv6_preferred_lifetime()} {m.ipv6_preferred_lifetime()}
</span> </span>&nbsp;
<span className="text-sm font-medium"> <span className="text-sm font-medium">
{addr.preferred_lifetime === "" ? ( {addr.preferred_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600"> <span className="text-slate-400 dark:text-slate-600">

View File

@ -39,7 +39,7 @@ export function MacroForm({
onSubmit, onSubmit,
onCancel, onCancel,
isSubmitting = false, isSubmitting = false,
}: MacroFormProps) { }: Readonly<MacroFormProps>) {
const [macro, setMacro] = useState<Partial<KeySequence>>(initialData); const [macro, setMacro] = useState<Partial<KeySequence>>(initialData);
const [keyQueries, setKeyQueries] = useState<Record<number, string>>({}); const [keyQueries, setKeyQueries] = useState<Record<number, string>>({});
const [errors, setErrors] = useState<ValidationErrors>({}); const [errors, setErrors] = useState<ValidationErrors>({});
@ -61,11 +61,11 @@ export function MacroForm({
newErrors.name = m.macro_name_too_long(); newErrors.name = m.macro_name_too_long();
} }
if (!macro.steps?.length) { const steps = (macro.steps || []);
newErrors.steps = { 0: { keys: m.macro_at_least_one_step_required() } };
} else { if (steps.length) {
const hasKeyOrModifier = macro.steps.some( const hasKeyOrModifier = steps.some(
step => (step.keys?.length || 0) > 0 || (step.modifiers?.length || 0) > 0, step => step.keys.length > 0 || step.modifiers.length > 0,
); );
if (!hasKeyOrModifier) { if (!hasKeyOrModifier) {
@ -73,6 +73,8 @@ export function MacroForm({
0: { keys: m.macro_at_least_one_step_keys_or_modifiers() }, 0: { keys: m.macro_at_least_one_step_keys_or_modifiers() },
}; };
} }
} else {
newErrors.steps = { 0: { keys: m.macro_at_least_one_step_required() } };
} }
setErrors(newErrors); setErrors(newErrors);
@ -89,9 +91,9 @@ export function MacroForm({
await onSubmit(macro); await onSubmit(macro);
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
showTemporaryError(error.message); showTemporaryError(m.macro_save_failed_error(error.message || m.unknown_error));
} else { } else {
showTemporaryError(m.macro_save_error()); showTemporaryError(m.macro_save_failed());
} }
} }
}; };
@ -104,14 +106,14 @@ export function MacroForm({
if (!newSteps[stepIndex]) return; if (!newSteps[stepIndex]) return;
if (option.keys) { if (option.keys) {
// they gave us a full set of keys (e.g. from deleting one)
newSteps[stepIndex].keys = option.keys; newSteps[stepIndex].keys = option.keys;
} else if (option.value) { } else if (option.value) {
// they gave us a single key to add
if (!newSteps[stepIndex].keys) { if (!newSteps[stepIndex].keys) {
newSteps[stepIndex].keys = []; newSteps[stepIndex].keys = [];
} }
const keysArray = Array.isArray(newSteps[stepIndex].keys) const keysArray = newSteps[stepIndex].keys;
? newSteps[stepIndex].keys
: [];
if (keysArray.length >= MAX_KEYS_PER_STEP) { if (keysArray.length >= MAX_KEYS_PER_STEP) {
showTemporaryError(m.macro_max_steps_error({ max: MAX_KEYS_PER_STEP })); showTemporaryError(m.macro_max_steps_error({ max: MAX_KEYS_PER_STEP }));
return; return;
@ -172,7 +174,6 @@ export function MacroForm({
const isMaxStepsReached = (macro.steps?.length || 0) >= MAX_STEPS_PER_MACRO; const isMaxStepsReached = (macro.steps?.length || 0) >= MAX_STEPS_PER_MACRO;
return ( return (
<>
<div className="space-y-4"> <div className="space-y-4">
<Fieldset> <Fieldset>
<InputFieldWithLabel <InputFieldWithLabel
@ -204,16 +205,16 @@ export function MacroForm({
{m.macro_step_count({ steps: macro.steps?.length || 0, max: MAX_STEPS_PER_MACRO })} {m.macro_step_count({ steps: macro.steps?.length || 0, max: MAX_STEPS_PER_MACRO })}
</span> </span>
</div> </div>
{errors.steps && errors.steps[0]?.keys && ( {errors.steps?.[0]?.keys && (
<div className="mt-2"> <div className="mt-2">
<FieldError error={errors.steps[0].keys} /> <FieldError error={errors.steps?.[0]?.keys} />
</div> </div>
)} )}
<Fieldset> <Fieldset>
<div className="mt-2 space-y-4"> <div className="mt-2 space-y-4">
{(macro.steps || []).map((step, stepIndex) => ( {(macro.steps || []).map((step, stepIndex) => (
<MacroStepCard <MacroStepCard
key={stepIndex} key={`step-{stepIndex}`}
step={step} step={step}
stepIndex={stepIndex} stepIndex={stepIndex}
onDelete={ onDelete={
@ -285,6 +286,5 @@ export function MacroForm({
</div> </div>
</div> </div>
</div> </div>
</>
); );
} }

View File

@ -12,7 +12,7 @@ import { keys, modifiers } from "@/keyboardMappings";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
// Filter out modifier keys since they're handled in the modifiers section // Filter out modifier keys since they're handled in the modifiers section
const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta']; const modifierKeyPrefixes = ["Alt", "Control", "Shift", "Meta"];
const modifierOptions = Object.keys(modifiers).map(modifier => ({ const modifierOptions = Object.keys(modifiers).map(modifier => ({
value: modifier, value: modifier,
@ -20,10 +20,10 @@ const modifierOptions = Object.keys(modifiers).map(modifier => ({
})); }));
const groupedModifiers: Record<string, typeof modifierOptions> = { const groupedModifiers: Record<string, typeof modifierOptions> = {
Control: modifierOptions.filter(mod => mod.value.startsWith('Control')), Control: modifierOptions.filter(mod => mod.value.startsWith("Control")),
Shift: modifierOptions.filter(mod => mod.value.startsWith('Shift')), Shift: modifierOptions.filter(mod => mod.value.startsWith("Shift")),
Alt: modifierOptions.filter(mod => mod.value.startsWith('Alt')), Alt: modifierOptions.filter(mod => mod.value.startsWith("Alt")),
Meta: modifierOptions.filter(mod => mod.value.startsWith('Meta')), Meta: modifierOptions.filter(mod => mod.value.startsWith("Meta")),
}; };
// not going to localize these since they're short time intervals // not going to localize these since they're short time intervals
@ -64,13 +64,17 @@ interface MacroStepCardProps {
onModifierChange: (modifiers: string[]) => void; onModifierChange: (modifiers: string[]) => void;
onDelayChange: (delay: number) => void; onDelayChange: (delay: number) => void;
isLastStep: boolean; isLastStep: boolean;
keyboard: KeyboardLayout keyboard: KeyboardLayout;
} }
const ensureArray = <T,>(arr: T[] | null | undefined): T[] => { const ensureArray = <T,>(arr: T[] | null | undefined): T[] => {
return Array.isArray(arr) ? arr : []; return Array.isArray(arr) ? arr : [];
}; };
const keyDisplay = (keyDisplayMap: Record<string, string>, key: string): string => {
return keyDisplayMap[key] || key
};
export function MacroStepCard({ export function MacroStepCard({
step, step,
stepIndex, stepIndex,
@ -83,28 +87,42 @@ export function MacroStepCard({
onModifierChange, onModifierChange,
onDelayChange, onDelayChange,
isLastStep, isLastStep,
keyboard keyboard,
}: MacroStepCardProps) { }: Readonly<MacroStepCardProps>) {
const { keyDisplayMap } = keyboard; const { keyDisplayMap } = keyboard;
const keyOptions = useMemo(() => const keyOptions = useMemo(
() =>
Object.keys(keys) Object.keys(keys)
.filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix))) .filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix)))
.map(key => ({ .map(key => ({
value: key, value: key,
label: keyDisplayMap[key] || key, label: keyDisplay(keyDisplayMap, key),
})), })),
[keyDisplayMap] [keyDisplayMap],
); );
const handleModifierToggle = (optionValue: string) => {
const modifiersArray = ensureArray(step.modifiers);
const isSelected = modifiersArray.includes(optionValue);
const newModifiers = isSelected
? modifiersArray.filter(m => m !== optionValue)
: [...modifiersArray, optionValue];
onModifierChange(newModifiers);
};
const filteredKeys = useMemo(() => { const filteredKeys = useMemo(() => {
const selectedKeys = ensureArray(step.keys); const selectedKeys = ensureArray(step.keys);
const availableKeys = keyOptions.filter(option => !selectedKeys.includes(option.value)); const availableKeys = keyOptions.filter(
option => !selectedKeys.includes(option.value),
);
if (keyQuery === '') { if (keyQuery === "") {
return availableKeys; return availableKeys;
} else { } else {
return availableKeys.filter(option => option.label.toLowerCase().includes(keyQuery.toLowerCase())); return availableKeys.filter(option =>
option.label.toLowerCase().includes(keyQuery.toLowerCase()),
);
} }
}, [keyOptions, keyQuery, step.keys]); }, [keyOptions, keyQuery, step.keys]);
@ -147,13 +165,19 @@ export function MacroStepCard({
</div> </div>
</div> </div>
<div className="space-y-4 mt-2"> <div className="mt-2 space-y-4">
<div className="w-full flex flex-col gap-2"> <div className="flex w-full flex-col gap-2">
<FieldLabel label={m.macro_step_modifiers_label()} description={m.macro_step_modifiers_description()} /> <FieldLabel
label={m.macro_step_modifiers_label()}
description={m.macro_step_modifiers_description()}
/>
<div className="inline-flex flex-wrap gap-3"> <div className="inline-flex flex-wrap gap-3">
{Object.entries(groupedModifiers).map(([group, mods]) => ( {Object.entries(groupedModifiers).map(([group, mods]) => (
<div key={group} className="relative min-w-[120px] rounded-md border border-slate-200 dark:border-slate-700 p-2"> <div
<span className="absolute -top-2.5 left-2 px-1 text-xs font-medium bg-white dark:bg-slate-800 text-slate-500 dark:text-slate-400"> key={group}
className="relative min-w-[120px] rounded-md border border-slate-200 p-2 dark:border-slate-700"
>
<span className="absolute -top-2.5 left-2 bg-white px-1 text-xs font-medium text-slate-500 dark:bg-slate-800 dark:text-slate-400">
{group} {group}
</span> </span>
<div className="flex flex-wrap gap-4 pt-1"> <div className="flex flex-wrap gap-4 pt-1">
@ -161,16 +185,13 @@ export function MacroStepCard({
<Button <Button
key={option.value} key={option.value}
size="XS" size="XS"
theme={ensureArray(step.modifiers).includes(option.value) ? "primary" : "light"} theme={
text={option.label.split(' ')[1] || option.label} ensureArray(step.modifiers).includes(option.value)
onClick={() => { ? "primary"
const modifiersArray = ensureArray(step.modifiers); : "light"
const isSelected = modifiersArray.includes(option.value); }
const newModifiers = isSelected text={option.label.split(" ")[1] || option.label}
? modifiersArray.filter(m => m !== option.value) onClick={() => handleModifierToggle(option.value)}
: [...modifiersArray, option.value];
onModifierChange(newModifiers);
}}
/> />
))} ))}
</div> </div>
@ -179,27 +200,30 @@ export function MacroStepCard({
</div> </div>
</div> </div>
<div className="flex w-full flex-col gap-1">
<div className="w-full flex flex-col gap-1">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FieldLabel label={m.macro_step_keys_label()} description={m.macro_step_keys_description({ max: MAX_KEYS_PER_STEP })} /> <FieldLabel
label={m.macro_step_keys_label()}
description={m.macro_step_keys_description({ max: MAX_KEYS_PER_STEP })}
/>
</div> </div>
{ensureArray(step.keys) && step.keys.length > 0 && (
{step.keys?.length > 0 && (
<div className="flex flex-wrap gap-1 pb-2"> <div className="flex flex-wrap gap-1 pb-2">
{step.keys.map((key, keyIndex) => ( {step.keys.map((key, keyIndex) => (
<span <span
key={keyIndex} key={`key-{keyIndex}`}
className="inline-flex items-center py-0.5 rounded-md bg-blue-100 px-1 text-xs font-medium text-blue-800 dark:bg-blue-900/40 dark:text-blue-200" className="inline-flex items-center rounded-md bg-blue-100 px-1 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900/40 dark:text-blue-200"
> >
<span className="px-1"> <span className="px-1">{keyDisplay(keyDisplayMap, key)}</span>
{keyDisplayMap[key] || key}
</span>
<Button <Button
size="XS" size="XS"
className="" className=""
theme="blank" theme="blank"
onClick={() => { onClick={() => {
const newKeys = ensureArray(step.keys).filter((_, i) => i !== keyIndex); const newKeys = step.keys.filter(
(_, i) => i !== keyIndex,
);
onKeySelect({ value: null, keys: newKeys }); onKeySelect({ value: null, keys: newKeys });
}} }}
LeadingIcon={LuX} LeadingIcon={LuX}
@ -210,10 +234,10 @@ export function MacroStepCard({
)} )}
<div className="relative w-full"> <div className="relative w-full">
<Combobox <Combobox
onChange={(option) => { onChange={option => {
const selectedOption = option as ComboboxOption | null; const selectedOption = option as ComboboxOption | null;
onKeySelect({ value: selectedOption?.value ?? null }); onKeySelect({ value: selectedOption?.value ?? null });
onKeyQueryChange(''); onKeyQueryChange("");
}} }}
displayValue={() => keyQuery} displayValue={() => keyQuery}
onInputChange={onKeyQueryChange} onInputChange={onKeyQueryChange}
@ -222,22 +246,29 @@ export function MacroStepCard({
size="SM" size="SM"
immediate immediate
disabled={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP} disabled={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP}
placeholder={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP ? m.macro_step_max_keys_reached() : m.macro_step_search_for_key()} placeholder={
ensureArray(step.keys).length >= MAX_KEYS_PER_STEP
? m.macro_step_max_keys_reached()
: m.macro_step_search_for_key()
}
emptyMessage={m.macro_step_no_matching_keys_found()} emptyMessage={m.macro_step_no_matching_keys_found()}
/> />
</div> </div>
</div> </div>
<div className="w-full flex flex-col gap-1"> <div className="flex w-full flex-col gap-1">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FieldLabel label={m.macro_step_duration_label()} description={m.macro_step_duration_description()} /> <FieldLabel
label={m.macro_step_duration_label()}
description={m.macro_step_duration_description()}
/>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM"
fullWidth fullWidth
value={step.delay.toString()} value={step.delay.toString()}
onChange={(e) => onDelayChange(parseInt(e.target.value, 10))} onChange={e => onDelayChange(Number.parseInt(e.target.value, 10))}
options={PRESET_DELAYS} options={PRESET_DELAYS}
/> />
</div> </div>

View File

@ -1,14 +1,15 @@
import { LuPlus, LuX } from "react-icons/lu";
import { useFieldArray, useFormContext } from "react-hook-form";
import { useEffect } from "react"; import { useEffect } from "react";
import validator from "validator"; import validator from "validator";
import { LuPlus, LuX } from "react-icons/lu";
import { useFieldArray, useFormContext } from "react-hook-form";
import { cx } from "cva"; import { cx } from "cva";
import { GridCard } from "@/components/Card"; import { NetworkSettings } from "@hooks/stores";
import { Button } from "@/components/Button"; import { Button } from "@components/Button";
import { InputFieldWithLabel } from "@/components/InputField"; import { GridCard } from "@components/Card";
import { NetworkSettings } from "@/hooks/stores"; import { InputFieldWithLabel } from "@components/InputField";
import { netMaskFromCidr4 } from "@/utils/ip"; import { netMaskFromCidr4 } from "@/utils/ip";
import { m } from "@localizations/messages.js";
export default function StaticIpv4Card() { export default function StaticIpv4Card() {
const formMethods = useFormContext<NetworkSettings>(); const formMethods = useFormContext<NetworkSettings>();
@ -24,6 +25,7 @@ export default function StaticIpv4Card() {
const ipv4StaticAddress = watch("ipv4_static.address"); const ipv4StaticAddress = watch("ipv4_static.address");
const hideSubnetMask = ipv4StaticAddress?.includes("/"); const hideSubnetMask = ipv4StaticAddress?.includes("/");
useEffect(() => { useEffect(() => {
const parts = ipv4StaticAddress?.split("/", 2); const parts = ipv4StaticAddress?.split("/", 2);
if (parts?.length !== 2) return; if (parts?.length !== 2) return;
@ -35,13 +37,13 @@ export default function StaticIpv4Card() {
setValue("ipv4_static.netmask", mask); setValue("ipv4_static.netmask", mask);
}, [ipv4StaticAddress, setValue]); }, [ipv4StaticAddress, setValue]);
const validate = (value: string) => { const ipv4Validation = (value: string) => {
if (!validator.isIP(value)) return "Invalid IP address"; if (!validator.isIP(value, 4)) return m.network_ipv4_invalid()
return true; return true;
}; };
const validateIsIPOrCIDR4 = (value: string) => { const validateIsIPOrCIDR4 = (value: string) => {
if (!validator.isIP(value, 4) && !validator.isIPRange(value, 4)) return "Invalid IP address or CIDR notation"; if (!validator.isIP(value) && !validator.isIPRange(value, 4)) return m.network_ipv4_invalid_cidr();
return true; return true;
}; };
@ -50,12 +52,12 @@ export default function StaticIpv4Card() {
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white"> <div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white"> <h3 className="text-base font-bold text-slate-900 dark:text-white">
Static IPv4 Configuration {m.network_static_ipv4_header()}
</h3> </h3>
<div className={cx("grid grid-cols-1 gap-4", hideSubnetMask ? "md:grid-cols-1" : "md:grid-cols-2")}> <div className={cx("grid grid-cols-1 gap-4", hideSubnetMask ? "md:grid-cols-1" : "md:grid-cols-2")}>
<InputFieldWithLabel <InputFieldWithLabel
label="IP Address" label={m.network_ipv4_address()}
type="text" type="text"
size="SM" size="SM"
placeholder="192.168.1.100" placeholder="192.168.1.100"
@ -67,21 +69,21 @@ export default function StaticIpv4Card() {
/> />
{!hideSubnetMask && <InputFieldWithLabel {!hideSubnetMask && <InputFieldWithLabel
label="Subnet Mask" label={m.network_ipv4_netmask()}
type="text" type="text"
size="SM" size="SM"
placeholder="255.255.255.0" placeholder="255.255.255.0"
{...register("ipv4_static.netmask", { validate: (value: string | undefined) => validate(value ?? "") })} {...register("ipv4_static.netmask", { validate: (value: string | undefined) => ipv4Validation(value ?? "") })}
error={formState.errors.ipv4_static?.netmask?.message} error={formState.errors.ipv4_static?.netmask?.message}
/>} />}
</div> </div>
<InputFieldWithLabel <InputFieldWithLabel
label="Gateway" label={m.network_ipv4_gateway()}
type="text" type="text"
size="SM" size="SM"
placeholder="192.168.1.1" placeholder="192.168.1.1"
{...register("ipv4_static.gateway", { validate: (value: string | undefined) => validate(value ?? "") })} {...register("ipv4_static.gateway", { validate: (value: string | undefined) => ipv4Validation(value ?? "") })}
error={formState.errors.ipv4_static?.gateway?.message} error={formState.errors.ipv4_static?.gateway?.message}
/> />
@ -93,13 +95,13 @@ export default function StaticIpv4Card() {
<div className="flex items-start gap-x-2"> <div className="flex items-start gap-x-2">
<div className="flex-1"> <div className="flex-1">
<InputFieldWithLabel <InputFieldWithLabel
label={index === 0 ? "DNS Server" : null} label={index === 0 ? m.network_ipv4_dns() : null}
type="text" type="text"
size="SM" size="SM"
placeholder="1.1.1.1" placeholder="1.1.1.1"
{...register( {...register(
`ipv4_static.dns.${index}`, `ipv4_static.dns.${index}`,
{ validate: (value: string | undefined) => validate(value ?? "") } { validate: (value: string | undefined) => ipv4Validation(value ?? "") }
)} )}
error={formState.errors.ipv4_static?.dns?.[index]?.message} error={formState.errors.ipv4_static?.dns?.[index]?.message}
/> />
@ -127,7 +129,7 @@ export default function StaticIpv4Card() {
onClick={() => append("", { shouldFocus: true })} onClick={() => append("", { shouldFocus: true })}
LeadingIcon={LuPlus} LeadingIcon={LuPlus}
type="button" type="button"
text="Add DNS Server" text={m.network_settings_add_dns()}
disabled={dns?.[0] === ""} disabled={dns?.[0] === ""}
/> />
</div> </div>

View File

@ -1,12 +1,13 @@
import { useEffect } from "react";
import validator from "validator";
import { LuPlus, LuX } from "react-icons/lu"; import { LuPlus, LuX } from "react-icons/lu";
import { useFieldArray, useFormContext } from "react-hook-form"; import { useFieldArray, useFormContext } from "react-hook-form";
import validator from "validator";
import { useEffect } from "react";
import { GridCard } from "@/components/Card"; import { NetworkSettings } from "@hooks/stores";
import { Button } from "@/components/Button"; import { Button } from "@components/Button";
import { InputFieldWithLabel } from "@/components/InputField"; import { GridCard } from "@components/Card";
import { NetworkSettings } from "@/hooks/stores"; import { InputFieldWithLabel } from "@components/InputField";
import { m } from "@localizations/messages.js";
export default function StaticIpv6Card() { export default function StaticIpv6Card() {
const formMethods = useFormContext<NetworkSettings>(); const formMethods = useFormContext<NetworkSettings>();
@ -25,20 +26,20 @@ export default function StaticIpv6Card() {
// Check if it's a valid IPv6 address with CIDR notation // Check if it's a valid IPv6 address with CIDR notation
const parts = value.split("/"); const parts = value.split("/");
if (parts.length !== 2) return "Please use CIDR notation (e.g., 2001:db8::1/64)"; if (parts.length !== 2) return m.network_ipv6_cidr_suggestion();
const [address, prefix] = parts; const [address, prefix] = parts;
if (!validator.isIP(address, 6)) return "Invalid IPv6 address"; if (!validator.isIP(address, 6)) return m.network_ipv6_invalid();
const prefixNum = parseInt(prefix); const prefixNum = parseInt(prefix);
if (isNaN(prefixNum) || prefixNum < 0 || prefixNum > 128) { if (isNaN(prefixNum) || prefixNum < 0 || prefixNum > 128) {
return "Prefix must be between 0 and 128"; return m.network_ipv6_prefix_invalid();
} }
return true; return true;
}; };
const ipv6Validation = (value: string) => { const ipv6Validation = (value: string) => {
if (!validator.isIP(value, 6)) return "Invalid IPv6 address"; if (!validator.isIP(value, 6)) return m.network_ipv6_invalid()
return true; return true;
}; };
@ -47,11 +48,11 @@ export default function StaticIpv6Card() {
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white"> <div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white"> <h3 className="text-base font-bold text-slate-900 dark:text-white">
Static IPv6 Configuration {m.network_static_ipv6_header()}
</h3> </h3>
<InputFieldWithLabel <InputFieldWithLabel
label="IP Prefix" label={m.network_ipv6_prefix()}
type="text" type="text"
size="SM" size="SM"
placeholder="2001:db8::1/64" placeholder="2001:db8::1/64"
@ -60,7 +61,7 @@ export default function StaticIpv6Card() {
/> />
<InputFieldWithLabel <InputFieldWithLabel
label="Gateway" label={m.network_ipv6_gateway()}
type="text" type="text"
size="SM" size="SM"
placeholder="2001:db8::1" placeholder="2001:db8::1"
@ -76,7 +77,7 @@ export default function StaticIpv6Card() {
<div className="flex items-start gap-x-2"> <div className="flex items-start gap-x-2">
<div className="flex-1"> <div className="flex-1">
<InputFieldWithLabel <InputFieldWithLabel
label={index === 0 ? "DNS Server" : null} label={index === 0 ? m.network_ipv6_dns() : null}
type="text" type="text"
size="SM" size="SM"
placeholder="2001:4860:4860::8888" placeholder="2001:4860:4860::8888"
@ -107,7 +108,7 @@ export default function StaticIpv6Card() {
onClick={() => append("", { shouldFocus: true })} onClick={() => append("", { shouldFocus: true })}
LeadingIcon={LuPlus} LeadingIcon={LuPlus}
type="button" type="button"
text="Add DNS Server" text={m.network_settings_add_dns()}
disabled={dns?.[0] === ""} disabled={dns?.[0] === ""}
/> />
</div> </div>

View File

@ -123,8 +123,8 @@ export default function PasteModal() {
<div className="h-full space-y-4"> <div className="h-full space-y-4">
<div className="space-y-4"> <div className="space-y-4">
<SettingsPageHeader <SettingsPageHeader
title={m.paste_modal_paste_text()} title={m.paste_text()}
description={m.paste_modal_paste_text_description()} description={m.paste_text_description()}
/> />
<div <div

View File

@ -116,7 +116,7 @@ export default function ConnectionStatsSidebar() {
metric: x.metric != null ? Math.round(x.metric * 1000) : null, metric: x.metric != null ? Math.round(x.metric * 1000) : null,
})} })}
domain={[0, 600]} domain={[0, 600]}
unit=" ms" unit={m.connection_stats_units_milliseconds()}
/> />
</div> </div>
@ -140,7 +140,7 @@ export default function ConnectionStatsSidebar() {
metric: x.metric != null ? Math.round(x.metric * 1000) : null, metric: x.metric != null ? Math.round(x.metric * 1000) : null,
})} })}
domain={[0, 10]} domain={[0, 10]}
unit=" ms" unit={m.connection_stats_units_milliseconds()}
/> />
{/* Playback Delay */} {/* Playback Delay */}
@ -162,7 +162,7 @@ export default function ConnectionStatsSidebar() {
) )
} }
domain={[0, 30]} domain={[0, 30]}
unit=" ms" unit={m.connection_stats_unit_milliseconds()}
/> />
{/* Packets Lost */} {/* Packets Lost */}
@ -172,7 +172,7 @@ export default function ConnectionStatsSidebar() {
stream={inboundVideoRtpStats} stream={inboundVideoRtpStats}
metric="packetsLost" metric="packetsLost"
domain={[0, 100]} domain={[0, 100]}
unit=" packets" unit={m.connection_stats_unit_packets()}
/> />
{/* Frames Per Second */} {/* Frames Per Second */}
@ -182,7 +182,7 @@ export default function ConnectionStatsSidebar() {
stream={inboundVideoRtpStats} stream={inboundVideoRtpStats}
metric="framesPerSecond" metric="framesPerSecond"
domain={[0, 80]} domain={[0, 80]}
unit=" fps" unit={m.connection_stats_unit_frames_per_second()}
/> />
</div> </div>
</div> </div>

View File

@ -11,11 +11,13 @@ import { useJsonRpc } from "@hooks/useJsonRpc";
import AutoHeight from "@components/AutoHeight"; import AutoHeight from "@components/AutoHeight";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import { ConfirmDialog } from "@components/ConfirmDialog"; import { ConfirmDialog } from "@components/ConfirmDialog";
import DhcpLeaseCard from "@components/DhcpLeaseCard";
import EmptyCard from "@components/EmptyCard"; import EmptyCard from "@components/EmptyCard";
import { GridCard } from "@components/Card"; import { GridCard } from "@components/Card";
import InputField, { InputFieldWithLabel } from "@components/InputField"; import InputField, { InputFieldWithLabel } from "@components/InputField";
import Ipv6NetworkCard from "@components/Ipv6NetworkCard"; import Ipv6NetworkCard from "@components/Ipv6NetworkCard";
import { SelectMenuBasic } from "@/components/SelectMenuBasic"; import { SelectMenuBasic } from "@/components/SelectMenuBasic";
import { SettingsItem } from "@components/SettingsItem";
import { SettingsPageHeader } from "@/components/SettingsPageheader"; import { SettingsPageHeader } from "@/components/SettingsPageheader";
import StaticIpv4Card from "@components/StaticIpv4Card"; import StaticIpv4Card from "@components/StaticIpv4Card";
import StaticIpv6Card from "@components/StaticIpv6Card"; import StaticIpv6Card from "@components/StaticIpv6Card";
@ -24,11 +26,11 @@ import { netMaskFromCidr4 } from "@/utils/ip";
import { getNetworkSettings, getNetworkState } from "@/utils/jsonrpc"; import { getNetworkSettings, getNetworkState } from "@/utils/jsonrpc";
import notifications from "@/notifications"; import notifications from "@/notifications";
import { m } from "@localizations/messages"; import { m } from "@localizations/messages";
import { SettingsItem } from "@components/SettingsItem";
import DhcpLeaseCard from "@components/DhcpLeaseCard";
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const isLLDPAvailable = false; // LLDP is not supported yet
const resolveOnRtcReady = () => { const resolveOnRtcReady = () => {
return new Promise(resolve => { return new Promise(resolve => {
// Check if RTC is already connected // Check if RTC is already connected
@ -160,7 +162,7 @@ export default function SettingsNetworkRoute() {
const parts = settings.ipv4_static.address.split("/"); const parts = settings.ipv4_static.address.split("/");
const cidrNotation = parseInt(parts[1]); const cidrNotation = parseInt(parts[1]);
if (isNaN(cidrNotation) || cidrNotation < 0 || cidrNotation > 32) { if (isNaN(cidrNotation) || cidrNotation < 0 || cidrNotation > 32) {
return notifications.error(m.network_settings_invalid_ipv4_cidr()); return notifications.error(m.network_ipv4_invalid_cidr());
} }
settings.ipv4_static.netmask = netMaskFromCidr4(cidrNotation); settings.ipv4_static.netmask = netMaskFromCidr4(cidrNotation);
settings.ipv4_static.address = parts[0]; settings.ipv4_static.address = parts[0];
@ -251,6 +253,30 @@ export default function SettingsNetworkRoute() {
}); });
} }
if (dirty.ipv6_static?.prefix) {
changes.push({
label: m.network_ipv6_prefix(),
from: initialSettingsRef.current?.ipv6_static?.prefix as string,
to: data.ipv6_static?.prefix as string,
});
}
if (dirty.ipv4_static?.gateway) {
changes.push({
label: m.network_ipv6_gateway(),
from: initialSettingsRef.current?.ipv6_static?.gateway as string,
to: data.ipv6_static?.gateway as string,
});
}
if (dirty.ipv6_static?.dns) {
changes.push({
label: m.network_ipv6_dns(),
from: initialSettingsRef.current?.ipv6_static?.dns.join(", ").toString() ?? "",
to: data.ipv4_static?.dns.join(", ").toString() ?? "",
});
}
// If no critical fields are changed, save immediately // If no critical fields are changed, save immediately
if (changes.length === 0) return onSubmit(settings); if (changes.length === 0) return onSubmit(settings);
@ -516,9 +542,7 @@ export default function SettingsNetworkRoute() {
</AutoHeight> </AutoHeight>
</div> </div>
{ isLLDPAvailable &&
{ // eslint-disable-next-line no-constant-condition
false ? /* LLDP is not supported yet */
( (
<div className="hidden space-y-4"> <div className="hidden space-y-4">
<SettingsItem <SettingsItem
@ -536,7 +560,7 @@ export default function SettingsNetworkRoute() {
/> />
</SettingsItem> </SettingsItem>
</div> </div>
) : null )
} }
<div className="animate-fadeInStill animation-duration-300"> <div className="animate-fadeInStill animation-duration-300">
@ -598,7 +622,7 @@ export default function SettingsNetworkRoute() {
<path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" /> <path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg> </svg>
<code className="rounded border border-slate-800/20 bg-slate-50 px-1.5 py-1 text-xs text-black font-mono dark:border-slate-300/20 dark:bg-slate-800 dark:text-slate-100"> <code className="rounded border border-slate-800/20 bg-slate-50 px-1.5 py-1 text-xs text-black font-mono dark:border-slate-300/20 dark:bg-slate-800 dark:text-slate-100">
{c.to} {c.to || "—"}
</code> </code>
</div> </div>
</div> </div>
@ -611,7 +635,7 @@ export default function SettingsNetworkRoute() {
<ConfirmDialog <ConfirmDialog
open={showRenewLeaseConfirm} open={showRenewLeaseConfirm}
title={m.network_dhcp_lease_renew()} title={m.dhcp_lease_renew()}
variant="warning" variant="warning"
confirmText={m.network_dhcp_lease_renew_confirm()} confirmText={m.network_dhcp_lease_renew_confirm()}
description={ description={

View File

@ -31,7 +31,7 @@ const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
const confirmPassword = formData.get("confirmPassword"); const confirmPassword = formData.get("confirmPassword");
if (password !== confirmPassword) { if (password !== confirmPassword) {
return { error: m.auth_mode_local_password_do_not_match() }; return { error: m.local_auth_error_passwords_not_match() };
} }
try { try {

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import json import json
import os
import re import re
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@ -26,12 +25,30 @@ def normalize(s, ignore_case=False, trim=False, collapse_ws=False):
return s return s
def main(): def main():
p = argparse.ArgumentParser(description="Find identical translation targets with different keys in en.json") p = argparse.ArgumentParser(
p.add_argument("--en", default="../ui/localization/messages/en.json", help="path to en.json") description="Find identical translation targets with different keys in en.json"
p.add_argument("--out", default="../reports/duplicate_translations.json", help="output report path (JSON)") )
p.add_argument("--ignore-case", action="store_true", help="ignore case when comparing values") p.add_argument(
p.add_argument("--trim", action="store_true", help="trim surrounding whitespace before comparing") "--en", default="./localization/messages/en.json", help="path to en.json"
p.add_argument("--collapse-ws", action="store_true", help="collapse internal whitespace before comparing") )
p.add_argument(
"--out",
default="./reports/duplicate_translation_targets.json",
help="output report path (JSON)",
)
p.add_argument(
"--ignore-case", default=True, action="store_true", help="ignore case when comparing values"
)
p.add_argument(
"--trim",
default=True, action="store_true",
help="trim surrounding whitespace before comparing",
)
p.add_argument(
"--collapse-ws",
default=True, action="store_true",
help="collapse internal whitespace before comparing",
)
args = p.parse_args() args = p.parse_args()
en_path = Path(args.en) en_path = Path(args.en)
@ -48,7 +65,12 @@ def main():
groups = {} groups = {}
original_values = {} original_values = {}
for key, val in entries: for key, val in entries:
norm = normalize(val, ignore_case=args.ignore_case, trim=args.trim, collapse_ws=args.collapse_ws) norm = normalize(
val,
ignore_case=args.ignore_case,
trim=args.trim,
collapse_ws=args.collapse_ws,
)
groups.setdefault(norm, []).append(key) groups.setdefault(norm, []).append(key)
# keep the first seen original for reporting # keep the first seen original for reporting
original_values.setdefault(norm, val) original_values.setdefault(norm, val)
@ -56,19 +78,23 @@ def main():
duplicates = [] duplicates = []
for norm, keys in groups.items(): for norm, keys in groups.items():
if len(keys) > 1: if len(keys) > 1:
duplicates.append({ duplicates.append(
{
"normalized_value": norm, "normalized_value": norm,
"original_value": original_values.get(norm), "original_value": original_values.get(norm),
"keys": sorted(keys), "keys": sorted(keys),
"count": len(keys) "count": len(keys),
}) }
)
report = { report = {
"generated_at": datetime.utcnow().isoformat() + "Z", "generated_at": datetime.utcnow().isoformat() + "Z",
"en_json": str(en_path), "en_json": str(en_path),
"total_string_keys": total_keys, "total_string_keys": total_keys,
"duplicate_groups": sorted(duplicates, key=lambda d: (-d["count"], d["normalized_value"])), "duplicate_groups": sorted(
"duplicate_count": len(duplicates) duplicates, key=lambda d: (-d["count"], d["normalized_value"])
),
"duplicate_count": len(duplicates),
} }
out_path = Path(args.out) out_path = Path(args.out)
@ -76,7 +102,9 @@ def main():
with out_path.open("w", encoding="utf-8") as f: with out_path.open("w", encoding="utf-8") as f:
json.dump(report, f, indent=2, ensure_ascii=False) json.dump(report, f, indent=2, ensure_ascii=False)
print(f"Wrote {out_path} — total keys: {total_keys}, duplicate groups: {len(duplicates)}") print(
f"Wrote {out_path} — total keys: {total_keys}, duplicate groups: {len(duplicates)}"
)
if __name__ == "__main__": if __name__ == "__main__":
main() sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Find excess translation keys that exist in other language files but are not in en.json
Usage:
python3 tools/find_excess_messages.py
Optional: set `--json` to print machine-readable JSON output.
Optional: set `--path` to point to the directory containing messages JSON files.
Optional: set `--out` to specify a file to write the JSON report to.
"""
import argparse
import json
import sys
from pathlib import Path
from json import JSONDecodeError
def load_json(path: Path):
try:
return json.loads(path.read_text(encoding='utf-8'))
except (JSONDecodeError, OSError) as e:
print(f"Failed to read {path}: {e}")
return {}
def main(argv):
p = argparse.ArgumentParser(
description="Sort translations keys in message *.json files"
)
p.add_argument("--path", default="./localization/messages/", help="path to messages *.json")
p.add_argument("--json", default=False, action="store_true", help="output excess keys as JSON")
p.add_argument("--out", default="./reports/excess_messages.json", help="output report file")
args = p.parse_args()
messages_path = Path(args.path)
if not messages_path.is_dir():
print(f"message path is not a directory: {messages_path}")
raise SystemExit(2)
files = list(messages_path.glob("*.json"))
if len(files) == 0:
print(f"no message files (*.json) found in: {messages_path}")
raise SystemExit(3)
en_path = messages_path / 'en.json'
en = load_json(en_path) if en_path.exists() else {}
en_keys = set(en.keys())
extras = {} # key -> list of files where present
for f in files:
if f.name == 'en.json':
continue
data = load_json(f)
for k in data.keys():
if k not in en_keys:
extras.setdefault(k, []).append(f.name)
if '--json' in argv:
print(json.dumps(extras, ensure_ascii=False, indent=2))
return 0
if not extras:
print('No excess message keys found in other languages.')
return 0
print(f"Message keys present in other languages but not in en.json ({len(extras)}):\n")
for key, files in sorted(extras.items()):
print(f"{key}: {', '.join(files)}")
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -14,7 +14,9 @@ def flatten(d, prefix=""):
else: else:
yield key yield key
def gather_files(src_dir, exts=(".ts", ".tsx", ".js", ".jsx", ".html", ".vue", ".json")): def gather_files(
src_dir, exts=(".ts", ".tsx", ".js", ".jsx")
):
for root, _, files in os.walk(src_dir): for root, _, files in os.walk(src_dir):
parts = root.split(os.sep) parts = root.split(os.sep)
if "node_modules" in parts or ".git" in parts: if "node_modules" in parts or ".git" in parts:
@ -25,29 +27,38 @@ def gather_files(src_dir, exts=(".ts", ".tsx", ".js", ".jsx", ".html", ".vue", "
def find_usages(keys, files): def find_usages(keys, files):
usages = {k: [] for k in keys} usages = {k: [] for k in keys}
print(f"Compiling {len(keys)} patterns for keys ...")
# Precompile patterns for speed # Precompile patterns for speed
patterns = {k: re.compile(r"\bm\." + re.escape(k) + r"\s*\(") for k in keys} patterns = {k: re.compile(r"\bm\." + re.escape(k) + r"\s*\(") for k in keys}
print(f"Scanning files...")
for file in files: for file in files:
try: try:
text = file.read_text(encoding="utf-8") text = file.read_text(encoding="utf-8")
except Exception: except (OSError, UnicodeDecodeError):
# skip files that can't be read due to I/O or decoding errors
continue continue
lines = text.splitlines() lines = text.splitlines()
for i, line in enumerate(lines, start=1): for i, line in enumerate(lines, start=1):
for k, pat in patterns.items(): for k, pat in patterns.items():
if pat.search(line): if pat.search(line):
usages[k].append({ usages[k].append(
"file": str(file), {"file": str(file), "line": i, "text": line.strip()}
"line": i, )
"text": line.strip()
})
return usages return usages
def main(): def main():
p = argparse.ArgumentParser(description="Generate JSON report of localization key usage (m.key_name_here()).") p = argparse.ArgumentParser(
p.add_argument("--en", default="../ui/localization/messages/en.json", help="path to en.json") description="Generate JSON report of localization key usage (m.key_name_here())."
p.add_argument("--src", default="../ui", help="root source directory to scan") )
p.add_argument("--out", default="../reports/localization_report.json", help="output report file") p.add_argument(
"--en", default="./localization/messages/en.json", help="path to en.json"
)
p.add_argument("--src", default="./", help="root source directory to scan")
p.add_argument(
"--out", default="./reports/localization_key_usage.json", help="output report file"
)
args = p.parse_args() args = p.parse_args()
en_path = Path(args.en) en_path = Path(args.en)
@ -55,34 +66,40 @@ def main():
print(f"en.json not found: {en_path}", flush=True) print(f"en.json not found: {en_path}", flush=True)
raise SystemExit(2) raise SystemExit(2)
print(f"Reading english from {en_path}")
with en_path.open(encoding="utf-8") as f: with en_path.open(encoding="utf-8") as f:
payload = json.load(f) payload = json.load(f)
keys = sorted(list(flatten(payload))) keys = sorted(list(flatten(payload)))
keys.pop(0) if keys and keys[0] == "$schema" else None # remove $schema if present
print(f"Found {len(keys)} localization keys")
print(f"Gathering source files in {args.src} ...")
files = list(gather_files(args.src)) files = list(gather_files(args.src))
print(f"Scanning {len(files)} source files ...")
usages = find_usages(keys, files) usages = find_usages(keys, files)
print(f"Generating report for {len(usages)} usages ...")
report = { report = {
"generated_at": datetime.utcnow().isoformat() + "Z", "generated_at": datetime.utcnow().isoformat() + "Z",
"en_json": str(en_path), "en_json": str(en_path),
"src_root": args.src, "src_root": args.src,
"total_keys": len(keys), "total_keys": len(keys),
"keys": {} "keys": {},
} }
unused_count = 0
for k in keys: for k in keys:
occ = usages.get(k, []) occ = usages.get(k, [])
used = bool(occ) used = bool(occ)
if not used: report["keys"][k] = {"used": used, "occurrences": occ}
unused_count += 1
report["keys"][k] = { unused_keys = [k for k, v in report["keys"].items() if not v["used"]]
"used": used, unused_count = len(unused_keys)
"occurrences": occ print(f"Found {unused_count} unused keys")
}
report["unused_count"] = unused_count report["unused_count"] = unused_count
report["unused_keys"] = [k for k, v in report["keys"].items() if not v["used"]] report["unused_keys"] = unused_keys
out_path = Path(args.out) out_path = Path(args.out)
out_path.parent.mkdir(parents=True, exist_ok=True) out_path.parent.mkdir(parents=True, exist_ok=True)
@ -92,5 +109,6 @@ def main():
print(f"Report written to {out_path}") print(f"Report written to {out_path}")
print(f"Total keys: {report['total_keys']}, Unused: {report['unused_count']}") print(f"Total keys: {report['total_keys']}, Unused: {report['unused_count']}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
def main():
p = argparse.ArgumentParser(
description="Sort translations keys in message *.json files"
)
p.add_argument(
"--path", default="./localization/messages/", help="path to messages *.json"
)
args = p.parse_args()
messages_path = Path(args.path)
if not messages_path.is_dir():
print(f"message path is not a directory: {messages_path}")
raise SystemExit(2)
files = list(messages_path.glob("*.json"))
if len(files) == 0:
print(f"no message files (*.json) found in: {messages_path}")
raise SystemExit(3)
for f in files:
print(f"Processing {f.name} ...")
data = json.loads(f.read_text(encoding="utf-8"))
# Keep $schema first if present
schema = None
if "$schema" in data:
schema = data.pop("$schema")
sorted_items = dict(sorted(data.items()))
if schema is not None:
out = {"$schema": schema}
out.update(sorted_items)
else:
out = sorted_items
f.write_text(
json.dumps(out, ensure_ascii=False, indent=4) + "\n", encoding="utf-8"
)
print(f"Processed {len(files)} files in {messages_path}")
if __name__ == "__main__":
main()