Added setting to choose locale

This commit is contained in:
Marc Brooks 2025-10-14 22:35:05 -05:00
parent 423bf1a53f
commit 748155d815
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
14 changed files with 208 additions and 5 deletions

View File

@ -31,9 +31,17 @@
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{locale}.json"
},
"plugin.inlang.mFunctionMatcher": {
"matchers": [
{
"type": "m-function",
"function": "plural",
"parameter": "count"
}
]
},
"strategy": [
"cookie",
"localStorage",
"preferredLanguage",
"baseLocale"
]

View File

@ -395,6 +395,17 @@
"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_update_password_button": "Opdater adgangskode",
"locale_auto": "Bil",
"locale_change_success": "Sproget er ændret til {locale}",
"locale_da": "Dansk",
"locale_de": "Tysk",
"locale_en": "Engelsk",
"locale_es": "Spansk",
"locale_fr": "Fransk",
"locale_it": "Italiensk",
"locale_nb": "Norsk (bokmål)",
"locale_sv": "Svensk",
"locale_zh": "中文 (简体)",
"log_in": "Log ind",
"log_out": "Log ud",
"logged_in_as": "Logget ind som",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Afbrudt",
"usb_state_low_power_mode": "Lavstrømstilstand",
"usb": "USB",
"user_interface_language_description": "Vælg det sprog, der skal bruges i JetKVM-brugergrænsefladen",
"user_interface_language_title": "Grænsefladesprog",
"video_brightness_description": "Lysstyrkeniveau ( {value} x)",
"video_brightness_title": "Lysstyrke",
"video_contrast_description": "Kontrastniveau ( {value} x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "Sie haben Ihr lokales Geräteschutzkennwort erfolgreich geändert. Merken Sie sich das neue Kennwort für zukünftige Zugriffe.",
"local_auth_success_password_updated_title": "Passwort erfolgreich aktualisiert",
"local_auth_update_password_button": "Kennwort aktualisieren",
"locale_auto": "Auto",
"locale_change_success": "Die Sprache wurde erfolgreich in {locale} geändert.",
"locale_da": "Dänisch",
"locale_de": "Deutsch",
"locale_en": "Englisch",
"locale_es": "Spanisch",
"locale_fr": "Deutsch",
"locale_it": "Italienisch",
"locale_nb": "Norwegisch (bokmål)",
"locale_sv": "Schwedisch",
"locale_zh": "中文 (简体)",
"log_in": "Einloggen",
"log_out": "Ausloggen",
"logged_in_as": "Angemeldet als",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Getrennt",
"usb_state_low_power_mode": "Energiesparmodus",
"usb": "USB",
"user_interface_language_description": "Wählen Sie die Sprache aus, die in der JetKVM-Benutzeroberfläche verwendet werden soll",
"user_interface_language_title": "Schnittstellensprache",
"video_brightness_description": "Helligkeitsstufe ( {value} x)",
"video_brightness_title": "Helligkeit",
"video_contrast_description": "Kontraststufe ( {value} x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "You've successfully changed your local device protection password. Make sure to remember your new password for future access.",
"local_auth_success_password_updated_title": "Password Updated Successfully",
"local_auth_update_password_button": "Update Password",
"locale_auto": "Auto",
"locale_change_success": "Language changed successfully to {locale}",
"locale_da": "Dansk",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italiano",
"locale_nb": "Norsk (bokmål)",
"locale_sv": "Svenska",
"locale_zh": "中文 (简体)",
"log_in": "Log In",
"log_out": "Log out",
"logged_in_as": "Logged in as",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Disconnected",
"usb_state_low_power_mode": "Low power mode",
"usb": "USB",
"user_interface_language_description": "Select the language to use in the JetKVM user interface",
"user_interface_language_title": "Interface Language",
"video_brightness_description": "Brightness level ({value}x)",
"video_brightness_title": "Brightness",
"video_contrast_description": "Contrast level ({value}x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "Has cambiado correctamente la contraseña de protección de tu dispositivo local. Recuerda la nueva contraseña para acceder en el futuro.",
"local_auth_success_password_updated_title": "Contraseña actualizada exitosamente",
"local_auth_update_password_button": "Actualizar contraseña",
"locale_auto": "Auto",
"locale_change_success": "El idioma se cambió correctamente a {locale}",
"locale_da": "Danés",
"locale_de": "Alemán",
"locale_en": "Inglés",
"locale_es": "Español",
"locale_fr": "Francés",
"locale_it": "Italiano",
"locale_nb": "Noruego (bokmål)",
"locale_sv": "Sueco",
"locale_zh": "中文 (简体)",
"log_in": "Acceso",
"log_out": "Finalizar la sesión",
"logged_in_as": "Inició sesión como",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Desconectado",
"usb_state_low_power_mode": "Modo de bajo consumo",
"usb": "USB",
"user_interface_language_description": "Seleccione el idioma que se utilizará en la interfaz de usuario de JetKVM",
"user_interface_language_title": "Lenguaje de interfaz",
"video_brightness_description": "Nivel de brillo ( {value} x)",
"video_brightness_title": "Brillo",
"video_contrast_description": "Nivel de contraste ( {value} x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "Vous avez modifié avec succès le mot de passe de protection de votre appareil local. N'oubliez pas de le mémoriser pour y accéder ultérieurement.",
"local_auth_success_password_updated_title": "Mot de passe mis à jour avec succès",
"local_auth_update_password_button": "Mettre à jour le mot de passe",
"locale_auto": "Auto",
"locale_change_success": "La langue a été modifiée avec succès en {locale}",
"locale_da": "danois",
"locale_de": "Allemand",
"locale_en": "Anglais",
"locale_es": "Espagnol",
"locale_fr": "Français",
"locale_it": "italien",
"locale_nb": "Norvégien (bokmål)",
"locale_sv": "suédois",
"locale_zh": "中文 (简体)",
"log_in": "Se connecter",
"log_out": "Se déconnecter",
"logged_in_as": "Connecté en tant que",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Déconnecté",
"usb_state_low_power_mode": "Mode basse consommation",
"usb": "USB",
"user_interface_language_description": "Sélectionnez la langue à utiliser dans l'interface utilisateur de JetKVM",
"user_interface_language_title": "Langue de l'interface",
"video_brightness_description": "Niveau de luminosité ( {value} x)",
"video_brightness_title": "Luminosité",
"video_contrast_description": "Niveau de contraste ( {value} x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "Hai modificato correttamente la password di protezione del tuo dispositivo locale. Assicurati di ricordare la nuova password per gli accessi futuri.",
"local_auth_success_password_updated_title": "Password aggiornata con successo",
"local_auth_update_password_button": "Aggiorna password",
"locale_auto": "Auto",
"locale_change_success": "Lingua modificata correttamente in {locale}",
"locale_da": "Danese",
"locale_de": "Tedesco",
"locale_en": "Inglese",
"locale_es": "Spagnolo",
"locale_fr": "Francese",
"locale_it": "Italiano",
"locale_nb": "Norvegese (bokmål)",
"locale_sv": "Svedese",
"locale_zh": "中文 (简体)",
"log_in": "Login",
"log_out": "Disconnetti",
"logged_in_as": "Accedi come",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Disconnesso",
"usb_state_low_power_mode": "Modalità a basso consumo",
"usb": "USB",
"user_interface_language_description": "Seleziona la lingua da utilizzare nell'interfaccia utente JetKVM",
"user_interface_language_title": "Lingua dell'interfaccia",
"video_brightness_description": "Livello di luminosità ( {value} x)",
"video_brightness_title": "Luminosità",
"video_contrast_description": "Livello di contrasto ( {value} x)",

