i18n polish + Swedish wired + roadmap slim + AppImage 1.2.4.1-beta

## i18n

- Swedish (sv) wired end-to-end: `MESSAGE_CATALOG` in `provider.tsx` now imports `sv/common.json`; `SUPPORTED_LANGUAGES` reordered as English + alphabetical native and all eight locales marked complete.
- Settings → Interface language: the per-locale status cards grid is retired now that all eight locales are populated end-to-end; the dropdown selector is the only control.
- CLI change-language dialog (`scripts/menus/config_menu.sh`) picks up Swedish and matches the Monitor ordering.
- Spanish catalog: label polish across the dashboard — *VM y LXC activos* / *11 VMs* / *11 detenidos*, LXC modal tabs shortened to *App / Actualiz. / Montajes / Copias*, *Limpiar / Cerrar* replacing the wrong *Claro / Cerca* in terminal controls, *En ejecución / Ejecutar* replacing the literal *Correr*.
- Cross-locale normalisation across ES / DE / FR / IT / PT / SK / SV: *OK* / *Proxmox System Dashboard* / *Warn* labels stay consistent, *AVG / MAX / MIN* kept verbatim as universal abbreviations so the stats row no longer overflows.
- Tailscale brand preserved verbatim across ES / FR / IT / PT — previous machine-translated variants like *"escala trasera"* / *"à grande échelle"* restored.

## Docs