View File

@ -395,6 +395,17 @@
"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_update_password_button": "Oppdater passord",
"locale_auto": "Bil",
"locale_change_success": "Språket er endret til {locale}",
"locale_da": "Dansk",
"locale_de": "Tysk",
"locale_en": "Engelsk",
"locale_es": "Spansk",
"locale_fr": "Fransk",
"locale_it": "Italiensk",
"locale_nb": "Norsk (bokmål)",
"locale_sv": "Svensk",
"locale_zh": "中文 (简体)",
"log_in": "Logg inn",
"log_out": "Logg ut",
"logged_in_as": "Logget inn som",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Frakoblet",
"usb_state_low_power_mode": "Lavstrømsmodus",
"usb": "USB",
"user_interface_language_description": "Velg språket som skal brukes i JetKVM-brukergrensesnittet",
"user_interface_language_title": "Grensesnittspråk",
"video_brightness_description": "Lysstyrkenivå ( {value} x)",
"video_brightness_title": "Lysstyrke",
"video_contrast_description": "Kontrastnivå ( {value} x)",

View File

@ -395,6 +395,17 @@
"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_update_password_button": "Uppdatera lösenord",
"locale_auto": "Bil",
"locale_change_success": "Språket har ändrats till {locale}",
"locale_da": "Danska",
"locale_de": "Deutsch",
"locale_en": "Engelska",
"locale_es": "Spanska",
"locale_fr": "Franska",
"locale_it": "italiensk",
"locale_nb": "Norska (bokmål)",
"locale_sv": "Svenska",
"locale_zh": "中文 (简体)",
"log_in": "Logga in",
"log_out": "Logga ut",
"logged_in_as": "Inloggad som",
@ -748,6 +759,8 @@
"usb_state_disconnected": "Osammanhängande",
"usb_state_low_power_mode": "Lågströmsläge",
"usb": "USB",
"user_interface_language_description": "Välj språket som ska användas i JetKVM-användargränssnittet",
"user_interface_language_title": "Gränssnittsspråk",
"video_brightness_description": "Ljusstyrka ( {value} x)",
"video_brightness_title": "Ljusstyrka",
"video_contrast_description": "Kontrastnivå ( {value} x)",

View File

@ -395,6 +395,17 @@
"local_auth_success_password_updated_description": "您已成功更改本地设备保护密码。请务必记住新密码,以便日后访问。",
"local_auth_success_password_updated_title": "密码更新成功",
"local_auth_update_password_button": "更新密码",
"locale_auto": "汽车",
"locale_change_success": "语言已成功更改为{locale}",
"locale_da": "丹麦语",
"locale_de": "德语",
"locale_en": "英语",
"locale_es": "西班牙语",
"locale_fr": "法语",
"locale_it": "意大利语",
"locale_nb": "挪威语(博克马尔语)",
"locale_sv": "瑞典语",
"locale_zh": "中文 (简体)",
"log_in": "登录",
"log_out": "登出",
"logged_in_as": "登录身份",
@ -748,6 +759,8 @@
"usb_state_disconnected": "断开连接",
"usb_state_low_power_mode": "低功耗模式",
"usb": "USB",
"user_interface_language_description": "选择 JetKVM 用户界面使用的语言",
"user_interface_language_title": "界面语言",
"video_brightness_description": "亮度级别( {value} x",
"video_brightness_title": "亮度",
"video_contrast_description": "对比度级别( {value} x",

View File

@ -244,6 +244,7 @@ export interface MouseMove {
y: number;
buttons: number;
}
export interface MouseState {
mouseX: number;
mouseY: number;
@ -347,8 +348,10 @@ export interface SettingsState {
// Video enhancement settings
videoSaturation: number;
setVideoSaturation: (value: number) => void;
videoBrightness: number;
setVideoBrightness: (value: number) => void;
videoContrast: number;
setVideoContrast: (value: number) => void;
}
@ -392,8 +395,10 @@ export const useSettingsStore = create(
// Video enhancement settings with default values (1.0 = normal)
videoSaturation: 1.0,
setVideoSaturation: (value: number) => set({ videoSaturation: value }),
videoBrightness: 1.0,
setVideoBrightness: (value: number) => set({ videoBrightness: value }),
videoContrast: 1.0,
setVideoContrast: (value: number) => set({ videoContrast: value }),
}),

View File