- `ROADMAP.md` rewritten as a short invitation to the [ProxMenux Roadmap project board](https://github.com/users/MacRimi/projects/1). `ROADMAP.es.md` removed — it mirrored the same outdated version-milestone plan and the docs site keeps the Spanish surface for users who need it.
- `release.md` annotated with nine pending sections for the v1.2.4.1 beta cycle.

## AppImage

- `ProxMenux-1.2.4.1-beta.AppImage` replaces the previous `ProxMenux-1.2.4.AppImage`. Sidecar `ProxMenux-Monitor.AppImage.sha256` regenerated.
This commit is contained in:
MacRimi
2026-08-10 13:23:26 +02:00
parent 2901b22be6
commit f2420d9978
13 changed files with 79 additions and 106 deletions
+1 -1
View File
@@ -1 +1 @@
5dcad163093e68c6d7742c6724fb7df8f3d5bdd5542462f3120d2a00ad7bf699 ProxMenux-1.2.4.AppImage
d972e4f1fa3e68bf1937135a3cec5f07dc13a04f525b5a7c566df1a2bfbb1ba7 ProxMenux-1.2.4.1-beta.AppImage
+1 -31
View File
@@ -15,7 +15,7 @@ import { Input } from "./ui/input"
import { getNetworkUnit } from "../lib/format-network"
import { fetchApi } from "../lib/api-config"
import { SUPPORTED_LANGUAGES, useI18n } from "../lib/i18n/provider"
import type { LanguageCode, LanguageStatus } from "../lib/i18n/languages"
import type { LanguageCode } from "../lib/i18n/languages"
// GitHub Dark color palette for bash syntax highlighting
const BASH_KEYWORDS = new Set([
@@ -917,17 +917,6 @@ export function Settings() {
k => pendingChanges[k] !== -2
)
const getLanguageStatusLabel = (status: LanguageStatus) => {
switch (status) {
case "complete":
return t("settings.interfaceLanguage.statusComplete")
case "partial":
return t("settings.interfaceLanguage.statusPartial")
case "needs-translation":
return t("settings.interfaceLanguage.statusNeedsTranslation")
}
}
return (
<div className="space-y-6">
<div>
@@ -963,25 +952,6 @@ export function Settings() {
</SelectContent>
</Select>
</div>
<div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
{SUPPORTED_LANGUAGES.map((item) => (
<div
key={item.code}
className={`rounded-md border px-3 py-2 text-sm ${
item.code === language ? "border-blue-500 bg-blue-500/10" : "border-border bg-muted/20"
}`}
>
<div className="flex items-center justify-between gap-2">
<span className="font-medium">{item.nativeName}</span>
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
{item.code}
</Badge>
</div>
<div className="text-xs text-muted-foreground mt-1">{getLanguageStatusLabel(item.status)}</div>
</div>
))}
</div>
</CardContent>
</Card>
+6 -6
View File
@@ -14,13 +14,13 @@ export interface SupportedLanguage {
export const SUPPORTED_LANGUAGES: SupportedLanguage[] = [
{ code: "en", englishName: "English", nativeName: "English", status: "complete" },
{ code: "de", englishName: "German", nativeName: "Deutsch", status: "complete" },
{ code: "es", englishName: "Spanish", nativeName: "Español", status: "complete" },
{ code: "fr", englishName: "French", nativeName: "Français", status: "complete" },
{ code: "it", englishName: "Italian", nativeName: "Italiano", status: "complete" },
{ code: "pt", englishName: "Portuguese", nativeName: "Português", status: "complete" },
{ code: "sk", englishName: "Slovak", nativeName: "Slovenčina", status: "complete" },
{ code: "es", englishName: "Spanish", nativeName: "Español", status: "needs-translation" },
{ code: "fr", englishName: "French", nativeName: "Français", status: "needs-translation" },
{ code: "de", englishName: "German", nativeName: "Deutsch", status: "needs-translation" },
{ code: "it", englishName: "Italian", nativeName: "Italiano", status: "needs-translation" },
{ code: "pt", englishName: "Portuguese", nativeName: "Português", status: "needs-translation" },
{ code: "sv", englishName: "Swedish", nativeName: "Svenska", status: "needs-translation" },
{ code: "sv", englishName: "Swedish", nativeName: "Svenska", status: "complete" },
]
export function isSupportedLanguage(value: string | null | undefined): value is LanguageCode {
+6 -4
View File
@@ -2,12 +2,13 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react"
import enMessages from "../../messages/en/common.json"
import skMessages from "../../messages/sk/common.json"
import deMessages from "../../messages/de/common.json"
import esMessages from "../../messages/es/common.json"
import frMessages from "../../messages/fr/common.json"
import deMessages from "../../messages/de/common.json"
import itMessages from "../../messages/it/common.json"
import ptMessages from "../../messages/pt/common.json"
import skMessages from "../../messages/sk/common.json"
import svMessages from "../../messages/sv/common.json"
import {
DEFAULT_LANGUAGE,
LANGUAGE_STORAGE_KEY,
@@ -22,12 +23,13 @@ type TranslationParams = Record<string, string | number>
const MESSAGE_CATALOG: Record<LanguageCode, MessageTree> = {
en: enMessages as MessageTree,
sk: skMessages as MessageTree,
de: deMessages as MessageTree,
es: esMessages as MessageTree,
fr: frMessages as MessageTree,
de: deMessages as MessageTree,
it: itMessages as MessageTree,
pt: ptMessages as MessageTree,
sk: skMessages as MessageTree,
sv: svMessages as MessageTree,
}
interface I18nContextValue {
+4 -4
View File
@@ -2,7 +2,7 @@
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": {
"title": "ProxMenux Monitor",
"description": "Proxmox-System-Dashboard",
"description": "Proxmox System Dashboard",
"loading": "Laden...",
"connecting": "Verbindung zum ProxMenux Monitor herstellen",
"unknown": "Unbekannt",
@@ -144,7 +144,7 @@
"year": "1 Jahr"
},
"stats": {
"avg": "Durchschn",
"avg": "avg",
"max": "max",
"min": "min"
},
@@ -840,7 +840,7 @@
"topCount": "Top {shown} von {total} Prozessen",
"loadFailed": "Prozesse konnten nicht abgerufen werden",
"lifetimeAverageTitle": "Durchschnittlicher CPU-Prozentsatz über die gesamte Lebensdauer dieses Prozesses nützlich, um Baselines mit langer Laufzeit im Leerlauf zu finden",
"averageShort": "Durchschn"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Verfahren",
@@ -4218,7 +4218,7 @@
"total": "Gesamt",
"healthy": "Gesund",
"info": "Info",
"warning": "Warnen",
"warning": "Warnung",
"critical": "Kritisch",
"unknown": "Unbekannt"
},
+27 -27
View File
@@ -2,7 +2,7 @@
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": {
"title": "ProxMenux Monitor",
"description": "Panel de control del sistema Proxmox",
"description": "Proxmox System Dashboard",
"loading": "Cargando...",
"connecting": "Conexión al ProxMenux Monitor",
"unknown": "Desconocido",
@@ -21,7 +21,7 @@
"cancel": "Cancelar",
"save": "Ahorrar",
"edit": "Editar",
"close": "Cerca",
"close": "Cerrar",
"resetAll": "Restablecer todo",
"undo": "Deshacer",
"copy": "Copiar",
@@ -94,10 +94,10 @@
"cached": "En caché",
"total": "Total",
"free": "Gratis",
"activeVmLxc": "Máquina virtual activa y LXC",
"activeVmLxc": "VM y LXC activos",
"runningCount": "{count} en ejecución",
"vmsCount": "{count} máquinas virtuales",
"stoppedCount": "{count} detenido",
"vmsCount": "{count} VMs",
"stoppedCount": "{count} detenidos",
"temperature": "Temperatura",
"noSensorAvailable": "No hay sensores disponibles",
"collectingData": "Recopilando datos...",
@@ -144,9 +144,9 @@
"year": "1 año"
},
"stats": {
"avg": "promedio",
"max": "máximo",
"min": "mín."
"avg": "avg",
"max": "max",
"min": "min"
},
"errors": {
"serverUnavailableTitle": "El servidor de monitor no está disponible",
@@ -322,7 +322,7 @@
"statusValues": {
"passed": "Aprobado",
"failed": "Fallido",
"running": "Correr",
"running": "En ejecución",
"aborted": "Abortado",
"unknown": "Desconocido"
}
@@ -457,7 +457,7 @@
"recommendations": "Recomendaciones"
},
"statusValues": {
"ok": "DE ACUERDO",
"ok": "OK",
"warning": "Advertencia",
"critical": "Crítico",
"failed": "Fallido",
@@ -840,7 +840,7 @@
"topCount": "Principales {shown} de {total} procesos",
"loadFailed": "No se pudieron recuperar los procesos",
"lifetimeAverageTitle": "% de CPU promedio durante la vida útil de este proceso: útil para encontrar líneas de base inactivas de larga duración",
"averageShort": "promedio"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Proceso",
@@ -880,7 +880,7 @@
"runningFor": "Corriendo por"
},
"states": {
"running": "Correr",
"running": "En ejecución",
"sleeping": "Durmiendo",
"diskWait": "espera de disco",
"zombie": "Zombi",
@@ -896,7 +896,7 @@
"loadingTitle": "Cargando máquinas virtuales y LXC...",
"loadingDescription": "Obteniendo el estado del contenedor VM y LXC",
"loadingError": "Error al cargar máquinas virtuales: {error}",
"totalVmLxc": "Total de máquinas virtuales y LXC",
"totalVmLxc": "Total de VM y LXC",
"totalCpuAllocated": "CPU total asignada",
"totalMemory": "Memoria Total",
"totalDisk": "Disco total",
@@ -1032,11 +1032,11 @@
},
"tabs": {
"status": "Estado",
"mounts": "monturas",
"backups": "Copias de seguridad",
"updates": "Actualizaciones",
"mounts": "Montajes",
"backups": "Copias",
"updates": "Actualiz.",
"firewall": "Cortafuegos",
"app": "Aplicación"
"app": "App"
},
"actions": {
"start": "Comenzar",
@@ -1879,7 +1879,7 @@
"recommended": "Recomendado",
"warning": "Advertencia",
"critical": "Crítico",
"ok": "DE ACUERDO",
"ok": "OK",
"warn": "ADVERTIR",
"crit": "CRÍTICO",
"ram": "RAM",
@@ -1979,7 +1979,7 @@
"avatar": "Avatar",
"change": "Cambiar",
"chooseImage": "Elige imagen",
"clear": "Claro",
"clear": "Limpiar",
"avatarHint": "PNG, JPEG, WebP o GIF · hasta 2 MB · cuadrado precortado para obtener mejores resultados.",
"settingUp": "Configurando...",
"setupAuthentication": "Configurar autenticación",
@@ -3486,7 +3486,7 @@
"apply": "Aplicar",
"back": "Atrás",
"change": "Cambiar",
"clear": "Claro",
"clear": "Limpiar",
"delete": "Borrar",
"disable": "Desactivar",
"download": "Descargar",
@@ -3969,7 +3969,7 @@
"inProgress": "Copia de seguridad manual en progreso",
"oneShotDescription": "Las copias de seguridad únicas se mantienen como trabajos congelados para que puedas inspeccionar su registro más tarde.",
"reopenLogTitle": "Reabrir registro",
"run": "Correr",
"run": "Ejecutar",
"runBackup": "Ejecutar copia de seguridad",
"runOneShotBackup": "Ejecute una copia de seguridad única",
"title": "Copia de seguridad manual",
@@ -4003,7 +4003,7 @@
"blocked": "Obstruido",
"error": "Error",
"failed": "Fallido",
"ok": "DE ACUERDO",
"ok": "OK",
"unknown": "Desconocido",
"warn": "Advertencia"
},
@@ -4208,7 +4208,7 @@
"title": "Estado de salud del sistema",
"description": "Comprobaciones detalladas de todos los componentes del sistema.",
"status": {
"ok": "DE ACUERDO",
"ok": "OK",
"info": "Información",
"warning": "Advertencia",
"critical": "Crítico",
@@ -4218,7 +4218,7 @@
"total": "Total",
"healthy": "Saludable",
"info": "Información",
"warning": "Advertir",
"warning": "Advertencia",
"critical": "Crítico",
"unknown": "Desconocido"
},
@@ -4470,7 +4470,7 @@
},
"componentStatus": {
"installing": "instalando",
"ok": "DE ACUERDO",
"ok": "OK",
"failed": "fallido"
},
"time": {
@@ -4547,7 +4547,7 @@
"gridLayout": "vista de cuadrícula 2×2",
"new": "Nuevo",
"search": "Buscar",
"clear": "Claro",
"clear": "Limpiar",
"searchCommands": "Comandos de búsqueda",
"onlineSource": "En línea: utilizando la API Cheat.sh",
"offlineSource": "Sin conexión: usando comandos locales",
@@ -4594,6 +4594,6 @@
"yes": "Sí",
"yourInput": "Su entrada:",
"submit": "Entregar",
"ok": "DE ACUERDO"
"ok": "OK"
}
}
+11 -11
View File
@@ -2,7 +2,7 @@
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": {
"title": "ProxMenux Monitor",
"description": "Tableau de bord du système Proxmox",
"description": "Proxmox System Dashboard",
"loading": "Chargement...",
"connecting": "Connexion au ProxMenux Monitor",
"unknown": "Inconnu",
@@ -144,8 +144,8 @@
"year": "1 an"
},
"stats": {
"avg": "moyenne",
"max": "maximum",
"avg": "avg",
"max": "max",
"min": "min"
},
"errors": {
@@ -457,7 +457,7 @@
"recommendations": "Recommandations"
},
"statusValues": {
"ok": "D'ACCORD",
"ok": "OK",
"warning": "Avertissement",
"critical": "Critique",
"failed": "Échoué",
@@ -840,7 +840,7 @@
"topCount": "{shown} principaux processus sur {total}",
"loadFailed": "Échec de la récupération des processus",
"lifetimeAverageTitle": "% CPU moyen sur la durée de vie de ce processus - utile pour trouver des lignes de base d'inactivité de longue durée",
"averageShort": "moyenne"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Processus",
@@ -1879,7 +1879,7 @@
"recommended": "Recommandé",
"warning": "Avertissement",
"critical": "Critique",
"ok": "D'ACCORD",
"ok": "OK",
"warn": "AVERTIR",
"crit": "CRIT",
"ram": "RAM",
@@ -4003,7 +4003,7 @@
"blocked": "Bloqué",
"error": "Erreur",
"failed": "Échoué",
"ok": "D'ACCORD",
"ok": "OK",
"unknown": "Inconnu",
"warn": "Avertissement"
},
@@ -4208,7 +4208,7 @@
"title": "État de santé du système",
"description": "Vérifications détaillées de tous les composants du système",
"status": {
"ok": "D'ACCORD",
"ok": "OK",
"info": "Informations",
"warning": "Avertissement",
"critical": "Critique",
@@ -4218,7 +4218,7 @@
"total": "Total",
"healthy": "En bonne santé",
"info": "Informations",
"warning": "Avertir",
"warning": "Avertissement",
"critical": "Critique",
"unknown": "Inconnu"
},
@@ -4470,7 +4470,7 @@
},
"componentStatus": {
"installing": "installation",
"ok": "D'ACCORD",
"ok": "OK",
"failed": "échoué"
},
"time": {
@@ -4594,6 +4594,6 @@
"yes": "Oui",
"yourInput": "Votre contribution :",
"submit": "Soumettre",
"ok": "D'ACCORD"
"ok": "OK"
}
}
+5 -5
View File
@@ -2,7 +2,7 @@
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": {
"title": "ProxMenux Monitor",
"description": "Cruscotto del sistema Proxmox",
"description": "Proxmox System Dashboard",
"loading": "Caricamento...",
"connecting": "Connessione al ProxMenux Monitor",
"unknown": "Sconosciuto",
@@ -144,8 +144,8 @@
"year": "1 anno"
},
"stats": {
"avg": "media",
"max": "massimo",
"avg": "avg",
"max": "max",
"min": "min"
},
"errors": {
@@ -840,7 +840,7 @@
"topCount": "Principali {shown} dei {total} processi",
"loadFailed": "Impossibile recuperare i processi",
"lifetimeAverageTitle": "% CPU media durante la durata di questo processo: utile per trovare linee di base inattive di lunga durata",
"averageShort": "media"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Processo",
@@ -4218,7 +4218,7 @@
"total": "Totale",
"healthy": "Salutare",
"info": "Informazioni",
"warning": "Avvisare",
"warning": "Avviso",
"critical": "Critico",
"unknown": "Sconosciuto"
},
+5 -5
View File
@@ -2,7 +2,7 @@
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": {
"title": "ProxMenux Monitor",
"description": "Painel do sistema Proxmox",
"description": "Proxmox System Dashboard",
"loading": "Carregando...",
"connecting": "Conectando ao ProxMenux Monitor",
"unknown": "Desconhecido",
@@ -144,8 +144,8 @@
"year": "1 ano"
},
"stats": {
"avg": "média",
"max": "máx.",
"avg": "avg",
"max": "max",
"min": "min"
},
"errors": {
@@ -840,7 +840,7 @@
"topCount": "Principais {shown} dos {total} processos",
"loadFailed": "Falha ao buscar processos",
"lifetimeAverageTitle": "% média de CPU ao longo da vida útil deste processo - útil para encontrar linhas de base ociosas de longa duração",
"averageShort": "média"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Processo",
@@ -4218,7 +4218,7 @@
"total": "Total",
"healthy": "Saudável",
"info": "Informações",
"warning": "Avisar",
"warning": "Aviso",
"critical": "Crítico",
"unknown": "Desconhecido"
},
+5 -5
View File
@@ -1,7 +1,7 @@
{
"app": {
"title": "ProxMenux Monitor",
"description": "Systémový prehľad Proxmoxu",
"description": "Proxmox System Dashboard",
"loading": "Načítava sa...",
"connecting": "Pripájam sa k ProxMenux Monitoru",
"unknown": "Neznáme",
@@ -143,7 +143,7 @@
"year": "1 rok"
},
"stats": {
"avg": "priemer",
"avg": "avg",
"max": "max",
"min": "min"
},
@@ -839,7 +839,7 @@
"topCount": "Prvých {shown} z {total} procesov",
"loadFailed": "Nepodarilo sa načítať procesy",
"lifetimeAverageTitle": "Priemerné CPU % za celý život procesu - užitočné pri hľadaní procesov, ktoré dlho bežia potichu na pozadí",
"averageShort": "priem."
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Proces",
@@ -4208,7 +4208,7 @@
"title": "Stav systému",
"description": "Podrobná kontrola všetkých častí systému",
"status": {
"ok": "V poriadku",
"ok": "OK",
"info": "Info",
"warning": "Upozornenie",
"critical": "Problém",
@@ -4218,7 +4218,7 @@
"total": "Spolu",
"healthy": "V poriadku",
"info": "Info",
"warning": "Upozornenia",
"warning": "Upozornenie",
"critical": "Problémy",
"unknown": "Neznáme"
},
+3 -3
View File
@@ -144,7 +144,7 @@
"year": "1 år"
},
"stats": {
"avg": "snitt",
"avg": "avg",
"max": "max",
"min": "min"
},
@@ -840,7 +840,7 @@
"topCount": "Topp {shown} av {total} processer",
"loadFailed": "Det gick inte att hämta processer",
"lifetimeAverageTitle": "Genomsnittlig CPU% över hela processens livstid - användbart för att hitta långvariga inaktiva baslinjer",
"averageShort": "snitt"
"averageShort": "avg"
},
"processInfo": {
"titleFallback": "Behandla",
@@ -4218,7 +4218,7 @@
"total": "Total",
"healthy": "Hälsosam",
"info": "Info",
"warning": "Varna",
"warning": "Varning",
"critical": "Kritisk",
"unknown": "Okänd"
},
+4 -3
View File
@@ -638,14 +638,15 @@ change_language() {
local new_language
new_language=$(dialog --clear --backtitle "$BACKTITLE" \
--title "$(translate "Change Language")" \
--menu "$(translate "Select a new language for the menu:")" 21 60 7 \
--menu "$(translate "Select a new language for the menu:")" 22 60 8 \
"en" "$(translate "English")" \
"de" "$(translate "German")" \
"es" "$(translate "Spanish")" \
"fr" "$(translate "French")" \
"de" "$(translate "German")" \
"it" "$(translate "Italian")" \
"pt" "$(translate "Portuguese")" \
"sk" "Slovenčina" 3>&1 1>&2 2>&3)
"sk" "Slovenčina" \
"sv" "Svenska" 3>&1 1>&2 2>&3)
if [ -z "$new_language" ]; then
dialog --clear --backtitle "$BACKTITLE" \