@ -1,14 +1,17 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useMemo } from "react";
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
import { useDeviceStore } from "@hooks/stores";
import { Button } from "@components/Button";
import Checkbox from "@components/Checkbox";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { SettingsItem } from "@components/SettingsItem";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import notifications from "@/notifications";
import { getLocale, setLocale, locales, baseLocale } from '@localizations/runtime.js';
import { m } from "@localizations/messages.js";
import { deleteCookie, map_locale_code_to_name } from "@/utils";
export default function SettingsGeneralRoute() {
const { send } = useJsonRpc();
@ -40,6 +43,37 @@ export default function SettingsGeneralRoute() {
});
};
const [currentLocale, setCurrentLocale] = useState(getLocale());
const localeOptions = useMemo(() => {
return ["", ...locales]
.map((code) => {
const [localizedName, nativeName] = map_locale_code_to_name(code);
// don't repeat the name if it's the same in both locales (or blank)
const label = nativeName && nativeName !== localizedName ? `${localizedName} - ${nativeName}` : localizedName;
return { value: code, label: label }
});
}, []);
const handleLocaleChange = (newLocale: string) => {
if (newLocale === currentLocale) return;
let validLocale = newLocale as typeof locales[number];
if (newLocale !== "") {
if (!locales.includes(validLocale)) {
validLocale = baseLocale;
}
setLocale(validLocale); // tell the i18n system to change locale
} else {
deleteCookie("JETKVM_LOCALE", "", "/"); // delete the cookie that the i18n system uses to store the locale
}
setCurrentLocale(validLocale);
notifications.success(m.locale_change_success({ locale: validLocale || m.locale_auto() }));
};
return (
<div className="space-y-4">
<SettingsPageHeader
@ -49,6 +83,20 @@ export default function SettingsGeneralRoute() {
<div className="space-y-4">
<div className="space-y-4 pb-2">
<div className="space-y-4">
<SettingsItem
title={m.user_interface_language_title()}
description={m.user_interface_language_description()}
>
<SelectMenuBasic
size="SM"
label=""
value={currentLocale}
options={localeOptions}
onChange={e => { handleLocaleChange(e.target.value); }}
/>
</SettingsItem>
</div>
<div className="mt-2 flex items-center justify-between gap-x-2">
<SettingsItem
title={m.general_check_for_updates()}
@ -82,7 +130,6 @@ export default function SettingsGeneralRoute() {
/>
</SettingsItem>
</div>
<div className="mt-2 flex items-center justify-between gap-x-2">
<SettingsItem
title={m.general_reboot_device()}

View File

@ -1,5 +1,6 @@
import { KeySequence } from "@hooks/stores";
import { getLocale } from '@localizations/runtime.js';
import { m } from "@localizations/messages.js";
export const formatters = {
date: (date: Date, options?: Intl.DateTimeFormatOptions) =>
@ -254,3 +255,29 @@ export function normalizeSortOrders(macros: KeySequence[]): KeySequence[] {
sortOrder: index + 1,
}));
};
export function map_locale_code_to_name(locale: string): [string, string] {
// the first is the name in the current app locale (e.g. Inglese),
// the second is the name in the language of the locale itself (e.g. English)
switch (locale) {
case '': return [m.locale_auto(), ""];
case 'en': return [m.locale_en(), m.locale_en({}, { locale })];
case 'da': return [m.locale_da(), m.locale_da({}, { locale })];
case 'de': return [m.locale_de(), m.locale_de({}, { locale })];
case 'es': return [m.locale_es(), m.locale_es({}, { locale })];
case 'fr': return [m.locale_fr(), m.locale_fr({}, { locale })];
case 'it': return [m.locale_it(), m.locale_it({}, { locale })];
case 'nb': return [m.locale_nb(), m.locale_nb({}, { locale })];
case 'sv': return [m.locale_sv(), m.locale_sv({}, { locale })];
case 'zh': return [m.locale_zh(), m.locale_zh({}, { locale })];
default: return [locale, ""];
}
}
export function deleteCookie(name: string, domain?: string, path = "/") {
const domainPart = domain ? `; domain=${domain}` : "";
// max-age=0 removes the cookie immediately in modern browsers
document.cookie = `${name}=; path=${path}; max-age=0${domainPart}`;
// fallback: set an expires in the past for older agents
document.cookie = `${name}=; path=${path}; expires=Thu, 01 Jan 1970 00:00:00 GMT${domainPart}`;
}

View File

@ -33,8 +33,7 @@ export default defineConfig(({ mode, command }) => {
outdir: "./localization/paraglide",
outputStructure: 'message-modules',
cookieName: 'JETKVM_LOCALE',
localStorageKey: 'JETKVM_LOCALE',
strategy: ['cookie', 'localStorage', 'preferredLanguage', 'baseLocale'],
strategy: ['cookie', 'preferredLanguage', 'baseLocale'],
}))
return {